about summary refs log tree commit diff
path: root/compiler/rustc_next_trait_solver/src
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-04-14 10:37:51 +0200
committerlcnr <rust@lcnr.de>2025-04-14 13:26:13 +0200
commitce9d8678ca9d25c603dfc655a5f955bee4a0a9fe (patch)
treeab6deefb36edb5e614fc1ff6fc2cb7681d2a007d /compiler/rustc_next_trait_solver/src
parentf836ae4e663b6e8938096b8559e094d18361be55 (diff)
downloadrust-ce9d8678ca9d25c603dfc655a5f955bee4a0a9fe.tar.gz
rust-ce9d8678ca9d25c603dfc655a5f955bee4a0a9fe.zip
do not leak auto traits in item bounds
Diffstat (limited to 'compiler/rustc_next_trait_solver/src')
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs32
1 files changed, 25 insertions, 7 deletions
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index d42c9980f46..3adfac3d429 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -164,6 +164,7 @@ where
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
     ) -> Result<Candidate<I>, NoSolution> {
+        let cx = ecx.cx();
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -174,20 +175,37 @@ where
 
         // Only consider auto impls of unsafe traits when there are no unsafe
         // fields.
-        if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
+        if cx.trait_is_unsafe(goal.predicate.def_id())
             && goal.predicate.self_ty().has_unsafe_fields()
         {
             return Err(NoSolution);
         }
 
-        // We only look into opaque types during analysis for opaque types
-        // outside of their defining scope. Doing so for opaques in the
-        // defining scope may require calling `typeck` on the same item we're
-        // currently type checking, which will result in a fatal cycle that
-        // ideally we want to avoid, since we can make progress on this goal
-        // via an alias bound or a locally-inferred hidden type instead.
+        // We leak the implemented auto traits of opaques outside of their defining scope.
+        // This depends on `typeck` of the defining scope of that opaque, which may result in
+        // fatal query cycles.
+        //
+        // We only get to this point if we're outside of the defining scope as we'd otherwise
+        // be able to normalize the opaque type. We may also cycle in case `typeck` of a defining
+        // scope relies on the current context, e.g. either because it also leaks auto trait
+        // bounds of opaques defined in the current context or by evaluating the current item.
+        //
+        // To avoid this we don't try to leak auto trait bounds if they can also be proven via
+        // item bounds of the opaque. These bounds are always applicable as auto traits must not
+        // have any generic parameters. They would also get preferred over the impl candidate
+        // when merging candidates anyways.
+        //
+        // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
         if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
             debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
+            for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() {
+                if item_bound
+                    .as_trait_clause()
+                    .is_some_and(|b| b.def_id() == goal.predicate.def_id())
+                {
+                    return Err(NoSolution);
+                }
+            }
         }
 
         ecx.probe_and_evaluate_goal_for_constituent_tys(