diff --git a/src/doc/unstable-book/src/language-features/crate_in_paths.md b/src/doc/unstable-book/src/language-features/crate_in_paths.md new file mode 100644 index 0000000000000..f1656993e87a5 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/crate_in_paths.md @@ -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(); +} +``` diff --git a/src/doc/unstable-book/src/language-features/extern_absolute_paths.md b/src/doc/unstable-book/src/language-features/extern_absolute_paths.md new file mode 100644 index 0000000000000..f45c5053e8dbc --- /dev/null +++ b/src/doc/unstable-book/src/language-features/extern_absolute_paths.md @@ -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"); +} +``` diff --git a/src/doc/unstable-book/src/language-features/extern_in_paths.md b/src/doc/unstable-book/src/language-features/extern_in_paths.md new file mode 100644 index 0000000000000..3ae6cc29df0b8 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/extern_in_paths.md @@ -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"); +} +``` diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 9f00bf97deba1..34e32dc9b640f 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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() { diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index e249ecfca588e..98cbb6609085c 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -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 = diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e5ef9393e7bf7..08eec0f9117f8 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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"); } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 812e3c4967a9d..e7c648d5e37d3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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::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)?)); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index bd4f7f9853d76..2be93c07d5ad7 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -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, diff --git a/src/test/parse-fail/keyword-extern-as-identifier.rs b/src/test/compile-fail/keyword-extern-as-identifier.rs similarity index 71% rename from src/test/parse-fail/keyword-extern-as-identifier.rs rename to src/test/compile-fail/keyword-extern-as-identifier.rs index 3bbe24ed56c63..e5927d09b4165 100644 --- a/src/test/parse-fail/keyword-extern-as-identifier.rs +++ b/src/test/compile-fail/keyword-extern-as-identifier.rs @@ -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` } diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs new file mode 100644 index 0000000000000..c3da4a518720d --- /dev/null +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs @@ -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 or the MIT license +// , 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; diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs new file mode 100644 index 0000000000000..7eba02ed444bb --- /dev/null +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs new file mode 100644 index 0000000000000..4d09a05253ec2 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs @@ -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 or the MIT license +// , 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` +} diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs new file mode 100644 index 0000000000000..402d294b2e324 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs new file mode 100644 index 0000000000000..ebc42aa9d4492 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs b/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs new file mode 100644 index 0000000000000..52b52b23c874a --- /dev/null +++ b/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs @@ -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 or the MIT license +// , 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"); +} diff --git a/src/test/ui/feature-gate-extern_in_paths.rs b/src/test/ui/feature-gate-extern_in_paths.rs new file mode 100644 index 0000000000000..3c01fcf68331b --- /dev/null +++ b/src/test/ui/feature-gate-extern_in_paths.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/ui/feature-gate-extern_in_paths.stderr b/src/test/ui/feature-gate-extern_in_paths.stderr new file mode 100644 index 0000000000000..ac68e79e1ca0f --- /dev/null +++ b/src/test/ui/feature-gate-extern_in_paths.stderr @@ -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 +