Skip to content

Commit 360a87d

Browse files
committed
hygiene: Asserts, comments, code cleanup
1 parent 4d30011 commit 360a87d

File tree

1 file changed

+91
-53
lines changed

1 file changed

+91
-53
lines changed

compiler/rustc_span/src/hygiene.rs

+91-53
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
use std::cell::RefCell;
2828
use std::collections::hash_map::Entry;
2929
use std::collections::hash_set::Entry as SetEntry;
30-
use std::fmt;
3130
use std::hash::Hash;
3231
use std::sync::Arc;
32+
use std::{fmt, iter, mem};
3333

3434
use rustc_data_structures::fingerprint::Fingerprint;
3535
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -57,7 +57,11 @@ pub struct SyntaxContext(u32);
5757
impl !Ord for SyntaxContext {}
5858
impl !PartialOrd for SyntaxContext {}
5959

60-
#[derive(Debug, Encodable, Decodable, Clone)]
60+
/// If this part of two syntax contexts is equal, then the whole syntax contexts should be equal.
61+
/// The other fields are only for caching.
62+
type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency);
63+
64+
#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable)]
6165
pub struct SyntaxContextData {
6266
outer_expn: ExpnId,
6367
outer_transparency: Transparency,
@@ -70,6 +74,27 @@ pub struct SyntaxContextData {
7074
dollar_crate_name: Symbol,
7175
}
7276

77+
impl SyntaxContextData {
78+
fn root() -> SyntaxContextData {
79+
SyntaxContextData {
80+
outer_expn: ExpnId::root(),
81+
outer_transparency: Transparency::Opaque,
82+
parent: SyntaxContext::root(),
83+
opaque: SyntaxContext::root(),
84+
opaque_and_semitransparent: SyntaxContext::root(),
85+
dollar_crate_name: kw::DollarCrate,
86+
}
87+
}
88+
89+
fn decode_placeholder() -> SyntaxContextData {
90+
SyntaxContextData { dollar_crate_name: kw::Empty, ..SyntaxContextData::root() }
91+
}
92+
93+
fn is_decode_placeholder(&self) -> bool {
94+
self.dollar_crate_name == kw::Empty
95+
}
96+
}
97+
7398
rustc_index::newtype_index! {
7499
/// A unique ID associated with a macro invocation and expansion.
75100
#[orderable]
@@ -342,7 +367,7 @@ pub(crate) struct HygieneData {
342367
foreign_expn_hashes: FxHashMap<ExpnId, ExpnHash>,
343368
expn_hash_to_expn_id: UnhashMap<ExpnHash, ExpnId>,
344369
syntax_context_data: Vec<SyntaxContextData>,
345-
syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>,
370+
syntax_context_map: FxHashMap<SyntaxContextKey, SyntaxContext>,
346371
/// Maps the `local_hash` of an `ExpnData` to the next disambiguator value.
347372
/// This is used by `update_disambiguator` to keep track of which `ExpnData`s
348373
/// would have collisions without a disambiguator.
@@ -361,21 +386,15 @@ impl HygieneData {
361386
None,
362387
);
363388

389+
let root_ctxt_data = SyntaxContextData::root();
364390
HygieneData {
365391
local_expn_data: IndexVec::from_elem_n(Some(root_data), 1),
366392
local_expn_hashes: IndexVec::from_elem_n(ExpnHash(Fingerprint::ZERO), 1),
367393
foreign_expn_data: FxHashMap::default(),
368394
foreign_expn_hashes: FxHashMap::default(),
369-
expn_hash_to_expn_id: std::iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root()))
395+
expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root()))
370396
.collect(),
371-
syntax_context_data: vec![SyntaxContextData {
372-
outer_expn: ExpnId::root(),
373-
outer_transparency: Transparency::Opaque,
374-
parent: SyntaxContext(0),
375-
opaque: SyntaxContext(0),
376-
opaque_and_semitransparent: SyntaxContext(0),
377-
dollar_crate_name: kw::DollarCrate,
378-
}],
397+
syntax_context_data: vec![root_ctxt_data],
379398
syntax_context_map: FxHashMap::default(),
380399
expn_data_disambiguators: UnhashMap::default(),
381400
}
@@ -425,23 +444,28 @@ impl HygieneData {
425444
}
426445

427446
fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext {
447+
debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
428448
self.syntax_context_data[ctxt.0 as usize].opaque
429449
}
430450

431451
fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext {
452+
debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
432453
self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent
433454
}
434455

435456
fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
457+
debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
436458
self.syntax_context_data[ctxt.0 as usize].outer_expn
437459
}
438460

439461
fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) {
462+
debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
440463
let data = &self.syntax_context_data[ctxt.0 as usize];
441464
(data.outer_expn, data.outer_transparency)
442465
}
443466

