Skip to content

Commit 94ef9f5

Browse files
committed
hygiene: Decouple transparencies from expansion IDs
1 parent 01b6d7c commit 94ef9f5

File tree

8 files changed

+122
-68
lines changed

8 files changed

+122
-68
lines changed

src/libproc_macro/lib.rs

+7-12
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ pub mod __internal {
13511351
use syntax::parse::token::{self, Token};
13521352
use syntax::tokenstream;
13531353
use syntax_pos::{BytePos, Loc, DUMMY_SP};
1354-
use syntax_pos::hygiene::{Mark, SyntaxContext, Transparency};
1354+
use syntax_pos::hygiene::{SyntaxContext, Transparency};
13551355

13561356
use super::{TokenStream, LexError, Span};
13571357

@@ -1436,20 +1436,15 @@ pub mod __internal {
14361436

14371437
// No way to determine def location for a proc macro right now, so use call location.
14381438
let location = cx.current_expansion.mark.expn_info().unwrap().call_site;
1439-
// Opaque mark was already created by expansion, now create its transparent twin.
1440-
// We can't use the call-site span literally here, even if it appears to provide
1441-
// correct name resolution, because it has all the `ExpnInfo` wrong, so the edition
1442-
// checks, lint macro checks, macro backtraces will all break.
1443-
let opaque_mark = cx.current_expansion.mark;
1444-
let transparent_mark = Mark::fresh_cloned(opaque_mark);
1445-
transparent_mark.set_transparency(Transparency::Transparent);
1446-
1447-
let to_span = |mark| Span(location.with_ctxt(SyntaxContext::empty().apply_mark(mark)));
1439+
let to_span = |transparency| Span(location.with_ctxt(
1440+
SyntaxContext::empty().apply_mark_with_transparency(cx.current_expansion.mark,
1441+
transparency))
1442+
);
14481443
p.set(ProcMacroSess {
14491444
parse_sess: cx.parse_sess,
14501445
data: ProcMacroData {
1451-
def_site: to_span(opaque_mark),
1452-
call_site: to_span(transparent_mark),
1446+
def_site: to_span(Transparency::Opaque),
1447+
call_site: to_span(Transparency::Transparent),
14531448
},
14541449
});
14551450
f()

src/librustc_resolve/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1996,17 +1996,17 @@ impl<'a> Resolver<'a> {
19961996
let mut iter = ctxt.marks().into_iter().rev().peekable();
19971997
let mut result = None;
19981998
// Find the last modern mark from the end if it exists.
1999-
while let Some(&mark) = iter.peek() {
2000-
if mark.transparency() == Transparency::Opaque {
1999+
while let Some(&(mark, transparency)) = iter.peek() {
2000+
if transparency == Transparency::Opaque {
20012001
result = Some(mark);
20022002
iter.next();
20032003
} else {
20042004
break;
20052005
}
20062006
}
20072007
// Then find the last legacy mark from the end if it exists.
2008-
for mark in iter {
2009-
if mark.transparency() == Transparency::SemiTransparent {
2008+
for (mark, transparency) in iter {
2009+
if transparency == Transparency::SemiTransparent {
20102010
result = Some(mark);
20112011
} else {
20122012
break;

src/librustc_resolve/macros.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder;
2424
use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
2525
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
2626
use syntax::ext::expand::{self, AstFragment, AstFragmentKind, Invocation, InvocationKind};
27-
use syntax::ext::hygiene::{self, Mark, Transparency};
27+
use syntax::ext::hygiene::{self, Mark};
2828
use syntax::ext::placeholders::placeholder;
2929
use syntax::ext::tt::macro_rules;
3030
use syntax::feature_gate::{self, emit_feature_err, GateIssue};
@@ -331,13 +331,8 @@ impl<'a> base::Resolver for Resolver<'a> {
331331

332332
self.unused_macros.remove(&def_id);
333333
let ext = self.get_macro(def);
334-
if ext.is_modern() {
335-
let transparency =
336-
if ext.is_transparent() { Transparency::Transparent } else { Transparency::Opaque };
337-
invoc.expansion_data.mark.set_transparency(transparency);
338-
} else if def_id.krate == BUILTIN_MACROS_CRATE {
339-
invoc.expansion_data.mark.set_is_builtin(true);
340-
}
334+
invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
335+
invoc.expansion_data.mark.set_is_builtin(def_id.krate == BUILTIN_MACROS_CRATE);
341336
Ok(Some(ext))
342337
}
343338

src/libsyntax/ext/base.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use syntax_pos::{Span, MultiSpan, DUMMY_SP};
1717
use edition::Edition;
1818
use errors::{DiagnosticBuilder, DiagnosticId};
1919
use ext::expand::{self, AstFragment, Invocation};
20-
use ext::hygiene::{self, Mark, SyntaxContext};
20+
use ext::hygiene::{self, Mark, SyntaxContext, Transparency};
2121
use fold::{self, Folder};
2222
use parse::{self, parser, DirectoryOwnership};
2323
use parse::token;
@@ -673,20 +673,14 @@ impl SyntaxExtension {
673673
}
674674
}
675675

676-
pub fn is_modern(&self) -> bool {
676+
pub fn default_transparency(&self) -> Transparency {
677677
match *self {
678-
SyntaxExtension::DeclMacro { .. } |
679678
SyntaxExtension::ProcMacro { .. } |
680679
SyntaxExtension::AttrProcMacro(..) |
681-
SyntaxExtension::ProcMacroDerive(..) => true,
682-
_ => false,
683-
}
684-
}
685-
686-
pub fn is_transparent(&self) -> bool {
687-
match *self {
688-
SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
689-
_ => false,
680+
SyntaxExtension::ProcMacroDerive(..) |
681+
SyntaxExtension::DeclMacro { is_transparent: false, .. } => Transparency::Opaque,
682+
SyntaxExtension::DeclMacro { is_transparent: true, .. } => Transparency::Transparent,
683+
_ => Transparency::SemiTransparent,
690684
}
691685
}
692686

src/libsyntax_pos/hygiene.rs

+44-32
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct SyntaxContext(u32);
3232
#[derive(Copy, Clone, Debug)]
3333
struct SyntaxContextData {
3434
outer_mark: Mark,
35+
transparency: Transparency,
3536
prev_ctxt: SyntaxContext,
3637
// This context, but with all transparent and semi-transparent marks filtered away.
3738
opaque: SyntaxContext,
@@ -46,14 +47,14 @@ pub struct Mark(u32);
4647
#[derive(Clone, Debug)]
4748
struct MarkData {
4849
parent: Mark,
49-
transparency: Transparency,
50+
default_transparency: Transparency,
5051
is_builtin: bool,
5152
expn_info: Option<ExpnInfo>,
5253
}
5354

5455
/// A property of a macro expansion that determines how identifiers
5556
/// produced by that expansion are resolved.
56-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
57+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
5758
pub enum Transparency {
5859
/// Identifier produced by a transparent expansion is always resolved at call-site.
5960
/// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
@@ -81,7 +82,7 @@ impl Mark {
8182
Mark::fresh_with_data(MarkData {
8283
parent,
8384
// By default expansions behave like `macro_rules`.
84-
transparency: Transparency::SemiTransparent,
85+
default_transparency: Transparency::SemiTransparent,
8586
is_builtin: false,
8687
expn_info: None,
8788
}, data)
@@ -127,34 +128,32 @@ impl Mark {
127128
})
128129
}
129130

131+
// FIXME: This operation doesn't really make sense when single macro expansion
132+
// can produce tokens with different transparencies. Figure out how to avoid it.
130133
pub fn modern(mut self) -> Mark {
131134
HygieneData::with(|data| {
132-
while data.marks[self.0 as usize].transparency != Transparency::Opaque {
135+
while data.marks[self.0 as usize].default_transparency != Transparency::Opaque {
133136
self = data.marks[self.0 as usize].parent;
134137
}
135138
self
136139
})
137140
}
138141

139142
#[inline]
140-
pub fn transparency(self) -> Transparency {
141-
assert_ne!(self, Mark::root());
142-
HygieneData::with(|data| data.marks[self.0 as usize].transparency)
143-
}
144-
145-
#[inline]
146-
pub fn set_transparency(self, transparency: Transparency) {
143+
pub fn set_default_transparency(self, transparency: Transparency) {
147144
assert_ne!(self, Mark::root());
148-
HygieneData::with(|data| data.marks[self.0 as usize].transparency = transparency)
145+
HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency)
149146
}
150147

151148
#[inline]
152149
pub fn is_builtin(self) -> bool {
150+
assert_ne!(self, Mark::root());
153151
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
154152
}
155153

156154
#[inline]
157155
pub fn set_is_builtin(self, is_builtin: bool) {
156+
assert_ne!(self, Mark::root());
158157
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
159158
}
160159

@@ -201,7 +200,7 @@ impl Mark {
201200
crate struct HygieneData {
202201
marks: Vec<MarkData>,
203202
syntax_contexts: Vec<SyntaxContextData>,
204-
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
203+
markings: HashMap<(SyntaxContext, Mark, Transparency), SyntaxContext>,
205204
default_edition: Edition,
206205
}
207206

@@ -212,12 +211,13 @@ impl HygieneData {
212211
parent: Mark::root(),
213212
// If the root is opaque, then loops searching for an opaque mark
214213
// will automatically stop after reaching it.
215-
transparency: Transparency::Opaque,
214+
default_transparency: Transparency::Opaque,
216215
is_builtin: true,
217216
expn_info: None,
218217
}],
219218
syntax_contexts: vec![SyntaxContextData {
220219
outer_mark: Mark::root(),
220+
transparency: Transparency::Opaque,
221221
prev_ctxt: SyntaxContext(0),
222222
opaque: SyntaxContext(0),
223223
opaque_and_semitransparent: SyntaxContext(0),
@@ -267,7 +267,7 @@ impl SyntaxContext {
267267
HygieneData::with(|data| {
268268
data.marks.push(MarkData {
269269
parent: Mark::root(),
270-
transparency: Transparency::SemiTransparent,
270+
default_transparency: Transparency::SemiTransparent,
271271
is_builtin: false,
272272
expn_info: Some(expansion_info),
273273
});
@@ -276,6 +276,7 @@ impl SyntaxContext {
276276

277277
data.syntax_contexts.push(SyntaxContextData {
278278
outer_mark: mark,
279+
transparency: Transparency::SemiTransparent,
279280
prev_ctxt: SyntaxContext::empty(),
280281
opaque: SyntaxContext::empty(),
281282
opaque_and_semitransparent: SyntaxContext::empty(),
@@ -284,22 +285,31 @@ impl SyntaxContext {
284285
})
285286
}
286287

287-
/// Extend a syntax context with a given mark
288288
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
289-
if mark.transparency() == Transparency::Opaque {
290-
return self.apply_mark_internal(mark);
289+
assert_ne!(mark, Mark::root());
290+
self.apply_mark_with_transparency(
291+
mark, HygieneData::with(|data| data.marks[mark.0 as usize].default_transparency)
292+
)
293+
}
294+
295+
/// Extend a syntax context with a given mark and transparency
296+
pub fn apply_mark_with_transparency(self, mark: Mark, transparency: Transparency)
297+
-> SyntaxContext {
298+
assert_ne!(mark, Mark::root());
299+
if transparency == Transparency::Opaque {
300+
return self.apply_mark_internal(mark, transparency);
291301
}
292302

293303
let call_site_ctxt =
294304
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
295-
let call_site_ctxt = if mark.transparency() == Transparency::SemiTransparent {
305+
let call_site_ctxt = if transparency == Transparency::SemiTransparent {
296306
call_site_ctxt.modern()
297307
} else {
298308
call_site_ctxt.modern_and_legacy()
299309
};
300310

301311
if call_site_ctxt == SyntaxContext::empty() {
302-
return self.apply_mark_internal(mark);
312+
return self.apply_mark_internal(mark, transparency);
303313
}
304314

305315
// Otherwise, `mark` is a macros 1.0 definition and the call site is in a
@@ -312,27 +322,26 @@ impl SyntaxContext {
312322
//
313323
// See the example at `test/run-pass/hygiene/legacy_interaction.rs`.
314324
let mut ctxt = call_site_ctxt;
315-
for mark in self.marks() {
316-
ctxt = ctxt.apply_mark_internal(mark);
325+
for (mark, transparency) in self.marks() {
326+
ctxt = ctxt.apply_mark_internal(mark, transparency);
317327
}
318-
ctxt.apply_mark_internal(mark)
328+
ctxt.apply_mark_internal(mark, transparency)
319329
}
320330

321-
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
331+
fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxContext {
322332
HygieneData::with(|data| {
323333
let syntax_contexts = &mut data.syntax_contexts;
324-
let transparency = data.marks[mark.0 as usize].transparency;
325-
326334
let mut opaque = syntax_contexts[self.0 as usize].opaque;
327335
let mut opaque_and_semitransparent =
328336
syntax_contexts[self.0 as usize].opaque_and_semitransparent;
329337

330338
if transparency >= Transparency::Opaque {
331339
let prev_ctxt = opaque;
332-
opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
340+
opaque = *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
333341
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
334342
syntax_contexts.push(SyntaxContextData {
335343
outer_mark: mark,
344+
transparency,
336345
prev_ctxt,
337346
opaque: new_opaque,
338347
opaque_and_semitransparent: new_opaque,
@@ -344,11 +353,12 @@ impl SyntaxContext {
344353
if transparency >= Transparency::SemiTransparent {
345354
let prev_ctxt = opaque_and_semitransparent;
346355
opaque_and_semitransparent =
347-
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
356+
*data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
348357
let new_opaque_and_semitransparent =
349358
SyntaxContext(syntax_contexts.len() as u32);
350359
syntax_contexts.push(SyntaxContextData {
351360
outer_mark: mark,
361+
transparency,
352362
prev_ctxt,
353363
opaque,
354364
opaque_and_semitransparent: new_opaque_and_semitransparent,
@@ -358,11 +368,12 @@ impl SyntaxContext {
358368
}
359369

360370
let prev_ctxt = self;
361-
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
371+
*data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
362372
let new_opaque_and_semitransparent_and_transparent =
363373
SyntaxContext(syntax_contexts.len() as u32);
364374
syntax_contexts.push(SyntaxContextData {
365375
outer_mark: mark,
376+
transparency,
366377
prev_ctxt,
367378
opaque,
368379
opaque_and_semitransparent,
@@ -396,12 +407,13 @@ impl SyntaxContext {
396407
})
397408
}
398409

399-
pub fn marks(mut self) -> Vec<Mark> {
410+
pub fn marks(mut self) -> Vec<(Mark, Transparency)> {
400411
HygieneData::with(|data| {
401412
let mut marks = Vec::new();
402413
while self != SyntaxContext::empty() {
403-
marks.push(data.syntax_contexts[self.0 as usize].outer_mark);
404-
self = data.syntax_contexts[self.0 as usize].prev_ctxt;
414+
let ctxt_data = &data.syntax_contexts[self.0 as usize];
415+
marks.push((ctxt_data.outer_mark, ctxt_data.transparency));
416+
self = ctxt_data.prev_ctxt;
405417
}
406418
marks.reverse();
407419
marks
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2016 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+
// run-pass
12+
// no-prefer-dynamic
13+
14+
#![feature(proc_macro)]
15+
#![crate_type = "proc-macro"]
16+
17+
extern crate proc_macro;
18+
use proc_macro::*;
19+
20+
#[proc_macro]
21+
pub fn check(_: TokenStream) -> TokenStream {
22+
"
23+
struct Outer;
24+
mod inner {
25+
type Inner = Outer; // `Outer` shouldn't be available from here
26+
}
27+
".parse().unwrap()
28+
}

0 commit comments

Comments
 (0)