Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 3 pull requests #115579

Merged
merged 11 commits into from
Sep 5, 2023
56 changes: 40 additions & 16 deletions compiler/rustc_smir/src/rustc_internal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
//! until stable MIR is complete.

use std::fmt::Debug;
use std::ops::Index;
use std::ops::{ControlFlow, Index};

use crate::rustc_internal;
use crate::stable_mir::CompilerError;
use crate::{
rustc_smir::Tables,
stable_mir::{self, with},
Expand Down Expand Up @@ -189,27 +190,45 @@ pub(crate) fn opaque<T: Debug>(value: &T) -> Opaque {
Opaque(format!("{value:?}"))
}

pub struct StableMir {
pub struct StableMir<B = (), C = ()>
where
B: Send,
C: Send,
{
args: Vec<String>,
callback: fn(TyCtxt<'_>),
callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>,
result: Option<ControlFlow<B, C>>,
}

impl StableMir {
impl<B, C> StableMir<B, C>
where
B: Send,
C: Send,
{
/// Creates a new `StableMir` instance, with given test_function and arguments.
pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>)) -> Self {
StableMir { args, callback }
pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self {
StableMir { args, callback, result: None }
}

/// Runs the compiler against given target and tests it with `test_function`
pub fn run(&mut self) {
rustc_driver::catch_fatal_errors(|| {
RunCompiler::new(&self.args.clone(), self).run().unwrap();
})
.unwrap();
pub fn run(&mut self) -> Result<C, CompilerError<B>> {
let compiler_result =
rustc_driver::catch_fatal_errors(|| RunCompiler::new(&self.args.clone(), self).run());
match (compiler_result, self.result.take()) {
(Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value),
(Ok(Ok(())), Some(ControlFlow::Break(value))) => Err(CompilerError::Interrupted(value)),
(Ok(Ok(_)), None) => Err(CompilerError::Skipped),
(Ok(Err(_)), _) => Err(CompilerError::CompilationFailed),
(Err(_), _) => Err(CompilerError::ICE),
}
}
}

impl Callbacks for StableMir {
impl<B, C> Callbacks for StableMir<B, C>
where
B: Send,
C: Send,
{
/// Called after analysis. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_analysis<'tcx>(
Expand All @@ -219,9 +238,14 @@ impl Callbacks for StableMir {
queries: &'tcx Queries<'tcx>,
) -> Compilation {
queries.global_ctxt().unwrap().enter(|tcx| {
rustc_internal::run(tcx, || (self.callback)(tcx));
});
// No need to keep going.
Compilation::Stop
rustc_internal::run(tcx, || {
self.result = Some((self.callback)(tcx));
});
if self.result.as_ref().is_some_and(|val| val.is_continue()) {
Compilation::Continue
} else {
Compilation::Stop
}
})
}
}
9 changes: 8 additions & 1 deletion compiler/rustc_smir/src/rustc_smir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
use crate::rustc_internal::{self, opaque};
use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx};
use crate::stable_mir::ty::{FloatTy, GenericParamDef, IntTy, Movability, RigidTy, TyKind, UintTy};
use crate::stable_mir::{self, Context};
use crate::stable_mir::{self, CompilerError, Context};
use rustc_hir as hir;
use rustc_middle::mir::interpret::{alloc_range, AllocId};
use rustc_middle::mir::{self, ConstantKind};
use rustc_middle::ty::{self, Ty, TyCtxt, Variance};
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_span::ErrorGuaranteed;
use rustc_target::abi::FieldIdx;
use tracing::debug;

Expand Down Expand Up @@ -1452,3 +1453,9 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span {
opaque(self)
}
}

impl<T> From<ErrorGuaranteed> for CompilerError<T> {
fn from(_error: ErrorGuaranteed) -> Self {
CompilerError::CompilationFailed
}
}
14 changes: 14 additions & 0 deletions compiler/rustc_smir/src/stable_mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ pub type TraitDecls = Vec<TraitDef>;
/// A list of impl trait decls.
pub type ImplTraitDecls = Vec<ImplDef>;

/// An error type used to represent an error that has already been reported by the compiler.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CompilerError<T> {
/// Internal compiler error (I.e.: Compiler crashed).
ICE,
/// Compilation failed.
CompilationFailed,
/// Compilation was interrupted.
Interrupted(T),
/// Compilation skipped. This happens when users invoke rustc to retrieve information such as
/// --version.
Skipped,
}

/// Holds information about a crate.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Crate {
Expand Down
14 changes: 5 additions & 9 deletions compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,12 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);

// We do not expect existential variables in implied bounds.
// We may however encounter unconstrained lifetime variables in invalid
// code. See #110161 for context.
// We may however encounter unconstrained lifetime variables
// in very rare cases.
//
// See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
// an example.
assert!(!ty.has_non_region_infer());
if ty.has_infer() {
self.tcx.sess.delay_span_bug(
self.tcx.def_span(body_id),
"skipped implied_outlives_bounds due to unconstrained lifetimes",
);
return vec![];
}

let mut canonical_var_values = OriginalQueryValues::default();
let canonical_ty =
Expand Down
8 changes: 6 additions & 2 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2707,9 +2707,13 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes, and must remain valid even
/// when `dst` is written for `count * size_of::<T>()` bytes. (This means if the memory ranges
/// overlap, the two pointers must not be subject to aliasing restrictions relative to each
/// other.)
///
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
/// when `src` is read for `count * size_of::<T>()` bytes.
///
/// * Both `src` and `dst` must be properly aligned.
///
Expand Down
4 changes: 3 additions & 1 deletion library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,9 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * Both `x` and `y` must be [valid] for both reads and writes.
/// * Both `x` and `y` must be [valid] for both reads and writes. They must remain valid even when the
/// other pointer is written. (This means if the memory ranges overlap, the two pointers must not
/// be subject to aliasing restrictions relative to each other.)
///
/// * Both `x` and `y` must be properly aligned.
///
Expand Down
77 changes: 77 additions & 0 deletions tests/ui-fulldeps/stable-mir/compilation-result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// run-pass
// Test StableMIR behavior when different results are given

