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

Preserve substitutions when making trait obligations for suggestions #71618

Merged
merged 7 commits into from
May 24, 2020

Conversation

ecstatic-morse
Copy link
Contributor

@ecstatic-morse ecstatic-morse commented Apr 27, 2020

Resolves #71394.

I think map_bound_ref is correct here. In any case, I think a lot of the diagnostic code is using skip_binder more aggressively than it should be, so I doubt that this is worse than the status quo. The assertion that new_self_ty has no escaping bound vars should be enough.

r? @estebank

cc @nikomatsakis Is the call to skip_binder on line 551 (and elsewhere in this file) appropriate?

fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
points_at_arg: bool,
has_custom_message: bool,
) -> bool {
if !points_at_arg {
return false;
}
let span = obligation.cause.span;
let param_env = obligation.param_env;
let trait_ref = trait_ref.skip_binder();
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
// Try to apply the original trait binding obligation by borrowing.
let self_ty = trait_ref.self_ty();
let found = self_ty.to_string();
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
let new_obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
new_trait_ref.without_const().to_predicate(),
);
if self.predicate_must_hold_modulo_regions(&new_obligation) {

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

@estebank estebank left a comment

Choose a reason for hiding this comment

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

The code changes themselves look ok, but I am hesitant to erasing the original cause.

Comment on lines -631 to +719
trait_ref.def_id,
trait_type,
ObligationCause::dummy(),
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_ref,
suggested_ty,
Copy link
Contributor

Choose a reason for hiding this comment

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

ignoring the previous obligation entirely? We used to use the same cause, right?

Is this what causes the "note: std::convert::From<std::string::String> is implemented for &mut str, but not for &str" to go away?

Copy link
Contributor Author

@ecstatic-morse ecstatic-morse Apr 27, 2020

Choose a reason for hiding this comment

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

No. That comes from propagating substitutions correctly. From<String> is not implemented for any of &mut str or &str.

My impression was that the cause for an obligation is where it originates from in the source. There is no underlying cause for the new obligation we create, since we're just trying various "self" types for diagnostic hints. From this POV, obligations that are only for speculation should have no cause code.

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Apr 28, 2020

@ecstatic-morse in answer to your question, I think this call to skip_binder looks wrong:

let trait_ref = trait_ref.skip_binder();

really, the only correct way to use skip_binder is if you are going to be comparing or extracting some information that can't involve any bound regions/types/things. This code seems to go on to construct obligations and try to prove them:

let new_obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
new_trait_ref.without_const().to_predicate(),
);

so that seems wrong. Probably better would be to do something like invoking replace_bound_vars_with_placeholders.

@nikomatsakis
Copy link
Contributor

Should I review this PR?

@ecstatic-morse
Copy link
Contributor Author

ecstatic-morse commented Apr 28, 2020

@nikomatsakis That would be great! I considered implementing a TypeFolder for changing the self type in a trait predicate, but I'm not sure if that is the idiomatic approach. It seems maybe like overkill? I was trying to follow the style used elsewhere in this module, but I think it's not quite correct (your comment supports this notion). Any tips would be much appreciated. I'm not very familiar with the trait solver, but I'm trying to level up enough that I can implement rust-lang/rfcs#2632.

@estebank
Copy link
Contributor

R? @nikomatsakis

@joelpalmer joelpalmer added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 7, 2020
@bors
Copy link
Contributor

bors commented May 12, 2020

☔ The latest upstream changes (presumably #72120) made this pull request unmergeable. Please resolve the merge conflicts.

@ecstatic-morse
Copy link
Contributor Author

ping @nikomatsakis

let new_trait_ref =
ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) };
Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate())
assert!(!new_self_ty.has_escaping_bound_vars());
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should comment this function and clarify that new_self_ty cannot have escaping bound variables

