From b9c15c5d3bfb503676fffccab69fe27c5b532283 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 14 Aug 2023 09:40:09 +0200 Subject: [PATCH 1/8] clarify safety documentation of ptr::swap and ptr::copy --- library/core/src/intrinsics.rs | 3 +++ library/core/src/ptr/mod.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 9ef2c7cde02eb..84b9a3bba88e3 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2709,6 +2709,9 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// /// * `dst` must be [valid] for writes of `count * size_of::()` bytes. /// +/// * `src` must remain valid for reads even after `dst` is written, and vice versa. +/// (In other words, there cannot be aliasing restrictions on the use of these pointers.) +/// /// * Both `src` and `dst` must be properly aligned. /// /// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5f094ac4e7e64..f8badc4130dca 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -797,6 +797,9 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// /// * Both `x` and `y` must be [valid] for both reads and writes. /// +/// * `x` must remain valid for reads and writes even after `y` is read/written, and vice versa. +/// (In other words, there cannot be aliasing restrictions on the use of these pointers.) +/// /// * Both `x` and `y` must be properly aligned. /// /// Note that even if `T` has size `0`, the pointers must be non-null and properly aligned. From 0188b9cbb43c2631111281fad0624f8bb0538e3a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Aug 2023 13:54:03 +0200 Subject: [PATCH 2/8] try to clarify wording --- library/core/src/intrinsics.rs | 11 ++++++----- library/core/src/ptr/mod.rs | 7 +++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 84b9a3bba88e3..f7469008d7c00 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2705,12 +2705,13 @@ pub const unsafe fn copy_nonoverlapping(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::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// * `src` must be [valid] for reads of `count * size_of::()` bytes, and must remain valid even +/// if `dst` is written for `count * size_of::()` bytes. (This means if the memory ranges +/// overlap, the two pointers must not be subject to aliasing restrictions relative to each +/// other.) /// -/// * `src` must remain valid for reads even after `dst` is written, and vice versa. -/// (In other words, there cannot be aliasing restrictions on the use of these pointers.) +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even +/// if `src` is read for `count * size_of::()` bytes. /// /// * Both `src` and `dst` must be properly aligned. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f8badc4130dca..69d775075f369 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -795,10 +795,9 @@ pub const fn slice_from_raw_parts_mut(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. -/// -/// * `x` must remain valid for reads and writes even after `y` is read/written, and vice versa. -/// (In other words, there cannot be aliasing restrictions on the use of these pointers.) +/// * Both `x` and `y` must be [valid] for both reads and writes. They must remain valid even if 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. /// From 98fa0c93ee031277e64eaee11fe9dbfe2147f532 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 5 Sep 2023 11:10:00 +0200 Subject: [PATCH 3/8] unconstrained region vars: do not ICE ICE baby --- .../src/traits/outlives_bounds.rs | 14 ++++------ .../implied-bounds-unconstrained-1.rs | 28 +++++++++++++++++++ .../implied-bounds-unconstrained-2.rs | 20 +++++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs create mode 100644 tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 339baf611f3c3..32bbd626d4e49 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -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 = diff --git a/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs b/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs new file mode 100644 index 0000000000000..025e5176ff7ee --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs @@ -0,0 +1,28 @@ +// check-pass + +// Regression test for #112832. +pub trait QueryDb { + type Db; +} + +pub struct QueryTable { + db: DB, + storage: Q, +} + +// We normalize `::Db` to `>::SendDb` +// using the where-bound. 'd is an unconstrained region variable which previously +// triggered an assert. +impl QueryTable::Db> where Q: for<'d> AsyncQueryFunction<'d> {} + +pub trait AsyncQueryFunction<'d>: QueryDb>::SendDb> { + type SendDb: 'd; +} + +pub trait QueryStorageOpsAsync +where + Q: for<'d> AsyncQueryFunction<'d>, +{ +} + +fn main() {} diff --git a/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs b/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs new file mode 100644 index 0000000000000..976054facee5e --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs @@ -0,0 +1,20 @@ +// check-pass + +// Another minimized regression test for #112832. +trait Trait { + type Assoc; +} + +trait Sub<'a>: Trait>::SubAssoc> { + type SubAssoc; +} + +// By using the where-clause we normalize `::Assoc` to +// `>::SubAssoc` where `'a` is an unconstrained region +// variable. +fn foo(x: ::Assoc) +where + for<'a> T: Sub<'a>, +{} + +fn main() {} From 4684ffaf2ac5c7bb1467baf4e7f01469488c8ef2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 Sep 2023 17:20:31 +0200 Subject: [PATCH 4/8] if -> when --- library/core/src/intrinsics.rs | 4 ++-- library/core/src/ptr/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f7469008d7c00..639e33762cf6b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2706,12 +2706,12 @@ pub const unsafe fn copy_nonoverlapping(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::()` bytes, and must remain valid even -/// if `dst` is written for `count * size_of::()` bytes. (This means if the memory ranges +/// when `dst` is written for `count * size_of::()` 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::()` bytes, and must remain valid even -/// if `src` is read for `count * size_of::()` bytes. +/// when `src` is read for `count * size_of::()` bytes. /// /// * Both `src` and `dst` must be properly aligned. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 69d775075f369..452800516f713 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -795,7 +795,7 @@ pub const fn slice_from_raw_parts_mut(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. They must remain valid even if the +/// * 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.) /// From 2db01be5844ded1139eb4fb5f1434d0090b6ba38 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 30 Aug 2023 16:50:07 -0700 Subject: [PATCH 5/8] Adjust StableMIR interface to return and not crash Invoking StableMir::run() on a crate that has any compilation error was crashing the entire process. Instead, return a `CompilerError` so the user knows compilation did not succeed. I believe ICE will also be converted to `CompilerError`. I'm also adding a return value to the callback, because I think it will be handy for users (at least it was for my current task of implementing a tool to validate stable-mir). However, if people disagree, I can remove that. --- compiler/rustc_smir/src/rustc_internal/mod.rs | 35 +++++++++++++------ compiler/rustc_smir/src/rustc_smir/mod.rs | 9 ++++- compiler/rustc_smir/src/stable_mir/mod.rs | 4 +++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 2e219e17a5f6e..4496a58327eba 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -7,6 +7,7 @@ use std::fmt::Debug; use std::ops::Index; use crate::rustc_internal; +use crate::stable_mir::CompilerError; use crate::{ rustc_smir::Tables, stable_mir::{self, with}, @@ -17,6 +18,7 @@ use rustc_middle::mir::interpret::AllocId; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyErrorHandler; pub use rustc_span::def_id::{CrateNum, DefId}; +use rustc_span::ErrorGuaranteed; fn with_tables(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R { let mut ret = None; @@ -189,27 +191,38 @@ pub(crate) fn opaque(value: &T) -> Opaque { Opaque(format!("{value:?}")) } -pub struct StableMir { +pub struct StableMir +where + T: Send, +{ args: Vec, - callback: fn(TyCtxt<'_>), + callback: fn(TyCtxt<'_>) -> T, + result: Option, } -impl StableMir { +impl StableMir +where + T: Send, +{ /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec, callback: fn(TyCtxt<'_>)) -> Self { - StableMir { args, callback } + pub fn new(args: Vec, callback: fn(TyCtxt<'_>) -> T) -> Self { + StableMir { args, callback, result: None } } /// Runs the compiler against given target and tests it with `test_function` - pub fn run(&mut self) { + pub fn run(mut self) -> Result { rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), self).run().unwrap(); + RunCompiler::new(&self.args.clone(), &mut self).run().unwrap(); }) - .unwrap(); + .map_err(|e| >::into(e))?; + Ok(self.result.unwrap()) } } -impl Callbacks for StableMir { +impl Callbacks for StableMir +where + T: Send, +{ /// Called after analysis. Return value instructs the compiler whether to /// continue the compilation afterwards (defaults to `Compilation::Continue`) fn after_analysis<'tcx>( @@ -219,7 +232,9 @@ impl Callbacks for StableMir { queries: &'tcx Queries<'tcx>, ) -> Compilation { queries.global_ctxt().unwrap().enter(|tcx| { - rustc_internal::run(tcx, || (self.callback)(tcx)); + rustc_internal::run(tcx, || { + self.result = Some((self.callback)(tcx)); + }); }); // No need to keep going. Compilation::Stop diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index b4c150c591c48..827b51e8a3658 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -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; @@ -1452,3 +1453,9 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { opaque(self) } } + +impl From for CompilerError { + fn from(_error: ErrorGuaranteed) -> Self { + CompilerError + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index bfc2c15a42eb0..281d5aafb4cc3 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -56,6 +56,10 @@ pub type TraitDecls = Vec; /// A list of impl trait decls. pub type ImplTraitDecls = Vec; +/// An error type used to represent an error that has already been reported by the compiler. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CompilerError; + /// Holds information about a crate. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Crate { From 3b01f65aa5cd19fe02b1bfd4e7e60390d8ef83fc Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 31 Aug 2023 16:53:28 -0700 Subject: [PATCH 6/8] Diferentiate between ICE and compilation error --- compiler/rustc_smir/src/rustc_internal/mod.rs | 14 ++++++++------ compiler/rustc_smir/src/rustc_smir/mod.rs | 2 +- compiler/rustc_smir/src/stable_mir/mod.rs | 7 ++++++- tests/ui-fulldeps/stable-mir/crate-info.rs | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 4496a58327eba..1c5a0924f4a95 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -18,7 +18,6 @@ use rustc_middle::mir::interpret::AllocId; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyErrorHandler; pub use rustc_span::def_id::{CrateNum, DefId}; -use rustc_span::ErrorGuaranteed; fn with_tables(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R { let mut ret = None; @@ -211,11 +210,14 @@ where /// Runs the compiler against given target and tests it with `test_function` pub fn run(mut self) -> Result { - rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), &mut self).run().unwrap(); - }) - .map_err(|e| >::into(e))?; - Ok(self.result.unwrap()) + let compiler_result = rustc_driver::catch_fatal_errors(|| { + RunCompiler::new(&self.args.clone(), &mut self).run() + }); + match compiler_result { + Ok(Ok(())) => Ok(self.result.unwrap()), + Ok(Err(_)) => Err(CompilerError::CompilationFailed), + Err(_) => Err(CompilerError::ICE), + } } } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 827b51e8a3658..3909c3d85ebfa 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -1456,6 +1456,6 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { impl From for CompilerError { fn from(_error: ErrorGuaranteed) -> Self { - CompilerError + CompilerError::CompilationFailed } } diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 281d5aafb4cc3..bdbbab68e396a 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -58,7 +58,12 @@ pub type ImplTraitDecls = Vec; /// An error type used to represent an error that has already been reported by the compiler. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct CompilerError; +pub enum CompilerError { + /// Internal compiler error (I.e.: Compiler crashed). + ICE, + /// Compilation failed. + CompilationFailed, +} /// Holds information about a crate. #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 00dce3e004e63..5439a884637e5 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -136,7 +136,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<()> { From 1a8a5d0a296f24b5184a3997e6dbedb2d31c079a Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 31 Aug 2023 20:40:48 -0700 Subject: [PATCH 7/8] SMIR: Allow users to pick if compilation continues Currently we stop compilation, but some users might want to keep going. This is needed for us to test against rustc tests. Other tools, such as Kani, also implements parts of their logic as a backend so it is important for compilation to continue. --- compiler/rustc_smir/src/rustc_internal/mod.rs | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 1c5a0924f4a95..26127c5eb854b 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -196,6 +196,7 @@ where { args: Vec, callback: fn(TyCtxt<'_>) -> T, + after_analysis: Compilation, result: Option, } @@ -205,16 +206,27 @@ where { /// Creates a new `StableMir` instance, with given test_function and arguments. pub fn new(args: Vec, callback: fn(TyCtxt<'_>) -> T) -> Self { - StableMir { args, callback, result: None } + StableMir { args, callback, result: None, after_analysis: Compilation::Stop } + } + + /// Configure object to stop compilation after callback is called. + pub fn stop_compilation(&mut self) -> &mut Self { + self.after_analysis = Compilation::Stop; + self + } + + /// Configure object to continue compilation after callback is called. + pub fn continue_compilation(&mut self) -> &mut Self { + self.after_analysis = Compilation::Continue; + self } /// Runs the compiler against given target and tests it with `test_function` - pub fn run(mut self) -> Result { - let compiler_result = rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), &mut self).run() - }); + pub fn run(&mut self) -> Result { + let compiler_result = + rustc_driver::catch_fatal_errors(|| RunCompiler::new(&self.args.clone(), self).run()); match compiler_result { - Ok(Ok(())) => Ok(self.result.unwrap()), + Ok(Ok(())) => Ok(self.result.take().unwrap()), Ok(Err(_)) => Err(CompilerError::CompilationFailed), Err(_) => Err(CompilerError::ICE), } @@ -238,7 +250,7 @@ where self.result = Some((self.callback)(tcx)); }); }); - // No need to keep going. - Compilation::Stop + // Let users define if they want to stop compilation. + self.after_analysis } } From d10d8290ac50b5ea8c0ca72bf216cf414b2ada24 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 1 Sep 2023 21:12:46 -0700 Subject: [PATCH 8/8] Add tests and use ControlFlow --- compiler/rustc_smir/src/rustc_internal/mod.rs | 59 +++++++------- compiler/rustc_smir/src/rustc_smir/mod.rs | 2 +- compiler/rustc_smir/src/stable_mir/mod.rs | 7 +- .../stable-mir/compilation-result.rs | 77 +++++++++++++++++++ tests/ui-fulldeps/stable-mir/crate-info.rs | 5 +- 5 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/compilation-result.rs diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 26127c5eb854b..73b8e060dd830 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -4,7 +4,7 @@ //! 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; @@ -190,52 +190,44 @@ pub(crate) fn opaque(value: &T) -> Opaque { Opaque(format!("{value:?}")) } -pub struct StableMir +pub struct StableMir where - T: Send, + B: Send, + C: Send, { args: Vec, - callback: fn(TyCtxt<'_>) -> T, - after_analysis: Compilation, - result: Option, + callback: fn(TyCtxt<'_>) -> ControlFlow, + result: Option>, } -impl StableMir +impl StableMir where - T: Send, + B: Send, + C: Send, { /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec, callback: fn(TyCtxt<'_>) -> T) -> Self { - StableMir { args, callback, result: None, after_analysis: Compilation::Stop } - } - - /// Configure object to stop compilation after callback is called. - pub fn stop_compilation(&mut self) -> &mut Self { - self.after_analysis = Compilation::Stop; - self - } - - /// Configure object to continue compilation after callback is called. - pub fn continue_compilation(&mut self) -> &mut Self { - self.after_analysis = Compilation::Continue; - self + pub fn new(args: Vec, callback: fn(TyCtxt<'_>) -> ControlFlow) -> Self { + StableMir { args, callback, result: None } } /// Runs the compiler against given target and tests it with `test_function` - pub fn run(&mut self) -> Result { + pub fn run(&mut self) -> Result> { let compiler_result = rustc_driver::catch_fatal_errors(|| RunCompiler::new(&self.args.clone(), self).run()); - match compiler_result { - Ok(Ok(())) => Ok(self.result.take().unwrap()), - Ok(Err(_)) => Err(CompilerError::CompilationFailed), - Err(_) => Err(CompilerError::ICE), + 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 Callbacks for StableMir where - T: Send, + B: Send, + C: Send, { /// Called after analysis. Return value instructs the compiler whether to /// continue the compilation afterwards (defaults to `Compilation::Continue`) @@ -249,8 +241,11 @@ where rustc_internal::run(tcx, || { self.result = Some((self.callback)(tcx)); }); - }); - // Let users define if they want to stop compilation. - self.after_analysis + if self.result.as_ref().is_some_and(|val| val.is_continue()) { + Compilation::Continue + } else { + Compilation::Stop + } + }) } } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 3909c3d85ebfa..52ba4bd4e579d 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -1454,7 +1454,7 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { } } -impl From for CompilerError { +impl From for CompilerError { fn from(_error: ErrorGuaranteed) -> Self { CompilerError::CompilationFailed } diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index bdbbab68e396a..f9eafd9de7ad8 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -58,11 +58,16 @@ pub type ImplTraitDecls = Vec; /// 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 { +pub enum CompilerError { /// 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. diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs new file mode 100644 index 0000000000000..23a9e2a064cb6 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -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) { + 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) { + let continue_fn = |_: TyCtxt| ControlFlow::Break::(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) { + 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) { + 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(()) +} diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 5439a884637e5..182f97373ea44 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -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); @@ -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.