444467
fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext {
468+
debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
445469
self.syntax_context_data[ctxt.0 as usize].parent
446470
}
447471

@@ -551,6 +575,7 @@ impl HygieneData {
551575
transparency: Transparency,
552576
) -> SyntaxContext {
553577
let syntax_context_data = &mut self.syntax_context_data;
578+
debug_assert!(!syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
554579
let mut opaque = syntax_context_data[ctxt.0 as usize].opaque;
555580
let mut opaque_and_semitransparent =
556581
syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent;
@@ -561,7 +586,7 @@ impl HygieneData {
561586
.syntax_context_map
562587
.entry((parent, expn_id, transparency))
563588
.or_insert_with(|| {
564-
let new_opaque = SyntaxContext(syntax_context_data.len() as u32);
589+
let new_opaque = SyntaxContext::from_usize(syntax_context_data.len());
565590
syntax_context_data.push(SyntaxContextData {
566591
outer_expn: expn_id,
567592
outer_transparency: transparency,
@@ -581,7 +606,7 @@ impl HygieneData {
581606
.entry((parent, expn_id, transparency))
582607
.or_insert_with(|| {
583608
let new_opaque_and_semitransparent =
584-
SyntaxContext(syntax_context_data.len() as u32);
609+
SyntaxContext::from_usize(syntax_context_data.len());
585610
syntax_context_data.push(SyntaxContextData {
586611
outer_expn: expn_id,
587612
outer_transparency: transparency,
@@ -596,8 +621,6 @@ impl HygieneData {
596621

597622
let parent = ctxt;
598623
*self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| {
599-
let new_opaque_and_semitransparent_and_transparent =
600-
SyntaxContext(syntax_context_data.len() as u32);
601624
syntax_context_data.push(SyntaxContextData {
602625
outer_expn: expn_id,
603626
outer_transparency: transparency,
@@ -606,7 +629,7 @@ impl HygieneData {
606629
opaque_and_semitransparent,
607630
dollar_crate_name: kw::DollarCrate,
608631
});
609-
new_opaque_and_semitransparent_and_transparent
632+
SyntaxContext::from_usize(syntax_context_data.len() - 1)
610633
})
611634
}
612635
}
@@ -713,6 +736,10 @@ impl SyntaxContext {
713736
SyntaxContext(raw as u32)
714737
}
715738

739+
fn from_usize(raw: usize) -> SyntaxContext {
740+
SyntaxContext(u32::try_from(raw).unwrap())
741+
}
742+
716743
/// Extend a syntax context with a given expansion and transparency.
717744
pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
718745
HygieneData::with(|data| data.apply_mark(self, expn_id, transparency))
@@ -893,7 +920,10 @@ impl SyntaxContext {
893920
}
894921

895922
pub(crate) fn dollar_crate_name(self) -> Symbol {
896-
HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name)
923+
HygieneData::with(|data| {
924+
debug_assert!(!data.syntax_context_data[self.0 as usize].is_decode_placeholder());
925+
data.syntax_context_data[self.0 as usize].dollar_crate_name
926+
})
897927
}
898928

899929
pub fn edition(self) -> Edition {
@@ -1244,7 +1274,7 @@ impl HygieneEncodeContext {
12441274

12451275
// Consume the current round of SyntaxContexts.
12461276
// Drop the lock() temporary early
1247-
let latest_ctxts = { std::mem::take(&mut *self.latest_ctxts.lock()) };
1277+
let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) };
12481278

12491279
// It's fine to iterate over a HashMap, because the serialization
12501280
// of the table that we insert data into doesn't depend on insertion
@@ -1256,7 +1286,7 @@ impl HygieneEncodeContext {
12561286
}
12571287
});
12581288

1259-
let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) };
1289+
let latest_expns = { mem::take(&mut *self.latest_expns.lock()) };
12601290

12611291
// Same as above, this is fine as we are inserting into a order-independent hashset
12621292
#[allow(rustc::potential_query_instability)]
@@ -1373,28 +1403,33 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
13731403
return SyntaxContext::root();
13741404
}
13751405

1376-
let ctxt = {
1406+
let pending_ctxt = {
13771407
let mut inner = context.inner.lock();
13781408

1409+
// Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
1410+
// raw ids from different crate metadatas.
13791411
if let Some(ctxt) = inner.remapped_ctxts.get(raw_id as usize).copied().flatten() {
13801412
// This has already been decoded.
13811413
return ctxt;
13821414
}
13831415

13841416
match inner.decoding.entry(raw_id) {
13851417
Entry::Occupied(ctxt_entry) => {
1418+
let pending_ctxt = *ctxt_entry.get();
13861419
match context.local_in_progress.borrow_mut().entry(raw_id) {
1387-
SetEntry::Occupied(..) => {
1388-
// We're decoding this already on the current thread. Return here
1389-
// and let the function higher up the stack finish decoding to handle
1390-
// recursive cases.
1391-
return *ctxt_entry.get();
1392-
}
1420+
// We're decoding this already on the current thread. Return here and let the
1421+
// function higher up the stack finish decoding to handle recursive cases.
1422+
// Hopefully having a `SyntaxContext` that refers to an incorrect data is ok
1423+
// during reminder of the decoding process, it's certainly not ok after the
1424+
// top level decoding function returns.
1425+
SetEntry::Occupied(..) => return pending_ctxt,
1426+
// Some other thread is currently decoding this.
1427+
// Race with it (alternatively we could wait here).
1428+
// We cannot return this value, unlike in the recursive case above, because it
1429+
// may expose a `SyntaxContext` pointing to incorrect data to arbitrary code.
13931430
SetEntry::Vacant(entry) => {
13941431
entry.insert();
1395-
1396-
// Some other thread is current decoding this. Race with it.
1397-
*ctxt_entry.get()
1432+
pending_ctxt
13981433
}
13991434
}
14001435
}
@@ -1405,18 +1440,10 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
14051440
// Allocate and store SyntaxContext id *before* calling the decoder function,
14061441
// as the SyntaxContextData may reference itself.
14071442
let new_ctxt = HygieneData::with(|hygiene_data| {
1408-
let new_ctxt = SyntaxContext(hygiene_data.syntax_context_data.len() as u32);
14091443
// Push a dummy SyntaxContextData to ensure that nobody else can get the
1410-
// same ID as us. This will be overwritten after call `decode_Data`
1411-
hygiene_data.syntax_context_data.push(SyntaxContextData {
1412-
outer_expn: ExpnId::root(),
1413-
outer_transparency: Transparency::Transparent,
1414-
parent: SyntaxContext::root(),
1415-
opaque: SyntaxContext::root(),
1416-
opaque_and_semitransparent: SyntaxContext::root(),
1417-
dollar_crate_name: kw::Empty,
1418-
});
1419-
new_ctxt
1444+
// same ID as us. This will be overwritten after call `decode_data`.
1445+
hygiene_data.syntax_context_data.push(SyntaxContextData::decode_placeholder());
1446+
SyntaxContext::from_usize(hygiene_data.syntax_context_data.len() - 1)
14201447
});
14211448
entry.insert(new_ctxt);
14221449
new_ctxt
@@ -1426,27 +1453,38 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
14261453

14271454
// Don't try to decode data while holding the lock, since we need to
14281455
// be able to recursively decode a SyntaxContext
1429-
let mut ctxt_data = decode_data(d, raw_id);
1430-
// Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`
1431-
// We don't care what the encoding crate set this to - we want to resolve it
1432-
// from the perspective of the current compilation session
1433-
ctxt_data.dollar_crate_name = kw::DollarCrate;
1456+
let ctxt_data = decode_data(d, raw_id);
14341457

1435-
// Overwrite the dummy data with our decoded SyntaxContextData
1436-
HygieneData::with(|hygiene_data| {
1437-
if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize)
1458+
let ctxt = HygieneData::with(|hygiene_data| {
1459+
let old = if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize)
14381460
&& old.outer_expn == ctxt_data.outer_expn
14391461
&& old.outer_transparency == ctxt_data.outer_transparency
14401462
&& old.parent == ctxt_data.parent
14411463
{
1442-
ctxt_data = old.clone();
1464+
Some(old.clone())
1465+
} else {
1466+
None
1467+
};
1468+
// Overwrite its placeholder data with our decoded data.
1469+
let ctxt_data_ref = &mut hygiene_data.syntax_context_data[pending_ctxt.as_u32() as usize];
1470+
let prev_ctxt_data = mem::replace(ctxt_data_ref, ctxt_data);
1471+
// Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`.
1472+
// We don't care what the encoding crate set this to - we want to resolve it
1473+
// from the perspective of the current compilation session
1474+
ctxt_data_ref.dollar_crate_name = kw::DollarCrate;
1475+
if let Some(old) = old {
1476+
*ctxt_data_ref = old;
14431477
}
1444-
1445-
hygiene_data.syntax_context_data[ctxt.as_u32() as usize] = ctxt_data;
1478+
// Make sure nothing weird happened while `decode_data` was running.
1479+
if !prev_ctxt_data.is_decode_placeholder() {
1480+
// Another thread may have already inserted the decoded data,
1481+
// but the decoded data should match.
1482+
assert_eq!(prev_ctxt_data, *ctxt_data_ref);
1483+
}
1484+
pending_ctxt
14461485
});
14471486

14481487
// Mark the context as completed
1449-
14501488
context.local_in_progress.borrow_mut().remove(&raw_id);
14511489

14521490
let mut inner = context.inner.lock();

0 commit comments

Comments
 (0)