about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-01-07 00:51:42 +0000
committerbors <bors@rust-lang.org>2018-01-07 00:51:42 +0000
commita704583d43dda19c2542845d8efd61bd4e1d82f1 (patch)
tree7f934100750e46fe9662f968413c5a99afd181a6
parent6828cf90146c7fefc4ba4f16dffe75f763f2d910 (diff)
parentef2b131144bc0d3d7814c9967e47a8d4e834caa5 (diff)
downloadrust-a704583d43dda19c2542845d8efd61bd4e1d82f1.tar.gz
rust-a704583d43dda19c2542845d8efd61bd4e1d82f1.zip
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
-rw-r--r--src/doc/unstable-book/src/language-features/crate_in_paths.md54
-rw-r--r--src/doc/unstable-book/src/language-features/extern_absolute_paths.md43
-rw-r--r--src/doc/unstable-book/src/language-features/extern_in_paths.md40
-rw-r--r--src/librustc_resolve/lib.rs26
-rw-r--r--src/librustc_resolve/resolve_imports.rs18
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/libsyntax/parse/parser.rs15
-rw-r--r--src/libsyntax/parse/token.rs1
-rw-r--r--src/test/compile-fail/keyword-extern-as-identifier.rs (renamed from src/test/parse-fail/keyword-extern-as-identifier.rs)6
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs23
-rw-r--r--src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs31
-rw-r--r--src/test/ui/feature-gate-extern_in_paths.rs15
-rw-r--r--src/test/ui/feature-gate-extern_in_paths.stderr10
17 files changed, 322 insertions, 26 deletions
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 00000000000..f1656993e87
--- /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 00000000000..f45c5053e8d
--- /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 00000000000..3ae6cc29df0
--- /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 9f00bf97deb..34e32dc9b64 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 e249ecfca58..98cbb660908 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 e5ef9393e7b..08eec0f9117 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 812e3c4967a..e7c648d5e37 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::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)?));
             }
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index bd4f7f9853d..2be93c07d5a 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
index 3bbe24ed56c..e5927d09b41 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 00000000000..c3da4a51872
--- /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 <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;
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 00000000000..7eba02ed444
--- /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 <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() {}
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 00000000000..4d09a05253e
--- /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 <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`
+}
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 00000000000..402d294b2e3
--- /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 <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() {}
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 00000000000..ebc42aa9d44
--- /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 <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
+}
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 00000000000..52b52b23c87
--- /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 <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");
+}
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 00000000000..3c01fcf6833
--- /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 <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
+}
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 00000000000..ac68e79e1ca
--- /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
+