about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-09-05 19:21:46 +0000
committerbors <bors@rust-lang.org>2023-09-05 19:21:46 +0000
commitf91c53d73820251cc222356dcb65586469cf2018 (patch)
tree4a30fae8d92047ec9e58349de633c0dbadd7cb41
parentab45885dec2a6552cb060a5b7183653baaecd580 (diff)
parent8c0490782e8ba3da4aaf9250e886478b9d3a7f48 (diff)
Auto merge of #115579 - matthiaskrgr:rollup-n4ijxl7, r=matthiaskrgr
Rollup of 3 pull requests

Successful merges:

 - #114794 (clarify safety documentation of ptr::swap and ptr::copy)
 - #115397 (Add support to return value in StableMIR interface and not crash due to compilation error)
 - #115559 (implied bounds: do not ICE on unconstrained region vars)

Failed merges:

 - #115532 (Implement SMIR generic parameter instantiation)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs56
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs9
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs14
-rw-r--r--library/core/src/intrinsics.rs8
-rw-r--r--library/core/src/ptr/mod.rs4
-rw-r--r--tests/ui-fulldeps/stable-mir/compilation-result.rs77
-rw-r--r--tests/ui-fulldeps/stable-mir/crate-info.rs7
-rw-r--r--tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs28
-rw-r--r--tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs20
10 files changed, 206 insertions, 31 deletions
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 2e219e17a5f..73b8e060dd8 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -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},
@@ -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>(
@@ -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
+            }
+        })
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index b4c150c591c..52ba4bd4e57 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<T> From<ErrorGuaranteed> for CompilerError<T> {
+    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 bfc2c15a42e..f9eafd9de7a 100644
--- a/compiler/rustc_smir/src/stable_mir/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -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 {
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 339baf611f3..32bbd626d4e 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/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 5a9c900f643..4c76662ac09 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -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.
 ///
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 01e36044899..41e67fd8435 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -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.
 ///
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 00000000000..23a9e2a064c
--- /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<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(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs
index 00dce3e004e..182f97373ea 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.
@@ -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<()> {
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 00000000000..025e5176ff7
--- /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<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() {}
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 00000000000..976054facee
--- /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<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() {}