Skip to content

Commit

Permalink
Auto merge of #47156 - petrochenkov:extpath, r=nikomatsakis
Browse files Browse the repository at this point in the history
Support `extern` in paths

Implement the primary alternative to #46613 + #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 #44660
r? @nikomatsakis
  • Loading branch information
bors committed Jan 7, 2018
2 parents 6828cf9 + ef2b131 commit a704583
Show file tree
Hide file tree
Showing 17 changed files with 322 additions and 26 deletions.
54 changes: 54 additions & 0 deletions src/doc/unstable-book/src/language-features/crate_in_paths.md
Original file line number Diff line number Diff line change
@@ -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();
}
```
Original file line number Diff line number Diff line change
@@ -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");
}
```
40 changes: 40 additions & 0 deletions src/doc/unstable-book/src/language-features/extern_in_paths.md
Original file line number Diff line number Diff line change
@@ -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");
}
```
26 changes: 16 additions & 10 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
}
}
}

Expand All @@ -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() {
Expand Down
18 changes: 10 additions & 8 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,26 +604,28 @@ 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: \
`use crate as name;`".to_string()));
} 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 =
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! (
Expand Down Expand Up @@ -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");
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)?));
}
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs
Original file line number Diff line number Diff line change
@@ -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;
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs
Original file line number Diff line number Diff line change
@@ -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() {}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs
Original file line number Diff line number Diff line change
@@ -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`
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading

0 comments on commit a704583

Please sign in to comment.