use std::borrow::Cow;
use std::cell::Cell;
-use rustc::hir::def::DefKind;
-use rustc::hir::def_id::DefId;
-use rustc::hir::HirId;
use rustc::mir::interpret::{InterpResult, PanicInfo, Scalar};
use rustc::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc::mir::{
read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant,
- Local, LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, ReadOnlyBodyAndCache, Rvalue,
+ Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue,
SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
UnOp, RETURN_PLACE,
};
use rustc::ty::layout::{
HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
};
-use rustc::ty::subst::InternalSubsts;
+use rustc::ty::subst::{InternalSubsts, Subst};
use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
use rustc_index::vec::IndexVec;
+use rustc_span::{Span, DUMMY_SP};
use syntax::ast::Mutability;
-use syntax_pos::{Span, DUMMY_SP};
use crate::const_eval::error_to_const_error;
use crate::interpret::{
LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
ScalarMaybeUndef, StackPopCleanup,
};
-use crate::rustc::ty::subst::Subst;
use crate::transform::{MirPass, MirSource};
/// The maximum number of bytes that we'll allocate space for a return value.
fn find_mir_or_eval_fn(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _span: Span,
_instance: ty::Instance<'tcx>,
_args: &[OpTy<'tcx>],
_ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
// the last known `SourceInfo` here and just keep revisiting it.
source_info: Option<SourceInfo>,
+ lint_root: Option<HirId>,
}
impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
local_decls: body.local_decls.clone(),
ret: ret.map(Into::into),
source_info: None,
+ lint_root: None,
}
}
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
{
self.ecx.tcx.span = source_info.span;
- // FIXME(eddyb) move this to the `Panic(_)` error case, so that
- // `f(self)` is always called, and that the only difference when the
- // scope's `local_data` is missing, is that the lint isn't emitted.
- let lint_root = self.lint_root(source_info)?;
let r = match f(self) {
Ok(val) => Some(val),
Err(error) => {
diagnostic.report_as_lint(
self.ecx.tcx,
"this expression will panic at runtime",
- lint_root,
+ self.lint_root?,
None,
);
}
r
}
- fn eval_constant(
- &mut self,
- c: &Constant<'tcx>,
- source_info: SourceInfo,
- ) -> Option<Const<'tcx>> {
+ fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
self.ecx.tcx.span = c.span;
+
+ // FIXME we need to revisit this for #67176
+ if c.needs_subst() {
+ return None;
+ }
+
match self.ecx.eval_const_to_op(c.literal, None) {
Ok(op) => Some(op),
Err(error) => {
let err = error_to_const_error(&self.ecx, error);
- match self.lint_root(source_info) {
+ match self.lint_root {
Some(lint_root) if c.literal.needs_subst() => {
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
match *op {
- Operand::Constant(ref c) => self.eval_constant(c, source_info),
+ Operand::Constant(ref c) => self.eval_constant(c),
Operand::Move(ref place) | Operand::Copy(ref place) => {
self.eval_place(place, source_info)
}
}
}
+ fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> {
+ self.use_ecx(source_info, |this| {
+ let ty = arg.ty(&this.local_decls, this.tcx);
+
+ if ty.is_integral() {
+ let arg = this.ecx.eval_operand(arg, None)?;
+ let prim = this.ecx.read_immediate(arg)?;
+ // Need to do overflow check here: For actual CTFE, MIR
+ // generation emits code that does this before calling the op.
+ if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
+ throw_panic!(OverflowNeg)
+ }
+ }
+
+ Ok(())
+ })?;
+
+ Some(())
+ }
+
+ fn check_binary_op(
+ &mut self,
+ op: BinOp,
+ left: &Operand<'tcx>,
+ right: &Operand<'tcx>,
+ source_info: SourceInfo,
+ place_layout: TyLayout<'tcx>,
+ overflow_check: bool,
+ ) -> Option<()> {
+ let r = self.use_ecx(source_info, |this| {
+ this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
+ })?;
+ if op == BinOp::Shr || op == BinOp::Shl {
+ let left_bits = place_layout.size.bits();
+ let right_size = r.layout.size;
+ let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
+ if r_bits.map_or(false, |b| b >= left_bits as u128) {
+ let lint_root = self.lint_root(source_info)?;
+ let dir = if op == BinOp::Shr { "right" } else { "left" };
+ self.tcx.lint_hir(
+ ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
+ lint_root,
+ source_info.span,
+ &format!("attempt to shift {} with overflow", dir),
+ );
+ return None;
+ }
+ }
+
+ // If overflow checking is enabled (like in debug mode by default),
+ // then we'll already catch overflow when we evaluate the `Assert` statement
+ // in MIR. However, if overflow checking is disabled, then there won't be any
+ // `Assert` statement and so we have to do additional checking here.
+ if !overflow_check {
+ self.use_ecx(source_info, |this| {
+ let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
+ let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
+
+ if overflow {
+ let err = err_panic!(Overflow(op)).into();
+ return Err(err);
+ }
+
+ Ok(())
+ })?;
+ }
+
+ Some(())
+ }
+
fn const_prop(
&mut self,
rvalue: &Rvalue<'tcx>,
source_info: SourceInfo,
place: &Place<'tcx>,
) -> Option<()> {
- let span = source_info.span;
-
// #66397: Don't try to eval into large places as that can cause an OOM
if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
return None;
}
+ // FIXME we need to revisit this for #67176
+ if rvalue.needs_subst() {
+ return None;
+ }
+
let overflow_check = self.tcx.sess.overflow_checks();
// Perform any special handling for specific Rvalue types.
// if an overflow would occur.
Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
-
- self.use_ecx(source_info, |this| {
- let ty = arg.ty(&this.local_decls, this.tcx);
-
- if ty.is_integral() {
- let arg = this.ecx.eval_operand(arg, None)?;
- let prim = this.ecx.read_immediate(arg)?;
- // Need to do overflow check here: For actual CTFE, MIR
- // generation emits code that does this before calling the op.
- if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
- throw_panic!(OverflowNeg)
- }
- }
-
- Ok(())
- })?;
+ self.check_unary_op(arg, source_info)?;
}
// Additional checking: check for overflows on integer binary operations and report
// them to the user as lints.
Rvalue::BinaryOp(op, left, right) => {
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
-
- let r = self.use_ecx(source_info, |this| {
- this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
- })?;
- if *op == BinOp::Shr || *op == BinOp::Shl {
- let left_bits = place_layout.size.bits();
- let right_size = r.layout.size;
- let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
- if r_bits.map_or(false, |b| b >= left_bits as u128) {
- let lint_root = self.lint_root(source_info)?;
- let dir = if *op == BinOp::Shr { "right" } else { "left" };
- self.tcx.lint_hir(
- ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
- lint_root,
- span,
- &format!("attempt to shift {} with overflow", dir),
- );
- return None;
- }
- }
-
- // If overflow checking is enabled (like in debug mode by default),
- // then we'll already catch overflow when we evaluate the `Assert` statement
- // in MIR. However, if overflow checking is disabled, then there won't be any
- // `Assert` statement and so we have to do additional checking here.
- if !overflow_check {
- self.use_ecx(source_info, |this| {
- let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
- let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
-
- if overflow {
- let err = err_panic!(Overflow(*op)).into();
- return Err(err);
- }
-
- Ok(())
- })?;
- }
+ self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?;
}
// Work around: avoid ICE in miri. FIXME(wesleywiser)
ScalarMaybeUndef::Scalar(r),
)) => l.is_bits() && r.is_bits(),
interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
- intern_const_alloc_recursive(&mut self.ecx, None, op.assert_mem_place())
+ let mplace = op.assert_mem_place(&self.ecx);
+ intern_const_alloc_recursive(&mut self.ecx, None, mplace, false)
.expect("failed to intern alloc");
true
}
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
trace!("visit_constant: {:?}", constant);
self.super_constant(constant, location);
- self.eval_constant(constant, self.source_info.unwrap());
+ self.eval_constant(constant);
}
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
trace!("visit_statement: {:?}", statement);
let source_info = statement.source_info;
self.source_info = Some(source_info);
+ self.lint_root = self.lint_root(source_info);
if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind {
let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
let source_info = terminator.source_info;
self.source_info = Some(source_info);
self.super_terminator(terminator, location);
+ self.lint_root = self.lint_root(source_info);
match &mut terminator.kind {
TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
if let Some(value) = self.eval_operand(&cond, source_info) {
// doesn't use the invalid value
match cond {
Operand::Move(ref place) | Operand::Copy(ref place) => {
- if let PlaceBase::Local(local) = place.base {
- self.remove_const(local);
- }
+ self.remove_const(place.local);
}
Operand::Constant(_) => {}
}