Merge pull request #103 from oli-obk/tuples
allow using tuple variant names as function handles
This commit is contained in:
commit
b490a4077c
6 changed files with 154 additions and 32 deletions
|
@ -8,7 +8,7 @@ extern crate rustc_errors;
|
|||
extern crate env_logger;
|
||||
extern crate log_settings;
|
||||
extern crate syntax;
|
||||
#[macro_use] extern crate log;
|
||||
extern crate log;
|
||||
|
||||
use rustc::session::Session;
|
||||
use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls};
|
||||
|
|
|
@ -223,7 +223,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
|
||||
let substituted = ty.subst(self.tcx, substs);
|
||||
// miri doesn't care about lifetimes, and will choke on some crazy ones
|
||||
// let's simply get rid of them
|
||||
let without_lifetimes = self.tcx.erase_regions(&ty);
|
||||
let substituted = without_lifetimes.subst(self.tcx, substs);
|
||||
self.tcx.normalize_associated_type(&substituted)
|
||||
}
|
||||
|
||||
|
@ -355,18 +358,44 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_fields<I: IntoIterator<Item = u64>>(
|
||||
pub fn assign_discr_and_fields<
|
||||
I: IntoIterator<Item = u64>,
|
||||
V: IntoValTyPair<'tcx>,
|
||||
J: IntoIterator<Item = V>,
|
||||
>(
|
||||
&mut self,
|
||||
dest: Lvalue<'tcx>,
|
||||
offsets: I,
|
||||
operands: &[mir::Operand<'tcx>],
|
||||
operands: J,
|
||||
discr_val: u128,
|
||||
discr_size: u64,
|
||||
) -> EvalResult<'tcx, ()> {
|
||||
// FIXME(solson)
|
||||
let dest_ptr = self.force_allocation(dest)?.to_ptr();
|
||||
|
||||
let mut offsets = offsets.into_iter();
|
||||
let discr_offset = offsets.next().unwrap();
|
||||
let discr_dest = dest_ptr.offset(discr_offset);
|
||||
self.memory.write_uint(discr_dest, discr_val, discr_size)?;
|
||||
|
||||
self.assign_fields(dest, offsets, operands)
|
||||
}
|
||||
|
||||
pub fn assign_fields<
|
||||
I: IntoIterator<Item = u64>,
|
||||
V: IntoValTyPair<'tcx>,
|
||||
J: IntoIterator<Item = V>,
|
||||
>(
|
||||
&mut self,
|
||||
dest: Lvalue<'tcx>,
|
||||
offsets: I,
|
||||
operands: J,
|
||||
) -> EvalResult<'tcx, ()> {
|
||||
// FIXME(solson)
|
||||
let dest = self.force_allocation(dest)?.to_ptr();
|
||||
|
||||
for (offset, operand) in offsets.into_iter().zip(operands) {
|
||||
let value = self.eval_operand(operand)?;
|
||||
let value_ty = self.operand_ty(operand);
|
||||
let (value, value_ty) = operand.into_val_ty_pair(self)?;
|
||||
let field_dest = dest.offset(offset);
|
||||
self.write_value_to_ptr(value, field_dest, value_ty)?;
|
||||
}
|
||||
|
@ -431,18 +460,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind {
|
||||
let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked();
|
||||
let discr_size = discr.size().bytes();
|
||||
let discr_offset = variants[variant].offsets[0].bytes();
|
||||
|
||||
// FIXME(solson)
|
||||
let dest = self.force_allocation(dest)?;
|
||||
let discr_dest = (dest.to_ptr()).offset(discr_offset);
|
||||
|
||||
self.memory.write_uint(discr_dest, discr_val, discr_size)?;
|
||||
|
||||
// Don't include the first offset; it's for the discriminant.
|
||||
let field_offsets = variants[variant].offsets.iter().skip(1)
|
||||
.map(|s| s.bytes());
|
||||
self.assign_fields(dest, field_offsets, operands)?;
|
||||
self.assign_discr_and_fields(
|
||||
dest,
|
||||
variants[variant].offsets.iter().cloned().map(Size::bytes),
|
||||
operands,
|
||||
discr_val,
|
||||
discr_size,
|
||||
)?;
|
||||
} else {
|
||||
bug!("tried to assign {:?} to Layout::General", kind);
|
||||
}
|
||||
|
@ -660,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)?;
|
||||
|
@ -722,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))
|
||||
|
@ -736,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))
|
||||
|
@ -1464,3 +1496,21 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::
|
|||
pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty()
|
||||
}
|
||||
|
||||
pub trait IntoValTyPair<'tcx> {
|
||||
fn into_val_ty_pair<'a>(self, ecx: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a;
|
||||
}
|
||||
|
||||
impl<'tcx> IntoValTyPair<'tcx> for (Value, Ty<'tcx>) {
|
||||
fn into_val_ty_pair<'a>(self, _: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'tcx: 'b> IntoValTyPair<'tcx> for &'b mir::Operand<'tcx> {
|
||||
fn into_val_ty_pair<'a>(self, ecx: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a {
|
||||
let value = ecx.eval_operand(self)?;
|
||||
let value_ty = ecx.operand_ty(self);
|
||||
Ok((value, value_ty))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc::hir::def_id::DefId;
|
|||
use rustc::mir;
|
||||
use rustc::traits::{self, Reveal};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::layout::Layout;
|
||||
use rustc::ty::layout::{Layout, Size};
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
use rustc::ty::{self, Ty, TyCtxt, BareFnTy};
|
||||
use syntax::codemap::{DUMMY_SP, Span};
|
||||
|
@ -238,20 +238,51 @@ 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)?;
|
||||
trace!("layout({:?}) = {:#?}", dest_ty, dest_layout);
|
||||
match *dest_layout {
|
||||
Layout::Univariant { ref variant, .. } => {
|
||||
assert_eq!(v.disr_val.to_u128_unchecked(), 0);
|
||||
let disr_val = v.disr_val.to_u128_unchecked();
|
||||
assert_eq!(disr_val, 0);
|
||||
let offsets = variant.offsets.iter().map(|s| s.bytes());
|
||||
|
||||
// FIXME: don't allocate for single or dual field structs
|
||||
let dest = self.force_allocation(lvalue)?.to_ptr();
|
||||
self.assign_fields(lvalue, offsets, args)?;
|
||||
},
|
||||
Layout::General { discr, ref variants, .. } => {
|
||||
let disr_val = v.disr_val.to_u128_unchecked();
|
||||
let discr_size = discr.size().bytes();
|
||||
self.assign_discr_and_fields(
|
||||
lvalue,
|
||||
variants[disr_val as usize].offsets.iter().cloned().map(Size::bytes),
|
||||
args,
|
||||
disr_val,
|
||||
discr_size,
|
||||
)?;
|
||||
},
|
||||
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)?;
|
||||
|
||||
for (offset, (value, value_ty)) in offsets.into_iter().zip(args) {
|
||||
let field_dest = dest.offset(offset);
|
||||
self.write_value_to_ptr(value, field_dest, value_ty)?;
|
||||
// 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)?;
|
||||
}
|
||||
},
|
||||
// FIXME: enum variant constructors
|
||||
Layout::RawNullablePointer { .. } => {
|
||||
assert_eq!(args.len(), 1);
|
||||
let (val, ty) = args.pop().unwrap();
|
||||
self.write_value(val, lvalue, ty)?;
|
||||
},
|
||||
_ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout),
|
||||
}
|
||||
self.goto_block(target);
|
||||
|
@ -295,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, .. } => {
|
||||
|
@ -332,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,
|
||||
|
|
3
tests/run-pass/tuple_like_enum_variant_constructor.rs
Normal file
3
tests/run-pass/tuple_like_enum_variant_constructor.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
assert_eq!(Some(42).map(Some), Some(Some(42)));
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
let x = 5;
|
||||
assert_eq!(Some(&x).map(Some), Some(Some(&x)));
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
struct A<'a> {
|
||||
x: i32,
|
||||
y: &'a i32,
|
||||
}
|
||||
|
||||
#[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 };
|
||||
assert_eq!(Some(a).map(Some), Some(Some(a)));
|
||||
let f = B;
|
||||
assert_eq!(Some(B(42, &x)), Some(f(42, &x)));
|
||||
// the following doesn't compile :(
|
||||
//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> {
|
||||
f(42, i)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue