Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macros: support invocation paths (e.g. foo::bar!()) behind #![feature(use_extern_macros)] #38082

Merged
merged 3 commits into from
Dec 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,11 +501,9 @@ impl<'b> Resolver<'b> {
})
}

pub fn get_macro(&mut self, binding: &'b NameBinding<'b>) -> Rc<SyntaxExtension> {
let def_id = match binding.kind {
NameBindingKind::Def(Def::Macro(def_id)) => def_id,
NameBindingKind::Import { binding, .. } => return self.get_macro(binding),
NameBindingKind::Ambiguity { b1, .. } => return self.get_macro(b1),
pub fn get_macro(&mut self, def: Def) -> Rc<SyntaxExtension> {
let def_id = match def {
Def::Macro(def_id) => def_id,
_ => panic!("Expected Def::Macro(..)"),
};
if let Some(ext) = self.macro_map.get(&def_id) {
Expand Down
35 changes: 28 additions & 7 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ pub struct ModuleS<'a> {

resolutions: RefCell<FxHashMap<(Name, Namespace), &'a RefCell<NameResolution<'a>>>>,
legacy_macro_resolutions: RefCell<Vec<(Mark, Name, Span)>>,
macro_resolutions: RefCell<Vec<(Box<[Ident]>, PathScope, Span)>>,

// Macro invocations that can expand into items in this module.
unresolved_invocations: RefCell<FxHashSet<Mark>>,
Expand Down Expand Up @@ -811,6 +812,7 @@ impl<'a> ModuleS<'a> {
normal_ancestor_id: None,
resolutions: RefCell::new(FxHashMap()),
legacy_macro_resolutions: RefCell::new(Vec::new()),
macro_resolutions: RefCell::new(Vec::new()),
unresolved_invocations: RefCell::new(FxHashSet()),
no_implicit_prelude: false,
glob_importers: RefCell::new(Vec::new()),
Expand Down Expand Up @@ -925,6 +927,14 @@ impl<'a> NameBinding<'a> {
}
}

fn get_macro(&self, resolver: &mut Resolver<'a>) -> Rc<SyntaxExtension> {
match self.kind {
NameBindingKind::Import { binding, .. } => binding.get_macro(resolver),
NameBindingKind::Ambiguity { b1, .. } => b1.get_macro(resolver),
_ => resolver.get_macro(self.def()),
}
}

// We sometimes need to treat variants as `pub` for backwards compatibility
fn pseudo_vis(&self) -> ty::Visibility {
if self.is_variant() { ty::Visibility::Public } else { self.vis }
Expand Down Expand Up @@ -1308,6 +1318,7 @@ impl<'a> Resolver<'a> {
pub fn resolve_crate(&mut self, krate: &Crate) {
ImportResolver { resolver: self }.finalize_imports();
self.current_module = self.graph_root;
self.finalize_current_module_macro_resolutions();
visit::walk_crate(self, krate);

check_unused::check_crate(self, krate);
Expand Down Expand Up @@ -2351,10 +2362,13 @@ impl<'a> Resolver<'a> {

let binding = if let Some(module) = module {
self.resolve_name_in_module(module, ident.name, ns, false, record_used)
} else if opt_ns == Some(MacroNS) {
self.resolve_lexical_macro_path_segment(ident.name, ns, record_used)
} else {
match self.resolve_ident_in_lexical_scope(ident, ns, record_used) {
Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
Some(LexicalScopeBinding::Def(def)) if opt_ns.is_some() => {
Some(LexicalScopeBinding::Def(def))
if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => {
return PathResult::NonModule(PathResolution {
base_def: def,
depth: path.len() - 1,
Expand All @@ -2370,7 +2384,7 @@ impl<'a> Resolver<'a> {
module = Some(next_module);
} else if binding.def() == Def::Err {
return PathResult::NonModule(err_path_resolution());
} else if opt_ns.is_some() {
} else if opt_ns.is_some() && !(opt_ns == Some(MacroNS) && !is_last) {
return PathResult::NonModule(PathResolution {
base_def: binding.def(),
depth: path.len() - i - 1,
Expand Down Expand Up @@ -3051,15 +3065,22 @@ impl<'a> Resolver<'a> {

for &AmbiguityError { span, name, b1, b2, lexical } in &self.ambiguity_errors {
if !reported_spans.insert(span) { continue }
let msg1 = format!("`{}` could resolve to the name imported here", name);
let msg2 = format!("`{}` could also resolve to the name imported here", name);
let participle = |binding: &NameBinding| {
if binding.is_import() { "imported" } else { "defined" }
};
let msg1 = format!("`{}` could resolve to the name {} here", name, participle(b1));
let msg2 = format!("`{}` could also resolve to the name {} here", name, participle(b2));
self.session.struct_span_err(span, &format!("`{}` is ambiguous", name))
.span_note(b1.span, &msg1)
.span_note(b2.span, &msg2)
.note(&if lexical || !b1.is_glob_import() {
"macro-expanded macro imports do not shadow".to_owned()
} else {
.note(&if !lexical && b1.is_glob_import() {
format!("consider adding an explicit import of `{}` to disambiguate", name)
} else if let Def::Macro(..) = b1.def() {
format!("macro-expanded {} do not shadow",
if b1.is_import() { "macro imports" } else { "macros" })
} else {
format!("macro-expanded {} do not shadow when used in a macro invocation path",
if b1.is_import() { "imports" } else { "items" })
})
.emit();
}
Expand Down
114 changes: 77 additions & 37 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use {Module, ModuleKind, NameBinding, NameBindingKind, Resolver, AmbiguityError};
use {AmbiguityError, Resolver, ResolutionError, resolve_error};
use {Module, ModuleKind, NameBinding, NameBindingKind, PathScope, PathResult};
use Namespace::{self, MacroNS};
use build_reduced_graph::BuildReducedGraphVisitor;
use resolve_imports::ImportResolver;
Expand All @@ -25,6 +26,7 @@ use syntax::ext::base::{NormalTT, SyntaxExtension};
use syntax::ext::expand::Expansion;
use syntax::ext::hygiene::Mark;
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{emit_feature_err, GateIssue};
use syntax::fold::Folder;
use syntax::ptr::P;
use syntax::util::lev_distance::find_best_match_for_name;
Expand Down Expand Up @@ -193,7 +195,7 @@ impl<'a> base::Resolver for Resolver<'a> {
fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
for i in 0..attrs.len() {
match self.builtin_macros.get(&attrs[i].name()).cloned() {
Some(binding) => match *self.get_macro(binding) {
Some(binding) => match *binding.get_macro(self) {
MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => {
return Some(attrs.remove(i))
}
Expand All @@ -207,75 +209,103 @@ impl<'a> base::Resolver for Resolver<'a> {

fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy> {
if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
self.session.span_err(path.span, "expected macro name without module separators");
let ast::Path { ref segments, global, span } = *path;
if segments.iter().any(|segment| !segment.parameters.is_empty()) {
let kind =
if segments.last().unwrap().parameters.is_empty() { "module" } else { "macro" };
let msg = format!("type parameters are not allowed on {}s", kind);
self.session.span_err(path.span, &msg);
return Err(Determinacy::Determined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: error recovery here would be trivial - just proceed further instead of returning Err.

}
let name = path.segments[0].identifier.name;

let path_scope = if global { PathScope::Global } else { PathScope::Lexical };
let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
let invocation = self.invocations[&scope];
self.current_module = invocation.module.get();

if path.len() > 1 || global {
if !self.use_extern_macros {
let msg = "non-ident macro paths are experimental";
let feature = "use_extern_macros";
emit_feature_err(&self.session.parse_sess, feature, span, GateIssue::Language, msg);
return Err(Determinacy::Determined);
}

let ext = match self.resolve_path(&path, path_scope, Some(MacroNS), None) {
PathResult::NonModule(path_res) => Ok(self.get_macro(path_res.base_def)),
PathResult::Module(..) => unreachable!(),
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
_ => Err(Determinacy::Determined),
};
self.current_module.macro_resolutions.borrow_mut()
.push((path.into_boxed_slice(), path_scope, span));
return ext;
}

let name = path[0].name;
let result = match self.resolve_legacy_scope(&invocation.legacy_scope, name, false) {
Some(MacroBinding::Legacy(binding)) => Ok(binding.ext.clone()),
Some(MacroBinding::Modern(binding)) => Ok(self.get_macro(binding)),
None => match self.resolve_in_item_lexical_scope(name, MacroNS, None) {
Some(binding) => Ok(self.get_macro(binding)),
None => return Err(if force {
Some(MacroBinding::Modern(binding)) => Ok(binding.get_macro(self)),
None => match self.resolve_lexical_macro_path_segment(name, MacroNS, None) {
Ok(binding) => Ok(binding.get_macro(self)),
Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
_ => {
let msg = format!("macro undefined: '{}!'", name);
let mut err = self.session.struct_span_err(path.span, &msg);
let mut err = self.session.struct_span_err(span, &msg);
self.suggest_macro_name(&name.as_str(), &mut err);
err.emit();
Determinacy::Determined
} else {
Determinacy::Undetermined
}),
return Err(Determinacy::Determined);
},
},
};

if self.use_extern_macros {
self.current_module.legacy_macro_resolutions.borrow_mut()
.push((scope, name, path.span));
self.current_module.legacy_macro_resolutions.borrow_mut().push((scope, name, span));
}
result
}
}

impl<'a> Resolver<'a> {
// Resolve the name in the module's lexical scope, excluding non-items.
fn resolve_in_item_lexical_scope(&mut self,
name: Name,
ns: Namespace,
record_used: Option<Span>)
-> Option<&'a NameBinding<'a>> {
// Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`)
pub fn resolve_lexical_macro_path_segment(&mut self,
name: Name,
ns: Namespace,
record_used: Option<Span>)
-> Result<&'a NameBinding<'a>, Determinacy> {
let mut module = self.current_module;
let mut potential_expanded_shadower = None;
let mut potential_expanded_shadower: Option<&NameBinding> = None;
loop {
// Since expanded macros may not shadow the lexical scope (enforced below),
// we can ignore unresolved invocations (indicated by the penultimate argument).
match self.resolve_name_in_module(module, name, ns, true, record_used) {
Ok(binding) => {
let span = match record_used {
Some(span) => span,
None => return Some(binding),
None => return Ok(binding),
};
if let Some(shadower) = potential_expanded_shadower {
self.ambiguity_errors.push(AmbiguityError {
span: span, name: name, b1: shadower, b2: binding, lexical: true,
});
return Some(shadower);
} else if binding.expansion == Mark::root() {
return Some(binding);
} else {
potential_expanded_shadower = Some(binding);
match potential_expanded_shadower {
Some(shadower) if shadower.def() != binding.def() => {
self.ambiguity_errors.push(AmbiguityError {
span: span, name: name, b1: shadower, b2: binding, lexical: true,
});
return Ok(shadower);
}
_ if binding.expansion == Mark::root() => return Ok(binding),
_ => potential_expanded_shadower = Some(binding),
}
},
Err(Determinacy::Undetermined) => return None,
Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
Err(Determinacy::Determined) => {}
}

match module.kind {
ModuleKind::Block(..) => module = module.parent.unwrap(),
ModuleKind::Def(..) => return potential_expanded_shadower,
ModuleKind::Def(..) => return match potential_expanded_shadower {
Some(binding) => Ok(binding),
None if record_used.is_some() => Err(Determinacy::Determined),
None => Err(Determinacy::Undetermined),
},
}
}
}
Expand Down Expand Up @@ -343,12 +373,22 @@ impl<'a> Resolver<'a> {

pub fn finalize_current_module_macro_resolutions(&mut self) {
let module = self.current_module;
for &(ref path, scope, span) in module.macro_resolutions.borrow().iter() {
match self.resolve_path(path, scope, Some(MacroNS), Some(span)) {
PathResult::NonModule(_) => {},
PathResult::Failed(msg, _) => {
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
}
_ => unreachable!(),
}
}

for &(mark, name, span) in module.legacy_macro_resolutions.borrow().iter() {
let legacy_scope = &self.invocations[&mark].legacy_scope;
let legacy_resolution = self.resolve_legacy_scope(legacy_scope, name, true);
let resolution = self.resolve_in_item_lexical_scope(name, MacroNS, Some(span));
let resolution = self.resolve_lexical_macro_path_segment(name, MacroNS, Some(span));
let (legacy_resolution, resolution) = match (legacy_resolution, resolution) {
(Some(legacy_resolution), Some(resolution)) => (legacy_resolution, resolution),
(Some(legacy_resolution), Ok(resolution)) => (legacy_resolution, resolution),
_ => continue,
};
let (legacy_span, participle) = match legacy_resolution {
Expand Down
17 changes: 16 additions & 1 deletion src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use self::ImportDirectiveSubclass::*;

use {Module, PerNS};
use {AmbiguityError, Module, PerNS};
use Namespace::{self, TypeNS, MacroNS};
use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding};
use Resolver;
Expand Down Expand Up @@ -73,6 +73,7 @@ pub struct NameResolution<'a> {
single_imports: SingleImports<'a>,
/// The least shadowable known binding for this name, or None if there are no known bindings.
pub binding: Option<&'a NameBinding<'a>>,
shadows_glob: Option<&'a NameBinding<'a>>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -151,6 +152,18 @@ impl<'a> Resolver<'a> {

if let Some(span) = record_used {
if let Some(binding) = resolution.binding {
if let Some(shadowed_glob) = resolution.shadows_glob {
// If we ignore unresolved invocations, we must forbid
// expanded shadowing to avoid time travel.
if ignore_unresolved_invocations &&
binding.expansion != Mark::root() &&
ns != MacroNS && // In MacroNS, `try_define` always forbids this shadowing
binding.def() != shadowed_glob.def() {
self.ambiguity_errors.push(AmbiguityError {
span: span, name: name, lexical: false, b1: binding, b2: shadowed_glob,
});
}
}
if self.record_use(name, ns, binding, span) {
return Ok(self.dummy_binding);
}
Expand Down Expand Up @@ -298,6 +311,7 @@ impl<'a> Resolver<'a> {
if binding.is_glob_import() {
if !old_binding.is_glob_import() &&
!(ns == MacroNS && old_binding.expansion != Mark::root()) {
resolution.shadows_glob = Some(binding);
} else if binding.def() != old_binding.def() {
resolution.binding = Some(this.ambiguity(old_binding, binding));
} else if !old_binding.vis.is_at_least(binding.vis, this) {
Expand All @@ -310,6 +324,7 @@ impl<'a> Resolver<'a> {
resolution.binding = Some(this.ambiguity(binding, old_binding));
} else {
resolution.binding = Some(binding);
resolution.shadows_glob = Some(old_binding);
}
} else {
return Err(old_binding);
Expand Down
7 changes: 1 addition & 6 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
&self.cx.ecfg.features.unwrap());
}

if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
self.cx.span_err(path.span, "expected macro name without module separators");
return kind.dummy(span);
}

let extname = path.segments[0].identifier.name;
let extname = path.segments.last().unwrap().identifier.name;
let ident = ident.unwrap_or(keywords::Invalid.ident());
let marked_tts = mark_tts(&tts, mark);
let opt_expanded = match *ext {
Expand Down
Loading