Rollup merge of #32131 - petrochenkov:prim, r=eddyb

resolve: Minimize hacks in name resolution of primitive types

When resolving the first unqualified segment in a path with `n` segments and `n - 1` associated item segments, e.g. (`a` or `a::assoc` or `a::assoc::assoc` etc) try to resolve `a` without considering primitive types first. If the "normal" lookup fails or results in a module, then try to resolve `a` as a primitive type as a fallback.

This way backward compatibility is respected, but the restriction from E0317 can be lifted, i.e. primitive names mostly can be shadowed like any other names.

Furthermore, if names of primitive types are [put into prelude](https://github.com/petrochenkov/rust/tree/prim2) (now it's possible to do), then most of names will be resolved in conventional way and amount of code relying on this fallback will be greatly reduced. Although, it's not entirely convenient to put them into prelude right now due to temporary conflicts like `use prelude::v1::*; use usize;` in libcore/libstd, I'd better wait for proper glob shadowing before doing it.
I wish the `no_prelude` attribute were unstable as intended :(

cc @jseyfried @arielb1
r? @eddyb
This commit is contained in:
Manish Goregaokar
2016-03-26 09:07:20 +05:30
3 changed files with 65 additions and 112 deletions

View File

@@ -1619,15 +1619,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
intravisit::walk_crate(self, krate);
}
fn check_if_primitive_type_name(&self, name: Name, span: Span) {
if let Some(_) = self.primitive_type_table.primitive_types.get(&name) {
span_err!(self.session,
span,
E0317,
"user-defined types or type parameters cannot shadow the primitive types");
}
}
fn resolve_item(&mut self, item: &Item) {
let name = item.name;
@@ -1637,8 +1628,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ItemEnum(_, ref generics) |
ItemTy(_, ref generics) |
ItemStruct(_, ref generics) => {
self.check_if_primitive_type_name(name, item.span);
self.with_type_parameter_rib(HasTypeParameters(generics, TypeSpace, ItemRibKind),
|this| intravisit::walk_item(this, item));
}
@@ -1659,8 +1648,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
ItemTrait(_, ref generics, ref bounds, ref trait_items) => {
self.check_if_primitive_type_name(name, item.span);
// Create a new rib for the trait-wide type parameters.
self.with_type_parameter_rib(HasTypeParameters(generics,
TypeSpace,
@@ -1695,8 +1682,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
});
}
hir::TypeTraitItem(..) => {
this.check_if_primitive_type_name(trait_item.name,
trait_item.span);
this.with_type_parameter_rib(NoTypeParameters, |this| {
intravisit::walk_trait_item(this, trait_item)
});
@@ -1720,28 +1705,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
ItemUse(ref view_path) => {
// check for imports shadowing primitive types
let check_rename = |this: &Self, id, name| {
match this.def_map.borrow().get(&id).map(|d| d.full_def()) {
Some(Def::Enum(..)) | Some(Def::TyAlias(..)) | Some(Def::Struct(..)) |
Some(Def::Trait(..)) | None => {
this.check_if_primitive_type_name(name, item.span);
}
_ => {}
}
};
match view_path.node {
hir::ViewPathSimple(name, _) => {
check_rename(self, item.id, name);
}
hir::ViewPathList(ref prefix, ref items) => {
for item in items {
if let Some(name) = item.node.rename() {
check_rename(self, item.node.id(), name);
}
}
// Resolve prefix of an import with empty braces (issue #28388)
if items.is_empty() && !prefix.segments.is_empty() {
match self.resolve_crate_relative_path(prefix.span,
@@ -1922,9 +1887,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
fn resolve_generics(&mut self, generics: &Generics) {
for type_parameter in generics.ty_params.iter() {
self.check_if_primitive_type_name(type_parameter.name, type_parameter.span);
}
for predicate in &generics.where_clause.predicates {
match predicate {
&hir::WherePredicate::BoundPredicate(_) |
@@ -2658,15 +2620,37 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Try to find a path to an item in a module.
let last_ident = segments.last().unwrap().identifier;
if segments.len() <= 1 {
let unqualified_def = self.resolve_identifier(last_ident, namespace, true);
return unqualified_def.and_then(|def| self.adjust_local_def(def, span))
.map(|def| {
PathResolution::new(def, path_depth)
});
// Resolve a single identifier with fallback to primitive types
let resolve_identifier_with_fallback = |this: &mut Self, record_used| {
let def = this.resolve_identifier(last_ident, namespace, record_used);
match def {
None | Some(LocalDef{def: Def::Mod(..), ..}) if namespace == TypeNS =>
this.primitive_type_table
.primitive_types
.get(&last_ident.unhygienic_name)
.map_or(def, |prim_ty| Some(LocalDef::from_def(Def::PrimTy(*prim_ty)))),
_ => def
}
};
if segments.len() == 1 {
// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
// don't report an error right away, but try to fallback to a primitive type.
// So, we are still able to successfully resolve something like
//
// use std::u8; // bring module u8 in scope
// fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8
// u8::max_value() // OK, resolves to associated function <u8>::max_value,
// // not to non-existent std::u8::max_value
// }
//
// Such behavior is required for backward compatibility.
// The same fallback is used when `a` resolves to nothing.
let unqualified_def = resolve_identifier_with_fallback(self, true);
return unqualified_def.and_then(|def| self.adjust_local_def(def, span)).map(mk_res);
}
let unqualified_def = self.resolve_identifier(last_ident, namespace, false);
let unqualified_def = resolve_identifier_with_fallback(self, false);
let def = self.resolve_module_relative_path(span, segments, namespace);
match (def, unqualified_def) {
(Some(d), Some(ref ud)) if d == ud.def => {
@@ -2692,15 +2676,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
return Some(LocalDef::from_def(Def::Err));
}
// First, check to see whether the name is a primitive type.
if namespace == TypeNS {
if let Some(&prim_ty) = self.primitive_type_table
.primitive_types
.get(&identifier.unhygienic_name) {
return Some(LocalDef::from_def(Def::PrimTy(prim_ty)));
}
}
self.resolve_identifier_in_local_ribs(identifier, namespace, record_used)
}