Skip to content

Commit

Permalink
Auto merge of #49789 - petrochenkov:prelext, r=nikomatsakis
Browse files Browse the repository at this point in the history
Module experiments: Add one more prelude layer for extern crate names passed with `--extern`

Implements one item from https://internals.rust-lang.org/t/the-great-module-adventure-continues/6678/183

When some name is looked up in lexical scope (`name`, i.e. not module-relative scope `some_mod::name` or `::name`), it's searched roughly in the next order:
- local variables
- items in unnamed blocks
- items in the current module
- ✨ NEW! ✨ crate names passed with `--extern` ("extern prelude")
- standard library prelude (`Vec`, `drop`)
- language prelude (built-in types like `u8`, `str`, etc)

The last two layers contain a limited set of names controlled by us and not arbitrary user-defined names like upper layers. We want to be able to add new names into these two layers without breaking user code, so "extern prelude" names have higher priority than std prelude and built-in types.
This is a one-time breaking change, that's why it would be nice to run this through crater.
Practical impact is expected to be minimal though due to stylistic reasons (there are not many `Uppercase` crates) and due to the way how primitive types are resolved (#32131).
  • Loading branch information
bors committed May 1, 2018
2 parents 0d8321b + c1492fe commit 4d7bbdd
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 8 deletions.
36 changes: 29 additions & 7 deletions src/librustc_resolve/lib.rs
Expand Up @@ -1407,6 +1407,7 @@ pub struct Resolver<'a> {
graph_root: Module<'a>,

prelude: Option<Module<'a>>,
extern_prelude: FxHashSet<Name>,

/// n.b. This is used only for better diagnostics, not name resolution itself.
has_self: FxHashSet<DefId>,
Expand Down Expand Up @@ -1715,6 +1716,7 @@ impl<'a> Resolver<'a> {
// AST.
graph_root,
prelude: None,
extern_prelude: session.opts.externs.iter().map(|kv| Symbol::intern(kv.0)).collect(),

has_self: FxHashSet(),
field_names: FxHashMap(),
Expand Down Expand Up @@ -1970,13 +1972,32 @@ impl<'a> Resolver<'a> {
}
}

match self.prelude {
Some(prelude) if !module.no_implicit_prelude => {
self.resolve_ident_in_module_unadjusted(prelude, ident, ns, false, false, path_span)
.ok().map(LexicalScopeBinding::Item)
if !module.no_implicit_prelude {
// `record_used` means that we don't try to load crates during speculative resolution
if record_used && ns == TypeNS && self.extern_prelude.contains(&ident.name) {
if !self.session.features_untracked().extern_prelude {
feature_err(&self.session.parse_sess, "extern_prelude",
ident.span, GateIssue::Language,
"access to extern crates through prelude is experimental").emit();
}

let crate_id = self.crate_loader.process_path_extern(ident.name, ident.span);
let crate_root = self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
self.populate_module_if_necessary(crate_root);

let binding = (crate_root, ty::Visibility::Public,
ident.span, Mark::root()).to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
}
if let Some(prelude) = self.prelude {
if let Ok(binding) = self.resolve_ident_in_module_unadjusted(prelude, ident, ns,
false, false, path_span) {
return Some(LexicalScopeBinding::Item(binding));
}
}
_ => None,
}

None
}

fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, span: &mut Span)
Expand Down Expand Up @@ -3587,8 +3608,9 @@ impl<'a> Resolver<'a> {
// We can see through blocks
} else {
// Items from the prelude
if let Some(prelude) = self.prelude {
if !module.no_implicit_prelude {
if !module.no_implicit_prelude {
names.extend(self.extern_prelude.iter().cloned());
if let Some(prelude) = self.prelude {
add_module_candidates(prelude, &mut names);
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/libsyntax/feature_gate.rs
Expand Up @@ -391,7 +391,7 @@ declare_features! (
(active, generic_associated_types, "1.23.0", Some(44265), None),

// Resolve absolute paths as paths from other crates
(active, extern_absolute_paths, "1.24.0", Some(44660), None),
(active, extern_absolute_paths, "1.24.0", Some(44660), Some(Edition::Edition2018)),

// `foo.rs` as an alternative to `foo/mod.rs`
(active, non_modrs_mods, "1.24.0", Some(44660), Some(Edition::Edition2018)),
Expand Down Expand Up @@ -460,6 +460,9 @@ declare_features! (

// #[doc(alias = "...")]
(active, doc_alias, "1.27.0", Some(50146), None),

// Access to crate names passed via `--extern` through prelude
(active, extern_prelude, "1.27.0", Some(44660), Some(Edition::Edition2018)),
);

declare_features! (
Expand Down
12 changes: 12 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/Makefile
@@ -0,0 +1,12 @@
-include ../tools.mk

all:
$(RUSTC) ep-lib.rs
$(RUSTC) ep-vec.rs

$(RUSTC) basic.rs --extern ep_lib=$(TMPDIR)/libep_lib.rlib
$(RUSTC) shadow-mod.rs --extern ep_lib=$(TMPDIR)/libep_lib.rlib
$(RUSTC) shadow-prelude.rs --extern Vec=$(TMPDIR)/libep_vec.rlib
$(RUSTC) feature-gate.rs --extern ep_lib=$(TMPDIR)/libep_lib.rlib 2>&1 | $(CGREP) "access to extern crates through prelude is experimental"
$(RUSTC) relative-only.rs --extern ep_lib=$(TMPDIR)/libep_lib.rlib 2>&1 | $(CGREP) "unresolved import"
$(RUSTC) relative-only.rs --extern ep_lib=$(TMPDIR)/libep_lib.rlib 2>&1 | $(CGREP) "failed to resolve"
16 changes: 16 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/basic.rs
@@ -0,0 +1,16 @@
// Copyright 2018 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_prelude)]

fn main() {
let s = ep_lib::S; // It works
s.external();
}
17 changes: 17 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/ep-lib.rs
@@ -0,0 +1,17 @@
// 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.

#![crate_type = "rlib"]

pub struct S;

impl S {
pub fn external(&self) {}
}
13 changes: 13 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/ep-vec.rs
@@ -0,0 +1,13 @@
// 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.

#![crate_type = "rlib"]

pub fn new(arg1: f32, arg2: ()) {}
13 changes: 13 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/feature-gate.rs
@@ -0,0 +1,13 @@
// Copyright 2018 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.

fn main() {
let s = ep_lib::S; // Feature error
}
19 changes: 19 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/relative-only.rs
@@ -0,0 +1,19 @@
// Copyright 2018 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.

// Extern prelude names are not available by absolute paths

#![feature(extern_prelude)]

use ep_lib::S;

fn main() {
let s = ::ep_lib::S;
}
24 changes: 24 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/shadow-mod.rs
@@ -0,0 +1,24 @@
// Copyright 2018 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.

// Local module shadows `ep_lib` from extern prelude

mod ep_lib {
pub struct S;

impl S {
pub fn internal(&self) {}
}
}

fn main() {
let s = ep_lib::S;
s.internal(); // OK
}
17 changes: 17 additions & 0 deletions src/test/run-make-fulldeps/extern-prelude/shadow-prelude.rs
@@ -0,0 +1,17 @@
// Copyright 2018 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.

// Extern prelude shadows standard library prelude

#![feature(extern_prelude)]

fn main() {
let x = Vec::new(0f32, ()); // OK
}
21 changes: 21 additions & 0 deletions src/test/run-pass/extern-prelude-no-speculative.rs
@@ -0,0 +1,21 @@
// Copyright 2014 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.

// compile-flags: --extern LooksLikeExternCrate=/path/to/nowhere

mod m {
pub struct LooksLikeExternCrate;
}

fn main() {
// OK, speculative resolution for `unused_qualifications` doesn't try
// to resolve this as an extern crate and load that crate
let s = m::LooksLikeExternCrate {};
}
11 changes: 11 additions & 0 deletions src/test/ui/feature-gate-extern_prelude.rs
@@ -0,0 +1,11 @@
// Copyright 2018 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.

can-only-test-this-in-run-make-fulldeps //~ ERROR expected one of `!` or `::`, found `-`
8 changes: 8 additions & 0 deletions src/test/ui/feature-gate-extern_prelude.stderr
@@ -0,0 +1,8 @@
error: expected one of `!` or `::`, found `-`
--> $DIR/feature-gate-extern_prelude.rs:11:4
|
LL | can-only-test-this-in-run-make-fulldeps //~ ERROR expected one of `!` or `::`, found `-`
| ^ expected one of `!` or `::` here

error: aborting due to previous error

0 comments on commit 4d7bbdd

Please sign in to comment.