Skip to content

Commit 556bd9a

Browse files
committed
hygiene: Implement transparent marks
1 parent 6cc1481 commit 556bd9a

File tree

14 files changed

+257
-27
lines changed

14 files changed

+257
-27
lines changed

src/librustc_resolve/lib.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,8 @@ impl<'a> Resolver<'a> {
18521852
} else {
18531853
ident.span.modern()
18541854
}
1855+
} else {
1856+
ident = ident.modern_and_legacy();
18551857
}
18561858

18571859
// Walk backwards up the ribs in scope.
@@ -1989,7 +1991,7 @@ impl<'a> Resolver<'a> {
19891991
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
19901992
// we don't want to pretend that the `macro_rules!` definition is in the `macro`
19911993
// as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks.
1992-
ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque)
1994+
ctxt.marks().into_iter().rev().find(|m| m.transparency() != Transparency::Transparent)
19931995
} else {
19941996
ctxt = ctxt.modern();
19951997
ctxt.adjust(Mark::root())
@@ -2630,6 +2632,7 @@ impl<'a> Resolver<'a> {
26302632
// must not add it if it's in the bindings map
26312633
// because that breaks the assumptions later
26322634
// passes make about or-patterns.)
2635+
let ident = ident.modern_and_legacy();
26332636
let mut def = Def::Local(pat_id);
26342637
match bindings.get(&ident).cloned() {
26352638
Some(id) if id == outer_pat_id => {
@@ -3784,7 +3787,8 @@ impl<'a> Resolver<'a> {
37843787
self.unused_labels.insert(id, label.ident.span);
37853788
let def = Def::Label(id);
37863789
self.with_label_rib(|this| {
3787-
this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
3790+
let ident = label.ident.modern_and_legacy();
3791+
this.label_ribs.last_mut().unwrap().bindings.insert(ident, def);
37883792
f(this);
37893793
});
37903794
} else {
@@ -3815,7 +3819,10 @@ impl<'a> Resolver<'a> {
38153819
}
38163820

38173821
ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
3818-
match self.search_label(label.ident, |rib, id| rib.bindings.get(&id).cloned()) {
3822+
let def = self.search_label(label.ident, |rib, ident| {
3823+
rib.bindings.get(&ident.modern_and_legacy()).cloned()
3824+
});
3825+
match def {
38193826
None => {
38203827
// Search again for close matches...
38213828
// Picks the first label that is "close enough", which is not necessarily

src/librustc_resolve/macros.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,9 @@ impl<'a> base::Resolver for Resolver<'a> {
332332
self.unused_macros.remove(&def_id);
333333
let ext = self.get_macro(def);
334334
if ext.is_modern() {
335-
invoc.expansion_data.mark.set_transparency(Transparency::Opaque);
335+
let transparency =
336+
if ext.is_transparent() { Transparency::Transparent } else { Transparency::Opaque };
337+
invoc.expansion_data.mark.set_transparency(transparency);
336338
} else if def_id.krate == BUILTIN_MACROS_CRATE {
337339
invoc.expansion_data.mark.set_is_builtin(true);
338340
}

src/libsyntax/ext/base.rs

+8
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ pub enum SyntaxExtension {
646646
DeclMacro {
647647
expander: Box<TTMacroExpander + sync::Sync + sync::Send>,
648648
def_info: Option<(ast::NodeId, Span)>,
649+
is_transparent: bool,
649650
edition: Edition,
650651
}
651652
}
@@ -679,6 +680,13 @@ impl SyntaxExtension {
679680
}
680681
}
681682

683+
pub fn is_transparent(&self) -> bool {
684+
match *self {
685+
SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
686+
_ => false,
687+
}
688+
}
689+
682690
pub fn edition(&self) -> Edition {
683691
match *self {
684692
SyntaxExtension::NormalTT { edition, .. } |

src/libsyntax/ext/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
735735
};
736736

737737
let opt_expanded = match *ext {
738-
DeclMacro { ref expander, def_info, edition } => {
738+
DeclMacro { ref expander, def_info, edition, .. } => {
739739
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
740740
false, false, None,
741741
edition) {

src/libsyntax/ext/tt/macro_rules.rs

+3
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
305305
edition,
306306
}
307307
} else {
308+
let is_transparent = attr::contains_name(&def.attrs, "rustc_transparent_macro");
309+
308310
SyntaxExtension::DeclMacro {
309311
expander,
310312
def_info: Some((def.id, def.span)),
313+
is_transparent,
311314
edition,
312315
}
313316
}

src/libsyntax_pos/hygiene.rs

+75-22
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ pub struct SyntaxContext(pub(super) u32);
3333
pub struct SyntaxContextData {
3434
pub outer_mark: Mark,
3535
pub prev_ctxt: SyntaxContext,
36-
pub modern: SyntaxContext,
36+
// This context, but with all transparent and semi-transparent marks filtered away.
37+
pub opaque: SyntaxContext,
38+
// This context, but with all transparent marks filtered away.
39+
pub opaque_and_semitransparent: SyntaxContext,
3740
}
3841

3942
/// A mark is a unique id associated with a macro expansion.
4043
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
4144
pub struct Mark(u32);
4245

43-
#[derive(Debug)]
46+
#[derive(Clone, Debug)]
4447
struct MarkData {
4548
parent: Mark,
4649
transparency: Transparency,
@@ -50,7 +53,7 @@ struct MarkData {
5053

5154
/// A property of a macro expansion that determines how identifiers
5255
/// produced by that expansion are resolved.
53-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
56+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
5457
pub enum Transparency {
5558
/// Identifier produced by a transparent expansion is always resolved at call-site.
5659
/// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
@@ -69,16 +72,26 @@ pub enum Transparency {
6972
}
7073

7174
impl Mark {
75+
fn fresh_with_data(mark_data: MarkData, data: &mut HygieneData) -> Self {
76+
data.marks.push(mark_data);
77+
Mark(data.marks.len() as u32 - 1)
78+
}
79+
7280
pub fn fresh(parent: Mark) -> Self {
7381
HygieneData::with(|data| {
74-
data.marks.push(MarkData {
82+
Mark::fresh_with_data(MarkData {
7583
parent,
7684
// By default expansions behave like `macro_rules`.
7785
transparency: Transparency::SemiTransparent,
7886
is_builtin: false,
7987
expn_info: None,
80-
});
81-
Mark(data.marks.len() as u32 - 1)
88+
}, data)
89+
})
90+
}
91+
92+
pub fn fresh_cloned(clone_from: Mark) -> Self {
93+
HygieneData::with(|data| {
94+
Mark::fresh_with_data(data.marks[clone_from.0 as usize].clone(), data)
8295
})
8396
}
8497

@@ -208,7 +221,8 @@ impl HygieneData {
208221
syntax_contexts: vec![SyntaxContextData {
209222
outer_mark: Mark::root(),
210223
prev_ctxt: SyntaxContext(0),
211-
modern: SyntaxContext(0),
224+
opaque: SyntaxContext(0),
225+
opaque_and_semitransparent: SyntaxContext(0),
212226
}],
213227
markings: HashMap::new(),
214228
gensym_to_ctxt: HashMap::new(),
@@ -241,7 +255,7 @@ impl SyntaxContext {
241255
// Allocate a new SyntaxContext with the given ExpnInfo. This is used when
242256
// deserializing Spans from the incr. comp. cache.
243257
// FIXME(mw): This method does not restore MarkData::parent or
244-
// SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
258+
// SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
245259
// don't seem to be used after HIR lowering, so everything should be fine
246260
// as long as incremental compilation does not kick in before that.
247261
pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
@@ -258,7 +272,8 @@ impl SyntaxContext {
258272
data.syntax_contexts.push(SyntaxContextData {
259273
outer_mark: mark,
260274
prev_ctxt: SyntaxContext::empty(),
261-
modern: SyntaxContext::empty(),
275+
opaque: SyntaxContext::empty(),
276+
opaque_and_semitransparent: SyntaxContext::empty(),
262277
});
263278
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
264279
})
@@ -271,7 +286,13 @@ impl SyntaxContext {
271286
}
272287

273288
let call_site_ctxt =
274-
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
289+
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
290+
let call_site_ctxt = if mark.transparency() == Transparency::SemiTransparent {
291+
call_site_ctxt.modern()
292+
} else {
293+
call_site_ctxt.modern_and_legacy()
294+
};
295+
275296
if call_site_ctxt == SyntaxContext::empty() {
276297
return self.apply_mark_internal(mark);
277298
}
@@ -295,26 +316,53 @@ impl SyntaxContext {
295316
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
296317
HygieneData::with(|data| {
297318
let syntax_contexts = &mut data.syntax_contexts;
298-
let mut modern = syntax_contexts[self.0 as usize].modern;
299-
if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
300-
modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
301-
let len = syntax_contexts.len() as u32;
319+
let transparency = data.marks[mark.0 as usize].transparency;
320+
321+
let mut opaque = syntax_contexts[self.0 as usize].opaque;
322+
let mut opaque_and_semitransparent =
323+
syntax_contexts[self.0 as usize].opaque_and_semitransparent;
324+
325+
if transparency >= Transparency::Opaque {
326+
let prev_ctxt = opaque;
327+
opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
328+
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
329+
syntax_contexts.push(SyntaxContextData {
330+
outer_mark: mark,
331+
prev_ctxt,
332+
opaque: new_opaque,
333+
opaque_and_semitransparent: new_opaque,
334+
});
335+
new_opaque
336+
});
337+
}
338+
339+
if transparency >= Transparency::SemiTransparent {
340+
let prev_ctxt = opaque_and_semitransparent;
341+
opaque_and_semitransparent =
342+
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
343+
let new_opaque_and_semitransparent =
344+
SyntaxContext(syntax_contexts.len() as u32);
302345
syntax_contexts.push(SyntaxContextData {
303346
outer_mark: mark,
304-
prev_ctxt: modern,
305-
modern: SyntaxContext(len),
347+
prev_ctxt,
348+
opaque,
349+
opaque_and_semitransparent: new_opaque_and_semitransparent,
306350
});
307-
SyntaxContext(len)
351+
new_opaque_and_semitransparent
308352
});
309353
}
310354

311-
*data.markings.entry((self, mark)).or_insert_with(|| {
355+
let prev_ctxt = self;
356+
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
357+
let new_opaque_and_semitransparent_and_transparent =
358+
SyntaxContext(syntax_contexts.len() as u32);
312359
syntax_contexts.push(SyntaxContextData {
313360
outer_mark: mark,
314-
prev_ctxt: self,
315-
modern,
361+
prev_ctxt,
362+
opaque,
363+
opaque_and_semitransparent,
316364
});
317-
SyntaxContext(syntax_contexts.len() as u32 - 1)
365+
new_opaque_and_semitransparent_and_transparent
318366
})
319367
})
320368
}
@@ -454,7 +502,12 @@ impl SyntaxContext {
454502

455503
#[inline]
456504
pub fn modern(self) -> SyntaxContext {
457-
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
505+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
506+
}
507+
508+
#[inline]
509+
pub fn modern_and_legacy(self) -> SyntaxContext {
510+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
458511
}
459512

460513
#[inline]

src/libsyntax_pos/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ impl Span {
485485
let span = self.data();
486486
span.with_ctxt(span.ctxt.modern())
487487
}
488+
489+
#[inline]
490+
pub fn modern_and_legacy(self) -> Span {
491+
let span = self.data();
492+
span.with_ctxt(span.ctxt.modern_and_legacy())
493+
}
488494
}
489495

490496
#[derive(Clone, Debug)]

src/libsyntax_pos/symbol.rs

+13
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,23 @@ impl Ident {
5959
Ident::new(Symbol::intern(self.as_str().trim_left_matches('\'')), self.span)
6060
}
6161

62+
// "Normalize" ident for use in comparisons using "item hygiene".
63+
// Identifiers with same string value become same if they came from the same "modern" macro
64+
// (e.g. `macro` item) and stay different if they came from different "modern" macros.
65+
// Technically, this operation strips all non-opaque marks from ident's syntactic context.
6266
pub fn modern(self) -> Ident {
6367
Ident::new(self.name, self.span.modern())
6468
}
6569

70+
// "Normalize" ident for use in comparisons using "local variable hygiene".
71+
// Identifiers with same string value become same if they came from the same non-transparent
72+
// macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
73+
// non-transparent macros.
74+
// Technically, this operation strips all transparent marks from ident's syntactic context.
75+
pub fn modern_and_legacy(self) -> Ident {
76+
Ident::new(self.name, self.span.modern_and_legacy())
77+
}
78+
6679
pub fn gensym(self) -> Ident {
6780
Ident::new(self.name.gensymed(), self.span)
6881
}

src/test/ui/hygiene/auxiliary/intercrate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ pub mod foo {
1919
}
2020
}
2121
}
22+
23+
pub struct SomeType;
24+
25+
pub macro uses_dollar_crate() {
26+
type Alias = $crate::SomeType;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(decl_macro, rustc_attrs)]
12+
13+
#[rustc_transparent_macro]
14+
pub macro dollar_crate() {
15+
let s = $crate::S;
16+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Make sure `$crate` works in `macro` macros.
12+
13+
// compile-pass
14+
// aux-build:intercrate.rs
15+
16+
#![feature(use_extern_macros)]
17+
18+
extern crate intercrate;
19+
20+
intercrate::uses_dollar_crate!();
21+
22+
fn main() {}

src/test/ui/hygiene/generate-mod.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This is an equivalent of issue #50504, but for declarative macros.
12+
13+
#![feature(decl_macro, rustc_attrs)]
14+
15+
#[rustc_transparent_macro]
16+
macro genmod() {
17+
mod m {
18+
type A = S; //~ ERROR cannot find type `S` in this scope
19+
}
20+
}
21+
22+
struct S;
23+
24+
genmod!();

0 commit comments

Comments
 (0)