@@ -24,7 +24,7 @@ use rustc::ty::layout::{
2424
2525use crate :: interpret:: {
2626 self , InterpCx , ScalarMaybeUndef , Immediate , OpTy ,
27- ImmTy , StackPopCleanup , LocalValue , LocalState ,
27+ StackPopCleanup , LocalValue , LocalState ,
2828} ;
2929use crate :: const_eval:: {
3030 CompileTimeInterpreter , error_to_const_error, mk_eval_cx,
@@ -311,102 +311,115 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
311311 place : & Place < ' tcx > ,
312312 ) -> Option < Const < ' tcx > > {
313313 let span = source_info. span ;
314- match * rvalue {
314+
315+ // if this isn't a supported operation, then return None
316+ match rvalue {
315317 Rvalue :: Repeat ( ..) |
316318 Rvalue :: Aggregate ( ..) |
317319 Rvalue :: NullaryOp ( NullOp :: Box , _) |
318- Rvalue :: Discriminant ( ..) => None ,
320+ Rvalue :: Discriminant ( ..) => return None ,
319321
320322 Rvalue :: Use ( _) |
321323 Rvalue :: Len ( _) |
322324 Rvalue :: Cast ( ..) |
323325 Rvalue :: NullaryOp ( ..) |
324326 Rvalue :: CheckedBinaryOp ( ..) |
325- Rvalue :: Ref ( ..) => {
326- self . use_ecx ( source_info, |this| {
327- this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
328- this. ecx . eval_place_to_op ( place, Some ( place_layout) )
329- } )
330- } ,
327+ Rvalue :: Ref ( ..) |
328+ Rvalue :: UnaryOp ( ..) |
329+ Rvalue :: BinaryOp ( ..) => { }
330+ }
331331
332- Rvalue :: UnaryOp ( op, ref arg) => {
333- let overflow_check = self . tcx . sess . overflow_checks ( ) ;
334-
335- self . use_ecx ( source_info, |this| {
336- // We check overflow in debug mode already
337- // so should only check in release mode.
338- if op == UnOp :: Neg && !overflow_check {
339- let ty = arg. ty ( & this. local_decls , this. tcx ) ;
340-
341- if ty. is_integral ( ) {
342- let arg = this. ecx . eval_operand ( arg, None ) ?;
343- let prim = this. ecx . read_immediate ( arg) ?;
344- // Need to do overflow check here: For actual CTFE, MIR
345- // generation emits code that does this before calling the op.
346- if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
347- throw_panic ! ( OverflowNeg )
348- }
332+ // perform any special checking for specific Rvalue types
333+ if let Rvalue :: UnaryOp ( op, arg) = rvalue {
334+ trace ! ( "checking UnaryOp(op = {:?}, arg = {:?})" , op, arg) ;
335+ let overflow_check = self . tcx . sess . overflow_checks ( ) ;
336+
337+ self . use_ecx ( source_info, |this| {
338+ // We check overflow in debug mode already
339+ // so should only check in release mode.
340+ if * op == UnOp :: Neg && !overflow_check {
341+ let ty = arg. ty ( & this. local_decls , this. tcx ) ;
342+
343+ if ty. is_integral ( ) {
344+ let arg = this. ecx . eval_operand ( arg, None ) ?;
345+ let prim = this. ecx . read_immediate ( arg) ?;
346+ // Need to do overflow check here: For actual CTFE, MIR
347+ // generation emits code that does this before calling the op.
348+ if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
349+ throw_panic ! ( OverflowNeg )
349350 }
350351 }
351-
352- this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
353- this. ecx . eval_place_to_op ( place, Some ( place_layout) )
354- } )
355- }
356- Rvalue :: BinaryOp ( op, ref left, ref right) => {
357- trace ! ( "rvalue binop {:?} for {:?} and {:?}" , op, left, right) ;
358-
359- let r = self . use_ecx ( source_info, |this| {
360- this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
361- } ) ?;
362- if op == BinOp :: Shr || op == BinOp :: Shl {
363- let left_bits = place_layout. size . bits ( ) ;
364- let right_size = r. layout . size ;
365- let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
366- if r_bits. ok ( ) . map_or ( false , |b| b >= left_bits as u128 ) {
367- let source_scope_local_data = match self . source_scope_local_data {
368- ClearCrossCrate :: Set ( ref data) => data,
369- ClearCrossCrate :: Clear => return None ,
370- } ;
371- let dir = if op == BinOp :: Shr {
372- "right"
373- } else {
374- "left"
375- } ;
376- let hir_id = source_scope_local_data[ source_info. scope ] . lint_root ;
377- self . tcx . lint_hir (
378- :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
379- hir_id,
380- span,
381- & format ! ( "attempt to shift {} with overflow" , dir) ) ;
382- return None ;
383- }
384352 }
385- trace ! ( "const evaluating {:?} for {:?} and {:?}" , op, left, right) ;
386- let val = self . use_ecx ( source_info, |this| {
387- let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
388- let ( val, overflow, _ty) = this. ecx . overflowing_binary_op ( op, l, r) ?;
389-
390- // We check overflow in debug mode already
391- // so should only check in release mode.
392- if !this. tcx . sess . overflow_checks ( ) && overflow {
393- let err = err_panic ! ( Overflow ( op) ) . into ( ) ;
394- return Err ( err) ;
395- }
396353
397- let val = ImmTy {
398- imm : Immediate :: Scalar ( val. into ( ) ) ,
399- layout : place_layout,
354+ Ok ( ( ) )
355+ } ) ?;
356+ } else if let Rvalue :: BinaryOp ( op, left, right) = rvalue {
357+ trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
358+
359+ let r = self . use_ecx ( source_info, |this| {
360+ this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
361+ } ) ?;
362+ if * op == BinOp :: Shr || * op == BinOp :: Shl {
363+ let left_bits = place_layout. size . bits ( ) ;
364+ let right_size = r. layout . size ;
365+ let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
366+ if r_bits. ok ( ) . map_or ( false , |b| b >= left_bits as u128 ) {
367+ let source_scope_local_data = match self . source_scope_local_data {
368+ ClearCrossCrate :: Set ( ref data) => data,
369+ ClearCrossCrate :: Clear => return None ,
400370 } ;
371+ let dir = if * op == BinOp :: Shr {
372+ "right"
373+ } else {
374+ "left"
375+ } ;
376+ let hir_id = source_scope_local_data[ source_info. scope ] . lint_root ;
377+ self . tcx . lint_hir (
378+ :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
379+ hir_id,
380+ span,
381+ & format ! ( "attempt to shift {} with overflow" , dir) ) ;
382+ return None ;
383+ }
384+ }
385+ self . use_ecx ( source_info, |this| {
386+ let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
387+ let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( * op, l, r) ?;
388+
389+ // We check overflow in debug mode already
390+ // so should only check in release mode.
391+ if !this. tcx . sess . overflow_checks ( ) && overflow {
392+ let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
393+ return Err ( err) ;
394+ }
401395
402- let dest = this. ecx . eval_place ( place) ?;
403- this. ecx . write_immediate ( * val, dest) ?;
404-
405- Ok ( val)
406- } ) ?;
407- Some ( val. into ( ) )
408- } ,
396+ Ok ( ( ) )
397+ } ) ?;
398+ } else if let Rvalue :: Ref ( _, _, place) = rvalue {
399+ trace ! ( "checking Ref({:?})" , place) ;
400+ // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
401+ // from a function argument that hasn't been assigned to in this function.
402+ if let Place {
403+ base : PlaceBase :: Local ( local) ,
404+ projection : box [ ]
405+ } = place {
406+ let alive =
407+ if let LocalValue :: Live ( _) = self . ecx . frame ( ) . locals [ * local] . value {
408+ true
409+ } else { false } ;
410+
411+ if local. as_usize ( ) <= self . ecx . frame ( ) . body . arg_count && !alive {
412+ trace ! ( "skipping Ref({:?})" , place) ;
413+ return None ;
414+ }
415+ }
409416 }
417+
418+ self . use_ecx ( source_info, |this| {
419+ trace ! ( "calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})" , rvalue, place) ;
420+ this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
421+ this. ecx . eval_place_to_op ( place, Some ( place_layout) )
422+ } )
410423 }
411424
412425 fn operand_from_scalar ( & self , scalar : Scalar , ty : Ty < ' tcx > , span : Span ) -> Operand < ' tcx > {
0 commit comments