diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 8f3dd72c4f245..621fc1ed3ff8c 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -2,6 +2,7 @@ //! assertion failures use rustc::hir::def::DefKind; +use rustc::hir::def_id::DefId; use rustc::mir::{ AggregateKind, Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local, NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind, @@ -17,8 +18,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::{self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind}; @@ -294,6 +294,26 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } } + fn get_global_id_for_static( + &self, + def_id: DefId, + promoted: Option, + ) -> Option> { + let generics = self.tcx.generics_of(def_id); + if generics.requires_monomorphization(self.tcx) { + trace!("can't monomorphize"); + // FIXME: can't handle code with generics + return None; + } + let substs = InternalSubsts::identity_for_item(self.tcx, def_id); + let instance = Instance::new(def_id, substs); + + Some(GlobalId { + instance, + promoted, + }) + } + fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option> { match *place { Place::Base(PlaceBase::Local(loc)) => self.places[loc].clone(), @@ -306,6 +326,14 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { })?; Some(res) }, + ProjectionElem::Deref => { + let base = self.eval_place(&proj.base, source_info)?; + let res = self.use_ecx(source_info, |this| { + this.ecx.deref_operand(base) + })?; + + Some(res.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. @@ -314,17 +342,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Place::Base( PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted), ..}) ) => { - let generics = self.tcx.generics_of(self.source.def_id()); - if generics.requires_monomorphization(self.tcx) { - // FIXME: can't handle code with generics - return None; - } - let substs = InternalSubsts::identity_for_item(self.tcx, self.source.def_id()); - let instance = Instance::new(self.source.def_id(), substs); - let cid = GlobalId { - instance, - promoted: Some(promoted), - }; + let cid = self.get_global_id_for_static(self.source.def_id(), Some(promoted))?; // cannot use `const_eval` here, because that would require having the MIR // for the current function available, but we're producing said MIR right now let res = self.use_ecx(source_info, |this| { @@ -334,7 +352,24 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { trace!("evaluated promoted {:?} to {:?}", promoted, res); Some(res.into()) }, - _ => None, + Place::Base( + PlaceBase::Static(box Static {kind: StaticKind::Static(def_id), .. }) + ) => { + // mutable statics can change value at any time so we can't const + // propagate their values + if self.tcx.is_mutable_static(def_id) { + trace!("can't const-prop mutable static"); + return None; + } + + let cid = self.get_global_id_for_static(def_id, None)?; + + let res = self.use_ecx(source_info, |this| { + this.ecx.const_eval_raw(cid) + })?; + trace!("evaluated static {:?} to {:?}", def_id, res); + Some(res.into()) + }, } } @@ -370,10 +405,34 @@ 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.to_mem_place(); - // FIXME(oli-obk): evaluate static/constant slice lengths - Rvalue::Len(_) => None, + let is_slice = + if let ty::Slice(_) = mplace.layout.ty.sty { true } + else { mplace.layout.ty.is_slice() }; + + if is_slice { + let len = mplace.meta.unwrap().to_usize(&self.ecx).unwrap(); + + 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 { diff --git a/src/test/mir-opt/const_prop/slice_len.rs b/src/test/mir-opt/const_prop/slice_len.rs index 3435ca07f4cd8..25e7677429310 100644 --- a/src/test/mir-opt/const_prop/slice_len.rs +++ b/src/test/mir-opt/const_prop/slice_len.rs @@ -1,22 +1,20 @@ -fn test() -> &'static [u32] { - &[1, 2] -} +static X: &[u32] = &[0, 1, 2]; fn main() { - let x = test()[0]; + let x = X[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; +// _2 = const 1usize; +// _3 = Len((*(X: &[u32]))); +// _4 = Lt(_2, _3); +// assert(move _4, "index out of bounds: the len is move _3 but the index is _2") -> bb1; // } -// bb2: { -// _1 = (*_2)[_3]; +// bb1: { +// _1 = (*(X: &[u32]))[_2]; // ... // return; // } @@ -24,13 +22,13 @@ 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; +// _2 = const 1usize; +// _3 = const 3usize; +// _4 = const true; +// assert(const true, "index out of bounds: the len is move _3 but the index is _2") -> bb1; // } -// bb2: { -// _1 = (*_2)[_3]; +// bb1: { +// _1 = (*(X: &[u32]))[_2]; // ... // return; // }