27
27
use std:: cell:: RefCell ;
28
28
use std:: collections:: hash_map:: Entry ;
29
29
use std:: collections:: hash_set:: Entry as SetEntry ;
30
- use std:: fmt;
31
30
use std:: hash:: Hash ;
32
31
use std:: sync:: Arc ;
32
+ use std:: { fmt, iter, mem} ;
33
33
34
34
use rustc_data_structures:: fingerprint:: Fingerprint ;
35
35
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -57,7 +57,11 @@ pub struct SyntaxContext(u32);
57
57
impl !Ord for SyntaxContext { }
58
58
impl !PartialOrd for SyntaxContext { }
59
59
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 ) ]
61
65
pub struct SyntaxContextData {
62
66
outer_expn : ExpnId ,
63
67
outer_transparency : Transparency ,
@@ -70,6 +74,27 @@ pub struct SyntaxContextData {
70
74
dollar_crate_name : Symbol ,
71
75
}
72
76
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
+
73
98
rustc_index:: newtype_index! {
74
99
/// A unique ID associated with a macro invocation and expansion.
75
100
#[ orderable]
@@ -342,7 +367,7 @@ pub(crate) struct HygieneData {
342
367
foreign_expn_hashes : FxHashMap < ExpnId , ExpnHash > ,
343
368
expn_hash_to_expn_id : UnhashMap < ExpnHash , ExpnId > ,
344
369
syntax_context_data : Vec < SyntaxContextData > ,
345
- syntax_context_map : FxHashMap < ( SyntaxContext , ExpnId , Transparency ) , SyntaxContext > ,
370
+ syntax_context_map : FxHashMap < SyntaxContextKey , SyntaxContext > ,
346
371
/// Maps the `local_hash` of an `ExpnData` to the next disambiguator value.
347
372
/// This is used by `update_disambiguator` to keep track of which `ExpnData`s
348
373
/// would have collisions without a disambiguator.
@@ -361,21 +386,15 @@ impl HygieneData {
361
386
None ,
362
387
) ;
363
388
389
+ let root_ctxt_data = SyntaxContextData :: root ( ) ;
364
390
HygieneData {
365
391
local_expn_data : IndexVec :: from_elem_n ( Some ( root_data) , 1 ) ,
366
392
local_expn_hashes : IndexVec :: from_elem_n ( ExpnHash ( Fingerprint :: ZERO ) , 1 ) ,
367
393
foreign_expn_data : FxHashMap :: default ( ) ,
368
394
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 ( ) ) )
370
396
. 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] ,
379
398
syntax_context_map : FxHashMap :: default ( ) ,
380
399
expn_data_disambiguators : UnhashMap :: default ( ) ,
381
400
}
@@ -425,23 +444,28 @@ impl HygieneData {
425
444
}
426
445
427
446
fn normalize_to_macros_2_0 ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
447
+ debug_assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
428
448
self . syntax_context_data [ ctxt. 0 as usize ] . opaque
429
449
}
430
450
431
451
fn normalize_to_macro_rules ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
452
+ debug_assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
432
453
self . syntax_context_data [ ctxt. 0 as usize ] . opaque_and_semitransparent
433
454
}
434
455
435
456
fn outer_expn ( & self , ctxt : SyntaxContext ) -> ExpnId {
457
+ debug_assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
436
458
self . syntax_context_data [ ctxt. 0 as usize ] . outer_expn
437
459
}
438
460
439
461
fn outer_mark ( & self , ctxt : SyntaxContext ) -> ( ExpnId , Transparency ) {
462
+ debug_assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
440
463
let data = & self . syntax_context_data [ ctxt. 0 as usize ] ;
441
464
( data. outer_expn , data. outer_transparency )
442
465
}
443
466
444
467
fn parent_ctxt ( & self , ctxt : SyntaxContext ) -> SyntaxContext {
468
+ debug_assert ! ( !self . syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
445
469
self . syntax_context_data [ ctxt. 0 as usize ] . parent
446
470
}
447
471
@@ -551,6 +575,7 @@ impl HygieneData {
551
575
transparency : Transparency ,
552
576
) -> SyntaxContext {
553
577
let syntax_context_data = & mut self . syntax_context_data ;
578
+ debug_assert ! ( !syntax_context_data[ ctxt. 0 as usize ] . is_decode_placeholder( ) ) ;
554
579
let mut opaque = syntax_context_data[ ctxt. 0 as usize ] . opaque ;
555
580
let mut opaque_and_semitransparent =
556
581
syntax_context_data[ ctxt. 0 as usize ] . opaque_and_semitransparent ;
@@ -561,7 +586,7 @@ impl HygieneData {
561
586
. syntax_context_map
562
587
. entry ( ( parent, expn_id, transparency) )
563
588
. 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 ( ) ) ;
565
590
syntax_context_data. push ( SyntaxContextData {
566
591
outer_expn : expn_id,
567
592
outer_transparency : transparency,
@@ -581,7 +606,7 @@ impl HygieneData {
581
606
. entry ( ( parent, expn_id, transparency) )
582
607
. or_insert_with ( || {
583
608
let new_opaque_and_semitransparent =
584
- SyntaxContext ( syntax_context_data. len ( ) as u32 ) ;
609
+ SyntaxContext :: from_usize ( syntax_context_data. len ( ) ) ;
585
610
syntax_context_data. push ( SyntaxContextData {
586
611
outer_expn : expn_id,
587
612
outer_transparency : transparency,
@@ -596,8 +621,6 @@ impl HygieneData {
596
621
597
622
let parent = ctxt;
598
623
* 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 ) ;
601
624
syntax_context_data. push ( SyntaxContextData {
602
625
outer_expn : expn_id,
603
626
outer_transparency : transparency,
@@ -606,7 +629,7 @@ impl HygieneData {
606
629
opaque_and_semitransparent,
607
630
dollar_crate_name : kw:: DollarCrate ,
608
631
} ) ;
609
- new_opaque_and_semitransparent_and_transparent
632
+ SyntaxContext :: from_usize ( syntax_context_data . len ( ) - 1 )
610
633
} )
611
634
}
612
635
}
@@ -713,6 +736,10 @@ impl SyntaxContext {
713
736
SyntaxContext ( raw as u32 )
714
737
}
715
738
739
+ fn from_usize ( raw : usize ) -> SyntaxContext {
740
+ SyntaxContext ( u32:: try_from ( raw) . unwrap ( ) )
741
+ }
742
+
716
743
/// Extend a syntax context with a given expansion and transparency.
717
744
pub fn apply_mark ( self , expn_id : ExpnId , transparency : Transparency ) -> SyntaxContext {
718
745
HygieneData :: with ( |data| data. apply_mark ( self , expn_id, transparency) )
@@ -893,7 +920,10 @@ impl SyntaxContext {
893
920
}
894
921
895
922
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
+ } )
897
927
}
898
928
899
929
pub fn edition ( self ) -> Edition {
@@ -1244,7 +1274,7 @@ impl HygieneEncodeContext {
1244
1274
1245
1275
// Consume the current round of SyntaxContexts.
1246
1276
// 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 ( ) ) } ;
1248
1278
1249
1279
// It's fine to iterate over a HashMap, because the serialization
1250
1280
// of the table that we insert data into doesn't depend on insertion
@@ -1256,7 +1286,7 @@ impl HygieneEncodeContext {
1256
1286
}
1257
1287
} ) ;
1258
1288
1259
- let latest_expns = { std :: mem:: take ( & mut * self . latest_expns . lock ( ) ) } ;
1289
+ let latest_expns = { mem:: take ( & mut * self . latest_expns . lock ( ) ) } ;
1260
1290
1261
1291
// Same as above, this is fine as we are inserting into a order-independent hashset
1262
1292
#[ allow( rustc:: potential_query_instability) ]
@@ -1373,28 +1403,33 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1373
1403
return SyntaxContext :: root ( ) ;
1374
1404
}
1375
1405
1376
- let ctxt = {
1406
+ let pending_ctxt = {
1377
1407
let mut inner = context. inner . lock ( ) ;
1378
1408
1409
+ // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
1410
+ // raw ids from different crate metadatas.
1379
1411
if let Some ( ctxt) = inner. remapped_ctxts . get ( raw_id as usize ) . copied ( ) . flatten ( ) {
1380
1412
// This has already been decoded.
1381
1413
return ctxt;
1382
1414
}
1383
1415
1384
1416
match inner. decoding . entry ( raw_id) {
1385
1417
Entry :: Occupied ( ctxt_entry) => {
1418
+ let pending_ctxt = * ctxt_entry. get ( ) ;
1386
1419
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.
1393
1430
SetEntry :: Vacant ( entry) => {
1394
1431
entry. insert ( ) ;
1395
-
1396
- // Some other thread is current decoding this. Race with it.
1397
- * ctxt_entry. get ( )
1432
+ pending_ctxt
1398
1433
}
1399
1434
}
1400
1435
}
@@ -1405,18 +1440,10 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1405
1440
// Allocate and store SyntaxContext id *before* calling the decoder function,
1406
1441
// as the SyntaxContextData may reference itself.
1407
1442
let new_ctxt = HygieneData :: with ( |hygiene_data| {
1408
- let new_ctxt = SyntaxContext ( hygiene_data. syntax_context_data . len ( ) as u32 ) ;
1409
1443
// 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 )
1420
1447
} ) ;
1421
1448
entry. insert ( new_ctxt) ;
1422
1449
new_ctxt
@@ -1426,27 +1453,38 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
1426
1453
1427
1454
// Don't try to decode data while holding the lock, since we need to
1428
1455
// 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) ;
1434
1457
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 )
1438
1460
&& old. outer_expn == ctxt_data. outer_expn
1439
1461
&& old. outer_transparency == ctxt_data. outer_transparency
1440
1462
&& old. parent == ctxt_data. parent
1441
1463
{
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;
1443
1477
}
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
1446
1485
} ) ;
1447
1486
1448
1487
// Mark the context as completed
1449
-
1450
1488
context. local_in_progress . borrow_mut ( ) . remove ( & raw_id) ;
1451
1489
1452
1490
let mut inner = context. inner . lock ( ) ;
0 commit comments