Skip to content

Commit 5de29df

Browse files
Use a proof tree visitor to refine the Obligation for error reporting
1 parent 8a422c7 commit 5de29df

32 files changed

+406
-69
lines changed

compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
453453
} else {
454454
self.infcx.enter_forall(kind, |kind| {
455455
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
456-
self.add_goal(GoalSource::Misc, goal);
456+
self.add_goal(GoalSource::ImplWhereBound, goal);
457457
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
458458
})
459459
}

compiler/rustc_trait_selection/src/solve/fulfill.rs

+146-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
use std::mem;
2+
use std::ops::ControlFlow;
23

34
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};
58
use rustc_infer::traits::{
6-
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
9+
self, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation,
710
PredicateObligation, SelectionError, TraitEngine,
811
};
912
use rustc_middle::ty;
1013
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1114

1215
use super::eval_ctxt::GenerateProofTree;
16+
use super::inspect::{ProofTreeInferCtxtExt, ProofTreeVisitor};
1317
use 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

193197
fn 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

240247
fn 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
}

tests/ui/coherence/occurs-check/opaques.next.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ error[E0282]: type annotations needed
1111
--> $DIR/opaques.rs:13:20
1212
|
1313
LL | pub fn cast<T>(x: Container<Alias<T>, T>) -> Container<T, T> {
14-
| ^ cannot infer type for struct `Container<T, T>`
14+
| ^ cannot infer type for associated type `<T as Trait<T>>::Assoc`
1515

1616
error: aborting due to 2 previous errors
1717

tests/ui/for/issue-20605.next.stderr

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ error[E0277]: `dyn Iterator<Item = &'a mut u8>` is not an iterator
22
--> $DIR/issue-20605.rs:6:17
33
|
44
LL | for item in *things { *item = 0 }
5-
| ^^^^^^^ `dyn Iterator<Item = &'a mut u8>` is not an iterator
5+
| ^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
66
|
7-
= help: the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
7+
= note: the trait bound `dyn Iterator<Item = &'a mut u8>: IntoIterator` is not satisfied
8+
= note: required for `dyn Iterator<Item = &'a mut u8>` to implement `IntoIterator`
9+
help: consider mutably borrowing here
10+
|
11+
LL | for item in &mut *things { *item = 0 }
12+
| ++++
813

914
error: the type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter` is not well-formed
1015
--> $DIR/issue-20605.rs:6:17

tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr

+14-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,20 @@ error[E0283]: type annotations needed
2323
LL | impls_indirect_leak::<Box<_>>();
2424
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_indirect_leak`
2525
|
26-
= note: cannot satisfy `for<'a> Box<_>: IndirectLeak<'a>`
26+
note: multiple `impl`s satisfying `for<'a> Box<_>: Leak<'a>` found
27+
--> $DIR/leak-check-in-selection-3.rs:9:1
28+
|
29+
LL | impl Leak<'_> for Box<u32> {}
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
LL | impl Leak<'static> for Box<u16> {}
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33+
note: required for `Box<_>` to implement `for<'a> IndirectLeak<'a>`
34+
--> $DIR/leak-check-in-selection-3.rs:23:23
35+
|
36+
LL | impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {}
37+
| -------- ^^^^^^^^^^^^^^^^ ^
38+
| |
39+
| unsatisfied trait bound introduced here
2740
note: required by a bound in `impls_indirect_leak`
2841
--> $DIR/leak-check-in-selection-3.rs:25:27
2942
|

tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
error[E0277]: `impl Future<Output = ()>` cannot be sent between threads safely
1+
error: future cannot be sent between threads safely
22
--> $DIR/auto-with-drop_tracking_mir.rs:25:13
33
|
44
LL | is_send(foo());
5-
| ------- ^^^^^ `impl Future<Output = ()>` cannot be sent between threads safely
6-
| |
7-
| required by a bound introduced by this call
5+
| ^^^^^ future returned by `foo` is not `Send`
86
|
9-
= help: the trait `Send` is not implemented for `impl Future<Output = ()>`
7+
= help: the trait `Sync` is not implemented for `impl Future<Output = ()>`, which is required by `impl Future<Output = ()>: Send`
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/auto-with-drop_tracking_mir.rs:16:11
10+
|
11+
LL | let x = &NotSync;
12+
| - has type `&NotSync` which is not `Send`
13+
LL | bar().await;
14+
| ^^^^^ await occurs here, with `x` maybe used later
1015
note: required by a bound in `is_send`
1116
--> $DIR/auto-with-drop_tracking_mir.rs:24:24
1217
|
1318
LL | fn is_send(_: impl Send) {}
1419
| ^^^^ required by this bound in `is_send`
20+
help: consider dereferencing here
21+
|
22+
LL | is_send(*foo());
23+
| +
1524

1625
error: aborting due to 1 previous error
1726

18-
For more information about this error, try `rustc --explain E0277`.

tests/ui/traits/next-solver/auto-with-drop_tracking_mir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ async fn bar() {}
2323
fn main() {
2424
fn is_send(_: impl Send) {}
2525
is_send(foo());
26-
//[fail]~^ ERROR `impl Future<Output = ()>` cannot be sent between threads safely
26+
//[fail]~^ ERROR future cannot be sent between threads safely
2727
}

tests/ui/traits/next-solver/builtin-fn-must-return-sized.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
1313

1414
fn main() {
1515
foo::<fn() -> str, _>(None, ());
16-
//~^ expected a `Fn<_>` closure, found `fn() -> str`
16+
//~^ the size for values of type `str` cannot be known at compilation time
1717
}

tests/ui/traits/next-solver/builtin-fn-must-return-sized.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
error[E0277]: expected a `Fn<_>` closure, found `fn() -> str`
1+
error[E0277]: the size for values of type `str` cannot be known at compilation time
22
--> $DIR/builtin-fn-must-return-sized.rs:15:11
33
|
44
LL | foo::<fn() -> str, _>(None, ());
5-
| ^^^^^^^^^^^ expected an `Fn<_>` closure, found `fn() -> str`
5+
| ^^^^^^^^^^^ doesn't have a size known at compile-time
66
|
7-
= help: the trait `Fn<_>` is not implemented for `fn() -> str`
7+
= help: within `fn() -> str`, the trait `Sized` is not implemented for `str`, which is required by `fn() -> str: Fn<_>`
8+
= note: required because it appears within the type `fn() -> str`
89
note: required by a bound in `foo`
910
--> $DIR/builtin-fn-must-return-sized.rs:10:11
1011
|

tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
error[E0119]: conflicting implementations of trait `Trait` for type `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
1+
error[E0119]: conflicting implementations of trait `Trait` for type `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>>`
22
--> $DIR/coherence-fulfill-overflow.rs:12:1
33
|
44
LL | impl<T: ?Sized + TwoW> Trait for W<T> {}
55
| ------------------------------------- first implementation here
66
LL | impl<T: ?Sized + TwoW> Trait for T {}
7-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>>`
88
|
9-
= note: overflow evaluating the requirement `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>: TwoW`
9+
= note: overflow evaluating the requirement `W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>: TwoW`
1010
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`coherence_fulfill_overflow`)
1111

