diff --git a/src/eval_context.rs b/src/eval_context.rs index 739274ce39..d2d33610f2 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -851,17 +851,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lvalue: Lvalue<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { - Lvalue::Local { frame, local } => { - match self.stack[frame].get_local(local) { - Value::ByRef(ptr) => Lvalue::from_ptr(ptr), + Lvalue::Local { frame, local, field } => { + // -1 since we don't store the return value + match self.stack[frame].locals[local.index() - 1] { + Value::ByRef(ptr) => { + assert!(field.is_none()); + Lvalue::from_ptr(ptr) + }, val => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].substs); let substs = self.stack[frame].substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].set_local(local, Value::ByRef(ptr)); + self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.write_value_to_ptr(val, ptr, ty)?; - Lvalue::from_ptr(ptr) + let lval = Lvalue::from_ptr(ptr); + if let Some((field, field_ty)) = field { + self.lvalue_field(lval, field, ty, field_ty)? + } else { + lval + } } } } @@ -924,8 +933,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(ptr, val, size) } - Lvalue::Local { frame, local } => { - self.stack[frame].set_local(local, Value::ByVal(val)); + Lvalue::Local { frame, local, field } => { + self.stack[frame].set_local(local, field.map(|(i, _)| i), Value::ByVal(val)); Ok(()) } Lvalue::Global(cid) => { @@ -966,11 +975,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(src_val, ptr, dest_ty) } - Lvalue::Local { frame, local } => { - let dest = self.stack[frame].get_local(local); + Lvalue::Local { frame, local, field } => { + let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i)); self.write_value_possibly_by_val( src_val, - |this, val| this.stack[frame].set_local(local, val), + |this, val| this.stack[frame].set_local(local, field.map(|(i, _)| i), val), dest, dest_ty, ) @@ -1355,16 +1364,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { - if let Lvalue::Local { frame, local } = lvalue { + if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); + if let Some((field, _)) = field { + write!(msg, ".{}", field).unwrap(); + } let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } write!(msg, ":").unwrap(); - match self.stack[frame].get_local(local) { + match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { Value::ByRef(ptr) => { allocs.push(ptr.alloc_id); } @@ -1402,13 +1414,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, frame: usize, local: mir::Local, + field: Option, f: F, ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let val = self.stack[frame].get_local(local); + let val = self.stack[frame].get_local(local, field); let new_val = f(self, val)?; - self.stack[frame].set_local(local, new_val); + self.stack[frame].set_local(local, field, new_val); // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; @@ -1418,14 +1431,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { - pub fn get_local(&self, local: mir::Local) -> Value { + pub fn get_local(&self, local: mir::Local, field: Option) -> Value { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + val @ Value::ByVal(_) => { + assert_eq!(field, 0); + val + }, + Value::ByValPair(a, b) => { + match field { + 0 => Value::ByVal(a), + 1 => Value::ByVal(b), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] + } } - fn set_local(&mut self, local: mir::Local, value: Value) { + fn set_local(&mut self, local: mir::Local, field: Option, value: Value) { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] = value; + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + Value::ByVal(_) => { + assert_eq!(field, 0); + self.set_local(local, None, value); + }, + Value::ByValPair(a, b) => { + let prim = match value { + Value::ByRef(_) => bug!("can't set ValPair field to ByRef"), + Value::ByVal(val) => val, + Value::ByValPair(_, _) => bug!("can't set ValPair field to ValPair"), + }; + match field { + 0 => self.set_local(local, None, Value::ByValPair(prim, b)), + 1 => self.set_local(local, None, Value::ByValPair(a, prim)), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] = value; + } } } diff --git a/src/lvalue.rs b/src/lvalue.rs index 5ac5ad78b8..f3f8c3fde0 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -23,6 +23,8 @@ pub enum Lvalue<'tcx> { Local { frame: usize, local: mir::Local, + /// Optionally, this lvalue can point to a field of the stack value + field: Option<(usize, Ty<'tcx>)>, }, /// An lvalue referring to a global @@ -114,16 +116,6 @@ impl<'tcx> Global<'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { - if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Local(index) = proj.base { - if let Value::ByValPair(a, b) = self.frame().get_local(index) { - if let mir::ProjectionElem::Field(ref field, _) = proj.elem { - let val = [a, b][field.index()]; - return Ok(Value::ByVal(val)); - } - } - } - } let lvalue = self.eval_lvalue(lvalue)?; Ok(self.read_lvalue(lvalue)) } @@ -134,7 +126,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(extra, LvalueExtra::None); Value::ByRef(ptr) } - Lvalue::Local { frame, local } => self.stack[frame].get_local(local), + Lvalue::Local { frame, local, field } => self.stack[frame].get_local(local, field.map(|(i, _)| i)), Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value, } } @@ -143,7 +135,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local }, + Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(def_id) => { let substs = self.tcx.intern_substs(&[]); @@ -163,45 +155,41 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn lvalue_field( &mut self, base: Lvalue<'tcx>, - field: usize, + field_index: usize, base_ty: Ty<'tcx>, field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { Univariant { ref variant, .. } => { - (variant.offsets[field], variant.packed) + (variant.offsets[field_index], variant.packed) }, General { ref variants, .. } => { + let (_, base_extra) = base.to_ptr_and_extra(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 - (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) + (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed) } else { bug!("field access on enum had no variant index"); } } RawNullablePointer { .. } => { - assert_eq!(field, 0); + assert_eq!(field_index, 0); return Ok(base); } StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field], nonnull.packed) + (nonnull.offsets[field_index], nonnull.packed) } UntaggedUnion { .. } => return Ok(base), Vector { element, count } => { - let field = field as u64; + let field = field_index as u64; assert!(field < count); let elem_size = element.size(&self.tcx.data_layout).bytes(); (Size::from_bytes(field * elem_size), false) @@ -210,6 +198,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; + let (base_ptr, base_extra) = match base { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { + Value::ByRef(ptr) => { + assert!(field.is_none(), "local can't be ByRef and have a field offset"); + (ptr, LvalueExtra::None) + }, + Value::ByVal(_) => { + assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + }, + Value::ByValPair(_, _) => { + assert!(field_index < 2); + return Ok(Lvalue::Local { + frame, + local, + field: Some((field_index, field_ty)), + }); + }, + }, + // FIXME: do for globals what we did for locals + Lvalue::Global(_) => self.force_allocation(base)?.to_ptr_and_extra(), + }; + let offset = match base_extra { LvalueExtra::Vtable(tab) => { let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; @@ -220,6 +232,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = base_ptr.offset(offset); + let field_ty = self.monomorphize(field_ty, self.substs()); + if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); self.memory.mark_packed(ptr, size); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2798790fe..f7e34d2bc6 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(zero_val) }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, @@ -386,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"),