-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Maintain predicate obligation chain for more detailed diagnostics #69709
Conversation
r? @eddyb (rust_highfive has picked a reviewer for you, use r? to override) |
cc @nikomatsakis @rust-lang/wg-diagnostics |
@@ -193,6 +193,8 @@ pub enum ObligationCauseCode<'tcx> { | |||
|
|||
ImplDerivedObligation(DerivedObligationCause<'tcx>), | |||
|
|||
DerivedCauseCode(Box<ObligationCauseCode<'tcx>>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have a system for this already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The closest is what is above but that requires a predicates to be available always, and that wasn't feasible for the cases I was handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WF definitely has WF predicates, so at least that should be doable.
I'll take a second look at the diff and note the places DerivedCauseCode
is created in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I see, DerivedObligationCause
is a bit limited, you want to replace the TraitRef
with a Predicate
(which would let you put a WF predicate in there).
But still, I'd prefer if this side of the PR was done separately from the ItemObligation
side.
@@ -442,6 +442,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { | |||
super::ImplDerivedObligation(ref cause) => { | |||
tcx.lift(cause).map(super::ImplDerivedObligation) | |||
} | |||
super::DerivedCauseCode(ref x) => tcx.lift(&**x).map(|x| x), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is stripping DerivedCauseCode
, please follow other match arms.
@@ -1430,7 +1430,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { | |||
crate fn note_obligation_cause_code<T>( | |||
&self, | |||
err: &mut DiagnosticBuilder<'_>, | |||
predicate: &T, | |||
predicate: Option<&T>, | |||
cause_code: &ObligationCauseCode<'tcx>, | |||
obligated_types: &mut Vec<&ty::TyS<'tcx>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a weird name and type. I assume this was meant to be &mut Vec<Ty<'tcx>>
? How is this not tripping the lint against types like TyS
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should indeed trip the internal lint. I want to revisit this lint anyway, to improve its suggestions. I'll look into this then.
struct Vec<T> { | ||
t: T, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you keep the original test and duplicate it with a local Vec
definition?
Ideally the original test would also have good diagnostics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests where I did this have good output, they just point into stdlib when their spans are available and changing them to a local type didn't compromise the integrity of what was being tested. It didn't feel necessary to have two types (increasing CI time), but if you insist I can duplicate them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
necessary to have two types (increasing CI time)
Presumably it takes negligible time, it's only 3 tests.
and changing them to a local type didn't compromise the integrity of what was being tested
Can never be too sure, especially for old tests.
// FIXME: missing sysroot spans (#53081) | ||
// ignore-i586-unknown-linux-gnu | ||
// ignore-i586-unknown-linux-musl | ||
// ignore-i686-unknown-linux-musl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😭
::: $SRC_DIR/libcore/cmp.rs:LL:COL | ||
| | ||
LL | pub struct AssertParamIsEq<T: Eq + ?Sized> { | ||
| -- required by `std::cmp::AssertParamIsEq` here | ||
| | ||
= note: required by `std::cmp::AssertParamIsEq` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @nikomatsakis @Centril Finally a use for the per-Predicate
Span
s! I think I added those for the type alias work that never landed itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although for AssertParamIsEq
specifically, we might want a custom #[rustc_on_unimplemented]
, since it's used by #[derive(Eq)]
specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking into this, rustc_on_unimplemented
would need to have support for targeting bounds in the obligation chain too.
LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { | ||
| ---- required by `std::clone::AssertParamIsCopy` here | ||
| | ||
= note: required by `std::clone::AssertParamIsCopy` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also use #[rustc_on_unimplemented]
, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. Do you have some suggested target output?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, but feel free to open an issue first to gather suggestions.
struct Vec<T> { | ||
t: T, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another instance of a case where I think it would be worth duplicating the test.
enum Option<T> { | ||
Some(T), | ||
None, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Third test, this time Option
is being duplicated in the test.
fn predicate_obligation<'tcx>( | ||
predicate: ty::Predicate<'tcx>, | ||
span: Option<Span>, | ||
) -> PredicateObligation<'tcx> { | ||
let mut cause = ObligationCause::dummy(); | ||
if let Some(span) = span { | ||
cause.span = span; | ||
} | ||
Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be easier, instead of all of this, to have Elaborator
produce (ty::Predicate, Span)
pairs, then it's basically just like tcx.predicates_of
.
Or Option<Span>
I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about those possibilities, but it felt that just dealing with Obligation
s everywhere will make more sense in the future. If we can move to a point where we only deal with bare Predicate
only when evaluating a single one, the we can keep as much diagnostic info as we (really) need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose, but you're introducing dummy Obligation
s that could end up getting mixed with true Obligation
s.
This may make more sense if elaborate_predicates
took in Obligation<Vec<Predicate>>
or something.
But I suppose I can defer to @nikomatsakis on this.
cause.code = | ||
traits::ObligationCauseCode::DerivedCauseCode(Box::new(obligation.cause.code)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this seems to be the only place where DerivedCauseCode
is created?
I think most of the test output changes are because of the extra Span
in ItemObligation
, not this, am I right?
If so, it might be worth splitting this PR into the non-DerivedCauseCode
changes and the DerivedCauseCode
changes, because the former seem far less controversial (and easier to understand) to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"most" might be the case. Removing this part will greatly diminish the benefit of this change, but I can oblige and let the PRs evolve independently :)
let cause = self.cause(traits::ItemObligation(def_id)); | ||
predicates | ||
.predicates | ||
.into_iter() | ||
.map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred)) | ||
.zip(predicates.spans.into_iter()) | ||
.map(|(pred, span)| { | ||
let cause = self.cause(traits::ItemObligation(def_id, Some(span))); | ||
traits::Obligation::new(cause, self.param_env, pred) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here it is, this (mostly) self-contained change is what I suspect is responsible for most of the test changes, I would love to have this landed ASAP (assuming sticking the Span
into ItemObligation
is the best way to go about this, I'd defer to @nikomatsakis on that).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear, this change is the one that precipitated the shift to keeping PredicateObligation
s instead of Predicate
s.
Making a new PR extracting these changes on their own.
/// Like `ItemObligation`, but with extra detail on the source of the obligation. | ||
BindingObligation(DefId, Span), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, looks like a version with a Span
already exists, or is it too restricted to work for you?
Or I guess BindingObligation
itself could be replaced with ItemObligation
now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the very least seeing this tells me that having a Span
in ObligationCauseCode
is probably fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#69745 acknowledges this and uses BindingObligation
instead. Some of the output is slightly worse, but can be iterated on.
/// In an impl of trait `X` for type `Y`, type `Y` must | ||
/// also implement all supertraits of `X`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow this comment is incredibly outdated (/me pokes @nikomatsakis).
Made #69745 with only a subset of these changes (no |
…=nikomatsakis,eddyb Use `PredicateObligation`s instead of `Predicate`s Keep more information about trait binding failures. Use more specific spans by pointing at bindings that introduce obligations. Subset of rust-lang#69709. r? @eddyb
…ikomatsakis Maintain chain of derived obligations When evaluating the derived obligations from super traits, maintain a reference to the original obligation in order to give more actionable context in the output. Continuation (and built on) rust-lang#69745, subset of rust-lang#69709. r? @eddyb
Keep obligations around instead of just predicates as much as possible, to maintain extra context, including chains of obligations.
Use more specific spans by pointing at bindings that introduce obligations.
Mention implicit
T: Sized
bound in E0277 errors.Fix #27964.