12
12
//! assertion failures
13
13
14
14
15
-
15
+ use rustc :: hir :: def :: Def ;
16
16
use rustc:: mir:: { Constant , Literal , Location , Place , Mir , Operand , Rvalue , Local } ;
17
17
use rustc:: mir:: { NullOp , StatementKind , Statement , BasicBlock , LocalKind } ;
18
- use rustc:: mir:: { TerminatorKind , ClearCrossCrate , SourceInfo , BinOp } ;
18
+ use rustc:: mir:: { TerminatorKind , ClearCrossCrate , SourceInfo , BinOp , ProjectionElem } ;
19
19
use rustc:: mir:: visit:: { Visitor , PlaceContext } ;
20
20
use rustc:: middle:: const_val:: ConstVal ;
21
21
use rustc:: ty:: { TyCtxt , self , Instance } ;
@@ -26,6 +26,10 @@ use syntax::codemap::Span;
26
26
use rustc:: ty:: subst:: Substs ;
27
27
use rustc_data_structures:: indexed_vec:: IndexVec ;
28
28
use rustc:: ty:: ParamEnv ;
29
+ use rustc:: ty:: layout:: {
30
+ LayoutOf , TyLayout , LayoutError ,
31
+ HasTyCtxt , TargetDataLayout , HasDataLayout ,
32
+ } ;
29
33
30
34
pub struct ConstProp ;
31
35
@@ -34,6 +38,15 @@ impl MirPass for ConstProp {
34
38
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
35
39
source : MirSource ,
36
40
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
+ }
37
50
trace ! ( "ConstProp starting for {:?}" , source. def_id) ;
38
51
39
52
// FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
@@ -59,6 +72,28 @@ struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
59
72
param_env : ParamEnv < ' tcx > ,
60
73
}
61
74
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
+
62
97
impl < ' b , ' a , ' tcx : ' b > ConstPropagator < ' b , ' a , ' tcx > {
63
98
fn new (
64
99
mir : & ' b Mir < ' tcx > ,
@@ -134,15 +169,43 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
134
169
}
135
170
}
136
171
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
+
137
205
fn eval_operand ( & mut self , op : & Operand < ' tcx > ) -> Option < Const < ' tcx > > {
138
206
match * op {
139
207
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) ,
146
209
}
147
210
}
148
211
@@ -235,18 +298,24 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
235
298
let r = ecx. value_to_primval ( ValTy { value : right. 0 , ty : right. 1 } ) . ok ( ) ?;
236
299
if op == BinOp :: Shr || op == BinOp :: Shl {
237
300
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 ( ) ;
239
303
if r. to_bytes ( ) . ok ( ) . map_or ( false , |b| b >= bits as u128 ) {
240
304
let scope_info = match self . mir . visibility_scope_info {
241
305
ClearCrossCrate :: Set ( ref data) => data,
242
306
ClearCrossCrate :: Clear => return None ,
243
307
} ;
308
+ let dir = if op == BinOp :: Shr {
309
+ "right"
310
+ } else {
311
+ "left"
312
+ } ;
244
313
let node_id = scope_info[ source_info. scope ] . lint_root ;
245
314
self . tcx . lint_node (
246
315
:: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
247
316
node_id,
248
317
span,
249
- "bitshift exceeds the type's number of bits" ) ;
318
+ & format ! ( "attempt to shift {} with overflow" , dir ) ) ;
250
319
return None ;
251
320
}
252
321
}
@@ -334,6 +403,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
334
403
Copy | Move |
335
404
StorageDead | StorageLive |
336
405
Validate |
406
+ Projection ( _) |
337
407
Inspect => { } ,
338
408
_ => self . can_const_prop [ local] = false ,
339
409
}
@@ -364,6 +434,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
364
434
. to_ty ( self . tcx ) ;
365
435
if let Some ( value) = self . const_prop ( rval, place_ty, statement. source_info ) {
366
436
if let Place :: Local ( local) = * place {
437
+ trace ! ( "checking whether {:?} can be stored to {:?}" , value, local) ;
367
438
if self . can_const_prop [ local] {
368
439
trace ! ( "storing {:?} to {:?}" , value, local) ;
369
440
assert ! ( self . places[ local] . is_none( ) ) ;
@@ -384,7 +455,22 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
384
455
self . super_terminator_kind ( block, kind, location) ;
385
456
if let TerminatorKind :: Assert { expected, msg, cond, .. } = kind {
386
457
if let Some ( value) = self . eval_operand ( cond) {
458
+ trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
387
459
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
+ }
388
474
let span = self . mir [ block]
389
475
. terminator
390
476
. as_ref ( )
@@ -396,21 +482,12 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
396
482
. hir
397
483
. as_local_node_id ( self . source . def_id )
398
484
. 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
- ) ;
405
485
use rustc:: mir:: AssertMessage :: * ;
406
- match msg {
486
+ let msg = match msg {
407
487
// Need proper const propagator for these
408
488
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 ( ) ,
414
491
BoundsCheck { ref len, ref index } => {
415
492
let len = self . eval_operand ( len) . expect ( "len must be const" ) ;
416
493
let len = match len. 0 {
@@ -424,17 +501,20 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
424
501
Value :: ByVal ( PrimVal :: Bytes ( n) ) => n,
425
502
_ => bug ! ( "const index not primitive: {:?}" , index) ,
426
503
} ;
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,
435
509
)
436
510
} ,
437
- } . emit ( ) ;
511
+ } ;
512
+ self . tcx . lint_node (
513
+ :: rustc:: lint:: builtin:: CONST_ERR ,
514
+ node_id,
515
+ span,
516
+ & msg,
517
+ ) ;
438
518
}
439
519
}
440
520
}
0 commit comments