about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs43
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs94
-rw-r--r--tests/ui/unsized/issue-75899.rs2
4 files changed, 94 insertions, 54 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 3750b3750bf..44c50b086bd 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -281,23 +281,27 @@ pub(super) trait GoalKind<'tcx>:
     ) -> QueryResult<'tcx>;
 
     /// Consider (possibly several) candidates to upcast or unsize a type to another
-    /// type.
-    ///
-    /// The most common forms of unsizing are array to slice, and concrete (Sized)
-    /// type into a `dyn Trait`. ADTs and Tuples can also have their final field
-    /// unsized if it's generic.
-    ///
-    /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
-    /// if `Trait2` is a (transitive) supertrait of `Trait2`.
+    /// type, excluding the coercion of a sized type into a `dyn Trait`.
     ///
     /// We return the `BuiltinImplSource` for each candidate as it is needed
     /// for unsize coercion in hir typeck and because it is difficult to
     /// otherwise recompute this for codegen. This is a bit of a mess but the
     /// easiest way to maintain the existing behavior for now.
-    fn consider_builtin_unsize_candidates(
+    fn consider_structural_builtin_unsize_candidates(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
+
+    /// Consider the `Unsize` candidate corresponding to coercing a sized type
+    /// into a `dyn Trait`.
+    ///
+    /// This is computed separately from the rest of the `Unsize` candidates
+    /// since it is only done once per self type, and not once per
+    /// *normalization step* (in `assemble_candidates_via_self_ty`).
+    fn consider_unsize_to_dyn_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
 }
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -312,6 +316,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
 
+        self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
+
         self.assemble_blanket_impl_candidates(goal, &mut candidates);
 
         self.assemble_param_env_candidates(goal, &mut candidates);
@@ -530,6 +536,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) {
+            match G::consider_unsize_to_dyn_candidate(self, goal) {
+                Ok(result) => candidates.push(Candidate {
+                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                    result,
+                }),
+                Err(NoSolution) => (),
+            }
+        }
+    }
+
     fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
@@ -610,7 +633,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // There may be multiple unsize candidates for a trait with several supertraits:
         // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
         if lang_items.unsize_trait() == Some(trait_def_id) {
-            for (result, source) in G::consider_builtin_unsize_candidates(self, goal) {
+            for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
                 candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
             }
         }
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index e1980f4d7bb..6117f51a378 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -497,7 +497,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         )
     }
 
-    fn consider_builtin_unsize_candidates(
+    fn consider_unsize_to_dyn_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Unsize` does not have an associated type: {:?}", goal)
+    }
+
+    fn consider_structural_builtin_unsize_candidates(
         _ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index ee6f1686b82..639400f0927 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -423,7 +423,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.evaluate_added_goals_and_make_canonical_response(certainty)
     }
 
-    fn consider_builtin_unsize_candidates(
+    fn consider_unsize_to_dyn_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
+            let a_ty = goal.predicate.self_ty();
+            // We need to normalize the b_ty since it's destructured as a `dyn Trait`.
+            let Some(b_ty) =
+                ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
+            else {
+                return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+            };
+
+            let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
+                return Err(NoSolution);
+            };
+
+            let tcx = ecx.tcx();
+
+            // Can only unsize to an object-safe trait.
+            if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
+                return Err(NoSolution);
+            }
+
+            // Check that the type implements all of the predicates of the trait object.
+            // (i.e. the principal, all of the associated types match, and any auto traits)
+            ecx.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
+
+            // The type must be `Sized` to be unsized.
+            if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
+                ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
+            } else {
+                return Err(NoSolution);
+            }
+
+            // The type must outlive the lifetime of the `dyn` we're unsizing into.
+            ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    /// ```ignore (builtin impl example)
+    /// trait Trait {
+    ///     fn foo(&self);
+    /// }
+    /// // results in the following builtin impl
+    /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
+    /// ```
+    fn consider_structural_builtin_unsize_candidates(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
@@ -468,11 +516,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                     goal, a_data, a_region, b_data, b_region,
                 ),
 
-                // `T` -> `dyn Trait` unsizing
-                (_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single(
-                    ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region),
-                    BuiltinImplSource::Misc,
-                ),
+                // `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
+                (_, &ty::Dynamic(..)) => vec![],
 
                 // `[T; N]` -> `[T]` unsizing
                 (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
@@ -572,43 +617,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         responses
     }
 
-    /// ```ignore (builtin impl example)
-    /// trait Trait {
-    ///     fn foo(&self);
-    /// }
-    /// // results in the following builtin impl
-    /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
-    /// ```
-    fn consider_builtin_unsize_to_dyn(
-        &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        b_region: ty::Region<'tcx>,
-    ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
-        let Goal { predicate: (a_ty, _b_ty), .. } = goal;
-
-        // Can only unsize to an object-safe trait
-        if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
-            return Err(NoSolution);
-        }
-
-        // Check that the type implements all of the predicates of the trait object.
-        // (i.e. the principal, all of the associated types match, and any auto traits)
-        self.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
-
-        // The type must be `Sized` to be unsized.
-        if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
-            self.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
-        } else {
-            return Err(NoSolution);
-        }
-
-        // The type must outlive the lifetime of the `dyn` we're unsizing into.
-        self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
-        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-    }
-
     fn consider_builtin_upcast_to_principal(
         &mut self,
         goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
diff --git a/tests/ui/unsized/issue-75899.rs b/tests/ui/unsized/issue-75899.rs
index abff17e11b5..71943103291 100644
--- a/tests/ui/unsized/issue-75899.rs
+++ b/tests/ui/unsized/issue-75899.rs
@@ -1,3 +1,5 @@
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
 // check-pass
 
 trait Trait {}