Skip to content

Commit 99ecdb3

Browse files
committed
hygiene: Implement transparent marks
1 parent 09856c8 commit 99ecdb3

File tree

14 files changed

+253
-27
lines changed

14 files changed

+253
-27
lines changed

src/librustc_resolve/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,8 @@ impl<'a> Resolver<'a> {
18501850
} else {
18511851
ident.span.modern()
18521852
}
1853+
} else {
1854+
ident = ident.modern_and_legacy();
18531855
}
18541856

18551857
// Walk backwards up the ribs in scope.
@@ -1987,7 +1989,7 @@ impl<'a> Resolver<'a> {
19871989
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
19881990
// we don't want to pretend that the `macro_rules!` definition is in the `macro`
19891991
// as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks.
1990-
ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque)
1992+
ctxt.marks().into_iter().rev().find(|m| m.transparency() != Transparency::Transparent)
19911993
} else {
19921994
ctxt = ctxt.modern();
19931995
ctxt.adjust(Mark::root())
@@ -2628,6 +2630,7 @@ impl<'a> Resolver<'a> {
26282630
// must not add it if it's in the bindings map
26292631
// because that breaks the assumptions later
26302632
// passes make about or-patterns.)
2633+
let ident = ident.modern_and_legacy();
26312634
let mut def = Def::Local(pat_id);
26322635
match bindings.get(&ident).cloned() {
26332636
Some(id) if id == outer_pat_id => {
@@ -3782,7 +3785,8 @@ impl<'a> Resolver<'a> {
37823785
self.unused_labels.insert(id, label.ident.span);
37833786
let def = Def::Label(id);
37843787
self.with_label_rib(|this| {
3785-
this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
3788+
let ident = label.ident.modern_and_legacy();
3789+
this.label_ribs.last_mut().unwrap().bindings.insert(ident, def);
37863790
f(this);
37873791
});
37883792
} else {
@@ -3813,7 +3817,10 @@ impl<'a> Resolver<'a> {
38133817
}
38143818

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

src/librustc_resolve/macros.rs

Lines changed: 3 additions & 1 deletion
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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ pub enum SyntaxExtension {
649649
DeclMacro {
650650
expander: Box<TTMacroExpander + sync::Sync + sync::Send>,
651651
def_info: Option<(ast::NodeId, Span)>,
652+
is_transparent: bool,
652653
edition: Edition,
653654
}
654655
}
@@ -682,6 +683,13 @@ impl SyntaxExtension {
682683
}
683684
}
684685

686+
pub fn is_transparent(&self) -> bool {
687+
match *self {
688+
SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
689+
_ => false,
690+
}
691+
}
692+
685693
pub fn edition(&self) -> Edition {
686694
match *self {
687695
SyntaxExtension::NormalTT { edition, .. } |

src/libsyntax/ext/expand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
738738
};
739739

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

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
312312
edition,
313313
}
314314
} else {
315+
let is_transparent = attr::contains_name(&def.attrs, "rustc_transparent_macro");
316+
315317
SyntaxExtension::DeclMacro {
316318
expander,
317319
def_info: Some((def.id, def.span)),
320+
is_transparent,
318321
edition,
319322
}
320323
}

src/libsyntax_pos/hygiene.rs

Lines changed: 75 additions & 22 deletions
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

@@ -207,7 +220,8 @@ impl HygieneData {
207220
syntax_contexts: vec![SyntaxContextData {
208221
outer_mark: Mark::root(),
209222
prev_ctxt: SyntaxContext(0),
210-
modern: SyntaxContext(0),
223+
opaque: SyntaxContext(0),
224+
opaque_and_semitransparent: SyntaxContext(0),
211225
}],
212226
markings: HashMap::new(),
213227
default_edition: Edition::Edition2015,
@@ -239,7 +253,7 @@ impl SyntaxContext {
239253
// Allocate a new SyntaxContext with the given ExpnInfo. This is used when
240254
// deserializing Spans from the incr. comp. cache.
241255
// FIXME(mw): This method does not restore MarkData::parent or
242-
// SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
256+
// SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
243257
// don't seem to be used after HIR lowering, so everything should be fine
244258
// as long as incremental compilation does not kick in before that.
245259
pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
@@ -256,7 +270,8 @@ impl SyntaxContext {
256270
data.syntax_contexts.push(SyntaxContextData {
257271
outer_mark: mark,
258272
prev_ctxt: SyntaxContext::empty(),
259-
modern: SyntaxContext::empty(),
273+
opaque: SyntaxContext::empty(),
274+
opaque_and_semitransparent: SyntaxContext::empty(),
260275
});
261276
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
262277
})
@@ -269,7 +284,13 @@ impl SyntaxContext {
269284
}
270285

271286
let call_site_ctxt =
272-
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
287+
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
288+
let call_site_ctxt = if mark.transparency() == Transparency::SemiTransparent {
289+
call_site_ctxt.modern()
290+
} else {
291+
call_site_ctxt.modern_and_legacy()
292+
};
293+
273294
if call_site_ctxt == SyntaxContext::empty() {
274295
return self.apply_mark_internal(mark);
275296
}
@@ -293,26 +314,53 @@ impl SyntaxContext {
293314
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
294315
HygieneData::with(|data| {
295316
let syntax_contexts = &mut data.syntax_contexts;
296-
let mut modern = syntax_contexts[self.0 as usize].modern;
297-
if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
298-
modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
299-
let len = syntax_contexts.len() as u32;
317+
let transparency = data.marks[mark.0 as usize].transparency;
318+
319+
let mut opaque = syntax_contexts[self.0 as usize].opaque;
320+
let mut opaque_and_semitransparent =
321+
syntax_contexts[self.0 as usize].opaque_and_semitransparent;
322+
323+
if transparency >= Transparency::Opaque {
324+
let prev_ctxt = opaque;
325+
opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
326+
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
327+
syntax_contexts.push(SyntaxContextData {
328+
outer_mark: mark,
329+
prev_ctxt,
330+
opaque: new_opaque,
331+
opaque_and_semitransparent: new_opaque,
332+
});
333+
new_opaque
334+
});
335+
}
336+
337+
if transparency >= Transparency::SemiTransparent {
338+
let prev_ctxt = opaque_and_semitransparent;
339+
opaque_and_semitransparent =
340+
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
341+
let new_opaque_and_semitransparent =
342+
SyntaxContext(syntax_contexts.len() as u32);
300343
syntax_contexts.push(SyntaxContextData {
301344
outer_mark: mark,
302-
prev_ctxt: modern,
303-
modern: SyntaxContext(len),
345+
prev_ctxt,
346+
opaque,
347+
opaque_and_semitransparent: new_opaque_and_semitransparent,
304348
});
305-
SyntaxContext(len)
349+
new_opaque_and_semitransparent
306350
});
307351
}
308352

309-
*data.markings.entry((self, mark)).or_insert_with(|| {
353+
let prev_ctxt = self;
354+
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
355+
let new_opaque_and_semitransparent_and_transparent =
356+
SyntaxContext(syntax_contexts.len() as u32);
310357
syntax_contexts.push(SyntaxContextData {
311358
outer_mark: mark,
312-
prev_ctxt: self,
313-
modern,
359+
prev_ctxt,
360+
opaque,
361+
opaque_and_semitransparent,
314362
});
315-
SyntaxContext(syntax_contexts.len() as u32 - 1)
363+
new_opaque_and_semitransparent_and_transparent
316364
})
317365
})
318366
}
@@ -452,7 +500,12 @@ impl SyntaxContext {
452500

453501
#[inline]
454502
pub fn modern(self) -> SyntaxContext {
455-
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
503+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
504+
}
505+
506+
#[inline]
507+
pub fn modern_and_legacy(self) -> SyntaxContext {
508+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
456509
}
457510

458511
#[inline]

src/libsyntax_pos/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,12 @@ impl Span {
491491
let span = self.data();
492492
span.with_ctxt(span.ctxt.modern())
493493
}
494+
495+
#[inline]
496+
pub fn modern_and_legacy(self) -> Span {
497+
let span = self.data();
498+
span.with_ctxt(span.ctxt.modern_and_legacy())
499+
}
494500
}
495501

496502
#[derive(Clone, Debug)]

src/libsyntax_pos/symbol.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ impl Ident {
6868
Ident::new(self.name, self.span.modern())
6969
}
7070

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

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

Lines changed: 6 additions & 0 deletions
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+
}
Lines changed: 16 additions & 0 deletions
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+
}
Lines changed: 22 additions & 0 deletions
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

Lines changed: 24 additions & 0 deletions
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)