diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e89fb53c23619..e585f9939a014 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -3089,7 +3089,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // comparison fails frequently, and we want to avoid the expensive // `modern()` calls required for the span comparison whenever possible. use_name.name == def_name.name && - self.adjust_ident(use_name, def_parent_def_id).span.ctxt() == def_name.modern().span.ctxt() + use_name.span.ctxt().hygienic_eq(def_name.span.ctxt(), + self.expansion_that_defined(def_parent_def_id)) } fn expansion_that_defined(self, scope: DefId) -> Mark { @@ -3100,15 +3101,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { - ident = ident.modern(); - ident.span.adjust(self.expansion_that_defined(scope)); + ident.span.modernize_and_adjust(self.expansion_that_defined(scope)); ident } pub fn adjust_ident_and_get_scope(self, mut ident: Ident, scope: DefId, block: hir::HirId) -> (Ident, DefId) { - ident = ident.modern(); - let scope = match ident.span.adjust(self.expansion_that_defined(scope)) { + let scope = match ident.span.modernize_and_adjust(self.expansion_that_defined(scope)) { Some(actual_expansion) => self.hir().definitions().parent_module_of_macro_def(actual_expansion), None => self.hir().get_module_parent_by_hir_id(block), diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc/ty/query/on_disk_cache.rs index 24ba0744a688a..4dbc2ab1b358e 100644 --- a/src/librustc/ty/query/on_disk_cache.rs +++ b/src/librustc/ty/query/on_disk_cache.rs @@ -844,9 +844,8 @@ impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx if span_data.ctxt == SyntaxContext::empty() { TAG_NO_EXPANSION_INFO.encode(self) } else { - let mark = span_data.ctxt.outer(); - - if let Some(expn_info) = mark.expn_info() { + let (mark, expn_info) = span_data.ctxt.outer_and_expn_info(); + if let Some(expn_info) = expn_info { if let Some(pos) = self.expn_info_shorthands.get(&mark).cloned() { TAG_EXPANSION_INFO_SHORTHAND.encode(self)?; pos.encode(self) diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 1d40d004e2ddc..dd69d35831318 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -128,14 +128,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Walk up the macro expansion chain until we reach a non-expanded span. // We also stop at the function body level because no line stepping can occur // at the level above that. - let mut span = source_info.span; - while span.ctxt() != NO_EXPANSION && span.ctxt() != self.mir.span.ctxt() { - if let Some(info) = span.ctxt().outer_expn_info() { - span = info.call_site; - } else { - break; - } - } + let span = syntax_pos::hygiene::walk_chain(source_info.span, self.mir.span.ctxt()); let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); // Use span of the outermost expansion site, while keeping the original lexical scope. (scope, span) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 99abe69017da7..9b9cf80f822b0 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2330,14 +2330,12 @@ impl<'a> Resolver<'a> { let orig_current_module = self.current_module; match module { ModuleOrUniformRoot::Module(module) => { - ident.span = ident.span.modern(); - if let Some(def) = ident.span.adjust(module.expansion) { + if let Some(def) = ident.span.modernize_and_adjust(module.expansion) { self.current_module = self.macro_def_scope(def); } } ModuleOrUniformRoot::ExternPrelude => { - ident.span = ident.span.modern(); - ident.span.adjust(Mark::root()); + ident.span.modernize_and_adjust(Mark::root()); } ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => { @@ -4525,7 +4523,7 @@ impl<'a> Resolver<'a> { let mut ident = ident; if ident.span.glob_adjust( module.expansion, - binding.span.ctxt().modern(), + binding.span, ).is_none() { continue } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index c0ff7b310b581..d24d8f8c2b5b1 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -388,7 +388,7 @@ impl<'a> Resolver<'a> { None => return Err((Undetermined, Weak::Yes)), }; let (orig_current_module, mut ident) = (self.current_module, ident.modern()); - match ident.span.glob_adjust(module.expansion, glob_import.span.ctxt().modern()) { + match ident.span.glob_adjust(module.expansion, glob_import.span) { Some(Some(def)) => self.current_module = self.macro_def_scope(def), Some(None) => {} None => continue, @@ -605,8 +605,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 scope = match ident.span.reverse_glob_adjust(module.expansion, - directive.span.ctxt().modern()) { + 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, None => continue, @@ -1359,8 +1358,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { resolution.borrow().binding().map(|binding| (ident, binding)) }).collect::>(); for ((mut ident, ns), binding) in bindings { - let scope = match ident.span.reverse_glob_adjust(module.expansion, - directive.span.ctxt().modern()) { + let scope = match ident.span.reverse_glob_adjust(module.expansion, directive.span) { Some(Some(def)) => self.macro_def_scope(def), Some(None) => self.current_module, None => continue, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ae1e5116c676e..3c65f8e0329d1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2083,13 +2083,6 @@ impl<'a> Parser<'a> { hi = path.span; return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs)); } - if self.span.rust_2018() && self.check_keyword(kw::Async) { - return if self.is_async_block() { // check for `async {` and `async move {` - self.parse_async_block(attrs) - } else { - self.parse_lambda_expr(attrs) - }; - } if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { return self.parse_lambda_expr(attrs); } @@ -2161,6 +2154,16 @@ impl<'a> Parser<'a> { assert!(self.eat_keyword(kw::Try)); return self.parse_try_block(lo, attrs); } + + // Span::rust_2018() is somewhat expensive; don't get it repeatedly. + let is_span_rust_2018 = self.span.rust_2018(); + if is_span_rust_2018 && self.check_keyword(kw::Async) { + return if self.is_async_block() { // check for `async {` and `async move {` + self.parse_async_block(attrs) + } else { + self.parse_lambda_expr(attrs) + }; + } if self.eat_keyword(kw::Return) { if self.token.can_begin_expr() { let e = self.parse_expr()?; @@ -2196,7 +2199,7 @@ impl<'a> Parser<'a> { db.span_label(self.span, "expected expression"); db.note("variable declaration using `let` is a statement"); return Err(db); - } else if self.span.rust_2018() && self.eat_keyword(kw::Await) { + } else if is_span_rust_2018 && self.eat_keyword(kw::Await) { let (await_hi, e_kind) = self.parse_await_macro_or_alt(lo, self.prev_span)?; hi = await_hi; ex = e_kind; diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 445d4271d8905..213993996a63c 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -5,6 +5,26 @@ //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. //! DOI=10.1017/S0956796812000093 +// Hygiene data is stored in a global variable and accessed via TLS, which +// means that accesses are somewhat expensive. (`HygieneData::with` +// encapsulates a single access.) Therefore, on hot code paths it is worth +// ensuring that multiple HygieneData accesses are combined into a single +// `HygieneData::with`. +// +// This explains why `HygieneData`, `SyntaxContext` and `Mark` have interfaces +// with a certain amount of redundancy in them. For example, +// `SyntaxContext::outer_expn_info` combines `SyntaxContext::outer` and +// `Mark::expn_info` so that two `HygieneData` accesses can be performed within +// a single `HygieneData::with` call. +// +// It also explains why many functions appear in `HygieneData` and again in +// `SyntaxContext` or `Mark`. For example, `HygieneData::outer` and +// `SyntaxContext::outer` do the same thing, but the former is for use within a +// `HygieneData::with` call while the latter is for use outside such a call. +// When modifying this file it is important to understand this distinction, +// because getting it wrong can lead to nested `HygieneData::with` calls that +// trigger runtime aborts. (Fortunately these are obvious and easy to fix.) + use crate::GLOBALS; use crate::Span; use crate::edition::Edition; @@ -98,7 +118,7 @@ impl Mark { #[inline] pub fn expn_info(self) -> Option { - HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone()) + HygieneData::with(|data| data.expn_info(self)) } #[inline] @@ -200,10 +220,6 @@ impl HygieneData { GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut())) } - fn outer(&self, ctxt: SyntaxContext) -> Mark { - self.syntax_contexts[ctxt.0 as usize].outer_mark - } - fn expn_info(&self, mark: Mark) -> Option { self.marks[mark.0 as usize].expn_info.clone() } @@ -217,12 +233,174 @@ impl HygieneData { } true } + + fn default_transparency(&self, mark: Mark) -> Transparency { + self.marks[mark.0 as usize].default_transparency + } + + fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_contexts[ctxt.0 as usize].opaque + } + + fn modern_and_legacy(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_contexts[ctxt.0 as usize].opaque_and_semitransparent + } + + fn outer(&self, ctxt: SyntaxContext) -> Mark { + self.syntax_contexts[ctxt.0 as usize].outer_mark + } + + fn transparency(&self, ctxt: SyntaxContext) -> Transparency { + self.syntax_contexts[ctxt.0 as usize].transparency + } + + fn prev_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_contexts[ctxt.0 as usize].prev_ctxt + } + + fn remove_mark(&self, ctxt: &mut SyntaxContext) -> Mark { + let outer_mark = self.syntax_contexts[ctxt.0 as usize].outer_mark; + *ctxt = self.prev_ctxt(*ctxt); + outer_mark + } + + fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(Mark, Transparency)> { + let mut marks = Vec::new(); + while ctxt != SyntaxContext::empty() { + let outer_mark = self.outer(ctxt); + let transparency = self.transparency(ctxt); + let prev_ctxt = self.prev_ctxt(ctxt); + marks.push((outer_mark, transparency)); + ctxt = prev_ctxt; + } + marks.reverse(); + marks + } + + fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { + while span.ctxt() != crate::NO_EXPANSION && span.ctxt() != to { + if let Some(info) = self.expn_info(self.outer(span.ctxt())) { + span = info.call_site; + } else { + break; + } + } + span + } + + fn adjust(&self, ctxt: &mut SyntaxContext, expansion: Mark) -> Option { + let mut scope = None; + while !self.is_descendant_of(expansion, self.outer(*ctxt)) { + scope = Some(self.remove_mark(ctxt)); + } + scope + } + + fn apply_mark(&mut self, ctxt: SyntaxContext, mark: Mark) -> SyntaxContext { + assert_ne!(mark, Mark::root()); + self.apply_mark_with_transparency(ctxt, mark, self.default_transparency(mark)) + } + + fn apply_mark_with_transparency(&mut self, ctxt: SyntaxContext, mark: Mark, + transparency: Transparency) -> SyntaxContext { + assert_ne!(mark, Mark::root()); + if transparency == Transparency::Opaque { + return self.apply_mark_internal(ctxt, mark, transparency); + } + + let call_site_ctxt = + self.expn_info(mark).map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()); + let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { + self.modern(call_site_ctxt) + } else { + self.modern_and_legacy(call_site_ctxt) + }; + + if call_site_ctxt == SyntaxContext::empty() { + return self.apply_mark_internal(ctxt, mark, transparency); + } + + // Otherwise, `mark` is a macros 1.0 definition and the call site is in a + // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. + // + // In this case, the tokens from the macros 1.0 definition inherit the hygiene + // at their invocation. That is, we pretend that the macros 1.0 definition + // was defined at its invocation (i.e., inside the macros 2.0 definition) + // so that the macros 2.0 definition remains hygienic. + // + // See the example at `test/run-pass/hygiene/legacy_interaction.rs`. + for (mark, transparency) in self.marks(ctxt) { + call_site_ctxt = self.apply_mark_internal(call_site_ctxt, mark, transparency); + } + self.apply_mark_internal(call_site_ctxt, mark, transparency) + } + + fn apply_mark_internal(&mut self, ctxt: SyntaxContext, mark: Mark, transparency: Transparency) + -> SyntaxContext { + let syntax_contexts = &mut self.syntax_contexts; + let mut opaque = syntax_contexts[ctxt.0 as usize].opaque; + let mut opaque_and_semitransparent = + syntax_contexts[ctxt.0 as usize].opaque_and_semitransparent; + + if transparency >= Transparency::Opaque { + let prev_ctxt = opaque; + opaque = *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { + let new_opaque = SyntaxContext(syntax_contexts.len() as u32); + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + transparency, + prev_ctxt, + opaque: new_opaque, + opaque_and_semitransparent: new_opaque, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque + }); + } + + if transparency >= Transparency::SemiTransparent { + let prev_ctxt = opaque_and_semitransparent; + opaque_and_semitransparent = + *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { + let new_opaque_and_semitransparent = + SyntaxContext(syntax_contexts.len() as u32); + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + transparency, + prev_ctxt, + opaque, + opaque_and_semitransparent: new_opaque_and_semitransparent, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque_and_semitransparent + }); + } + + let prev_ctxt = ctxt; + *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { + let new_opaque_and_semitransparent_and_transparent = + SyntaxContext(syntax_contexts.len() as u32); + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + transparency, + prev_ctxt, + opaque, + opaque_and_semitransparent, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque_and_semitransparent_and_transparent + }) + } } pub fn clear_markings() { HygieneData::with(|data| data.markings = FxHashMap::default()); } +pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { + HygieneData::with(|data| data.walk_chain(span, to)) +} + impl SyntaxContext { #[inline] pub const fn empty() -> Self { @@ -269,104 +447,13 @@ impl SyntaxContext { /// Extend a syntax context with a given mark and default transparency for that mark. pub fn apply_mark(self, mark: Mark) -> SyntaxContext { - assert_ne!(mark, Mark::root()); - self.apply_mark_with_transparency( - mark, HygieneData::with(|data| data.marks[mark.0 as usize].default_transparency) - ) + HygieneData::with(|data| data.apply_mark(self, mark)) } /// Extend a syntax context with a given mark and transparency pub fn apply_mark_with_transparency(self, mark: Mark, transparency: Transparency) -> SyntaxContext { - assert_ne!(mark, Mark::root()); - if transparency == Transparency::Opaque { - return self.apply_mark_internal(mark, transparency); - } - - let call_site_ctxt = - mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()); - let call_site_ctxt = if transparency == Transparency::SemiTransparent { - call_site_ctxt.modern() - } else { - call_site_ctxt.modern_and_legacy() - }; - - if call_site_ctxt == SyntaxContext::empty() { - return self.apply_mark_internal(mark, transparency); - } - - // Otherwise, `mark` is a macros 1.0 definition and the call site is in a - // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. - // - // In this case, the tokens from the macros 1.0 definition inherit the hygiene - // at their invocation. That is, we pretend that the macros 1.0 definition - // was defined at its invocation (i.e., inside the macros 2.0 definition) - // so that the macros 2.0 definition remains hygienic. - // - // See the example at `test/run-pass/hygiene/legacy_interaction.rs`. - let mut ctxt = call_site_ctxt; - for (mark, transparency) in self.marks() { - ctxt = ctxt.apply_mark_internal(mark, transparency); - } - ctxt.apply_mark_internal(mark, transparency) - } - - fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxContext { - HygieneData::with(|data| { - let syntax_contexts = &mut data.syntax_contexts; - let mut opaque = syntax_contexts[self.0 as usize].opaque; - let mut opaque_and_semitransparent = - syntax_contexts[self.0 as usize].opaque_and_semitransparent; - - if transparency >= Transparency::Opaque { - let prev_ctxt = opaque; - opaque = *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { - let new_opaque = SyntaxContext(syntax_contexts.len() as u32); - syntax_contexts.push(SyntaxContextData { - outer_mark: mark, - transparency, - prev_ctxt, - opaque: new_opaque, - opaque_and_semitransparent: new_opaque, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque - }); - } - - if transparency >= Transparency::SemiTransparent { - let prev_ctxt = opaque_and_semitransparent; - opaque_and_semitransparent = - *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { - let new_opaque_and_semitransparent = - SyntaxContext(syntax_contexts.len() as u32); - syntax_contexts.push(SyntaxContextData { - outer_mark: mark, - transparency, - prev_ctxt, - opaque, - opaque_and_semitransparent: new_opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent - }); - } - - let prev_ctxt = self; - *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| { - let new_opaque_and_semitransparent_and_transparent = - SyntaxContext(syntax_contexts.len() as u32); - syntax_contexts.push(SyntaxContextData { - outer_mark: mark, - transparency, - prev_ctxt, - opaque, - opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent_and_transparent - }) - }) + HygieneData::with(|data| data.apply_mark_with_transparency(self, mark, transparency)) } /// Pulls a single mark off of the syntax context. This effectively moves the @@ -386,24 +473,11 @@ impl SyntaxContext { /// invocation of f that created g1. /// Returns the mark that was removed. pub fn remove_mark(&mut self) -> Mark { - HygieneData::with(|data| { - let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark; - *self = data.syntax_contexts[self.0 as usize].prev_ctxt; - outer_mark - }) + HygieneData::with(|data| data.remove_mark(self)) } - pub fn marks(mut self) -> Vec<(Mark, Transparency)> { - HygieneData::with(|data| { - let mut marks = Vec::new(); - while self != SyntaxContext::empty() { - let ctxt_data = &data.syntax_contexts[self.0 as usize]; - marks.push((ctxt_data.outer_mark, ctxt_data.transparency)); - self = ctxt_data.prev_ctxt; - } - marks.reverse(); - marks - }) + pub fn marks(self) -> Vec<(Mark, Transparency)> { + HygieneData::with(|data| data.marks(self)) } /// Adjust this context for resolution in a scope created by the given expansion. @@ -431,11 +505,15 @@ impl SyntaxContext { /// This returns the expansion whose definition scope we use to privacy check the resolution, /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). pub fn adjust(&mut self, expansion: Mark) -> Option { - let mut scope = None; - while !expansion.outer_is_descendant_of(*self) { - scope = Some(self.remove_mark()); - } - scope + HygieneData::with(|data| data.adjust(self, expansion)) + } + + /// Like `SyntaxContext::adjust`, but also modernizes `self`. + pub fn modernize_and_adjust(&mut self, expansion: Mark) -> Option { + HygieneData::with(|data| { + *self = data.modern(*self); + data.adjust(self, expansion) + }) } /// Adjust this context for resolution in a scope created by the given expansion @@ -463,19 +541,21 @@ impl SyntaxContext { /// ``` /// This returns `None` if the context cannot be glob-adjusted. /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). - pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) - -> Option> { - let mut scope = None; - while !expansion.outer_is_descendant_of(glob_ctxt) { - scope = Some(glob_ctxt.remove_mark()); - if self.remove_mark() != scope.unwrap() { + pub fn glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { + HygieneData::with(|data| { + let mut scope = None; + let mut glob_ctxt = data.modern(glob_span.ctxt()); + while !data.is_descendant_of(expansion, data.outer(glob_ctxt)) { + scope = Some(data.remove_mark(&mut glob_ctxt)); + if data.remove_mark(self) != scope.unwrap() { + return None; + } + } + if data.adjust(self, expansion).is_some() { return None; } - } - if self.adjust(expansion).is_some() { - return None; - } - Some(scope) + Some(scope) + }) } /// Undo `glob_adjust` if possible: @@ -485,32 +565,43 @@ impl SyntaxContext { /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); /// } /// ``` - pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) + pub fn reverse_glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { - if self.adjust(expansion).is_some() { - return None; - } + HygieneData::with(|data| { + if data.adjust(self, expansion).is_some() { + return None; + } - let mut marks = Vec::new(); - while !expansion.outer_is_descendant_of(glob_ctxt) { - marks.push(glob_ctxt.remove_mark()); - } + let mut glob_ctxt = data.modern(glob_span.ctxt()); + let mut marks = Vec::new(); + while !data.is_descendant_of(expansion, data.outer(glob_ctxt)) { + marks.push(data.remove_mark(&mut glob_ctxt)); + } - let scope = marks.last().cloned(); - while let Some(mark) = marks.pop() { - *self = self.apply_mark(mark); - } - Some(scope) + let scope = marks.last().cloned(); + while let Some(mark) = marks.pop() { + *self = data.apply_mark(*self, mark); + } + Some(scope) + }) + } + + pub fn hygienic_eq(self, other: SyntaxContext, mark: Mark) -> bool { + HygieneData::with(|data| { + let mut self_modern = data.modern(self); + data.adjust(&mut self_modern, mark); + self_modern == data.modern(other) + }) } #[inline] pub fn modern(self) -> SyntaxContext { - HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque) + HygieneData::with(|data| data.modern(self)) } #[inline] pub fn modern_and_legacy(self) -> SyntaxContext { - HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent) + HygieneData::with(|data| data.modern_and_legacy(self)) } #[inline] @@ -525,6 +616,16 @@ impl SyntaxContext { HygieneData::with(|data| data.expn_info(data.outer(self))) } + /// `ctxt.outer_and_expn_info()` is equivalent to but faster than + /// `{ let outer = ctxt.outer(); (outer, outer.expn_info()) }`. + #[inline] + pub fn outer_and_expn_info(self) -> (Mark, Option) { + HygieneData::with(|data| { + let outer = data.outer(self); + (outer, data.expn_info(outer)) + }) + } + pub fn dollar_crate_name(self) -> Symbol { HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name) } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 30e075a339613..24aa82184ced5 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -535,19 +535,26 @@ impl Span { } #[inline] - pub fn glob_adjust(&mut self, expansion: Mark, glob_ctxt: SyntaxContext) - -> Option> { + pub fn modernize_and_adjust(&mut self, expansion: Mark) -> Option { let mut span = self.data(); - let mark = span.ctxt.glob_adjust(expansion, glob_ctxt); + let mark = span.ctxt.modernize_and_adjust(expansion); *self = Span::new(span.lo, span.hi, span.ctxt); mark } #[inline] - pub fn reverse_glob_adjust(&mut self, expansion: Mark, glob_ctxt: SyntaxContext) + pub fn glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { + let mut span = self.data(); + let mark = span.ctxt.glob_adjust(expansion, glob_span); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn reverse_glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { let mut span = self.data(); - let mark = span.ctxt.reverse_glob_adjust(expansion, glob_ctxt); + let mark = span.ctxt.reverse_glob_adjust(expansion, glob_span); *self = Span::new(span.lo, span.hi, span.ctxt); mark }