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::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 @@ pub mod __internal {
// 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()

View File

@ -1996,8 +1996,8 @@ impl<'a> Resolver<'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 @@ impl<'a> Resolver<'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;

View File

@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
use syntax::ext::expand::{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 @@ impl<'a> base::Resolver for Resolver<'a> {
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))
}

View File

@ -17,7 +17,7 @@ use syntax_pos::{Span, MultiSpan, DUMMY_SP};
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 @@ impl SyntaxExtension {
}
}
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,
}
}

View File

@ -32,6 +32,7 @@ pub struct SyntaxContext(u32);
#[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 @@ pub struct Mark(u32);
#[derive(Clone, Debug)]
struct MarkData {
parent: Mark,
transparency: Transparency,
default_transparency: Transparency,
is_builtin: bool,
expn_info: Option<ExpnInfo>,
}
/// 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 @@ impl Mark {
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 @@ 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 {
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 @@ impl Mark {
}
#[inline]
pub fn transparency(self) -> Transparency {
pub fn set_default_transparency(self, transparency: Transparency) {
assert_ne!(self, Mark::root());
HygieneData::with(|data| data.marks[self.0 as usize].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)
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 @@ impl Mark {
crate struct HygieneData {
marks: Vec<MarkData>,
syntax_contexts: Vec<SyntaxContextData>,
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 @@ impl SyntaxContext {
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 @@ impl SyntaxContext {
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 @@ impl SyntaxContext {
})
}
/// 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 @@ impl 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 @@ impl 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 @@ impl 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 @@ impl SyntaxContext {
})
}
pub fn marks(mut self) -> Vec<Mark> {
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

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