Rollup merge of #64623 - matthewjasper:underscore-imports, r=petrochenkov
Remove last uses of gensyms Underscore bindings now use unique `SyntaxContext`s to avoid collisions. This was the last use of gensyms in the compiler, so this PR also removes them. closes #49300 cc #60869 r? @petrochenkov
This commit is contained in:
commit
af3d9e57e7
12 changed files with 247 additions and 154 deletions
|
@ -1483,7 +1483,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
|
|||
}
|
||||
|
||||
// Replace any anonymous late-bound regions with named
|
||||
// variants, using gensym'd identifiers, so that we can
|
||||
// variants, using new unique identifiers, so that we can
|
||||
// clearly differentiate between named and unnamed regions in
|
||||
// the output. We'll probably want to tweak this over time to
|
||||
// decide just how much information to give.
|
||||
|
|
|
@ -94,7 +94,8 @@ impl<'a> Resolver<'a> {
|
|||
where T: ToNameBinding<'a>,
|
||||
{
|
||||
let binding = def.to_name_binding(self.arenas);
|
||||
if let Err(old_binding) = self.try_define(parent, ident, ns, binding) {
|
||||
let key = self.new_key(ident, ns);
|
||||
if let Err(old_binding) = self.try_define(parent, key, binding) {
|
||||
self.report_conflict(parent, ident, ns, old_binding, &binding);
|
||||
}
|
||||
}
|
||||
|
@ -349,9 +350,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
|||
|
||||
self.r.indeterminate_imports.push(directive);
|
||||
match directive.subclass {
|
||||
// Don't add unresolved underscore imports to modules
|
||||
SingleImport { target: Ident { name: kw::Underscore, .. }, .. } => {}
|
||||
SingleImport { target, type_ns_only, .. } => {
|
||||
self.r.per_ns(|this, ns| if !type_ns_only || ns == TypeNS {
|
||||
let mut resolution = this.resolution(current_module, target, ns).borrow_mut();
|
||||
let key = this.new_key(target, ns);
|
||||
let mut resolution = this.resolution(current_module, key).borrow_mut();
|
||||
resolution.add_single_import(directive);
|
||||
});
|
||||
}
|
||||
|
@ -407,7 +411,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
|||
};
|
||||
match use_tree.kind {
|
||||
ast::UseTreeKind::Simple(rename, ..) => {
|
||||
let mut ident = use_tree.ident().gensym_if_underscore();
|
||||
let mut ident = use_tree.ident();
|
||||
let mut module_path = prefix;
|
||||
let mut source = module_path.pop().unwrap();
|
||||
let mut type_ns_only = false;
|
||||
|
@ -585,7 +589,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
|||
let parent_scope = &self.parent_scope;
|
||||
let parent = parent_scope.module;
|
||||
let expansion = parent_scope.expansion;
|
||||
let ident = item.ident.gensym_if_underscore();
|
||||
let ident = item.ident;
|
||||
let sp = item.span;
|
||||
let vis = self.resolve_visibility(&item.vis);
|
||||
|
||||
|
@ -851,10 +855,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
|||
fn build_reduced_graph_for_external_crate_res(&mut self, child: Export<NodeId>) {
|
||||
let parent = self.parent_scope.module;
|
||||
let Export { ident, res, vis, span } = child;
|
||||
// FIXME: We shouldn't create the gensym here, it should come from metadata,
|
||||
// but metadata cannot encode gensyms currently, so we create it here.
|
||||
// This is only a guess, two equivalent idents may incorrectly get different gensyms here.
|
||||
let ident = ident.gensym_if_underscore();
|
||||
let expansion = ExpnId::root(); // FIXME(jseyfried) intercrate hygiene
|
||||
// Record primary definitions.
|
||||
match res {
|
||||
|
|
|
@ -80,11 +80,11 @@ impl<'a> Resolver<'a> {
|
|||
names: &mut Vec<TypoSuggestion>,
|
||||
filter_fn: &impl Fn(Res) -> bool,
|
||||
) {
|
||||
for (&(ident, _), resolution) in self.resolutions(module).borrow().iter() {
|
||||
for (key, resolution) in self.resolutions(module).borrow().iter() {
|
||||
if let Some(binding) = resolution.borrow().binding {
|
||||
let res = binding.res();
|
||||
if filter_fn(res) {
|
||||
names.push(TypoSuggestion::from_res(ident.name, res));
|
||||
names.push(TypoSuggestion::from_res(key.ident.name, res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -849,7 +849,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
}
|
||||
|
||||
let resolutions = self.r.resolutions(crate_module).borrow();
|
||||
let resolution = resolutions.get(&(ident, MacroNS))?;
|
||||
let resolution = resolutions.get(&self.r.new_key(ident, MacroNS))?;
|
||||
let binding = resolution.borrow().binding()?;
|
||||
if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() {
|
||||
let module_name = crate_module.kind.name().unwrap();
|
||||
|
|
|
@ -432,7 +432,22 @@ impl ModuleKind {
|
|||
}
|
||||
}
|
||||
|
||||
type Resolutions<'a> = RefCell<FxIndexMap<(Ident, Namespace), &'a RefCell<NameResolution<'a>>>>;
|
||||
/// A key that identifies a binding in a given `Module`.
|
||||
///
|
||||
/// Multiple bindings in the same module can have the same key (in a valid
|
||||
/// program) if all but one of them come from glob imports.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct BindingKey {
|
||||
/// The identifier for the binding, aways the `modern` version of the
|
||||
/// identifier.
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
/// 0 if ident is not `_`, otherwise a value that's unique to the specific
|
||||
/// `_` in the expanded AST that introduced this binding.
|
||||
disambiguator: u32,
|
||||
}
|
||||
|
||||
type Resolutions<'a> = RefCell<FxIndexMap<BindingKey, &'a RefCell<NameResolution<'a>>>>;
|
||||
|
||||
/// One node in the tree of modules.
|
||||
pub struct ModuleData<'a> {
|
||||
|
@ -492,8 +507,8 @@ impl<'a> ModuleData<'a> {
|
|||
fn for_each_child<R, F>(&'a self, resolver: &mut R, mut f: F)
|
||||
where R: AsMut<Resolver<'a>>, F: FnMut(&mut R, Ident, Namespace, &'a NameBinding<'a>)
|
||||
{
|
||||
for (&(ident, ns), name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
|
||||
name_resolution.borrow().binding.map(|binding| f(resolver, ident, ns, binding));
|
||||
for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
|
||||
name_resolution.borrow().binding.map(|binding| f(resolver, key.ident, key.ns, binding));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -882,6 +897,7 @@ pub struct Resolver<'a> {
|
|||
module_map: FxHashMap<DefId, Module<'a>>,
|
||||
extern_module_map: FxHashMap<DefId, Module<'a>>,
|
||||
binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
|
||||
underscore_disambiguator: u32,
|
||||
|
||||
/// Maps glob imports to the names of items actually imported.
|
||||
pub glob_map: GlobMap,
|
||||
|
@ -1160,6 +1176,7 @@ impl<'a> Resolver<'a> {
|
|||
extern_crate_map: Default::default(),
|
||||
export_map: FxHashMap::default(),
|
||||
trait_map: Default::default(),
|
||||
underscore_disambiguator: 0,
|
||||
empty_module,
|
||||
module_map,
|
||||
block_map: Default::default(),
|
||||
|
@ -1284,6 +1301,17 @@ impl<'a> Resolver<'a> {
|
|||
self.arenas.alloc_module(module)
|
||||
}
|
||||
|
||||
fn new_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey {
|
||||
let ident = ident.modern();
|
||||
let disambiguator = if ident.name == kw::Underscore {
|
||||
self.underscore_disambiguator += 1;
|
||||
self.underscore_disambiguator
|
||||
} else {
|
||||
0
|
||||
};
|
||||
BindingKey { ident, ns, disambiguator }
|
||||
}
|
||||
|
||||
fn resolutions(&mut self, module: Module<'a>) -> &'a Resolutions<'a> {
|
||||
if module.populate_on_access.get() {
|
||||
module.populate_on_access.set(false);
|
||||
|
@ -1292,9 +1320,9 @@ impl<'a> Resolver<'a> {
|
|||
&module.lazy_resolutions
|
||||
}
|
||||
|
||||
fn resolution(&mut self, module: Module<'a>, ident: Ident, ns: Namespace)
|
||||
fn resolution(&mut self, module: Module<'a>, key: BindingKey)
|
||||
-> &'a RefCell<NameResolution<'a>> {
|
||||
*self.resolutions(module).borrow_mut().entry((ident.modern(), ns))
|
||||
*self.resolutions(module).borrow_mut().entry(key)
|
||||
.or_insert_with(|| self.arenas.alloc_name_resolution())
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{CrateLint, Module, ModuleOrUniformRoot, PerNS, ScopeSet, ParentScope
|
|||
use crate::Determinacy::{self, *};
|
||||
use crate::Namespace::{self, TypeNS, MacroNS};
|
||||
use crate::{NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
||||
use crate::{Resolver, ResolutionError, Segment, ModuleKind};
|
||||
use crate::{Resolver, ResolutionError, BindingKey, Segment, ModuleKind};
|
||||
use crate::{names_to_string, module_to_string};
|
||||
use crate::diagnostics::Suggestion;
|
||||
|
||||
|
@ -235,7 +235,8 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let resolution = self.resolution(module, ident, ns)
|
||||
let key = self.new_key(ident, ns);
|
||||
let resolution = self.resolution(module, key)
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
|
||||
|
||||
|
@ -447,17 +448,16 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
|
||||
// Define the name or return the existing binding if there is a collision.
|
||||
pub fn try_define(
|
||||
crate fn try_define(
|
||||
&mut self,
|
||||
module: Module<'a>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
key: BindingKey,
|
||||
binding: &'a NameBinding<'a>,
|
||||
) -> Result<(), &'a NameBinding<'a>> {
|
||||
let res = binding.res();
|
||||
self.check_reserved_macro_name(ident, res);
|
||||
self.check_reserved_macro_name(key.ident, res);
|
||||
self.set_binding_parent_module(binding, module);
|
||||
self.update_resolution(module, ident, ns, |this, resolution| {
|
||||
self.update_resolution(module, key, |this, resolution| {
|
||||
if let Some(old_binding) = resolution.binding {
|
||||
if res == Res::Err {
|
||||
// Do not override real bindings with `Res::Err`s from error recovery.
|
||||
|
@ -479,8 +479,9 @@ impl<'a> Resolver<'a> {
|
|||
} else {
|
||||
(binding, old_binding)
|
||||
};
|
||||
if glob_binding.res() != nonglob_binding.res() &&
|
||||
ns == MacroNS && nonglob_binding.expansion != ExpnId::root() {
|
||||
if glob_binding.res() != nonglob_binding.res()
|
||||
&& key.ns == MacroNS && nonglob_binding.expansion != ExpnId::root()
|
||||
{
|
||||
resolution.binding = Some(this.ambiguity(
|
||||
AmbiguityKind::GlobVsExpanded,
|
||||
nonglob_binding,
|
||||
|
@ -499,9 +500,9 @@ impl<'a> Resolver<'a> {
|
|||
DUPLICATE_MACRO_EXPORTS,
|
||||
CRATE_NODE_ID,
|
||||
binding.span,
|
||||
&format!("a macro named `{}` has already been exported", ident),
|
||||
&format!("a macro named `{}` has already been exported", key.ident),
|
||||
BuiltinLintDiagnostics::DuplicatedMacroExports(
|
||||
ident, old_binding.span, binding.span));
|
||||
key.ident, old_binding.span, binding.span));
|
||||
|
||||
resolution.binding = Some(binding);
|
||||
} else {
|
||||
|
@ -531,9 +532,9 @@ impl<'a> Resolver<'a> {
|
|||
// Use `f` to mutate the resolution of the name in the module.
|
||||
// If the resolution becomes a success, define it in the module's glob importers.
|
||||
fn update_resolution<T, F>(
|
||||
&mut self, module: Module<'a>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
&mut self,
|
||||
module: Module<'a>,
|
||||
key: BindingKey,
|
||||
f: F,
|
||||
) -> T
|
||||
where F: FnOnce(&mut Resolver<'a>, &mut NameResolution<'a>) -> T
|
||||
|
@ -541,7 +542,7 @@ impl<'a> Resolver<'a> {
|
|||
// Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
|
||||
// during which the resolution might end up getting re-defined via a glob cycle.
|
||||
let (binding, t) = {
|
||||
let resolution = &mut *self.resolution(module, ident, ns).borrow_mut();
|
||||
let resolution = &mut *self.resolution(module, key).borrow_mut();
|
||||
let old_binding = resolution.binding();
|
||||
|
||||
let t = f(self, resolution);
|
||||
|
@ -558,7 +559,7 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
// Define `binding` in `module`s glob importers.
|
||||
for directive in module.glob_importers.borrow_mut().iter() {
|
||||
let mut ident = ident.modern();
|
||||
let mut ident = key.ident;
|
||||
let scope = match ident.span.reverse_glob_adjust(module.expansion, directive.span) {
|
||||
Some(Some(def)) => self.macro_def_scope(def),
|
||||
Some(None) => directive.parent_scope.module,
|
||||
|
@ -566,7 +567,8 @@ impl<'a> Resolver<'a> {
|
|||
};
|
||||
if self.is_accessible_from(binding.vis, scope) {
|
||||
let imported_binding = self.import(binding, directive);
|
||||
let _ = self.try_define(directive.parent_scope.module, ident, ns, imported_binding);
|
||||
let key = BindingKey { ident, ..key };
|
||||
let _ = self.try_define(directive.parent_scope.module, key, imported_binding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,7 +582,8 @@ impl<'a> Resolver<'a> {
|
|||
let dummy_binding = self.dummy_binding;
|
||||
let dummy_binding = self.import(dummy_binding, directive);
|
||||
self.per_ns(|this, ns| {
|
||||
let _ = this.try_define(directive.parent_scope.module, target, ns, dummy_binding);
|
||||
let key = this.new_key(target, ns);
|
||||
let _ = this.try_define(directive.parent_scope.module, key, dummy_binding);
|
||||
// Consider erroneous imports used to avoid duplicate diagnostics.
|
||||
this.record_use(target, ns, dummy_binding, false);
|
||||
});
|
||||
|
@ -820,8 +823,11 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
let parent = directive.parent_scope.module;
|
||||
match source_bindings[ns].get() {
|
||||
Err(Undetermined) => indeterminate = true,
|
||||
// Don't update the resolution, because it was never added.
|
||||
Err(Determined) if target.name == kw::Underscore => {}
|
||||
Err(Determined) => {
|
||||
this.update_resolution(parent, target, ns, |_, resolution| {
|
||||
let key = this.new_key(target, ns);
|
||||
this.update_resolution(parent, key, |_, resolution| {
|
||||
resolution.single_imports.remove(&PtrKey(directive));
|
||||
});
|
||||
}
|
||||
|
@ -1052,7 +1058,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
_ => None,
|
||||
};
|
||||
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
|
||||
let names = resolutions.filter_map(|(&(ref i, _), resolution)| {
|
||||
let names = resolutions.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
|
||||
if *i == ident { return None; } // Never suggest the same name
|
||||
match *resolution.borrow() {
|
||||
NameResolution { binding: Some(name_binding), .. } => {
|
||||
|
@ -1301,19 +1307,18 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
|
||||
// Ensure that `resolutions` isn't borrowed during `try_define`,
|
||||
// since it might get updated via a glob cycle.
|
||||
let bindings = self.r.resolutions(module).borrow().iter().filter_map(|(ident, resolution)| {
|
||||
resolution.borrow().binding().map(|binding| (*ident, binding))
|
||||
let bindings = self.r.resolutions(module).borrow().iter().filter_map(|(key, resolution)| {
|
||||
resolution.borrow().binding().map(|binding| (*key, binding))
|
||||
}).collect::<Vec<_>>();
|
||||
for ((mut ident, ns), binding) in bindings {
|
||||
let scope = match ident.span.reverse_glob_adjust(module.expansion, directive.span) {
|
||||
for (mut key, binding) in bindings {
|
||||
let scope = match key.ident.span.reverse_glob_adjust(module.expansion, directive.span) {
|
||||
Some(Some(def)) => self.r.macro_def_scope(def),
|
||||
Some(None) => directive.parent_scope.module,
|
||||
None => continue,
|
||||
};
|
||||
if self.r.is_accessible_from(binding.pseudo_vis(), scope) {
|
||||
let imported_binding = self.r.import(binding, directive);
|
||||
let _ =
|
||||
self.r.try_define(directive.parent_scope.module, ident, ns, imported_binding);
|
||||
let _ = self.r.try_define(directive.parent_scope.module, key, imported_binding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1329,29 +1334,23 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
|
||||
let mut reexports = Vec::new();
|
||||
|
||||
for (&(ident, ns), resolution) in self.r.resolutions(module).borrow().iter() {
|
||||
let resolution = &mut *resolution.borrow_mut();
|
||||
let binding = match resolution.binding {
|
||||
Some(binding) => binding,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
module.for_each_child(self.r, |this, ident, ns, binding| {
|
||||
// Filter away ambiguous imports and anything that has def-site
|
||||
// hygiene.
|
||||
// FIXME: Implement actual cross-crate hygiene.
|
||||
let is_good_import = binding.is_import() && !binding.is_ambiguity()
|
||||
&& !ident.span.modern().from_expansion();
|
||||
&& !ident.span.from_expansion();
|
||||
if is_good_import || binding.is_macro_def() {
|
||||
let res = binding.res();
|
||||
if res != Res::Err {
|
||||
if let Some(def_id) = res.opt_def_id() {
|
||||
if !def_id.is_local() {
|
||||
self.r.cstore.export_macros_untracked(def_id.krate);
|
||||
this.cstore.export_macros_untracked(def_id.krate);
|
||||
}
|
||||
}
|
||||
reexports.push(Export {
|
||||
ident: ident.modern(),
|
||||
res: res,
|
||||
ident,
|
||||
res,
|
||||
span: binding.span,
|
||||
vis: binding.vis,
|
||||
});
|
||||
|
@ -1360,7 +1359,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
|
||||
if let NameBindingKind::Import { binding: orig_binding, directive, .. } = binding.kind {
|
||||
if ns == TypeNS && orig_binding.is_variant() &&
|
||||
!orig_binding.vis.is_at_least(binding.vis, &*self) {
|
||||
!orig_binding.vis.is_at_least(binding.vis, &*this) {
|
||||
let msg = match directive.subclass {
|
||||
ImportDirectiveSubclass::SingleImport { .. } => {
|
||||
format!("variant `{}` is private and cannot be re-exported",
|
||||
|
@ -1372,33 +1371,34 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
let error_id = (DiagnosticMessageId::ErrorId(0), // no code?!
|
||||
Some(binding.span),
|
||||
msg.clone());
|
||||
let fresh = self.r.session.one_time_diagnostics
|
||||
let fresh = this.session.one_time_diagnostics
|
||||
.borrow_mut().insert(error_id);
|
||||
if !fresh {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
msg
|
||||
},
|
||||
ref s @ _ => bug!("unexpected import subclass {:?}", s)
|
||||
};
|
||||
let mut err = self.r.session.struct_span_err(binding.span, &msg);
|
||||
let mut err = this.session.struct_span_err(binding.span, &msg);
|
||||
|
||||
let imported_module = match directive.imported_module.get() {
|
||||
Some(ModuleOrUniformRoot::Module(module)) => module,
|
||||
_ => bug!("module should exist"),
|
||||
};
|
||||
let parent_module = imported_module.parent.expect("parent should exist");
|
||||
let resolutions = self.r.resolutions(parent_module).borrow();
|
||||
let resolutions = this.resolutions(parent_module).borrow();
|
||||
let enum_path_segment_index = directive.module_path.len() - 1;
|
||||
let enum_ident = directive.module_path[enum_path_segment_index].ident;
|
||||
|
||||
let enum_resolution = resolutions.get(&(enum_ident, TypeNS))
|
||||
let key = this.new_key(enum_ident, TypeNS);
|
||||
let enum_resolution = resolutions.get(&key)
|
||||
.expect("resolution should exist");
|
||||
let enum_span = enum_resolution.borrow()
|
||||
.binding.expect("binding should exist")
|
||||
.span;
|
||||
let enum_def_span = self.r.session.source_map().def_span(enum_span);
|
||||
let enum_def_snippet = self.r.session.source_map()
|
||||
let enum_def_span = this.session.source_map().def_span(enum_span);
|
||||
let enum_def_snippet = this.session.source_map()
|
||||
.span_to_snippet(enum_def_span).expect("snippet should exist");
|
||||
// potentially need to strip extant `crate`/`pub(path)` for suggestion
|
||||
let after_vis_index = enum_def_snippet.find("enum")
|
||||
|
@ -1406,7 +1406,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
let suggestion = format!("pub {}",
|
||||
&enum_def_snippet[after_vis_index..]);
|
||||
|
||||
self.r.session
|
||||
this.session
|
||||
.diag_span_suggestion_once(&mut err,
|
||||
DiagnosticMessageId::ErrorId(0),
|
||||
enum_def_span,
|
||||
|
@ -1415,7 +1415,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if reexports.len() > 0 {
|
||||
if let Some(def_id) = module.def_id() {
|
||||
|
|
|
@ -334,8 +334,7 @@ impl Ident {
|
|||
if !Self::is_valid(&string) {
|
||||
panic!("`{:?}` is not a valid identifier", string)
|
||||
}
|
||||
// Get rid of gensyms to conservatively check rawness on the string contents only.
|
||||
if is_raw && !sym.as_interned_str().as_symbol().can_be_raw() {
|
||||
if is_raw && !sym.can_be_raw() {
|
||||
panic!("`{}` cannot be a raw identifier", string);
|
||||
}
|
||||
Ident { sym, is_raw, span }
|
||||
|
|
|
@ -808,25 +808,13 @@ impl Ident {
|
|||
Ident::new(self.name, self.span.modern_and_legacy())
|
||||
}
|
||||
|
||||
/// Transforms an underscore identifier into one with the same name, but
|
||||
/// gensymed. Leaves non-underscore identifiers unchanged.
|
||||
pub fn gensym_if_underscore(self) -> Ident {
|
||||
if self.name == kw::Underscore {
|
||||
let name = with_interner(|interner| interner.gensymed(self.name));
|
||||
Ident::new(name, self.span)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the name to a `LocalInternedString`. This is a slowish
|
||||
/// operation because it requires locking the symbol interner.
|
||||
pub fn as_str(self) -> LocalInternedString {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
/// Convert the name to an `InternedString`. This is a slowish operation
|
||||
/// because it requires locking the symbol interner.
|
||||
/// Convert the name to an `InternedString`.
|
||||
pub fn as_interned_str(self) -> InternedString {
|
||||
self.name.as_interned_str()
|
||||
}
|
||||
|
@ -881,26 +869,9 @@ impl UseSpecializedDecodable for Ident {
|
|||
}
|
||||
}
|
||||
|
||||
/// A symbol is an interned or gensymed string. A gensym is a symbol that is
|
||||
/// never equal to any other symbol.
|
||||
/// An interned string.
|
||||
///
|
||||
/// Conceptually, a gensym can be thought of as a normal symbol with an
|
||||
/// invisible unique suffix. Gensyms are useful when creating new identifiers
|
||||
/// that must not match any existing identifiers, e.g. during macro expansion
|
||||
/// and syntax desugaring. Because gensyms should always be identifiers, all
|
||||
/// gensym operations are on `Ident` rather than `Symbol`. (Indeed, in the
|
||||
/// future the gensym-ness may be moved from `Symbol` to hygiene data.)
|
||||
///
|
||||
/// Examples:
|
||||
/// ```
|
||||
/// assert_eq!(Ident::from_str("_"), Ident::from_str("_"))
|
||||
/// assert_ne!(Ident::from_str("_").gensym_if_underscore(), Ident::from_str("_"))
|
||||
/// assert_ne!(
|
||||
/// Ident::from_str("_").gensym_if_underscore(),
|
||||
/// Ident::from_str("_").gensym_if_underscore(),
|
||||
/// )
|
||||
/// ```
|
||||
/// Internally, a symbol is implemented as an index, and all operations
|
||||
/// Internally, a `Symbol` is implemented as an index, and all operations
|
||||
/// (including hashing, equality, and ordering) operate on that index. The use
|
||||
/// of `rustc_index::newtype_index!` means that `Option<Symbol>` only takes up 4 bytes,
|
||||
/// because `rustc_index::newtype_index!` reserves the last 256 values for tagging purposes.
|
||||
|
@ -951,12 +922,9 @@ impl Symbol {
|
|||
})
|
||||
}
|
||||
|
||||
/// Convert to an `InternedString`. This is a slowish operation because it
|
||||
/// requires locking the symbol interner.
|
||||
/// Convert to an `InternedString`.
|
||||
pub fn as_interned_str(self) -> InternedString {
|
||||
with_interner(|interner| InternedString {
|
||||
symbol: interner.interned(self)
|
||||
})
|
||||
InternedString { symbol: self }
|
||||
}
|
||||
|
||||
pub fn as_u32(self) -> u32 {
|
||||
|
@ -966,12 +934,7 @@ impl Symbol {
|
|||
|
||||
impl fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
|
||||
if is_gensymed {
|
||||
write!(f, "{}({:?})", self, self.0)
|
||||
} else {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -994,15 +957,11 @@ impl Decodable for Symbol {
|
|||
}
|
||||
|
||||
// The `&'static str`s in this type actually point into the arena.
|
||||
//
|
||||
// Note that normal symbols are indexed upward from 0, and gensyms are indexed
|
||||
// downward from SymbolIndex::MAX_AS_U32.
|
||||
#[derive(Default)]
|
||||
pub struct Interner {
|
||||
arena: DroplessArena,
|
||||
names: FxHashMap<&'static str, Symbol>,
|
||||
strings: Vec<&'static str>,
|
||||
gensyms: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Interner {
|
||||
|
@ -1035,34 +994,10 @@ impl Interner {
|
|||
self.names.insert(string, name);
|
||||
name
|
||||
}
|
||||
|
||||
fn interned(&self, symbol: Symbol) -> Symbol {
|
||||
if (symbol.0.as_usize()) < self.strings.len() {
|
||||
symbol
|
||||
} else {
|
||||
self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
fn gensymed(&mut self, symbol: Symbol) -> Symbol {
|
||||
self.gensyms.push(symbol);
|
||||
Symbol::new(SymbolIndex::MAX_AS_U32 - self.gensyms.len() as u32 + 1)
|
||||
}
|
||||
|
||||
fn is_gensymed(&mut self, symbol: Symbol) -> bool {
|
||||
symbol.0.as_usize() >= self.strings.len()
|
||||
}
|
||||
|
||||
// Get the symbol as a string. `Symbol::as_str()` should be used in
|
||||
// preference to this function.
|
||||
pub fn get(&self, symbol: Symbol) -> &str {
|
||||
match self.strings.get(symbol.0.as_usize()) {
|
||||
Some(string) => string,
|
||||
None => {
|
||||
let symbol = self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize];
|
||||
self.strings[symbol.0.as_usize()]
|
||||
}
|
||||
}
|
||||
self.strings[symbol.0.as_usize()]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1223,19 +1158,12 @@ impl fmt::Display for LocalInternedString {
|
|||
}
|
||||
}
|
||||
|
||||
/// An alternative to `Symbol` that is focused on string contents. It has two
|
||||
/// main differences to `Symbol`.
|
||||
/// An alternative to `Symbol` that is focused on string contents.
|
||||
///
|
||||
/// First, its implementations of `Hash`, `PartialOrd` and `Ord` work with the
|
||||
/// Its implementations of `Hash`, `PartialOrd` and `Ord` work with the
|
||||
/// string chars rather than the symbol integer. This is useful when hash
|
||||
/// stability is required across compile sessions, or a guaranteed sort
|
||||
/// ordering is required.
|
||||
///
|
||||
/// Second, gensym-ness is irrelevant. E.g.:
|
||||
/// ```
|
||||
/// assert_ne!(Symbol::gensym("x"), Symbol::gensym("x"))
|
||||
/// assert_eq!(Symbol::gensym("x").as_interned_str(), Symbol::gensym("x").as_interned_str())
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct InternedString {
|
||||
symbol: Symbol,
|
||||
|
|
|
@ -14,13 +14,6 @@ fn interner_tests() {
|
|||
assert_eq!(i.intern("cat"), Symbol::new(1));
|
||||
// dog is still at zero
|
||||
assert_eq!(i.intern("dog"), Symbol::new(0));
|
||||
let z = i.intern("zebra");
|
||||
assert_eq!(i.gensymed(z), Symbol::new(SymbolIndex::MAX_AS_U32));
|
||||
// gensym of same string gets new number:
|
||||
assert_eq!(i.gensymed(z), Symbol::new(SymbolIndex::MAX_AS_U32 - 1));
|
||||
// gensym of *existing* string gets new number:
|
||||
let d = i.intern("dog");
|
||||
assert_eq!(i.gensymed(d), Symbol::new(SymbolIndex::MAX_AS_U32 - 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
33
src/test/ui/underscore-imports/hygiene-2.rs
Normal file
33
src/test/ui/underscore-imports/hygiene-2.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Make sure that underscore imports with different contexts can exist in the
|
||||
// same scope.
|
||||
|
||||
// check-pass
|
||||
|
||||
#![feature(decl_macro)]
|
||||
|
||||
mod x {
|
||||
pub use std::ops::Deref as _;
|
||||
}
|
||||
|
||||
macro n() {
|
||||
pub use crate::x::*;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! p {
|
||||
() => { pub use crate::x::*; }
|
||||
}
|
||||
|
||||
macro m($y:ident) {
|
||||
mod $y {
|
||||
crate::n!(); // Reexport of `Deref` should not be imported in `main`
|
||||
crate::p!(); // Reexport of `Deref` should be imported into `main`
|
||||
}
|
||||
}
|
||||
|
||||
m!(y);
|
||||
|
||||
fn main() {
|
||||
use crate::y::*;
|
||||
(&()).deref();
|
||||
}
|
40
src/test/ui/underscore-imports/hygiene.rs
Normal file
40
src/test/ui/underscore-imports/hygiene.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Make sure that underscore imports have the same hygiene considerations as
|
||||
// other imports.
|
||||
|
||||
#![feature(decl_macro)]
|
||||
|
||||
mod x {
|
||||
pub use std::ops::Deref as _;
|
||||
}
|
||||
|
||||
|
||||
macro glob_import() {
|
||||
pub use crate::x::*;
|
||||
}
|
||||
|
||||
macro underscore_import() {
|
||||
use std::ops::DerefMut as _;
|
||||
}
|
||||
|
||||
mod y {
|
||||
crate::glob_import!();
|
||||
crate::underscore_import!();
|
||||
}
|
||||
|
||||
macro create_module($y:ident) {
|
||||
mod $y {
|
||||
crate::glob_import!();
|
||||
crate::underscore_import!();
|
||||
}
|
||||
}
|
||||
|
||||
create_module!(z);
|
||||
|
||||
fn main() {
|
||||
use crate::y::*;
|
||||
use crate::z::*;
|
||||
glob_import!();
|
||||
underscore_import!();
|
||||
(&()).deref(); //~ ERROR no method named `deref`
|
||||
(&mut ()).deref_mut(); //~ ERROR no method named `deref_mut`
|
||||
}
|
27
src/test/ui/underscore-imports/hygiene.stderr
Normal file
27
src/test/ui/underscore-imports/hygiene.stderr
Normal file
|
@ -0,0 +1,27 @@
|
|||
error[E0599]: no method named `deref` found for type `&()` in the current scope
|
||||
--> $DIR/hygiene.rs:38:11
|
||||
|
|
||||
LL | (&()).deref();
|
||||
| ^^^^^ method not found in `&()`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is in scope
|
||||
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
||||
|
|
||||
LL | use std::ops::Deref;
|
||||
|
|
||||
|
||||
error[E0599]: no method named `deref_mut` found for type `&mut ()` in the current scope
|
||||
--> $DIR/hygiene.rs:39:15
|
||||
|
|
||||
LL | (&mut ()).deref_mut();
|
||||
| ^^^^^^^^^ method not found in `&mut ()`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is in scope
|
||||
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
||||
|
|
||||
LL | use std::ops::DerefMut;
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
45
src/test/ui/underscore-imports/macro-expanded.rs
Normal file
45
src/test/ui/underscore-imports/macro-expanded.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Check that macro expanded underscore imports behave as expected
|
||||
|
||||
// check-pass
|
||||
|
||||
#![feature(decl_macro, rustc_attrs)]
|
||||
|
||||
mod x {
|
||||
pub use std::ops::Not as _;
|
||||
}
|
||||
|
||||
macro m() {
|
||||
mod w {
|
||||
mod y {
|
||||
pub use std::ops::Deref as _;
|
||||
}
|
||||
use crate::x::*;
|
||||
use self::y::*;
|
||||
use std::ops::DerefMut as _;
|
||||
fn f() {
|
||||
false.not();
|
||||
(&()).deref();
|
||||
(&mut ()).deref_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_macro_transparency = "transparent"]
|
||||
macro n() {
|
||||
mod z {
|
||||
pub use std::ops::Deref as _;
|
||||
}
|
||||
use crate::x::*;
|
||||
use crate::z::*;
|
||||
use std::ops::DerefMut as _;
|
||||
fn f() {
|
||||
false.not();
|
||||
(&()).deref();
|
||||
(&mut ()).deref_mut();
|
||||
}
|
||||
}
|
||||
|
||||
m!();
|
||||
n!();
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Reference in a new issue