about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs17
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs42
-rw-r--r--tests/ui/codegen/sub-principals-in-codegen.rs8
5 files changed, 49 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 582b5a763e6..7ea8c300088 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3448,6 +3448,7 @@ dependencies = [
  "rustc_span",
  "rustc_symbol_mangling",
  "rustc_target",
+ "rustc_trait_selection",
  "rustc_type_ir",
  "serde_json",
  "smallvec",
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index 339628053a9..5c297ebfadb 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -2,6 +2,7 @@
 //!
 //! [`PointerCoercion::Unsize`]: `rustc_middle::ty::adjustment::PointerCoercion::Unsize`
 
+use rustc_codegen_ssa::base::validate_trivial_unsize;
 use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 
 use crate::base::codegen_panic_nounwind;
@@ -34,20 +35,8 @@ pub(crate) fn unsized_info<'tcx>(
             let old_info =
                 old_info.expect("unsized_info: missing old info for trait upcasting coercion");
             if data_a.principal_def_id() == data_b.principal_def_id() {
-                // Codegen takes advantage of the additional assumption, where if the
-                // principal trait def id of what's being casted doesn't change,
-                // then we don't need to adjust the vtable at all. This
-                // corresponds to the fact that `dyn Tr<A>: Unsize<dyn Tr<B>>`
-                // requires that `A = B`; we don't allow *upcasting* objects
-                // between the same trait with different args. If we, for
-                // some reason, were to relax the `Unsize` trait, it could become
-                // unsound, so let's assert here that the trait refs are *equal*.
-                //
-                // We can use `assert_eq` because the binders should have been anonymized,
-                // and because higher-ranked equality now requires the binders are equal.
-                debug_assert_eq!(
-                    data_a.principal(),
-                    data_b.principal(),
+                debug_assert!(
+                    validate_trivial_unsize(fx.tcx, data_a, data_b),
                     "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}"
                 );
                 return old_info;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 81590674ec7..346f07dfa0a 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -34,6 +34,7 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 serde_json = "1.0.59"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 5c67600e4ee..1d391254983 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -27,6 +27,8 @@ use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
 use rustc_span::symbol::sym;
 use rustc_span::{DUMMY_SP, Symbol};
 use rustc_target::abi::FIRST_VARIANT;
+use rustc_trait_selection::infer::TyCtxtInferExt;
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 use tracing::{debug, info};
 
 use crate::assert_module_sources::CguReuse;
@@ -101,6 +103,38 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx.sext(cmp, ret_ty)
 }
 
+/// Codegen takes advantage of the additional assumption, where if the
+/// principal trait def id of what's being casted doesn't change,
+/// then we don't need to adjust the vtable at all. This
+/// corresponds to the fact that `dyn Tr<A>: Unsize<dyn Tr<B>>`
+/// requires that `A = B`; we don't allow *upcasting* objects
+/// between the same trait with different args. If we, for
+/// some reason, were to relax the `Unsize` trait, it could become
+/// unsound, so let's validate here that the trait refs are subtypes.
+pub fn validate_trivial_unsize<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    data_a: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+    data_b: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+) -> bool {
+    match (data_a.principal(), data_b.principal()) {
+        (Some(principal_a), Some(principal_b)) => {
+            let infcx = tcx.infer_ctxt().build();
+            let ocx = ObligationCtxt::new(&infcx);
+            let Ok(()) = ocx.sub(
+                &ObligationCause::dummy(),
+                ty::ParamEnv::reveal_all(),
+                principal_a,
+                principal_b,
+            ) else {
+                return false;
+            };
+            ocx.select_all_or_error().is_empty()
+        }
+        (None, None) => true,
+        _ => false,
+    }
+}
+
 /// Retrieves the information we are losing (making dynamic) in an unsizing
 /// adjustment.
 ///
@@ -133,12 +167,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 // between the same trait with different args. If we, for
                 // some reason, were to relax the `Unsize` trait, it could become
                 // unsound, so let's assert here that the trait refs are *equal*.
-                //
-                // We can use `assert_eq` because the binders should have been anonymized,
-                // and because higher-ranked equality now requires the binders are equal.
-                debug_assert_eq!(
-                    data_a.principal(),
-                    data_b.principal(),
+                debug_assert!(
+                    validate_trivial_unsize(cx.tcx(), data_a, data_b),
                     "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}"
                 );
 
diff --git a/tests/ui/codegen/sub-principals-in-codegen.rs b/tests/ui/codegen/sub-principals-in-codegen.rs
new file mode 100644
index 00000000000..178c10da596
--- /dev/null
+++ b/tests/ui/codegen/sub-principals-in-codegen.rs
@@ -0,0 +1,8 @@
+//@ build-pass
+
+// Regression test for an overly aggressive assertion in #130855.
+
+fn main() {
+    let subtype: &(dyn for<'a> Fn(&'a i32) -> &'a i32) = &|x| x;
+    let supertype: &(dyn Fn(&'static i32) -> &'static i32) = subtype;
+}