From 8797e8cabdd47d1731bbd12099d0cd5503d22d76 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 31 May 2019 16:50:44 +1000 Subject: [PATCH 01/16] Move `modern` calls inside `glob_adjust` and `reverse_glob_adjust`. --- src/librustc_resolve/lib.rs | 2 +- src/librustc_resolve/resolve_imports.rs | 8 +++----- src/libsyntax_pos/hygiene.rs | 7 ++++--- src/libsyntax_pos/lib.rs | 9 ++++----- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 99abe69017da7..5f076d16bed5e 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4525,7 +4525,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_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 445d4271d8905..39a7124c756d4 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -463,9 +463,9 @@ 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> { + pub fn glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { let mut scope = None; + let mut glob_ctxt = glob_span.ctxt().modern(); while !expansion.outer_is_descendant_of(glob_ctxt) { scope = Some(glob_ctxt.remove_mark()); if self.remove_mark() != scope.unwrap() { @@ -485,12 +485,13 @@ 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; } + let mut glob_ctxt = glob_span.ctxt().modern(); let mut marks = Vec::new(); while !expansion.outer_is_descendant_of(glob_ctxt) { marks.push(glob_ctxt.remove_mark()); diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 30e075a339613..9bab95efd1bb7 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -535,19 +535,18 @@ impl Span { } #[inline] - pub fn glob_adjust(&mut self, expansion: Mark, glob_ctxt: SyntaxContext) - -> Option> { + 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_ctxt); + 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_ctxt: SyntaxContext) + 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 } From 0ba36ea7c1d67d852b5f68a186c338ac8e54c7fc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 1 Jun 2019 07:19:58 +1000 Subject: [PATCH 02/16] Add some useful methods to `HygieneData`. --- src/libsyntax_pos/hygiene.rs | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 39a7124c756d4..c23d27879f781 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -200,10 +200,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,6 +213,26 @@ impl HygieneData { } true } + + 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 + } } pub fn clear_markings() { @@ -388,7 +404,7 @@ impl SyntaxContext { 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; + *self = data.prev_ctxt(*self); outer_mark }) } @@ -397,9 +413,11 @@ impl SyntaxContext { 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; + let outer_mark = data.outer(self); + let transparency = data.transparency(self); + let prev_ctxt = data.prev_ctxt(self); + marks.push((outer_mark, transparency)); + self = prev_ctxt; } marks.reverse(); marks @@ -506,12 +524,12 @@ impl SyntaxContext { #[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] From 7bec8c94b47054c09e01564e478f044850fbf178 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 1 Jun 2019 07:24:51 +1000 Subject: [PATCH 03/16] Add `HygieneData::default_transparency`. Also use `HygieneData::expn_info` in an appropriate place. --- src/libsyntax_pos/hygiene.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index c23d27879f781..1f09bb1df710a 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -98,7 +98,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] @@ -214,6 +214,10 @@ 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 } @@ -287,7 +291,7 @@ impl SyntaxContext { 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) + mark, HygieneData::with(|data| data.default_transparency(mark)) ) } From cd64cc835dc8342a596222a7f82646adcf1cbca6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 1 Jun 2019 07:28:15 +1000 Subject: [PATCH 04/16] Add `HygieneData::remove_mark`. --- src/libsyntax_pos/hygiene.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 1f09bb1df710a..97fbd228e26e9 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -237,6 +237,12 @@ impl HygieneData { 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 + } } pub fn clear_markings() { @@ -406,11 +412,7 @@ 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.prev_ctxt(*self); - outer_mark - }) + HygieneData::with(|data| data.remove_mark(self)) } pub fn marks(mut self) -> Vec<(Mark, Transparency)> { From 88fd7a8eb7a0d0ada3e33060969ccd91cba0132b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 1 Jun 2019 07:44:14 +1000 Subject: [PATCH 05/16] Add `HygieneData::adjust`. --- src/libsyntax_pos/hygiene.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 97fbd228e26e9..e8dadf09648d8 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -243,6 +243,14 @@ impl HygieneData { *ctxt = self.prev_ctxt(*ctxt); outer_mark } + + 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 + } } pub fn clear_markings() { @@ -455,11 +463,7 @@ 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)) } /// Adjust this context for resolution in a scope created by the given expansion From a02b2e36c0f1b601519ec6bc71b2d8ff621b4625 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Jun 2019 08:27:36 +1000 Subject: [PATCH 06/16] Add `HygieneData::marks`. --- src/libsyntax_pos/hygiene.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index e8dadf09648d8..b79cd707e01bf 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -244,6 +244,19 @@ impl HygieneData { 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 adjust(&self, ctxt: &mut SyntaxContext, expansion: Mark) -> Option { let mut scope = None; while !self.is_descendant_of(expansion, self.outer(*ctxt)) { @@ -423,19 +436,8 @@ impl SyntaxContext { 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 outer_mark = data.outer(self); - let transparency = data.transparency(self); - let prev_ctxt = data.prev_ctxt(self); - marks.push((outer_mark, transparency)); - self = 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. From a84aee3dbe053218579e69a9b093c82cd7714f75 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Jun 2019 08:16:46 +1000 Subject: [PATCH 07/16] Add `HygieneData::apply_mark_internal`. --- src/libsyntax_pos/hygiene.rs | 113 ++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index b79cd707e01bf..affbb46a1f8f3 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -264,6 +264,63 @@ impl HygieneData { } scope } + + 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() { @@ -359,61 +416,7 @@ impl SyntaxContext { } 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_internal(self, mark, transparency)) } /// Pulls a single mark off of the syntax context. This effectively moves the From 4527a868d6e8bac66a9115bb581686ce71b710aa Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Jun 2019 08:23:54 +1000 Subject: [PATCH 08/16] Add `HygieneData::apply_mark_with_transparency`. Also remove `HygieneData::apply_mark_internal`, which is no longer needed. --- src/libsyntax_pos/hygiene.rs | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index affbb46a1f8f3..f3b0937701e10 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -265,6 +265,40 @@ impl HygieneData { scope } + 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; @@ -382,41 +416,7 @@ impl SyntaxContext { /// 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| data.apply_mark_internal(self, mark, transparency)) + HygieneData::with(|data| data.apply_mark_with_transparency(self, mark, transparency)) } /// Pulls a single mark off of the syntax context. This effectively moves the From 58a486928e4bf78cc33aaae43ad835eb482b14f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Jun 2019 08:35:37 +1000 Subject: [PATCH 09/16] Add `HygieneData::apply_mark`. This combines two `HygieneData::with` calls into one. --- src/libsyntax_pos/hygiene.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index f3b0937701e10..ff17256b732b3 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -265,6 +265,11 @@ impl HygieneData { 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()); @@ -407,10 +412,7 @@ 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.default_transparency(mark)) - ) + HygieneData::with(|data| data.apply_mark(self, mark)) } /// Extend a syntax context with a given mark and transparency From e19857c4dbbde65803d619011af4415fbdda8c01 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 1 Jun 2019 08:35:01 +1000 Subject: [PATCH 10/16] Optimize `glob_adjust` and `reverse_glob_adjust`. They can each now do a single `HygieneData::with` call by replacing the `SyntaxContext` and `Mark` methods with the equivalent methods from `HygieneData`. --- src/libsyntax_pos/hygiene.rs | 50 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index ff17256b732b3..e50b9da62e761 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -499,18 +499,20 @@ 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, glob_span: Span) -> Option> { - let mut scope = None; - let mut glob_ctxt = glob_span.ctxt().modern(); - while !expansion.outer_is_descendant_of(glob_ctxt) { - scope = Some(glob_ctxt.remove_mark()); - if self.remove_mark() != scope.unwrap() { + 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: @@ -522,21 +524,23 @@ impl 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 glob_ctxt = glob_span.ctxt().modern(); - 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) + }) } #[inline] From f9209fcd634fd5fc1fc83387af3b16b7cab62b74 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 09:21:27 +1000 Subject: [PATCH 11/16] Add and use `SyntaxContext::outer_and_expn_info`. This combines two `HygieneData::with` calls into one on a hot path. --- src/librustc/ty/query/on_disk_cache.rs | 5 ++--- src/libsyntax_pos/hygiene.rs | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) 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/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index e50b9da62e761..5691258152a9d 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -565,6 +565,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) } From 6596743d5e710e69d88f9522a207f41731177ef0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 09:43:20 +1000 Subject: [PATCH 12/16] Add `SyntaxContext::hygienic_eq`. This combines multiple `HygieneData::with` calls into one, by combining parts of `hygienic_eq` and `adjust_ident`. --- src/librustc/ty/mod.rs | 3 ++- src/libsyntax_pos/hygiene.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e89fb53c23619..408f2d9f2493c 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 { diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 5691258152a9d..451ae5808f65a 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -543,6 +543,14 @@ impl SyntaxContext { }) } + 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.modern(self)) From dc807a9f7ec02d425b4e3350a87b29f6cf7fa4ff Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 12:07:51 +1000 Subject: [PATCH 13/16] Add `walk_chain`. This combines multiple `HygieneData::with` calls on a hot path. --- src/librustc_codegen_ssa/mir/mod.rs | 9 +-------- src/libsyntax_pos/hygiene.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) 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/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 451ae5808f65a..7ff7a9db49770 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -257,6 +257,17 @@ impl HygieneData { 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)) { @@ -366,6 +377,10 @@ 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 { From 425736dc976f786ea1da6eb4e18064477f88c5bc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 13:08:15 +1000 Subject: [PATCH 14/16] Add a useful comment about this file. --- src/libsyntax_pos/hygiene.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 7ff7a9db49770..62a2d93a52e41 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; From ab9bbf48db3ef8cd83bbcc4dfd40f0308a738cc1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 15:34:54 +1000 Subject: [PATCH 15/16] Avoid unnecessary `rust_2018` calls. The commit combines two calls into one by saving the result in a local variable. The commit also moves the check for `async` later, so that when a different keyword is present the `rust_2018` call will be avoided completely. --- src/libsyntax/parse/parser.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) 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; From 4c9ecbf3d1a0fdac1d97c77783685c6281136c0b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2019 16:10:03 +1000 Subject: [PATCH 16/16] Add `modernize_and_adjust` methods. These combine two `HygieneData::with` calls into one. --- src/librustc/ty/mod.rs | 6 ++---- src/librustc_resolve/lib.rs | 6 ++---- src/libsyntax_pos/hygiene.rs | 8 ++++++++ src/libsyntax_pos/lib.rs | 8 ++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 408f2d9f2493c..e585f9939a014 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -3101,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_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5f076d16bed5e..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 => { diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 62a2d93a52e41..213993996a63c 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -508,6 +508,14 @@ impl SyntaxContext { 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 /// via a glob import with the given `SyntaxContext`. /// For example: diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 9bab95efd1bb7..24aa82184ced5 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -534,6 +534,14 @@ impl Span { mark } + #[inline] + pub fn modernize_and_adjust(&mut self, expansion: Mark) -> Option { + let mut span = self.data(); + let mark = span.ctxt.modernize_and_adjust(expansion); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + #[inline] pub fn glob_adjust(&mut self, expansion: Mark, glob_span: Span) -> Option> { let mut span = self.data();