Auto merge of #47156 - petrochenkov:extpath, r=nikomatsakis

Support `extern` in paths

Implement the primary alternative to https://github.com/rust-lang/rust/pull/46613 + https://github.com/rust-lang/rust/pull/45771, achieving the same effect without requiring changes to other imports.
Both need to be experimentally evaluated before making further progress.

The PR also adds docs for all these related features into the unstable book.

cc https://github.com/rust-lang/rust/issues/44660
r? @nikomatsakis
This commit is contained in:
bors 2018-01-07 00:51:42 +00:00
commit a704583d43
17 changed files with 322 additions and 26 deletions

View file

@ -0,0 +1,54 @@
# `crate_in_paths`
The tracking issue for this feature is: [#44660]
[#44660]: https://github.com/rust-lang/rust/issues/44660
------------------------
The `crate_in_paths` feature allows to explicitly refer to the crate root in absolute paths
using keyword `crate`.
`crate` can be used *only* in absolute paths, i.e. either in `::crate::a::b::c` form or in `use`
items where the starting `::` is added implicitly.
Paths like `crate::a::b::c` are not accepted currently.
This feature is required in `feature(extern_absolute_paths)` mode to refer to any absolute path
in the local crate (absolute paths refer to extern crates by default in that mode), but can be
used without `feature(extern_absolute_paths)` as well.
```rust
#![feature(crate_in_paths)]
// Imports, `::` is added implicitly
use crate::m::f;
use crate as root;
mod m {
pub fn f() -> u8 { 1 }
pub fn g() -> u8 { 2 }
pub fn h() -> u8 { 3 }
// OK, visibilities implicitly add starting `::` as well, like imports
pub(in crate::m) struct S;
}
mod n
{
use crate::m::f;
use crate as root;
pub fn check() {
assert_eq!(f(), 1);
// `::` is required in non-import paths
assert_eq!(::crate::m::g(), 2);
assert_eq!(root::m::h(), 3);
}
}
fn main() {
assert_eq!(f(), 1);
assert_eq!(::crate::m::g(), 2);
assert_eq!(root::m::h(), 3);
n::check();
}
```

View file

@ -0,0 +1,43 @@
# `extern_absolute_paths`
The tracking issue for this feature is: [#44660]
[#44660]: https://github.com/rust-lang/rust/issues/44660
------------------------
The `extern_absolute_paths` feature enables mode allowing to refer to names from other crates
"inline", without introducing `extern crate` items, using absolute paths like `::my_crate::a::b`.
`::my_crate::a::b` will resolve to path `a::b` in crate `my_crate`.
`feature(crate_in_paths)` can be used in `feature(extern_absolute_paths)` mode for referring
to absolute paths in the local crate (`::crate::a::b`).
`feature(extern_in_paths)` provides the same effect by using keyword `extern` to refer to
paths from other crates (`extern::my_crate::a::b`).
```rust,ignore
#![feature(extern_absolute_paths)]
// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
// options, or standard Rust distribution, or some other means.
use xcrate::Z;
fn f() {
use xcrate;
use xcrate as ycrate;
let s = xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = ycrate::Z;
assert_eq!(format!("{:?}", z), "Z");
}
fn main() {
let s = ::xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = Z;
assert_eq!(format!("{:?}", z), "Z");
}
```

View file

@ -0,0 +1,40 @@
# `extern_in_paths`
The tracking issue for this feature is: [#44660]
[#44660]: https://github.com/rust-lang/rust/issues/44660
------------------------
The `extern_in_paths` feature allows to refer to names from other crates "inline", without
introducing `extern crate` items, using keyword `extern`.
For example, `extern::my_crat::a::b` will resolve to path `a::b` in crate `my_crate`.
`feature(extern_absolute_paths)` mode provides the same effect by resolving absolute paths like
`::my_crate::a::b` to paths from extern crates by default.
```rust,ignore
#![feature(extern_in_paths)]
// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
// options, or standard Rust distribution, or some other means.
use extern::xcrate::Z;
fn f() {
use extern::xcrate;
use extern::xcrate as ycrate;
let s = xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = ycrate::Z;
assert_eq!(format!("{:?}", z), "Z");
}
fn main() {
let s = extern::xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = Z;
assert_eq!(format!("{:?}", z), "Z");
}
```

View file

@ -2982,6 +2982,8 @@ impl<'a> Resolver<'a> {
let msg = "There are too many initial `super`s.".to_string();
return PathResult::Failed(ident.span, msg, false);
}
} else if i == 0 && ns == TypeNS && name == keywords::Extern.name() {
continue;
}
allow_super = false;
@ -2996,16 +2998,19 @@ impl<'a> Resolver<'a> {
// `$crate::a::b`
module = Some(self.resolve_crate_root(ident.node.ctxt));
continue
} else if i == 1 && self.session.features.borrow().extern_absolute_paths &&
path[0].node.name == keywords::CrateRoot.name() &&
!token::Ident(ident.node).is_path_segment_keyword() {
// `::extern_crate::a::b`
let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
let crate_root =
self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
self.populate_module_if_necessary(crate_root);
module = Some(crate_root);
continue
} else if i == 1 && !token::Ident(ident.node).is_path_segment_keyword() {
let prev_name = path[0].node.name;
if prev_name == keywords::Extern.name() ||
prev_name == keywords::CrateRoot.name() &&
self.session.features.borrow().extern_absolute_paths {
// `::extern_crate::a::b`
let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
let crate_root =
self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
self.populate_module_if_necessary(crate_root);
module = Some(crate_root);
continue
}
}
}
@ -3015,6 +3020,7 @@ impl<'a> Resolver<'a> {
name == keywords::SelfValue.name() && i != 0 ||
name == keywords::SelfType.name() && i != 0 ||
name == keywords::Super.name() && i != 0 ||
name == keywords::Extern.name() && i != 0 ||
name == keywords::Crate.name() && i != 1 &&
path[0].node.name != keywords::CrateRoot.name() {
let name_str = if name == keywords::CrateRoot.name() {

View file

@ -604,17 +604,20 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
self.current_module = directive.parent;
let ImportDirective { ref module_path, span, .. } = *directive;
// Extern crate mode for absolute paths needs some
// special support for single-segment imports.
let extern_absolute_paths = self.session.features.borrow().extern_absolute_paths;
if module_path.len() == 1 && module_path[0].node.name == keywords::CrateRoot.name() {
// FIXME: Last path segment is treated specially in import resolution, so extern crate
// mode for absolute paths needs some special support for single-segment imports.
if module_path.len() == 1 && (module_path[0].node.name == keywords::CrateRoot.name() ||
module_path[0].node.name == keywords::Extern.name()) {
let is_extern = module_path[0].node.name == keywords::Extern.name() ||
self.session.features.borrow().extern_absolute_paths;
match directive.subclass {
GlobImport { .. } if extern_absolute_paths => {
GlobImport { .. } if is_extern => {
return Some((directive.span,
"cannot glob-import all possible crates".to_string()));
}
SingleImport { source, target, .. } => {
let crate_root = if source.name == keywords::Crate.name() {
let crate_root = if source.name == keywords::Crate.name() &&
module_path[0].node.name != keywords::Extern.name() {
if target.name == keywords::Crate.name() {
return Some((directive.span,
"crate root imports need to be explicitly named: \
@ -622,8 +625,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
} else {
Some(self.resolve_crate_root(source.ctxt.modern()))
}
} else if extern_absolute_paths &&
!token::Ident(source).is_path_segment_keyword() {
} else if is_extern && !token::Ident(source).is_path_segment_keyword() {
let crate_id =
self.crate_loader.resolve_crate_from_path(source.name, directive.span);
let crate_root =

View file

@ -450,6 +450,9 @@ declare_features! (
// Allows use of the :lifetime macro fragment specifier
(active, macro_lifetime_matcher, "1.24.0", Some(46895)),
// `extern` in paths
(active, extern_in_paths, "1.23.0", Some(44660)),
);
declare_features! (
@ -1790,6 +1793,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
if segment.identifier.name == keywords::Crate.name() {
gate_feature_post!(&self, crate_in_paths, segment.span,
"`crate` in paths is experimental");
} else if segment.identifier.name == keywords::Extern.name() {
gate_feature_post!(&self, extern_in_paths, segment.span,
"`extern` in paths is experimental");
}
}

View file

@ -1387,7 +1387,7 @@ impl<'a> Parser<'a> {
None
};
(ident, TraitItemKind::Const(ty, default), ast::Generics::default())
} else if self.token.is_path_start() {
} else if self.token.is_path_start() && !self.is_extern_non_path() {
// trait item macro.
// code copied from parse_macro_use_or_failure... abstraction!
let prev_span = self.prev_span;
@ -4037,6 +4037,10 @@ impl<'a> Parser<'a> {
self.token.is_keyword(keywords::Crate) && self.look_ahead(1, |t| t != &token::ModSep)
}
fn is_extern_non_path(&self) -> bool {
self.token.is_keyword(keywords::Extern) && self.look_ahead(1, |t| t != &token::ModSep)
}
fn eat_auto_trait(&mut self) -> bool {
if self.token.is_keyword(keywords::Auto)
&& self.look_ahead(1, |t| t.is_keyword(keywords::Trait))
@ -4152,10 +4156,12 @@ impl<'a> Parser<'a> {
// like a path (1 token), but it fact not a path.
// `union::b::c` - path, `union U { ... }` - not a path.
// `crate::b::c` - path, `crate struct S;` - not a path.
// `extern::b::c` - path, `extern crate c;` - not a path.
} else if self.token.is_path_start() &&
!self.token.is_qpath_start() &&
!self.is_union_item() &&
!self.is_crate_vis() {
!self.is_crate_vis() &&
!self.is_extern_non_path() {
let pth = self.parse_path(PathStyle::Expr)?;
if !self.eat(&token::Not) {
@ -5236,7 +5242,7 @@ impl<'a> Parser<'a> {
-> PResult<'a, (Ident, Vec<ast::Attribute>, ast::Generics,
ast::ImplItemKind)> {
// code copied from parse_macro_use_or_failure... abstraction!
if self.token.is_path_start() {
if self.token.is_path_start() && !self.is_extern_non_path() {
// Method macro.
let prev_span = self.prev_span;
@ -6238,7 +6244,8 @@ impl<'a> Parser<'a> {
return Ok(Some(item));
}
if self.eat_keyword(keywords::Extern) {
if self.check_keyword(keywords::Extern) && self.is_extern_non_path() {
self.bump(); // `extern`
if self.eat_keyword(keywords::Crate) {
return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?));
}

View file

@ -359,6 +359,7 @@ impl Token {
Some(id) => id.name == keywords::Super.name() ||
id.name == keywords::SelfValue.name() ||
id.name == keywords::SelfType.name() ||
id.name == keywords::Extern.name() ||
id.name == keywords::Crate.name() ||
id.name == keywords::DollarCrate.name(),
None => false,

View file

@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// This file was auto-generated using 'src/etc/generate-keyword-tests.py extern'
#![feature(extern_in_paths)]
fn main() {
let extern = "foo"; //~ error: expected pattern, found keyword `extern`
let extern = 0; //~ ERROR expected unit struct/variant or constant, found module `extern`
}

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[derive(Debug)]
pub struct S;
#[derive(Debug)]
pub struct Z;

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(extern_in_paths)]
use extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
fn main() {}

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(extern_in_paths)]
fn main() {
let s = extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
}

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(extern_in_paths)]
use extern::ycrate; //~ ERROR can't find crate for `ycrate`
fn main() {}

View file

@ -0,0 +1,23 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:xcrate.rs
#![feature(extern_in_paths)]
use extern; //~ ERROR unresolved import `extern`
//~^ NOTE no `extern` in the root
use extern::*; //~ ERROR unresolved import `extern::*`
//~^ NOTE cannot glob-import all possible crates
fn main() {
let s = extern::xcrate; //~ ERROR expected value, found module `extern::xcrate`
//~^ NOTE not a value
}

View file

@ -0,0 +1,31 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:xcrate.rs
#![feature(extern_in_paths)]
use extern::xcrate::Z;
fn f() {
use extern::xcrate;
use extern::xcrate as ycrate;
let s = xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = ycrate::Z;
assert_eq!(format!("{:?}", z), "Z");
}
fn main() {
let s = extern::xcrate::S;
assert_eq!(format!("{:?}", s), "S");
let z = Z;
assert_eq!(format!("{:?}", z), "Z");
}

View file

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct S;
fn main() {
let _ = extern::std::vec::Vec::new(); //~ ERROR `extern` in paths is experimental
}

View file

@ -0,0 +1,10 @@
error: `extern` in paths is experimental (see issue #44660)
--> $DIR/feature-gate-extern_in_paths.rs:14:13
|
14 | let _ = extern::std::vec::Vec::new(); //~ ERROR `extern` in paths is experimental
| ^^^^^^
|
= help: add #![feature(extern_in_paths)] to the crate attributes to enable
error: aborting due to previous error