@@ -164,6 +164,7 @@ where
164
164
ecx : & mut EvalCtxt < ' _ , D > ,
165
165
goal : Goal < I , Self > ,
166
166
) -> Result < Candidate < I > , NoSolution > {
167
+ let cx = ecx. cx ( ) ;
167
168
if goal. predicate . polarity != ty:: PredicatePolarity :: Positive {
168
169
return Err ( NoSolution ) ;
169
170
}
@@ -174,20 +175,37 @@ where
174
175
175
176
// Only consider auto impls of unsafe traits when there are no unsafe
176
177
// fields.
177
- if ecx . cx ( ) . trait_is_unsafe ( goal. predicate . def_id ( ) )
178
+ if cx . trait_is_unsafe ( goal. predicate . def_id ( ) )
178
179
&& goal. predicate . self_ty ( ) . has_unsafe_fields ( )
179
180
{
180
181
return Err ( NoSolution ) ;
181
182
}
182
183
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.
189
199
if let ty:: Alias ( ty:: Opaque , opaque_ty) = goal. predicate . self_ty ( ) . kind ( ) {
190
200
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
+ }
191
209
}
192
210
193
211
ecx. probe_and_evaluate_goal_for_constituent_tys (
@@ -1238,10 +1256,11 @@ where
1238
1256
D : SolverDelegate < Interner = I > ,
1239
1257
I : Interner ,
1240
1258
{
1259
+ #[ instrument( level = "debug" , skip( self , goal) , ret) ]
1241
1260
pub ( super ) fn merge_trait_candidates (
1242
1261
& mut self ,
1243
1262
goal : Goal < I , TraitPredicate < I > > ,
1244
- candidates : Vec < Candidate < I > > ,
1263
+ mut candidates : Vec < Candidate < I > > ,
1245
1264
) -> Result < ( CanonicalResponse < I > , Option < TraitGoalProvenVia > ) , NoSolution > {
1246
1265
if let TypingMode :: Coherence = self . typing_mode ( ) {
1247
1266
let all_candidates: Vec < _ > = candidates. into_iter ( ) . map ( |c| c. result ) . collect ( ) ;
@@ -1323,13 +1342,16 @@ where
1323
1342
1324
1343
// If there are *only* global where bounds, then make sure to return that this
1325
1344
// 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.
1327
1347
let proven_via =
1328
1348
if candidates. iter ( ) . all ( |c| matches ! ( c. source, CandidateSource :: ParamEnv ( _) ) ) {
1329
1349
TraitGoalProvenVia :: ParamEnv
1330
1350
} else {
1351
+ candidates. retain ( |c| !matches ! ( c. source, CandidateSource :: ParamEnv ( _) ) ) ;
1331
1352
TraitGoalProvenVia :: Misc
1332
1353
} ;
1354
+
1333
1355
let all_candidates: Vec < _ > = candidates. into_iter ( ) . map ( |c| c. result ) . collect ( ) ;
1334
1356
if let Some ( response) = self . try_merge_responses ( & all_candidates) {
1335
1357
Ok ( ( response, Some ( proven_via) ) )
0 commit comments