Skip to content

Commit 50b5f1d

Browse files
committed
Auto merge of #51457 - petrochenkov:hygnoparent, r=<try>
hygiene: Eliminate expansion hierarchy in favor of call-site hierarchy "Expansion hierarchy" and "call-site hierarchy" are two slightly different ways to nest macro invocations and answer the question "who is the parent" for a given invocation. For example, here both hierarchies coincide ```rust macro inner() { ... } macro outer() { inner!() } outer!(); // expansions: root -> outer -> inner // call-sites: root -> outer -> inner ``` but here they are different ```rust macro inner() { ... } macro outer($stuff: stuff) { $stuff } // expansions: root -> outer -> inner (`inner` is expanded as a part of `outer`'s output) // call-sites: root -> outer; root -> inner outer!(inner!()) ``` All our talk about hygiene was in terms of "def-site" and "call-site" name resolution so far and the "expansion hierarchy" is an internal detail, but it was actually used in few places in the way affecting resolution results. For example, in the attached test case the structure `S` itself was previously resolved succesfully, but resolution of its field `field` failed due to use of "expansion hierarchy". This PR eliminates expansion hierarchy from hygiene algorithm and uses call-site hierarchy instead. This is also a nice simplification of the model. Instead of growing in *three* dimensions in similar but subtly different ways (def-site, call-site, expansion) hygiene hierarchies now grow in *two* dimensions in similar but subtly different ways (def-site, call-site).
2 parents 0d76317 + ef001ae commit 50b5f1d

File tree

11 files changed

+77
-21
lines changed

11 files changed

+77
-21
lines changed

src/libproc_macro/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl FromStr for TokenStream {
123123
let expn_info = mark.expn_info().unwrap();
124124
let call_site = expn_info.call_site;
125125
// notify the expansion info that it is unhygienic
126-
let mark = Mark::fresh(mark);
126+
let mark = Mark::fresh();
127127
mark.set_expn_info(expn_info);
128128
let span = call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark));
129129
let stream = parse::parse_stream_from_source_str(name, src, sess, Some(span));

src/librustc/hir/lowering.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ impl<'a> LoweringContext<'a> {
580580
}
581581

