Skip to content

[const-prop] Support Rvalue::{Ref,Len} and Deref #61532

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

Merged
merged 5 commits into from
Jun 7, 2019
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
17 changes: 17 additions & 0 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,23 @@ where
Ok(())
}

/// Write an `Immediate` to memory.
#[inline(always)]
pub fn write_immediate_to_mplace(
&mut self,
src: Immediate<M::PointerTag>,
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.
Expand Down
51 changes: 39 additions & 12 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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| {
Expand Down
21 changes: 21 additions & 0 deletions src/test/mir-opt/const_prop/ref_deref.rs
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions src/test/mir-opt/const_prop/reify_fn_ptr.rs
Original file line number Diff line number Diff line change
@@ -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
40 changes: 22 additions & 18 deletions src/test/mir-opt/const_prop/slice_len.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
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;
// }
// END rustc.main.ConstProp.before.mir
// 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;
// }
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/consts/const-eval/promoted_errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand All @@ -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
|
Expand Down