@@ -17,20 +17,76 @@ use rustc::mir::*;
1717use rustc:: mir:: interpret:: ConstValue ;
1818use rustc:: mir:: visit:: { PlaceContext , MutatingUseContext , MutVisitor , Visitor } ;
1919use rustc:: mir:: traversal:: ReversePostorder ;
20- use rustc:: ty:: { self , List , TyCtxt } ;
20+ use rustc:: ty:: { self , List , TyCtxt , TypeFoldable } ;
2121use rustc:: ty:: subst:: InternalSubsts ;
2222use rustc:: ty:: cast:: CastTy ;
2323use syntax:: ast:: LitKind ;
2424use syntax:: symbol:: sym;
2525use syntax_pos:: { Span , DUMMY_SP } ;
2626
2727use rustc_index:: vec:: { IndexVec , Idx } ;
28+ use rustc_index:: bit_set:: HybridBitSet ;
2829use rustc_target:: spec:: abi:: Abi ;
2930
31+ use std:: cell:: Cell ;
3032use std:: { iter, mem, usize} ;
3133
34+ use crate :: transform:: { MirPass , MirSource } ;
3235use crate :: transform:: check_consts:: { qualifs, Item , ConstKind , is_lang_panic_fn} ;
3336
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+
3490/// State of a temporary during collection and promotion.
3591#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
3692pub enum TempState {
@@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
538594 // is gone - we can always promote constants even if they
539595 // fail to pass const-checking, as compilation would've
540596 // 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) ;
542598 if bits == super :: qualify_consts:: QUALIF_ERROR_BIT {
543599 self . tcx . sess . delay_span_bug (
544600 constant. span ,
@@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
11541210 should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag) ;
11551211 should_promote && !feature_flag
11561212}
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