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
@@ -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 (
@@ -213,14 +219,14 @@ fn fulfillment_error_for_no_solution<'tcx>(
213219 }
214220 ty:: PredicateKind :: Subtype ( pred) => {
215221 let ( a, b) = infcx. enter_forall_and_leak_universe (
216- obligation . predicate . kind ( ) . rebind ( ( pred. a , pred. b ) ) ,
222+ root_obligation . predicate . kind ( ) . rebind ( ( pred. a , pred. b ) ) ,
217223 ) ;
218224 let expected_found = ExpectedFound :: new ( true , a, b) ;
219225 FulfillmentErrorCode :: SubtypeError ( expected_found, TypeError :: Sorts ( expected_found) )
220226 }
221227 ty:: PredicateKind :: Coerce ( pred) => {
222228 let ( a, b) = infcx. enter_forall_and_leak_universe (
223- obligation . predicate . kind ( ) . rebind ( ( pred. a , pred. b ) ) ,
229+ root_obligation . predicate . kind ( ) . rebind ( ( pred. a , pred. b ) ) ,
224230 ) ;
225231 let expected_found = ExpectedFound :: new ( false , a, b) ;
226232 FulfillmentErrorCode :: SubtypeError ( expected_found, TypeError :: Sorts ( expected_found) )
@@ -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,135 @@ 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+ struct BestObligation < ' tcx > {
276+ obligation : PredicateObligation < ' tcx > ,
277+ }
278+
279+ impl < ' tcx > BestObligation < ' tcx > {
280+ fn with_derived_obligation (
281+ & mut self ,
282+ derive_obligation : impl FnOnce ( & mut Self ) -> PredicateObligation < ' tcx > ,
283+ and_then : impl FnOnce ( & mut Self ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result ,
284+ ) -> <Self as ProofTreeVisitor < ' tcx > >:: Result {
285+ let derived_obligation = derive_obligation ( self ) ;
286+ let old_obligation = std:: mem:: replace ( & mut self . obligation , derived_obligation) ;
287+ let res = and_then ( self ) ;
288+ self . obligation = old_obligation;
289+ res
290+ }
291+ }
292+
293+ impl < ' tcx > ProofTreeVisitor < ' tcx > for BestObligation < ' tcx > {
294+ type Result = ControlFlow < PredicateObligation < ' tcx > > ;
295+
296+ fn span ( & self ) -> rustc_span:: Span {
297+ self . obligation . cause . span
298+ }
299+
300+ fn visit_goal ( & mut self , goal : & super :: inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
301+ let candidates = goal. candidates ( ) ;
302+ // FIXME: Throw out candidates that have no failing WC and >1 failing misc goal.
303+
304+ // HACK:
305+ if self . obligation . recursion_depth > 3 {
306+ return ControlFlow :: Break ( self . obligation . clone ( ) ) ;
307+ }
308+
309+ let [ candidate] = candidates. as_slice ( ) else {
310+ return ControlFlow :: Break ( self . obligation . clone ( ) ) ;
311+ } ;
312+
313+ // FIXME: Could we extract a trait ref from a projection here too?
314+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
315+ // for normalizes-to.
316+ let Some ( parent_trait_pred) = goal. goal ( ) . predicate . to_opt_poly_trait_pred ( ) else {
317+ return ControlFlow :: Break ( self . obligation . clone ( ) ) ;
318+ } ;
319+
320+ let tcx = goal. infcx ( ) . tcx ;
321+ let mut impl_where_bound_count = 0 ;
322+ for nested_goal in candidate. instantiate_nested_goals ( self . span ( ) ) {
323+ if matches ! ( nested_goal. source( ) , GoalSource :: ImplWhereBound ) {
324+ impl_where_bound_count += 1 ;
325+ } else {
326+ continue ;
327+ }
328+
329+ // Skip nested goals that hold.
330+ if matches ! ( nested_goal. result( ) , Ok ( Certainty :: Yes ) ) {
331+ continue ;
332+ }
333+
334+ self . with_derived_obligation (
335+ |self_| {
336+ let mut cause = self_. obligation . cause . clone ( ) ;
337+ cause = match candidate. kind ( ) {
338+ ProbeKind :: TraitCandidate {
339+ source : CandidateSource :: Impl ( impl_def_id) ,
340+ result : _,
341+ } => {
342+ let idx = impl_where_bound_count - 1 ;
343+ if let Some ( ( _, span) ) = tcx
344+ . predicates_of ( impl_def_id)
345+ . instantiate_identity ( tcx)
346+ . iter ( )
347+ . nth ( idx)
348+ {
349+ cause. derived_cause ( parent_trait_pred, |derived| {
350+ traits:: ImplDerivedObligation ( Box :: new (
351+ traits:: ImplDerivedObligationCause {
352+ derived,
353+ impl_or_alias_def_id : impl_def_id,
354+ impl_def_predicate_index : Some ( idx) ,
355+ span,
356+ } ,
357+ ) )
358+ } )
359+ } else {
360+ cause
361+ }
362+ }
363+ ProbeKind :: TraitCandidate {
364+ source : CandidateSource :: BuiltinImpl ( ..) ,
365+ result : _,
366+ } => {
367+ cause. derived_cause ( parent_trait_pred, traits:: BuiltinDerivedObligation )
368+ }
369+ _ => cause,
370+ } ;
371+
372+ Obligation {
373+ cause,
374+ param_env : nested_goal. goal ( ) . param_env ,
375+ predicate : nested_goal. goal ( ) . predicate ,
376+ recursion_depth : self_. obligation . recursion_depth + 1 ,
377+ }
378+ } ,
379+ |self_| self_. visit_goal ( & nested_goal) ,
380+ ) ?;
381+ }
382+
383+ ControlFlow :: Break ( self . obligation . clone ( ) )
384+ }
385+ }
386+
387+ fn find_best_leaf_obligation < ' tcx > (
388+ infcx : & InferCtxt < ' tcx > ,
389+ obligation : & PredicateObligation < ' tcx > ,
390+ ) -> PredicateObligation < ' tcx > {
391+ let obligation = infcx. resolve_vars_if_possible ( obligation. clone ( ) ) ;
392+ infcx
393+ . visit_proof_tree (
394+ obligation. clone ( ) . into ( ) ,
395+ & mut BestObligation { obligation : obligation. clone ( ) } ,
396+ )
397+ . break_value ( )
398+ . unwrap_or ( obligation)
262399}
0 commit comments