@@ -164,6 +164,7 @@ where
164164 ecx : & mut EvalCtxt < ' _ , D > ,
165165 goal : Goal < I , Self > ,
166166 ) -> Result < Candidate < I > , NoSolution > {
167+ let cx = ecx. cx ( ) ;
167168 if goal. predicate . polarity != ty:: PredicatePolarity :: Positive {
168169 return Err ( NoSolution ) ;
169170 }
@@ -174,20 +175,37 @@ where
174175
175176 // Only consider auto impls of unsafe traits when there are no unsafe
176177 // fields.
177- if ecx . cx ( ) . trait_is_unsafe ( goal. predicate . def_id ( ) )
178+ if cx . trait_is_unsafe ( goal. predicate . def_id ( ) )
178179 && goal. predicate . self_ty ( ) . has_unsafe_fields ( )
179180 {
180181 return Err ( NoSolution ) ;
181182 }
182183
183- // We only look into opaque types during analysis for opaque types
184- // outside of their defining scope. Doing so for opaques in the
185- // defining scope may require calling `typeck` on the same item we're
186- // currently type checking, which will result in a fatal cycle that
187- // ideally we want to avoid, since we can make progress on this goal
188- // via an alias bound or a locally-inferred hidden type instead.
184+ // We leak the implemented auto traits of opaques outside of their defining scope.
185+ // This depends on `typeck` of the defining scope of that opaque, which may result in
186+ // fatal query cycles.
187+ //
188+ // We only get to this point if we're outside of the defining scope as we'd otherwise
189+ // be able to normalize the opaque type. We may also cycle in case `typeck` of a defining
190+ // scope relies on the current context, e.g. either because it also leaks auto trait
191+ // bounds of opaques defined in the current context or by evaluating the current item.
192+ //
193+ // To avoid this we don't try to leak auto trait bounds if they can also be proven via
194+ // item bounds of the opaque. These bounds are always applicable as auto traits must not
195+ // have any generic parameters. They would also get preferred over the impl candidate
196+ // when merging candidates anyways.
197+ //
198+ // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
189199 if let ty:: Alias ( ty:: Opaque , opaque_ty) = goal. predicate . self_ty ( ) . kind ( ) {
190200 debug_assert ! ( ecx. opaque_type_is_rigid( opaque_ty. def_id) ) ;
201+ for item_bound in cx. item_self_bounds ( opaque_ty. def_id ) . skip_binder ( ) {
202+ if item_bound
203+ . as_trait_clause ( )
204+ . is_some_and ( |b| b. def_id ( ) == goal. predicate . def_id ( ) )
205+ {
206+ return Err ( NoSolution ) ;
207+ }
208+ }
191209 }
192210
193211 ecx. probe_and_evaluate_goal_for_constituent_tys (
@@ -1238,10 +1256,11 @@ where
12381256 D : SolverDelegate < Interner = I > ,
12391257 I : Interner ,
12401258{
1259+ #[ instrument( level = "debug" , skip( self , goal) , ret) ]
12411260 pub ( super ) fn merge_trait_candidates (
12421261 & mut self ,
12431262 goal : Goal < I , TraitPredicate < I > > ,
1244- candidates : Vec < Candidate < I > > ,
1263+ mut candidates : Vec < Candidate < I > > ,
12451264 ) -> Result < ( CanonicalResponse < I > , Option < TraitGoalProvenVia > ) , NoSolution > {
12461265 if let TypingMode :: Coherence = self . typing_mode ( ) {
12471266 let all_candidates: Vec < _ > = candidates. into_iter ( ) . map ( |c| c. result ) . collect ( ) ;
@@ -1323,13 +1342,16 @@ where
13231342
13241343 // If there are *only* global where bounds, then make sure to return that this
13251344 // is still reported as being proven-via the param-env so that rigid projections
1326- // operate correctly.
1345+ // operate correctly. Otherwise, drop all global where-bounds before merging the
1346+ // remaining candidates.
13271347 let proven_via =
13281348 if candidates. iter ( ) . all ( |c| matches ! ( c. source, CandidateSource :: ParamEnv ( _) ) ) {
13291349 TraitGoalProvenVia :: ParamEnv
13301350 } else {
1351+ candidates. retain ( |c| !matches ! ( c. source, CandidateSource :: ParamEnv ( _) ) ) ;
13311352 TraitGoalProvenVia :: Misc
13321353 } ;
1354+
13331355 let all_candidates: Vec < _ > = candidates. into_iter ( ) . map ( |c| c. result ) . collect ( ) ;
13341356 if let Some ( response) = self . try_merge_responses ( & all_candidates) {
13351357 Ok ( ( response, Some ( proven_via) ) )
0 commit comments