@@ -469,15 +469,128 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
469469 }
470470 }
471471
472+ fn check_unary_op ( & mut self , arg : & Operand < ' tcx > , source_info : SourceInfo ) -> Option < ( ) > {
473+ self . use_ecx ( source_info, |this| {
474+ let ty = arg. ty ( & this. local_decls , this. tcx ) ;
475+
476+ if ty. is_integral ( ) {
477+ let arg = this. ecx . eval_operand ( arg, None ) ?;
478+ let prim = this. ecx . read_immediate ( arg) ?;
479+ // Need to do overflow check here: For actual CTFE, MIR
480+ // generation emits code that does this before calling the op.
481+ if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
482+ throw_panic ! ( OverflowNeg )
483+ }
484+ }
485+
486+ Ok ( ( ) )
487+ } ) ?;
488+
489+ Some ( ( ) )
490+ }
491+
492+ fn check_binary_op (
493+ & mut self ,
494+ op : BinOp ,
495+ left : & Operand < ' tcx > ,
496+ right : & Operand < ' tcx > ,
497+ source_info : SourceInfo ,
498+ place_layout : TyLayout < ' tcx > ,
499+ overflow_check : bool ,
500+ ) -> Option < ( ) > {
501+ let r = self . use_ecx ( source_info, |this| {
502+ this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
503+ } ) ?;
504+ if op == BinOp :: Shr || op == BinOp :: Shl {
505+ let left_bits = place_layout. size . bits ( ) ;
506+ let right_size = r. layout . size ;
507+ let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
508+ if r_bits. map_or ( false , |b| b >= left_bits as u128 ) {
509+ let lint_root = self . lint_root ( source_info) ?;
510+ let dir = if op == BinOp :: Shr { "right" } else { "left" } ;
511+ self . tcx . lint_hir (
512+ :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
513+ lint_root,
514+ source_info. span ,
515+ & format ! ( "attempt to shift {} with overflow" , dir) ,
516+ ) ;
517+ return None ;
518+ }
519+ }
520+
521+ // If overflow checking is enabled (like in debug mode by default),
522+ // then we'll already catch overflow when we evaluate the `Assert` statement
523+ // in MIR. However, if overflow checking is disabled, then there won't be any
524+ // `Assert` statement and so we have to do additional checking here.
525+ if !overflow_check {
526+ self . use_ecx ( source_info, |this| {
527+ let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
528+ let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( op, l, r) ?;
529+
530+ if overflow {
531+ let err = err_panic ! ( Overflow ( op) ) . into ( ) ;
532+ return Err ( err) ;
533+ }
534+
535+ Ok ( ( ) )
536+ } ) ?;
537+ }
538+
539+ Some ( ( ) )
540+ }
541+
542+ fn check_cast (
543+ & mut self ,
544+ op : & Operand < ' tcx > ,
545+ ty : Ty < ' tcx > ,
546+ source_info : SourceInfo ,
547+ place_layout : TyLayout < ' tcx > ,
548+ ) -> Option < ( ) > {
549+ if ty. is_integral ( ) && op. ty ( & self . local_decls , self . tcx ) . is_integral ( ) {
550+ let value = self . use_ecx ( source_info, |this| {
551+ this. ecx . read_immediate ( this. ecx . eval_operand ( op, None ) ?)
552+ } ) ?;
553+
554+ // Do not try to read bits for ZSTs
555+ if !value. layout . is_zst ( ) {
556+ let value_size = value. layout . size ;
557+ let value_bits = value. to_scalar ( ) . and_then ( |r| r. to_bits ( value_size) ) ;
558+ if let Ok ( value_bits) = value_bits {
559+ let truncated = truncate ( value_bits, place_layout. size ) ;
560+ if truncated != value_bits {
561+ let scope = source_info. scope ;
562+ let lint_root = match & self . source_scopes [ scope] . local_data {
563+ ClearCrossCrate :: Set ( data) => data. lint_root ,
564+ ClearCrossCrate :: Clear => return None ,
565+ } ;
566+ self . tcx . lint_hir (
567+ :: rustc:: lint:: builtin:: CONST_ERR ,
568+ lint_root,
569+ source_info. span ,
570+ & format ! (
571+ "truncating cast: the value {} requires {} bits but \
572+ the target type is only {} bits",
573+ value_bits,
574+ value_size. bits( ) ,
575+ place_layout. size. bits( )
576+ ) ,
577+ ) ;
578+ return None ;
579+ }
580+ }
581+ }
582+ }
583+
584+ Some ( ( ) )
585+ }
586+
472587 fn const_prop (
473588 & mut self ,
474589 rvalue : & Rvalue < ' tcx > ,
475590 place_layout : TyLayout < ' tcx > ,
476591 source_info : SourceInfo ,
477592 place : & Place < ' tcx > ,
478593 ) -> Option < ( ) > {
479- let span = source_info. span ;
480-
481594 // #66397: Don't try to eval into large places as that can cause an OOM
482595 if place_layout. size >= Size :: from_bytes ( MAX_ALLOC_LIMIT ) {
483596 return None ;
@@ -498,66 +611,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
498611 // if an overflow would occur.
499612 Rvalue :: UnaryOp ( UnOp :: Neg , arg) if !overflow_check => {
500613 trace ! ( "checking UnaryOp(op = Neg, arg = {:?})" , arg) ;
501-
502- self . use_ecx ( source_info, |this| {
503- let ty = arg. ty ( & this. local_decls , this. tcx ) ;
504-
505- if ty. is_integral ( ) {
506- let arg = this. ecx . eval_operand ( arg, None ) ?;
507- let prim = this. ecx . read_immediate ( arg) ?;
508- // Need to do overflow check here: For actual CTFE, MIR
509- // generation emits code that does this before calling the op.
510- if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
511- throw_panic ! ( OverflowNeg )
512- }
513- }
514-
515- Ok ( ( ) )
516- } ) ?;
614+ self . check_unary_op ( arg, source_info) ?;
517615 }
518616
519617 // Additional checking: check for overflows on integer binary operations and report
520618 // them to the user as lints.
521619 Rvalue :: BinaryOp ( op, left, right) => {
522620 trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
523-
524- let r = self . use_ecx ( source_info, |this| {
525- this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
526- } ) ?;
527- if * op == BinOp :: Shr || * op == BinOp :: Shl {
528- let left_bits = place_layout. size . bits ( ) ;
529- let right_size = r. layout . size ;
530- let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
531- if r_bits. map_or ( false , |b| b >= left_bits as u128 ) {
532- let lint_root = self . lint_root ( source_info) ?;
533- let dir = if * op == BinOp :: Shr { "right" } else { "left" } ;
534- self . tcx . lint_hir (
535- :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
536- lint_root,
537- span,
538- & format ! ( "attempt to shift {} with overflow" , dir) ,
539- ) ;
540- return None ;
541- }
542- }
543-
544- // If overflow checking is enabled (like in debug mode by default),
545- // then we'll already catch overflow when we evaluate the `Assert` statement
546- // in MIR. However, if overflow checking is disabled, then there won't be any
547- // `Assert` statement and so we have to do additional checking here.
548- if !overflow_check {
549- self . use_ecx ( source_info, |this| {
550- let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
551- let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( * op, l, r) ?;
552-
553- if overflow {
554- let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
555- return Err ( err) ;
556- }
557-
558- Ok ( ( ) )
559- } ) ?;
560- }
621+ self . check_binary_op ( * op, left, right, source_info, place_layout, overflow_check) ?;
561622 }
562623
563624 // Work around: avoid ICE in miri. FIXME(wesleywiser)
@@ -586,41 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
586647
587648 Rvalue :: Cast ( CastKind :: Misc , op, ty) => {
588649 trace ! ( "checking Cast(Misc, {:?}, {:?})" , op, ty) ;
589-
590- if ty. is_integral ( ) && op. ty ( & self . local_decls , self . tcx ) . is_integral ( ) {
591- let value = self . use_ecx ( source_info, |this| {
592- this. ecx . read_immediate ( this. ecx . eval_operand ( op, None ) ?)
593- } ) ?;
594-
595- // Do not try to read bits for ZSTs
596- if !value. layout . is_zst ( ) {
597- let value_size = value. layout . size ;
598- let value_bits = value. to_scalar ( ) . and_then ( |r| r. to_bits ( value_size) ) ;
599- if let Ok ( value_bits) = value_bits {
600- let truncated = truncate ( value_bits, place_layout. size ) ;
601- if truncated != value_bits {
602- let scope = source_info. scope ;
603- let lint_root = match & self . source_scopes [ scope] . local_data {
604- ClearCrossCrate :: Set ( data) => data. lint_root ,
605- ClearCrossCrate :: Clear => return None ,
606- } ;
607- self . tcx . lint_hir (
608- :: rustc:: lint:: builtin:: CONST_ERR ,
609- lint_root,
610- span,
611- & format ! (
612- "truncating cast: the value {} requires {} bits but \
613- the target type is only {} bits",
614- value_bits,
615- value_size. bits( ) ,
616- place_layout. size. bits( )
617- ) ,
618- ) ;
619- return None ;
620- }
621- }
622- }
623- }
650+ self . check_cast ( op, ty, source_info, place_layout) ?;
624651 }
625652
626653 _ => { }
0 commit comments