about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs16
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs31
-rw-r--r--tests/ui/traits/new-solver/object-soundness-requires-generalization.rs20
3 files changed, 44 insertions, 23 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index ab90db6ff58..b1c9d7f3d19 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -153,16 +153,12 @@ pub(super) trait GoalKind<'tcx>:
             let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
                 bug!("expected object type in `consider_object_bound_candidate`");
             };
-            ecx.add_goals(
-                structural_traits::predicates_for_object_candidate(
-                    &ecx,
-                    goal.param_env,
-                    goal.predicate.trait_ref(tcx),
-                    bounds,
-                )
-                .into_iter()
-                .map(|pred| goal.with(tcx, pred)),
-            );
+            ecx.add_goals(structural_traits::predicates_for_object_candidate(
+                &ecx,
+                goal.param_env,
+                goal.predicate.trait_ref(tcx),
+                bounds,
+            ));
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index a2db35e069e..45dbd4b9269 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -3,6 +3,7 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id::DefId, Movability, Mutability};
 use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
@@ -345,7 +346,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
     param_env: ty::ParamEnv<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
     object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-) -> Vec<ty::Clause<'tcx>> {
+) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> {
     let tcx = ecx.tcx();
     let mut requirements = vec![];
     requirements.extend(
@@ -376,17 +377,22 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
         }
     }
 
-    requirements.fold_with(&mut ReplaceProjectionWith {
-        ecx,
-        param_env,
-        mapping: replace_projection_with,
-    })
+    let mut folder =
+        ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] };
+    let folded_requirements = requirements.fold_with(&mut folder);
+
+    folder
+        .nested
+        .into_iter()
+        .chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause)))
+        .collect()
 }
 
 struct ReplaceProjectionWith<'a, 'tcx> {
     ecx: &'a EvalCtxt<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>,
+    nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
 
 impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
@@ -402,13 +408,12 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
             // but the where clauses we instantiated are not. We can solve this by instantiating
             // the binder at the usage site.
             let proj = self.ecx.instantiate_binder_with_infer(*replacement);
-            // FIXME: Technically this folder could be fallible?
-            let nested = self
-                .ecx
-                .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
-                .expect("expected to be able to unify goal projection with dyn's projection");
-            // FIXME: Technically we could register these too..
-            assert!(nested.is_empty(), "did not expect unification to have any nested goals");
+            // FIXME: Technically this equate could be fallible...
+            self.nested.extend(
+                self.ecx
+                    .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
+                    .expect("expected to be able to unify goal projection with dyn's projection"),
+            );
             proj.term.ty().unwrap()
         } else {
             ty.super_fold_with(self)
diff --git a/tests/ui/traits/new-solver/object-soundness-requires-generalization.rs b/tests/ui/traits/new-solver/object-soundness-requires-generalization.rs
new file mode 100644
index 00000000000..d02dada72c9
--- /dev/null
+++ b/tests/ui/traits/new-solver/object-soundness-requires-generalization.rs
@@ -0,0 +1,20 @@
+// compile-flags: -Ztrait-solver=next
+// ignore-test
+
+trait Trait {
+    type Gat<'lt>;
+}
+impl Trait for u8 {
+    type Gat<'lt> = u8;
+}
+
+fn test<T: Trait, F: FnOnce(<T as Trait>::Gat<'_>) -> S + ?Sized, S>() {}
+
+fn main() {
+    // Proving `dyn FnOnce: FnOnce` requires making sure that all of the supertraits
+    // of the trait and associated type bounds hold. We check this in
+    // `predicates_for_object_candidate`, and eagerly replace projections using equality
+    // which may generalize a type and emit a nested AliasRelate goal. Make sure that
+    // we don't ICE in that case, and bubble that goal up to the caller.
+    test::<u8, dyn FnOnce(<u8 as Trait>::Gat<'_>) + 'static, _>();
+}