diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 792edf4d12b..2052918747b 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -45,7 +45,7 @@ use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; use syntax::codemap::CodeMap; -use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext}; +use syntax::ext::hygiene::{Mark, Transparency, SyntaxContext}; use syntax::ast::{self, Name, NodeId, Ident, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; @@ -1988,7 +1988,7 @@ impl<'a> Resolver<'a> { // When resolving `$crate` from a `macro_rules!` invoked in a `macro`, // we don't want to pretend that the `macro_rules!` definition is in the `macro` // as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks. - ctxt.marks().into_iter().find(|&mark| mark.kind() != MarkKind::Modern) + ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque) } else { ctxt = ctxt.modern(); ctxt.adjust(Mark::root()) diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 649e8858b09..65dec8ad16c 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator}; use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver}; use syntax::ext::expand::{Expansion, ExpansionKind, Invocation, InvocationKind, find_attr_invoc}; -use syntax::ext::hygiene::{self, Mark, MarkKind}; +use syntax::ext::hygiene::{self, Mark, Transparency}; use syntax::ext::placeholders::placeholder; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{self, emit_feature_err, GateIssue}; @@ -331,9 +331,9 @@ impl<'a> base::Resolver for Resolver<'a> { self.unused_macros.remove(&def_id); let ext = self.get_macro(def); if ext.is_modern() { - invoc.expansion_data.mark.set_kind(MarkKind::Modern); + invoc.expansion_data.mark.set_transparency(Transparency::Opaque); } else if def_id.krate == BUILTIN_MACROS_CRATE { - invoc.expansion_data.mark.set_kind(MarkKind::Builtin); + invoc.expansion_data.mark.set_is_builtin(true); } Ok(Some(ext)) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ac8088507dd..70c4324a056 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -18,7 +18,7 @@ use util::parser::{self, AssocOp, Fixity}; use attr; use codemap::{self, CodeMap}; use syntax_pos::{self, BytePos}; -use syntax_pos::hygiene::{Mark, MarkKind, SyntaxContext}; +use syntax_pos::hygiene::{Mark, SyntaxContext}; use parse::token::{self, BinOpToken, Token}; use parse::lexer::comments; use parse::{self, ParseSess}; @@ -842,7 +842,7 @@ pub trait PrintState<'a> { fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> { if let Some(mark) = ctxt.adjust(Mark::root()) { // Make a best effort to print something that complies - if mark.kind() == MarkKind::Builtin { + if mark.is_builtin() { if let Some(name) = std_inject::injected_crate_name() { self.writer().word("::")?; self.writer().word(name)?; diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 64cd31a485c..cd2b8b2bff8 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -43,21 +43,41 @@ pub struct Mark(u32); #[derive(Debug)] struct MarkData { parent: Mark, - kind: MarkKind, + transparency: Transparency, + is_builtin: bool, expn_info: Option, } +/// A property of a macro expansion that determines how identifiers +/// produced by that expansion are resolved. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum MarkKind { - Modern, - Builtin, - Legacy, +pub enum Transparency { + /// Identifier produced by a transparent expansion is always resolved at call-site. + /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. + /// (Not used yet.) + Transparent, + /// Identifier produced by a semi-transparent expansion may be resolved + /// either at call-site or at definition-site. + /// If it's a local variable, label or `$crate` then it's resolved at def-site. + /// Otherwise it's resolved at call-site. + /// `macro_rules` macros behave like this, built-in macros currently behave like this too, + /// but that's an implementation detail. + SemiTransparent, + /// Identifier produced by an opaque expansion is always resolved at definition-site. + /// Def-site spans in procedural macros, identifiers from `macro` by default use this. + Opaque, } impl Mark { pub fn fresh(parent: Mark) -> Self { HygieneData::with(|data| { - data.marks.push(MarkData { parent: parent, kind: MarkKind::Legacy, expn_info: None }); + data.marks.push(MarkData { + parent, + // By default expansions behave like `macro_rules`. + transparency: Transparency::SemiTransparent, + is_builtin: false, + expn_info: None, + }); Mark(data.marks.len() as u32 - 1) }) } @@ -97,23 +117,31 @@ impl Mark { pub fn modern(mut self) -> Mark { HygieneData::with(|data| { - loop { - if self == Mark::root() || data.marks[self.0 as usize].kind == MarkKind::Modern { - return self; - } + while data.marks[self.0 as usize].transparency != Transparency::Opaque { self = data.marks[self.0 as usize].parent; } + self }) } #[inline] - pub fn kind(self) -> MarkKind { - HygieneData::with(|data| data.marks[self.0 as usize].kind) + pub fn transparency(self) -> Transparency { + HygieneData::with(|data| data.marks[self.0 as usize].transparency) } #[inline] - pub fn set_kind(self, kind: MarkKind) { - HygieneData::with(|data| data.marks[self.0 as usize].kind = kind) + pub fn set_transparency(self, transparency: Transparency) { + HygieneData::with(|data| data.marks[self.0 as usize].transparency = transparency) + } + + #[inline] + pub fn is_builtin(self) -> bool { + HygieneData::with(|data| data.marks[self.0 as usize].is_builtin) + } + + #[inline] + pub fn set_is_builtin(self, is_builtin: bool) { + HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin) } pub fn is_descendant_of(mut self, ancestor: Mark) -> bool { @@ -169,7 +197,10 @@ impl HygieneData { HygieneData { marks: vec![MarkData { parent: Mark::root(), - kind: MarkKind::Builtin, + // If the root is opaque, then loops searching for an opaque mark + // will automatically stop after reaching it. + transparency: Transparency::Opaque, + is_builtin: true, expn_info: None, }], syntax_contexts: vec![SyntaxContextData { @@ -215,8 +246,9 @@ impl SyntaxContext { HygieneData::with(|data| { data.marks.push(MarkData { parent: Mark::root(), - kind: MarkKind::Legacy, - expn_info: Some(expansion_info) + transparency: Transparency::SemiTransparent, + is_builtin: false, + expn_info: Some(expansion_info), }); let mark = Mark(data.marks.len() as u32 - 1); @@ -232,7 +264,7 @@ impl SyntaxContext { /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { - if mark.kind() == MarkKind::Modern { + if mark.transparency() == Transparency::Opaque { return self.apply_mark_internal(mark); } @@ -262,7 +294,7 @@ impl SyntaxContext { HygieneData::with(|data| { let syntax_contexts = &mut data.syntax_contexts; let mut modern = syntax_contexts[self.0 as usize].modern; - if data.marks[mark.0 as usize].kind == MarkKind::Modern { + if data.marks[mark.0 as usize].transparency == Transparency::Opaque { modern = *data.markings.entry((modern, mark)).or_insert_with(|| { let len = syntax_contexts.len() as u32; syntax_contexts.push(SyntaxContextData {