26
26
27
27
use std:: cell:: RefCell ;
28
28
use std:: collections:: hash_map:: Entry ;
29
- use std:: fmt;
30
29
use std:: hash:: Hash ;
30
+ use std:: { fmt, iter, mem} ;
31
31
32
32
use rustc_data_structures:: fingerprint:: Fingerprint ;
33
33
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -54,7 +54,11 @@ pub struct SyntaxContext(u32);
54
54
impl !Ord for SyntaxContext { }
55
55
impl !PartialOrd for SyntaxContext { }
56
56
57
- #[ derive( Debug , Encodable , Decodable , Clone ) ]
57
+ /// If this part of two syntax contexts is equal, then the whole syntax contexts should be equal.
58
+ /// The other fields are only for caching.
59
+ type SyntaxContextKey = ( SyntaxContext , ExpnId , Transparency ) ;
60
+
61
+ #[ derive( Clone , Copy , PartialEq , Debug , Encodable , Decodable ) ]
58
62
pub struct SyntaxContextData {
59
63
outer_expn : ExpnId ,
60
64
outer_transparency : Transparency ,
@@ -67,6 +71,31 @@ pub struct SyntaxContextData {
67
71
dollar_crate_name : Symbol ,
68
72
}
69
73
74
+ impl SyntaxContextData {
75
+ fn root ( ) -> SyntaxContextData {
76
+ SyntaxContextData {
77
+ outer_expn : ExpnId :: root ( ) ,
78
+ outer_transparency : Transparency :: Opaque ,
79
+ parent : SyntaxContext :: root ( ) ,
80
+ opaque : SyntaxContext :: root ( ) ,
81
+ opaque_and_semitransparent : SyntaxContext :: root ( ) ,
82
+ dollar_crate_name : kw:: DollarCrate ,
83
+ }
84
+ }
85
+
86
+ fn decode_placeholder ( ) -> SyntaxContextData {
87
+ SyntaxContextData { dollar_crate_name : kw:: Empty , ..SyntaxContextData :: root ( ) }
88
+ }
89
+
90
+ fn is_decode_placeholder ( & self ) -> bool {
91
+ self . dollar_crate_name == kw:: Empty
92
+ }
93
+
94
+ fn key ( & self ) -> SyntaxContextKey {
95
+ ( self . parent , self . outer_expn , self . outer_transparency )
96
+ }
97
+ }
98
+
70
99
rustc_index:: newtype_index! {
71
100
/// A unique ID associated with a macro invocation and expansion.
72
101
#[ orderable]
@@ -333,7 +362,7 @@ pub(crate) struct HygieneData {
333
362
foreign_expn_hashes : FxHashMap < ExpnId , ExpnHash > ,
334
363
expn_hash_to_expn_id : UnhashMap < ExpnHash , ExpnId > ,
335
364
syntax_context_data : Vec < SyntaxContextData > ,
336
- syntax_context_map : FxHashMap < ( SyntaxContext , ExpnId , Transparency ) , SyntaxContext > ,
365
+ syntax_context_map : FxHashMap < SyntaxContextKey , SyntaxContext > ,
337
366
/// Maps the `local_hash` of an `ExpnData` to the next disambiguator value.
338
367
/// This is used by `update_disambiguator` to keep track of which `ExpnData`s
339
368
/// would have collisions without a disambiguator.
@@ -352,22 +381,16 @@ impl HygieneData {
352
381
None ,
353
382
) ;
354
383
384
+ let root_ctxt_data = SyntaxContextData :: root ( ) ;
355
385
HygieneData {
356
386
local_expn_data : IndexVec :: from_elem_n ( Some ( root_data) , 1 ) ,
357
387
local_expn_hashes : IndexVec :: from_elem_n ( ExpnHash ( Fingerprint :: ZERO ) , 1 ) ,
358
388
foreign_expn_data : FxHashMap :: default ( ) ,
359
389
foreign_expn_hashes : FxHashMap :: default ( ) ,
360
- expn_hash_to_expn_id : std :: iter:: once ( ( ExpnHash ( Fingerprint :: ZERO ) , ExpnId :: root ( ) ) )
390
+ expn_hash_to_expn_id : iter:: once ( ( ExpnHash ( Fingerprint :: ZERO ) , ExpnId :: root ( ) ) )
361
391
. collect ( ) ,
362
- syntax_context_data : vec ! [ SyntaxContextData {
363
- outer_expn: ExpnId :: root( ) ,
364
- outer_transparency: Transparency :: Opaque ,
365
- parent: SyntaxContext ( 0 ) ,
366
- opaque: SyntaxContext ( 0 ) ,
367
- opaque_and_semitransparent: SyntaxContext ( 0 ) ,
368
- dollar_crate_name: kw:: DollarCrate ,
369
- } ] ,
370
- syntax_context_map : FxHashMap :: default ( ) ,
392
+ syntax_context_data : vec ! [ root_ctxt_data] ,
393
+ syntax_context_map : iter:: once ( ( root_ctxt_data. key ( ) , SyntaxContext ( 0 ) ) ) . collect ( ) ,
371
394
expn_data_disambiguators : UnhashMap :: default ( ) ,
372
395
}
373
396
}
@@ -416,23 +439,28 @@ impl HygieneData {
416
439
}
417
440
418
441
fn normalize_to_macros_2_0 ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
442
+ assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
419
443
self . syntax_context_data [ ctxt. 0 as usize ] . opaque
420
444
}
421
445
422
446
fn normalize_to_macro_rules ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
447
+ assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
423
448
self . syntax_context_data [ ctxt. 0 as usize ] . opaque_and_semitransparent
424
449
}
425
450
426
451
fn outer_expn ( & self , ctxt : SyntaxContext ) -> ExpnId {
452
+ assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
427
453
self . syntax_context_data [ ctxt. 0 as usize ] . outer_expn
428
454
}
429
455
430
456
fn outer_mark ( & self , ctxt : SyntaxContext ) -> ( ExpnId , Transparency ) {
457
+ assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
431
458
let data = & self . syntax_context_data [ ctxt. 0 as usize ] ;
432
459
( data. outer_expn , data. outer_transparency )
433
460
}
434
461
435
462
fn parent_ctxt ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
463
+ assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
436
464
self . syntax_context_data [ ctxt. 0 as usize ] . parent
437
465
}
438
466
@@ -542,6 +570,7 @@ impl HygieneData {
542
570
transparency : Transparency ,
543
571
) -> SyntaxContext {
544
572
let syntax_context_data = & mut self . syntax_context_data ;
573
+ assert ! ( !syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
545
574
let mut opaque = syntax_context_data[ ctxt. 0 as usize ] . opaque ;
546
575
let mut opaque_and_semitransparent =
547
576
syntax_context_data[ ctxt. 0 as usize ] . opaque_and_semitransparent ;
@@ -552,7 +581,7 @@ impl HygieneData {
552
581
. syntax_context_map
553
582
. entry ( ( parent, expn_id, transparency) )
554
583
. or_insert_with ( || {
555
- let new_opaque = SyntaxContext ( syntax_context_data. len ( ) as u32 ) ;
584
+ let new_opaque = SyntaxContext :: from_usize ( syntax_context_data. len ( ) ) ;
556
585
syntax_context_data. push ( SyntaxContextData {
557
586
outer_expn : expn_id,
558
587
outer_transparency : transparency,
@@ -572,7 +601,7 @@ impl HygieneData {
572
601
. entry ( ( parent, expn_id, transparency) )
573
602
. or_insert_with ( || {
574
603
let new_opaque_and_semitransparent =
575
- SyntaxContext ( syntax_context_data. len ( ) as u32 ) ;
604
+ SyntaxContext :: from_usize ( syntax_context_data. len ( ) ) ;
576
605
syntax_context_data. push ( SyntaxContextData {
577
606
outer_expn : expn_id,
578
607
outer_transparency : transparency,
@@ -587,8 +616,6 @@ impl HygieneData {
587
616
588
617
let parent = ctxt;
589
618
* self . syntax_context_map . entry ( ( parent, expn_id, transparency) ) . or_insert_with ( || {
590
- let new_opaque_and_semitransparent_and_transparent =
591
- SyntaxContext ( syntax_context_data. len ( ) as u32 ) ;
592
619
syntax_context_data. push ( SyntaxContextData {
593
620
outer_expn : expn_id,
594
621
outer_transparency : transparency,
@@ -597,7 +624,7 @@ impl HygieneData {
597
624
opaque_and_semitransparent,
598
625
dollar_crate_name : kw:: DollarCrate ,
599
626
} ) ;
600
- new_opaque_and_semitransparent_and_transparent
627
+ SyntaxContext :: from_usize ( syntax_context_data . len ( ) - 1 )
601
628
} )
602
629
}
603
630
}
@@ -704,6 +731,10 @@ impl SyntaxContext {
704
731
SyntaxContext ( raw as u32 )
705
732
}
706
733
734
+ fn from_usize ( raw : usize ) -> SyntaxContext {
735
+ SyntaxContext ( u32:: try_from ( raw) . unwrap ( ) )
736
+ }
737
+
707
738
/// Extend a syntax context with a given expansion and transparency.
708
739
pub fn apply_mark ( self , expn_id : ExpnId , transparency : Transparency ) -> SyntaxContext {
709
740
HygieneData :: with ( |data| data. apply_mark ( self , expn_id, transparency) )
@@ -884,7 +915,10 @@ impl SyntaxContext {
884
915
}
885
916
886
917
pub ( crate ) fn dollar_crate_name ( self ) -> Symbol {
887
- HygieneData :: with ( |data| data. syntax_context_data [ self . 0 as usize ] . dollar_crate_name )
918
+ HygieneData :: with ( |data| {
919
+ assert ! ( !data. syntax_context_data[ self . 0 as usize ] . is_decode_placeholder( ) ) ;
920
+ data. syntax_context_data [ self . 0 as usize ] . dollar_crate_name
921
+ } )
888
922
}
889
923
890
924
pub fn edition ( self ) -> Edition {
@@ -1224,7 +1258,7 @@ impl HygieneEncodeContext {
1224
1258
1225
1259
// Consume the current round of SyntaxContexts.
1226
1260
// Drop the lock() temporary early
1227
- let latest_ctxts = { std :: mem:: take ( & mut * self . latest_ctxts . lock ( ) ) } ;
1261
+ let latest_ctxts = { mem:: take ( & mut * self . latest_ctxts . lock ( ) ) } ;
1228
1262
1229
1263
// It's fine to iterate over a HashMap, because the serialization
1230
1264
// of the table that we insert data into doesn't depend on insertion
@@ -1236,7 +1270,7 @@ impl HygieneEncodeContext {
1236
1270
}
1237
1271
} ) ;
1238
1272
1239
- let latest_expns = { std :: mem:: take ( & mut * self . latest_expns . lock ( ) ) } ;
1273
+ let latest_expns = { mem:: take ( & mut * self . latest_expns . lock ( ) ) } ;
1240
1274
1241
1275
// Same as above, this is fine as we are inserting into a order-independent hashset
1242
1276
#[ allow( rustc:: potential_query_instability) ]
@@ -1270,6 +1304,7 @@ pub struct HygieneDecodeContext {
1270
1304
inner : Lock < HygieneDecodeContextInner > ,
1271
1305
1272
1306
/// A set of serialized `SyntaxContext` ids that are currently being decoded on each thread.
1307
+ /// Uses a hash map instead of hash set to use entry APIs.
1273
1308
local_in_progress : WorkerLocal < RefCell < FxHashMap < u32 , ( ) > > > ,
1274
1309
}
1275
1310
@@ -1354,28 +1389,33 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1354
1389
return SyntaxContext :: root ( ) ;
1355
1390
}
1356
1391
1357
- let ctxt = {
1392
+ let pending_ctxt = {
1358
1393
let mut inner = context. inner . lock ( ) ;
1359
1394
1395
+ // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
1396
+ // raw ids from different crate metadatas.
1360
1397
if let Some ( ctxt) = inner. remapped_ctxts . get ( raw_id as usize ) . copied ( ) . flatten ( ) {
1361
1398
// This has already been decoded.
1362
1399
return ctxt;
1363
1400
}
1364
1401
1365
1402
match inner. decoding . entry ( raw_id) {
1366
1403
Entry :: Occupied ( ctxt_entry) => {
1404
+ let pending_ctxt = * ctxt_entry. get ( ) ;
1367
1405
match context. local_in_progress . borrow_mut ( ) . entry ( raw_id) {
1368
- Entry :: Occupied ( ..) => {
1369
- // We're decoding this already on the current thread. Return here
1370
- // and let the function higher up the stack finish decoding to handle
1371
- // recursive cases.
1372
- return * ctxt_entry. get ( ) ;
1373
- }
1406
+ // We're decoding this already on the current thread. Return here and let the
1407
+ // function higher up the stack finish decoding to handle recursive cases.
1408
+ // Hopefully having a `SyntaxContext` that refers to an incorrect data is ok
1409
+ // during reminder of the decoding process, it's certainly not ok after the
1410
+ // top level decoding function returns.
1411
+ Entry :: Occupied ( ..) => return pending_ctxt,
1412
+ // Some other thread is current decoding this.
1413
+ // Race with it (alternatively we could wait here).
1414
+ // We cannot return this value, unlike in the recursive case above, because it
1415
+ // may expose a `SyntaxContext` pointing to incorrect data to arbitrary code.
1374
1416
Entry :: Vacant ( entry) => {
1375
1417
entry. insert ( ( ) ) ;
1376
-
1377
- // Some other thread is current decoding this. Race with it.
1378
- * ctxt_entry. get ( )
1418
+ pending_ctxt
1379
1419
}
1380
1420
}
1381
1421
}
@@ -1386,18 +1426,10 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1386
1426
// Allocate and store SyntaxContext id *before* calling the decoder function,
1387
1427
// as the SyntaxContextData may reference itself.
1388
1428
let new_ctxt = HygieneData :: with ( |hygiene_data| {
1389
- let new_ctxt = SyntaxContext ( hygiene_data. syntax_context_data . len ( ) as u32 ) ;
1390
1429
// Push a dummy SyntaxContextData to ensure that nobody else can get the
1391
- // same ID as us. This will be overwritten after call `decode_Data`
1392
- hygiene_data. syntax_context_data . push ( SyntaxContextData {
1393
- outer_expn : ExpnId :: root ( ) ,
1394
- outer_transparency : Transparency :: Transparent ,
1395
- parent : SyntaxContext :: root ( ) ,
1396
- opaque : SyntaxContext :: root ( ) ,
1397
- opaque_and_semitransparent : SyntaxContext :: root ( ) ,
1398
- dollar_crate_name : kw:: Empty ,
1399
- } ) ;
1400
- new_ctxt
1430
+ // same ID as us. This will be overwritten after call `decode_data`.
1431
+ hygiene_data. syntax_context_data . push ( SyntaxContextData :: decode_placeholder ( ) ) ;
1432
+ SyntaxContext :: from_usize ( hygiene_data. syntax_context_data . len ( ) - 1 )
1401
1433
} ) ;
1402
1434
entry. insert ( new_ctxt) ;
1403
1435
new_ctxt
@@ -1407,38 +1439,43 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1407
1439
1408
1440
// Don't try to decode data while holding the lock, since we need to
1409
1441
// be able to recursively decode a SyntaxContext
1410
- let mut ctxt_data = decode_data ( d, raw_id) ;
1411
- // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`
1412
- // We don't care what the encoding crate set this to - we want to resolve it
1413
- // from the perspective of the current compilation session
1414
- ctxt_data. dollar_crate_name = kw:: DollarCrate ;
1415
-
1416
- // Overwrite the dummy data with our decoded SyntaxContextData
1417
- HygieneData :: with ( |hygiene_data| {
1418
- if let Some ( old) = hygiene_data. syntax_context_data . get ( raw_id as usize )
1419
- && old. outer_expn == ctxt_data. outer_expn
1420
- && old. outer_transparency == ctxt_data. outer_transparency
1421
- && old. parent == ctxt_data. parent
1422
- {
1423
- ctxt_data = old. clone ( ) ;
1424
- }
1425
-
1426
- let dummy = std:: mem:: replace (
1427
- & mut hygiene_data. syntax_context_data [ ctxt. as_u32 ( ) as usize ] ,
1428
- ctxt_data,
1429
- ) ;
1430
- if cfg ! ( not( parallel_compiler) ) {
1431
- // Make sure nothing weird happened while `decode_data` was running.
1432
- // We used `kw::Empty` for the dummy value and we expect nothing to be
1433
- // modifying the dummy entry.
1434
- // This does not hold for the parallel compiler as another thread may
1435
- // have inserted the fully decoded data.
1436
- assert_eq ! ( dummy. dollar_crate_name, kw:: Empty ) ;
1442
+ let ctxt_data = decode_data ( d, raw_id) ;
1443
+ let ctxt_key = ctxt_data. key ( ) ;
1444
+
1445
+ let ctxt = HygieneData :: with ( |hygiene_data| {
1446
+ match hygiene_data. syntax_context_map . get ( & ctxt_key) {
1447
+ // Ensure that syntax contexts are unique.
1448
+ // If syntax contexts with the given key already exists, reuse it instead of
1449
+ // using `pending_ctxt`.
1450
+ // `pending_ctxt` will leave an unused hole in the vector of syntax contexts.
1451
+ // Hopefully its value isn't stored anywhere during decoding and its dummy data
1452
+ // is never accessed later. The `is_decode_placeholder` asserts on all
1453
+ // accesses to syntax context data attempt to ensure it.
1454
+ Some ( & ctxt) => ctxt,
1455
+ // This is a completely new context.
1456
+ // Overwrite its placeholder data with our decoded data.
1457
+ None => {
1458
+ let ctxt_data_ref =
1459
+ & mut hygiene_data. syntax_context_data [ pending_ctxt. as_u32 ( ) as usize ] ;
1460
+ let prev_ctxt_data = mem:: replace ( ctxt_data_ref, ctxt_data) ;
1461
+ // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`.
1462
+ // We don't care what the encoding crate set this to - we want to resolve it
1463
+ // from the perspective of the current compilation session
1464
+ ctxt_data_ref. dollar_crate_name = kw:: DollarCrate ;
1465
+ // Make sure nothing weird happened while `decode_data` was running.
1466
+ if !prev_ctxt_data. is_decode_placeholder ( ) {
1467
+ // With parallel compiler another thread may have already inserted the decoded
1468
+ // data, but the decoded data should match.
1469
+ assert ! ( cfg!( parallel_compiler) ) ;
1470
+ assert_eq ! ( prev_ctxt_data, * ctxt_data_ref) ;
1471
+ }
1472
+ hygiene_data. syntax_context_map . insert ( ctxt_key, pending_ctxt) ;
1473
+ pending_ctxt
1474
+ }
1437
1475
}
1438
1476
} ) ;
1439
1477
1440
1478
// Mark the context as completed
1441
-
1442
1479
context. local_in_progress . borrow_mut ( ) . remove ( & raw_id) ;
1443
1480
1444
1481
let mut inner = context. inner . lock ( ) ;
0 commit comments