11//! Propagates constants for early reporting of statically known
22//! assertion failures
33
4+ use std:: cell:: Cell ;
5+
46use rustc:: hir:: def:: DefKind ;
57use rustc:: mir:: {
68 AggregateKind , Constant , Location , Place , PlaceBase , Body , Operand , Rvalue ,
@@ -21,7 +23,8 @@ use rustc::ty::layout::{
2123} ;
2224
2325use crate :: interpret:: {
24- self , InterpretCx , ScalarMaybeUndef , Immediate , OpTy , ImmTy , MemoryKind ,
26+ self , InterpretCx , ScalarMaybeUndef , Immediate , OpTy ,
27+ ImmTy , MemoryKind , StackPopCleanup , LocalValue , LocalState ,
2528} ;
2629use crate :: const_eval:: {
2730 CompileTimeInterpreter , error_to_const_error, eval_promoted, mk_eval_cx,
@@ -56,21 +59,54 @@ impl MirPass for ConstProp {
5659
5760 trace ! ( "ConstProp starting for {:?}" , source. def_id( ) ) ;
5861
62+ // Steal some data we need from `body`.
63+ let source_scope_local_data = std:: mem:: replace (
64+ & mut body. source_scope_local_data ,
65+ ClearCrossCrate :: Clear
66+ ) ;
67+ let promoted = std:: mem:: replace (
68+ & mut body. promoted ,
69+ IndexVec :: new ( )
70+ ) ;
71+
72+ let dummy_body =
73+ & Body :: new (
74+ body. basic_blocks ( ) . clone ( ) ,
75+ Default :: default ( ) ,
76+ ClearCrossCrate :: Clear ,
77+ Default :: default ( ) ,
78+ None ,
79+ body. local_decls . clone ( ) ,
80+ Default :: default ( ) ,
81+ body. arg_count ,
82+ Default :: default ( ) ,
83+ tcx. def_span ( source. def_id ( ) ) ,
84+ Default :: default ( ) ,
85+ ) ;
86+
5987 // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
6088 // constants, instead of just checking for const-folding succeeding.
6189 // That would require an uniform one-def no-mutation analysis
6290 // and RPO (or recursing when needing the value of a local).
63- let mut optimization_finder = ConstPropagator :: new ( body, tcx, source) ;
91+ let mut optimization_finder = ConstPropagator :: new (
92+ body,
93+ dummy_body,
94+ source_scope_local_data,
95+ promoted,
96+ tcx,
97+ source
98+ ) ;
6499 optimization_finder. visit_body ( body) ;
65100
66101 // put back the data we stole from `mir`
102+ let ( source_scope_local_data, promoted) = optimization_finder. release_stolen_data ( ) ;
67103 std:: mem:: replace (
68104 & mut body. source_scope_local_data ,
69- optimization_finder . source_scope_local_data
105+ source_scope_local_data
70106 ) ;
71107 std:: mem:: replace (
72108 & mut body. promoted ,
73- optimization_finder . promoted
109+ promoted
74110 ) ;
75111
76112 trace ! ( "ConstProp done for {:?}" , source. def_id( ) ) ;
@@ -84,7 +120,6 @@ struct ConstPropagator<'mir, 'tcx> {
84120 ecx : InterpretCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
85121 tcx : TyCtxt < ' tcx > ,
86122 source : MirSource < ' tcx > ,
87- places : IndexVec < Local , Option < Const < ' tcx > > > ,
88123 can_const_prop : IndexVec < Local , bool > ,
89124 param_env : ParamEnv < ' tcx > ,
90125 source_scope_local_data : ClearCrossCrate < IndexVec < SourceScope , SourceScopeLocalData > > ,
@@ -117,36 +152,87 @@ impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
117152
118153impl < ' mir , ' tcx > ConstPropagator < ' mir , ' tcx > {
119154 fn new (
120- body : & mut Body < ' tcx > ,
155+ body : & Body < ' tcx > ,
156+ dummy_body : & ' mir Body < ' tcx > ,
157+ source_scope_local_data : ClearCrossCrate < IndexVec < SourceScope , SourceScopeLocalData > > ,
158+ promoted : IndexVec < Promoted , Body < ' tcx > > ,
121159 tcx : TyCtxt < ' tcx > ,
122160 source : MirSource < ' tcx > ,
123161 ) -> ConstPropagator < ' mir , ' tcx > {
124- let param_env = tcx. param_env ( source. def_id ( ) ) ;
125- let ecx = mk_eval_cx ( tcx, tcx. def_span ( source. def_id ( ) ) , param_env) ;
162+ let def_id = source. def_id ( ) ;
163+ let param_env = tcx. param_env ( def_id) ;
164+ let span = tcx. def_span ( def_id) ;
165+ let mut ecx = mk_eval_cx ( tcx, span, param_env) ;
126166 let can_const_prop = CanConstProp :: check ( body) ;
127- let source_scope_local_data = std:: mem:: replace (
128- & mut body. source_scope_local_data ,
129- ClearCrossCrate :: Clear
130- ) ;
131- let promoted = std:: mem:: replace (
132- & mut body. promoted ,
133- IndexVec :: new ( )
134- ) ;
167+
168+ ecx. push_stack_frame (
169+ Instance :: new ( def_id, & InternalSubsts :: identity_for_item ( tcx, def_id) ) ,
170+ span,
171+ dummy_body,
172+ None ,
173+ StackPopCleanup :: None {
174+ cleanup : false ,
175+ } ,
176+ ) . expect ( "failed to push initial stack frame" ) ;
135177
136178 ConstPropagator {
137179 ecx,
138180 tcx,
139181 source,
140182 param_env,
141183 can_const_prop,
142- places : IndexVec :: from_elem ( None , & body. local_decls ) ,
143184 source_scope_local_data,
144185 //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
145186 local_decls : body. local_decls . clone ( ) ,
146187 promoted,
147188 }
148189 }
149190
191+ fn release_stolen_data (
192+ self ,
193+ ) -> (
194+ ClearCrossCrate < IndexVec < SourceScope , SourceScopeLocalData > > ,
195+ IndexVec < Promoted , Body < ' tcx > > ,
196+ ) {
197+ ( self . source_scope_local_data , self . promoted )
198+ }
199+
200+ fn get_const ( & self , local : Local ) -> Option < Const < ' tcx > > {
201+ let l = & self . ecx . frame ( ) . locals [ local] ;
202+
203+ // If the local is `Unitialized` or `Dead` then we haven't propagated a value into it.
204+ //
205+ // `InterpretCx::access_local()` mostly takes care of this for us however, for ZSTs,
206+ // it will synthesize a value for us. In doing so, that will cause the
207+ // `get_const(l).is_empty()` assert right before we call `set_const()` in `visit_statement`
208+ // to fail.
209+ if let LocalValue :: Uninitialized | LocalValue :: Dead = l. value {
210+ return None ;
211+ }
212+
213+ self . ecx . access_local ( self . ecx . frame ( ) , local, None ) . ok ( )
214+ }
215+
216+ fn set_const ( & mut self , local : Local , c : Const < ' tcx > ) {
217+ let frame = self . ecx . frame_mut ( ) ;
218+
219+ if let Some ( layout) = frame. locals [ local] . layout . get ( ) {
220+ debug_assert_eq ! ( c. layout, layout) ;
221+ }
222+
223+ frame. locals [ local] = LocalState {
224+ value : LocalValue :: Live ( * c) ,
225+ layout : Cell :: new ( Some ( c. layout ) ) ,
226+ } ;
227+ }
228+
229+ fn remove_const ( & mut self , local : Local ) {
230+ self . ecx . frame_mut ( ) . locals [ local] = LocalState {
231+ value : LocalValue :: Uninitialized ,
232+ layout : Cell :: new ( None ) ,
233+ } ;
234+ }
235+
150236 fn use_ecx < F , T > (
151237 & mut self ,
152238 source_info : SourceInfo ,
@@ -296,7 +382,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
296382 trace ! ( "eval_place(place={:?})" , place) ;
297383 place. iterate ( |place_base, place_projection| {
298384 let mut eval = match place_base {
299- PlaceBase :: Local ( loc) => self . places [ * loc] . clone ( ) ?,
385+ PlaceBase :: Local ( loc) => self . get_const ( * loc) . clone ( ) ?,
300386 PlaceBase :: Static ( box Static { kind : StaticKind :: Promoted ( promoted) , ..} ) => {
301387 let generics = self . tcx . generics_of ( self . source . def_id ( ) ) ;
302388 if generics. requires_monomorphization ( self . tcx ) {
@@ -699,8 +785,8 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
699785 trace ! ( "checking whether {:?} can be stored to {:?}" , value, local) ;
700786 if self . can_const_prop [ local] {
701787 trace ! ( "storing {:?} to {:?}" , value, local) ;
702- assert ! ( self . places [ local] . is_none( ) ) ;
703- self . places [ local] = Some ( value) ;
788+ assert ! ( self . get_const ( local) . is_none( ) ) ;
789+ self . set_const ( local, value) ;
704790
705791 if self . should_const_prop ( ) {
706792 self . replace_with_const (
@@ -740,7 +826,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
740826 place = & proj. base ;
741827 }
742828 if let Place :: Base ( PlaceBase :: Local ( local) ) = * place {
743- self . places [ local] = None ;
829+ self . remove_const ( local) ;
744830 }
745831 } ,
746832 Operand :: Constant ( _) => { }
0 commit comments