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
@@ -192,8 +196,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
192
196
193
197
fn fulfillment_error_for_no_solution < ' tcx > (
194
198
infcx : & InferCtxt < ' tcx > ,
195
- obligation : PredicateObligation < ' tcx > ,
199
+ root_obligation : PredicateObligation < ' tcx > ,
196
200
) -> FulfillmentError < ' tcx > {
201
+ let obligation = find_best_leaf_obligation ( infcx, & root_obligation) ;
202
+
197
203
let code = match obligation. predicate . kind ( ) . skip_binder ( ) {
198
204
ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Projection ( _) ) => {
199
205
FulfillmentErrorCode :: ProjectionError (
@@ -213,14 +219,14 @@ fn fulfillment_error_for_no_solution<'tcx>(
213
219
}
214
220
ty:: PredicateKind :: Subtype ( pred) => {
215
221
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 ) ) ,
217
223
) ;
218
224
let expected_found = ExpectedFound :: new ( true , a, b) ;
219
225
FulfillmentErrorCode :: SubtypeError ( expected_found, TypeError :: Sorts ( expected_found) )
220
226
}
221
227
ty:: PredicateKind :: Coerce ( pred) => {
222
228
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 ) ) ,
224
230
) ;
225
231
let expected_found = ExpectedFound :: new ( false , a, b) ;
226
232
FulfillmentErrorCode :: SubtypeError ( expected_found, TypeError :: Sorts ( expected_found) )
@@ -234,7 +240,8 @@ fn fulfillment_error_for_no_solution<'tcx>(
234
240
bug ! ( "unexpected goal: {obligation:?}" )
235
241
}
236
242
} ;
237
- FulfillmentError { root_obligation : obligation. clone ( ) , code, obligation }
243
+
244
+ FulfillmentError { obligation, code, root_obligation }
238
245
}
239
246
240
247
fn fulfillment_error_for_stalled < ' tcx > (
@@ -258,5 +265,135 @@ fn fulfillment_error_for_stalled<'tcx>(
258
265
}
259
266
} ) ;
260
267
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)
262
399
}
0 commit comments