11use std:: mem;
2+ use std:: ops:: ControlFlow ;
23
34use 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 } ;
58use rustc_infer:: traits:: {
6- query :: NoSolution , FulfillmentError , FulfillmentErrorCode , MismatchedProjectionTypes ,
9+ self , FulfillmentError , FulfillmentErrorCode , MismatchedProjectionTypes , Obligation ,
710 PredicateObligation , SelectionError , TraitEngine ,
811} ;
912use rustc_middle:: ty;
1013use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
1114
1215use super :: eval_ctxt:: GenerateProofTree ;
16+ use super :: inspect:: { ProofTreeInferCtxtExt , ProofTreeVisitor } ;
1317use super :: { Certainty , InferCtxtEvalExt } ;
1418
1519/// A trait engine using the new trait solver.
@@ -133,9 +137,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
133137 . collect ( ) ;
134138
135139 errors. extend ( self . obligations . overflowed . drain ( ..) . map ( |obligation| FulfillmentError {
136- root_obligation : obligation . clone ( ) ,
140+ obligation : find_best_leaf_obligation ( infcx , & obligation ) ,
137141 code : FulfillmentErrorCode :: Ambiguity { overflow : Some ( true ) } ,
138- obligation,
142+ root_obligation : obligation,
139143 } ) ) ;
140144
141145 errors
@@ -234,7 +238,12 @@ fn fulfillment_error_for_no_solution<'tcx>(
234238 bug ! ( "unexpected goal: {obligation:?}" )
235239 }
236240 } ;
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+ }
238247}
239248
240249fn fulfillment_error_for_stalled < ' tcx > (
@@ -258,5 +267,149 @@ fn fulfillment_error_for_stalled<'tcx>(
258267 }
259268 } ) ;
260269
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)
262415}
0 commit comments