diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index fac9665d968e2..a3297666113d4 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -663,6 +663,23 @@ where Ok(()) } + /// Write an `Immediate` to memory. + #[inline(always)] + pub fn write_immediate_to_mplace( + &mut self, + src: Immediate, + dest: MPlaceTy<'tcx, M::PointerTag>, + ) -> EvalResult<'tcx> { + self.write_immediate_to_mplace_no_validate(src, dest)?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(dest.into(), vec![], None, /*const_mode*/ false)?; + } + + Ok(()) + } + /// Write an immediate to a place. /// If you use this you are responsible for validating that things got copied at the /// right type. diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index dbaa4e557c66f..0d389b31f9075 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -17,8 +17,7 @@ use syntax_pos::{Span, DUMMY_SP}; use rustc::ty::subst::InternalSubsts; use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty::layout::{ - LayoutOf, TyLayout, LayoutError, - HasTyCtxt, TargetDataLayout, HasDataLayout, + LayoutOf, TyLayout, LayoutError, HasTyCtxt, TargetDataLayout, HasDataLayout, Size, }; use crate::interpret::{ @@ -333,6 +332,12 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { 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. @@ -363,8 +368,12 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { 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()) + }, Rvalue::Repeat(..) | - Rvalue::Ref(..) | Rvalue::Aggregate(..) | Rvalue::NullaryOp(NullOp::Box, _) | Rvalue::Discriminant(..) => None, @@ -376,10 +385,30 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { 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.sty { + let len = mplace.meta.unwrap().to_usize(&self.ecx).unwrap(); - // FIXME(oli-obk): evaluate static/constant slice lengths - Rvalue::Len(_) => None, + Some(ImmTy { + imm: Immediate::Scalar( + Scalar::from_uint( + len, + Size::from_bits( + self.tcx.sess.target.usize_ty.bit_width().unwrap() as u64 + ) + ).into(), + ), + layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, + }.into()) + } else { + trace!("not slice: {:?}", mplace.layout.ty.sty); + None + } + }, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some( ImmTy { @@ -525,12 +554,10 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { source_info: SourceInfo, ) { trace!("attepting to replace {:?} with {:?}", rval, value); - self.ecx.validate_operand( - value, - vec![], - None, - true, - ).expect("value should already be a valid const"); + if let Err(e) = self.ecx.validate_operand(value, vec![], None, true) { + trace!("validation error, attempt failed: {:?}", e); + return; + } // FIXME> figure out what tho do when try_read_immediate fails let imm = self.use_ecx(source_info, |this| { diff --git a/src/test/mir-opt/const_prop/ref_deref.rs b/src/test/mir-opt/const_prop/ref_deref.rs new file mode 100644 index 0000000000000..2d04822c0e789 --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref.rs @@ -0,0 +1,21 @@ +fn main() { + *(&4); +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _2 = &(promoted[0]: i32); +// _1 = (*_2); +// ... +//} +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _2 = const Scalar(AllocId(0).0x0) : &i32; +// _1 = const 4i32; +// ... +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.rs b/src/test/mir-opt/const_prop/reify_fn_ptr.rs new file mode 100644 index 0000000000000..809eb19ade899 --- /dev/null +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.rs @@ -0,0 +1,25 @@ +fn main() { + let _ = main as usize as *const fn(); +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _3 = const main as fn() (Pointer(ReifyFnPointer)); +// _2 = move _3 as usize (Misc); +// ... +// _1 = move _2 as *const fn() (Misc); +// ... +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _3 = const Scalar(AllocId(1).0x0) : fn(); +// _2 = move _3 as usize (Misc); +// ... +// _1 = const Scalar(AllocId(1).0x0) : *const fn(); +// ... +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/slice_len.rs b/src/test/mir-opt/const_prop/slice_len.rs index 3435ca07f4cd8..5babeb195a826 100644 --- a/src/test/mir-opt/const_prop/slice_len.rs +++ b/src/test/mir-opt/const_prop/slice_len.rs @@ -1,22 +1,22 @@ -fn test() -> &'static [u32] { - &[1, 2] -} - fn main() { - let x = test()[0]; + (&[1u32, 2, 3] as &[u32])[1]; } // END RUST SOURCE // START rustc.main.ConstProp.before.mir -// bb1: { +// bb0: { // ... -// _3 = const 0usize; -// _4 = Len((*_2)); -// _5 = Lt(_3, _4); -// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb2; +// _4 = &(promoted[0]: [u32; 3]); +// _3 = _4; +// _2 = move _3 as &[u32] (Pointer(Unsize)); +// ... +// _6 = const 1usize; +// _7 = Len((*_2)); +// _8 = Lt(_6, _7); +// assert(move _8, "index out of bounds: the len is move _7 but the index is _6") -> bb1; // } -// bb2: { -// _1 = (*_2)[_3]; +// bb1: { +// _1 = (*_2)[_6]; // ... // return; // } @@ -24,13 +24,17 @@ fn main() { // START rustc.main.ConstProp.after.mir // bb0: { // ... -// _3 = const 0usize; -// _4 = Len((*_2)); -// _5 = Lt(_3, _4); -// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb2; +// _4 = const Scalar(AllocId(0).0x0) : &[u32; 3]; +// _3 = const Scalar(AllocId(0).0x0) : &[u32; 3]; +// _2 = move _3 as &[u32] (Pointer(Unsize)); +// ... +// _6 = const 1usize; +// _7 = const 3usize; +// _8 = const true; +// assert(const true, "index out of bounds: the len is move _7 but the index is _6") -> bb1; // } -// bb2: { -// _1 = (*_2)[_3]; +// bb1: { +// _1 = (*_2)[_6]; // ... // return; // } diff --git a/src/test/ui/consts/const-eval/promoted_errors.stderr b/src/test/ui/consts/const-eval/promoted_errors.stderr index c9d5ede61ade4..c9f3a7659f9cd 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.stderr @@ -16,6 +16,12 @@ warning: attempt to divide by zero LL | println!("{}", 1/(1-1)); | ^^^^^^^ +warning: this expression will panic at runtime + --> $DIR/promoted_errors.rs:9:20 + | +LL | println!("{}", 1/(1-1)); + | ^^^^^^^ attempt to divide by zero + warning: attempt to divide by zero --> $DIR/promoted_errors.rs:11:14 | @@ -34,6 +40,12 @@ warning: attempt to divide by zero LL | println!("{}", 1/(false as u32)); | ^^^^^^^^^^^^^^^^ +warning: this expression will panic at runtime + --> $DIR/promoted_errors.rs:14:20 + | +LL | println!("{}", 1/(false as u32)); + | ^^^^^^^^^^^^^^^^ attempt to divide by zero + warning: attempt to divide by zero --> $DIR/promoted_errors.rs:16:14 |