2727use std:: cell:: RefCell ;
2828use std:: collections:: hash_map:: Entry ;
2929use std:: collections:: hash_set:: Entry as SetEntry ;
30- use std:: fmt;
3130use std:: hash:: Hash ;
3231use std:: sync:: Arc ;
32+ use std:: { fmt, iter, mem} ;
3333
3434use rustc_data_structures:: fingerprint:: Fingerprint ;
3535use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -57,7 +57,11 @@ pub struct SyntaxContext(u32);
5757impl !Ord for SyntaxContext { }
5858impl !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 ) ]
6165pub 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+
7398rustc_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