2424// because getting it wrong can lead to nested `HygieneData::with` calls that
2525// trigger runtime aborts. (Fortunately these are obvious and easy to fix.)
2626
27+ use std:: cell:: RefCell ;
28+ use std:: collections:: hash_map:: Entry ;
29+ use std:: collections:: hash_set:: Entry as SetEntry ;
2730use std:: hash:: Hash ;
2831use std:: sync:: Arc ;
2932use std:: { fmt, iter, mem} ;
3033
3134use rustc_data_structures:: fingerprint:: Fingerprint ;
3235use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
3336use rustc_data_structures:: stable_hasher:: { HashStable , HashingControls , StableHasher } ;
34- use rustc_data_structures:: sync:: Lock ;
37+ use rustc_data_structures:: sync:: { Lock , WorkerLocal } ;
3538use rustc_data_structures:: unhash:: UnhashMap ;
3639use rustc_hashes:: Hash64 ;
3740use rustc_index:: IndexVec ;
@@ -56,10 +59,10 @@ impl !PartialOrd for SyntaxContext {}
5659
5760/// If this part of two syntax contexts is equal, then the whole syntax contexts should be equal.
5861/// The other fields are only for caching.
59- pub type SyntaxContextKey = ( SyntaxContext , ExpnId , Transparency ) ;
62+ type SyntaxContextKey = ( SyntaxContext , ExpnId , Transparency ) ;
6063
6164#[ derive( Clone , Copy , PartialEq , Debug , Encodable , Decodable ) ]
62- struct SyntaxContextData {
65+ pub struct SyntaxContextData {
6366 outer_expn : ExpnId ,
6467 outer_transparency : Transparency ,
6568 parent : SyntaxContext ,
@@ -91,21 +94,6 @@ impl SyntaxContextData {
9194 self . dollar_crate_name == kw:: Empty
9295 }
9396
94- fn new (
95- ( parent, outer_expn, outer_transparency) : SyntaxContextKey ,
96- opaque : SyntaxContext ,
97- opaque_and_semitransparent : SyntaxContext ,
98- ) -> Self {
99- SyntaxContextData {
100- parent,
101- outer_expn,
102- outer_transparency,
103- opaque,
104- opaque_and_semitransparent,
105- dollar_crate_name : kw:: DollarCrate ,
106- }
107- }
108-
10997 fn key ( & self ) -> SyntaxContextKey {
11098 ( self . parent , self . outer_expn , self . outer_transparency )
11199 }
@@ -586,49 +574,67 @@ impl HygieneData {
586574
587575 fn apply_mark_internal (
588576 & mut self ,
589- parent : SyntaxContext ,
577+ ctxt : SyntaxContext ,
590578 expn_id : ExpnId ,
591579 transparency : Transparency ,
592580 ) -> SyntaxContext {
593- debug_assert ! ( !self . syntax_context_data[ parent. 0 as usize ] . is_decode_placeholder( ) ) ;
594- // Look into the cache first.
595- let key = ( parent, expn_id, transparency) ;
596- if let Some ( ctxt) = self . syntax_context_map . get ( & key) {
597- return * ctxt;
581+ let syntax_context_data = & mut self . syntax_context_data ;
582+ debug_assert ! ( !syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
583+ let mut opaque = syntax_context_data[ ctxt. 0 as usize ] . opaque ;
584+ let mut opaque_and_semitransparent =
585+ syntax_context_data[ ctxt. 0 as usize ] . opaque_and_semitransparent ;
586+
587+ if transparency >= Transparency :: Opaque {
588+ let parent = opaque;
589+ opaque = * self
590+ . syntax_context_map
591+ . entry ( ( parent, expn_id, transparency) )
592+ . or_insert_with ( || {
593+ let new_opaque = SyntaxContext :: from_usize ( syntax_context_data. len ( ) ) ;
594+ syntax_context_data. push ( SyntaxContextData {
595+ outer_expn : expn_id,
596+ outer_transparency : transparency,
597+ parent,
598+ opaque : new_opaque,
599+ opaque_and_semitransparent : new_opaque,
600+ dollar_crate_name : kw:: DollarCrate ,
601+ } ) ;
602+ new_opaque
603+ } ) ;
604+ }
605+
606+ if transparency >= Transparency :: SemiTransparent {
607+ let parent = opaque_and_semitransparent;
608+ opaque_and_semitransparent = * self
609+ . syntax_context_map
610+ . entry ( ( parent, expn_id, transparency) )
611+ . or_insert_with ( || {
612+ let new_opaque_and_semitransparent =
613+ SyntaxContext :: from_usize ( syntax_context_data. len ( ) ) ;
614+ syntax_context_data. push ( SyntaxContextData {
615+ outer_expn : expn_id,
616+ outer_transparency : transparency,
617+ parent,
618+ opaque,
619+ opaque_and_semitransparent : new_opaque_and_semitransparent,
620+ dollar_crate_name : kw:: DollarCrate ,
621+ } ) ;
622+ new_opaque_and_semitransparent
623+ } ) ;
598624 }
599- // Reserve a new syntax context.
600- let ctxt = SyntaxContext :: from_usize ( self . syntax_context_data . len ( ) ) ;
601- self . syntax_context_data . push ( SyntaxContextData :: decode_placeholder ( ) ) ;
602- self . syntax_context_map . insert ( key, ctxt) ;
603-
604- // Opaque and semi-transparent versions of the parent. Note that they may be equal to the
605- // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques,
606- // and `parent_opaque_and_semitransparent` == `parent` if the expn contains only opaques
607- // and semi-transparents.
608- let parent_opaque = self . syntax_context_data [ parent. 0 as usize ] . opaque ;
609- let parent_opaque_and_semitransparent =
610- self . syntax_context_data [ parent. 0 as usize ] . opaque_and_semitransparent ;
611-
612- // Evaluate opaque and semi-transparent versions of the new syntax context.
613- let ( opaque, opaque_and_semitransparent) = match transparency {
614- Transparency :: Transparent => ( parent_opaque, parent_opaque_and_semitransparent) ,
615- Transparency :: SemiTransparent => (
616- parent_opaque,
617- // Will be the same as `ctxt` if the expn chain contains only opaques and semi-transparents.
618- self . apply_mark_internal ( parent_opaque_and_semitransparent, expn_id, transparency) ,
619- ) ,
620- Transparency :: Opaque => (
621- // Will be the same as `ctxt` if the expn chain contains only opaques.
622- self . apply_mark_internal ( parent_opaque, expn_id, transparency) ,
623- // Will be the same as `ctxt` if the expn chain contains only opaques and semi-transparents.
624- self . apply_mark_internal ( parent_opaque_and_semitransparent, expn_id, transparency) ,
625- ) ,
626- } ;
627625
628- // Fill the full data, now that we have it.
629- self . syntax_context_data [ ctxt. as_u32 ( ) as usize ] =
630- SyntaxContextData :: new ( key, opaque, opaque_and_semitransparent) ;
631- ctxt
626+ let parent = ctxt;
627+ * self . syntax_context_map . entry ( ( parent, expn_id, transparency) ) . or_insert_with ( || {
628+ syntax_context_data. push ( SyntaxContextData {
629+ outer_expn : expn_id,
630+ outer_transparency : transparency,
631+ parent,
632+ opaque,
633+ opaque_and_semitransparent,
634+ dollar_crate_name : kw:: DollarCrate ,
635+ } ) ;
636+ SyntaxContext :: from_usize ( syntax_context_data. len ( ) - 1 )
637+ } )
632638 }
633639}
634640
@@ -1259,7 +1265,7 @@ impl HygieneEncodeContext {
12591265 pub fn encode < T > (
12601266 & self ,
12611267 encoder : & mut T ,
1262- mut encode_ctxt : impl FnMut ( & mut T , u32 , & SyntaxContextKey ) ,
1268+ mut encode_ctxt : impl FnMut ( & mut T , u32 , & SyntaxContextData ) ,
12631269 mut encode_expn : impl FnMut ( & mut T , ExpnId , & ExpnData , ExpnHash ) ,
12641270 ) {
12651271 // When we serialize a `SyntaxContextData`, we may end up serializing
@@ -1317,6 +1323,9 @@ struct HygieneDecodeContextInner {
13171323/// Additional information used to assist in decoding hygiene data
13181324pub struct HygieneDecodeContext {
13191325 inner : Lock < HygieneDecodeContextInner > ,
1326+
1327+ /// A set of serialized `SyntaxContext` ids that are currently being decoded on each thread.
1328+ local_in_progress : WorkerLocal < RefCell < FxHashSet < u32 > > > ,
13201329}
13211330
13221331/// Register an expansion which has been decoded from the on-disk-cache for the local crate.
@@ -1387,10 +1396,10 @@ pub fn decode_expn_id(
13871396// to track which `SyntaxContext`s we have already decoded.
13881397// The provided closure will be invoked to deserialize a `SyntaxContextData`
13891398// if we haven't already seen the id of the `SyntaxContext` we are deserializing.
1390- pub fn decode_syntax_context < D : Decoder , F : FnOnce ( & mut D , u32 ) -> SyntaxContextKey > (
1399+ pub fn decode_syntax_context < D : Decoder , F : FnOnce ( & mut D , u32 ) -> SyntaxContextData > (
13911400 d : & mut D ,
13921401 context : & HygieneDecodeContext ,
1393- decode_ctxt_key : F ,
1402+ decode_data : F ,
13941403) -> SyntaxContext {
13951404 let raw_id: u32 = Decodable :: decode ( d) ;
13961405 if raw_id == 0 {
@@ -1399,9 +1408,58 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
13991408 return SyntaxContext :: root ( ) ;
14001409 }
14011410
1411+ let pending_ctxt = {
1412+ let mut inner = context. inner . lock ( ) ;
1413+
1414+ // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
1415+ // raw ids from different crate metadatas.
1416+ if let Some ( ctxt) = inner. remapped_ctxts . get ( raw_id as usize ) . copied ( ) . flatten ( ) {
1417+ // This has already been decoded.
1418+ return ctxt;
1419+ }
1420+
1421+ match inner. decoding . entry ( raw_id) {
1422+ Entry :: Occupied ( ctxt_entry) => {
1423+ let pending_ctxt = * ctxt_entry. get ( ) ;
1424+ match context. local_in_progress . borrow_mut ( ) . entry ( raw_id) {
1425+ // We're decoding this already on the current thread. Return here and let the
1426+ // function higher up the stack finish decoding to handle recursive cases.
1427+ // Hopefully having a `SyntaxContext` that refers to an incorrect data is ok
1428+ // during reminder of the decoding process, it's certainly not ok after the
1429+ // top level decoding function returns.
1430+ SetEntry :: Occupied ( ..) => return pending_ctxt,
1431+ // Some other thread is currently decoding this.
1432+ // Race with it (alternatively we could wait here).
1433+ // We cannot return this value, unlike in the recursive case above, because it
1434+ // may expose a `SyntaxContext` pointing to incorrect data to arbitrary code.
1435+ SetEntry :: Vacant ( entry) => {
1436+ entry. insert ( ) ;
1437+ pending_ctxt
1438+ }
1439+ }
1440+ }
1441+ Entry :: Vacant ( entry) => {
1442+ // We are the first thread to start decoding. Mark the current thread as being progress.
1443+ context. local_in_progress . borrow_mut ( ) . insert ( raw_id) ;
1444+
1445+ // Allocate and store SyntaxContext id *before* calling the decoder function,
1446+ // as the SyntaxContextData may reference itself.
1447+ let new_ctxt = HygieneData :: with ( |hygiene_data| {
1448+ // Push a dummy SyntaxContextData to ensure that nobody else can get the
1449+ // same ID as us. This will be overwritten after call `decode_data`.
1450+ hygiene_data. syntax_context_data . push ( SyntaxContextData :: decode_placeholder ( ) ) ;
1451+ SyntaxContext :: from_usize ( hygiene_data. syntax_context_data . len ( ) - 1 )
1452+ } ) ;
1453+ entry. insert ( new_ctxt) ;
1454+ new_ctxt
1455+ }
1456+ }
1457+ } ;
1458+
14021459 // Don't try to decode data while holding the lock, since we need to
14031460 // be able to recursively decode a SyntaxContext
1404- let ctxt_key = decode_ctxt_key ( d, raw_id) ;
1461+ let ctxt_data = decode_data ( d, raw_id) ;
1462+ let ctxt_key = ctxt_data. key ( ) ;
14051463
14061464 let ctxt = HygieneData :: with ( |hygiene_data| {
14071465 match hygiene_data. syntax_context_map . get ( & ctxt_key) {
@@ -1415,10 +1473,29 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
14151473 Some ( & ctxt) => ctxt,
14161474 // This is a completely new context.
14171475 // Overwrite its placeholder data with our decoded data.
1418- None => hygiene_data. apply_mark_internal ( ctxt_key. 0 , ctxt_key. 1 , ctxt_key. 2 ) ,
1476+ None => {
1477+ let ctxt_data_ref =
1478+ & mut hygiene_data. syntax_context_data [ pending_ctxt. as_u32 ( ) as usize ] ;
1479+ let prev_ctxt_data = mem:: replace ( ctxt_data_ref, ctxt_data) ;
1480+ // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`.
1481+ // We don't care what the encoding crate set this to - we want to resolve it
1482+ // from the perspective of the current compilation session.
1483+ ctxt_data_ref. dollar_crate_name = kw:: DollarCrate ;
1484+ // Make sure nothing weird happened while `decode_data` was running.
1485+ if !prev_ctxt_data. is_decode_placeholder ( ) {
1486+ // Another thread may have already inserted the decoded data,
1487+ // but the decoded data should match.
1488+ assert_eq ! ( prev_ctxt_data, * ctxt_data_ref) ;
1489+ }
1490+ hygiene_data. syntax_context_map . insert ( ctxt_key, pending_ctxt) ;
1491+ pending_ctxt
1492+ }
14191493 }
14201494 } ) ;
14211495
1496+ // Mark the context as completed
1497+ context. local_in_progress . borrow_mut ( ) . remove ( & raw_id) ;
1498+
14221499 let mut inner = context. inner . lock ( ) ;
14231500 let new_len = raw_id as usize + 1 ;
14241501 if inner. remapped_ctxts . len ( ) < new_len {
@@ -1430,15 +1507,15 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
14301507 ctxt
14311508}
14321509
1433- fn for_all_ctxts_in < F : FnMut ( u32 , SyntaxContext , & SyntaxContextKey ) > (
1510+ fn for_all_ctxts_in < F : FnMut ( u32 , SyntaxContext , & SyntaxContextData ) > (
14341511 ctxts : impl Iterator < Item = SyntaxContext > ,
14351512 mut f : F ,
14361513) {
14371514 let all_data: Vec < _ > = HygieneData :: with ( |data| {
14381515 ctxts. map ( |ctxt| ( ctxt, data. syntax_context_data [ ctxt. 0 as usize ] . clone ( ) ) ) . collect ( )
14391516 } ) ;
14401517 for ( ctxt, data) in all_data. into_iter ( ) {
1441- f ( ctxt. 0 , ctxt, & data. key ( ) ) ;
1518+ f ( ctxt. 0 , ctxt, & data) ;
14421519 }
14431520}
14441521
0 commit comments