// ignore-stage1
// ignore-cross-compile
// ignore-remote
// edition: 2021

#![feature(rustc_private)]
#![feature(assert_matches)]

extern crate rustc_middle;
extern crate rustc_smir;

use rustc_middle::ty::TyCtxt;
use rustc_smir::{rustc_internal, stable_mir};
use std::io::Write;
use std::ops::ControlFlow;

/// This test will generate and analyze a dummy crate using the stable mir.
/// For that, it will first write the dummy crate into a file.
/// Then it will create a `StableMir` using custom arguments and then
/// it will run the compiler.
fn main() {
let path = "input_compilation_result_test.rs";
generate_input(&path).unwrap();
let args = vec!["rustc".to_string(), path.to_string()];
test_continue(args.clone());
test_break(args.clone());
test_failed(args.clone());
test_skipped(args);
}

fn test_continue(args: Vec<String>) {
let continue_fn = |_: TyCtxt| ControlFlow::Continue::<(), bool>(true);
let result = rustc_internal::StableMir::new(args, continue_fn).run();
assert_eq!(result, Ok(true));
}

fn test_break(args: Vec<String>) {
let continue_fn = |_: TyCtxt| ControlFlow::Break::<bool, i32>(false);
let result = rustc_internal::StableMir::new(args, continue_fn).run();
assert_eq!(result, Err(stable_mir::CompilerError::Interrupted(false)));
}

fn test_skipped(mut args: Vec<String>) {
args.push("--version".to_string());
let unreach_fn = |_: TyCtxt| -> ControlFlow<()> { unreachable!() };
let result = rustc_internal::StableMir::new(args, unreach_fn).run();
assert_eq!(result, Err(stable_mir::CompilerError::Skipped));
}

fn test_failed(mut args: Vec<String>) {
args.push("--cfg=broken".to_string());
let unreach_fn = |_: TyCtxt| -> ControlFlow<()> { unreachable!() };
let result = rustc_internal::StableMir::new(args, unreach_fn).run();
assert_eq!(result, Err(stable_mir::CompilerError::CompilationFailed));
}

fn generate_input(path: &str) -> std::io::Result<()> {
let mut file = std::fs::File::create(path)?;
write!(
file,
r#"
// This should trigger a compilation failure when enabled.
#[cfg(broken)]
mod broken_mod {{
fn call_invalid() {{
invalid_fn();
}}
}}

fn main() {{}}
"#
)?;
Ok(())
}
7 changes: 5 additions & 2 deletions tests/ui-fulldeps/stable-mir/crate-info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ use rustc_middle::ty::TyCtxt;
use rustc_smir::{rustc_internal, stable_mir};
use std::assert_matches::assert_matches;
use std::io::Write;
use std::ops::ControlFlow;

const CRATE_NAME: &str = "input";

/// This function uses the Stable MIR APIs to get information about the test crate.
fn test_stable_mir(tcx: TyCtxt<'_>) {
fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> {
// Get the local crate using stable_mir API.
let local = stable_mir::local_crate();
assert_eq!(&local.name, CRATE_NAME);
Expand Down Expand Up @@ -108,6 +109,8 @@ fn test_stable_mir(tcx: TyCtxt<'_>) {
stable_mir::mir::Terminator::Assert { .. } => {}
other => panic!("{other:?}"),
}

ControlFlow::Continue(())
}

// Use internal API to find a function in a crate.
Expand Down Expand Up @@ -136,7 +139,7 @@ fn main() {
CRATE_NAME.to_string(),
path.to_string(),
];
rustc_internal::StableMir::new(args, test_stable_mir).run();
rustc_internal::StableMir::new(args, test_stable_mir).run().unwrap();
}

fn generate_input(path: &str) -> std::io::Result<()> {
Expand Down
28 changes: 28 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// check-pass

// Regression test for #112832.
pub trait QueryDb {
type Db;
}

pub struct QueryTable<Q, DB> {
db: DB,
storage: Q,
}

// We normalize `<Q as QueryDb>::Db` to `<Q as AsyncQueryFunction<'d>>::SendDb`
// using the where-bound. 'd is an unconstrained region variable which previously
// triggered an assert.
impl<Q> QueryTable<Q, <Q as QueryDb>::Db> where Q: for<'d> AsyncQueryFunction<'d> {}

pub trait AsyncQueryFunction<'d>: QueryDb<Db = <Self as AsyncQueryFunction<'d>>::SendDb> {
type SendDb: 'd;
}

pub trait QueryStorageOpsAsync<Q>
where
Q: for<'d> AsyncQueryFunction<'d>,
{
}

fn main() {}
20 changes: 20 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// check-pass

// Another minimized regression test for #112832.
trait Trait {
type Assoc;
}

trait Sub<'a>: Trait<Assoc = <Self as Sub<'a>>::SubAssoc> {
type SubAssoc;
}

// By using the where-clause we normalize `<T as Trait>::Assoc` to
// `<T as Sub<'a>>::SubAssoc` where `'a` is an unconstrained region
// variable.
fn foo<T>(x: <T as Trait>::Assoc)
where
for<'a> T: Sub<'a>,
{}

fn main() {}