diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 0f8bb68549b..eb7db8b354e 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -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}; diff --git a/src/eval_context.rs b/src/eval_context.rs index e02ea09be86..ce44e34f9ed 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -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>( + pub fn assign_discr_and_fields< + I: IntoIterator, + V: IntoValTyPair<'tcx>, + J: IntoIterator, + >( &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, + V: IntoValTyPair<'tcx>, + J: IntoIterator, + >( + &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>(&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>( + &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)) + } +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 11082c633a7..b2124486f49 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -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, diff --git a/tests/run-pass/tuple_like_enum_variant_constructor.rs b/tests/run-pass/tuple_like_enum_variant_constructor.rs new file mode 100644 index 00000000000..5cf91b3f4d1 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor.rs @@ -0,0 +1,3 @@ +fn main() { + assert_eq!(Some(42).map(Some), Some(Some(42))); +} diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs new file mode 100644 index 00000000000..fb57d4f4c16 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 5; + assert_eq!(Some(&x).map(Some), Some(Some(&x))); +} diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs new file mode 100644 index 00000000000..44441ed1d36 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs @@ -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) +}