hygiene: Decouple transparencies from expansion IDs

This commit is contained in:
Vadim Petrochenkov 2018-06-30 19:53:46 +03:00
parent 01b6d7cc6f
commit 94ef9f57f5
8 changed files with 122 additions and 68 deletions

View File

@ -1351,7 +1351,7 @@ pub mod __internal {
use syntax::parse::token::{self, Token}; use syntax::parse::token::{self, Token};
use syntax::tokenstream; use syntax::tokenstream;
use syntax_pos::{BytePos, Loc, DUMMY_SP}; 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}; use super::{TokenStream, LexError, Span};
@ -1436,20 +1436,15 @@ pub mod __internal {
// No way to determine def location for a proc macro right now, so use call location. // 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; let location = cx.current_expansion.mark.expn_info().unwrap().call_site;
// Opaque mark was already created by expansion, now create its transparent twin. let to_span = |transparency| Span(location.with_ctxt(
// We can't use the call-site span literally here, even if it appears to provide SyntaxContext::empty().apply_mark_with_transparency(cx.current_expansion.mark,
// correct name resolution, because it has all the `ExpnInfo` wrong, so the edition transparency))
// 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)));
p.set(ProcMacroSess { p.set(ProcMacroSess {
parse_sess: cx.parse_sess, parse_sess: cx.parse_sess,
data: ProcMacroData { data: ProcMacroData {
def_site: to_span(opaque_mark), def_site: to_span(Transparency::Opaque),
call_site: to_span(transparent_mark), call_site: to_span(Transparency::Transparent),
}, },
}); });
f() f()

View File

@ -1996,8 +1996,8 @@ impl<'a> Resolver<'a> {
let mut iter = ctxt.marks().into_iter().rev().peekable(); let mut iter = ctxt.marks().into_iter().rev().peekable();
let mut result = None; let mut result = None;
// Find the last modern mark from the end if it exists. // Find the last modern mark from the end if it exists.
while let Some(&mark) = iter.peek() { while let Some(&(mark, transparency)) = iter.peek() {
if mark.transparency() == Transparency::Opaque { if transparency == Transparency::Opaque {
result = Some(mark); result = Some(mark);
iter.next(); iter.next();
} else { } else {
@ -2005,8 +2005,8 @@ impl<'a> Resolver<'a> {
} }
} }
// Then find the last legacy mark from the end if it exists. // Then find the last legacy mark from the end if it exists.
for mark in iter { for (mark, transparency) in iter {
if mark.transparency() == Transparency::SemiTransparent { if transparency == Transparency::SemiTransparent {
result = Some(mark); result = Some(mark);
} else { } else {
break; break;

View File

@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator}; use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver}; use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
use syntax::ext::expand::{self, AstFragment, AstFragmentKind, Invocation, InvocationKind}; 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::placeholders::placeholder;
use syntax::ext::tt::macro_rules; use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{self, emit_feature_err, GateIssue}; use syntax::feature_gate::{self, emit_feature_err, GateIssue};
@ -331,13 +331,8 @@ impl<'a> base::Resolver for Resolver<'a> {
self.unused_macros.remove(&def_id); self.unused_macros.remove(&def_id);
let ext = self.get_macro(def); let ext = self.get_macro(def);
if ext.is_modern() { invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
let transparency = invoc.expansion_data.mark.set_is_builtin(def_id.krate == BUILTIN_MACROS_CRATE);
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);
}
Ok(Some(ext)) Ok(Some(ext))
} }

View File

@ -17,7 +17,7 @@ use syntax_pos::{Span, MultiSpan, DUMMY_SP};
use edition::Edition; use edition::Edition;
use errors::{DiagnosticBuilder, DiagnosticId}; use errors::{DiagnosticBuilder, DiagnosticId};
use ext::expand::{self, AstFragment, Invocation}; use ext::expand::{self, AstFragment, Invocation};
use ext::hygiene::{self, Mark, SyntaxContext}; use ext::hygiene::{self, Mark, SyntaxContext, Transparency};
use fold::{self, Folder}; use fold::{self, Folder};
use parse::{self, parser, DirectoryOwnership}; use parse::{self, parser, DirectoryOwnership};
use parse::token; use parse::token;
@ -673,20 +673,14 @@ impl SyntaxExtension {
} }
} }
pub fn is_modern(&self) -> bool { pub fn default_transparency(&self) -> Transparency {
match *self { match *self {
SyntaxExtension::DeclMacro { .. } |
SyntaxExtension::ProcMacro { .. } | SyntaxExtension::ProcMacro { .. } |
SyntaxExtension::AttrProcMacro(..) | SyntaxExtension::AttrProcMacro(..) |
SyntaxExtension::ProcMacroDerive(..) => true, SyntaxExtension::ProcMacroDerive(..) |
_ => false, SyntaxExtension::DeclMacro { is_transparent: false, .. } => Transparency::Opaque,
} SyntaxExtension::DeclMacro { is_transparent: true, .. } => Transparency::Transparent,
} _ => Transparency::SemiTransparent,
pub fn is_transparent(&self) -> bool {
match *self {
SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
_ => false,
} }
} }

