1
1
use std:: mem;
2
+ use std:: ops:: ControlFlow ;
2
3
3
4
use rustc_infer:: infer:: InferCtxt ;
4
- use rustc_infer:: traits:: solve:: MaybeCause ;
5
+ use rustc_infer:: traits:: query:: NoSolution ;
6
+ use rustc_infer:: traits:: solve:: inspect:: ProbeKind ;
7
+ use rustc_infer:: traits:: solve:: { CandidateSource , GoalSource , MaybeCause } ;
5
8
use rustc_infer:: traits:: {
6
- query :: NoSolution , FulfillmentError , FulfillmentErrorCode , MismatchedProjectionTypes ,
9
+ self , FulfillmentError , FulfillmentErrorCode , MismatchedProjectionTypes , Obligation ,
7
10
PredicateObligation , SelectionError , TraitEngine ,
8
11
} ;
9
12
use rustc_middle:: ty;
10
13
use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
11
14
12
15
use super :: eval_ctxt:: GenerateProofTree ;
16
+ use super :: inspect:: { ProofTreeInferCtxtExt , ProofTreeVisitor } ;
13
17
use super :: { Certainty , InferCtxtEvalExt } ;
14
18
15
19
/// A trait engine using the new trait solver.
@@ -133,9 +137,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
133
137
. collect ( ) ;
134
138
135
139
errors. extend ( self . obligations . overflowed . drain ( ..) . map ( |obligation| FulfillmentError {
136
- root_obligation : obligation . clone ( ) ,
140
+ obligation : find_best_leaf_obligation ( infcx , & obligation ) ,
137
141
code : FulfillmentErrorCode :: Ambiguity { overflow : Some ( true ) } ,
138
- obligation,
142
+ root_obligation : obligation,
139
143
} ) ) ;
140
144
141
145
errors
@@ -234,7 +238,12 @@ fn fulfillment_error_for_no_solution<'tcx>(
234
238
bug ! ( "unexpected goal: {obligation:?}" )
235
239
}
236
240
} ;
237
- FulfillmentError { root_obligation : obligation. clone ( ) , code, obligation }
241
+
242
+ FulfillmentError {
243
+ obligation : find_best_leaf_obligation ( infcx, & obligation) ,
244
+ code,
245
+ root_obligation : obligation,
246
+ }
238
247
}
239
248
240
249
fn fulfillment_error_for_stalled < ' tcx > (
@@ -258,5 +267,149 @@ fn fulfillment_error_for_stalled<'tcx>(
258
267
}
259
268
} ) ;
260
269
261
- FulfillmentError { obligation : obligation. clone ( ) , code, root_obligation : obligation }
270
+ FulfillmentError {
271
+ obligation : find_best_leaf_obligation ( infcx, & obligation) ,
272
+ code,
273
+ root_obligation : obligation,
274
+ }
275
+ }
276
+
277
+ struct BestObligation < ' tcx > {
278
+ parent_kind : Option < ProbeKind < ' tcx > > ,
279
+ parent_obligation : PredicateObligation < ' tcx > ,
280
+ impl_where_bound_count : usize ,
281
+ }
282
+
283
+ impl < ' tcx > BestObligation < ' tcx > {
284
+ fn with_derived_obligation (
285
+ & mut self ,
286
+ new_kind : ProbeKind < ' tcx > ,
287
+ derive_obligation : impl FnOnce ( & mut Self , ProbeKind < ' tcx > ) -> PredicateObligation < ' tcx > ,
288
+ and_then : impl FnOnce ( & mut Self ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result ,
289
+ ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result {
290
+ let mut old_obligation = None ;
291
+ let old_kind = self . parent_kind . replace ( new_kind) ;
292
+ if let Some ( old_kind) = old_kind {
293
+ let new_obligation = derive_obligation ( self , old_kind) ;
294
+ old_obligation = Some ( std:: mem:: replace ( & mut self . parent_obligation , new_obligation) ) ;
295
+ }
296
+ let old_impl_where_bound_count = std:: mem:: replace ( & mut self . impl_where_bound_count , 0 ) ;
297
+
298
+ let res = and_then ( self ) ;
299
+
300
+ if let Some ( old_obligation) = old_obligation {
301
+ self . parent_obligation = old_obligation;
302
+ }
303
+ self . parent_kind = old_kind;
304
+ self . impl_where_bound_count = old_impl_where_bound_count;
305
+
306
+ res
307
+ }
308
+ }
309
+
310
+ impl < ' tcx > ProofTreeVisitor < ' tcx > for BestObligation < ' tcx > {
311
+ type Result = ControlFlow < PredicateObligation < ' tcx > > ;
312
+
313
+ fn span ( & self ) -> rustc_span:: Span {
314
+ self . parent_obligation . cause . span
315
+ }
316
+
317
+ fn visit_goal ( & mut self , goal : & super :: inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
318
+ if matches ! ( goal. source( ) , GoalSource :: ImplWhereBound ) {
319
+ self . impl_where_bound_count += 1 ;
320
+ }
321
+
322
+ if goal. result ( ) . is_ok ( ) {
323
+ return ControlFlow :: Continue ( ( ) ) ;
324
+ }
325
+
326
+ let candidates = goal. candidates ( ) ;
327
+ // FIXME: We should try to throw out the candidates that are definitely
328
+ // not worthwhile, such as param-env and impl candidates whose headers
329
+ // won't even unify. We already do this with deep-reject for impls, but
330
+ // we shouldn't rely on this for diagnostic correctness.
331
+ let [ candidate] = candidates. as_slice ( ) else {
332
+ return ControlFlow :: Break ( self . parent_obligation . clone ( ) ) ;
333
+ } ;
334
+
335
+ // FIXME: Could we extract a trait ref from a projection here too?
336
+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
337
+ // for normalizes-to.
338
+ let Some ( parent_trait_pred) = self . parent_obligation . predicate . to_opt_poly_trait_pred ( )
339
+ else {
340
+ return ControlFlow :: Break ( self . parent_obligation . clone ( ) ) ;
341
+ } ;
342
+
343
+ self . with_derived_obligation (
344
+ candidate. kind ( ) ,
345
+ |self_, parent_kind| {
346
+ let mut cause = self_. parent_obligation . cause . clone ( ) ;
347
+ cause = match ( parent_kind, goal. source ( ) ) {
348
+ (
349
+ ProbeKind :: TraitCandidate {
350
+ source : CandidateSource :: Impl ( impl_def_id) ,
351
+ result : _,
352
+ } ,
353
+ GoalSource :: ImplWhereBound ,
354
+ ) => {
355
+ let idx = self_. impl_where_bound_count - 1 ;
356
+ if let Some ( ( _, span) ) = goal
357
+ . infcx ( )
358
+ . tcx
359
+ . predicates_of ( impl_def_id)
360
+ . instantiate_identity ( goal. infcx ( ) . tcx )
361
+ . iter ( )
362
+ . nth ( idx)
363
+ {
364
+ cause. derived_cause ( parent_trait_pred, |derived| {
365
+ traits:: ImplDerivedObligation ( Box :: new (
366
+ traits:: ImplDerivedObligationCause {
367
+ derived,
368
+ impl_or_alias_def_id : impl_def_id,
369
+ impl_def_predicate_index : Some ( idx) ,
370
+ span,
371
+ } ,
372
+ ) )
373
+ } )
374
+ } else {
375
+ cause
376
+ }
377
+ }
378
+ ( _, GoalSource :: ImplWhereBound ) => {
379
+ cause. derived_cause ( parent_trait_pred, traits:: BuiltinDerivedObligation )
380
+ }
381
+ _ => cause,
382
+ } ;
383
+
384
+ Obligation {
385
+ cause,
386
+ param_env : goal. goal ( ) . param_env ,
387
+ predicate : goal. goal ( ) . predicate ,
388
+ recursion_depth : self_. parent_obligation . recursion_depth + 1 ,
389
+ }
390
+ } ,
391
+ |self_| {
392
+ candidate. visit_nested_no_probe ( self_) ?;
393
+ ControlFlow :: Break ( self_. parent_obligation . clone ( ) )
394
+ } ,
395
+ )
396
+ }
397
+ }
398
+
399
+ fn find_best_leaf_obligation < ' tcx > (
400
+ infcx : & InferCtxt < ' tcx > ,
401
+ obligation : & PredicateObligation < ' tcx > ,
402
+ ) -> PredicateObligation < ' tcx > {
403
+ let obligation = infcx. resolve_vars_if_possible ( obligation. clone ( ) ) ;
404
+ infcx
405
+ . visit_proof_tree (
406
+ obligation. clone ( ) . into ( ) ,
407
+ & mut BestObligation {
408
+ parent_kind : None ,
409
+ parent_obligation : obligation. clone ( ) ,
410
+ impl_where_bound_count : 0 ,
411
+ } ,
412
+ )
413
+ . break_value ( )
414
+ . unwrap_or ( obligation)
262
415
}
0 commit comments