Auto merge of #32167 - jseyfried:refactor_prelude, r=nikomatsakis

resolve: Refactor how the prelude is handled

This PR refactors how the prelude is handled in `resolve`.

Instead of importing names from the prelude into each module's `resolutions`, this PR adds a new field `prelude: RefCell<Option<Module>>` to `ModuleS` that is set during import resolution but used only when resolving in a lexical scope (i.e. the scope of an initial segment of a relative path).

r? @nikomatsakis
This commit is contained in:
bors 2016-03-25 15:53:16 -07:00
commit a1e29daf1a
3 changed files with 70 additions and 90 deletions

View File

@ -22,7 +22,6 @@ use {NameBinding, NameBindingKind};
use module_to_string; use module_to_string;
use ParentLink::{ModuleParentLink, BlockParentLink}; use ParentLink::{ModuleParentLink, BlockParentLink};
use Resolver; use Resolver;
use resolve_imports::Shadowable;
use {resolve_error, resolve_struct_error, ResolutionError}; use {resolve_error, resolve_struct_error, ResolutionError};
use rustc::middle::cstore::{CrateStore, ChildItem, DlDef, DlField, DlImpl}; use rustc::middle::cstore::{CrateStore, ChildItem, DlDef, DlField, DlImpl};
@ -161,14 +160,9 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
}; };
// Build up the import directives. // Build up the import directives.
let shadowable = item.attrs.iter().any(|attr| { let is_prelude = item.attrs.iter().any(|attr| {
attr.name() == special_idents::prelude_import.name.as_str() attr.name() == special_idents::prelude_import.name.as_str()
}); });
let shadowable = if shadowable {
Shadowable::Always
} else {
Shadowable::Never
};
match view_path.node { match view_path.node {
ViewPathSimple(binding, ref full_path) => { ViewPathSimple(binding, ref full_path) => {
@ -186,7 +180,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
view_path.span, view_path.span,
item.id, item.id,
is_public, is_public,
shadowable); is_prelude);
} }
ViewPathList(_, ref source_items) => { ViewPathList(_, ref source_items) => {
// Make sure there's at most one `mod` import in the list. // Make sure there's at most one `mod` import in the list.
@ -237,7 +231,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
source_item.span, source_item.span,
source_item.node.id(), source_item.node.id(),
is_public, is_public,
shadowable); is_prelude);
} }
} }
ViewPathGlob(_) => { ViewPathGlob(_) => {
@ -247,7 +241,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
view_path.span, view_path.span,
item.id, item.id,
is_public, is_public,
shadowable); is_prelude);
} }
} }
parent parent
@ -631,7 +625,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
span: Span, span: Span,
id: NodeId, id: NodeId,
is_public: bool, is_public: bool,
shadowable: Shadowable) { is_prelude: bool) {
// Bump the reference count on the name. Or, if this is a glob, set // Bump the reference count on the name. Or, if this is a glob, set
// the appropriate flag. // the appropriate flag.
@ -640,15 +634,18 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
module_.increment_outstanding_references_for(target, ValueNS, is_public); module_.increment_outstanding_references_for(target, ValueNS, is_public);
module_.increment_outstanding_references_for(target, TypeNS, is_public); module_.increment_outstanding_references_for(target, TypeNS, is_public);
} }
GlobImport => { GlobImport if !is_prelude => {
// Set the glob flag. This tells us that we don't know the // Set the glob flag. This tells us that we don't know the
// module's exports ahead of time. // module's exports ahead of time.
module_.inc_glob_count(is_public) module_.inc_glob_count(is_public)
} }
// Prelude imports are not included in the glob counts since they do not get added to
// `resolved_globs` -- they are handled separately in `resolve_imports`.
GlobImport => {}
} }
let directive = let directive =
ImportDirective::new(module_path, subclass, span, id, is_public, shadowable); ImportDirective::new(module_path, subclass, span, id, is_public, is_prelude);
module_.add_import_directive(directive); module_.add_import_directive(directive);
self.unresolved_imports += 1; self.unresolved_imports += 1;
} }

