hygiene: Implement transparent marks
This commit is contained in:
parent
09856c85b7
commit
99ecdb3f5f
@ -1850,6 +1850,8 @@ impl<'a> Resolver<'a> {
|
||||
} else {
|
||||
ident.span.modern()
|
||||
}
|
||||
} else {
|
||||
ident = ident.modern_and_legacy();
|
||||
}
|
||||
|
||||
// Walk backwards up the ribs in scope.
|
||||
@ -1987,7 +1989,7 @@ impl<'a> Resolver<'a> {
|
||||
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
|
||||
// we don't want to pretend that the `macro_rules!` definition is in the `macro`
|
||||
// as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks.
|
||||
ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque)
|
||||
ctxt.marks().into_iter().rev().find(|m| m.transparency() != Transparency::Transparent)
|
||||
} else {
|
||||
ctxt = ctxt.modern();
|
||||
ctxt.adjust(Mark::root())
|
||||
@ -2628,6 +2630,7 @@ impl<'a> Resolver<'a> {
|
||||
// must not add it if it's in the bindings map
|
||||
// because that breaks the assumptions later
|
||||
// passes make about or-patterns.)
|
||||
let ident = ident.modern_and_legacy();
|
||||
let mut def = Def::Local(pat_id);
|
||||
match bindings.get(&ident).cloned() {
|
||||
Some(id) if id == outer_pat_id => {
|
||||
@ -3782,7 +3785,8 @@ impl<'a> Resolver<'a> {
|
||||
self.unused_labels.insert(id, label.ident.span);
|
||||
let def = Def::Label(id);
|
||||
self.with_label_rib(|this| {
|
||||
this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
|
||||
let ident = label.ident.modern_and_legacy();
|
||||
this.label_ribs.last_mut().unwrap().bindings.insert(ident, def);
|
||||
f(this);
|
||||
});
|
||||
} else {
|
||||
@ -3813,7 +3817,10 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
|
||||
ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
|
||||
match self.search_label(label.ident, |rib, id| rib.bindings.get(&id).cloned()) {
|
||||
let def = self.search_label(label.ident, |rib, ident| {
|
||||
rib.bindings.get(&ident.modern_and_legacy()).cloned()
|
||||
});
|
||||
match def {
|
||||
None => {
|
||||
// Search again for close matches...
|
||||
// Picks the first label that is "close enough", which is not necessarily
|
||||
|
@ -332,7 +332,9 @@ impl<'a> base::Resolver for Resolver<'a> {
|
||||
self.unused_macros.remove(&def_id);
|
||||
let ext = self.get_macro(def);
|
||||
if ext.is_modern() {
|
||||
invoc.expansion_data.mark.set_transparency(Transparency::Opaque);
|
||||
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);
|
||||
}
|
||||
|
@ -649,6 +649,7 @@ pub enum SyntaxExtension {
|
||||
DeclMacro {
|
||||
expander: Box<TTMacroExpander + sync::Sync + sync::Send>,
|
||||
def_info: Option<(ast::NodeId, Span)>,
|
||||
is_transparent: bool,
|
||||
edition: Edition,
|
||||
}
|
||||
}
|
||||
@ -682,6 +683,13 @@ impl SyntaxExtension {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
match *self {
|
||||
SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edition(&self) -> Edition {
|
||||
match *self {
|
||||
SyntaxExtension::NormalTT { edition, .. } |
|
||||
|
@ -738,7 +738,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
};
|
||||
|
||||
let opt_expanded = match *ext {
|
||||
DeclMacro { ref expander, def_info, edition } => {
|
||||
DeclMacro { ref expander, def_info, edition, .. } => {
|
||||
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
|
||||
false, false, false, None,
|
||||
edition) {
|
||||
|
@ -312,9 +312,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
|
||||
edition,
|
||||
}
|
||||
} else {
|
||||
let is_transparent = attr::contains_name(&def.attrs, "rustc_transparent_macro");
|
||||
|
||||
SyntaxExtension::DeclMacro {
|
||||
expander,
|
||||
def_info: Some((def.id, def.span)),
|
||||
is_transparent,
|
||||
edition,
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,17 @@ pub struct SyntaxContext(pub(super) u32);
|
||||
pub struct SyntaxContextData {
|
||||
pub outer_mark: Mark,
|
||||
pub prev_ctxt: SyntaxContext,
|
||||
pub modern: SyntaxContext,
|
||||
// This context, but with all transparent and semi-transparent marks filtered away.
|
||||
pub opaque: SyntaxContext,
|
||||
// This context, but with all transparent marks filtered away.
|
||||
pub opaque_and_semitransparent: SyntaxContext,
|
||||
}
|
||||
|
||||
/// A mark is a unique id associated with a macro expansion.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct Mark(u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct MarkData {
|
||||
parent: Mark,
|
||||
transparency: Transparency,
|
||||
@ -50,7 +53,7 @@ struct MarkData {
|
||||
|
||||
/// A property of a macro expansion that determines how identifiers
|
||||
/// produced by that expansion are resolved.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, 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.
|
||||
@ -69,16 +72,26 @@ pub enum Transparency {
|
||||
}
|
||||
|
||||
impl Mark {
|
||||
fn fresh_with_data(mark_data: MarkData, data: &mut HygieneData) -> Self {
|
||||
data.marks.push(mark_data);
|
||||
Mark(data.marks.len() as u32 - 1)
|
||||
}
|
||||
|
||||
pub fn fresh(parent: Mark) -> Self {
|
||||
HygieneData::with(|data| {
|
||||
data.marks.push(MarkData {
|
||||
Mark::fresh_with_data(MarkData {
|
||||
parent,
|
||||
// By default expansions behave like `macro_rules`.
|
||||
transparency: Transparency::SemiTransparent,
|
||||
is_builtin: false,
|
||||
expn_info: None,
|
||||
});
|
||||
Mark(data.marks.len() as u32 - 1)
|
||||
}, data)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fresh_cloned(clone_from: Mark) -> Self {
|
||||
HygieneData::with(|data| {
|
||||
Mark::fresh_with_data(data.marks[clone_from.0 as usize].clone(), data)
|
||||
})
|
||||
}
|
||||
|
||||
@ -207,7 +220,8 @@ impl HygieneData {
|
||||
syntax_contexts: vec![SyntaxContextData {
|
||||
outer_mark: Mark::root(),
|
||||
prev_ctxt: SyntaxContext(0),
|
||||
modern: SyntaxContext(0),
|
||||
opaque: SyntaxContext(0),
|
||||
opaque_and_semitransparent: SyntaxContext(0),
|
||||
}],
|
||||
markings: HashMap::new(),
|
||||
default_edition: Edition::Edition2015,
|
||||
@ -239,7 +253,7 @@ impl SyntaxContext {
|
||||
// Allocate a new SyntaxContext with the given ExpnInfo. This is used when
|
||||
// deserializing Spans from the incr. comp. cache.
|
||||
// FIXME(mw): This method does not restore MarkData::parent or
|
||||
// SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
|
||||
// SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
|
||||
// don't seem to be used after HIR lowering, so everything should be fine
|
||||
// as long as incremental compilation does not kick in before that.
|
||||
pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
|
||||
@ -256,7 +270,8 @@ impl SyntaxContext {
|
||||
data.syntax_contexts.push(SyntaxContextData {
|
||||
outer_mark: mark,
|
||||
prev_ctxt: SyntaxContext::empty(),
|
||||
modern: SyntaxContext::empty(),
|
||||
opaque: SyntaxContext::empty(),
|
||||
opaque_and_semitransparent: SyntaxContext::empty(),
|
||||
});
|
||||
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
|
||||
})
|
||||
@ -269,7 +284,13 @@ impl SyntaxContext {
|
||||
}
|
||||
|
||||
let call_site_ctxt =
|
||||
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
|
||||
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
|
||||
let call_site_ctxt = if mark.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);
|
||||
}
|
||||
@ -293,26 +314,53 @@ impl SyntaxContext {
|
||||
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
|
||||
HygieneData::with(|data| {
|
||||
let syntax_contexts = &mut data.syntax_contexts;
|
||||
let mut modern = syntax_contexts[self.0 as usize].modern;
|
||||
if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
|
||||
modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
|
||||
let len = syntax_contexts.len() as u32;
|
||||
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(|| {
|
||||
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
|
||||
syntax_contexts.push(SyntaxContextData {
|
||||
outer_mark: mark,
|
||||
prev_ctxt: modern,
|
||||
modern: SyntaxContext(len),
|
||||
prev_ctxt,
|
||||
opaque: new_opaque,
|
||||
opaque_and_semitransparent: new_opaque,
|
||||
});
|
||||
SyntaxContext(len)
|
||||
new_opaque
|
||||
});
|
||||
}
|
||||
|
||||
*data.markings.entry((self, mark)).or_insert_with(|| {
|
||||
if transparency >= Transparency::SemiTransparent {
|
||||
let prev_ctxt = opaque_and_semitransparent;
|
||||
opaque_and_semitransparent =
|
||||
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
|
||||
let new_opaque_and_semitransparent =
|
||||
SyntaxContext(syntax_contexts.len() as u32);
|
||||
syntax_contexts.push(SyntaxContextData {
|
||||
outer_mark: mark,
|
||||
prev_ctxt,
|
||||
opaque,
|
||||
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
||||
});
|
||||
new_opaque_and_semitransparent
|
||||
});
|
||||
}
|
||||
|
||||
let prev_ctxt = self;
|
||||
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
|
||||
let new_opaque_and_semitransparent_and_transparent =
|
||||
SyntaxContext(syntax_contexts.len() as u32);
|
||||
syntax_contexts.push(SyntaxContextData {
|
||||
outer_mark: mark,
|
||||
prev_ctxt: self,
|
||||
modern,
|
||||
prev_ctxt,
|
||||
opaque,
|
||||
opaque_and_semitransparent,
|
||||
});
|
||||
SyntaxContext(syntax_contexts.len() as u32 - 1)
|
||||
new_opaque_and_semitransparent_and_transparent
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -452,7 +500,12 @@ impl SyntaxContext {
|
||||
|
||||
#[inline]
|
||||
pub fn modern(self) -> SyntaxContext {
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn modern_and_legacy(self) -> SyntaxContext {
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -491,6 +491,12 @@ impl Span {
|
||||
let span = self.data();
|
||||
span.with_ctxt(span.ctxt.modern())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn modern_and_legacy(self) -> Span {
|
||||
let span = self.data();
|
||||
span.with_ctxt(span.ctxt.modern_and_legacy())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -68,6 +68,15 @@ impl Ident {
|
||||
Ident::new(self.name, self.span.modern())
|
||||
}
|
||||
|
||||
// "Normalize" ident for use in comparisons using "local variable hygiene".
|
||||
// Identifiers with same string value become same if they came from the same non-transparent
|
||||
// macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
|
||||
// non-transparent macros.
|
||||
// Technically, this operation strips all transparent marks from ident's syntactic context.
|
||||
pub fn modern_and_legacy(self) -> Ident {
|
||||
Ident::new(self.name, self.span.modern_and_legacy())
|
||||
}
|
||||
|
||||
pub fn gensym(self) -> Ident {
|
||||
Ident::new(self.name.gensymed(), self.span)
|
||||
}
|
||||
|
@ -19,3 +19,9 @@ pub mod foo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SomeType;
|
||||
|
||||
pub macro uses_dollar_crate() {
|
||||
type Alias = $crate::SomeType;
|
||||
}
|
||||
|
16
src/test/ui/hygiene/auxiliary/transparent-basic.rs
Normal file
16
src/test/ui/hygiene/auxiliary/transparent-basic.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
|
||||
#![feature(decl_macro, rustc_attrs)]
|
||||
|
||||
#[rustc_transparent_macro]
|
||||
pub macro dollar_crate() {
|
||||
let s = $crate::S;
|
||||
}
|
22
src/test/ui/hygiene/dollar-crate-modern.rs
Normal file
22
src/test/ui/hygiene/dollar-crate-modern.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
// Make sure `$crate` works in `macro` macros.
|
||||
|
||||
// compile-pass
|
||||
// aux-build:intercrate.rs
|
||||
|
||||
#![feature(use_extern_macros)]
|
||||
|
||||
extern crate intercrate;
|
||||
|
||||
intercrate::uses_dollar_crate!();
|
||||
|
||||
fn main() {}
|
24
src/test/ui/hygiene/generate-mod.rs
Normal file
24
src/test/ui/hygiene/generate-mod.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
// This is an equivalent of issue #50504, but for declarative macros.
|
||||
|
||||
#![feature(decl_macro, rustc_attrs)]
|
||||
|
||||
#[rustc_transparent_macro]
|
||||
macro genmod() {
|
||||
mod m {
|
||||
type A = S; //~ ERROR cannot find type `S` in this scope
|
||||
}
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
genmod!();
|
17
src/test/ui/hygiene/generate-mod.stderr
Normal file
17
src/test/ui/hygiene/generate-mod.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
error[E0412]: cannot find type `S` in this scope
|
||||
--> $DIR/generate-mod.rs:18:18
|
||||
|
|
||||
LL | type A = S; //~ ERROR cannot find type `S` in this scope
|
||||
| ^ did you mean `A`?
|
||||
...
|
||||
LL | genmod!();
|
||||
| ---------- in this macro invocation
|
||||
|
||||
error[E0601]: `main` function not found in crate `generate_mod`
|
||||
|
|
||||
= note: consider adding a `main` function to `$DIR/generate-mod.rs`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors occurred: E0412, E0601.
|
||||
For more information about an error, try `rustc --explain E0412`.
|
53
src/test/ui/hygiene/transparent-basic.rs
Normal file
53
src/test/ui/hygiene/transparent-basic.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
// compile-pass
|
||||
// aux-build:transparent-basic.rs
|
||||
|
||||
#![feature(decl_macro, rustc_attrs)]
|
||||
|
||||
extern crate transparent_basic;
|
||||
|
||||
#[rustc_transparent_macro]
|
||||
macro binding() {
|
||||
let x = 10;
|
||||
}
|
||||
|
||||
#[rustc_transparent_macro]
|
||||
macro label() {
|
||||
break 'label
|
||||
}
|
||||
|
||||
macro_rules! legacy {
|
||||
() => {
|
||||
binding!();
|
||||
let y = x;
|
||||
}
|
||||
}
|
||||
|
||||
fn legacy_interaction1() {
|
||||
legacy!();
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
fn check_dollar_crate() {
|
||||
// `$crate::S` inside the macro resolves to `S` from this crate.
|
||||
transparent_basic::dollar_crate!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
binding!();
|
||||
let y = x;
|
||||
|
||||
'label: loop {
|
||||
label!();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user