Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Const propagate casts #52498

Merged
merged 3 commits into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 143 additions & 3 deletions src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
133 changes: 3 additions & 130 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
}
let src = self.eval_operand(operand)?;
self.cast(src, kind, dest_ty, dest)?;
}

Discriminant(ref place) => {
Expand Down Expand Up @@ -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>,
Expand Down
48 changes: 34 additions & 14 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -145,17 +145,23 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
let r = match f(self) {
Ok(val) => Some(val),
Err(err) => {
let (frames, span) = self.ecx.generate_stacktrace(None);
let err = ConstEvalErr {
span,
error: err,
stacktrace: frames,
};
err.report_as_lint(
self.ecx.tcx,
"this expression will panic at runtime",
lint_root,
);
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,
error: err,
stacktrace: frames,
};
err.report_as_lint(
self.ecx.tcx,
"this expression will panic at runtime",
lint_root,
);
}
}
None
},
};
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion src/test/run-pass/cast-rfc0401.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,12 @@ fn main()

assert!(foo as usize != bar as usize);

assert_eq!(foo as i16, foo as usize as i16);
// 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

Expand Down
Loading