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

[WIP] Traits in scope and def-site hygiene #65351

Closed
wants to merge 2 commits into from

Conversation

petrochenkov
Copy link
Contributor

@petrochenkov petrochenkov commented Oct 13, 2019

This PR makes all traits defined in or imported into the module M to be in scope in that module for the purposes of method resolution, even if those traits or imports are produced by opaque macros.

We should probably redefine and reimplement the collection of traits in scope without hitting the issues described in the inline comments.
However, maybe it's not so urgent and we can make an issue and leave this as is for now? That will immediately unblock removal of gensyms and implementation of underscores in terms of def-site hygiene and unique macros.

cc #64623
r? @matthewjasper

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 13, 2019
).is_none() {
continue
}
if self.r.resolve_ident_in_module_unadjusted(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, what I don't like here is that we are conflating the strict part (determining the set of traits in scope) with the optimization part (pruning the traits that certainly don't fit).
For trait aliases we must perform the first part as well (we don't do that currently, as you can see below), but for them we cannot perform the second part (or at least let's assume that we cannot).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, traits "from macros" should be rejected without using the ident.

let mut ident = ident;
if ident.span.glob_adjust(
module.expansion,
binding.span,
Copy link
Contributor Author

@petrochenkov petrochenkov Oct 13, 2019

Choose a reason for hiding this comment

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

This is not the right span to determine whether the trait is in scope, it's a span of the whole import or the whole item, while we need to use a span of a single identifier, like span(Foo) in use Trait as Foo;.
Otherwise we can't correctly process imports constructed from parts, e.g.

// Currently on nightly
#![feature(decl_macro)]

mod m {
    pub trait Tr {
        fn method(&self) {}
    }

    impl Tr for u8 {}
}

macro gen_import($Br: ident) {
    use m::Tr as $Br;
}
gen_import!(Br);

fn main() {
    0u8.method(); // ERROR: no method named `method`, despite passing `Br` from the outside
}

let is_candidate = binding.module().map_or(true, |trait_module| {
self.r.resolutions(trait_module).borrow().iter().any(|resolution| {
let (&(assoc_ident, assoc_ns), _) = resolution;
assoc_ns == ns && assoc_ident.name == ident.name
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an almost perfect pruning heuristic in practice, traits rarely have different associated items with the same name in the same namespace, but different hygiene.
So, for optimization we can avoid dealing with hygiene.


fn main() {
0u8.single(); // FIXME, should be an error, `Single` shouldn't be in scope here
0u8.glob(); // FIXME, should be an error, `Glob` shouldn't be in scope here
Copy link
Contributor Author

@petrochenkov petrochenkov Oct 13, 2019

Choose a reason for hiding this comment

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

Curiously, we didn't have test cases covering imports of traits, where the traits are not produced by opaque macros (so their methods can be resolved), but the imports are produced by opaque macros (so the traits are not in scope).
So the PR didn't cause any regressions in the existing test suite until I added this test.

@petrochenkov
Copy link
Contributor Author

I won't have time to work on this.

m-ou-se added a commit to m-ou-se/rust that referenced this pull request Jan 16, 2021
…ewjasper

resolve: Simplify collection of traits in scope

"Traits in scope" for a given location are collected by walking all scopes in type namespace, collecting traits in them and pruning traits that don't have an associated item with the given name and namespace.

Previously we tried to prune traits using some kind of hygienic resolution for associated items, but that was complex and likely incorrect, e.g. in rust-lang#80762 correction to visibilites of trait items caused some traits to not be in scope anymore.
I previously had some comments and concerns about this in rust-lang#65351.

In this PR we are doing some much simpler pruning based on `Symbol` and `Namespace` comparisons, it should be enough to throw away 99.9% of unnecessary traits.
It is not necessary for pruning to be precise because for trait aliases, for example, we don't do any pruning at all, and precise hygienic resolution for associated items needs to be done in typeck anyway.

The somewhat unexpected effect is that trait imports introduced by macros 2.0 now bring traits into scope due to the removed hygienic check on associated item names.
I'm not sure whether it is desirable or not, but I think it's acceptable for now.
The old check was certainly incorrect because macros 2.0 did bring trait aliases into scope.
If doing this is not desirable, then we should come up with some other way to avoid bringing traits from macros 2.0 into scope, that would accommodate for trait aliases as well.

---

The PR also contains a couple of pure refactorings
- Scope walk is done by using `visit_scopes` instead of a hand-rolled version.
- Code is restructured to accomodate for rustdoc that also wants to query traits in scope, but doesn't want to filter them by associated items at all.

r? `@matthewjasper`
m-ou-se added a commit to m-ou-se/rust that referenced this pull request Jan 17, 2021
…ewjasper

resolve: Simplify collection of traits in scope

"Traits in scope" for a given location are collected by walking all scopes in type namespace, collecting traits in them and pruning traits that don't have an associated item with the given name and namespace.

Previously we tried to prune traits using some kind of hygienic resolution for associated items, but that was complex and likely incorrect, e.g. in rust-lang#80762 correction to visibilites of trait items caused some traits to not be in scope anymore.
I previously had some comments and concerns about this in rust-lang#65351.

In this PR we are doing some much simpler pruning based on `Symbol` and `Namespace` comparisons, it should be enough to throw away 99.9% of unnecessary traits.
It is not necessary for pruning to be precise because for trait aliases, for example, we don't do any pruning at all, and precise hygienic resolution for associated items needs to be done in typeck anyway.

The somewhat unexpected effect is that trait imports introduced by macros 2.0 now bring traits into scope due to the removed hygienic check on associated item names.
I'm not sure whether it is desirable or not, but I think it's acceptable for now.
The old check was certainly incorrect because macros 2.0 did bring trait aliases into scope.
If doing this is not desirable, then we should come up with some other way to avoid bringing traits from macros 2.0 into scope, that would accommodate for trait aliases as well.

---

The PR also contains a couple of pure refactorings
- Scope walk is done by using `visit_scopes` instead of a hand-rolled version.
- Code is restructured to accomodate for rustdoc that also wants to query traits in scope, but doesn't want to filter them by associated items at all.

r? ``@matthewjasper``
m-ou-se added a commit to m-ou-se/rust that referenced this pull request Jan 17, 2021
…ewjasper

resolve: Simplify collection of traits in scope

"Traits in scope" for a given location are collected by walking all scopes in type namespace, collecting traits in them and pruning traits that don't have an associated item with the given name and namespace.

Previously we tried to prune traits using some kind of hygienic resolution for associated items, but that was complex and likely incorrect, e.g. in rust-lang#80762 correction to visibilites of trait items caused some traits to not be in scope anymore.
I previously had some comments and concerns about this in rust-lang#65351.

In this PR we are doing some much simpler pruning based on `Symbol` and `Namespace` comparisons, it should be enough to throw away 99.9% of unnecessary traits.
It is not necessary for pruning to be precise because for trait aliases, for example, we don't do any pruning at all, and precise hygienic resolution for associated items needs to be done in typeck anyway.

The somewhat unexpected effect is that trait imports introduced by macros 2.0 now bring traits into scope due to the removed hygienic check on associated item names.
I'm not sure whether it is desirable or not, but I think it's acceptable for now.
The old check was certainly incorrect because macros 2.0 did bring trait aliases into scope.
If doing this is not desirable, then we should come up with some other way to avoid bringing traits from macros 2.0 into scope, that would accommodate for trait aliases as well.

---

The PR also contains a couple of pure refactorings
- Scope walk is done by using `visit_scopes` instead of a hand-rolled version.
- Code is restructured to accomodate for rustdoc that also wants to query traits in scope, but doesn't want to filter them by associated items at all.

r? ```@matthewjasper```
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.

3 participants