reintroduce Lvalue and LvalueExtra
This commit is contained in:
parent
e28f873756
commit
d2d73a908d
3 changed files with 80 additions and 96 deletions
|
|
@ -101,6 +101,20 @@ pub struct Frame<'a, 'tcx: 'a> {
|
|||
pub stmt: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
struct Lvalue {
|
||||
ptr: Pointer,
|
||||
extra: LvalueExtra,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum LvalueExtra {
|
||||
None,
|
||||
Length(u64),
|
||||
Vtable(Pointer),
|
||||
DowncastVariant(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CachedMir<'mir, 'tcx: 'mir> {
|
||||
Ref(&'mir mir::Mir<'tcx>),
|
||||
|
|
@ -553,8 +567,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let ty = self.lvalue_ty(lvalue);
|
||||
match ty.sty {
|
||||
ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?,
|
||||
ty::TySlice(_) => if let Value::ByValPair(_, len) = src {
|
||||
self.memory.write_primval(dest, len)?;
|
||||
ty::TySlice(_) => if let LvalueExtra::Length(len) = src.extra {
|
||||
self.memory.write_usize(dest, len)?;
|
||||
} else {
|
||||
bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src);
|
||||
},
|
||||
|
|
@ -563,10 +577,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
Ref(_, _, ref lvalue) => {
|
||||
match self.eval_lvalue(lvalue)? {
|
||||
Value::ByRef(ptr) => self.memory.write_ptr(dest, ptr)?,
|
||||
Value::ByVal(..) => bug!("cannot take reference of immediate"),
|
||||
pair @ Value::ByValPair(..) => self.write_value(pair, dest, dest_ty)?,
|
||||
let lvalue = self.eval_lvalue(lvalue)?;
|
||||
self.memory.write_ptr(dest, lvalue.ptr)?;
|
||||
let extra_ptr = dest.offset(self.memory.pointer_size() as isize);
|
||||
match lvalue.extra {
|
||||
LvalueExtra::None => {},
|
||||
LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?,
|
||||
LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?,
|
||||
LvalueExtra::DowncastVariant(..) =>
|
||||
bug!("attempted to take a reference to an enum downcast lvalue"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -762,7 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
use rustc::mir::repr::Operand::*;
|
||||
match *op {
|
||||
Consume(ref lvalue) => self.eval_lvalue(lvalue),
|
||||
Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())),
|
||||
|
||||
Constant(mir::Constant { ref literal, ty, .. }) => {
|
||||
use rustc::mir::repr::Literal;
|
||||
|
|
@ -802,14 +821,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
|
||||
use rustc::mir::repr::Lvalue::*;
|
||||
let value = match *lvalue {
|
||||
ReturnPointer => Value::ByRef(self.frame().return_ptr
|
||||
.expect("ReturnPointer used in a function with no return value")),
|
||||
Arg(i) => Value::ByRef(self.frame().locals[i.index()]),
|
||||
Var(i) => Value::ByRef(self.frame().locals[self.frame().var_offset + i.index()]),
|
||||
Temp(i) => Value::ByRef(self.frame().locals[self.frame().temp_offset + i.index()]),
|
||||
let ptr = match *lvalue {
|
||||
ReturnPointer => self.frame().return_ptr
|
||||
.expect("ReturnPointer used in a function with no return value"),
|
||||
Arg(i) => self.frame().locals[i.index()],
|
||||
Var(i) => self.frame().locals[self.frame().var_offset + i.index()],
|
||||
Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()],
|
||||
|
||||
Static(def_id) => {
|
||||
let substs = subst::Substs::empty(self.tcx);
|
||||
|
|
@ -818,17 +837,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
substs: substs,
|
||||
kind: ConstantKind::Global,
|
||||
};
|
||||
Value::ByRef(*self.statics.get(&cid).expect("static should have been cached (lvalue)"))
|
||||
*self.statics.get(&cid).expect("static should have been cached (lvalue)")
|
||||
},
|
||||
|
||||
Projection(ref proj) => {
|
||||
let base = self.eval_lvalue(&proj.base)?;
|
||||
trace!("projection base: {:?}", base);
|
||||
trace!("projection: {:?}", proj.elem);
|
||||
match base {
|
||||
Value::ByRef(ptr) => self.memory.dump(ptr.alloc_id),
|
||||
_ => {},
|
||||
}
|
||||
let base_ty = self.lvalue_ty(&proj.base);
|
||||
let base_layout = self.type_layout(base_ty);
|
||||
|
||||
|
|
@ -840,12 +853,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let variant = match *base_layout {
|
||||
Univariant { ref variant, .. } => variant,
|
||||
General { ref variants, .. } => {
|
||||
if let Value::ByValPair(PrimVal::Ptr(ptr), variant_idx) = base {
|
||||
// early exit, because enum variant field access is passed
|
||||
// as a non-fat-pointer ByValPair
|
||||
let idx = variant_idx.expect_uint("enum variant id not integral") as usize;
|
||||
let offset = variants[idx].field_offset(field.index()).bytes() as isize;
|
||||
return Ok(ByRef(ptr.offset(offset)));
|
||||
if let LvalueExtra::DowncastVariant(variant_idx) = base.extra {
|
||||
&variants[variant_idx]
|
||||
} else {
|
||||
bug!("field access on enum had no variant index");
|
||||
}
|
||||
|
|
@ -858,21 +867,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
_ => bug!("field access on non-product type: {:?}", base_layout),
|
||||
};
|
||||
|
||||
let offset = variant.field_offset(field.index()).bytes() as isize;
|
||||
use self::value::Value::*;
|
||||
match base {
|
||||
ByRef(ptr) => if self.type_is_fat_ptr(field_ty) {
|
||||
self.read_value(ptr.offset(offset), field_ty)?
|
||||
} else {
|
||||
ByRef(ptr.offset(offset))
|
||||
},
|
||||
// indexing into a field of an unsized struct
|
||||
ByValPair(PrimVal::Ptr(ptr), extra) => if self.type_is_sized(field_ty) {
|
||||
ByRef(ptr.offset(offset))
|
||||
} else {
|
||||
ByValPair(PrimVal::Ptr(ptr.offset(offset)), extra)
|
||||
},
|
||||
other => bug!("expected thin ptr, got: {:?}", other),
|
||||
let offset = variant.field_offset(field.index()).bytes();
|
||||
let ptr = base.ptr.offset(offset as isize);
|
||||
trace!("{:?}", base);
|
||||
trace!("{:?}", field_ty);
|
||||
if self.type_is_sized(field_ty) {
|
||||
ptr
|
||||
} else {
|
||||
match base.extra {
|
||||
LvalueExtra::None => bug!("expected fat pointer"),
|
||||
LvalueExtra::DowncastVariant(..) => bug!("Rust doesn't support unsized fields in enum variants"),
|
||||
LvalueExtra::Vtable(_) |
|
||||
LvalueExtra::Length(_) => {},
|
||||
}
|
||||
return Ok(Lvalue {
|
||||
ptr: ptr,
|
||||
extra: base.extra,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -880,14 +891,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
use rustc::ty::layout::Layout::*;
|
||||
match *base_layout {
|
||||
General { ref variants, .. } => {
|
||||
use self::value::Value::*;
|
||||
match base {
|
||||
ByRef(ptr) => return Ok(ByValPair(
|
||||
PrimVal::Ptr(ptr.offset(variants[variant].field_offset(1).bytes() as isize)),
|
||||
PrimVal::U64(variant as u64),
|
||||
)),
|
||||
other => bug!("bad downcast base: {:?}", other),
|
||||
}
|
||||
return Ok(Lvalue {
|
||||
ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize),
|
||||
extra: LvalueExtra::DowncastVariant(variant),
|
||||
});
|
||||
}
|
||||
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
|
||||
return Ok(base);
|
||||
|
|
@ -896,14 +903,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
},
|
||||
|
||||
Deref => match self.follow_ref(base, base_ty)? {
|
||||
Value::ByRef(..) => bug!("follow_ref broken"),
|
||||
// magical deref
|
||||
Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr),
|
||||
Value::ByVal(..) => bug!("can't deref non pointer types"),
|
||||
// deref ops on fat pointers are no-ops
|
||||
pair @ Value::ByValPair(..) => pair,
|
||||
},
|
||||
Deref => {
|
||||
use primval::PrimVal::*;
|
||||
use interpreter::value::Value::*;
|
||||
let (ptr, extra) = match self.read_value(base.ptr, base_ty)? {
|
||||
ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)),
|
||||
ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))),
|
||||
ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None),
|
||||
_ => bug!("can't deref non pointer types"),
|
||||
};
|
||||
return Ok(Lvalue { ptr: ptr, extra: extra });
|
||||
}
|
||||
|
||||
Index(ref operand) => {
|
||||
let elem_size = match base_ty.sty {
|
||||
|
|
@ -914,12 +924,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let n_ptr = self.eval_operand(operand)?;
|
||||
let usize = self.tcx.types.usize;
|
||||
let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize");
|
||||
match base {
|
||||
Value::ByRef(ptr) |
|
||||
Value::ByValPair(PrimVal::Ptr(ptr), _) |
|
||||
Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr.offset(n as isize * elem_size as isize)),
|
||||
other => bug!("index op on {:?}", other),
|
||||
}
|
||||
base.ptr.offset(n as isize * elem_size as isize)
|
||||
}
|
||||
|
||||
ConstantIndex { .. } => unimplemented!(),
|
||||
|
|
@ -927,7 +932,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
};
|
||||
Ok(value)
|
||||
|
||||
Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None })
|
||||
}
|
||||
|
||||
fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
|
||||
|
|
@ -1002,14 +1008,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// ensures that this value isn't a `ByRef` anymore
|
||||
fn follow_ref(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
match value {
|
||||
Value::ByRef(ptr) => self.read_value(ptr, ty),
|
||||
other => Ok(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
let val = match &ty.sty {
|
||||
|
|
@ -1112,6 +1110,13 @@ fn pointee_type(ptr_ty: ty::Ty) -> Option<ty::Ty> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Lvalue {
|
||||
fn to_ptr(self) -> Pointer {
|
||||
assert_eq!(self.extra, LvalueExtra::None);
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
|
||||
type Target = mir::Mir<'tcx>;
|
||||
fn deref(&self) -> &mir::Mir<'tcx> {
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
SwitchInt { ref discr, ref values, ref targets, .. } => {
|
||||
let discr_ptr = self.eval_lvalue(discr)?;
|
||||
let discr_ptr = self.eval_lvalue(discr)?.to_ptr();
|
||||
let discr_ty = self.lvalue_ty(discr);
|
||||
let discr_size = self
|
||||
.type_layout(discr_ty)
|
||||
.size(&self.tcx.data_layout)
|
||||
.bytes() as usize;
|
||||
let discr_val = discr_ptr.read_uint(&self.memory, discr_size)?;
|
||||
let discr_val = self.memory.read_uint(discr_ptr, discr_size)?;
|
||||
if let ty::TyChar = discr_ty.sty {
|
||||
if ::std::char::from_u32(discr_val as u32).is_none() {
|
||||
return Err(EvalError::InvalidChar(discr_val as u64));
|
||||
|
|
|
|||
|
|
@ -30,27 +30,6 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_uint<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>, size: usize) -> EvalResult<'tcx, u64> {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
ByRef(ptr) => mem.read_uint(ptr, size),
|
||||
ByVal(PrimVal::U8(u)) => Ok(u as u64),
|
||||
ByVal(PrimVal::U16(u)) => Ok(u as u64),
|
||||
ByVal(PrimVal::U32(u)) => Ok(u as u64),
|
||||
ByVal(PrimVal::U64(u)) => Ok(u as u64),
|
||||
ByValPair(..) => unimplemented!(),
|
||||
ByVal(_other) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_ptr(&self) -> Pointer {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
ByRef(ptr) => ptr,
|
||||
other => bug!("expected pointer, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue