diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 36d80d0cb5767..c1d4cd35ef66b 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -505,7 +505,7 @@ pub fn const_field<'tcx>( trace!("const_field: {:?}, {:?}", field, value); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); // get the operand again - let op = ecx.eval_const_to_op(value, None).unwrap(); + let op = ecx.eval_const_to_op(value.val, value.ty, None).unwrap(); // downcast let down = match variant { None => op, @@ -527,7 +527,7 @@ pub fn const_variant_index<'tcx>( ) -> VariantIdx { trace!("const_variant_index: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); - let op = ecx.eval_const_to_op(val, None).unwrap(); + let op = ecx.eval_const_to_op(val.val, val.ty, None).unwrap(); ecx.read_discriminant(op).unwrap().1 } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 26cfbfe53a35c..a3a237a1d3c0d 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -67,7 +67,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // The src operand does not matter, just its type match src.layout.ty.sty { ty::Closure(def_id, substs) => { - let substs = self.subst_and_normalize_erasing_regions(substs)?; let instance = ty::Instance::resolve_closure( *self.tcx, def_id, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 6f4227ed34cc4..520dc0ab38a61 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -9,7 +9,7 @@ use rustc::mir; use rustc::ty::layout::{ self, Size, Align, HasDataLayout, LayoutOf, TyLayout }; -use rustc::ty::subst::{Subst, SubstsRef}; +use rustc::ty::subst::{SubstsRef, Subst}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::query::TyCtxtAt; use rustc_data_structures::indexed_vec::IndexVec; @@ -291,22 +291,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty.is_freeze(*self.tcx, self.param_env, DUMMY_SP) } - pub(super) fn subst_and_normalize_erasing_regions>( + /// Call this whenever you have a value that you took from the current frame's `mir::Body`. + /// Not guaranteed to actually monomorphize the value. + /// If we are e.g. const propagating inside a generic function, some + /// things may depend on a generic parameter and thus can never be monomorphized. + pub(super) fn subst_and_normalize_erasing_regions_in_frame>( &self, - substs: T, + value: T, ) -> InterpResult<'tcx, T> { - match self.stack.last() { - Some(frame) => Ok(self.tcx.subst_and_normalize_erasing_regions( - frame.instance.substs, - self.param_env, - &substs, - )), - None => if substs.needs_subst() { - throw_inval!(TooGeneric) - } else { - Ok(substs) - }, + self.subst_and_normalize_erasing_regions(self.frame().instance.substs, value) + } + + /// Same thing as `subst_and_normalize_erasing_regions_in_frame` but not taking its substs + /// from the top stack frame, but requiring you to pass specific substs. + /// + /// Only call this function if you want to apply the substs of a specific frame (that is + /// definitely not the frame currently being evaluated). You need to make sure to pass correct + /// substs. + fn subst_and_normalize_erasing_regions>( + &self, + param_substs: SubstsRef<'tcx>, + value: T, + ) -> InterpResult<'tcx, T> { + let substituted = value.subst(self.tcx.tcx, param_substs); + // we duplicate the body of `TyCtxt::subst_and_normalize_erasing_regions` here, because + // we can't normalize values with generic parameters. The difference between this function + // and the `TyCtxt` version is this early abort + if substituted.needs_subst() { + // FIXME(oli-obk): This aborts evaluating `fn foo() -> i32 { 42 }` inside an + // associated constant of a generic trait, even though that should be doable. + throw_inval!(TooGeneric); } + Ok(self.tcx.normalize_erasing_regions(self.param_env, substituted)) } pub(super) fn resolve( @@ -315,9 +331,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { substs: SubstsRef<'tcx> ) -> InterpResult<'tcx, ty::Instance<'tcx>> { trace!("resolve: {:?}, {:#?}", def_id, substs); - trace!("param_env: {:#?}", self.param_env); - let substs = self.subst_and_normalize_erasing_regions(substs)?; - trace!("substs: {:#?}", substs); ty::Instance::resolve( *self.tcx, self.param_env, @@ -349,36 +362,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - pub(super) fn monomorphize + Subst<'tcx>>( - &self, - t: T, - ) -> InterpResult<'tcx, T> { - match self.stack.last() { - Some(frame) => Ok(self.monomorphize_with_substs(t, frame.instance.substs)?), - None => if t.needs_subst() { - throw_inval!(TooGeneric) - } else { - Ok(t) - }, - } - } - - fn monomorphize_with_substs + Subst<'tcx>>( - &self, - t: T, - substs: SubstsRef<'tcx> - ) -> InterpResult<'tcx, T> { - // miri doesn't care about lifetimes, and will choke on some crazy ones - // let's simply get rid of them - let substituted = t.subst(*self.tcx, substs); - - if substituted.needs_subst() { - throw_inval!(TooGeneric) - } - - Ok(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted)) - } - pub fn layout_of_local( &self, frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, @@ -391,7 +374,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { None => { let layout = crate::interpret::operand::from_known_layout(layout, || { let local_ty = frame.body.local_decls[local].ty; - let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs)?; + let local_ty = self.subst_and_normalize_erasing_regions( + frame.instance.substs, local_ty, + )?; self.layout_of(local_ty) })?; if let Some(state) = frame.locals.get(local) { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index f778eb1734c4b..a04215f11aff0 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; -use rustc::{mir, ty}; +use rustc::{mir, ty::{self, Ty}}; use rustc::ty::layout::{ self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx, }; @@ -511,7 +511,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Move(ref place) => self.eval_place_to_op(place, layout)?, - Constant(ref constant) => self.eval_const_to_op(constant.literal, layout)?, + Constant(ref constant) => { + let val = self.subst_and_normalize_erasing_regions_in_frame(constant.literal.val)?; + let ty = self.subst_and_normalize_erasing_regions_in_frame(constant.ty)?; + self.eval_const_to_op(val, ty, layout)? + }, }; trace!("{:?}: {:?}", mir_op, *op); Ok(op) @@ -531,7 +535,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // in patterns via the `const_eval` module crate fn eval_const_to_op( &self, - val: &'tcx ty::Const<'tcx>, + value: ConstValue<'tcx>, + ty: Ty<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let tag_scalar = |scalar| match scalar { @@ -539,10 +544,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Scalar::Raw { data, size } => Scalar::Raw { data, size }, }; // Early-return cases. - match val.val { - ConstValue::Param(_) => - // FIXME(oli-obk): try to monomorphize - throw_inval!(TooGeneric), + match value { + ConstValue::Param(_) => throw_inval!(TooGeneric), ConstValue::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; return Ok(OpTy::from(self.const_eval_raw(GlobalId { @@ -553,10 +556,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => {} } // Other cases need layout. - let layout = from_known_layout(layout, || { - self.layout_of(self.monomorphize(val.ty)?) - })?; - let op = match val.val { + let layout = from_known_layout(layout, || self.layout_of(ty))?; + let op = match value { ConstValue::ByRef { alloc, offset } => { let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc); // We rely on mutability being set correctly in that allocation to prevent writes @@ -583,7 +584,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ConstValue::Infer(..) | ConstValue::Placeholder(..) | ConstValue::Unevaluated(..) => - bug!("eval_const_to_op: Unexpected ConstValue {:?}", val), + bug!("eval_const_to_op: Unexpected ConstValue {:?}", value), }; Ok(OpTy { op, layout }) } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index f66c4adf76397..8128518d8031d 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -616,10 +616,11 @@ where Some(return_place) => { // We use our layout to verify our assumption; caller will validate // their layout on return. + let ret_ty = self.frame().body.return_ty(); + let ret_ty = self.subst_and_normalize_erasing_regions_in_frame(ret_ty)?; PlaceTy { place: *return_place, - layout: self - .layout_of(self.monomorphize(self.frame().body.return_ty())?)?, + layout: self.layout_of(ret_ty)?, } } None => throw_unsup!(InvalidNullPointerUsage), diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index d152e2b50fa1b..1b0ecb8aa3647 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -249,7 +249,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } NullaryOp(mir::NullOp::SizeOf, ty) => { - let ty = self.monomorphize(ty)?; + let ty = self.subst_and_normalize_erasing_regions_in_frame(ty)?; let layout = self.layout_of(ty)?; assert!(!layout.is_unsized(), "SizeOf nullary MIR operator called for unsized type"); diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index e55b0d0fb1f2a..01171e6020b34 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -77,7 +77,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { for (i, method) in methods.iter().enumerate() { if let Some((def_id, substs)) = *method { // resolve for vtable: insert shims where needed - let substs = self.subst_and_normalize_erasing_regions(substs)?; + let substs = self.subst_and_normalize_erasing_regions_in_frame(substs)?; let instance = ty::Instance::resolve_for_vtable( *self.tcx, self.param_env, diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 38d26d0ba50a4..2fbea12819452 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -162,11 +162,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let def_id = source.def_id(); let param_env = tcx.param_env(def_id); let span = tcx.def_span(def_id); + let substs = InternalSubsts::identity_for_item(tcx, def_id); let mut ecx = mk_eval_cx(tcx, span, param_env); let can_const_prop = CanConstProp::check(body); ecx.push_stack_frame( - Instance::new(def_id, &InternalSubsts::identity_for_item(tcx, def_id)), + Instance::new(def_id, &substs), span, dummy_body, None, @@ -286,7 +287,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { c: &Constant<'tcx>, ) -> Option> { self.ecx.tcx.span = c.span; - match self.ecx.eval_const_to_op(c.literal, None) { + match self.ecx.eval_const_to_op(c.literal.val, c.literal.ty, None) { Ok(op) => { Some(op) }, diff --git a/src/test/incremental/no_mangle2.rs b/src/test/incremental/no_mangle2.rs new file mode 100644 index 0000000000000..d243fd97e7aed --- /dev/null +++ b/src/test/incremental/no_mangle2.rs @@ -0,0 +1,10 @@ +// revisions:cfail1 cfail2 +// check-pass +// compile-flags: --crate-type staticlib + +#![deny(unused_attributes)] + +#[no_mangle] +pub extern "C" fn rust_no_mangle() -> i32 { + 42 +} diff --git a/src/test/ui/consts/too_generic_eval_ice.rs b/src/test/ui/consts/too_generic_eval_ice.rs new file mode 100644 index 0000000000000..7a299169bc4e1 --- /dev/null +++ b/src/test/ui/consts/too_generic_eval_ice.rs @@ -0,0 +1,13 @@ +pub struct Foo(A, B); + +impl Foo { + const HOST_SIZE: usize = std::mem::size_of::(); + + pub fn crash() -> bool { + [5; Self::HOST_SIZE] == [6; 0] //~ ERROR no associated item named `HOST_SIZE` + //~^ the size for values of type `A` cannot be known + //~| the size for values of type `B` cannot be known + } +} + +fn main() {} diff --git a/src/test/ui/consts/too_generic_eval_ice.stderr b/src/test/ui/consts/too_generic_eval_ice.stderr new file mode 100644 index 0000000000000..eef79421270ce --- /dev/null +++ b/src/test/ui/consts/too_generic_eval_ice.stderr @@ -0,0 +1,47 @@ +error[E0599]: no associated item named `HOST_SIZE` found for type `Foo` in the current scope + --> $DIR/too_generic_eval_ice.rs:7:19 + | +LL | pub struct Foo(A, B); + | --------------------------- associated item `HOST_SIZE` not found for this +... +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^ associated item not found in `Foo` + | + = note: the method `HOST_SIZE` exists but the following trait bounds were not satisfied: + `A : std::marker::Sized` + `B : std::marker::Sized` + +error[E0277]: the size for values of type `A` cannot be known at compilation time + --> $DIR/too_generic_eval_ice.rs:7:13 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `A` + = note: to learn more, visit + = help: consider adding a `where A: std::marker::Sized` bound +note: required by `Foo` + --> $DIR/too_generic_eval_ice.rs:1:1 + | +LL | pub struct Foo(A, B); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the size for values of type `B` cannot be known at compilation time + --> $DIR/too_generic_eval_ice.rs:7:13 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `B` + = note: to learn more, visit + = help: consider adding a `where B: std::marker::Sized` bound +note: required by `Foo` + --> $DIR/too_generic_eval_ice.rs:1:1 + | +LL | pub struct Foo(A, B); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-58435-ice-with-assoc-const.rs b/src/test/ui/issues/issue-58435-ice-with-assoc-const.rs index fac727d2d7dc9..f51d19ed5e59d 100644 --- a/src/test/ui/issues/issue-58435-ice-with-assoc-const.rs +++ b/src/test/ui/issues/issue-58435-ice-with-assoc-const.rs @@ -1,6 +1,6 @@ // run-pass -// The const-evaluator was at one point ICE'ing while trying to -// evaluate the body of `fn id` during the `s.id()` call in main. +// The const propagator was at one point ICE'ing while trying to +// propagate constants in the body of `fn id` during the `s.id()` call in main. struct S(T);