@@ -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  { 
@@ -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