diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index e69e7a522ab73..7bcf4ef6588d5 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -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, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index a92c81e046a0f..fac6e8b69efab 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -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) => { @@ -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>, diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 3b3c28f6ae265..d15dd14084aad 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -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,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 }, }; @@ -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; diff --git a/src/test/run-pass/cast-rfc0401.rs b/src/test/run-pass/cast-rfc0401.rs index 5b6f6ccc627a4..022ed6c28c3bd 100644 --- a/src/test/run-pass/cast-rfc0401.rs +++ b/src/test/run-pass/cast-rfc0401.rs @@ -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 diff --git a/src/test/ui/const-eval/promoted_errors.rs b/src/test/ui/const-eval/promoted_errors.rs index a39afb9bdd4a4..04e9bfbc03e27 100644 --- a/src/test/ui/const-eval/promoted_errors.rs +++ b/src/test/ui/const-eval/promoted_errors.rs @@ -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 } diff --git a/src/test/ui/const-eval/promoted_errors.stderr b/src/test/ui/const-eval/promoted_errors.stderr index a4c1c48a03dfe..9afe5f20af394 100644 --- a/src/test/ui/const-eval/promoted_errors.stderr +++ b/src/test/ui/const-eval/promoted_errors.stderr @@ -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 +