1
1
//! Propagates constants for early reporting of statically known
2
2
//! assertion failures
3
3
4
- use either:: { Left , Right } ;
4
+ use either:: Left ;
5
5
6
6
use rustc_const_eval:: interpret:: Immediate ;
7
7
use rustc_const_eval:: interpret:: {
@@ -129,9 +129,6 @@ struct ConstPropagator<'mir, 'tcx> {
129
129
ecx : InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > ,
130
130
tcx : TyCtxt < ' tcx > ,
131
131
param_env : ParamEnv < ' tcx > ,
132
- // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
133
- // the last known `SourceInfo` here and just keep revisiting it.
134
- source_info : Option < SourceInfo > ,
135
132
}
136
133
137
134
impl < ' tcx > LayoutOfHelpers < ' tcx > for ConstPropagator < ' _ , ' tcx > {
@@ -206,7 +203,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
206
203
)
207
204
. expect ( "failed to push initial stack frame" ) ;
208
205
209
- ConstPropagator { ecx, tcx, param_env, source_info : None }
206
+ ConstPropagator { ecx, tcx, param_env }
210
207
}
211
208
212
209
fn body ( & self ) -> & ' mir Body < ' tcx > {
@@ -252,12 +249,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
252
249
source_info. scope . lint_root ( & self . body ( ) . source_scopes )
253
250
}
254
251
255
- fn use_ecx < F , T > ( & mut self , source_info : SourceInfo , f : F ) -> Option < T >
252
+ fn use_ecx < F , T > ( & mut self , location : Location , f : F ) -> Option < T >
256
253
where
257
254
F : FnOnce ( & mut Self ) -> InterpResult < ' tcx , T > ,
258
255
{
259
256
// Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway.
260
- self . ecx . frame_mut ( ) . loc = Right ( source_info . span ) ;
257
+ self . ecx . frame_mut ( ) . loc = Left ( location ) ;
261
258
match f ( self ) {
262
259
Ok ( val) => Some ( val) ,
263
260
Err ( error) => {
@@ -276,7 +273,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
276
273
}
277
274
278
275
/// Returns the value, if any, of evaluating `c`.
279
- fn eval_constant ( & mut self , c : & Constant < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
276
+ fn eval_constant ( & mut self , c : & Constant < ' tcx > , location : Location ) -> Option < OpTy < ' tcx > > {
280
277
// FIXME we need to revisit this for #67176
281
278
if c. needs_subst ( ) {
282
279
return None ;
@@ -290,45 +287,41 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
290
287
// manually normalized.
291
288
let val = self . tcx . try_normalize_erasing_regions ( self . param_env , c. literal ) . ok ( ) ?;
292
289
293
- self . use_ecx ( source_info , |this| this. ecx . eval_mir_constant ( & val, Some ( c. span ) , None ) )
290
+ self . use_ecx ( location , |this| this. ecx . eval_mir_constant ( & val, Some ( c. span ) , None ) )
294
291
}
295
292
296
293
/// Returns the value, if any, of evaluating `place`.
297
- fn eval_place ( & mut self , place : Place < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
294
+ fn eval_place ( & mut self , place : Place < ' tcx > , location : Location ) -> Option < OpTy < ' tcx > > {
298
295
trace ! ( "eval_place(place={:?})" , place) ;
299
- self . use_ecx ( source_info , |this| this. ecx . eval_place_to_op ( place, None ) )
296
+ self . use_ecx ( location , |this| this. ecx . eval_place_to_op ( place, None ) )
300
297
}
301
298
302
299
/// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
303
300
/// or `eval_place`, depending on the variant of `Operand` used.
304
- fn eval_operand ( & mut self , op : & Operand < ' tcx > , source_info : SourceInfo ) -> Option < OpTy < ' tcx > > {
301
+ fn eval_operand ( & mut self , op : & Operand < ' tcx > , location : Location ) -> Option < OpTy < ' tcx > > {
305
302
match * op {
306
- Operand :: Constant ( ref c) => self . eval_constant ( c, source_info ) ,
307
- Operand :: Move ( place) | Operand :: Copy ( place) => self . eval_place ( place, source_info ) ,
303
+ Operand :: Constant ( ref c) => self . eval_constant ( c, location ) ,
304
+ Operand :: Move ( place) | Operand :: Copy ( place) => self . eval_place ( place, location ) ,
308
305
}
309
306
}
310
307
311
308
fn report_assert_as_lint (
312
309
& self ,
313
310
lint : & ' static lint:: Lint ,
314
- source_info : SourceInfo ,
311
+ location : Location ,
315
312
message : & ' static str ,
316
313
panic : AssertKind < impl std:: fmt:: Debug > ,
317
314
) {
318
- if let Some ( lint_root) = self . lint_root ( source_info) {
315
+ let source_info = self . body ( ) . source_info ( location) ;
316
+ if let Some ( lint_root) = self . lint_root ( * source_info) {
319
317
self . tcx . struct_span_lint_hir ( lint, lint_root, source_info. span , message, |lint| {
320
318
lint. span_label ( source_info. span , format ! ( "{:?}" , panic) )
321
319
} ) ;
322
320
}
323
321
}
324
322
325
- fn check_unary_op (
326
- & mut self ,
327
- op : UnOp ,
328
- arg : & Operand < ' tcx > ,
329
- source_info : SourceInfo ,
330
- ) -> Option < ( ) > {
331
- if let ( val, true ) = self . use_ecx ( source_info, |this| {
323
+ fn check_unary_op ( & mut self , op : UnOp , arg : & Operand < ' tcx > , location : Location ) -> Option < ( ) > {
324
+ if let ( val, true ) = self . use_ecx ( location, |this| {
332
325
let val = this. ecx . read_immediate ( & this. ecx . eval_operand ( arg, None ) ?) ?;
333
326
let ( _res, overflow, _ty) = this. ecx . overflowing_unary_op ( op, & val) ?;
334
327
Ok ( ( val, overflow) )
@@ -338,7 +331,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
338
331
assert_eq ! ( op, UnOp :: Neg , "Neg is the only UnOp that can overflow" ) ;
339
332
self . report_assert_as_lint (
340
333
lint:: builtin:: ARITHMETIC_OVERFLOW ,
341
- source_info ,
334
+ location ,
342
335
"this arithmetic operation will overflow" ,
343
336
AssertKind :: OverflowNeg ( val. to_const_int ( ) ) ,
344
337
) ;
@@ -353,14 +346,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
353
346
op : BinOp ,
354
347
left : & Operand < ' tcx > ,
355
348
right : & Operand < ' tcx > ,
356
- source_info : SourceInfo ,
349
+ location : Location ,
357
350
) -> Option < ( ) > {
358
- let r = self . use_ecx ( source_info , |this| {
351
+ let r = self . use_ecx ( location , |this| {
359
352
this. ecx . read_immediate ( & this. ecx . eval_operand ( right, None ) ?)
360
353
} ) ;
361
- let l = self . use_ecx ( source_info, |this| {
362
- this. ecx . read_immediate ( & this. ecx . eval_operand ( left, None ) ?)
363
- } ) ;
354
+ let l = self
355
+ . use_ecx ( location, |this| this. ecx . read_immediate ( & this. ecx . eval_operand ( left, None ) ?) ) ;
364
356
// Check for exceeding shifts *even if* we cannot evaluate the LHS.
365
357
if matches ! ( op, BinOp :: Shr | BinOp :: Shl ) {
366
358
let r = r. clone ( ) ?;
@@ -371,10 +363,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
371
363
let right_size = r. layout . size ;
372
364
let r_bits = r. to_scalar ( ) . to_bits ( right_size) . ok ( ) ;
373
365
if r_bits. map_or ( false , |b| b >= left_size. bits ( ) as u128 ) {
374
- debug ! ( "check_binary_op: reporting assert for {:?}" , source_info ) ;
366
+ debug ! ( "check_binary_op: reporting assert for {:?}" , location ) ;
375
367
self . report_assert_as_lint (
376
368
lint:: builtin:: ARITHMETIC_OVERFLOW ,
377
- source_info ,
369
+ location ,
378
370
"this arithmetic operation will overflow" ,
379
371
AssertKind :: Overflow (
380
372
op,
@@ -396,13 +388,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
396
388
397
389
if let ( Some ( l) , Some ( r) ) = ( l, r) {
398
390
// The remaining operators are handled through `overflowing_binary_op`.
399
- if self . use_ecx ( source_info , |this| {
391
+ if self . use_ecx ( location , |this| {
400
392
let ( _res, overflow, _ty) = this. ecx . overflowing_binary_op ( op, & l, & r) ?;
401
393
Ok ( overflow)
402
394
} ) ? {
403
395
self . report_assert_as_lint (
404
396
lint:: builtin:: ARITHMETIC_OVERFLOW ,
405
- source_info ,
397
+ location ,
406
398
"this arithmetic operation will overflow" ,
407
399
AssertKind :: Overflow ( op, l. to_const_int ( ) , r. to_const_int ( ) ) ,
408
400
) ;
@@ -412,7 +404,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
412
404
Some ( ( ) )
413
405
}
414
406
415
- fn check_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , source_info : SourceInfo ) -> Option < ( ) > {
407
+ fn check_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) -> Option < ( ) > {
416
408
// Perform any special handling for specific Rvalue types.
417
409
// Generally, checks here fall into one of two categories:
418
410
// 1. Additional checking to provide useful lints to the user
@@ -427,11 +419,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
427
419
// lint.
428
420
Rvalue :: UnaryOp ( op, arg) => {
429
421
trace ! ( "checking UnaryOp(op = {:?}, arg = {:?})" , op, arg) ;
430
- self . check_unary_op ( * op, arg, source_info ) ?;
422
+ self . check_unary_op ( * op, arg, location ) ?;
431
423
}
432
424
Rvalue :: BinaryOp ( op, box ( left, right) ) => {
433
425
trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
434
- self . check_binary_op ( * op, left, right, source_info ) ?;
426
+ self . check_binary_op ( * op, left, right, location ) ?;
435
427
}
436
428
Rvalue :: CheckedBinaryOp ( op, box ( left, right) ) => {
437
429
trace ! (
@@ -440,7 +432,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
440
432
left,
441
433
right
442
434
) ;
443
- self . check_binary_op ( * op, left, right, source_info ) ?;
435
+ self . check_binary_op ( * op, left, right, location ) ?;
444
436
}
445
437
446
438
// Do not try creating references (#67862)
@@ -516,22 +508,21 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
516
508
fn visit_constant ( & mut self , constant : & Constant < ' tcx > , location : Location ) {
517
509
trace ! ( "visit_constant: {:?}" , constant) ;
518
510
self . super_constant ( constant, location) ;
519
- self . eval_constant ( constant, self . source_info . unwrap ( ) ) ;
511
+ self . eval_constant ( constant, location ) ;
520
512
}
521
513
522
514
fn visit_assign ( & mut self , place : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , location : Location ) {
523
515
self . super_assign ( place, rvalue, location) ;
524
516
525
- let source_info = self . source_info . unwrap ( ) ;
526
- let Some ( ( ) ) = self . check_rvalue ( rvalue, source_info) else { return } ;
517
+ let Some ( ( ) ) = self . check_rvalue ( rvalue, location) else { return } ;
527
518
528
519
match self . ecx . machine . can_const_prop [ place. local ] {
529
520
// Do nothing if the place is indirect.
530
521
_ if place. is_indirect ( ) => { }
531
522
ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
532
523
ConstPropMode :: OnlyInsideOwnBlock | ConstPropMode :: FullConstProp => {
533
524
if self
534
- . use_ecx ( source_info , |this| this. ecx . eval_rvalue_into_place ( rvalue, * place) )
525
+ . use_ecx ( location , |this| this. ecx . eval_rvalue_into_place ( rvalue, * place) )
535
526
. is_none ( )
536
527
{
537
528
// Const prop failed, so erase the destination, ensuring that whatever happens
@@ -557,8 +548,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
557
548
558
549
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
559
550
trace ! ( "visit_statement: {:?}" , statement) ;
560
- let source_info = statement. source_info ;
561
- self . source_info = Some ( source_info) ;
562
551
563
552
// We want to evaluate operands before any change to the assigned-to value,
564
553
// so we recurse first.
@@ -571,8 +560,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
571
560
_ if place. is_indirect ( ) => { }
572
561
ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
573
562
ConstPropMode :: FullConstProp | ConstPropMode :: OnlyInsideOwnBlock => {
574
- if self . use_ecx ( source_info, |this| this. ecx . statement ( statement) ) . is_some ( )
575
- {
563
+ if self . use_ecx ( location, |this| this. ecx . statement ( statement) ) . is_some ( ) {
576
564
trace ! ( "propped discriminant into {:?}" , place) ;
577
565
} else {
578
566
Self :: remove_const ( & mut self . ecx , place. local ) ;
@@ -594,12 +582,10 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
594
582
}
595
583
596
584
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
597
- let source_info = terminator. source_info ;
598
- self . source_info = Some ( source_info) ;
599
585
self . super_terminator ( terminator, location) ;
600
586
match & terminator. kind {
601
587
TerminatorKind :: Assert { expected, ref msg, ref cond, .. } => {
602
- if let Some ( ref value) = self . eval_operand ( & cond, source_info ) {
588
+ if let Some ( ref value) = self . eval_operand ( & cond, location ) {
603
589
trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
604
590
let expected = Scalar :: from_bool ( * expected) ;
605
591
let Ok ( value_const) = self . ecx . read_scalar ( & value) else {
@@ -623,7 +609,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
623
609
let mut eval_to_int = |op| {
624
610
// This can be `None` if the lhs wasn't const propagated and we just
625
611
// triggered the assert on the value of the rhs.
626
- self . eval_operand ( op, source_info )
612
+ self . eval_operand ( op, location )
627
613
. and_then ( |op| self . ecx . read_immediate ( & op) . ok ( ) )
628
614
. map_or ( DbgVal :: Underscore , |op| DbgVal :: Val ( op. to_const_int ( ) ) )
629
615
} ;
@@ -664,7 +650,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
664
650
if let Some ( msg) = msg {
665
651
self . report_assert_as_lint (
666
652
lint:: builtin:: UNCONDITIONAL_PANIC ,
667
- source_info ,
653
+ location ,
668
654
"this operation will panic at runtime" ,
669
655
msg,
670
656
) ;
0 commit comments