582582
fn allow_internal_unstable(&self, reason: CompilerDesugaringKind, span: Span) -> Span {
583-
let mark = Mark::fresh(Mark::root());
583+
let mark = Mark::fresh();
584584
mark.set_expn_info(codemap::ExpnInfo {
585585
call_site: span,
586586
callee: codemap::NameAndSpan {

src/librustc_allocator/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> {
7878
}
7979
self.found = true;
8080

81-
let mark = Mark::fresh(Mark::root());
81+
let mark = Mark::fresh();
8282
mark.set_expn_info(ExpnInfo {
8383
call_site: DUMMY_SP,
8484
callee: NameAndSpan {

src/librustc_resolve/macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl<'a> base::Resolver for Resolver<'a> {
123123
}
124124

125125
fn get_module_scope(&mut self, id: ast::NodeId) -> Mark {
126-
let mark = Mark::fresh(Mark::root());
126+
let mark = Mark::fresh();
127127
let module = self.module_map[&self.definitions.local_def_id(id)];
128128
self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData {
129129
module: Cell::new(module),

src/libsyntax/ext/expand.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
347347
let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new);
348348

349349
for path in &traits {
350-
let mark = Mark::fresh(self.cx.current_expansion.mark);
350+
let mark = Mark::fresh();
351351
derives.push(mark);
352352
let item = match self.cx.resolver.resolve_macro(
353353
Mark::root(), path, MacroKind::Derive, false) {
@@ -999,7 +999,7 @@ struct InvocationCollector<'a, 'b: 'a> {
999999

10001000
impl<'a, 'b> InvocationCollector<'a, 'b> {
10011001
fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion {
1002-
let mark = Mark::fresh(self.cx.current_expansion.mark);
1002+
let mark = Mark::fresh();
10031003
self.invocations.push(Invocation {
10041004
kind,
10051005
expansion_kind,

src/libsyntax/std_inject.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use tokenstream::TokenStream;
2222
/// call to codemap's `is_internal` check.
2323
/// The expanded code uses the unstable `#[prelude_import]` attribute.
2424
fn ignored_span(sp: Span) -> Span {
25-
let mark = Mark::fresh(Mark::root());
25+
let mark = Mark::fresh();
2626
mark.set_expn_info(ExpnInfo {
2727
call_site: DUMMY_SP,
2828
callee: NameAndSpan {

src/libsyntax/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ fn generate_test_harness(sess: &ParseSess,
275275
let mut cleaner = EntryPointCleaner { depth: 0 };
276276
let krate = cleaner.fold_crate(krate);
277277

278-
let mark = Mark::fresh(Mark::root());
278+
let mark = Mark::fresh();
279279

280280
let mut econfig = ExpansionConfig::default("test".to_string());
281281
econfig.features = Some(features);

src/libsyntax_ext/deriving/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn call_intrinsic(cx: &ExtCtxt,
159159
} else { // Avoid instability errors with user defined curstom derives, cc #36316
160160
let mut info = cx.current_expansion.mark.expn_info().unwrap();
161161
info.callee.allow_internal_unstable = true;
162-
let mark = Mark::fresh(Mark::root());
162+
let mark = Mark::fresh();
163163
mark.set_expn_info(info);
164164
span = span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
165165
}

src/libsyntax_ext/proc_macro_registrar.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ fn mk_registrar(cx: &mut ExtCtxt,
361361
custom_derives: &[ProcMacroDerive],
362362
custom_attrs: &[ProcMacroDef],
363363
custom_macros: &[ProcMacroDef]) -> P<ast::Item> {
364-
let mark = Mark::fresh(Mark::root());
364+
let mark = Mark::fresh();
365365
mark.set_expn_info(ExpnInfo {
366366
call_site: DUMMY_SP,
367367
callee: NameAndSpan {

src/libsyntax_pos/hygiene.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ pub struct SyntaxContextData {
4141
pub struct Mark(u32);
4242

4343
struct MarkData {
44-
parent: Mark,
4544
kind: MarkKind,
4645
expn_info: Option<ExpnInfo>,
4746
}
@@ -54,9 +53,9 @@ pub enum MarkKind {
5453
}
5554

5655
impl Mark {
57-
pub fn fresh(parent: Mark) -> Self {
56+
pub fn fresh() -> Self {
5857
HygieneData::with(|data| {
59-
data.marks.push(MarkData { parent: parent, kind: MarkKind::Legacy, expn_info: None });
58+
data.marks.push(MarkData { kind: MarkKind::Legacy, expn_info: None });
6059
Mark(data.marks.len() as u32 - 1)
6160
})
6261
}
@@ -93,7 +92,7 @@ impl Mark {
9392
if self == Mark::root() || data.marks[self.0 as usize].kind == MarkKind::Modern {
9493
return self;
9594
}
96-
self = data.marks[self.0 as usize].parent;
95+
self = self.call_site_mark(data);
9796
}
9897
})
9998
}
@@ -114,7 +113,7 @@ impl Mark {
114113
if self == Mark::root() {
115114
return false;
116115
}
117-
self = data.marks[self.0 as usize].parent;
116+
self = self.call_site_mark(data);
118117
}
119118
true
120119
})
@@ -134,17 +133,24 @@ impl Mark {
134133
let mut a_path = FxHashSet::<Mark>();
135134
while a != Mark::root() {
136135
a_path.insert(a);
137-
a = data.marks[a.0 as usize].parent;
136+
a = a.call_site_mark(data);
138137
}
139138

140139
// While the path from b to the root hasn't intersected, move up the tree
141140
while !a_path.contains(&b) {
142-
b = data.marks[b.0 as usize].parent;
141+
b = b.call_site_mark(data);
143142
}
144143

145144
b
146145
})
147146
}
147+
148+
/// Private helpers not acquiring a lock around global data
149+
fn call_site_mark(self, data: &HygieneData) -> Mark {
150+
data.marks[self.0 as usize].expn_info.as_ref()
151+
.map(|einfo| data.syntax_contexts[einfo.call_site.ctxt().0 as usize].outer_mark)
152+
.unwrap_or(Mark::root())
153+
}
148154
}
149155

150156
pub struct HygieneData {
@@ -159,7 +165,6 @@ impl HygieneData {
159165
pub fn new() -> Self {
160166
HygieneData {
161167
marks: vec![MarkData {
162-
parent: Mark::root(),
163168
kind: MarkKind::Builtin,
164169
expn_info: None,
165170
}],
@@ -198,14 +203,13 @@ impl SyntaxContext {
198203

199204
// Allocate a new SyntaxContext with the given ExpnInfo. This is used when
200205
// deserializing Spans from the incr. comp. cache.
201-
// FIXME(mw): This method does not restore MarkData::parent or
202-
// SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
206+
// FIXME(mw): This method does not restore SyntaxContextData::prev_ctxt or
207+
// SyntaxContextData::modern. These things
203208
// don't seem to be used after HIR lowering, so everything should be fine
204209
// as long as incremental compilation does not kick in before that.
205210
pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
206211
HygieneData::with(|data| {
207212
data.marks.push(MarkData {
208-
parent: Mark::root(),
209213
kind: MarkKind::Legacy,
210214
expn_info: Some(expansion_info)
211215
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
// compile-pass
12+
13+
#![feature(decl_macro)]
14+
15+
macro_rules! define_field {
16+
() => {
17+
struct S { field: u8 }
18+
};
19+
($field: ident) => {
20+
struct Z { $field: u8 }
21+
};
22+
}
23+
24+
mod modern {
25+
macro use_field($define_field1: item, $define_field2: item) {
26+
$define_field1
27+
$define_field2
28+
29+
// OK, both struct name `S` and field `name` resolve to definitions
30+
// produced by `define_field` and living in the "root" context
31+
// that is in scope at `use_field`'s def-site.
32+
fn f() { S { field: 0 }; }
33+
fn g() { Z { field: 0 }; }
34+
}
35+
36+
use_field!(define_field!{}, define_field!{ field });
37+
}
38+
39+
mod legacy {
40+
macro_rules! use_field {($define_field1: item, $define_field2: item) => {
41+
$define_field1
42+
$define_field2
43+
44+
// OK, everything is at the same call-site.
45+
fn f() { S { field: 0 }; }
46+
fn g() { Z { field: 0 }; }
47+
}}
48+
49+
use_field!(define_field!{}, define_field!{ field });
50+
}
51+
52+
fn main() {}

0 commit comments

Comments
 (0)