1212
error: aborting due to 1 previous error

tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
error[E0275]: overflow evaluating the requirement `W<_>: Trait`
1+
error[E0275]: overflow evaluating the requirement `_: Sized`
22
--> $DIR/fixpoint-exponential-growth.rs:33:13
33
|
44
LL | impls::<W<_>>();
55
| ^^^^
66
|
7+
note: required for `W<(W<_>, W<_>)>` to implement `Trait`
8+
--> $DIR/fixpoint-exponential-growth.rs:23:12
9+
|
10+
LL | impl<T, U> Trait for W<(W<T>, W<U>)>
11+
| - ^^^^^ ^^^^^^^^^^^^^^^
12+
| |
13+
| unsatisfied trait bound introduced here
714
note: required by a bound in `impls`
815
--> $DIR/fixpoint-exponential-growth.rs:30:13
916
|

tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ where
6161
// entering the cycle from `A` fails, but would work if we were to use the cache
6262
// result of `B<X>`.
6363
impls_trait::<A<X>, _, _, _>();
64-
//~^ ERROR the trait bound `A<X>: Trait<_, _, _>` is not satisfied
64+
//~^ ERROR the trait bound `X: IncompleteGuidance<_, _>` is not satisfied
6565
}
6666

6767
fn main() {

tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1-
error[E0277]: the trait bound `A<X>: Trait<_, _, _>` is not satisfied
1+
error[E0277]: the trait bound `X: IncompleteGuidance<_, _>` is not satisfied
22
--> $DIR/incompleteness-unstable-result.rs:63:19
33
|
44
LL | impls_trait::<A<X>, _, _, _>();
5-
| ^^^^ the trait `Trait<_, _, _>` is not implemented for `A<X>`
5+
| ^^^^ the trait `IncompleteGuidance<_, _>` is not implemented for `X`, which is required by `A<X>: Trait<_, _, _>`
66
|
7-
= help: the trait `Trait<U, V, D>` is implemented for `A<T>`
7+
= help: the following other types implement trait `IncompleteGuidance<T, V>`:
8+
<T as IncompleteGuidance<U, i16>>
9+
<T as IncompleteGuidance<U, i8>>
10+
<T as IncompleteGuidance<U, u8>>
11+
note: required for `A<X>` to implement `Trait<_, _, u8>`
12+
--> $DIR/incompleteness-unstable-result.rs:32:50
13+
|
14+
LL | impl<T: ?Sized, U: ?Sized, V: ?Sized, D: ?Sized> Trait<U, V, D> for A<T>
15+
| ^^^^^^^^^^^^^^ ^^^^
16+
LL | where
17+
LL | T: IncompleteGuidance<U, V>,
18+
| ------------------------ unsatisfied trait bound introduced here
819
note: required by a bound in `impls_trait`
920
--> $DIR/incompleteness-unstable-result.rs:51:28
1021
|

0 commit comments

Comments
 (0)