View File

@ -32,6 +32,7 @@ pub struct SyntaxContext(u32);
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
struct SyntaxContextData { struct SyntaxContextData {
outer_mark: Mark, outer_mark: Mark,
transparency: Transparency,
prev_ctxt: SyntaxContext, prev_ctxt: SyntaxContext,
// This context, but with all transparent and semi-transparent marks filtered away. // This context, but with all transparent and semi-transparent marks filtered away.
opaque: SyntaxContext, opaque: SyntaxContext,
@ -46,14 +47,14 @@ pub struct Mark(u32);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct MarkData { struct MarkData {
parent: Mark, parent: Mark,
transparency: Transparency, default_transparency: Transparency,
is_builtin: bool, is_builtin: bool,
expn_info: Option<ExpnInfo>, expn_info: Option<ExpnInfo>,
} }
/// A property of a macro expansion that determines how identifiers /// A property of a macro expansion that determines how identifiers
/// produced by that expansion are resolved. /// produced by that expansion are resolved.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
pub enum Transparency { pub enum Transparency {
/// Identifier produced by a transparent expansion is always resolved at call-site. /// 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. /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
@ -81,7 +82,7 @@ impl Mark {
Mark::fresh_with_data(MarkData { Mark::fresh_with_data(MarkData {
parent, parent,
// By default expansions behave like `macro_rules`. // By default expansions behave like `macro_rules`.
transparency: Transparency::SemiTransparent, default_transparency: Transparency::SemiTransparent,
is_builtin: false, is_builtin: false,
expn_info: None, expn_info: None,
}, data) }, data)
@ -127,9 +128,11 @@ impl Mark {
}) })
} }
// 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 { pub fn modern(mut self) -> Mark {
HygieneData::with(|data| { 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 = data.marks[self.0 as usize].parent;
} }
self self
@ -137,24 +140,20 @@ impl Mark {
} }
#[inline] #[inline]
pub fn transparency(self) -> Transparency { pub fn set_default_transparency(self, transparency: Transparency) {
assert_ne!(self, Mark::root()); assert_ne!(self, Mark::root());
HygieneData::with(|data| data.marks[self.0 as usize].transparency) HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency)
}
#[inline]
pub fn set_transparency(self, transparency: Transparency) {
assert_ne!(self, Mark::root());
HygieneData::with(|data| data.marks[self.0 as usize].transparency = transparency)
} }
#[inline] #[inline]
pub fn is_builtin(self) -> bool { pub fn is_builtin(self) -> bool {
assert_ne!(self, Mark::root());
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin) HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
} }
#[inline] #[inline]
pub fn set_is_builtin(self, is_builtin: bool) { 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) HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
} }
@ -201,7 +200,7 @@ impl Mark {
crate struct HygieneData { crate struct HygieneData {
marks: Vec<MarkData>, marks: Vec<MarkData>,
syntax_contexts: Vec<SyntaxContextData>, syntax_contexts: Vec<SyntaxContextData>,
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, markings: HashMap<(SyntaxContext, Mark, Transparency), SyntaxContext>,
default_edition: Edition, default_edition: Edition,
} }
@ -212,12 +211,13 @@ impl HygieneData {
parent: Mark::root(), parent: Mark::root(),
// If the root is opaque, then loops searching for an opaque mark // If the root is opaque, then loops searching for an opaque mark
// will automatically stop after reaching it. // will automatically stop after reaching it.
transparency: Transparency::Opaque, default_transparency: Transparency::Opaque,
is_builtin: true, is_builtin: true,
expn_info: None, expn_info: None,
}], }],
syntax_contexts: vec![SyntaxContextData { syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark::root(), outer_mark: Mark::root(),
transparency: Transparency::Opaque,
prev_ctxt: SyntaxContext(0), prev_ctxt: SyntaxContext(0),
opaque: SyntaxContext(0), opaque: SyntaxContext(0),
opaque_and_semitransparent: SyntaxContext(0), opaque_and_semitransparent: SyntaxContext(0),
@ -267,7 +267,7 @@ impl SyntaxContext {
HygieneData::with(|data| { HygieneData::with(|data| {
data.marks.push(MarkData { data.marks.push(MarkData {
parent: Mark::root(), parent: Mark::root(),
transparency: Transparency::SemiTransparent, default_transparency: Transparency::SemiTransparent,
is_builtin: false, is_builtin: false,
expn_info: Some(expansion_info), expn_info: Some(expansion_info),
}); });
@ -276,6 +276,7 @@ impl SyntaxContext {
data.syntax_contexts.push(SyntaxContextData { data.syntax_contexts.push(SyntaxContextData {
outer_mark: mark, outer_mark: mark,
transparency: Transparency::SemiTransparent,
prev_ctxt: SyntaxContext::empty(), prev_ctxt: SyntaxContext::empty(),
opaque: SyntaxContext::empty(), opaque: SyntaxContext::empty(),
opaque_and_semitransparent: SyntaxContext::empty(), opaque_and_semitransparent: SyntaxContext::empty(),
@ -284,22 +285,31 @@ impl SyntaxContext {
}) })
} }
/// Extend a syntax context with a given mark
pub fn apply_mark(self, mark: Mark) -> SyntaxContext { pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
if mark.transparency() == Transparency::Opaque { assert_ne!(mark, Mark::root());
return self.apply_mark_internal(mark); 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 = let call_site_ctxt =
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.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() call_site_ctxt.modern()
} else { } else {
call_site_ctxt.modern_and_legacy() call_site_ctxt.modern_and_legacy()
}; };
if call_site_ctxt == SyntaxContext::empty() { 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 // Otherwise, `mark` is a macros 1.0 definition and the call site is in a
@ -312,27 +322,26 @@ impl SyntaxContext {
// //
// See the example at `test/run-pass/hygiene/legacy_interaction.rs`. // See the example at `test/run-pass/hygiene/legacy_interaction.rs`.
let mut ctxt = call_site_ctxt; let mut ctxt = call_site_ctxt;
for mark in self.marks() { for (mark, transparency) in self.marks() {
ctxt = ctxt.apply_mark_internal(mark); 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| { HygieneData::with(|data| {
let syntax_contexts = &mut data.syntax_contexts; 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 = syntax_contexts[self.0 as usize].opaque;
let mut opaque_and_semitransparent = let mut opaque_and_semitransparent =
syntax_contexts[self.0 as usize].opaque_and_semitransparent; syntax_contexts[self.0 as usize].opaque_and_semitransparent;
if transparency >= Transparency::Opaque { if transparency >= Transparency::Opaque {
let prev_ctxt = 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); let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData { syntax_contexts.push(SyntaxContextData {
outer_mark: mark, outer_mark: mark,
transparency,
prev_ctxt, prev_ctxt,
opaque: new_opaque, opaque: new_opaque,
opaque_and_semitransparent: new_opaque, opaque_and_semitransparent: new_opaque,
@ -344,11 +353,12 @@ impl SyntaxContext {
if transparency >= Transparency::SemiTransparent { if transparency >= Transparency::SemiTransparent {
let prev_ctxt = opaque_and_semitransparent; let prev_ctxt = opaque_and_semitransparent;
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 = let new_opaque_and_semitransparent =
SyntaxContext(syntax_contexts.len() as u32); SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData { syntax_contexts.push(SyntaxContextData {
outer_mark: mark, outer_mark: mark,
transparency,
prev_ctxt, prev_ctxt,
opaque, opaque,
opaque_and_semitransparent: new_opaque_and_semitransparent, opaque_and_semitransparent: new_opaque_and_semitransparent,
@ -358,11 +368,12 @@ impl SyntaxContext {
} }
let prev_ctxt = self; 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 = let new_opaque_and_semitransparent_and_transparent =
SyntaxContext(syntax_contexts.len() as u32); SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData { syntax_contexts.push(SyntaxContextData {
outer_mark: mark, outer_mark: mark,
transparency,
prev_ctxt, prev_ctxt,
opaque, opaque,
opaque_and_semitransparent, opaque_and_semitransparent,
@ -396,12 +407,13 @@ impl SyntaxContext {
}) })
} }
pub fn marks(mut self) -> Vec<Mark> { pub fn marks(mut self) -> Vec<(Mark, Transparency)> {
HygieneData::with(|data| { HygieneData::with(|data| {
let mut marks = Vec::new(); let mut marks = Vec::new();
while self != SyntaxContext::empty() { while self != SyntaxContext::empty() {
marks.push(data.syntax_contexts[self.0 as usize].outer_mark); let ctxt_data = &data.syntax_contexts[self.0 as usize];
self = data.syntax_contexts[self.0 as usize].prev_ctxt; marks.push((ctxt_data.outer_mark, ctxt_data.transparency));
self = ctxt_data.prev_ctxt;
} }
marks.reverse(); marks.reverse();
marks marks

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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()
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View File

@ -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`.