use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind};
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
use rustc::mir::visit::{Visitor, PlaceContext};
-use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind};
+use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind, ScalarMaybeUndef};
use rustc::ty::{TyCtxt, self, Instance};
use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
use interpret::EvalContext;
}
}
-type Const<'tcx> = (Value, ty::Ty<'tcx>, Span);
+type Const<'tcx> = (Value, TyLayout<'tcx>, Span);
/// Finds optimization opportunities on the MIR.
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
};
let r = match f(self) {
Ok(val) => Some(val),
- Err(err) => {
- match err.kind {
+ Err(error) => {
+ let (stacktrace, span) = self.ecx.generate_stacktrace(None);
+ let diagnostic = ConstEvalErr { span, error, stacktrace };
+ use rustc::mir::interpret::EvalErrorKind::*;
+ match diagnostic.error.kind {
// don't report these, they make no sense in a const prop context
- EvalErrorKind::MachineError(_) => {},
- _ => {
- let (frames, span) = self.ecx.generate_stacktrace(None);
- let err = ConstEvalErr {
- span,
- error: err,
- stacktrace: frames,
- };
- err.report_as_lint(
+ | MachineError(_)
+ // at runtime these transformations might make sense
+ // FIXME: figure out the rules and start linting
+ | FunctionPointerTyMismatch(..)
+ // fine at runtime, might be a register address or sth
+ | ReadBytesAsPointer
+ // fine at runtime
+ | ReadForeignStatic
+ | Unimplemented(_)
+ // don't report const evaluator limits
+ | StackFrameLimitReached
+ | NoMirFor(..)
+ | InlineAsm
+ => {},
+
+ | InvalidMemoryAccess
+ | DanglingPointerDeref
+ | DoubleFree
+ | InvalidFunctionPointer
+ | InvalidBool
+ | InvalidDiscriminant
+ | PointerOutOfBounds { .. }
+ | InvalidNullPointerUsage
+ | MemoryLockViolation { .. }
+ | MemoryAcquireConflict { .. }
+ | ValidationFailure(..)
+ | InvalidMemoryLockRelease { .. }
+ | DeallocatedLockedMemory { .. }
+ | InvalidPointerMath
+ | ReadUndefBytes
+ | DeadLocal
+ | InvalidBoolOp(_)
+ | DerefFunctionPointer
+ | ExecuteMemory
+ | Intrinsic(..)
+ | InvalidChar(..)
+ | AbiViolation(_)
+ | AlignmentCheckFailed{..}
+ | CalledClosureAsFunction
+ | VtableForArgumentlessMethod
+ | ModifiedConstantMemory
+ | AssumptionNotHeld
+ // FIXME: should probably be removed and turned into a bug! call
+ | TypeNotPrimitive(_)
+ | ReallocatedWrongMemoryKind(_, _)
+ | DeallocatedWrongMemoryKind(_, _)
+ | ReallocateNonBasePtr
+ | DeallocateNonBasePtr
+ | IncorrectAllocationInformation(..)
+ | UnterminatedCString(_)
+ | HeapAllocZeroBytes
+ | HeapAllocNonPowerOfTwoAlignment(_)
+ | Unreachable
+ | ReadFromReturnPointer
+ | GeneratorResumedAfterReturn
+ | GeneratorResumedAfterPanic
+ | ReferencedConstant(_)
+ | InfiniteLoop
+ => {
+ // FIXME: report UB here
+ },
+
+ | OutOfTls
+ | TlsOutOfBounds
+ | PathNotFound(_)
+ => bug!("these should not be in rustc, but in miri's machine errors"),
+
+ | Layout(_)
+ | UnimplementedTraitSelection
+ | TypeckError
+ | TooGeneric
+ | CheckMatchError
+ // these are just noise
+ => {},
+
+ // non deterministic
+ | ReadPointerAsBytes
+ // FIXME: implement
+ => {},
+
+ | Panic
+ | BoundsCheck{..}
+ | Overflow(_)
+ | OverflowNeg
+ | DivisionByZero
+ | RemainderByZero
+ => {
+ diagnostic.report_as_lint(
self.ecx.tcx,
"this expression will panic at runtime",
lint_root,
) -> Option<Const<'tcx>> {
self.ecx.tcx.span = source_info.span;
match self.ecx.const_to_value(c.literal.val) {
- Ok(val) => Some((val, c.literal.ty, c.span)),
+ Ok(val) => {
+ let layout = self.tcx.layout_of(self.param_env.and(c.literal.ty)).ok()?;
+ Some((val, layout, c.span))
+ },
Err(error) => {
let (stacktrace, span) = self.ecx.generate_stacktrace(None);
let err = ConstEvalErr {
Place::Projection(ref proj) => match proj.elem {
ProjectionElem::Field(field, _) => {
trace!("field proj on {:?}", proj.base);
- let (base, ty, span) = self.eval_place(&proj.base, source_info)?;
+ let (base, layout, span) = self.eval_place(&proj.base, source_info)?;
let valty = self.use_ecx(source_info, |this| {
- this.ecx.read_field(base, None, field, ty)
+ this.ecx.read_field(base, None, field, layout)
})?;
- Some((valty.value, valty.ty, span))
+ Some((valty.0, valty.1, span))
},
_ => None,
},
fn const_prop(
&mut self,
rvalue: &Rvalue<'tcx>,
- place_ty: ty::Ty<'tcx>,
+ place_layout: TyLayout<'tcx>,
source_info: SourceInfo,
) -> Option<Const<'tcx>> {
let span = source_info.span;
match *rvalue {
// This branch exists for the sanity type check
Rvalue::Use(Operand::Constant(ref c)) => {
- assert_eq!(c.ty, place_ty);
+ assert_eq!(c.ty, place_layout.ty);
self.eval_constant(c, source_info)
},
Rvalue::Use(ref op) => {
Rvalue::Discriminant(..) => None,
Rvalue::Cast(kind, ref operand, _) => {
- let (value, ty, span) = self.eval_operand(operand, source_info)?;
+ let (value, layout, span) = self.eval_operand(operand, source_info)?;
self.use_ecx(source_info, |this| {
- let dest_ptr = this.ecx.alloc_ptr(place_ty)?;
- let place_align = this.ecx.layout_of(place_ty)?.align;
+ let dest_ptr = this.ecx.alloc_ptr(place_layout)?;
+ let place_align = place_layout.align;
let dest = ::interpret::Place::from_ptr(dest_ptr, place_align);
- this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?;
+ this.ecx.cast(ValTy { value, ty: layout.ty }, kind, place_layout.ty, dest)?;
Ok((
Value::ByRef(dest_ptr.into(), place_align),
- place_ty,
+ place_layout,
span,
))
})
// FIXME(oli-obk): evaluate static/constant slice lengths
Rvalue::Len(_) => None,
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
- let param_env = self.tcx.param_env(self.source.def_id);
- type_size_of(self.tcx, param_env, ty).map(|n| (
+ type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some((
Value::Scalar(Scalar::Bits {
bits: n as u128,
- defined: self.tcx.data_layout.pointer_size.bits() as u8,
- }),
- self.tcx.types.usize,
+ size: self.tcx.data_layout.pointer_size.bytes() as u8,
+ }.into()),
+ self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
span,
- ))
+ )))
}
Rvalue::UnaryOp(op, ref arg) => {
let def_id = if self.tcx.is_closure(self.source.def_id) {
let val = self.eval_operand(arg, source_info)?;
let prim = self.use_ecx(source_info, |this| {
- this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1 })
+ this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1.ty })
})?;
let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1))?;
- Some((Value::Scalar(val), place_ty, span))
+ Some((Value::Scalar(val.into()), place_layout, span))
}
Rvalue::CheckedBinaryOp(op, ref left, ref right) |
Rvalue::BinaryOp(op, ref left, ref right) => {
}
let r = self.use_ecx(source_info, |this| {
- this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1 })
+ this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1.ty })
})?;
if op == BinOp::Shr || op == BinOp::Shl {
let left_ty = left.ty(self.mir, self.tcx);
.unwrap()
.size
.bits();
- let right_size = self.tcx.layout_of(self.param_env.and(right.1)).unwrap().size;
+ let right_size = right.1.size;
if r.to_bits(right_size).ok().map_or(false, |b| b >= left_bits as u128) {
let source_scope_local_data = match self.mir.source_scope_local_data {
ClearCrossCrate::Set(ref data) => data,
}
let left = self.eval_operand(left, source_info)?;
let l = self.use_ecx(source_info, |this| {
- this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1 })
+ this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1.ty })
})?;
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
let (val, overflow) = self.use_ecx(source_info, |this| {
- this.ecx.binary_op(op, l, left.1, r, right.1)
+ this.ecx.binary_op(op, l, left.1.ty, r, right.1.ty)
})?;
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
Value::ScalarPair(
- val,
- Scalar::from_bool(overflow),
+ val.into(),
+ Scalar::from_bool(overflow).into(),
)
} else {
if overflow {
let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
return None;
}
- Value::Scalar(val)
+ Value::Scalar(val.into())
};
- Some((val, place_ty, span))
+ Some((val, place_layout, span))
},
}
}
) {
trace!("visit_statement: {:?}", statement);
if let StatementKind::Assign(ref place, ref rval) = statement.kind {
- let place_ty = place
+ let place_ty: ty::Ty<'tcx> = place
.ty(&self.mir.local_decls, self.tcx)
.to_ty(self.tcx);
- if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) {
- if let Place::Local(local) = *place {
- trace!("checking whether {:?} can be stored to {:?}", value, local);
- if self.can_const_prop[local] {
- trace!("storing {:?} to {:?}", value, local);
- assert!(self.places[local].is_none());
- self.places[local] = Some(value);
+ if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
+ if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) {
+ if let Place::Local(local) = *place {
+ trace!("checking whether {:?} can be stored to {:?}", value, local);
+ if self.can_const_prop[local] {
+ trace!("storing {:?} to {:?}", value, local);
+ assert!(self.places[local].is_none());
+ self.places[local] = Some(value);
+ }
}
}
}
if let TerminatorKind::Assert { expected, msg, cond, .. } = kind {
if let Some(value) = self.eval_operand(cond, source_info) {
trace!("assertion on {:?} should be {:?}", value, expected);
- if Value::Scalar(Scalar::from_bool(*expected)) != value.0 {
+ if Value::Scalar(Scalar::from_bool(*expected).into()) != value.0 {
// poison all places this operand references so that further code
// doesn't use the invalid value
match cond {
.eval_operand(len, source_info)
.expect("len must be const");
let len = match len.0 {
- Value::Scalar(Scalar::Bits { bits, ..}) => bits,
+ Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
+ bits, ..
+ })) => bits,
_ => bug!("const len not primitive: {:?}", len),
};
let index = self
.eval_operand(index, source_info)
.expect("index must be const");
let index = match index.0 {
- Value::Scalar(Scalar::Bits { bits, .. }) => bits,
+ Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
+ bits, ..
+ })) => bits,
_ => bug!("const index not primitive: {:?}", index),
};
format!(