Auto merge of #52498 - oli-obk:const_prop, r=nikomatsakis
Const propagate casts fixes #49760 So... This fixes the original issue about the missing warnings. But our test suite contains fun things like ```rust fn foo() {} assert_eq!(foo as i16, foo as usize as i16); ``` Which, will result in > a raw memory access tried to access part of a pointer value as raw bytes on both sides of the assertion. Because well... that's exactly what's going on! We're ripping out 16 bits of a pointer.
This commit is contained in:
commit
509cbf3e8e
6 changed files with 207 additions and 148 deletions
|
@ -1,13 +1,153 @@
|
|||
use rustc::ty::Ty;
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{self, LayoutOf};
|
||||
use syntax::ast::{FloatTy, IntTy, UintTy};
|
||||
|
||||
use rustc_apfloat::ieee::{Single, Double};
|
||||
use super::{EvalContext, Machine};
|
||||
use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic};
|
||||
use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic, Value, EvalErrorKind};
|
||||
use rustc::mir::CastKind;
|
||||
use rustc_apfloat::Float;
|
||||
use interpret::eval_context::ValTy;
|
||||
use interpret::Place;
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
crate fn cast(
|
||||
&mut self,
|
||||
src: ValTy<'tcx>,
|
||||
kind: CastKind,
|
||||
dest_ty: Ty<'tcx>,
|
||||
dest: Place,
|
||||
) -> EvalResult<'tcx> {
|
||||
use rustc::mir::CastKind::*;
|
||||
match kind {
|
||||
Unsize => {
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
let dst_layout = self.layout_of(dest_ty)?;
|
||||
self.unsize_into(src.value, src_layout, dest, dst_layout)?;
|
||||
}
|
||||
|
||||
Misc => {
|
||||
if self.type_is_fat_ptr(src.ty) {
|
||||
match (src.value, self.type_is_fat_ptr(dest_ty)) {
|
||||
(Value::ByRef { .. }, _) |
|
||||
// pointers to extern types
|
||||
(Value::Scalar(_),_) |
|
||||
// slices and trait objects to other slices/trait objects
|
||||
(Value::ScalarPair(..), true) => {
|
||||
let valty = ValTy {
|
||||
value: src.value,
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
// slices and trait objects to thin pointers (dropping the metadata)
|
||||
(Value::ScalarPair(data, _), false) => {
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(data),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
match src_layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = src.ty.ty_adt_def() {
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(*self.tcx, index)
|
||||
.val;
|
||||
let defined = self
|
||||
.layout_of(dest_ty)
|
||||
.unwrap()
|
||||
.size
|
||||
.bits() as u8;
|
||||
return self.write_scalar(
|
||||
dest,
|
||||
Scalar::Bits {
|
||||
bits: discr_val,
|
||||
defined,
|
||||
},
|
||||
dest_ty);
|
||||
}
|
||||
}
|
||||
layout::Variants::Tagged { .. } |
|
||||
layout::Variants::NicheFilling { .. } => {},
|
||||
}
|
||||
|
||||
let src_val = self.value_to_scalar(src)?;
|
||||
let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(dest_val),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
ReifyFnPointer => {
|
||||
match src.ty.sty {
|
||||
ty::TyFnDef(def_id, substs) => {
|
||||
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
|
||||
bug!("reifying a fn ptr that requires \
|
||||
const arguments");
|
||||
}
|
||||
let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
|
||||
*self.tcx,
|
||||
self.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance?);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(fn_ptr.into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
ref other => bug!("reify fn pointer on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeFnPointer => {
|
||||
match dest_ty.sty {
|
||||
ty::TyFnPtr(_) => {
|
||||
let mut src = src;
|
||||
src.ty = dest_ty;
|
||||
self.write_value(src, dest)?;
|
||||
}
|
||||
ref other => bug!("fn to unsafe fn cast on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
ClosureFnPointer => {
|
||||
match src.ty.sty {
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
let substs = self.tcx.subst_and_normalize_erasing_regions(
|
||||
self.substs(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
&substs,
|
||||
);
|
||||
let instance = ty::Instance::resolve_closure(
|
||||
*self.tcx,
|
||||
def_id,
|
||||
substs,
|
||||
ty::ClosureKind::FnOnce,
|
||||
);
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(fn_ptr.into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
ref other => bug!("closure fn pointer on {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn cast_scalar(
|
||||
&self,
|
||||
val: Scalar,
|
||||
|
|
|
@ -770,135 +770,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||
|
||||
Cast(kind, ref operand, cast_ty) => {
|
||||
debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty);
|
||||
use rustc::mir::CastKind::*;
|
||||
match kind {
|
||||
Unsize => {
|
||||
let src = self.eval_operand(operand)?;
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
let dst_layout = self.layout_of(dest_ty)?;
|
||||
self.unsize_into(src.value, src_layout, dest, dst_layout)?;
|
||||
}
|
||||
|
||||
Misc => {
|
||||
let src = self.eval_operand(operand)?;
|
||||
if self.type_is_fat_ptr(src.ty) {
|
||||
match (src.value, self.type_is_fat_ptr(dest_ty)) {
|
||||
(Value::ByRef { .. }, _) |
|
||||
// pointers to extern types
|
||||
(Value::Scalar(_),_) |
|
||||
// slices and trait objects to other slices/trait objects
|
||||
(Value::ScalarPair(..), true) => {
|
||||
let valty = ValTy {
|
||||
value: src.value,
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
// slices and trait objects to thin pointers (dropping the metadata)
|
||||
(Value::ScalarPair(data, _), false) => {
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(data),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
match src_layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = src.ty.ty_adt_def() {
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(*self.tcx, index)
|
||||
.val;
|
||||
let defined = self
|
||||
.layout_of(dest_ty)
|
||||
.unwrap()
|
||||
.size
|
||||
.bits() as u8;
|
||||
return self.write_scalar(
|
||||
dest,
|
||||
Scalar::Bits {
|
||||
bits: discr_val,
|
||||
defined,
|
||||
},
|
||||
dest_ty);
|
||||
}
|
||||
}
|
||||
layout::Variants::Tagged { .. } |
|
||||
layout::Variants::NicheFilling { .. } => {},
|
||||
}
|
||||
|
||||
let src_val = self.value_to_scalar(src)?;
|
||||
let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(dest_val),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
ReifyFnPointer => {
|
||||
match self.eval_operand(operand)?.ty.sty {
|
||||
ty::TyFnDef(def_id, substs) => {
|
||||
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
|
||||
bug!("reifying a fn ptr that requires \
|
||||
const arguments");
|
||||
}
|
||||
let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
|
||||
*self.tcx,
|
||||
self.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance?);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(fn_ptr.into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
ref other => bug!("reify fn pointer on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeFnPointer => {
|
||||
match dest_ty.sty {
|
||||
ty::TyFnPtr(_) => {
|
||||
let mut src = self.eval_operand(operand)?;
|
||||
src.ty = dest_ty;
|
||||
self.write_value(src, dest)?;
|
||||
}
|
||||
ref other => bug!("fn to unsafe fn cast on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
ClosureFnPointer => {
|
||||
match self.eval_operand(operand)?.ty.sty {
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
let substs = self.tcx.subst_and_normalize_erasing_regions(
|
||||
self.substs(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
&substs,
|
||||
);
|
||||
let instance = ty::Instance::resolve_closure(
|
||||
*self.tcx,
|
||||
def_id,
|
||||
substs,
|
||||
ty::ClosureKind::FnOnce,
|
||||
);
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(fn_ptr.into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
}
|
||||
ref other => bug!("closure fn pointer on {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cast(src, kind, dest_ty, dest)?;
|
||||
}
|
||||
|
||||
Discriminant(ref place) => {
|
||||
|
@ -1565,7 +1438,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||
}
|
||||
}
|
||||
|
||||
fn unsize_into(
|
||||
crate fn unsize_into(
|
||||
&mut self,
|
||||
src: Value,
|
||||
src_layout: TyLayout<'tcx>,
|
||||
|
|
|
@ -17,7 +17,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local
|
|||
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;
|
||||
use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind};
|
||||
use rustc::ty::{TyCtxt, self, Instance};
|
||||
use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
|
||||
use interpret::EvalContext;
|
||||
|
@ -145,6 +145,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
let r = match f(self) {
|
||||
Ok(val) => Some(val),
|
||||
Err(err) => {
|
||||
match err.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,
|
||||
|
@ -156,6 +160,8 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
"this expression will panic at runtime",
|
||||
lint_root,
|
||||
);
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
};
|
||||
|
@ -257,10 +263,25 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
},
|
||||
Rvalue::Repeat(..) |
|
||||
Rvalue::Ref(..) |
|
||||
Rvalue::Cast(..) |
|
||||
Rvalue::Aggregate(..) |
|
||||
Rvalue::NullaryOp(NullOp::Box, _) |
|
||||
Rvalue::Discriminant(..) => None,
|
||||
|
||||
Rvalue::Cast(kind, ref operand, _) => {
|
||||
let (value, ty, 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 = ::interpret::Place::from_ptr(dest_ptr, place_align);
|
||||
this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?;
|
||||
Ok((
|
||||
Value::ByRef(dest_ptr.into(), place_align),
|
||||
place_ty,
|
||||
span,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME(oli-obk): evaluate static/constant slice lengths
|
||||
Rvalue::Len(_) => None,
|
||||
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
|
||||
|
@ -354,7 +375,6 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
)
|
||||
} else {
|
||||
if overflow {
|
||||
use rustc::mir::interpret::EvalErrorKind;
|
||||
let err = EvalErrorKind::Overflow(op).into();
|
||||
let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
|
||||
return None;
|
||||
|
|
|
@ -166,7 +166,12 @@ fn main()
|
|||
|
||||
assert!(foo as usize != bar as usize);
|
||||
|
||||
// Taking a few bits of a function's address is totally pointless and we detect that
|
||||
// Disabling the lint to ensure that the assertion can still be run
|
||||
#[allow(const_err)]
|
||||
{
|
||||
assert_eq!(foo as i16, foo as usize as i16);
|
||||
}
|
||||
|
||||
// fptr-ptr-cast
|
||||
|
||||
|
|
|
@ -24,5 +24,8 @@ fn main() {
|
|||
//~| WARN const_err
|
||||
println!("{}", 1/(false as u32));
|
||||
//~^ WARN const_err
|
||||
//~| WARN const_err
|
||||
let _x = 1/(false as u32);
|
||||
//~^ WARN const_err
|
||||
//~| WARN const_err
|
||||
}
|
||||
|
|
|
@ -34,9 +34,27 @@ warning: this expression will panic at runtime
|
|||
LL | let _x = 1/(1-1);
|
||||
| ^^^^^^^ attempt to divide by zero
|
||||
|
||||
warning: attempt to divide by zero
|
||||
--> $DIR/promoted_errors.rs:25:20
|
||||
|
|
||||
LL | println!("{}", 1/(false as u32));
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: this expression will panic at runtime
|
||||
--> $DIR/promoted_errors.rs:25:20
|
||||
|
|
||||
LL | println!("{}", 1/(false as u32));
|
||||
| ^^^^^^^^^^^^^^^^ attempt to divide by zero
|
||||
|
||||
warning: attempt to divide by zero
|
||||
--> $DIR/promoted_errors.rs:28:14
|
||||
|
|
||||
LL | let _x = 1/(false as u32);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: this expression will panic at runtime
|
||||
--> $DIR/promoted_errors.rs:28:14
|
||||
|
|
||||
LL | let _x = 1/(false as u32);
|
||||
| ^^^^^^^^^^^^^^^^ attempt to divide by zero
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue