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 ,
7- PredicateObligation , SelectionError , TraitEngine ,
9+ self , FulfillmentError , FulfillmentErrorCode , MismatchedProjectionTypes , Obligation ,
10+ ObligationCause , PredicateObligation , SelectionError , TraitEngine ,
811} ;
9- use rustc_middle:: ty;
1012use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
13+ use rustc_middle:: ty:: { self , TyCtxt } ;
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
@@ -192,8 +196,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
192196
193197fn fulfillment_error_for_no_solution < ' tcx > (
194198 infcx : & InferCtxt < ' tcx > ,
195- obligation : PredicateObligation < ' tcx > ,
199+ root_obligation : PredicateObligation < ' tcx > ,
196200) -> FulfillmentError < ' tcx > {
201+ let obligation = find_best_leaf_obligation ( infcx, & root_obligation) ;
202+
197203 let code = match obligation. predicate . kind ( ) . skip_binder ( ) {
198204 ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Projection ( _) ) => {
199205 FulfillmentErrorCode :: ProjectionError (
@@ -234,7 +240,8 @@ fn fulfillment_error_for_no_solution<'tcx>(
234240 bug ! ( "unexpected goal: {obligation:?}" )
235241 }
236242 } ;
237- FulfillmentError { root_obligation : obligation. clone ( ) , code, obligation }
243+
244+ FulfillmentError { obligation, code, root_obligation }
238245}
239246
240247fn fulfillment_error_for_stalled < ' tcx > (
@@ -258,5 +265,136 @@ fn fulfillment_error_for_stalled<'tcx>(
258265 }
259266 } ) ;
260267
261- FulfillmentError { obligation : obligation. clone ( ) , code, root_obligation : obligation }
268+ FulfillmentError {
269+ obligation : find_best_leaf_obligation ( infcx, & obligation) ,
270+ code,
271+ root_obligation : obligation,
272+ }
273+ }
274+
275+ fn find_best_leaf_obligation < ' tcx > (
276+ infcx : & InferCtxt < ' tcx > ,
277+ obligation : & PredicateObligation < ' tcx > ,
278+ ) -> PredicateObligation < ' tcx > {
279+ let obligation = infcx. resolve_vars_if_possible ( obligation. clone ( ) ) ;
280+ infcx
281+ . visit_proof_tree (
282+ obligation. clone ( ) . into ( ) ,
283+ & mut BestObligation { obligation : obligation. clone ( ) } ,
284+ )
285+ . break_value ( )
286+ . unwrap_or ( obligation)
287+ }
288+
289+ struct BestObligation < ' tcx > {
290+ obligation : PredicateObligation < ' tcx > ,
291+ }
292+
293+ impl < ' tcx > BestObligation < ' tcx > {
294+ fn with_derived_obligation (
295+ & mut self ,
296+ derived_obligation : PredicateObligation < ' tcx > ,
297+ and_then : impl FnOnce ( & mut Self ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result ,
298+ ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result {
299+ let old_obligation = std:: mem:: replace ( & mut self . obligation , derived_obligation) ;
300+ let res = and_then ( self ) ;
301+ self . obligation = old_obligation;
302+ res
303+ }
304+ }
305+
306+ impl < ' tcx > ProofTreeVisitor < ' tcx > for BestObligation < ' tcx > {
307+ type Result = ControlFlow < PredicateObligation < ' tcx > > ;
308+
309+ fn span ( & self ) -> rustc_span:: Span {
310+ self . obligation . cause . span
311+ }
312+
313+ fn visit_goal ( & mut self , goal : & super :: inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
314+ // FIXME: Throw out candidates that have no failing WC and >0 failing misc goal.
315+ // This most likely means that the goal just didn't unify at all, e.g. a param
316+ // candidate with an alias in it.
317+ let candidates = goal. candidates ( ) ;
318+
319+ let [ candidate] = candidates. as_slice ( ) else {
320+ return ControlFlow :: Break ( self . obligation . clone ( ) ) ;
321+ } ;
322+
323+ // FIXME: Could we extract a trait ref from a projection here too?
324+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
325+ // for normalizes-to.
326+ let Some ( parent_trait_pred) = goal. goal ( ) . predicate . to_opt_poly_trait_pred ( ) else {
327+ return ControlFlow :: Break ( self . obligation . clone ( ) ) ;
328+ } ;
329+
330+ let tcx = goal. infcx ( ) . tcx ;
331+ let mut impl_where_bound_count = 0 ;
332+ for nested_goal in candidate. instantiate_nested_goals ( self . span ( ) ) {
333+ let obligation;
334+ match nested_goal. source ( ) {
335+ GoalSource :: Misc => {
336+ continue ;
337+ }
338+ GoalSource :: ImplWhereBound => {
339+ obligation = Obligation {
340+ cause : derive_cause (
341+ tcx,
342+ candidate. kind ( ) ,
343+ self . obligation . cause . clone ( ) ,
344+ impl_where_bound_count,
345+ parent_trait_pred,
346+ ) ,
347+ param_env : nested_goal. goal ( ) . param_env ,
348+ predicate : nested_goal. goal ( ) . predicate ,
349+ recursion_depth : self . obligation . recursion_depth + 1 ,
350+ } ;
351+ impl_where_bound_count += 1 ;
352+ }
353+ GoalSource :: InstantiateHigherRanked => {
354+ obligation = self . obligation . clone ( ) ;
355+ }
356+ }
357+
358+ // Skip nested goals that hold.
359+ //FIXME: We should change the max allowed certainty based on if we're
360+ // visiting an ambiguity or error obligation.
361+ if matches ! ( nested_goal. result( ) , Ok ( Certainty :: Yes ) ) {
362+ continue ;
363+ }
364+
365+ self . with_derived_obligation ( obligation, |this| nested_goal. visit_with ( this) ) ?;
366+ }
367+
368+ ControlFlow :: Break ( self . obligation . clone ( ) )
369+ }
370+ }
371+
372+ fn derive_cause < ' tcx > (
373+ tcx : TyCtxt < ' tcx > ,
374+ candidate_kind : ProbeKind < ' tcx > ,
375+ mut cause : ObligationCause < ' tcx > ,
376+ idx : usize ,
377+ parent_trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
378+ ) -> ObligationCause < ' tcx > {
379+ match candidate_kind {
380+ ProbeKind :: TraitCandidate { source : CandidateSource :: Impl ( impl_def_id) , result : _ } => {
381+ if let Some ( ( _, span) ) =
382+ tcx. predicates_of ( impl_def_id) . instantiate_identity ( tcx) . iter ( ) . nth ( idx)
383+ {
384+ cause = cause. derived_cause ( parent_trait_pred, |derived| {
385+ traits:: ImplDerivedObligation ( Box :: new ( traits:: ImplDerivedObligationCause {
386+ derived,
387+ impl_or_alias_def_id : impl_def_id,
388+ impl_def_predicate_index : Some ( idx) ,
389+ span,
390+ } ) )
391+ } )
392+ }
393+ }
394+ ProbeKind :: TraitCandidate { source : CandidateSource :: BuiltinImpl ( ..) , result : _ } => {
395+ cause = cause. derived_cause ( parent_trait_pred, traits:: BuiltinDerivedObligation ) ;
396+ }
397+ _ => { }
398+ } ;
399+ cause
262400}
0 commit comments