1
Fork 0

hygiene: Ensure uniqueness of SyntaxContextDatas

This commit is contained in:
Vadim Petrochenkov 2024-09-13 23:08:37 +03:00
parent 360a87d51d
commit 6e1effe948

View file

@ -93,6 +93,10 @@ impl SyntaxContextData {
fn is_decode_placeholder(&self) -> bool { fn is_decode_placeholder(&self) -> bool {
self.dollar_crate_name == kw::Empty self.dollar_crate_name == kw::Empty
} }
fn key(&self) -> SyntaxContextKey {
(self.parent, self.outer_expn, self.outer_transparency)
}
} }
rustc_index::newtype_index! { rustc_index::newtype_index! {
@ -395,7 +399,7 @@ impl HygieneData {
expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root()))
.collect(), .collect(),
syntax_context_data: vec![root_ctxt_data], syntax_context_data: vec![root_ctxt_data],
syntax_context_map: FxHashMap::default(), syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(),
expn_data_disambiguators: UnhashMap::default(), expn_data_disambiguators: UnhashMap::default(),
} }
} }
@ -1454,34 +1458,38 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
// Don't try to decode data while holding the lock, since we need to // Don't try to decode data while holding the lock, since we need to
// be able to recursively decode a SyntaxContext // be able to recursively decode a SyntaxContext
let ctxt_data = decode_data(d, raw_id); let ctxt_data = decode_data(d, raw_id);
let ctxt_key = ctxt_data.key();
let ctxt = HygieneData::with(|hygiene_data| { let ctxt = HygieneData::with(|hygiene_data| {
let old = if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize) match hygiene_data.syntax_context_map.get(&ctxt_key) {
&& old.outer_expn == ctxt_data.outer_expn // Ensure that syntax contexts are unique.
&& old.outer_transparency == ctxt_data.outer_transparency // If syntax contexts with the given key already exists, reuse it instead of
&& old.parent == ctxt_data.parent // using `pending_ctxt`.
{ // `pending_ctxt` will leave an unused hole in the vector of syntax contexts.
Some(old.clone()) // Hopefully its value isn't stored anywhere during decoding and its dummy data
} else { // is never accessed later. The `is_decode_placeholder` asserts on all
None // accesses to syntax context data attempt to ensure it.
}; Some(&ctxt) => ctxt,
// Overwrite its placeholder data with our decoded data. // This is a completely new context.
let ctxt_data_ref = &mut hygiene_data.syntax_context_data[pending_ctxt.as_u32() as usize]; // Overwrite its placeholder data with our decoded data.
let prev_ctxt_data = mem::replace(ctxt_data_ref, ctxt_data); None => {
// Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`. let ctxt_data_ref =
// We don't care what the encoding crate set this to - we want to resolve it &mut hygiene_data.syntax_context_data[pending_ctxt.as_u32() as usize];
// from the perspective of the current compilation session let prev_ctxt_data = mem::replace(ctxt_data_ref, ctxt_data);
ctxt_data_ref.dollar_crate_name = kw::DollarCrate; // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`.
if let Some(old) = old { // We don't care what the encoding crate set this to - we want to resolve it
*ctxt_data_ref = old; // from the perspective of the current compilation session.
ctxt_data_ref.dollar_crate_name = kw::DollarCrate;
// Make sure nothing weird happened while `decode_data` was running.
if !prev_ctxt_data.is_decode_placeholder() {
// Another thread may have already inserted the decoded data,
// but the decoded data should match.
assert_eq!(prev_ctxt_data, *ctxt_data_ref);
}
hygiene_data.syntax_context_map.insert(ctxt_key, pending_ctxt);
pending_ctxt
}
} }
// Make sure nothing weird happened while `decode_data` was running.
if !prev_ctxt_data.is_decode_placeholder() {
// Another thread may have already inserted the decoded data,
// but the decoded data should match.
assert_eq!(prev_ctxt_data, *ctxt_data_ref);
}
pending_ctxt
}); });
// Mark the context as completed // Mark the context as completed