1212//! assertion failures
1313
1414
15-
15+ use rustc :: hir :: def :: Def ;
1616use rustc:: mir:: { Constant , Literal , Location , Place , Mir , Operand , Rvalue , Local } ;
1717use rustc:: mir:: { NullOp , StatementKind , Statement , BasicBlock , LocalKind } ;
18- use rustc:: mir:: { TerminatorKind , ClearCrossCrate , SourceInfo , BinOp } ;
18+ use rustc:: mir:: { TerminatorKind , ClearCrossCrate , SourceInfo , BinOp , ProjectionElem } ;
1919use rustc:: mir:: visit:: { Visitor , PlaceContext } ;
2020use rustc:: middle:: const_val:: ConstVal ;
2121use rustc:: ty:: { TyCtxt , self , Instance } ;
@@ -26,6 +26,10 @@ use syntax::codemap::Span;
2626use rustc:: ty:: subst:: Substs ;
2727use rustc_data_structures:: indexed_vec:: IndexVec ;
2828use rustc:: ty:: ParamEnv ;
29+ use rustc:: ty:: layout:: {
30+ LayoutOf , TyLayout , LayoutError ,
31+ HasTyCtxt , TargetDataLayout , HasDataLayout ,
32+ } ;
2933
3034pub struct ConstProp ;
3135
@@ -34,6 +38,15 @@ impl MirPass for ConstProp {
3438 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
3539 source : MirSource ,
3640 mir : & mut Mir < ' tcx > ) {
41+ // will be evaluated by miri and produce its errors there
42+ if source. promoted . is_some ( ) {
43+ return ;
44+ }
45+ match tcx. describe_def ( source. def_id ) {
46+ // skip statics because they'll be evaluated by miri anyway
47+ Some ( Def :: Static ( ..) ) => return ,
48+ _ => { } ,
49+ }
3750 trace ! ( "ConstProp starting for {:?}" , source. def_id) ;
3851
3952 // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
@@ -59,6 +72,28 @@ struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
5972 param_env : ParamEnv < ' tcx > ,
6073}
6174
75+ impl < ' a , ' b , ' tcx > LayoutOf < ty:: Ty < ' tcx > > for & ' a ConstPropagator < ' a , ' b , ' tcx > {
76+ type TyLayout = Result < TyLayout < ' tcx > , LayoutError < ' tcx > > ;
77+
78+ fn layout_of ( self , ty : ty:: Ty < ' tcx > ) -> Self :: TyLayout {
79+ self . tcx . layout_of ( self . param_env . and ( ty) )
80+ }
81+ }
82+
83+ impl < ' a , ' b , ' tcx > HasDataLayout for & ' a ConstPropagator < ' a , ' b , ' tcx > {
84+ #[ inline]
85+ fn data_layout ( & self ) -> & TargetDataLayout {
86+ & self . tcx . data_layout
87+ }
88+ }
89+
90+ impl < ' a , ' b , ' tcx > HasTyCtxt < ' tcx > for & ' a ConstPropagator < ' a , ' b , ' tcx > {
91+ #[ inline]
92+ fn tcx < ' c > ( & ' c self ) -> TyCtxt < ' c , ' tcx , ' tcx > {
93+ self . tcx
94+ }
95+ }
96+
6297impl < ' b , ' a , ' tcx : ' b > ConstPropagator < ' b , ' a , ' tcx > {
6398 fn new (
6499 mir : & ' b Mir < ' tcx > ,
@@ -134,15 +169,43 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
134169 }
135170 }
136171
172+ fn eval_place ( & mut self , place : & Place < ' tcx > ) -> Option < Const < ' tcx > > {
173+ match * place {
174+ Place :: Local ( loc) => self . places [ loc] . clone ( ) ,
175+ Place :: Projection ( ref proj) => match proj. elem {
176+ ProjectionElem :: Field ( field, _) => {
177+ trace ! ( "field proj on {:?}" , proj. base) ;
178+ let ( base, ty, span) = self . eval_place ( & proj. base ) ?;
179+ match base {
180+ Value :: ByValPair ( a, b) => {
181+ trace ! ( "by val pair: {:?}, {:?}" , a, b) ;
182+ let base_layout = self . tcx . layout_of ( self . param_env . and ( ty) ) . ok ( ) ?;
183+ trace ! ( "layout computed" ) ;
184+ use rustc_data_structures:: indexed_vec:: Idx ;
185+ let field_index = field. index ( ) ;
186+ let val = if field_index == 0 {
187+ a
188+ } else {
189+ assert_eq ! ( field_index, 1 ) ;
190+ b
191+ } ;
192+ let field = base_layout. field ( & * self , field_index) . ok ( ) ?;
193+ trace ! ( "projection resulted in: {:?}" , val) ;
194+ Some ( ( Value :: ByVal ( val) , field. ty , span) )
195+ } ,
196+ _ => None ,
197+ }
198+ } ,
199+ _ => None ,
200+ } ,
201+ _ => None ,
202+ }
203+ }
204+
137205 fn eval_operand ( & mut self , op : & Operand < ' tcx > ) -> Option < Const < ' tcx > > {
138206 match * op {
139207 Operand :: Constant ( ref c) => self . eval_constant ( c) ,
140- Operand :: Move ( ref place) | Operand :: Copy ( ref place) => match * place {
141- Place :: Local ( loc) => self . places [ loc] . clone ( ) ,
142- // FIXME(oli-obk): field and index projections
143- Place :: Projection ( _) => None ,
144- _ => None ,
145- } ,
208+ Operand :: Move ( ref place) | Operand :: Copy ( ref place) => self . eval_place ( place) ,
146209 }
147210 }
148211
@@ -235,18 +298,24 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
235298 let r = ecx. value_to_primval ( ValTy { value : right. 0 , ty : right. 1 } ) . ok ( ) ?;
236299 if op == BinOp :: Shr || op == BinOp :: Shl {
237300 let param_env = self . tcx . param_env ( self . source . def_id ) ;
238- let bits = self . tcx . layout_of ( param_env. and ( place_ty) ) . unwrap ( ) . size . bits ( ) ;
301+ let left_ty = left. ty ( self . mir , self . tcx ) ;
302+ let bits = self . tcx . layout_of ( param_env. and ( left_ty) ) . unwrap ( ) . size . bits ( ) ;
239303 if r. to_bytes ( ) . ok ( ) . map_or ( false , |b| b >= bits as u128 ) {
240304 let scope_info = match self . mir . visibility_scope_info {
241305 ClearCrossCrate :: Set ( ref data) => data,
242306 ClearCrossCrate :: Clear => return None ,
243307 } ;
308+ let dir = if op == BinOp :: Shr {
309+ "right"
310+ } else {
311+ "left"
312+ } ;
244313 let node_id = scope_info[ source_info. scope ] . lint_root ;
245314 self . tcx . lint_node (
246315 :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
247316 node_id,
248317 span,
249- "bitshift exceeds the type's number of bits" ) ;
318+ & format ! ( "attempt to shift {} with overflow" , dir ) ) ;
250319 return None ;
251320 }
252321 }
@@ -334,6 +403,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
334403 Copy | Move |
335404 StorageDead | StorageLive |
336405 Validate |
406+ Projection ( _) |
337407 Inspect => { } ,
338408 _ => self . can_const_prop [ local] = false ,
339409 }
@@ -364,6 +434,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
364434 . to_ty ( self . tcx ) ;
365435 if let Some ( value) = self . const_prop ( rval, place_ty, statement. source_info ) {
366436 if let Place :: Local ( local) = * place {
437+ trace ! ( "checking whether {:?} can be stored to {:?}" , value, local) ;
367438 if self . can_const_prop [ local] {
368439 trace ! ( "storing {:?} to {:?}" , value, local) ;
369440 assert ! ( self . places[ local] . is_none( ) ) ;
@@ -384,7 +455,22 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
384455 self . super_terminator_kind ( block, kind, location) ;
385456 if let TerminatorKind :: Assert { expected, msg, cond, .. } = kind {
386457 if let Some ( value) = self . eval_operand ( cond) {
458+ trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
387459 if Value :: ByVal ( PrimVal :: from_bool ( * expected) ) != value. 0 {
460+ // poison all places this operand references so that further code
461+ // doesn't use the invalid value
462+ match cond {
463+ Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
464+ let mut place = place;
465+ while let Place :: Projection ( ref proj) = * place {
466+ place = & proj. base ;
467+ }
468+ if let Place :: Local ( local) = * place {
469+ self . places [ local] = None ;
470+ }
471+ } ,
472+ Operand :: Constant ( _) => { }
473+ }
388474 let span = self . mir [ block]
389475 . terminator
390476 . as_ref ( )
@@ -396,21 +482,12 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
396482 . hir
397483 . as_local_node_id ( self . source . def_id )
398484 . expect ( "some part of a failing const eval must be local" ) ;
399- let mut lint = self . tcx . struct_span_lint_node (
400- :: rustc:: lint:: builtin:: CONST_ERR ,
401- node_id,
402- span,
403- "constant evaluation error" ,
404- ) ;
405485 use rustc:: mir:: AssertMessage :: * ;
406- match msg {
486+ let msg = match msg {
407487 // Need proper const propagator for these
408488 GeneratorResumedAfterReturn |
409- GeneratorResumedAfterPanic => {
410- lint. cancel ( ) ;
411- return ;
412- } ,
413- Math ( ref err) => lint. span_label ( span, err. description ( ) ) ,
489+ GeneratorResumedAfterPanic => return ,
490+ Math ( ref err) => err. description ( ) . to_owned ( ) ,
414491 BoundsCheck { ref len, ref index } => {
415492 let len = self . eval_operand ( len) . expect ( "len must be const" ) ;
416493 let len = match len. 0 {
@@ -424,17 +501,20 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
424501 Value :: ByVal ( PrimVal :: Bytes ( n) ) => n,
425502 _ => bug ! ( "const index not primitive: {:?}" , index) ,
426503 } ;
427- lint. span_label (
428- span,
429- format ! (
430- "index out of bounds: \
431- the len is {} but the index is {}",
432- len,
433- index,
434- ) ,
504+ format ! (
505+ "index out of bounds: \
506+ the len is {} but the index is {}",
507+ len,
508+ index,
435509 )
436510 } ,
437- } . emit ( ) ;
511+ } ;
512+ self . tcx . lint_node (
513+ :: rustc:: lint:: builtin:: CONST_ERR ,
514+ node_id,
515+ span,
516+ & msg,
517+ ) ;
438518 }
439519 }
440520 }
0 commit comments