obligation.param_env,
trait_ref,
output_ty.skip_binder(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason that we think output_ty cannot reference bound variables? (The assertion suggests we do, but I don't immediately see it.)

I'd prefer to see output_ty.no_bound_vars().unwrap() if we believe this to be true (no_bound_vars docs)

Copy link
Contributor Author

@ecstatic-morse ecstatic-morse May 18, 2020

Choose a reason for hiding this comment

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

I think a function return type can indeed have late bound variables. fn<'a>() -> &'a dyn Any is late bound, correct? Perhaps we should just bail out if it does? Alternatively, is there a way to preserve binding levels within mk_trait_obligation_with_new_self_ty?

@@ -671,17 +669,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
return;
}

let mut trait_type = trait_ref.self_ty();
let mut suggested_ty = trait_ref.self_ty();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused how the types work out here. This is a Binders<Ty>, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a plain Ty if that helps.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I noticed that recently. Hiding the skip_binder call like that is...just so wrong. But it explains how this code type-checks, anyway.


for refs_remaining in 0..refs_number {
if let ty::Ref(_, t_type, _) = trait_type.kind {
trait_type = t_type;
if let ty::Ref(_, inner_ty, _) = suggested_ty.kind {
Copy link
Contributor

Choose a reason for hiding this comment

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

...but I must be wrong, because how do we access the suggested_ty.kind then?

obligation.param_env,
&trait_ref,
suggested_ty,
Copy link
Contributor

Choose a reason for hiding this comment

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

as above I don't 100% understand where this type is coming from or how we can be sure that it has no bound references in it -- doesn't it come from the trait_ref?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PolyTraitRef::self_ty is equivalent to self.skip_binder().substs.type_at(0). It seems we are assuming that the self type has no escaping bound variables. Is this assumption correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One other thing that I found confusing: According to the docs for TraitRef,

[it] introduces a level of region binding, to account for higher-ranked trait bounds like T: for<'a> Foo<&'a U> or higher-ranked object types.

If that's true, why do we use PolyTraitRef in so many places? Is it just for higher-ranked self types? If so, why is PolyTraitRef::self_ty, which calls skip_binder, correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

These docs are wrong. Binders are always represented explicitly by a Binder -- so TraitRef alone cannot account for for<'a>, only a PolyTraitRef can.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this assumption correct?

I think this is sort of what I was asking. I doubt it's correct. I think a reasonable thing to do might be to test the assumption by using no_bound_vars instead of relying on skip_binder (and, if the assumption is false, not generating the suggestions).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like we want to change PolyTraitRef::self_ty to return a Binder<Ty>. Is this correct? This will have a significant amount of fallout I think. Do you want me to open an issue to track this? I could check the result of self_ty for no_bound_vars in this PR, but we need to be doing this across all diagnostics code, not just here.

@nikomatsakis
Copy link
Contributor

@ecstatic-morse left some questions!

@rust-highfive

This comment has been minimized.

@nikomatsakis
Copy link
Contributor

OK, so, I think these changes are definitely going in the right direction. I'd probably be happy to merge as-is, seems like an improvement, though I suspect we could do a bit more refactoring to avoid some potential ICEs with higher-ranked regions down the line (along the lines of using no_bound_vars more often).

@bors
Copy link
Contributor

bors commented May 22, 2020

☔ The latest upstream changes (presumably #72433) made this pull request unmergeable. Please resolve the merge conflicts.

`mk_obligation_for_def_id` is only correct if the trait and self type do
not have any substitutions. Use a different method,
`mk_trait_obligation_with_new_self_ty` that is more clear about what is
happening.
Otherwise inserting it to the `Binder` used by `trait_ref` would cause
problems. This is just to be extra carefult: we aren't going to
start recommending that the user start using HKTs anytime soon.
@ecstatic-morse
Copy link
Contributor Author

Rebased to fix merge conflict. Can I r+ this? The original intent was to fix a rather embarrassing diagnostics bug. I will open an issue for removing self_ty and replacing it with no_bound_vars, as well as one for auditing uses of skip_binder in the diagnostics code more generally.

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented May 22, 2020

📌 Commit 1fad3b7 has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented May 22, 2020

🌲 The tree is currently closed for pull requests below priority 1000, this pull request will be tested once the tree is reopened

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 22, 2020
@ecstatic-morse
Copy link
Contributor Author

@bors rollup

RalfJung added a commit to RalfJung/rust that referenced this pull request May 23, 2020
…atsakis

Preserve substitutions when making trait obligations for suggestions

Resolves rust-lang#71394.

I *think* `map_bound_ref` is correct here. In any case, I think a lot of the diagnostic code is using `skip_binder` more aggressively than it should be, so I doubt that this is worse than the status quo. The assertion that `new_self_ty` has no escaping bound vars should be enough.

r? @estebank

cc @nikomatsakis Is the call to `skip_binder` on line 551 (and elsewhere in this file) appropriate? https://github.com/rust-lang/rust/blob/46ec74e60f238f694b46c976d6217e7cf8d4cf1a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs#L537-L565
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 23, 2020
…atsakis

Preserve substitutions when making trait obligations for suggestions

Resolves rust-lang#71394.

I *think* `map_bound_ref` is correct here. In any case, I think a lot of the diagnostic code is using `skip_binder` more aggressively than it should be, so I doubt that this is worse than the status quo. The assertion that `new_self_ty` has no escaping bound vars should be enough.

r? @estebank

cc @nikomatsakis Is the call to `skip_binder` on line 551 (and elsewhere in this file) appropriate? https://github.com/rust-lang/rust/blob/46ec74e60f238f694b46c976d6217e7cf8d4cf1a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs#L537-L565
bors added a commit to rust-lang-ci/rust that referenced this pull request May 24, 2020
Rollup of 5 pull requests

Successful merges:

 - rust-lang#71618 (Preserve substitutions when making trait obligations for suggestions)
 - rust-lang#72092 (Unblock font loading in rustdoc.css)
 - rust-lang#72400 (Add missing ASM arena declarations to librustc_middle)
 - rust-lang#72489 (Fix ice-72487)
 - rust-lang#72502 (fix discriminant type in generator transform)

Failed merges:

r? @ghost
@bors bors merged commit ff48fc9 into rust-lang:master May 24, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 27, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Jun 5, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Jun 5, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
RalfJung added a commit to RalfJung/rust that referenced this pull request Jun 6, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
RalfJung added a commit to RalfJung/rust that referenced this pull request Jun 6, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
RalfJung added a commit to RalfJung/rust that referenced this pull request Jun 6, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
tesuji pushed a commit to tesuji/rustc that referenced this pull request Jun 9, 2020
…matsakis

Make `PolyTraitRef::self_ty` return `Binder<Ty>`

This came up during review of rust-lang#71618. The current implementation is the same as a call to `skip_binder` but harder to audit. Make it preserve binding levels and add a call to `skip_binder` at all use sites so they can be audited as part of rust-lang#72507.
@ecstatic-morse ecstatic-morse deleted the issue-71394 branch October 6, 2020 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Compiler suggestions send me into an endless loop
6 participants