-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Conversation
r? @davidtwco (rust_highfive has picked a reviewer for you, use r? to override) |
a4a675c
to
54c0927
Compare
Very nice work! |
54c0927
to
759a56d
Compare
@bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit 759a56dcc21e49eac014e195d579f5a26effa251 with merge 6024cfef3a82f9a7688f07054f1650081bad3af5... |
@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
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:
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). |
☀️ Try build successful - checks-actions, checks-azure |
Queued 6024cfef3a82f9a7688f07054f1650081bad3af5 with parent e1beee4, future comparison URL. |
Finished benchmarking try commit (6024cfef3a82f9a7688f07054f1650081bad3af5): comparison url. |
@craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. 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.
Implementation looks reasonable to me, but someone else should have a look.
src/librustc_typeck/astconv.rs
Outdated
ty::Param(param) if param.name == kw::SelfUpper => true, | ||
_ => false, | ||
}; | ||
if !(visitor.0 && skip_self_param_evaluation && is_self_param) { |
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.
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.
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
r? @nikomatsakis ? |
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 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 |
759a56d
to
d745f92
Compare
@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. |
16ce81a
to
369c2b8
Compare
369c2b8
to
ac20dee
Compare
This comment has been minimized.
This comment has been minimized.
23e943a
to
3380862
Compare
☔ The latest upstream changes (presumably #75666) made this pull request unmergeable. Please resolve the merge conflicts. |
3380862
to
304235b
Compare
☔ 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.
304235b
to
53bacac
Compare
☔ 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:
|
Triage: there's merge conflicts 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.
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, |
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.
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); |
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.
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.
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 |
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. |
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. |
Skip the recursive evaluation of super-traits that reference associated
types on
Self
directly (without using a fully-qualified path) in orderto allow its use.
The following code would previously emit a cycle evaluation error and
will now successfuly compile:
The following will also work:
Fix #35237.