Skip to content

Commit a704583

Browse files
committed
Auto merge of #47156 - petrochenkov:extpath, r=nikomatsakis
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
2 parents 6828cf9 + ef2b131 commit a704583

File tree

17 files changed

+322
-26
lines changed

17 files changed

+322
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# `crate_in_paths`
2+
3+
The tracking issue for this feature is: [#44660]
4+
5+
[#44660]: https://github.com/rust-lang/rust/issues/44660
6+
7+
------------------------
8+
9+
The `crate_in_paths` feature allows to explicitly refer to the crate root in absolute paths
10+
using keyword `crate`.
11+
12+
`crate` can be used *only* in absolute paths, i.e. either in `::crate::a::b::c` form or in `use`
13+
items where the starting `::` is added implicitly.
14+
Paths like `crate::a::b::c` are not accepted currently.
15+
16+
This feature is required in `feature(extern_absolute_paths)` mode to refer to any absolute path
17+
in the local crate (absolute paths refer to extern crates by default in that mode), but can be
18+
used without `feature(extern_absolute_paths)` as well.
19+
20+
```rust
21+
#![feature(crate_in_paths)]
22+
23+
// Imports, `::` is added implicitly
24+
use crate::m::f;
25+
use crate as root;
26+
27+
mod m {
28+
pub fn f() -> u8 { 1 }
29+
pub fn g() -> u8 { 2 }
30+
pub fn h() -> u8 { 3 }
31+
32+
// OK, visibilities implicitly add starting `::` as well, like imports
33+
pub(in crate::m) struct S;
34+
}
35+
36+
mod n
37+
{
38+
use crate::m::f;
39+
use crate as root;
40+
pub fn check() {
41+
assert_eq!(f(), 1);
42+
// `::` is required in non-import paths
43+
assert_eq!(::crate::m::g(), 2);
44+
assert_eq!(root::m::h(), 3);
45+
}
46+
}
47+
48+
fn main() {
49+
assert_eq!(f(), 1);
50+
assert_eq!(::crate::m::g(), 2);
51+
assert_eq!(root::m::h(), 3);
52+
n::check();
53+
}
54+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# `extern_absolute_paths`
2+
3+
The tracking issue for this feature is: [#44660]
4+
5+
[#44660]: https://github.com/rust-lang/rust/issues/44660
6+
7+
------------------------
8+
9+
The `extern_absolute_paths` feature enables mode allowing to refer to names from other crates
10+
"inline", without introducing `extern crate` items, using absolute paths like `::my_crate::a::b`.
11+
12+
`::my_crate::a::b` will resolve to path `a::b` in crate `my_crate`.
13+
14+
`feature(crate_in_paths)` can be used in `feature(extern_absolute_paths)` mode for referring
15+
to absolute paths in the local crate (`::crate::a::b`).
16+
17+
`feature(extern_in_paths)` provides the same effect by using keyword `extern` to refer to
18+
paths from other crates (`extern::my_crate::a::b`).
19+
20+
```rust,ignore
21+
#![feature(extern_absolute_paths)]
22+
23+
// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
24+
// options, or standard Rust distribution, or some other means.
25+
26+
use xcrate::Z;
27+
28+
fn f() {
29+
use xcrate;
30+
use xcrate as ycrate;
31+
let s = xcrate::S;
32+
assert_eq!(format!("{:?}", s), "S");
33+
let z = ycrate::Z;
34+
assert_eq!(format!("{:?}", z), "Z");
35+
}
36+
37+
fn main() {
38+
let s = ::xcrate::S;
39+
assert_eq!(format!("{:?}", s), "S");
40+
let z = Z;
41+
assert_eq!(format!("{:?}", z), "Z");
42+
}
43+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# `extern_in_paths`
2+
3+
The tracking issue for this feature is: [#44660]
4+
5+
[#44660]: https://github.com/rust-lang/rust/issues/44660
6+
7+
------------------------
8+
9+
The `extern_in_paths` feature allows to refer to names from other crates "inline", without
10+
introducing `extern crate` items, using keyword `extern`.
11+
12+
For example, `extern::my_crat::a::b` will resolve to path `a::b` in crate `my_crate`.
13+
14+
`feature(extern_absolute_paths)` mode provides the same effect by resolving absolute paths like
15+
`::my_crate::a::b` to paths from extern crates by default.
16+
17+
```rust,ignore
18+
#![feature(extern_in_paths)]
19+
20+
// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
21+
// options, or standard Rust distribution, or some other means.
22+
23+
use extern::xcrate::Z;
24+
25+
fn f() {
26+
use extern::xcrate;
27+
use extern::xcrate as ycrate;
28+
let s = xcrate::S;
29+
assert_eq!(format!("{:?}", s), "S");
30+
let z = ycrate::Z;
31+
assert_eq!(format!("{:?}", z), "Z");
32+
}
33+
34+
fn main() {
35+
let s = extern::xcrate::S;
36+
assert_eq!(format!("{:?}", s), "S");
37+
let z = Z;
38+
assert_eq!(format!("{:?}", z), "Z");
39+
}
40+
```

src/librustc_resolve/lib.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -2982,6 +2982,8 @@ impl<'a> Resolver<'a> {
29822982
let msg = "There are too many initial `super`s.".to_string();
29832983
return PathResult::Failed(ident.span, msg, false);
29842984
}
2985+
} else if i == 0 && ns == TypeNS && name == keywords::Extern.name() {
2986+
continue;
29852987
}
29862988
allow_super = false;
29872989

@@ -2996,16 +2998,19 @@ impl<'a> Resolver<'a> {
29962998
// `$crate::a::b`
29972999
module = Some(self.resolve_crate_root(ident.node.ctxt));
29983000
continue
2999-
} else if i == 1 && self.session.features.borrow().extern_absolute_paths &&
3000-
path[0].node.name == keywords::CrateRoot.name() &&
3001-
!token::Ident(ident.node).is_path_segment_keyword() {
3002-
// `::extern_crate::a::b`
3003-
let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
3004-
let crate_root =
3005-
self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
3006-
self.populate_module_if_necessary(crate_root);
3007-
module = Some(crate_root);
3008-
continue
3001+
} else if i == 1 && !token::Ident(ident.node).is_path_segment_keyword() {
3002+
let prev_name = path[0].node.name;
3003+
if prev_name == keywords::Extern.name() ||
3004+
prev_name == keywords::CrateRoot.name() &&
3005+
self.session.features.borrow().extern_absolute_paths {
3006+
// `::extern_crate::a::b`
3007+
let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
3008+
let crate_root =
3009+
self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
3010+
self.populate_module_if_necessary(crate_root);
3011+
module = Some(crate_root);
3012+
continue
3013+
}
30093014
}
30103015
}
30113016

@@ -3015,6 +3020,7 @@ impl<'a> Resolver<'a> {
30153020
name == keywords::SelfValue.name() && i != 0 ||
30163021
name == keywords::SelfType.name() && i != 0 ||
30173022
name == keywords::Super.name() && i != 0 ||
3023+
name == keywords::Extern.name() && i != 0 ||
30183024
name == keywords::Crate.name() && i != 1 &&
30193025
path[0].node.name != keywords::CrateRoot.name() {
30203026
let name_str = if name == keywords::CrateRoot.name() {

src/librustc_resolve/resolve_imports.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -604,26 +604,28 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
604604
self.current_module = directive.parent;
605605
let ImportDirective { ref module_path, span, .. } = *directive;
606606

607-
// Extern crate mode for absolute paths needs some
608-
// special support for single-segment imports.
609-
let extern_absolute_paths = self.session.features.borrow().extern_absolute_paths;
610-
if module_path.len() == 1 && module_path[0].node.name == keywords::CrateRoot.name() {
607+
// FIXME: Last path segment is treated specially in import resolution, so extern crate
608+
// mode for absolute paths needs some special support for single-segment imports.
609+
if module_path.len() == 1 && (module_path[0].node.name == keywords::CrateRoot.name() ||
610+
module_path[0].node.name == keywords::Extern.name()) {
611+
let is_extern = module_path[0].node.name == keywords::Extern.name() ||
612+
self.session.features.borrow().extern_absolute_paths;
611613
match directive.subclass {
612-
GlobImport { .. } if extern_absolute_paths => {
614+
GlobImport { .. } if is_extern => {
613615
return Some((directive.span,
614616
"cannot glob-import all possible crates".to_string()));
615617
}
616618
SingleImport { source, target, .. } => {
617-
let crate_root = if source.name == keywords::Crate.name() {
619+
let crate_root = if source.name == keywords::Crate.name() &&
620+
module_path[0].node.name != keywords::Extern.name() {
618621
if target.name == keywords::Crate.name() {
619622
return Some((directive.span,
620623
"crate root imports need to be explicitly named: \
621624
`use crate as name;`".to_string()));
622625
} else {
623626
Some(self.resolve_crate_root(source.ctxt.modern()))
624627
}
625-
} else if extern_absolute_paths &&
626-
!token::Ident(source).is_path_segment_keyword() {
628+
} else if is_extern && !token::Ident(source).is_path_segment_keyword() {
627629
let crate_id =
628630
self.crate_loader.resolve_crate_from_path(source.name, directive.span);
629631
let crate_root =

src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,9 @@ declare_features! (
450450

451451
// Allows use of the :lifetime macro fragment specifier
452452
(active, macro_lifetime_matcher, "1.24.0", Some(46895)),
453+
454+
// `extern` in paths
455+
(active, extern_in_paths, "1.23.0", Some(44660)),
453456
);
454457

455458
declare_features! (
@@ -1790,6 +1793,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
17901793
if segment.identifier.name == keywords::Crate.name() {
17911794
gate_feature_post!(&self, crate_in_paths, segment.span,
17921795
"`crate` in paths is experimental");
1796+
} else if segment.identifier.name == keywords::Extern.name() {
1797+
gate_feature_post!(&self, extern_in_paths, segment.span,
1798+
"`extern` in paths is experimental");
17931799
}
17941800
}
17951801

src/libsyntax/parse/parser.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ impl<'a> Parser<'a> {
13871387
None
13881388
};
13891389
(ident, TraitItemKind::Const(ty, default), ast::Generics::default())
1390-
} else if self.token.is_path_start() {
1390+
} else if self.token.is_path_start() && !self.is_extern_non_path() {
13911391
// trait item macro.
13921392
// code copied from parse_macro_use_or_failure... abstraction!
13931393
let prev_span = self.prev_span;
@@ -4037,6 +4037,10 @@ impl<'a> Parser<'a> {
40374037
self.token.is_keyword(keywords::Crate) && self.look_ahead(1, |t| t != &token::ModSep)
40384038
}
40394039

4040+
fn is_extern_non_path(&self) -> bool {
4041+
self.token.is_keyword(keywords::Extern) && self.look_ahead(1, |t| t != &token::ModSep)
4042+
}
4043+
40404044
fn eat_auto_trait(&mut self) -> bool {
40414045
if self.token.is_keyword(keywords::Auto)
40424046
&& self.look_ahead(1, |t| t.is_keyword(keywords::Trait))
@@ -4152,10 +4156,12 @@ impl<'a> Parser<'a> {
41524156
// like a path (1 token), but it fact not a path.
41534157
// `union::b::c` - path, `union U { ... }` - not a path.
41544158
// `crate::b::c` - path, `crate struct S;` - not a path.
4159+
// `extern::b::c` - path, `extern crate c;` - not a path.
41554160
} else if self.token.is_path_start() &&
41564161
!self.token.is_qpath_start() &&
41574162
!self.is_union_item() &&
4158-
!self.is_crate_vis() {
4163+
!self.is_crate_vis() &&
4164+
!self.is_extern_non_path() {
41594165
let pth = self.parse_path(PathStyle::Expr)?;
41604166

41614167
if !self.eat(&token::Not) {
@@ -5236,7 +5242,7 @@ impl<'a> Parser<'a> {
52365242
-> PResult<'a, (Ident, Vec<ast::Attribute>, ast::Generics,
52375243
ast::ImplItemKind)> {
52385244
// code copied from parse_macro_use_or_failure... abstraction!
5239-
if self.token.is_path_start() {
5245+
if self.token.is_path_start() && !self.is_extern_non_path() {
52405246
// Method macro.
52415247

52425248
let prev_span = self.prev_span;
@@ -6238,7 +6244,8 @@ impl<'a> Parser<'a> {
62386244
return Ok(Some(item));
62396245
}
62406246

6241-
if self.eat_keyword(keywords::Extern) {
6247+
if self.check_keyword(keywords::Extern) && self.is_extern_non_path() {
6248+
self.bump(); // `extern`
62426249
if self.eat_keyword(keywords::Crate) {
62436250
return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?));
62446251
}

src/libsyntax/parse/token.rs

+1
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ impl Token {
359359
Some(id) => id.name == keywords::Super.name() ||
360360
id.name == keywords::SelfValue.name() ||
361361
id.name == keywords::SelfType.name() ||
362+
id.name == keywords::Extern.name() ||
362363
id.name == keywords::Crate.name() ||
363364
id.name == keywords::DollarCrate.name(),
364365
None => false,

src/test/parse-fail/keyword-extern-as-identifier.rs src/test/compile-fail/keyword-extern-as-identifier.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
12-
13-
// This file was auto-generated using 'src/etc/generate-keyword-tests.py extern'
11+
#![feature(extern_in_paths)]
1412

1513
fn main() {
16-
let extern = "foo"; //~ error: expected pattern, found keyword `extern`
14+
let extern = 0; //~ ERROR expected unit struct/variant or constant, found module `extern`
1715
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[derive(Debug)]
12+
pub struct S;
13+
14+
#[derive(Debug)]
15+
pub struct Z;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(extern_in_paths)]
12+
13+
use extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(extern_in_paths)]
12+
13+
fn main() {
14+
let s = extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(extern_in_paths)]
12+
13+
use extern::ycrate; //~ ERROR can't find crate for `ycrate`
14+
15+
fn main() {}

0 commit comments

Comments
 (0)