Auto merge of #64419 - wesleywiser:const_prop_use_ecx, r=oli-obk
Deduplicate some code between miri and const prop r? @oli-obk
This commit is contained in:
commit
488381ce9e
@ -389,6 +389,10 @@ pub enum UnsupportedOpInfo<'tcx> {
|
||||
/// Free-form case. Only for errors that are never caught!
|
||||
Unsupported(String),
|
||||
|
||||
/// FIXME(#64506) Error used to work around accessing projections of
|
||||
/// uninhabited types.
|
||||
UninhabitedValue,
|
||||
|
||||
// -- Everything below is not categorized yet --
|
||||
FunctionAbiMismatch(Abi, Abi),
|
||||
FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
|
||||
@ -552,6 +556,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
not a power of two"),
|
||||
Unsupported(ref msg) =>
|
||||
write!(f, "{}", msg),
|
||||
UninhabitedValue =>
|
||||
write!(f, "tried to use an uninhabited value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
use crate::interpret::{self,
|
||||
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
|
||||
RawConst, ConstValue,
|
||||
RawConst, ConstValue, Machine,
|
||||
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
|
||||
Allocation, AllocId, MemoryKind, Memory,
|
||||
snapshot, RefTracking, intern_const_alloc_recursive,
|
||||
@ -41,7 +41,7 @@
|
||||
/// that inform us about the generic bounds of the constant. E.g., using an associated constant
|
||||
/// of a function's generic parameter will require knowledge about the bounds on the generic
|
||||
/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
|
||||
pub(crate) fn mk_eval_cx<'mir, 'tcx>(
|
||||
fn mk_eval_cx<'mir, 'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
@ -169,7 +169,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ConstEvalError {
|
||||
pub enum ConstEvalError {
|
||||
NeedsRfc(String),
|
||||
}
|
||||
|
||||
@ -521,8 +521,8 @@ pub fn const_variant_index<'tcx>(
|
||||
/// Turn an interpreter error into something to report to the user.
|
||||
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
||||
/// Should be called only if the error is actually going to to be reported!
|
||||
pub fn error_to_const_error<'mir, 'tcx>(
|
||||
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||
pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
mut error: InterpErrorInfo<'tcx>,
|
||||
) -> ConstEvalErr<'tcx> {
|
||||
error.print_backtrace();
|
||||
|
@ -12,6 +12,7 @@
|
||||
use super::{
|
||||
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
|
||||
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
|
||||
Frame, Operand,
|
||||
};
|
||||
|
||||
/// Whether this kind of memory is allowed to leak
|
||||
@ -184,6 +185,22 @@ fn box_alloc(
|
||||
dest: PlaceTy<'tcx, Self::PointerTag>,
|
||||
) -> InterpResult<'tcx>;
|
||||
|
||||
/// Called to read the specified `local` from the `frame`.
|
||||
fn access_local(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
|
||||
local: mir::Local,
|
||||
) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
|
||||
frame.locals[local].access()
|
||||
}
|
||||
|
||||
/// Called before a `StaticKind::Static` value is accessed.
|
||||
fn before_access_static(
|
||||
_allocation: &Allocation,
|
||||
) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called to initialize the "extra" state of an allocation and make the pointers
|
||||
/// it contains (in relocations) tagged. The way we construct allocations is
|
||||
/// to always first construct it without extra and then add the extra.
|
||||
|
@ -462,6 +462,8 @@ fn get_static_alloc(
|
||||
// Make sure we use the ID of the resolved memory, not the lazy one!
|
||||
let id = raw_const.alloc_id;
|
||||
let allocation = tcx.alloc_map.lock().unwrap_memory(id);
|
||||
|
||||
M::before_access_static(allocation)?;
|
||||
Cow::Borrowed(allocation)
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ pub fn access_local(
|
||||
// Do not read from ZST, they might not be initialized
|
||||
Operand::Immediate(Scalar::zst().into())
|
||||
} else {
|
||||
frame.locals[local].access()?
|
||||
M::access_local(&self, frame, local)?
|
||||
};
|
||||
Ok(OpTy { op, layout })
|
||||
}
|
||||
@ -481,7 +481,7 @@ pub fn place_to_op(
|
||||
|
||||
// Evaluate a place with the goal of reading from it. This lets us sometimes
|
||||
// avoid allocations.
|
||||
pub(super) fn eval_place_to_op(
|
||||
pub fn eval_place_to_op(
|
||||
&self,
|
||||
place: &mir::Place<'tcx>,
|
||||
layout: Option<TyLayout<'tcx>>,
|
||||
|
@ -9,7 +9,7 @@
|
||||
use rustc::mir::interpret::truncate;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{
|
||||
self, Size, Align, LayoutOf, TyLayout, HasDataLayout, VariantIdx, PrimitiveExt
|
||||
self, Size, Abi, Align, LayoutOf, TyLayout, HasDataLayout, VariantIdx, PrimitiveExt
|
||||
};
|
||||
use rustc::ty::TypeFoldable;
|
||||
|
||||
@ -385,6 +385,10 @@ pub fn mplace_field(
|
||||
stride * field
|
||||
}
|
||||
layout::FieldPlacement::Union(count) => {
|
||||
// FIXME(#64506) `UninhabitedValue` can be removed when this issue is resolved
|
||||
if base.layout.abi == Abi::Uninhabited {
|
||||
throw_unsup!(UninhabitedValue);
|
||||
}
|
||||
assert!(field < count as u64,
|
||||
"Tried to access field {} of union with {} fields", field, count);
|
||||
// Offset is always 0
|
||||
|
@ -132,7 +132,7 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
|
||||
///
|
||||
/// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
|
||||
/// type writes its results directly into the memory specified by the place.
|
||||
fn eval_rvalue_into_place(
|
||||
pub fn eval_rvalue_into_place(
|
||||
&mut self,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
place: &mir::Place<'tcx>,
|
||||
|
@ -1,22 +1,26 @@
|
||||
//! Propagates constants for early reporting of statically known
|
||||
//! assertion failures
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
|
||||
use rustc::hir::def::DefKind;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::{
|
||||
AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue,
|
||||
Local, NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind,
|
||||
TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem,
|
||||
SourceScope, SourceScopeLocalData, LocalDecl,
|
||||
Local, NullOp, UnOp, StatementKind, Statement, LocalKind,
|
||||
TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp,
|
||||
SourceScope, SourceScopeLocalData, LocalDecl, BasicBlock,
|
||||
};
|
||||
use rustc::mir::visit::{
|
||||
Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
|
||||
};
|
||||
use rustc::mir::interpret::{Scalar, GlobalId, InterpResult, PanicInfo};
|
||||
use rustc::mir::interpret::{Scalar, InterpResult, PanicInfo};
|
||||
use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use syntax::ast::Mutability;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
use rustc::ty::subst::InternalSubsts;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc::ty::layout::{
|
||||
LayoutOf, TyLayout, LayoutError, HasTyCtxt, TargetDataLayout, HasDataLayout,
|
||||
@ -24,11 +28,11 @@
|
||||
|
||||
use crate::interpret::{
|
||||
self, InterpCx, ScalarMaybeUndef, Immediate, OpTy,
|
||||
ImmTy, MemoryKind, StackPopCleanup, LocalValue, LocalState,
|
||||
};
|
||||
use crate::const_eval::{
|
||||
CompileTimeInterpreter, error_to_const_error, mk_eval_cx,
|
||||
StackPopCleanup, LocalValue, LocalState, AllocId, Frame,
|
||||
Allocation, MemoryKind, ImmTy, Pointer, Memory, PlaceTy,
|
||||
Operand as InterpOperand,
|
||||
};
|
||||
use crate::const_eval::error_to_const_error;
|
||||
use crate::transform::{MirPass, MirSource};
|
||||
|
||||
pub struct ConstProp;
|
||||
@ -57,6 +61,14 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'
|
||||
return
|
||||
}
|
||||
|
||||
let is_generator = tcx.type_of(source.def_id()).is_generator();
|
||||
// FIXME(welseywiser) const prop doesn't work on generators because of query cycles
|
||||
// computing their layout.
|
||||
if is_generator {
|
||||
trace!("ConstProp skipped for generator {:?}", source.def_id());
|
||||
return
|
||||
}
|
||||
|
||||
trace!("ConstProp starting for {:?}", source.def_id());
|
||||
|
||||
// Steal some data we need from `body`.
|
||||
@ -103,11 +115,154 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstPropMachine;
|
||||
|
||||
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||
type MemoryKinds= !;
|
||||
type PointerTag = ();
|
||||
type ExtraFnVal = !;
|
||||
|
||||
type FrameExtra = ();
|
||||
type MemoryExtra = ();
|
||||
type AllocExtra = ();
|
||||
|
||||
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
|
||||
|
||||
const STATIC_KIND: Option<!> = None;
|
||||
|
||||
const CHECK_ALIGN: bool = false;
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn find_fn(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_instance: ty::Instance<'tcx>,
|
||||
_args: &[OpTy<'tcx>],
|
||||
_dest: Option<PlaceTy<'tcx>>,
|
||||
_ret: Option<BasicBlock>,
|
||||
) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn call_extra_fn(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
fn_val: !,
|
||||
_args: &[OpTy<'tcx>],
|
||||
_dest: Option<PlaceTy<'tcx>>,
|
||||
_ret: Option<BasicBlock>,
|
||||
) -> InterpResult<'tcx> {
|
||||
match fn_val {}
|
||||
}
|
||||
|
||||
fn call_intrinsic(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_instance: ty::Instance<'tcx>,
|
||||
_args: &[OpTy<'tcx>],
|
||||
_dest: PlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
|
||||
}
|
||||
|
||||
fn ptr_to_int(
|
||||
_mem: &Memory<'mir, 'tcx, Self>,
|
||||
_ptr: Pointer,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
throw_unsup_format!("ptr-to-int casts aren't supported in ConstProp");
|
||||
}
|
||||
|
||||
fn binary_ptr_op(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
_bin_op: BinOp,
|
||||
_left: ImmTy<'tcx>,
|
||||
_right: ImmTy<'tcx>,
|
||||
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
|
||||
// We can't do this because aliasing of memory can differ between const eval and llvm
|
||||
throw_unsup_format!("pointer arithmetic or comparisons aren't supported in ConstProp");
|
||||
}
|
||||
|
||||
fn find_foreign_static(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_def_id: DefId,
|
||||
) -> InterpResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
|
||||
throw_unsup!(ReadForeignStatic)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_allocation<'b>(
|
||||
_memory_extra: &(),
|
||||
_id: AllocId,
|
||||
alloc: Cow<'b, Allocation>,
|
||||
_kind: Option<MemoryKind<!>>,
|
||||
) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
|
||||
// We do not use a tag so we can just cheaply forward the allocation
|
||||
(alloc, ())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_static_base_pointer(
|
||||
_memory_extra: &(),
|
||||
_id: AllocId,
|
||||
) -> Self::PointerTag {
|
||||
()
|
||||
}
|
||||
|
||||
fn box_alloc(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_dest: PlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
throw_unsup_format!("can't const prop `box` keyword");
|
||||
}
|
||||
|
||||
fn access_local(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
|
||||
local: Local,
|
||||
) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> {
|
||||
let l = &frame.locals[local];
|
||||
|
||||
if l.value == LocalValue::Uninitialized {
|
||||
throw_unsup_format!("tried to access an uninitialized local");
|
||||
}
|
||||
|
||||
l.access()
|
||||
}
|
||||
|
||||
fn before_access_static(
|
||||
allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// if the static allocation is mutable or if it has relocations (it may be legal to mutate
|
||||
// the memory behind that in the future), then we can't const prop it
|
||||
if allocation.mutability == Mutability::Mutable || allocation.relocations().len() > 0 {
|
||||
throw_unsup_format!("can't eval mutable statics in ConstProp");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called immediately before a stack frame gets popped.
|
||||
#[inline(always)]
|
||||
fn stack_pop(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _extra: ()) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
type Const<'tcx> = OpTy<'tcx>;
|
||||
|
||||
/// Finds optimization opportunities on the MIR.
|
||||
struct ConstPropagator<'mir, 'tcx> {
|
||||
ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||
ecx: InterpCx<'mir, 'tcx, ConstPropMachine>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
source: MirSource<'tcx>,
|
||||
can_const_prop: IndexVec<Local, bool>,
|
||||
@ -150,7 +305,7 @@ fn new(
|
||||
let def_id = source.def_id();
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let span = tcx.def_span(def_id);
|
||||
let mut ecx = mk_eval_cx(tcx, span, param_env);
|
||||
let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
|
||||
let can_const_prop = CanConstProp::check(body);
|
||||
|
||||
ecx.push_stack_frame(
|
||||
@ -282,53 +437,9 @@ fn eval_constant(
|
||||
|
||||
fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
|
||||
trace!("eval_place(place={:?})", place);
|
||||
let mut eval = match place.base {
|
||||
PlaceBase::Local(loc) => self.get_const(loc).clone()?,
|
||||
PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted, _), ..}) => {
|
||||
let generics = self.tcx.generics_of(self.source.def_id());
|
||||
if generics.requires_monomorphization(self.tcx) {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
let substs = InternalSubsts::identity_for_item(self.tcx, self.source.def_id());
|
||||
let instance = Instance::new(self.source.def_id(), substs);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: Some(promoted),
|
||||
};
|
||||
let res = self.use_ecx(source_info, |this| {
|
||||
this.ecx.const_eval_raw(cid)
|
||||
})?;
|
||||
trace!("evaluated promoted {:?} to {:?}", promoted, res);
|
||||
res.into()
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
for (i, elem) in place.projection.iter().enumerate() {
|
||||
let proj_base = &place.projection[..i];
|
||||
|
||||
match elem {
|
||||
ProjectionElem::Field(field, _) => {
|
||||
trace!("field proj on {:?}", proj_base);
|
||||
eval = self.use_ecx(source_info, |this| {
|
||||
this.ecx.operand_field(eval, field.index() as u64)
|
||||
})?;
|
||||
},
|
||||
ProjectionElem::Deref => {
|
||||
trace!("processing deref");
|
||||
eval = self.use_ecx(source_info, |this| {
|
||||
this.ecx.deref_operand(eval)
|
||||
})?.into();
|
||||
}
|
||||
// We could get more projections by using e.g., `operand_projection`,
|
||||
// but we do not even have the stack frame set up properly so
|
||||
// an `Index` projection would throw us off-track.
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
Some(eval)
|
||||
self.use_ecx(source_info, |this| {
|
||||
this.ecx.eval_place_to_op(place, None)
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
|
||||
@ -344,166 +455,118 @@ fn const_prop(
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
place_layout: TyLayout<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
place: &Place<'tcx>,
|
||||
) -> Option<Const<'tcx>> {
|
||||
let span = source_info.span;
|
||||
match *rvalue {
|
||||
Rvalue::Use(ref op) => {
|
||||
self.eval_operand(op, source_info)
|
||||
},
|
||||
Rvalue::Ref(_, _, ref place) => {
|
||||
let src = self.eval_place(place, source_info)?;
|
||||
let mplace = src.try_as_mplace().ok()?;
|
||||
Some(ImmTy::from_scalar(mplace.ptr.into(), place_layout).into())
|
||||
},
|
||||
|
||||
// if this isn't a supported operation, then return None
|
||||
match rvalue {
|
||||
Rvalue::Repeat(..) |
|
||||
Rvalue::Aggregate(..) |
|
||||
Rvalue::NullaryOp(NullOp::Box, _) |
|
||||
Rvalue::Discriminant(..) => None,
|
||||
Rvalue::Discriminant(..) => return None,
|
||||
|
||||
Rvalue::Cast(kind, ref operand, _) => {
|
||||
let op = self.eval_operand(operand, source_info)?;
|
||||
self.use_ecx(source_info, |this| {
|
||||
let dest = this.ecx.allocate(place_layout, MemoryKind::Stack);
|
||||
this.ecx.cast(op, kind, dest.into())?;
|
||||
Ok(dest.into())
|
||||
})
|
||||
},
|
||||
Rvalue::Len(ref place) => {
|
||||
let place = self.eval_place(&place, source_info)?;
|
||||
let mplace = place.try_as_mplace().ok()?;
|
||||
|
||||
if let ty::Slice(_) = mplace.layout.ty.kind {
|
||||
let len = mplace.meta.unwrap().to_usize(&self.ecx).unwrap();
|
||||
|
||||
Some(ImmTy::from_uint(
|
||||
len,
|
||||
self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
|
||||
).into())
|
||||
} else {
|
||||
trace!("not slice: {:?}", mplace.layout.ty.kind);
|
||||
None
|
||||
}
|
||||
},
|
||||
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
|
||||
type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(
|
||||
ImmTy::from_uint(
|
||||
n,
|
||||
self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
|
||||
).into()
|
||||
))
|
||||
}
|
||||
Rvalue::UnaryOp(op, ref arg) => {
|
||||
let def_id = if self.tcx.is_closure(self.source.def_id()) {
|
||||
self.tcx.closure_base_def_id(self.source.def_id())
|
||||
} else {
|
||||
self.source.def_id()
|
||||
};
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
if generics.requires_monomorphization(self.tcx) {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
|
||||
let arg = self.eval_operand(arg, source_info)?;
|
||||
let oflo_check = self.tcx.sess.overflow_checks();
|
||||
let val = self.use_ecx(source_info, |this| {
|
||||
let prim = this.ecx.read_immediate(arg)?;
|
||||
match op {
|
||||
UnOp::Neg => {
|
||||
// We check overflow in debug mode already
|
||||
// so should only check in release mode.
|
||||
if !oflo_check
|
||||
&& prim.layout.ty.is_signed()
|
||||
&& prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
|
||||
throw_panic!(OverflowNeg)
|
||||
}
|
||||
}
|
||||
UnOp::Not => {
|
||||
// Cannot overflow
|
||||
}
|
||||
}
|
||||
// Now run the actual operation.
|
||||
this.ecx.unary_op(op, prim)
|
||||
})?;
|
||||
Some(val.into())
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, ref left, ref right) |
|
||||
Rvalue::BinaryOp(op, ref left, ref right) => {
|
||||
trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right);
|
||||
let right = self.eval_operand(right, source_info)?;
|
||||
let def_id = if self.tcx.is_closure(self.source.def_id()) {
|
||||
self.tcx.closure_base_def_id(self.source.def_id())
|
||||
} else {
|
||||
self.source.def_id()
|
||||
};
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
if generics.requires_monomorphization(self.tcx) {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
|
||||
let r = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(right)
|
||||
})?;
|
||||
if op == BinOp::Shr || op == BinOp::Shl {
|
||||
let left_ty = left.ty(&self.local_decls, self.tcx);
|
||||
let left_bits = self
|
||||
.tcx
|
||||
.layout_of(self.param_env.and(left_ty))
|
||||
.unwrap()
|
||||
.size
|
||||
.bits();
|
||||
let right_size = right.layout.size;
|
||||
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
|
||||
if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
|
||||
let source_scope_local_data = match self.source_scope_local_data {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return None,
|
||||
};
|
||||
let dir = if op == BinOp::Shr {
|
||||
"right"
|
||||
} else {
|
||||
"left"
|
||||
};
|
||||
let hir_id = source_scope_local_data[source_info.scope].lint_root;
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
hir_id,
|
||||
span,
|
||||
&format!("attempt to shift {} with overflow", dir));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let left = self.eval_operand(left, source_info)?;
|
||||
let l = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_immediate(left)
|
||||
})?;
|
||||
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
|
||||
let (val, overflow, _ty) = self.use_ecx(source_info, |this| {
|
||||
this.ecx.overflowing_binary_op(op, l, r)
|
||||
})?;
|
||||
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
|
||||
Immediate::ScalarPair(
|
||||
val.into(),
|
||||
Scalar::from_bool(overflow).into(),
|
||||
)
|
||||
} else {
|
||||
// We check overflow in debug mode already
|
||||
// so should only check in release mode.
|
||||
if !self.tcx.sess.overflow_checks() && overflow {
|
||||
let err = err_panic!(Overflow(op)).into();
|
||||
let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
|
||||
return None;
|
||||
}
|
||||
Immediate::Scalar(val.into())
|
||||
};
|
||||
let res = ImmTy {
|
||||
imm: val,
|
||||
layout: place_layout,
|
||||
};
|
||||
Some(res.into())
|
||||
},
|
||||
Rvalue::Use(_) |
|
||||
Rvalue::Len(_) |
|
||||
Rvalue::Cast(..) |
|
||||
Rvalue::NullaryOp(..) |
|
||||
Rvalue::CheckedBinaryOp(..) |
|
||||
Rvalue::Ref(..) |
|
||||
Rvalue::UnaryOp(..) |
|
||||
Rvalue::BinaryOp(..) => { }
|
||||
}
|
||||
|
||||
// perform any special checking for specific Rvalue types
|
||||
if let Rvalue::UnaryOp(op, arg) = rvalue {
|
||||
trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
|
||||
let overflow_check = self.tcx.sess.overflow_checks();
|
||||
|
||||
self.use_ecx(source_info, |this| {
|
||||
// We check overflow in debug mode already
|
||||
// so should only check in release mode.
|
||||
if *op == UnOp::Neg && !overflow_check {
|
||||
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(())
|
||||
})?;
|
||||
} else if let Rvalue::BinaryOp(op, left, right) = rvalue {
|
||||
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.ok().map_or(false, |b| b >= left_bits as u128) {
|
||||
let source_scope_local_data = match self.source_scope_local_data {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return None,
|
||||
};
|
||||
let dir = if *op == BinOp::Shr {
|
||||
"right"
|
||||
} else {
|
||||
"left"
|
||||
};
|
||||
let hir_id = source_scope_local_data[source_info.scope].lint_root;
|
||||
self.tcx.lint_hir(
|
||||
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
|
||||
hir_id,
|
||||
span,
|
||||
&format!("attempt to shift {} with overflow", dir));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
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)?;
|
||||
|
||||
// We check overflow in debug mode already
|
||||
// so should only check in release mode.
|
||||
if !this.tcx.sess.overflow_checks() && overflow {
|
||||
let err = err_panic!(Overflow(*op)).into();
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
} else if let Rvalue::Ref(_, _, place) = rvalue {
|
||||
trace!("checking Ref({:?})", place);
|
||||
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
|
||||
// from a function argument that hasn't been assigned to in this function.
|
||||
if let Place {
|
||||
base: PlaceBase::Local(local),
|
||||
projection: box []
|
||||
} = place {
|
||||
let alive =
|
||||
if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value {
|
||||
true
|
||||
} else { false };
|
||||
|
||||
if local.as_usize() <= self.ecx.frame().body.arg_count && !alive {
|
||||
trace!("skipping Ref({:?})", place);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.use_ecx(source_info, |this| {
|
||||
trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
|
||||
this.ecx.eval_rvalue_into_place(rvalue, place)?;
|
||||
this.ecx.eval_place_to_op(place, Some(place_layout))
|
||||
})
|
||||
}
|
||||
|
||||
fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
|
||||
@ -577,14 +640,6 @@ fn should_const_prop(&self) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn type_size_of<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<u64> {
|
||||
tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes())
|
||||
}
|
||||
|
||||
struct CanConstProp {
|
||||
can_const_prop: IndexVec<Local, bool>,
|
||||
// false at the beginning, once set, there are not allowed to be any more assignments
|
||||
@ -670,15 +725,19 @@ fn visit_statement(
|
||||
.ty(&self.local_decls, self.tcx)
|
||||
.ty;
|
||||
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 {
|
||||
base: PlaceBase::Local(local),
|
||||
projection: box [],
|
||||
} = *place {
|
||||
if let Place {
|
||||
base: PlaceBase::Local(local),
|
||||
projection: box [],
|
||||
} = *place {
|
||||
if let Some(value) = self.const_prop(rval,
|
||||
place_layout,
|
||||
statement.source_info,
|
||||
place) {
|
||||
trace!("checking whether {:?} can be stored to {:?}", value, local);
|
||||
if self.can_const_prop[local] {
|
||||
trace!("storing {:?} to {:?}", value, local);
|
||||
assert!(self.get_const(local).is_none());
|
||||
assert!(self.get_const(local).is_none() ||
|
||||
self.get_const(local) == Some(value));
|
||||
self.set_const(local, value);
|
||||
|
||||
if self.should_const_prop() {
|
||||
@ -692,7 +751,22 @@ fn visit_statement(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match statement.kind {
|
||||
StatementKind::StorageLive(local) |
|
||||
StatementKind::StorageDead(local) if self.can_const_prop[local] => {
|
||||
let frame = self.ecx.frame_mut();
|
||||
frame.locals[local].value =
|
||||
if let StatementKind::StorageLive(_) = statement.kind {
|
||||
LocalValue::Uninitialized
|
||||
} else {
|
||||
LocalValue::Dead
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
|
29
src/test/mir-opt/const_prop/read_immutable_static.rs
Normal file
29
src/test/mir-opt/const_prop/read_immutable_static.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// compile-flags: -O
|
||||
|
||||
static FOO: u8 = 2;
|
||||
|
||||
fn main() {
|
||||
let x = FOO + FOO;
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.ConstProp.before.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _2 = (FOO: u8);
|
||||
// ...
|
||||
// _3 = (FOO: u8);
|
||||
// _1 = Add(move _2, move _3);
|
||||
// ...
|
||||
// }
|
||||
// END rustc.main.ConstProp.before.mir
|
||||
// START rustc.main.ConstProp.after.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _2 = const 2u8;
|
||||
// ...
|
||||
// _3 = const 2u8;
|
||||
// _1 = Add(move _2, move _3);
|
||||
// ...
|
||||
// }
|
||||
// END rustc.main.ConstProp.after.mir
|
@ -16,7 +16,7 @@ fn main() {
|
||||
// START rustc.main.ConstProp.after.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _3 = const Scalar(AllocId(1).0x0) : fn();
|
||||
// _3 = const Scalar(AllocId(0).0x0) : fn();
|
||||
// _2 = move _3 as usize (Misc);
|
||||
// ...
|
||||
// _1 = move _2 as *const fn() (Misc);
|
||||
|
@ -34,7 +34,7 @@ fn main() {
|
||||
// assert(const true, "index out of bounds: the len is move _7 but the index is _6") -> bb1;
|
||||
// }
|
||||
// bb1: {
|
||||
// _1 = (*_2)[_6];
|
||||
// _1 = const 2u32;
|
||||
// ...
|
||||
// return;
|
||||
// }
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = 1_i32 << 32;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = 1 << -1;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = 1_u64 << 64;
|
||||
|
@ -5,6 +5,7 @@
|
||||
// sidestep the overflow checking.
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
// this signals overflow when checking is on
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = -1_i32 >> 32;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = -1_i32 >> -1;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
let _x = -1_i64 >> 64;
|
||||
|
@ -5,6 +5,7 @@
|
||||
// truncation does not sidestep the overflow checking.
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![warn(const_err)]
|
||||
|
||||
fn main() {
|
||||
// this signals overflow when checking is on
|
||||
|
@ -11,6 +11,7 @@ impl Unsigned for U8 {
|
||||
|
||||
impl<A: Unsigned, B: Unsigned> Unsigned for Sum<A,B> {
|
||||
const MAX: u8 = A::MAX + B::MAX; //~ ERROR any use of this value will cause an error
|
||||
//~| ERROR any use of this value will cause an error
|
||||
}
|
||||
|
||||
fn foo<T>(_: T) -> &'static u8 {
|
||||
|
@ -9,13 +9,21 @@ LL | const MAX: u8 = A::MAX + B::MAX;
|
||||
= note: `#[deny(const_err)]` on by default
|
||||
|
||||
error[E0080]: evaluation of constant expression failed
|
||||
--> $DIR/issue-50814.rs:17:5
|
||||
--> $DIR/issue-50814.rs:18:5
|
||||
|
|
||||
LL | &Sum::<U8,U8>::MAX
|
||||
| ^-----------------
|
||||
| |
|
||||
| referenced constant has errors
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: any use of this value will cause an error
|
||||
--> $DIR/issue-50814.rs:13:21
|
||||
|
|
||||
LL | const MAX: u8 = A::MAX + B::MAX;
|
||||
| ----------------^^^^^^^^^^^^^^^-
|
||||
| |
|
||||
| attempt to add with overflow
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
12
src/test/ui/consts/const-prop-read-static-in-const.rs
Normal file
12
src/test/ui/consts/const-prop-read-static-in-const.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// compile-flags: -Zunleash-the-miri-inside-of-you
|
||||
// run-pass
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
const TEST: u8 = MY_STATIC;
|
||||
//~^ skipping const checks
|
||||
|
||||
static MY_STATIC: u8 = 4;
|
||||
|
||||
fn main() {
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
warning: skipping const checks
|
||||
--> $DIR/const-prop-read-static-in-const.rs:6:18
|
||||
|
|
||||
LL | const TEST: u8 = MY_STATIC;
|
||||
| ^^^^^^^^^
|
||||
|
@ -1,3 +1,5 @@
|
||||
// compile-flags: -O
|
||||
|
||||
#![deny(exceeding_bitshifts, const_err)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
@ -1,113 +1,113 @@
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:7:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:9:15
|
||||
|
|
||||
LL | let n = 1u8 << 8;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:1:9
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:3:9
|
||||
|
|
||||
LL | #![deny(exceeding_bitshifts, const_err)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:9:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:11:15
|
||||
|
|
||||
LL | let n = 1u16 << 16;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:11:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:13:15
|
||||
|
|
||||
LL | let n = 1u32 << 32;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:13:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:15:15
|
||||
|
|
||||
LL | let n = 1u64 << 64;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:15:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:17:15
|
||||
|
|
||||
LL | let n = 1i8 << 8;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:17:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:19:15
|
||||
|
|
||||
LL | let n = 1i16 << 16;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:19:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:21:15
|
||||
|
|
||||
LL | let n = 1i32 << 32;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:21:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:23:15
|
||||
|
|
||||
LL | let n = 1i64 << 64;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:24:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:26:15
|
||||
|
|
||||
LL | let n = 1u8 >> 8;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:26:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:28:15
|
||||
|
|
||||
LL | let n = 1u16 >> 16;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:28:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:30:15
|
||||
|
|
||||
LL | let n = 1u32 >> 32;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:30:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:32:15
|
||||
|
|
||||
LL | let n = 1u64 >> 64;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:32:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:34:15
|
||||
|
|
||||
LL | let n = 1i8 >> 8;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:34:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:36:15
|
||||
|
|
||||
LL | let n = 1i16 >> 16;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:36:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:38:15
|
||||
|
|
||||
LL | let n = 1i32 >> 32;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift right with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:38:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:40:15
|
||||
|
|
||||
LL | let n = 1i64 >> 64;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:42:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:44:15
|
||||
|
|
||||
LL | let n = n << 8;
|
||||
| ^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:44:15
|
||||
--> $DIR/lint-exceeding-bitshifts.rs:46:15
|
||||
|
|
||||
LL | let n = 1u8 << -8;
|
||||
| ^^^^^^^^^
|
||||
|
@ -1,3 +1,5 @@
|
||||
// compile-flags: -O
|
||||
|
||||
#![deny(exceeding_bitshifts, const_err)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
@ -1,23 +1,23 @@
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:7:15
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:9:15
|
||||
|
|
||||
LL | let n = 1u8 << (4+4);
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:1:9
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:3:9
|
||||
|
|
||||
LL | #![deny(exceeding_bitshifts, const_err)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:15:15
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:17:15
|
||||
|
|
||||
LL | let n = 1_isize << BITS;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: attempt to shift left with overflow
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:16:15
|
||||
--> $DIR/lint-exceeding-bitshifts2.rs:18:15
|
||||
|
|
||||
LL | let n = 1_usize << BITS;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
Loading…
Reference in New Issue
Block a user