@@ -17,20 +17,76 @@ use rustc::mir::*;
17
17
use rustc:: mir:: interpret:: ConstValue ;
18
18
use rustc:: mir:: visit:: { PlaceContext , MutatingUseContext , MutVisitor , Visitor } ;
19
19
use rustc:: mir:: traversal:: ReversePostorder ;
20
- use rustc:: ty:: { self , List , TyCtxt } ;
20
+ use rustc:: ty:: { self , List , TyCtxt , TypeFoldable } ;
21
21
use rustc:: ty:: subst:: InternalSubsts ;
22
22
use rustc:: ty:: cast:: CastTy ;
23
23
use syntax:: ast:: LitKind ;
24
24
use syntax:: symbol:: sym;
25
25
use syntax_pos:: { Span , DUMMY_SP } ;
26
26
27
27
use rustc_index:: vec:: { IndexVec , Idx } ;
28
+ use rustc_index:: bit_set:: HybridBitSet ;
28
29
use rustc_target:: spec:: abi:: Abi ;
29
30
31
+ use std:: cell:: Cell ;
30
32
use std:: { iter, mem, usize} ;
31
33
34
+ use crate :: transform:: { MirPass , MirSource } ;
32
35
use crate :: transform:: check_consts:: { qualifs, Item , ConstKind , is_lang_panic_fn} ;
33
36
37
+ /// A `MirPass` for promotion.
38
+ ///
39
+ /// In this case, "promotion" entails the following:
40
+ /// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
41
+ /// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
42
+ /// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
43
+ ///
44
+ /// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
45
+ /// newly created `StaticKind::Promoted`.
46
+ #[ derive( Default ) ]
47
+ pub struct PromoteTemps < ' tcx > {
48
+ pub promoted_fragments : Cell < IndexVec < Promoted , Body < ' tcx > > > ,
49
+ }
50
+
51
+ impl < ' tcx > MirPass < ' tcx > for PromoteTemps < ' tcx > {
52
+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
53
+ // There's not really any point in promoting errorful MIR.
54
+ //
55
+ // This does not include MIR that failed const-checking, which we still try to promote.
56
+ if body. return_ty ( ) . references_error ( ) {
57
+ tcx. sess . delay_span_bug ( body. span , "PromoteTemps: MIR had errors" ) ;
58
+ return ;
59
+ }
60
+
61
+ if src. promoted . is_some ( ) {
62
+ return ;
63
+ }
64
+
65
+ let def_id = src. def_id ( ) ;
66
+
67
+ let item = Item :: new ( tcx, def_id, body) ;
68
+ let mut rpo = traversal:: reverse_postorder ( body) ;
69
+ let ( temps, all_candidates) = collect_temps_and_candidates ( tcx, body, & mut rpo) ;
70
+
71
+ let promotable_candidates = validate_candidates ( tcx, body, def_id, & temps, & all_candidates) ;
72
+
73
+ // For now, lifetime extension is done in `const` and `static`s without creating promoted
74
+ // MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
75
+ // not work inside loops when they are allowed in `const`s.
76
+ //
77
+ // FIXME: use promoted MIR fragments everywhere?
78
+ let promoted_fragments = if should_create_promoted_mir_fragments ( item. const_kind ) {
79
+ promote_candidates ( def_id, body, tcx, temps, promotable_candidates)
80
+ } else {
81
+ // FIXME: promote const array initializers in consts.
82
+ remove_drop_and_storage_dead_on_promoted_locals ( tcx, body, & promotable_candidates) ;
83
+ IndexVec :: new ( )
84
+ } ;
85
+
86
+ self . promoted_fragments . set ( promoted_fragments) ;
87
+ }
88
+ }
89
+
34
90
/// State of a temporary during collection and promotion.
35
91
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
36
92
pub enum TempState {
@@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
538
594
// is gone - we can always promote constants even if they
539
595
// fail to pass const-checking, as compilation would've
540
596
// errored independently and promotion can't change that.
541
- let ( bits, _ ) = self . tcx . at ( constant. span ) . mir_const_qualif ( def_id) ;
597
+ let bits = self . tcx . at ( constant. span ) . mir_const_qualif ( def_id) ;
542
598
if bits == super :: qualify_consts:: QUALIF_ERROR_BIT {
543
599
self . tcx . sess . delay_span_bug (
544
600
constant. span ,
@@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
1154
1210
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag) ;
1155
1211
should_promote && !feature_flag
1156
1212
}
1213
+
1214
+ fn should_create_promoted_mir_fragments ( const_kind : Option < ConstKind > ) -> bool {
1215
+ match const_kind {
1216
+ Some ( ConstKind :: ConstFn ) | None => true ,
1217
+ Some ( ConstKind :: Const ) | Some ( ConstKind :: Static ) | Some ( ConstKind :: StaticMut ) => false ,
1218
+ }
1219
+ }
1220
+
1221
+ /// In `const` and `static` everything without `StorageDead`
1222
+ /// is `'static`, we don't have to create promoted MIR fragments,
1223
+ /// just remove `Drop` and `StorageDead` on "promoted" locals.
1224
+ fn remove_drop_and_storage_dead_on_promoted_locals (
1225
+ tcx : TyCtxt < ' tcx > ,
1226
+ body : & mut Body < ' tcx > ,
1227
+ promotable_candidates : & [ Candidate ] ,
1228
+ ) {
1229
+ debug ! ( "run_pass: promotable_candidates={:?}" , promotable_candidates) ;
1230
+
1231
+ // Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
1232
+ // simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
1233
+ //
1234
+ // FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
1235
+ if body. is_cfg_cyclic ( ) {
1236
+ tcx. sess . delay_span_bug ( body. span , "Control-flow cycle detected in `const`" ) ;
1237
+ return ;
1238
+ }
1239
+
1240
+ // The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
1241
+ let mut requires_lifetime_extension = HybridBitSet :: new_empty ( body. local_decls . len ( ) ) ;
1242
+
1243
+ promotable_candidates
1244
+ . iter ( )
1245
+ . filter_map ( |c| {
1246
+ match c {
1247
+ Candidate :: Ref ( loc) => Some ( loc) ,
1248
+ Candidate :: Repeat ( _) | Candidate :: Argument { .. } => None ,
1249
+ }
1250
+ } )
1251
+ . map ( |& Location { block, statement_index } | {
1252
+ // FIXME: store the `Local` for each `Candidate` when it is created.
1253
+ let place = match & body[ block] . statements [ statement_index] . kind {
1254
+ StatementKind :: Assign ( box ( _, Rvalue :: Ref ( _, _, place) ) ) => place,
1255
+ _ => bug ! ( "`Candidate::Ref` without corresponding assignment" ) ,
1256
+ } ;
1257
+
1258
+ match place. base {
1259
+ PlaceBase :: Local ( local) => local,
1260
+ PlaceBase :: Static ( _) => bug ! ( "`Candidate::Ref` for a non-local" ) ,
1261
+ }
1262
+ } )
1263
+ . for_each ( |local| {
1264
+ requires_lifetime_extension. insert ( local) ;
1265
+ } ) ;
1266
+
1267
+ // Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
1268
+ // lifetime extension.
1269
+ for block in body. basic_blocks_mut ( ) {
1270
+ block. statements . retain ( |statement| {
1271
+ match statement. kind {
1272
+ StatementKind :: StorageDead ( index) => !requires_lifetime_extension. contains ( index) ,
1273
+ _ => true
1274
+ }
1275
+ } ) ;
1276
+ let terminator = block. terminator_mut ( ) ;
1277
+ match & terminator. kind {
1278
+ TerminatorKind :: Drop {
1279
+ location,
1280
+ target,
1281
+ ..
1282
+ } => {
1283
+ if let Some ( index) = location. as_local ( ) {
1284
+ if requires_lifetime_extension. contains ( index) {
1285
+ terminator. kind = TerminatorKind :: Goto { target : * target } ;
1286
+ }
1287
+ }
1288
+ }
1289
+ _ => { }
1290
+ }
1291
+ }
1292
+ }
0 commit comments