correctly implement pointers to enum variant constructors

This commit is contained in:
Oliver Schneider 2017-01-28 15:46:46 +01:00
parent b6e79dbbf5
commit ce95ae5927
3 changed files with 50 additions and 14 deletions

View File

@ -685,22 +685,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let path = discrfield.iter().skip(2).map(|&i| i as usize);
// Handle the field index for the outer non-null variant.
let inner_ty = match ty.sty {
let (inner_offset, inner_ty) = match ty.sty {
ty::TyAdt(adt_def, substs) => {
let variant = &adt_def.variants[nndiscr as usize];
let index = discrfield[1];
let field = &variant.fields[index as usize];
field.ty(self.tcx, substs)
(self.get_field_offset(ty, index as usize)?, field.ty(self.tcx, substs))
}
_ => bug!("non-enum for StructWrappedNullablePointer: {}", ty),
};
self.field_path_offset_and_ty(inner_ty, path)
self.field_path_offset_and_ty(inner_offset, inner_ty, path)
}
fn field_path_offset_and_ty<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
let mut offset = Size::from_bytes(0);
fn field_path_offset_and_ty<I: Iterator<Item = usize>>(
&self,
mut offset: Size,
mut ty: Ty<'tcx>,
path: I,
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
// Skip the initial 0 intended for LLVM GEP.
for field_index in path {
let field_offset = self.get_field_offset(ty, field_index)?;
@ -747,6 +750,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let bytes = field_index as u64 * self.memory.pointer_size();
Ok(Size::from_bytes(bytes))
}
StructWrappedNullablePointer { ref nonnull, .. } => {
Ok(nonnull.offsets[field_index])
}
_ => {
let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout);
Err(EvalError::Unimplemented(msg))
@ -761,6 +767,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match *layout {
Univariant { ref variant, .. } => Ok(variant.offsets.len()),
FatPointer { .. } => Ok(2),
StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()),
_ => {
let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout);
Err(EvalError::Unimplemented(msg))

View File

@ -238,27 +238,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let (lvalue, target) = destination.expect("tuple struct constructors can't diverge");
let dest_ty = self.tcx.item_type(adt_def.did);
let dest_layout = self.type_layout(dest_ty)?;
let disr = v.disr_val.to_u128_unchecked();
trace!("layout({:?}) = {:#?}", dest_ty, dest_layout);
match *dest_layout {
Layout::Univariant { ref variant, .. } => {
assert_eq!(disr, 0);
let disr_val = v.disr_val.to_u128_unchecked();
assert_eq!(disr_val, 0);
let offsets = variant.offsets.iter().map(|s| s.bytes());
self.assign_fields(lvalue, offsets, args)?;
},
Layout::General { discr, ref variants, .. } => {
// FIXME: report a proper error for invalid discriminants
// right now we simply go into index out of bounds
let disr_val = v.disr_val.to_u128_unchecked();
let discr_size = discr.size().bytes();
self.assign_discr_and_fields(
lvalue,
variants[disr as usize].offsets.iter().cloned().map(Size::bytes),
variants[disr_val as usize].offsets.iter().cloned().map(Size::bytes),
args,
disr,
disr_val,
discr_size,
)?;
},
Layout::StructWrappedNullablePointer { .. } |
Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => {
let disr_val = v.disr_val.to_u128_unchecked();
if nndiscr as u128 == disr_val {
let offsets = nonnull.offsets.iter().map(|s| s.bytes());
self.assign_fields(lvalue, offsets, args)?;
} else {
for (_, ty) in args {
assert_eq!(self.type_size(ty)?, Some(0));
}
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
// FIXME(solson)
let dest = self.force_allocation(lvalue)?.to_ptr();
let dest = dest.offset(offset.bytes());
let dest_size = self.type_size(ty)?
.expect("bad StructWrappedNullablePointer discrfield");
self.memory.write_int(dest, 0, dest_size)?;
}
},
Layout::RawNullablePointer { .. } => {
assert_eq!(args.len(), 1);
let (val, ty) = args.pop().unwrap();
@ -307,7 +326,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
use rustc::ty::layout::Layout::*;
let adt_layout = self.type_layout(adt_ty)?;
trace!("read_discriminant_value {:?}", adt_layout);
trace!("read_discriminant_value {:#?}", adt_layout);
let discr_val = match *adt_layout {
General { discr, .. } | CEnum { discr, signed: false, .. } => {
@ -344,6 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> {
trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size);
let not_null = match self.memory.read_uint(ptr, discr_size) {
Ok(0) => false,
Ok(_) | Err(EvalError::ReadPointerAsBytes) => true,

View File

@ -7,6 +7,13 @@ struct A<'a> {
#[derive(Copy, Clone, PartialEq, Debug)]
struct B<'a>(i32, &'a i32);
#[derive(Copy, Clone, PartialEq, Debug)]
enum C<'a> {
Value(i32, &'a i32),
#[allow(dead_code)]
NoValue,
}
fn main() {
let x = 5;
let a = A { x: 99, y: &x };
@ -17,6 +24,8 @@ fn main() {
//let f: for<'a> fn(i32, &'a i32) -> B<'a> = B;
//assert_eq!(Some(B(42, &x)), Some(f(42, &x)));
assert_eq!(B(42, &x), foo(&x, B));
let f = C::Value;
assert_eq!(C::Value(42, &x), f(42, &x));
}
fn foo<'a, F: Fn(i32, &'a i32) -> B<'a>>(i: &'a i32, f: F) -> B<'a> {