View File

@ -349,7 +349,8 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
if let Some(sp) = resolver.ast_map.span_if_local(did) { if let Some(sp) = resolver.ast_map.span_if_local(did) {
err.span_note(sp, "constant defined here"); err.span_note(sp, "constant defined here");
} }
if let Success(binding) = resolver.current_module.resolve_name(name, ValueNS, true) { if let Some(binding) = resolver.current_module
.resolve_name_in_lexical_scope(name, ValueNS) {
if binding.is_import() { if binding.is_import() {
err.span_note(binding.span.unwrap(), "constant imported here"); err.span_note(binding.span.unwrap(), "constant imported here");
} }
@ -820,7 +821,7 @@ pub struct ModuleS<'a> {
// entry block for `f`. // entry block for `f`.
module_children: RefCell<NodeMap<Module<'a>>>, module_children: RefCell<NodeMap<Module<'a>>>,
shadowed_traits: RefCell<Vec<&'a NameBinding<'a>>>, prelude: RefCell<Option<Module<'a>>>,
glob_importers: RefCell<Vec<(Module<'a>, &'a ImportDirective)>>, glob_importers: RefCell<Vec<(Module<'a>, &'a ImportDirective)>>,
resolved_globs: RefCell<(Vec<Module<'a>> /* public */, Vec<Module<'a>> /* private */)>, resolved_globs: RefCell<(Vec<Module<'a>> /* public */, Vec<Module<'a>> /* private */)>,
@ -855,7 +856,7 @@ impl<'a> ModuleS<'a> {
resolutions: RefCell::new(HashMap::new()), resolutions: RefCell::new(HashMap::new()),
unresolved_imports: RefCell::new(Vec::new()), unresolved_imports: RefCell::new(Vec::new()),
module_children: RefCell::new(NodeMap()), module_children: RefCell::new(NodeMap()),
shadowed_traits: RefCell::new(Vec::new()), prelude: RefCell::new(None),
glob_importers: RefCell::new(Vec::new()), glob_importers: RefCell::new(Vec::new()),
resolved_globs: RefCell::new((Vec::new(), Vec::new())), resolved_globs: RefCell::new((Vec::new(), Vec::new())),
public_glob_count: Cell::new(0), public_glob_count: Cell::new(0),
@ -932,8 +933,7 @@ bitflags! {
// Variants are considered `PUBLIC`, but some of them live in private enums. // Variants are considered `PUBLIC`, but some of them live in private enums.
// We need to track them to prohibit reexports like `pub use PrivEnum::Variant`. // We need to track them to prohibit reexports like `pub use PrivEnum::Variant`.
const PRIVATE_VARIANT = 1 << 2, const PRIVATE_VARIANT = 1 << 2,
const PRELUDE = 1 << 3, const GLOB_IMPORTED = 1 << 3,
const GLOB_IMPORTED = 1 << 4,
} }
} }
@ -1537,13 +1537,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
module: Module<'a>, module: Module<'a>,
name: Name, name: Name,
namespace: Namespace, namespace: Namespace,
allow_private_imports: bool, use_lexical_scope: bool,
record_used: bool) record_used: bool)
-> ResolveResult<&'a NameBinding<'a>> { -> ResolveResult<&'a NameBinding<'a>> {
debug!("(resolving name in module) resolving `{}` in `{}`", name, module_to_string(module)); debug!("(resolving name in module) resolving `{}` in `{}`", name, module_to_string(module));
build_reduced_graph::populate_module_if_necessary(self, module); build_reduced_graph::populate_module_if_necessary(self, module);
module.resolve_name(name, namespace, allow_private_imports).and_then(|binding| { match use_lexical_scope {
true => module.resolve_name_in_lexical_scope(name, namespace)
.map(Success).unwrap_or(Failed(None)),
false => module.resolve_name(name, namespace, false),
}.and_then(|binding| {
if record_used { if record_used {
self.record_use(name, namespace, binding); self.record_use(name, namespace, binding);
} }
@ -2962,7 +2966,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if name_path.len() == 1 { if name_path.len() == 1 {
match this.primitive_type_table.primitive_types.get(last_name) { match this.primitive_type_table.primitive_types.get(last_name) {
Some(_) => None, Some(_) => None,
None => this.current_module.resolve_name(*last_name, TypeNS, true).success() None => this.current_module.resolve_name_in_lexical_scope(*last_name, TypeNS)
.and_then(NameBinding::module) .and_then(NameBinding::module)
} }
} else { } else {
@ -3019,7 +3023,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Look for a method in the current self type's impl module. // Look for a method in the current self type's impl module.
if let Some(module) = get_module(self, path.span, &name_path) { if let Some(module) = get_module(self, path.span, &name_path) {
if let Success(binding) = module.resolve_name(name, ValueNS, true) { if let Some(binding) = module.resolve_name_in_lexical_scope(name, ValueNS) {
if let Some(Def::Method(did)) = binding.def() { if let Some(Def::Method(did)) = binding.def() {
if is_static_method(self, did) { if is_static_method(self, did) {
return StaticMethod(path_names_to_string(&path, 0)); return StaticMethod(path_names_to_string(&path, 0));
@ -3336,33 +3340,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
} }
// Look for trait children. // Look for trait children.
build_reduced_graph::populate_module_if_necessary(self, &search_module); let mut search_in_module = |module: Module<'a>| module.for_each_child(|_, ns, binding| {
search_module.for_each_child(|_, ns, name_binding| {
if ns != TypeNS { return } if ns != TypeNS { return }
let trait_def_id = match name_binding.def() { let trait_def_id = match binding.def() {
Some(Def::Trait(trait_def_id)) => trait_def_id, Some(Def::Trait(trait_def_id)) => trait_def_id,
Some(..) | None => return, Some(..) | None => return,
}; };
if self.trait_item_map.contains_key(&(name, trait_def_id)) { if self.trait_item_map.contains_key(&(name, trait_def_id)) {
add_trait_info(&mut found_traits, trait_def_id, name); add_trait_info(&mut found_traits, trait_def_id, name);
let trait_name = self.get_trait_name(trait_def_id); let trait_name = self.get_trait_name(trait_def_id);
self.record_use(trait_name, TypeNS, name_binding);
}
});
// Look for shadowed traits.
for binding in search_module.shadowed_traits.borrow().iter() {
let did = binding.def().unwrap().def_id();
if self.trait_item_map.contains_key(&(name, did)) {
add_trait_info(&mut found_traits, did, name);
let trait_name = self.get_trait_name(did);
self.record_use(trait_name, TypeNS, binding); self.record_use(trait_name, TypeNS, binding);
} }
} });
search_in_module(search_module);
match search_module.parent_link { match search_module.parent_link {
NoParentLink | ModuleParentLink(..) => break, NoParentLink | ModuleParentLink(..) => {
search_module.prelude.borrow().map(search_in_module);
break;
}
BlockParentLink(parent_module, _) => { BlockParentLink(parent_module, _) => {
search_module = parent_module; search_module = parent_module;
} }

View File

@ -57,13 +57,6 @@ impl ImportDirectiveSubclass {
} }
} }
/// Whether an import can be shadowed by another import.
#[derive(Debug,PartialEq,Clone,Copy)]
pub enum Shadowable {
Always,
Never,
}
/// One import directive. /// One import directive.
#[derive(Debug,Clone)] #[derive(Debug,Clone)]
pub struct ImportDirective { pub struct ImportDirective {
@ -72,7 +65,7 @@ pub struct ImportDirective {
pub span: Span, pub span: Span,
pub id: NodeId, pub id: NodeId,
pub is_public: bool, // see note in ImportResolutionPerNamespace about how to use this pub is_public: bool, // see note in ImportResolutionPerNamespace about how to use this
pub shadowable: Shadowable, pub is_prelude: bool,
} }
impl ImportDirective { impl ImportDirective {
@ -81,7 +74,7 @@ impl ImportDirective {
span: Span, span: Span,
id: NodeId, id: NodeId,
is_public: bool, is_public: bool,
shadowable: Shadowable) is_prelude: bool)
-> ImportDirective { -> ImportDirective {
ImportDirective { ImportDirective {
module_path: module_path, module_path: module_path,
@ -89,7 +82,7 @@ impl ImportDirective {
span: span, span: span,
id: id, id: id,
is_public: is_public, is_public: is_public,
shadowable: shadowable, is_prelude: is_prelude,
} }
} }
@ -105,9 +98,6 @@ impl ImportDirective {
if let GlobImport = self.subclass { if let GlobImport = self.subclass {
modifiers = modifiers | DefModifiers::GLOB_IMPORTED; modifiers = modifiers | DefModifiers::GLOB_IMPORTED;
} }
if self.shadowable == Shadowable::Always {
modifiers = modifiers | DefModifiers::PRELUDE;
}
NameBinding { NameBinding {
kind: NameBindingKind::Import { kind: NameBindingKind::Import {
@ -135,44 +125,36 @@ pub struct NameResolution<'a> {
impl<'a> NameResolution<'a> { impl<'a> NameResolution<'a> {
fn try_define(&mut self, binding: &'a NameBinding<'a>) -> Result<(), &'a NameBinding<'a>> { fn try_define(&mut self, binding: &'a NameBinding<'a>) -> Result<(), &'a NameBinding<'a>> {
match self.binding { if let Some(old_binding) = self.binding {
Some(old_binding) if !old_binding.defined_with(DefModifiers::PRELUDE) => { if binding.defined_with(DefModifiers::GLOB_IMPORTED) {
if binding.defined_with(DefModifiers::GLOB_IMPORTED) { self.duplicate_globs.push(binding);
self.duplicate_globs.push(binding); } else if old_binding.defined_with(DefModifiers::GLOB_IMPORTED) {
} else if old_binding.defined_with(DefModifiers::GLOB_IMPORTED) { self.duplicate_globs.push(old_binding);
self.duplicate_globs.push(old_binding); self.binding = Some(binding);
self.binding = Some(binding); } else {
} else { return Err(old_binding);
return Err(old_binding);
}
} }
_ => self.binding = Some(binding), } else {
self.binding = Some(binding);
} }
Ok(()) Ok(())
} }
// Returns the resolution of the name assuming no more globs will define it.
fn result(&self, allow_private_imports: bool) -> ResolveResult<&'a NameBinding<'a>> {
match self.binding {
Some(binding) if !binding.defined_with(DefModifiers::GLOB_IMPORTED) => Success(binding),
// If we don't allow private imports and no public imports can define the name, fail.
_ if !allow_private_imports && self.pub_outstanding_references == 0 &&
!self.binding.map(NameBinding::is_public).unwrap_or(false) => Failed(None),
_ if self.outstanding_references > 0 => Indeterminate,
Some(binding) => Success(binding),
None => Failed(None),
}
}
// Returns Some(the resolution of the name), or None if the resolution depends // Returns Some(the resolution of the name), or None if the resolution depends
// on whether more globs can define the name. // on whether more globs can define the name.
fn try_result(&self, allow_private_imports: bool) fn try_result(&self, allow_private_imports: bool)
-> Option<ResolveResult<&'a NameBinding<'a>>> { -> Option<ResolveResult<&'a NameBinding<'a>>> {
match self.result(allow_private_imports) { match self.binding {
Success(binding) if binding.defined_with(DefModifiers::PRELUDE) => None, Some(binding) if !binding.defined_with(DefModifiers::GLOB_IMPORTED) =>
Failed(_) => None, Some(Success(binding)),
result @ _ => Some(result), // If (1) we don't allow private imports, (2) no public single import can define the
// name, and (3) no public glob has defined the name, the resolution depends on globs.
_ if !allow_private_imports && self.pub_outstanding_references == 0 &&
!self.binding.map(NameBinding::is_public).unwrap_or(false) => None,
_ if self.outstanding_references > 0 => Some(Indeterminate),
Some(binding) => Some(Success(binding)),
None => None,
} }
} }
@ -202,8 +184,6 @@ impl<'a> NameResolution<'a> {
}; };
for duplicate_glob in self.duplicate_globs.iter() { for duplicate_glob in self.duplicate_globs.iter() {
if duplicate_glob.defined_with(DefModifiers::PRELUDE) { continue }
// FIXME #31337: We currently allow items to shadow glob-imported re-exports. // FIXME #31337: We currently allow items to shadow glob-imported re-exports.
if !binding.is_import() { if !binding.is_import() {
if let NameBindingKind::Import { binding, .. } = duplicate_glob.kind { if let NameBindingKind::Import { binding, .. } = duplicate_glob.kind {
@ -259,7 +239,16 @@ impl<'a> ::ModuleS<'a> {
} }
} }
resolution.result(true) Failed(None)
}
// Invariant: this may not be called until import resolution is complete.
pub fn resolve_name_in_lexical_scope(&self, name: Name, ns: Namespace)
-> Option<&'a NameBinding<'a>> {
self.resolutions.borrow().get(&(name, ns)).and_then(|resolution| resolution.binding)
.or_else(|| self.prelude.borrow().and_then(|prelude| {
prelude.resolve_name(name, ns, false).success()
}))
} }
// Define the name or return the existing binding if there is a collision. // Define the name or return the existing binding if there is a collision.
@ -369,7 +358,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
// resolution for it so that later resolve stages won't complain. // resolution for it so that later resolve stages won't complain.
if let SingleImport { target, .. } = e.import_directive.subclass { if let SingleImport { target, .. } = e.import_directive.subclass {
let dummy_binding = self.resolver.arenas.alloc_name_binding(NameBinding { let dummy_binding = self.resolver.arenas.alloc_name_binding(NameBinding {
modifiers: DefModifiers::PRELUDE, modifiers: DefModifiers::GLOB_IMPORTED,
kind: NameBindingKind::Def(Def::Err), kind: NameBindingKind::Def(Def::Err),
span: None, span: None,
}); });
@ -623,6 +612,11 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
} }
build_reduced_graph::populate_module_if_necessary(self.resolver, target_module); build_reduced_graph::populate_module_if_necessary(self.resolver, target_module);
if directive.is_prelude {
*module_.prelude.borrow_mut() = Some(target_module);
return Success(());
}
// Add to target_module's glob_importers and module_'s resolved_globs // Add to target_module's glob_importers and module_'s resolved_globs
target_module.glob_importers.borrow_mut().push((module_, directive)); target_module.glob_importers.borrow_mut().push((module_, directive));
match *module_.resolved_globs.borrow_mut() { match *module_.resolved_globs.borrow_mut() {
@ -685,13 +679,6 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
self.resolver.session.add_lint(lint, id, binding.span.unwrap(), msg); self.resolver.session.add_lint(lint, id, binding.span.unwrap(), msg);
} }
} }
// We can always use methods from the prelude traits
for glob_binding in resolution.duplicate_globs.iter() {
if glob_binding.defined_with(DefModifiers::PRELUDE) {
module.shadowed_traits.borrow_mut().push(glob_binding);
}
}
} }
if reexports.len() > 0 { if reexports.len() > 0 {