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

Permit evaluation of assoc items on Self by avoiding cycle error #74130

Closed
wants to merge 1 commit into from

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Jul 7, 2020

Skip the recursive evaluation of super-traits that reference associated
types on Self directly (without using a fully-qualified path) in order
to allow its use.

The following code would previously emit a cycle evaluation error and
will now successfuly compile:

trait Bar<T> {
    fn foo() -> T;
}
trait Foo: Bar<Self::Qux> {
    type Qux;
}

The following will also work:

trait Bar<T> {
    fn foo() -> T;
}
trait Baz {
    type Qux;
}
trait Foo: Bar<Self::Qux> + Baz {}

Fix #35237.

@rust-highfive
Copy link
Collaborator

r? @davidtwco

(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 Jul 7, 2020
@estebank estebank force-pushed the self-param-assoc-item branch 4 times, most recently from a4a675c to 54c0927 Compare July 7, 2020 17:47
@estebank
Copy link
Contributor Author

estebank commented Jul 7, 2020

cc @nikomatsakis @joshtriplett

@joshtriplett
Copy link
Member

Very nice work!

@estebank
Copy link
Contributor Author

estebank commented Jul 7, 2020

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@bors
Copy link
Contributor

bors commented Jul 7, 2020

⌛ Trying commit 759a56dcc21e49eac014e195d579f5a26effa251 with merge 6024cfef3a82f9a7688f07054f1650081bad3af5...

@estebank
Copy link
Contributor Author

estebank commented Jul 7, 2020

@rust-lang/lang this PR introduces new behavior letting us refer to associated types in super-trait arguments without using a fully qualified path. Only edge case I've been able to come up with is the following:

trait Foo<T> {
    type B;
}
trait Bar<T> {}
trait Baz {
    type A;
}
trait Qux: Foo<Self::A> + Bar<Self::B> + Baz {}

which causes the compile to fail because we proactively skip all bounds that reference Self when evaluating associated types:

error[E0220]: associated type `B` not found for `Self`
 --> fil12.rs:8:37
  |
8 | trait Qux: Foo<Self::A> + Bar<Self::B> + Baz {}
  |                                     ^ help: there is an associated type with a similar name: `A`

You can still make it work with fully qualified paths:

trait Foo<T> {
    type B;
}
trait Bar<T> {}
trait Baz {
    type A;
}
trait Qux: Foo<<Self as Baz>::A> + Bar<<Self as Foo<<Self as Baz>::A>>::B> + Baz {}

or even rely on the new behavior to reduce the verbosity:

trait Foo<T> {
    type B;
}
trait Bar<T> {}
trait Baz {
    type A;
}
trait Qux: Foo<<Self as Baz>::A> + Bar<Self::B> + Baz {}

but I don't know if we want such a "special case" in the language. I think it is worth it (particularly if we at least improve the diagnostics to suggest the appropriate syntax).

@bors
Copy link
Contributor

bors commented Jul 7, 2020

☀️ Try build successful - checks-actions, checks-azure
Build commit: 6024cfef3a82f9a7688f07054f1650081bad3af5 (6024cfef3a82f9a7688f07054f1650081bad3af5)

@rust-timer
Copy link
Collaborator

Queued 6024cfef3a82f9a7688f07054f1650081bad3af5 with parent e1beee4, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit (6024cfef3a82f9a7688f07054f1650081bad3af5): comparison url.

@estebank
Copy link
Contributor Author

estebank commented Jul 8, 2020

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-74130 created and queued.
🤖 Automatically detected try build 6024cfef3a82f9a7688f07054f1650081bad3af5
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 8, 2020
Copy link
Member

@davidtwco davidtwco left a comment

Choose a reason for hiding this comment

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

Implementation looks reasonable to me, but someone else should have a look.

ty::Param(param) if param.name == kw::SelfUpper => true,
_ => false,
};
if !(visitor.0 && skip_self_param_evaluation && is_self_param) {
Copy link
Contributor Author

@estebank estebank Jul 8, 2020

Choose a reason for hiding this comment

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

Ideally instead of just skipping super traits we would synthesize a new PolyTraitRef with a TraitRef with params set to some placeholder to avoid the weird edge cases I mentioned before, but couldn't see a good way to do that to a hir node.

@craterbot
Copy link
Collaborator

🚧 Experiment pr-74130 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-74130 is completed!
📊 7 regressed and 8 fixed (112019 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the blacklist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Jul 14, 2020
@estebank
Copy link
Contributor Author

r? @nikomatsakis ?

@nikomatsakis
Copy link
Contributor

So, I'm not sure yet what I think about this specific change, but I agree this is a common pain point that we should try to address.

In short, the current code that handles "short-hands" like T::Foo is already based on some heuristics that sometimes falldown and also are occasionally inaccurate. So I guess that in principle extending these heuristics to cover this case in a semi ad-hoc fashion feels ok.

I think one thing that might make me feel better about it overall is trying to describe the algorithm in some form that would be suitable for the Rust reference, or at least rustc-dev-guide or a nice comment, so that people can understand what's going on. Might be a useful exercise either for you @estebank or for me as a reviewer. =)

(Longer term, I had toyed with ideas of integrating this selection into associated type projection; chalk had a way to represent an "unspecific projection" like T::Iter where the trait was not yet known, and it would figure out what trait the projection might be from, but we kind of canned that out for later.)

@estebank
Copy link
Contributor Author

@nikomatsakis expanded a little bit on the documentation with some extra doc comments. Let me know if it seems like it goes into enough detail.

@estebank estebank force-pushed the self-param-assoc-item branch 3 times, most recently from 16ce81a to 369c2b8 Compare July 31, 2020 18:24
@bors

This comment has been minimized.

@estebank estebank force-pushed the self-param-assoc-item branch 2 times, most recently from 23e943a to 3380862 Compare August 23, 2020 01:57
@bors
Copy link
Contributor

bors commented Aug 25, 2020

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

@bors
Copy link
Contributor

bors commented Aug 30, 2020

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

Skip the recursive evaluation of super-traits that reference associated
types on `Self` directly (without using a fully-qualified path) in order
to allow its use.

The following code would previously emit a cycle evaluation error and
will now successfuly compile:

```rust
trait Bar<T> {
    fn foo() -> T;
}
trait Foo: Bar<Self::Qux> {
    type Qux;
}
```

The following will also work:

```rust
trait Bar<T> {
    fn foo() -> T;
}
trait Baz {
    type Qux;
}
trait Foo: Bar<Self::Qux> + Baz {}
```

Fix rust-lang#35237.
@crlf0710 crlf0710 added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 18, 2020
@bors
Copy link
Contributor

bors commented Sep 22, 2020

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

Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:

@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

@crlf0710
Copy link
Member

crlf0710 commented Oct 8, 2020

Triage: there's merge conflicts now.

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

OK, I read this through and I understand it now. I am feeling positively inclined. The question, I guess, is whether it introduces any kinds of backwards compatibility hazards. Let me post separately on this.

@@ -1446,6 +1531,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
qself_res: Res,
assoc_segment: &hir::PathSegment<'_>,
permit_variants: bool,
dont_recurse: bool,
Copy link
Contributor

Choose a reason for hiding this comment

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

Pre-existing, sort of, but could we introduce struct PermitVariants(bool) and struct DontRecurse(bool) or something? I found it hard to read calls like self.associated_path_to_ty(..., true, false) etc and remember what those booleans were meant to represent.

trait_bounds.push((b, constness))
hir::GenericBound::Trait(ref b, modif @ hir::TraitBoundModifier::None) => {
let mut visitor = SelfFinder(false);
visitor.visit_poly_trait_ref(b, modif);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be better to avoid the visitor if !skip_self_param_evaluation or !is_self_param, right?

I would also maybe find that if below a little easier to read if it were restructured.

@nikomatsakis
Copy link
Contributor

I was thinking about what kind of backwards compatibility hazard this code might introduce. I guess that the case we might worry about it something like this

trait Foo<T> {
    type A;
}
trait Baz {
    type A;
}
trait Qux: Foo<Self::A> + Baz {}

@estebank I think that what will happen here is that, to resolve Self::A, we'll skip the first bound and just look at the second one, does that sound correct?

@nikomatsakis
Copy link
Contributor

I suspect we might be able to make all the cases that we want to accept work without accepting negative cases with a different approach. The idea would be to make a query that walks a given trait and extracts the names of all associated types found in that trait or any supertrait -- but it does this without any sort of substitution or creation of types. We can then leverage this query to skip "elaborating" things that don't potentially reference an associated type with the given name. Does this make sense, @estebank? I would find this approach a bit more elegant, I think.

@crlf0710 crlf0710 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 Nov 6, 2020
@nikomatsakis
Copy link
Contributor

So @spastorino is currently exploring the alternative idea I proposed. I'm going to close this PR for the time being because I think that the back compat hazard I mentioned here is kind of a blocker to this approach. We can always re-open. Thanks @estebank for poking at this though, it'd be great to fix this problem.

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. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Trait inheritance gives unexpected compile error when inherited traits use associated types defined in trait
9 participants