Skip to content

Commit 0b333ef

Browse files
committed
Move Ref-from-arg checking from step.rs to const_prop.rs
1 parent 039fc48 commit 0b333ef

File tree

2 files changed

+95
-99
lines changed

2 files changed

+95
-99
lines changed

src/librustc_mir/interpret/step.rs

+2-19
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
//!
33
//! The main entry point is the `step` method.
44
5-
use rustc::mir::{self, Place, PlaceBase};
5+
use rustc::mir;
66
use rustc::ty::layout::LayoutOf;
77
use rustc::mir::interpret::{InterpResult, Scalar, PointerArithmetic};
88

9-
use super::{InterpCx, LocalValue, Machine};
9+
use super::{InterpCx, Machine};
1010

1111
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
1212
/// same type as the result.
@@ -240,23 +240,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
240240
}
241241

242242
Ref(_, _, ref place) => {
243-
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
244-
// from a function argument that hasn't been assigned to in this function. So just
245-
// report those as uninitialized for now.
246-
if let Place {
247-
base: PlaceBase::Local(local),
248-
projection: None
249-
} = place {
250-
let alive =
251-
if let LocalValue::Live(_) = self.frame().locals[*local].value {
252-
true
253-
} else { false };
254-
255-
if local.as_usize() <= self.frame().body.arg_count && !alive {
256-
trace!("skipping Ref({:?})", place);
257-
throw_unsup!(UninitializedLocal);
258-
}
259-
}
260243
let src = self.eval_place(place)?;
261244
let place = self.force_allocation(src)?;
262245
if place.layout.size.bytes() > 0 {

src/librustc_mir/transform/const_prop.rs

+93-80
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc::ty::layout::{
2424

2525
use crate::interpret::{
2626
self, InterpCx, ScalarMaybeUndef, Immediate, OpTy,
27-
ImmTy, StackPopCleanup, LocalValue, LocalState,
27+
StackPopCleanup, LocalValue, LocalState,
2828
};
2929
use 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

Comments
 (0)