-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
#[feature(uniform_paths)]: allow use x::y;
to resolve through self::x
, not just ::x
.
#52923
Conversation
r? @varkor (rust_highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I had a look at the changes: everything looks good to me (apart from needing more tests), but I'm not familiar with this part of the code. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
self.session.features_untracked().relative_imports && | ||
if let Some(second_segment) = module_path.get(1) { | ||
module_path[0].name == keywords::CrateRoot.name() && | ||
self.extern_prelude.contains(&second_segment.name) |
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.
Talking to @joshtriplett, turns out this condition is wrong, we should always fork paths that don't start with ::
or a keyword.
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.
If we want to be more forward-compatible with "1path", then crates outside of extern_prelude
(not passed with ---extern
) should not be accessible with non-disambiguated paths.
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.
(So the condition needs to stay in some form.)
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.
@petrochenkov I guess with relative_imports
, we'd have to separate relative use
paths (which would be the default?) from allowing to resolve through an extern
crate (which would be gated by extern_prelude
), but preferably still detect ambiguities when a crate could be loaded?
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.
Do you mean something like this?
mod rustc {}
// refers to the local module rustc as a relative path
// however, there's an extern crate `rustc` that can be loaded even if it's not in extern prelude
use rustc;
We can certainly detect this "ambiguity" in a post-processing step if rustc
was successfully resolved locally, and possibly report an error.
let self_ident = Ident { name: keywords::SelfValue.name(), span: use_tree.span }; | ||
let extern_ident = Ident { name: keywords::Extern.name(), span: use_tree.span }; | ||
self_module_path[0] = self_ident; | ||
extern_module_path[0] = extern_ident; |
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.
This doesn't need to use Extern
, it could rely on CrateRoot
which has same effect.
(reminder to self to incorporate these) Tricky expansion-related tests: https://internals.rust-lang.org/t/relative-paths-in-rust-2018/7883/105 An intricate testcase from @joshtriplettExpected output:
pub const A: usize = 0;
pub mod foo {
pub const B: usize = 1;
pub mod bar {
pub const C: usize = 2;
pub enum E {
V1(usize),
V2(String),
}
pub fn test() {
println!("{} {} {}", crate::A, crate::foo::B, C);
}
pub fn test_use() {
use crate::A;
use crate::foo::B;
println!("{} {} {}", A, B, C);
}
pub fn test_enum() {
use E::*;
match E::V1(10) {
V1(i) => { println!("V1: {}", i); }
V2(s) => { println!("V2: {}", s); }
}
}
}
pub fn test() {
println!("{} {} {}", crate::A, B, bar::C);
}
pub fn test_use() {
use crate::A;
use bar::C;
println!("{} {} {}", A, B, C);
}
pub fn test_enum() {
use bar::E::*;
match bar::E::V1(10) {
V1(i) => { println!("V1: {}", i); }
V2(s) => { println!("V2: {}", s); }
}
}
}
pub fn test() {
println!("{} {} {}", A, foo::B, foo::bar::C);
}
pub fn test_use() {
use foo::B;
use foo::bar::C;
println!("{} {} {}", A, B, C);
}
pub fn test_enum() {
use foo::bar::E::*;
match foo::bar::E::V1(10) {
V1(i) => { println!("V1: {}", i); }
V2(s) => { println!("V2: {}", s); }
}
}
fn main() {
test();
foo::test();
foo::bar::test();
test_use();
foo::test_use();
foo::bar::test_use();
test_enum();
foo::test_enum();
foo::bar::test_enum();
} |
Updated version of the edition guide for these changes: rust-lang/edition-guide#74 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
{ | ||
return Ok(()); | ||
} | ||
} |
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.
Does this mean "both self::path
and extern::path
exist, but they actually refer to the same non-import entity in the end"?
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.
(Looks like this should be unnecessary if the "backtracking" is not supported.)
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.
I'm trying to remember if I added this or @cramertj but I think it was here when I started hacking on it. IMO this should probably be an ambiguity error to stay on the safe side.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@bors: p=100 I'm going to go ahead and set high priority on this, since it's blocking the Preview 2 announcement. |
…::x`, not just `::x`.
…ths)] is enabled.
…`uniform_paths` imports.
98478ca
to
13bc0b5
Compare
Huh, we don't run those two accursed tests on the PR Travis checks?! @bors r=petrochenkov |
📌 Commit 13bc0b5 has been approved by |
#[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just `::x`. _Branch originally by @cramertj, based on @petrochenkov's [description on the internals forum](https://internals.rust-lang.org/t/relative-paths-in-rust-2018/7883/30?u=petrochenkov)._ _(note, however, that the approach has significantly changed since)_ Implements `#[feature(uniform_paths)]` from #53130, by treating unqualified `use` paths as maybe-relative. That is, `use x::y;`, where `x` is a plain identifier (not a keyword), is no longer the same as `use ::x::y;`, and before picking an external crate named `x`, it first looks for an item named `x` in the same module (i.e. `self::x`) and prefers that local item instead. Such a "maybe-relative" `x` can only resolve to an external crate if it's listed in "`extern_prelude`" (i.e. `core` / `std` and all the crates passed to `--extern`; the latter includes Cargo dependencies) - this is the same condition as being able to refer to the external crate from an unqualified, non-`use` path. All other crates must be explicitly imported with an absolute path, e.g. `use ::x::y;` To detect an ambiguity between the external crate and the local item with the same name, a "canary" import (e.g. `use self::x as _;`), tagged with the `is_uniform_paths_canary` flag, is injected. As the initial implementation is not sophisticated enough to handle all possible ways in which `self::x` could appear (e.g. from macro expansion), this also guards against accidentally picking the external crate, when it might actually get "shadowed" later. Also, more canaries are injected for each block scope around the `use`, as `self::x` cannot resolve to any items named `x` in those scopes, but non-`use` paths can, and that could be confusing or even backwards-incompatible. Errors are emitted only if the main "canary" import succeeds while an external crate exists (or if any of the block-scoped ones succeed at all), and ambiguities have custom error reporting, e.g.: ```rust #![feature(uniform_paths)] pub mod foo { use std::io; pub mod std { pub mod io {} } } ``` ```rust error: import from `std` is ambiguous --> test.rs:3:9 | 3 | use std::io; | ^^^ could refer to external crate `::std` 4 | pub mod std { pub mod io {} } | ----------------------------- could also refer to `self::std` | = help: write `::std` or `self::std` explicitly instead = note: relative `use` paths enabled by `#![feature(uniform_paths)]` ``` Another example, this time with a block-scoped item shadowing a module-scoped one: ```rust #![feature(uniform_paths)] enum Foo { A, B } fn main() { enum Foo {} use Foo::*; } ``` ```rust error: import from `Foo` is ambiguous --> test.rs:5:9 | 4 | enum Foo {} | ----------- shadowed by block-scoped `Foo` 5 | use Foo::*; | ^^^ | = help: write `::Foo` or `self::Foo` explicitly instead = note: relative `use` paths enabled by `#![feature(uniform_paths)]` ``` Additionally, this PR, because replacing "the `finalize_import` hack" was a blocker: * fixes #52140 * fixes #52141 * fixes #52705 cc @aturon @joshtriplett
☀️ Test successful - status-appveyor, status-travis |
📣 Toolstate changed by #52923! Tested on commit 3e05f80. 💔 clippy-driver on windows: test-pass → build-fail (cc @Manishearth @llogiq @mcarton @oli-obk, @rust-lang/infra). |
Tested on commit rust-lang/rust@3e05f80. Direct link to PR: <rust-lang/rust#52923> 💔 clippy-driver on windows: test-pass → build-fail (cc @Manishearth @llogiq @mcarton @oli-obk, @rust-lang/infra). 💔 clippy-driver on linux: test-pass → build-fail (cc @Manishearth @llogiq @mcarton @oli-obk, @rust-lang/infra). 💔 rls on windows: test-pass → build-fail (cc @nrc, @rust-lang/infra). 💔 rls on linux: test-pass → build-fail (cc @nrc, @rust-lang/infra).
Edit: removed bug report for nonexistent bug caused by rustup falling back to an oudated cargo. This hits an ICE on clippy_lints
|
@Manishearth Oh right I'll have to look into the ICE, that's unexpected! (EDIT: see #53333). |
Branch originally by @cramertj, based on @petrochenkov's description on the internals forum.
(note, however, that the approach has significantly changed since)
Implements
#[feature(uniform_paths)]
from #53130, by treating unqualifieduse
paths as maybe-relative. That is,use x::y;
, wherex
is a plain identifier (not a keyword), is no longer the same asuse ::x::y;
, and before picking an external crate namedx
, it first looks for an item namedx
in the same module (i.e.self::x
) and prefers that local item instead.Such a "maybe-relative"
x
can only resolve to an external crate if it's listed in "extern_prelude
" (i.e.core
/std
and all the crates passed to--extern
; the latter includes Cargo dependencies) - this is the same condition as being able to refer to the external crate from an unqualified, non-use
path.All other crates must be explicitly imported with an absolute path, e.g.
use ::x::y;
To detect an ambiguity between the external crate and the local item with the same name, a "canary" import (e.g.
use self::x as _;
), tagged with theis_uniform_paths_canary
flag, is injected. As the initial implementation is not sophisticated enough to handle all possible ways in whichself::x
could appear (e.g. from macro expansion), this also guards against accidentally picking the external crate, when it might actually get "shadowed" later.Also, more canaries are injected for each block scope around the
use
, asself::x
cannot resolve to any items namedx
in those scopes, but non-use
paths can, and that could be confusing or even backwards-incompatible.Errors are emitted only if the main "canary" import succeeds while an external crate exists (or if any of the block-scoped ones succeed at all), and ambiguities have custom error reporting, e.g.:
Another example, this time with a block-scoped item shadowing a module-scoped one:
Additionally, this PR, because replacing "the
finalize_import
hack" was a blocker:use
statement #52705cc @aturon @joshtriplett