From 94ef9f57f5fa985beb7588e5cb4c73f1b5f2dcba Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 30 Jun 2018 19:53:46 +0300 Subject: [PATCH] hygiene: Decouple transparencies from expansion IDs --- src/libproc_macro/lib.rs | 19 ++--- src/librustc_resolve/lib.rs | 8 +- src/librustc_resolve/macros.rs | 11 +-- src/libsyntax/ext/base.rs | 18 ++--- src/libsyntax_pos/hygiene.rs | 76 +++++++++++-------- .../proc-macro/auxiliary/generate-mod.rs | 28 +++++++ .../ui-fulldeps/proc-macro/generate-mod.rs | 21 +++++ .../proc-macro/generate-mod.stderr | 9 +++ 8 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 src/test/ui-fulldeps/proc-macro/auxiliary/generate-mod.rs create mode 100644 src/test/ui-fulldeps/proc-macro/generate-mod.rs create mode 100644 src/test/ui-fulldeps/proc-macro/generate-mod.stderr diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index fb5cbf473a3..876cf295acc 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -1351,7 +1351,7 @@ pub mod __internal { use syntax::parse::token::{self, Token}; use syntax::tokenstream; use syntax_pos::{BytePos, Loc, DUMMY_SP}; - use syntax_pos::hygiene::{Mark, SyntaxContext, Transparency}; + use syntax_pos::hygiene::{SyntaxContext, Transparency}; use super::{TokenStream, LexError, Span}; @@ -1436,20 +1436,15 @@ fn drop(&mut self) { // No way to determine def location for a proc macro right now, so use call location. let location = cx.current_expansion.mark.expn_info().unwrap().call_site; - // Opaque mark was already created by expansion, now create its transparent twin. - // We can't use the call-site span literally here, even if it appears to provide - // correct name resolution, because it has all the `ExpnInfo` wrong, so the edition - // checks, lint macro checks, macro backtraces will all break. - let opaque_mark = cx.current_expansion.mark; - let transparent_mark = Mark::fresh_cloned(opaque_mark); - transparent_mark.set_transparency(Transparency::Transparent); - - let to_span = |mark| Span(location.with_ctxt(SyntaxContext::empty().apply_mark(mark))); + let to_span = |transparency| Span(location.with_ctxt( + SyntaxContext::empty().apply_mark_with_transparency(cx.current_expansion.mark, + transparency)) + ); p.set(ProcMacroSess { parse_sess: cx.parse_sess, data: ProcMacroData { - def_site: to_span(opaque_mark), - call_site: to_span(transparent_mark), + def_site: to_span(Transparency::Opaque), + call_site: to_span(Transparency::Transparent), }, }); f() diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index b8dfd21e540..e8dcfc027ef 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1996,8 +1996,8 @@ fn resolve_crate_root(&mut self, ident: Ident) -> Module<'a> { let mut iter = ctxt.marks().into_iter().rev().peekable(); let mut result = None; // Find the last modern mark from the end if it exists. - while let Some(&mark) = iter.peek() { - if mark.transparency() == Transparency::Opaque { + while let Some(&(mark, transparency)) = iter.peek() { + if transparency == Transparency::Opaque { result = Some(mark); iter.next(); } else { @@ -2005,8 +2005,8 @@ fn resolve_crate_root(&mut self, ident: Ident) -> Module<'a> { } } // Then find the last legacy mark from the end if it exists. - for mark in iter { - if mark.transparency() == Transparency::SemiTransparent { + for (mark, transparency) in iter { + if transparency == Transparency::SemiTransparent { result = Some(mark); } else { break; diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 0523765ea18..9ce1e21d0d0 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -24,7 +24,7 @@ use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator}; use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver}; use syntax::ext::expand::{self, AstFragment, AstFragmentKind, Invocation, InvocationKind}; -use syntax::ext::hygiene::{self, Mark, Transparency}; +use syntax::ext::hygiene::{self, Mark}; use syntax::ext::placeholders::placeholder; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{self, emit_feature_err, GateIssue}; @@ -331,13 +331,8 @@ fn resolve_invoc(&mut self, invoc: &mut Invocation, scope: Mark, force: bool) self.unused_macros.remove(&def_id); let ext = self.get_macro(def); - if ext.is_modern() { - let transparency = - if ext.is_transparent() { Transparency::Transparent } else { Transparency::Opaque }; - invoc.expansion_data.mark.set_transparency(transparency); - } else if def_id.krate == BUILTIN_MACROS_CRATE { - invoc.expansion_data.mark.set_is_builtin(true); - } + invoc.expansion_data.mark.set_default_transparency(ext.default_transparency()); + invoc.expansion_data.mark.set_is_builtin(def_id.krate == BUILTIN_MACROS_CRATE); Ok(Some(ext)) } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index e2424de4d14..2e9c7d6f96c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -17,7 +17,7 @@ use edition::Edition; use errors::{DiagnosticBuilder, DiagnosticId}; use ext::expand::{self, AstFragment, Invocation}; -use ext::hygiene::{self, Mark, SyntaxContext}; +use ext::hygiene::{self, Mark, SyntaxContext, Transparency}; use fold::{self, Folder}; use parse::{self, parser, DirectoryOwnership}; use parse::token; @@ -673,20 +673,14 @@ pub fn kind(&self) -> MacroKind { } } - pub fn is_modern(&self) -> bool { + pub fn default_transparency(&self) -> Transparency { match *self { - SyntaxExtension::DeclMacro { .. } | SyntaxExtension::ProcMacro { .. } | SyntaxExtension::AttrProcMacro(..) | - SyntaxExtension::ProcMacroDerive(..) => true, - _ => false, - } - } - - pub fn is_transparent(&self) -> bool { - match *self { - SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent, - _ => false, + SyntaxExtension::ProcMacroDerive(..) | + SyntaxExtension::DeclMacro { is_transparent: false, .. } => Transparency::Opaque, + SyntaxExtension::DeclMacro { is_transparent: true, .. } => Transparency::Transparent, + _ => Transparency::SemiTransparent, } } diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index ff23c2fb534..385174ea7fe 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -32,6 +32,7 @@ #[derive(Copy, Clone, Debug)] struct SyntaxContextData { outer_mark: Mark, + transparency: Transparency, prev_ctxt: SyntaxContext, // This context, but with all transparent and semi-transparent marks filtered away. opaque: SyntaxContext, @@ -46,14 +47,14 @@ struct SyntaxContextData { #[derive(Clone, Debug)] struct MarkData { parent: Mark, - transparency: Transparency, + default_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, PartialOrd, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)] 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. @@ -81,7 +82,7 @@ pub fn fresh(parent: Mark) -> Self { Mark::fresh_with_data(MarkData { parent, // By default expansions behave like `macro_rules`. - transparency: Transparency::SemiTransparent, + default_transparency: Transparency::SemiTransparent, is_builtin: false, expn_info: None, }, data) @@ -127,9 +128,11 @@ pub fn set_expn_info(self, info: ExpnInfo) { }) } + // FIXME: This operation doesn't really make sense when single macro expansion + // can produce tokens with different transparencies. Figure out how to avoid it. pub fn modern(mut self) -> Mark { HygieneData::with(|data| { - while data.marks[self.0 as usize].transparency != Transparency::Opaque { + while data.marks[self.0 as usize].default_transparency != Transparency::Opaque { self = data.marks[self.0 as usize].parent; } self @@ -137,24 +140,20 @@ pub fn modern(mut self) -> Mark { } #[inline] - pub fn transparency(self) -> Transparency { - assert_ne!(self, Mark::root()); - HygieneData::with(|data| data.marks[self.0 as usize].transparency) - } - - #[inline] - pub fn set_transparency(self, transparency: Transparency) { + pub fn set_default_transparency(self, transparency: Transparency) { assert_ne!(self, Mark::root()); - HygieneData::with(|data| data.marks[self.0 as usize].transparency = transparency) + HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency) } #[inline] pub fn is_builtin(self) -> bool { + assert_ne!(self, Mark::root()); HygieneData::with(|data| data.marks[self.0 as usize].is_builtin) } #[inline] pub fn set_is_builtin(self, is_builtin: bool) { + assert_ne!(self, Mark::root()); HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin) } @@ -201,7 +200,7 @@ pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark { crate struct HygieneData { marks: Vec, syntax_contexts: Vec, - markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, + markings: HashMap<(SyntaxContext, Mark, Transparency), SyntaxContext>, default_edition: Edition, } @@ -212,12 +211,13 @@ impl HygieneData { parent: Mark::root(), // If the root is opaque, then loops searching for an opaque mark // will automatically stop after reaching it. - transparency: Transparency::Opaque, + default_transparency: Transparency::Opaque, is_builtin: true, expn_info: None, }], syntax_contexts: vec![SyntaxContextData { outer_mark: Mark::root(), + transparency: Transparency::Opaque, prev_ctxt: SyntaxContext(0), opaque: SyntaxContext(0), opaque_and_semitransparent: SyntaxContext(0), @@ -267,7 +267,7 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self { HygieneData::with(|data| { data.marks.push(MarkData { parent: Mark::root(), - transparency: Transparency::SemiTransparent, + default_transparency: Transparency::SemiTransparent, is_builtin: false, expn_info: Some(expansion_info), }); @@ -276,6 +276,7 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self { data.syntax_contexts.push(SyntaxContextData { outer_mark: mark, + transparency: Transparency::SemiTransparent, prev_ctxt: SyntaxContext::empty(), opaque: SyntaxContext::empty(), opaque_and_semitransparent: SyntaxContext::empty(), @@ -284,22 +285,31 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self { }) } - /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { - if mark.transparency() == Transparency::Opaque { - return self.apply_mark_internal(mark); + assert_ne!(mark, Mark::root()); + self.apply_mark_with_transparency( + mark, HygieneData::with(|data| data.marks[mark.0 as usize].default_transparency) + ) + } + + /// 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 mark.transparency() == Transparency::SemiTransparent { + 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); + return self.apply_mark_internal(mark, transparency); } // Otherwise, `mark` is a macros 1.0 definition and the call site is in a @@ -312,27 +322,26 @@ pub fn apply_mark(self, mark: Mark) -> SyntaxContext { // // See the example at `test/run-pass/hygiene/legacy_interaction.rs`. let mut ctxt = call_site_ctxt; - for mark in self.marks() { - ctxt = ctxt.apply_mark_internal(mark); + for (mark, transparency) in self.marks() { + ctxt = ctxt.apply_mark_internal(mark, transparency); } - ctxt.apply_mark_internal(mark) + ctxt.apply_mark_internal(mark, transparency) } - fn apply_mark_internal(self, mark: Mark) -> SyntaxContext { + fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| { let syntax_contexts = &mut data.syntax_contexts; - let transparency = data.marks[mark.0 as usize].transparency; - 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)).or_insert_with(|| { + 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, @@ -344,11 +353,12 @@ fn apply_mark_internal(self, mark: Mark) -> SyntaxContext { if transparency >= Transparency::SemiTransparent { let prev_ctxt = opaque_and_semitransparent; opaque_and_semitransparent = - *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| { + *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, @@ -358,11 +368,12 @@ fn apply_mark_internal(self, mark: Mark) -> SyntaxContext { } let prev_ctxt = self; - *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| { + *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, @@ -396,12 +407,13 @@ pub fn remove_mark(&mut self) -> Mark { }) } - pub fn marks(mut self) -> Vec { + pub fn marks(mut self) -> Vec<(Mark, Transparency)> { HygieneData::with(|data| { let mut marks = Vec::new(); while self != SyntaxContext::empty() { - marks.push(data.syntax_contexts[self.0 as usize].outer_mark); - self = data.syntax_contexts[self.0 as usize].prev_ctxt; + 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 diff --git a/src/test/ui-fulldeps/proc-macro/auxiliary/generate-mod.rs b/src/test/ui-fulldeps/proc-macro/auxiliary/generate-mod.rs new file mode 100644 index 00000000000..1741b0eed89 --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/auxiliary/generate-mod.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass +// no-prefer-dynamic + +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn check(_: TokenStream) -> TokenStream { + " + struct Outer; + mod inner { + type Inner = Outer; // `Outer` shouldn't be available from here + } + ".parse().unwrap() +} diff --git a/src/test/ui-fulldeps/proc-macro/generate-mod.rs b/src/test/ui-fulldeps/proc-macro/generate-mod.rs new file mode 100644 index 00000000000..509cd33d93d --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/generate-mod.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Modules generated by transparent proc macros still acts as barriers for names (issue #50504). + +// aux-build:generate-mod.rs + +#![feature(proc_macro, proc_macro_gen)] + +extern crate generate_mod; + +generate_mod::check!(); //~ ERROR cannot find type `Outer` in this scope + +fn main() {} diff --git a/src/test/ui-fulldeps/proc-macro/generate-mod.stderr b/src/test/ui-fulldeps/proc-macro/generate-mod.stderr new file mode 100644 index 00000000000..80213b04dce --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/generate-mod.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Outer` in this scope + --> $DIR/generate-mod.rs:19:1 + | +LL | generate_mod::check!(); //~ ERROR cannot find type `Outer` in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. -- GitLab