Skip to content
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

Closed
wants to merge 2 commits into from

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Mar 4, 2020

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.

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 4, 2020
@estebank
Copy link
Contributor Author

estebank commented Mar 4, 2020

cc @nikomatsakis @rust-lang/wg-diagnostics

@@ -193,6 +193,8 @@ pub enum ObligationCauseCode<'tcx> {

ImplDerivedObligation(DerivedObligationCause<'tcx>),

DerivedCauseCode(Box<ObligationCauseCode<'tcx>>),
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

@eddyb eddyb Mar 5, 2020

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.

Copy link
Member

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),
Copy link
Member

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>>,
Copy link
Member

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

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.

Comment on lines +16 to +18
struct Vec<T> {
t: T,
}
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Comment on lines +1 to +4
// FIXME: missing sysroot spans (#53081)
// ignore-i586-unknown-linux-gnu
// ignore-i586-unknown-linux-musl
// ignore-i686-unknown-linux-musl
Copy link
Member

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`
Copy link
Member

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 Spans! I think I added those for the type alias work that never landed itself.

Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines +9 to -7
LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> {
| ---- required by `std::clone::AssertParamIsCopy` here
|
= note: required by `std::clone::AssertParamIsCopy`
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

Comment on lines +8 to +10
struct Vec<T> {
t: T,
}
Copy link
Member

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.

Comment on lines +7 to +10
enum Option<T> {
Some(T),
None,
}
Copy link
Member

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.

Comment on lines +134 to 143
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 }
}
Copy link
Member

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.

Copy link
Contributor Author

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 Obligations 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.

Copy link
Member

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 Obligations that could end up getting mixed with true Obligations.

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.

Comment on lines +325 to +326
cause.code =
traits::ObligationCauseCode::DerivedCauseCode(Box::new(obligation.cause.code));
Copy link
Member

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.

Copy link
Contributor Author

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 :)

Comment on lines -607 to +615
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)
})
Copy link
Member

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).

Copy link
Contributor Author

@estebank estebank Mar 5, 2020

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 PredicateObligations instead of Predicates.

Making a new PR extracting these changes on their own.

Comment on lines 143 to 144
/// Like `ItemObligation`, but with extra detail on the source of the obligation.
BindingObligation(DefId, Span),
Copy link
Member

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines 139 to 140
/// In an impl of trait `X` for type `Y`, type `Y` must
/// also implement all supertraits of `X`.
Copy link
Member

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).

@estebank
Copy link
Contributor Author

estebank commented Mar 5, 2020

Made #69745 with only a subset of these changes (no ?Sized and no obligation chain).

@estebank
Copy link
Contributor Author

estebank commented Mar 7, 2020

This PR is fully subsumed by #69793, #69791 and #69745. I'll close next week, but leaving open for now to give a chance to people tagged in inline comments to see them.

@estebank estebank closed this Mar 9, 2020
Centril added a commit to Centril/rust that referenced this pull request Apr 10, 2020
…=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
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 19, 2020
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Note where implicit Sized requirement comes from
4 participants