From c6a18cead8885b229f5c7bc5d11cf6ed42d4dc6c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Mar 2017 09:21:51 +0100 Subject: [PATCH 01/31] Rustup to rustc 1.17.0-nightly (134c4a0f0 2017-03-20) --- src/bin/miri.rs | 6 +++--- src/lib.rs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f1a97d0a44b5..98ad4e9f9b36 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -82,7 +82,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> { fn visit_item(&mut self, i: &'hir hir::Item) { if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { - if i.attrs.iter().any(|attr| attr.value.name == "test") { + if i.attrs.iter().any(|attr| attr.name().map_or(true, |name| name == "test")) { let did = self.1.hir.body_owner_def_id(body_id); println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); miri::eval_main(self.1, did, self.0); @@ -117,8 +117,8 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits } }; - for attr in krate.attrs.iter().filter(|a| a.name() == "miri") { - if let MetaItemKind::List(ref items) = attr.value.node { + for attr in krate.attrs.iter().filter(|a| a.name().map_or(true, |n| n == "miri")) { + if let Some(ref items) = attr.meta_item_list() { for item in items { if let NestedMetaItemKind::MetaItem(ref inner) = item.node { if let MetaItemKind::NameValue(ref value) = inner.node { diff --git a/src/lib.rs b/src/lib.rs index d1d8e8cf229f..b7303566384b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,4 @@ #![feature( - btree_range, - collections, i128_type, pub_restricted, rustc_private, From dc1b0fb436da6d7e391e14b3a98cd5f96b2726bb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Mar 2017 13:53:55 +0100 Subject: [PATCH 02/31] Compiles again --- src/error.rs | 11 +- src/eval_context.rs | 168 ++++++++++++++--------- src/lvalue.rs | 18 +-- src/memory.rs | 124 ++--------------- src/step.rs | 30 ++--- src/terminator/drop.rs | 224 ------------------------------ src/terminator/intrinsic.rs | 79 ++++------- src/terminator/mod.rs | 219 ++++-------------------------- src/traits.rs | 262 ++++-------------------------------- 9 files changed, 212 insertions(+), 923 deletions(-) delete mode 100644 src/terminator/drop.rs diff --git a/src/error.rs b/src/error.rs index 370d59e5a3a3..fd692ef8b64a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt; use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; -use memory::{Pointer, Function}; +use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -52,9 +52,6 @@ pub enum EvalError<'tcx> { DeallocatedStaticMemory, Layout(layout::LayoutError<'tcx>), Unreachable, - ExpectedConcreteFunction(Function<'tcx>), - ExpectedDropGlue(Function<'tcx>), - ManuallyCalledDropGlue, Panic, } @@ -128,12 +125,6 @@ impl<'tcx> Error for EvalError<'tcx> { "attempted to get length of a null terminated string, but no null found before end of allocation", EvalError::Unreachable => "entered unreachable code", - EvalError::ExpectedConcreteFunction(_) => - "tried to use glue function as function", - EvalError::ExpectedDropGlue(_) => - "tried to use non-drop-glue function as drop glue", - EvalError::ManuallyCalledDropGlue => - "tried to manually invoke drop glue", EvalError::Panic => "the evaluated program panicked", } diff --git a/src/eval_context.rs b/src/eval_context.rs index 011a25774aca..9b2e5e1ceaba 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -8,7 +8,7 @@ use rustc::middle::const_val::ConstVal; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; -use rustc::ty::subst::{self, Subst, Substs}; +use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; @@ -52,11 +52,8 @@ pub struct Frame<'tcx> { /// The MIR for the function called on this frame. pub mir: MirRef<'tcx>, - /// The def_id of the current function. - pub def_id: DefId, - - /// type substitutions for the current function invocation. - pub substs: &'tcx Substs<'tcx>, + /// The def_id and substs of the current function + pub instance: ty::Instance<'tcx>, /// The span of the call site. pub span: codemap::Span, @@ -78,12 +75,6 @@ pub struct Frame<'tcx> { /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`. pub locals: Vec, - /// Temporary allocations introduced to save stackframes - /// This is pure interpreter magic and has nothing to do with how rustc does it - /// An example is calling an FnMut closure that has been converted to a FnOnce closure - /// The value's destructor will be called and the memory freed when the stackframe finishes - pub interpreter_temporaries: Vec<(Pointer, Ty<'tcx>)>, - //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// @@ -208,12 +199,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, MirRef<'tcx>> { - trace!("load mir {:?}", def_id); - if def_id.is_local() || self.tcx.sess.cstore.is_item_mir_available(def_id) { - Ok(self.tcx.item_mir(def_id)) - } else { - Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id))) + pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, MirRef<'tcx>> { + trace!("load mir {:?}", instance); + match instance { + ty::InstanceDef::Item(def_id) => self.tcx.maybe_item_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))), + _ => Ok(self.tcx.instance_mir(instance)), } } @@ -272,13 +262,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn push_stack_frame( &mut self, - def_id: DefId, + instance: ty::Instance<'tcx>, span: codemap::Span, mir: MirRef<'tcx>, - substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, - temporaries: Vec<(Pointer, Ty<'tcx>)>, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -293,10 +281,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, return_lvalue, locals, - interpreter_temporaries: temporaries, span, - def_id, - substs, + instance, stmt: 0, }); @@ -352,13 +338,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } - // drop and deallocate all temporary allocations - for (ptr, ty) in frame.interpreter_temporaries { - trace!("dropping temporary allocation"); - let mut drops = Vec::new(); - self.drop(Lvalue::from_ptr(ptr), ty, &mut drops)?; - self.eval_drop_impls(drops, frame.span)?; - } + Ok(()) } @@ -665,8 +645,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ReifyFnPointer => match self.operand_ty(operand).sty { - ty::TyFnDef(def_id, substs, sig) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, sig); + ty::TyFnDef(def_id, substs, _) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -682,8 +662,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ClosureFnPointer => match self.operand_ty(operand).sty { ty::TyClosure(def_id, substs) => { - let fn_ty = self.tcx.closure_type(def_id); - let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(def_id, substs, fn_ty); + let instance = resolve_closure(self.tcx, def_id, substs, ty::ClosureKind::FnOnce); + let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -845,16 +825,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // function items are zero sized Value::ByRef(self.memory.allocate(0, 0)?) } else { - let (def_id, substs) = self.resolve_associated_const(def_id, substs); - let cid = GlobalId { def_id, substs, promoted: None }; + let instance = self.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; self.globals.get(&cid).expect("static/const not cached").value } } Literal::Promoted { index } => { let cid = GlobalId { - def_id: self.frame().def_id, - substs: self.substs(), + instance: self.frame().instance, promoted: Some(index), }; self.globals.get(&cid).expect("promoted not cached").value @@ -891,8 +870,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, 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 ty = self.monomorphize(ty, self.stack[frame].instance.substs); + let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.write_value_to_ptr(val, ptr, ty)?; @@ -911,7 +890,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match global_val.value { Value::ByRef(ptr) => Lvalue::from_ptr(ptr), _ => { - let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; + let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; // see comment on `initialized` field @@ -1289,7 +1268,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn substs(&self) -> &'tcx Substs<'tcx> { - self.frame().substs + self.frame().instance.substs } fn unsize_into_ptr( @@ -1320,7 +1299,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (_, &ty::TyDynamic(ref data, _)) => { let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); - let vtable = self.get_vtable(trait_ref)?; + let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; let ptr = PrimVal::Ptr(ptr); let extra = PrimVal::Ptr(vtable); @@ -1519,7 +1498,8 @@ pub fn eval_main<'a, 'tcx: 'a>( limits: ResourceLimits, ) { let mut ecx = EvalContext::new(tcx, limits); - let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); + let instance = ty::Instance::mono(tcx, def_id); + let mir = ecx.load_mir(instance.def).expect("main function's MIR not found"); if !mir.return_ty.is_nil() || mir.arg_count != 0 { let msg = "miri does not support main functions without `fn()` type signatures"; @@ -1528,13 +1508,11 @@ pub fn eval_main<'a, 'tcx: 'a>( } ecx.push_stack_frame( - def_id, + instance, DUMMY_SP, mir, - tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, - Vec::new(), ).expect("could not allocate first stack frame"); loop { @@ -1564,23 +1542,12 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { block.terminator().source_info.span }; let mut err = tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { - if tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { + for &Frame { instance, span, .. } in ecx.stack().iter().rev() { + if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { err.span_note(span, "inside call to closure"); continue; } - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); - impl<'tcx> ::std::panic::UnwindSafe for Instance<'tcx> {} - impl<'tcx> ::std::panic::RefUnwindSafe for Instance<'tcx> {} - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, &[]) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + err.span_note(span, &format!("inside call to {}", instance)); } err.emit(); } @@ -1657,3 +1624,80 @@ impl<'b, 'tcx: 'b> IntoValTyPair<'tcx> for &'b mir::Operand<'tcx> { Ok((value, value_ty)) } } + + +/// FIXME: expose trans::monomorphize::resolve_closure +pub fn resolve_closure<'a, 'tcx> ( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind, +) -> ty::Instance<'tcx> { + let actual_kind = tcx.closure_kind(def_id); + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), + _ => ty::Instance::new(def_id, substs.substs) + } +} + +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, +) -> ty::Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items.fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.closure_type(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + ty::Instance { def, substs } +} + +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} diff --git a/src/lvalue.rs b/src/lvalue.rs index 35fe958f7567..62855c045057 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,7 +1,5 @@ -use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::layout::{Size, Align}; -use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; @@ -42,15 +40,9 @@ pub enum LvalueExtra { /// Uniquely identifies a specific constant or static. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalId<'tcx> { - /// For a constant or static, the `DefId` of the item itself. - /// For a promoted global, the `DefId` of the function they belong to. - pub(super) def_id: DefId, - - /// For statics and constants this is `Substs::empty()`, so only promoteds and associated - /// constants actually have something useful here. We could special case statics and constants, - /// but that would only require more branching when working with constants, and not bring any - /// real benefits. - pub(super) substs: &'tcx Substs<'tcx>, + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub(super) instance: ty::Instance<'tcx>, /// The index for promoted globals within their function's `Mir`. pub(super) promoted: Option, @@ -138,8 +130,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(ref static_) => { - let substs = self.tcx.intern_substs(&[]); - Lvalue::Global(GlobalId { def_id: static_.def_id, substs, promoted: None }) + let instance = ty::Instance::mono(self.tcx, static_.def_id); + Lvalue::Global(GlobalId { instance, promoted: None }) } Projection(ref proj) => return self.eval_lvalue_projection(proj), diff --git a/src/memory.rs b/src/memory.rs index d6563c4c703c..058f8b478cac 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,7 +3,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet use std::{fmt, iter, ptr, mem, io}; use rustc::hir::def_id::DefId; -use rustc::ty::{self, PolyFnSig, ClosureSubsts}; +use rustc::ty; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; @@ -102,44 +102,6 @@ impl Pointer { } } -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] -/// Identifies a specific monomorphized function -pub struct FunctionDefinition<'tcx> { - pub def_id: DefId, - pub substs: &'tcx Substs<'tcx>, - pub sig: PolyFnSig<'tcx>, -} - -/// Either a concrete function, or a glue function -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] -pub enum Function<'tcx> { - /// A function or method created by compiling code - Concrete(FunctionDefinition<'tcx>), - /// Glue required to call a regular function through a Fn(Mut|Once) trait object - FnDefAsTraitObject(FunctionDefinition<'tcx>), - /// A drop glue function only needs to know the real type, and then miri can extract - /// that type from a vtable's drop pointer. - /// Instead of storing some drop function, we act as if there are no trait objects, by - /// mapping trait objects to their real types before acting on them. - DropGlue(ty::Ty<'tcx>), - /// Glue required to treat the ptr part of a fat pointer - /// as a function pointer - FnPtrAsTraitObject(PolyFnSig<'tcx>), - /// Glue for Closures - Closure(FunctionDefinition<'tcx>), - /// Glue for noncapturing closures casted to function pointers - NonCaptureClosureAsFnPtr(FunctionDefinition<'tcx>), -} - -impl<'tcx> Function<'tcx> { - pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> { - match self { - Function::DropGlue(real_ty) => Ok(real_ty), - other => Err(EvalError::ExpectedDropGlue(other)), - } - } -} - //////////////////////////////////////////////////////////////////////////////// // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// @@ -165,10 +127,10 @@ pub struct Memory<'a, 'tcx> { /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. - functions: HashMap>, + functions: HashMap>, /// Inverse map of `functions` so we don't allocate a new pointer every time we need one - function_alloc_cache: HashMap, AllocId>, + function_alloc_cache: HashMap, AllocId>, /// Target machine data layout to emulate. pub layout: &'a TargetDataLayout, @@ -214,55 +176,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::Closure(FunctionDefinition { - def_id, - substs: substs.substs, - sig, - })) + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { + let instance = ty::Instance::new(def_id, substs); + self.create_fn_alloc(instance) } - pub fn create_fn_ptr_from_noncapture_closure(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition { - def_id, - substs: substs.substs, - sig, - })) - } - - pub fn create_fn_as_trait_glue(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition { - def_id, - substs, - sig, - })) - } - - pub fn create_fn_ptr_as_trait_glue(&mut self, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::FnPtrAsTraitObject(sig)) - } - - pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer { - self.create_fn_alloc(Function::DropGlue(ty)) - } - - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::Concrete(FunctionDefinition { - def_id, - substs, - sig, - })) - } - - fn create_fn_alloc(&mut self, def: Function<'tcx>) -> Pointer { - if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { + pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer { + if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { return Pointer::new(alloc_id, 0); } let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; - self.functions.insert(id, def); - self.function_alloc_cache.insert(def, id); + self.functions.insert(id, instance); + self.function_alloc_cache.insert(instance, id); Pointer::new(id, 0) } @@ -469,7 +396,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, Function<'tcx>> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, ty::Instance<'tcx>> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { Some(&fndef) => Ok(fndef), @@ -501,28 +428,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { (Some(a), None) => a, - (None, Some(&Function::Concrete(fn_def))) => { - trace!("{} {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::DropGlue(real_ty))) => { - trace!("{} drop glue for {}", msg, real_ty); - continue; - }, - (None, Some(&Function::FnDefAsTraitObject(fn_def))) => { - trace!("{} fn as Fn glue for {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::FnPtrAsTraitObject(fn_def))) => { - trace!("{} fn ptr as Fn glue (signature: {:?})", msg, fn_def); - continue; - }, - (None, Some(&Function::Closure(fn_def))) => { - trace!("{} closure glue for {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::NonCaptureClosureAsFnPtr(fn_def))) => { - trace!("{} non-capture closure as fn ptr glue for {}", msg, dump_fn_def(fn_def)); + (None, Some(instance)) => { + trace!("{} {}", msg, instance); continue; }, (None, None) => { @@ -594,11 +501,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } -fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String { - let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); - format!("function pointer: {}: {}", name, fn_def.sig.skip_binder()) -} - /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { diff --git a/src/step.rs b/src/step.rs index 23f0142a7f53..8a0cbc4a8f1c 100644 --- a/src/step.rs +++ b/src/step.rs @@ -45,8 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut new = Ok(0); ConstantExtractor { span: stmt.source_info.span, - substs: self.substs(), - def_id: self.frame().def_id, + instance: self.frame().instance, ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, @@ -63,8 +62,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut new = Ok(0); ConstantExtractor { span: terminator.source_info.span, - substs: self.substs(), - def_id: self.frame().def_id, + instance: self.frame().instance, ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, @@ -145,8 +143,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, ecx: &'a mut EvalContext<'b, 'tcx>, mir: MirRef<'tcx>, - def_id: DefId, - substs: &'tcx subst::Substs<'tcx>, + instance: ty::Instance<'tcx>, new_constants: &'a mut EvalResult<'tcx, u64>, } @@ -158,26 +155,24 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { span: Span, shared: bool, ) { - let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs); - let cid = GlobalId { def_id, substs, promoted: None }; + let instance = self.ecx.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; if self.ecx.globals.contains_key(&cid) { return; } self.try(|this| { - let mir = this.ecx.load_mir(def_id)?; + let mir = this.ecx.load_mir(instance.def)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe(); let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); trace!("pushing stack frame for global: {}", name); this.ecx.push_stack_frame( - def_id, + instance, span, mir, - substs, Lvalue::Global(cid), cleanup, - Vec::new(), ) }); } @@ -210,8 +205,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { }, mir::Literal::Promoted { index } => { let cid = GlobalId { - def_id: self.def_id, - substs: self.substs, + instance: self.instance, promoted: Some(index), }; if self.ecx.globals.contains_key(&cid) { @@ -220,16 +214,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let mir = Ref::clone(&self.mir); let mir = Ref::map(mir, |mir| &mir.promoted[index]); self.try(|this| { - let ty = this.ecx.monomorphize(mir.return_ty, this.substs); + let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs); this.ecx.globals.insert(cid, Global::uninitialized(ty)); trace!("pushing stack frame for {:?}", index); - this.ecx.push_stack_frame(this.def_id, + this.ecx.push_stack_frame(this.instance, constant.span, mir, - this.substs, Lvalue::Global(cid), - StackPopCleanup::MarkStatic(false), - Vec::new()) + StackPopCleanup::MarkStatic(false)) }); } } diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs deleted file mode 100644 index efa415679328..000000000000 --- a/src/terminator/drop.rs +++ /dev/null @@ -1,224 +0,0 @@ -use rustc::hir::def_id::DefId; -use rustc::traits; -use rustc::ty::layout::Layout; -use rustc::ty::subst::{Substs, Kind}; -use rustc::ty::{self, Ty}; -use rustc::mir; -use syntax::codemap::Span; - -use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, monomorphize_field_ty, StackPopCleanup}; -use lvalue::{Lvalue, LvalueExtra}; -use memory::Pointer; -use value::PrimVal; -use value::Value; - -impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Creates stack frames for all drop impls. See `drop` for the actual content. - pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { - // add them to the stack in reverse order, because the impl that needs to run the last - // is the one that needs to be at the bottom of the stack - for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { - let mir = self.load_mir(drop_def_id)?; - trace!("substs for drop glue: {:?}", substs); - self.push_stack_frame( - drop_def_id, - span, - mir, - substs, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - Vec::new(), - )?; - let mut arg_locals = self.frame().mir.args_iter(); - let first = arg_locals.next().expect("drop impl has self arg"); - assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; - let ty = self.frame().mir.local_decls[first].ty; - self.write_value(self_arg, dest, ty)?; - } - Ok(()) - } - - /// push DefIds of drop impls and their argument on the given vector - pub fn drop( - &mut self, - lval: Lvalue<'tcx>, - ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> { - if !self.type_needs_drop(ty) { - debug!("no need to drop {:?}", ty); - return Ok(()); - } - trace!("need to drop {:?} at {:?}", ty, lval); - - match ty.sty { - // special case `Box` to deallocate the inner allocation - ty::TyAdt(ref def, _) if def.is_box() => { - let contents_ty = ty.boxed_ty(); - let val = self.read_lvalue(lval); - // we are going through the read_value path, because that already does all the - // checks for the trait object types. We'd only be repeating ourselves here. - let val = self.follow_by_ref_value(val, ty)?; - trace!("box dealloc on {:?}", val); - match val { - Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), - Value::ByVal(ptr) => { - assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.to_ptr()?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; - }, - Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.to_ptr()?; - let extra = match self.tcx.struct_tail(contents_ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), - _ => bug!("invalid fat pointer type: {}", ty), - }; - self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; - }, - } - // We cannot use Box's destructor, because it is a no-op and only exists to reduce - // the number of hacks required in the compiler around the Box type. - let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); - let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); - // this is somewhat hacky, but hey, there's no representation difference between - // pointers, `Box`es and references, so - // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) - // is the same as - // fn drop(&mut self) if Self is Box - drop.push((box_free_fn, val, substs)); - } - - ty::TyAdt(adt_def, substs) => { - // FIXME: some structs are represented as ByValPair - let mut lval = self.force_allocation(lval)?; - let (adt_ptr, extra) = lval.to_ptr_and_extra(); - - // run drop impl before the fields' drop impls - if let Some(destructor) = adt_def.destructor(self.tcx) { - let trait_ref = ty::Binder(ty::TraitRef { - def_id: self.tcx.lang_items.drop_trait().unwrap(), - substs: self.tcx.mk_substs_trait(ty, &[]), - }); - let vtable = match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(data) => data, - _ => bug!("dtor for {:?} is not an impl???", ty) - }; - let val = match extra { - LvalueExtra::None => Value::ByVal(PrimVal::Ptr(adt_ptr)), - LvalueExtra::DowncastVariant(_) => bug!("downcast variant in drop"), - LvalueExtra::Length(n) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::from_u128(n as u128)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::Ptr(vtable)), - }; - drop.push((destructor.did, val, vtable.substs)); - } - - let layout = self.type_layout(ty)?; - let fields = match *layout { - Layout::Univariant { .. } => &adt_def.struct_variant().fields, - Layout::General { .. } => { - let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; - let ptr = self.force_allocation(lval)?.to_ptr(); - match adt_def.discriminants(self.tcx).position(|v| discr_val == v.to_u128_unchecked()) { - Some(i) => { - lval = Lvalue::Ptr { - ptr, - extra: LvalueExtra::DowncastVariant(i), - }; - &adt_def.variants[i].fields - }, - None => return Err(EvalError::InvalidDiscriminant), - } - }, - Layout::StructWrappedNullablePointer { .. } | - Layout::RawNullablePointer { .. } => { - let discr = self.read_discriminant_value(adt_ptr, ty)?; - assert_eq!(discr as usize as u128, discr); - &adt_def.variants[discr as usize].fields - }, - Layout::CEnum { .. } => return Ok(()), - _ => bug!("{:?} is not an adt layout", layout), - }; - let tcx = self.tcx; - self.drop_fields( - fields.iter().map(|field| monomorphize_field_ty(tcx, field, substs)), - lval, - ty, - drop, - )?; - } - - ty::TyTuple(fields, _) => - self.drop_fields(fields.into_iter().cloned(), lval, ty, drop)?, - - ty::TyDynamic(..) => { - let (ptr, vtable) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), - _ => bug!("expected an lvalue with a vtable"), - }; - if let Some(real_ty) = self.read_drop_type_from_vtable(vtable)? { - self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - } - } - - ty::TySlice(elem_ty) => { - let (ptr, len) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), - _ => bug!("expected an lvalue with a length"), - }; - let size = self.type_size(elem_ty)?.expect("slice element must be sized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..len { - self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; - } - } - - ty::TyArray(elem_ty, len) => { - let lval = self.force_allocation(lval)?; - let (ptr, extra) = match lval { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("expected an lvalue with optional extra data"), - }; - let size = self.type_size(elem_ty)?.expect("array element cannot be unsized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..(len as u64) { - self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; - } - } - - ty::TyClosure(def_id, substs) => { - let fields = substs.upvar_tys(def_id, self.tcx); - self.drop_fields(fields, lval, ty, drop)?; - } - - _ => bug!(), - } - - Ok(()) - } - - fn drop_fields( - &mut self, - fields: I, - lval: Lvalue<'tcx>, - ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> - where I: Iterator>, - { - trace!("drop_fields: {:?} of type {}", lval, ty); - for (i, field_ty) in fields.enumerate() { - let field_lval = self.lvalue_field(lval, i, ty, field_ty)?; - self.drop(field_lval, field_ty, drop)?; - } - Ok(()) - } - - fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) - } -} diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2e1202ab5d1..a84b87f49a73 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -1,4 +1,3 @@ -use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::subst::Substs; @@ -13,8 +12,7 @@ use value::{PrimVal, PrimValKind, Value}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, + instance: ty::Instance<'tcx>, args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, @@ -31,7 +29,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let f32 = self.tcx.types.f32; let f64 = self.tcx.types.f64; - let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..]; + let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?, @@ -60,7 +58,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -69,7 +67,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_relaxed" | "atomic_store_rel" | "volatile_store" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -79,7 +77,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_xchg") => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -93,7 +91,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_cxchg") => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; @@ -115,7 +113,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" | "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -144,7 +142,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy" | "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs - let elem_ty = substs.type_at(0); + let elem_ty = instance.substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; @@ -157,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "cttz" | "ctlz" | "bswap" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let num = self.value_to_primval(arg_vals[0], ty)?; let kind = self.ty_to_primval_kind(ty)?; let num = numeric_intrinsic(intrinsic_name, num, kind)?; @@ -165,41 +163,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } - "drop_in_place" => { - let ty = substs.type_at(0); - trace!("drop in place on {}", ty); - let ptr_ty = self.tcx.mk_mut_ptr(ty); - let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { - Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()?), - Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.to_ptr()?, - extra: match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), - _ => bug!("invalid fat pointer type: {}", ptr_ty), - }, - }, - }; - let mut drops = Vec::new(); - self.drop(lvalue, ty, &mut drops)?; - let span = { - let frame = self.frame(); - frame.mir[frame.block].terminator().source_info.span - }; - // need to change the block before pushing the drop impl stack frames - // we could do this for all intrinsics before evaluating the intrinsics, but if - // the evaluation fails, we should not have moved forward - self.goto_block(target); - return self.eval_drop_impls(drops, span); - } - "sinf32" | "fabsf32" | "cosf32" | "sqrtf32" | "expf32" | "exp2f32" | "logf32" | "log10f32" | "log2f32" | @@ -247,7 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[1], ty)?; @@ -279,7 +248,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { - let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; + let ptr = this.alloc_ptr_with_substs(dest_ty, instance.substs)?; this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) } @@ -299,14 +268,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "min_align_of" => { - let elem_ty = substs.type_at(0); + let elem_ty = instance.substs.type_at(0); let elem_align = self.type_align(elem_ty)?; let align_val = PrimVal::from_u128(elem_align as u128); self.write_primval(dest, align_val, dest_ty)?; } "pref_align_of" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); let align_val = PrimVal::from_u128(align as u128); @@ -314,20 +283,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "move_val_init" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } "needs_drop" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } "offset" => { - let pointee_ty = substs.type_at(0); + let pointee_ty = instance.substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; @@ -388,7 +357,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") @@ -398,32 +367,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of_val" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; } "type_name" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ty_name = ty.to_string(); let s = self.str_to_value(&ty_name)?; self.write_value(s, dest, dest_ty)?; } "type_id" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let n = self.tcx.type_id_hash(ty); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } "transmute" => { - let dest_ty = substs.type_at(1); + let dest_ty = instance.substs.type_at(1); self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -449,7 +418,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write_bytes" => { let u8 = self.tcx.types.u8; - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 659f8e315204..8e0623dcba17 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,9 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::layout::Layout; -use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use rustc_const_math::ConstInt; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -11,12 +8,11 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, FunctionDefinition, Function}; +use memory::Pointer; use value::PrimVal; use value::Value; mod intrinsic; -mod drop; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { @@ -64,46 +60,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); let fn_def = match func_ty.sty { - ty::TyFnPtr(bare_sig) => { - let bare_sig = self.erase_lifetimes(&bare_sig); + ty::TyFnPtr(_) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; - match fn_def { - Function::Concrete(fn_def) => { - // transmuting function pointers in miri is fine as long as the number of - // arguments and the abi don't change. - let sig = self.erase_lifetimes(&fn_def.sig); - if sig.abi != bare_sig.abi || - sig.variadic != bare_sig.variadic || - sig.inputs_and_output != bare_sig.inputs_and_output { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - assert_eq!(sig.abi, Abi::RustCall); - if sig.variadic != bare_sig.variadic || - sig.inputs().len() != 1 { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - if let ty::TyTuple(fields, _) = sig.inputs()[0].sty { - if **fields != *bare_sig.inputs() { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - } else { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - }, - other => return Err(EvalError::ExpectedConcreteFunction(other)), - } self.memory.get_fn(fn_ptr.alloc_id)? }, - ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition { - def_id, - substs, - sig: fn_ty, - }), - + ty::TyFnDef(def_id, substs, _) => ty::Instance::new(def_id, substs), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); @@ -112,19 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?; } - Drop { ref location, target, .. } => { - let lval = self.eval_lvalue(location)?; - - let ty = self.lvalue_ty(location); - - // we can't generate the drop stack frames on the fly, - // because that would change our call stack - // and very much confuse the further processing of the drop glue - let mut drops = Vec::new(); - self.drop(lval, ty, &mut drops)?; - self.goto_block(target); - self.eval_drop_impls(drops, terminator.source_info.span)?; - } + Drop { .. } => unreachable!(), Assert { ref cond, expected, ref msg, target, .. } => { let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; @@ -158,15 +107,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_fn_call( &mut self, - fn_def: Function<'tcx>, + instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx> { - use syntax::abi::Abi; - match fn_def { - // Intrinsics can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { + let sig = match instance.def.def_ty(self.tcx).sty { + ty::TyFnPtr(bare_sig) => bare_sig, + ty::TyFnDef(_, _, fn_ty) => fn_ty, + ref other => bug!("expected function or pointer, got {:?}", other), + }; + match sig.abi() { + Abi::RustIntrinsic => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let layout = self.type_layout(ty)?; @@ -174,139 +126,51 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Some(dest) if is_inhabited(self.tcx, ty) => dest, _ => return Err(EvalError::Unreachable), }; - self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; + self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); Ok(()) }, - // C functions can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => { + Abi::C => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let (ret, target) = destination.unwrap(); - self.call_c_abi(def_id, arg_operands, ret, ty)?; + match instance.def { + ty::InstanceDef::Item(_) => {}, + _ => bug!("C abi function must be InstanceDef::Item"), + } + self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; self.dump_local(ret); self.goto_block(target); Ok(()) }, - Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(FunctionDefinition { def_id, sig, substs }) if sig.abi() == Abi::Rust || sig.abi() == Abi::RustCall => { + Abi::Rust | Abi::RustCall => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs, temporaries) = - if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs, &mut args)? - } else { - (def_id, substs, Vec::new()) - }; - - // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { - let dids = adt_def.variants.iter().map(|v| v.did); - let discrs = adt_def.discriminants(self.tcx).map(ConstInt::to_u128_unchecked); - if let Some((_, disr_val)) = dids.zip(discrs).find(|&(did, _)| resolved_def_id == did) { - 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 { .. } => { - assert_eq!(disr_val, 0); - self.assign_fields(lvalue, dest_ty, args)?; - }, - Layout::General { discr, ref variants, .. } => { - let discr_size = discr.size().bytes(); - self.assign_discr_and_fields( - lvalue, - dest_ty, - variants[disr_val as usize].offsets[0].bytes(), - args, - disr_val, - disr_val as usize, - discr_size, - )?; - }, - Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - if nndiscr as u128 == disr_val { - self.assign_fields(lvalue, dest_ty, 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(); - self.write_value(val, lvalue, ty)?; - }, - _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), - } - self.goto_block(target); - return Ok(()); - } - } self.eval_fn_call_inner( - resolved_def_id, - resolved_substs, + instance, destination, args, - temporaries, span, ) }, - Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { - let sig = self.erase_lifetimes(&sig); - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - sig.inputs()[0], - )); - self.eval_fn_call_inner( - def_id, - substs, - destination, - args, - Vec::new(), - span, - ) - } - Function::Concrete(fn_def) => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", fn_def.sig.abi()))), - other => Err(EvalError::Unimplemented(format!("can't call function kind {:#?}", other))), + other => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", other))), } } fn eval_fn_call_inner( &mut self, - resolved_def_id: DefId, - resolved_substs: &'tcx Substs, + instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, args: Vec<(Value, Ty<'tcx>)>, - temporaries: Vec<(Pointer, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", args, temporaries, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}", args, destination); - let mir = match self.load_mir(resolved_def_id) { + let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { match &path[..] { @@ -344,13 +208,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.push_stack_frame( - resolved_def_id, + instance, span, mir, - resolved_substs, return_lvalue, return_to_block, - temporaries, )?; let arg_locals = self.frame().mir.args_iter(); @@ -530,33 +392,4 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // current frame. Ok(()) } - - pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { - if let Some((last, last_ty)) = args.pop() { - let last_layout = self.type_layout(last_ty)?; - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields, _), - &Layout::Univariant { ref variant, .. }) => { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - match last { - Value::ByRef(last_ptr) => { - for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset)); - args.push((arg, ty)); - } - }, - // propagate undefs - undef @ Value::ByVal(PrimVal::Undef) => { - for field_ty in fields { - args.push((undef, field_ty)); - } - }, - _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - Ok(()) - } } diff --git a/src/traits.rs b/src/traits.rs index 72de17801d5e..74cb6966c067 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -8,171 +8,11 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use syntax::codemap::DUMMY_SP; -use syntax::{ast, abi}; +use syntax::ast; -use error::{EvalError, EvalResult}; -use memory::Function; -use value::PrimVal; -use value::Value; +use error::EvalResult; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Trait method, which has to be resolved to an impl method. - pub(crate) fn trait_method( - &mut self, - trait_id: DefId, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec<(Pointer, Ty<'tcx>)>)> { - let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); - let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); - - match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(vtable_impl) => { - let impl_did = vtable_impl.impl_def_id; - let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the impl - // and those from the method: - let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - - Ok((did, substs, Vec::new())) - } - - traits::VtableClosure(vtable_closure) => { - let trait_closure_kind = self.tcx - .lang_items - .fn_trait_kind(trait_id) - .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); - let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); - trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); - self.unpack_fn_args(args)?; - let mut temporaries = Vec::new(); - match (closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. - - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. - // We want a `fn(self, ...)`. - // We can produce this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - - // Interpreter magic: insert an intermediate pointer, so we can skip the - // intermediate function call. - let ptr = match args[0].0 { - Value::ByRef(ptr) => ptr, - Value::ByVal(primval) => { - let ptr = self.alloc_ptr(args[0].1)?; - let size = self.type_size(args[0].1)?.expect("closures are sized"); - self.memory.write_primval(ptr, primval, size)?; - ptr - }, - Value::ByValPair(a, b) => { - let ptr = self.alloc_ptr(args[0].1)?; - self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - ptr - }, - }; - temporaries.push((ptr, args[0].1)); - args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); - args[0].1 = self.tcx.mk_mut_ptr(args[0].1); - } - - _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), - } - Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries)) - } - - traits::VtableFnPointer(vtable_fn_ptr) => { - if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty { - args.remove(0); - self.unpack_fn_args(args)?; - Ok((did, substs, Vec::new())) - } else { - bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) - } - } - - traits::VtableObject(ref data) => { - let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; - if args.is_empty() { - return Err(EvalError::VtableForArgumentlessMethod); - } - let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; - let idx = idx + 3; - let offset = idx * self.memory.pointer_size(); - let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; - trace!("args: {:#?}", args); - match self.memory.get_fn(fn_ptr.alloc_id)? { - Function::FnDefAsTraitObject(fn_def) => { - trace!("sig: {:#?}", fn_def.sig); - assert!(fn_def.sig.abi() != abi::Abi::RustCall); - assert_eq!(args.len(), 2); - // a function item turned into a closure trait object - // the first arg is just there to give use the vtable - args.remove(0); - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - trace!("sig: {:#?}", sig); - args[0] = ( - Value::ByVal(PrimVal::Ptr(self_ptr)), - sig.inputs()[0], - ); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - sig.inputs()[0], - )); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - Function::Closure(fn_def) => { - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - Function::FnPtrAsTraitObject(sig) => { - let sig = self.erase_lifetimes(&sig); - trace!("sig: {:#?}", sig); - // the first argument was the fat ptr - args.remove(0); - self.unpack_fn_args(args)?; - let fn_ptr = self.memory.read_ptr(self_ptr)?; - let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { - Function::Concrete(fn_def) => { - let fn_def_sig = self.erase_lifetimes(&fn_def.sig); - assert_eq!(sig, fn_def_sig); - fn_def - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let fn_def_sig = self.erase_lifetimes(&fn_def.sig); - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - fn_def_sig.inputs()[0], - )); - fn_def - }, - other => bug!("FnPtrAsTraitObject for {:?}", other), - }; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - } - }, - vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), - } - } pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are @@ -202,7 +42,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// The `trait_ref` encodes the erased self type. Hence if we are /// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. - pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { + pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { let tcx = self.tcx; debug!("get_vtable(trait_ref={:?})", trait_ref); @@ -219,13 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { - let fn_ty = self.tcx.item_type(mth.method.def_id); - let fn_ty = match fn_ty.sty { - ty::TyFnDef(_, _, fn_ty) => fn_ty, - _ => bug!("bad function type: {}", fn_ty), - }; - let fn_ty = self.tcx.erase_regions(&fn_ty); - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) + self.memory.create_fn_ptr(mth.method.def_id, mth.substs) })) .collect::>() .into_iter() @@ -238,18 +72,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .. } ) => { - let closure_type = self.tcx.closure_type(closure_def_id); - vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() + let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce); + vec![Some(self.memory.create_fn_alloc(instance))].into_iter() } // turn a function definition into a Fn trait object traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { match fn_ty.sty { - ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_as_trait_glue(did, substs, bare_fn_ty))].into_iter() + ty::TyFnDef(did, substs, _) => { + let instance = ty::Instance { + def: ty::InstanceDef::FnPtrShim(did, fn_ty), + substs, + }; + vec![Some(self.memory.create_fn_alloc(instance))].into_iter() }, - ty::TyFnPtr(bare_fn_ty) => { - vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter() + ty::TyFnPtr(_) => { + unimplemented!(); }, _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), } @@ -279,19 +117,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // in case there is no drop function to be called, this still needs to be initialized self.memory.write_usize(vtable, 0)?; + let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if let Some(destructor) = adt_def.destructor(self.tcx) { - let fn_ty = match self.tcx.item_type(destructor.did).sty { - ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), - _ => bug!("drop method is not a TyFnDef"), + if adt_def.has_dtor(self.tcx) { + let env = self.tcx.empty_parameter_environment(); + let def = if self.tcx.type_needs_drop_given_env(ty, &env) { + ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) + } else { + ty::InstanceDef::DropGlue(drop_in_place, None) }; - let fn_ty = self.erase_lifetimes(&fn_ty); - // The real type is taken from the self argument in `fn drop(&mut self)` - let real_ty = match fn_ty.inputs()[0].sty { - ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), - _ => bug!("first argument of Drop::drop must be &mut T"), - }; - let fn_ptr = self.memory.create_drop_glue(real_ty); + let instance = ty::Instance { substs, def }; + let fn_ptr = self.memory.create_fn_alloc(instance); self.memory.write_ptr(vtable, fn_ptr)?; } } @@ -310,20 +146,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } - pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { - let drop_fn = self.memory.read_ptr(vtable)?; - - // just a sanity check - assert_eq!(drop_fn.offset, 0); - - // some values don't need to call a drop impl, so the value is null - if drop_fn == Pointer::from_int(0) { - Ok(None) - } else { - self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty().map(Some) - } - } - pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_usize(vtable.offset(pointer_size))?; @@ -423,7 +245,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self, def_id: DefId, substs: &'tcx Substs<'tcx>, - ) -> (DefId, &'tcx Substs<'tcx>) { + ) -> ty::Instance<'tcx> { if let Some(trait_id) = self.tcx.trait_of_item(def_id) { let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); let vtable = self.fulfill_obligation(trait_ref); @@ -432,11 +254,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id) .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); if let Some(assoc_const) = assoc_const_opt { - return (assoc_const.def_id, vtable_impl.substs); + return ty::Instance::new(assoc_const.def_id, vtable_impl.substs); } } } - (def_id, substs) + ty::Instance::new(def_id, substs) } } @@ -483,35 +305,3 @@ pub(super) fn get_impl_method<'a, 'tcx>( } } } - -/// Locates the applicable definition of a method, given its name. -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name) - -> (DefId, &'tcx Substs<'tcx>) -{ - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("find_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - (node_item.item.def_id, substs) - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} From 26c3335dbfd510a95284bd953e869cf31d5127ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 13:13:52 +0100 Subject: [PATCH 03/31] Closures work again --- src/eval_context.rs | 259 +++++++++++++++++++++++++++++++++++++++++- src/terminator/mod.rs | 114 +++++++++++++++---- 2 files changed, 352 insertions(+), 21 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9b2e5e1ceaba..1ec690e62049 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -10,8 +10,11 @@ use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; +use rustc::traits; use rustc_data_structures::indexed_vec::Idx; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; +use syntax::ast; +use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -1701,3 +1704,257 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, _ => Err(()), } } + +/// The point where linking happens. Resolve a (def_id, substs) +/// pair to an instance. +pub fn resolve<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx> +) -> ty::Instance<'tcx> { + debug!("resolve(def_id={:?}, substs={:?})", + def_id, substs); + let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + debug!(" => associated item, attempting to find impl"); + let item = tcx.associated_item(def_id); + resolve_associated_item(tcx, &item, trait_def_id, substs) + } else { + let item_type = def_ty(tcx, def_id, substs); + let def = match item_type.sty { + ty::TyFnDef(_, _, f) if + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def_id) + } + _ => { + if Some(def_id) == tcx.lang_items.drop_in_place_fn() { + let ty = substs.type_at(0); + if needs_drop_glue(tcx, ty) { + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } else { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } + } + }; + ty::Instance { def, substs } + }; + debug!("resolve(def_id={:?}, substs={:?}) = {}", + def_id, substs, result); + result +} + +pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bool { + assert!(t.is_normalized_for_trans()); + + let t = tcx.erase_regions(&t); + + // FIXME (#22815): note that type_needs_drop conservatively + // approximates in some cases and may say a type expression + // requires drop glue when it actually does not. + // + // (In this case it is not clear whether any harm is done, i.e. + // erroneously returning `true` in some cases where we could have + // returned `false` does not appear unsound. The impact on + // code quality is unknown at this time.) + + let env = tcx.empty_parameter_environment(); + if !tcx.type_needs_drop_given_env(t, &env) { + return false; + } + match t.sty { + ty::TyAdt(def, _) if def.is_box() => { + let typ = t.boxed_ty(); + if !tcx.type_needs_drop_given_env(typ, &env) && type_is_sized(tcx, typ) { + tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { + let layout = t.layout(&infcx).unwrap(); + if layout.size(&tcx.data_layout).bytes() == 0 { + // `Box` does not allocate. + false + } else { + true + } + }) + } else { + true + } + } + _ => true + } +} + +fn resolve_associated_item<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + trait_item: &ty::AssociatedItem, + trait_id: DefId, + rcvr_substs: &'tcx Substs<'tcx> +) -> ty::Instance<'tcx> { + let def_id = trait_item.def_id; + debug!("resolve_associated_item(trait_item={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, trait_id, rcvr_substs); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = fulfill_obligation(tcx, DUMMY_SP, ty::Binder(trait_ref)); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + ::rustc::traits::VtableImpl(impl_data) => { + let (def_id, substs) = ::rustc::traits::find_associated_item( + tcx, trait_item, rcvr_substs, &impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) + } + ::rustc::traits::VtableClosure(closure_data) => { + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs, + trait_closure_kind) + } + ::rustc::traits::VtableFnPointer(ref data) => { + ty::Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs + } + } + ::rustc::traits::VtableObject(ref data) => { + let index = tcx.get_vtable_index_of_object_method(data, def_id); + ty::Instance { + def: ty::InstanceDef::Virtual(def_id, index), + substs: rcvr_substs + } + } + _ => { + bug!("static call to invalid vtable: {:?}", vtbl) + } + } +} + +pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> Ty<'tcx> +{ + let ty = tcx.item_type(def_id); + apply_param_substs(tcx, substs, &ty) +} + +/// Monomorphizes a type from the AST by first applying the in-scope +/// substitutions and then normalizing any associated types. +pub fn apply_param_substs<'a, 'tcx, T>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_substs: &Substs<'tcx>, + value: &T) + -> T + where T: ::rustc::infer::TransNormalize<'tcx> +{ + debug!("apply_param_substs(param_substs={:?}, value={:?})", param_substs, value); + let substituted = value.subst(tcx, param_substs); + let substituted = tcx.erase_regions(&substituted); + AssociatedTypeNormalizer{ tcx }.fold(&substituted) +} + + +struct AssociatedTypeNormalizer<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> AssociatedTypeNormalizer<'a, 'tcx> { + fn fold>(&mut self, value: &T) -> T { + if !value.has_projection_types() { + value.clone() + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNormalizer<'a, 'tcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_projection_types() { + ty + } else { + self.tcx.normalize_associated_type(&ty) + } + } +} + +fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + // generics are weird, don't run this function on a generic + assert!(!ty.needs_subst()); + ty.is_sized(tcx, &tcx.empty_parameter_environment(), DUMMY_SP) +} + +/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we +/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should +/// guarantee to us that all nested obligations *could be* resolved if we wanted to. +fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + span: Span, + trait_ref: ty::PolyTraitRef<'tcx>) + -> traits::Vtable<'tcx, ()> +{ + // Remove any references to regions; this helps improve caching. + let trait_ref = tcx.erase_regions(&trait_ref); + + debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", + trait_ref, trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation_cause = traits::ObligationCause::misc(span, + ast::DUMMY_NODE_ID); + let obligation = traits::Obligation::new(obligation_cause, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{:?}` during trans, \ + presuming due to overflow", + trait_ref); + tcx.sess.span_fatal(span, + "reached the recursion limit during monomorphization \ + (selection ambiguity)"); + } + Err(e) => { + span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8e0623dcba17..8b5b88322e2e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; +use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -59,21 +60,44 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let func_ty = self.operand_ty(func); - let fn_def = match func_ty.sty { - ty::TyFnPtr(_) => { + let (fn_def, abi) = match func_ty.sty { + ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - self.memory.get_fn(fn_ptr.alloc_id)? + (self.memory.get_fn(fn_ptr.alloc_id)?, sig.abi()) }, - ty::TyFnDef(def_id, substs, _) => ty::Instance::new(def_id, substs), + ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig.abi()), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); } }; - self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?; + self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, abi)?; } - Drop { .. } => unreachable!(), + Drop { ref location, target, .. } => { + let lval = self.eval_lvalue(location)?; + + let ty = self.lvalue_ty(location); + + self.goto_block(target); + let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); + let env = self.tcx.empty_parameter_environment(); + let def = if self.tcx.type_needs_drop_given_env(ty, &env) { + ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) + } else { + ty::InstanceDef::DropGlue(drop_in_place, None) + }; + let substs = self.substs(); + let instance = ty::Instance { substs, def }; + let mir = self.load_mir(instance.def)?; + self.push_stack_frame( + instance, + terminator.source_info.span, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + } Assert { ref cond, expected, ref msg, target, .. } => { let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; @@ -111,15 +135,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, + abi: Abi, ) -> EvalResult<'tcx> { - let sig = match instance.def.def_ty(self.tcx).sty { - ty::TyFnPtr(bare_sig) => bare_sig, - ty::TyFnDef(_, _, fn_ty) => fn_ty, - ref other => bug!("expected function or pointer, got {:?}", other), - }; - match sig.abi() { - Abi::RustIntrinsic => { - let sig = self.erase_lifetimes(&sig); + trace!("eval_fn_call: {:#?}", instance); + match instance.def { + ty::InstanceDef::Intrinsic(..) => { + unimplemented!(); + /*let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { @@ -128,9 +150,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); - Ok(()) + Ok(())*/ }, - Abi::C => { + /*Abi::C => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let (ret, target) = destination.unwrap(); @@ -142,14 +164,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.dump_local(ret); self.goto_block(target); Ok(()) - }, - Abi::Rust | Abi::RustCall => { + },*/ + ty::InstanceDef::ClosureOnceShim{..} => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } + assert_eq!(abi, Abi::RustCall); + self.eval_fn_call_inner( + instance, + destination, + args, + span, + ) + } + ty::InstanceDef::Item(_) => { + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + match abi { + Abi::C => unimplemented!(), + Abi::Rust => {}, + Abi::RustCall => self.unpack_fn_args(&mut args)?, + _ => unimplemented!(), + } self.eval_fn_call_inner( instance, destination, @@ -157,7 +200,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, - other => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", other))), + _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), } } @@ -168,7 +211,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: Vec<(Value, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}", args, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", instance, args, destination); + + // Only trait methods can have a Self parameter. let mir = match self.load_mir(instance.def) { Ok(mir) => mir, @@ -392,4 +437,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // current frame. Ok(()) } + + pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { + if let Some((last, last_ty)) = args.pop() { + let last_layout = self.type_layout(last_ty)?; + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields, _), + &Layout::Univariant { ref variant, .. }) => { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + match last { + Value::ByRef(last_ptr) => { + for (offset, ty) in offsets.zip(fields) { + let arg = Value::ByRef(last_ptr.offset(offset)); + args.push((arg, ty)); + } + }, + // propagate undefs + undef @ Value::ByVal(PrimVal::Undef) => { + for field_ty in fields { + args.push((undef, field_ty)); + } + }, + _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), + } + } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + Ok(()) + } } From 030f00a8a1929a0f140f0f4ec14937efb720d6dd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 14:19:29 +0100 Subject: [PATCH 04/31] Fix drop terminator --- src/terminator/mod.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8b5b88322e2e..53c22926438e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -60,22 +60,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let func_ty = self.operand_ty(func); - let (fn_def, abi) = match func_ty.sty { + let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - (self.memory.get_fn(fn_ptr.alloc_id)?, sig.abi()) + (self.memory.get_fn(fn_ptr.alloc_id)?, sig) }, - ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig.abi()), + ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); } }; - self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, abi)?; + let sig = self.erase_lifetimes(&sig); + self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, sig)?; } Drop { ref location, target, .. } => { let lval = self.eval_lvalue(location)?; + let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); @@ -97,6 +99,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, )?; + + let mut arg_locals = self.frame().mir.args_iter(); + assert_eq!(self.frame().mir.arg_count, 1); + let arg_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let arg_ty = self.tcx.mk_mut_ptr(ty); + self.write_value(Value::ByVal(PrimVal::Ptr(src_ptr)), dest, arg_ty)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -135,22 +144,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, - abi: Abi, + sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx> { trace!("eval_fn_call: {:#?}", instance); match instance.def { ty::InstanceDef::Intrinsic(..) => { - unimplemented!(); - /*let sig = self.erase_lifetimes(&sig); - let ty = sig.output(); - let layout = self.type_layout(ty)?; let (ret, target) = match destination { - Some(dest) if is_inhabited(self.tcx, ty) => dest, + Some(dest) => dest, _ => return Err(EvalError::Unreachable), }; + let ty = sig.output(); + if !is_inhabited(self.tcx, ty) { + return Err(EvalError::Unreachable); + } + let layout = self.type_layout(ty)?; self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); - Ok(())*/ + Ok(()) }, /*Abi::C => { let sig = self.erase_lifetimes(&sig); @@ -172,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - assert_eq!(abi, Abi::RustCall); + assert_eq!(sig.abi, Abi::RustCall); self.eval_fn_call_inner( instance, destination, @@ -187,7 +197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - match abi { + match sig.abi { Abi::C => unimplemented!(), Abi::Rust => {}, Abi::RustCall => self.unpack_fn_args(&mut args)?, @@ -200,7 +210,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, - _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } } From 7c12ebc78dbe86896c66e8cfb4e6923184f89e24 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 16:16:23 +0100 Subject: [PATCH 05/31] Roll our own MIR for dropping arrays. --- src/eval_context.rs | 185 ++++++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 44 +++++----- 2 files changed, 206 insertions(+), 23 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 1ec690e62049..5fe66ee50c74 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -5,6 +5,7 @@ use std::fmt::Write; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; use rustc::middle::const_val::ConstVal; +use rustc_const_math::{ConstInt, ConstUsize}; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; @@ -44,6 +45,9 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: u64, + + /// Drop glue for arrays and slices + pub(crate) seq_drop_glue: MirRef<'tcx>, } /// A stack frame. @@ -124,6 +128,176 @@ impl Default for ResourceLimits { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { + let source_info = mir::SourceInfo { + span: DUMMY_SP, + scope: mir::ARGUMENT_VISIBILITY_SCOPE + }; + // i = 0; len = Len(*a0); goto head; + let start_block = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(2)), + mir::Rvalue::Use(mir::Operand::Constant(mir::Constant { + span: DUMMY_SP, + ty: tcx.types.usize, + literal: mir::Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + }, + })) + ) + }, + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(3)), + mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection { + base: mir::Lvalue::Local(mir::Local::new(1)), + elem: mir::ProjectionElem::Deref, + }))), + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, + }), + is_cleanup: false + }; + // head: done = i == len; switch done { 1 => ret, 0 => loop } + let head = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(4)), + mir::Rvalue::BinaryOp( + mir::BinOp::Eq, + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))), + ) + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::SwitchInt { + targets: vec![ + mir::BasicBlock::new(2), + mir::BasicBlock::new(4), + ], + discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))), + switch_ty: tcx.types.bool, + values: vec![ConstInt::U8(0)].into(), + }, + }), + is_cleanup: false + }; + // loop: drop (*a0)[i]; goto inc; + let loop_ = mir::BasicBlockData { + statements: Vec::new(), + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Drop { + target: mir::BasicBlock::new(3), + unwind: None, + location: mir::Lvalue::Projection(Box::new( + mir::LvalueProjection { + base: mir::Lvalue::Projection(Box::new( + mir::LvalueProjection { + base: mir::Lvalue::Local(mir::Local::new(1)), + elem: mir::ProjectionElem::Deref, + } + )), + elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))), + } + )), + }, + }), + is_cleanup: false + }; + // inc: i++; goto head; + let inc = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(2)), + mir::Rvalue::BinaryOp( + mir::BinOp::Add, + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), + mir::Operand::Constant(mir::Constant { + span: DUMMY_SP, + ty: tcx.types.usize, + literal: mir::Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + }, + }), + ) + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, + }), + is_cleanup: false + }; + // ret: return; + let ret = mir::BasicBlockData { + statements: Vec::new(), + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Return, + }), + is_cleanup: false + }; + let locals = vec![ + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.mk_nil(), + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.mk_mut_ptr(tcx.mk_self_type()), + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.usize, + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.usize, + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.bool, + name: None, + source_info: None, + }, + ]; + let seq_drop_glue = mir::Mir::new( + vec![start_block, head, loop_, inc, ret].into_iter().collect(), + Vec::new().into_iter().collect(), // vis scopes + Vec::new().into_iter().collect(), // promoted + tcx.mk_nil(), // return type + locals.into_iter().collect(), + 1, // arg_count + Vec::new(), // upvars + DUMMY_SP, + ); + let seq_drop_glue = tcx.alloc_mir(seq_drop_glue); + // Perma-borrow MIR from shims to prevent mutation. + ::std::mem::forget(seq_drop_glue.borrow()); EvalContext { tcx, memory: Memory::new(&tcx.data_layout, limits.memory_size), @@ -131,6 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, + seq_drop_glue: seq_drop_glue.borrow(), } } @@ -1958,3 +2133,13 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, vtable }) } + +pub fn resolve_drop_in_place<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, +) -> ty::Instance<'tcx> +{ + let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem); + let substs = tcx.intern_substs(&[Kind::from(ty)]); + resolve(tcx, def_id, substs) +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 53c22926438e..8258395c402d 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -80,18 +80,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); + let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); self.goto_block(target); - let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); - let env = self.tcx.empty_parameter_environment(); - let def = if self.tcx.type_needs_drop_given_env(ty, &env) { - ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) - } else { - ty::InstanceDef::DropGlue(drop_in_place, None) + let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + + if let ty::InstanceDef::DropGlue(_, None) = instance.def { + // we don't actually need to drop anything + return Ok(()); + } + + let mir = match ty.sty { + ty::TyDynamic(..) => unimplemented!(), + ty::TyArray(..) | ty::TySlice(..) => ::eval_context::MirRef::clone(&self.seq_drop_glue), + _ => self.load_mir(instance.def)?, }; - let substs = self.substs(); - let instance = ty::Instance { substs, def }; - let mir = self.load_mir(instance.def)?; + self.push_stack_frame( instance, terminator.source_info.span, @@ -162,19 +166,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.dump_local(ret); Ok(()) }, - /*Abi::C => { - let sig = self.erase_lifetimes(&sig); - let ty = sig.output(); - let (ret, target) = destination.unwrap(); - match instance.def { - ty::InstanceDef::Item(_) => {}, - _ => bug!("C abi function must be InstanceDef::Item"), - } - self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; - self.dump_local(ret); - self.goto_block(target); - Ok(()) - },*/ ty::InstanceDef::ClosureOnceShim{..} => { let mut args = Vec::new(); for arg in arg_operands { @@ -198,7 +189,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((arg_val, arg_ty)); } match sig.abi { - Abi::C => unimplemented!(), + Abi::C => { + let ty = sig.output(); + let (ret, target) = destination.unwrap(); + self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; + self.dump_local(ret); + self.goto_block(target); + return Ok(()); + }, Abi::Rust => {}, Abi::RustCall => self.unpack_fn_args(&mut args)?, _ => unimplemented!(), From 9e4e6cdb5c89f68fce06eb8b716ce0068ed8f817 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:32:20 +0100 Subject: [PATCH 06/31] Dropping arrays works again --- src/eval_context.rs | 5 +++-- src/terminator/mod.rs | 27 ++++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 5fe66ee50c74..bf847051d8ab 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -16,6 +16,7 @@ use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast; use syntax::abi::Abi; +use syntax::symbol::Symbol; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -231,7 +232,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: DUMMY_SP, ty: tcx.types.usize, literal: mir::Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())), }, }), ) @@ -262,7 +263,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, mir::LocalDecl { mutability: mir::Mutability::Mut, - ty: tcx.mk_mut_ptr(tcx.mk_self_type()), + ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))), name: None, source_info: None, }, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8258395c402d..ed178f6de0d7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,6 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; +use rustc::ty::subst::Kind; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -76,24 +77,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Drop { ref location, target, .. } => { + trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; + trace!("drop lval: {:#?}", lval); let src_ptr = self.force_allocation(lval)?.to_ptr(); - let ty = self.lvalue_ty(location); + self.goto_block(target); + let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); - self.goto_block(target); - let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let mut instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); if let ty::InstanceDef::DropGlue(_, None) = instance.def { // we don't actually need to drop anything return Ok(()); } - + let arg; let mir = match ty.sty { ty::TyDynamic(..) => unimplemented!(), - ty::TyArray(..) | ty::TySlice(..) => ::eval_context::MirRef::clone(&self.seq_drop_glue), - _ => self.load_mir(instance.def)?, + ty::TyArray(elem, n) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + ty::TySlice(ref elem) => unimplemented!(), + _ => { + arg = Value::ByVal(PrimVal::Ptr(src_ptr)); + self.load_mir(instance.def)? + }, }; self.push_stack_frame( @@ -109,7 +122,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = arg_locals.next().unwrap(); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; let arg_ty = self.tcx.mk_mut_ptr(ty); - self.write_value(Value::ByVal(PrimVal::Ptr(src_ptr)), dest, arg_ty)?; + self.write_value(arg, dest, arg_ty)?; } Assert { ref cond, expected, ref msg, target, .. } => { From c4090794420b789bc8566fc7757711c9019ea096 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:48:16 +0100 Subject: [PATCH 07/31] Dropping trait objects works again --- src/terminator/mod.rs | 21 ++++++++++++++++++--- src/traits.rs | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ed178f6de0d7..5b4215dec17e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; -use lvalue::Lvalue; +use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; use value::PrimVal; use value::Value; @@ -80,7 +80,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; trace!("drop lval: {:#?}", lval); - let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); self.goto_block(target); @@ -94,16 +93,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let arg; let mir = match ty.sty { - ty::TyDynamic(..) => unimplemented!(), + ty::TyDynamic(..) => { + if let Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } = lval { + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)); + match self.read_drop_type_from_vtable(vtable)? { + Some(func) => { + instance = func; + self.load_mir(func.def)? + }, + // no drop fn -> bail out + None => return Ok(()), + } + } else { + panic!("expected fat lvalue, got {:?}", lval); + } + }, ty::TyArray(elem, n) => { instance.substs = self.tcx.mk_substs([ Kind::from(elem), ].iter().cloned()); + let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); ::eval_context::MirRef::clone(&self.seq_drop_glue) }, ty::TySlice(ref elem) => unimplemented!(), _ => { + let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByVal(PrimVal::Ptr(src_ptr)); self.load_mir(instance.def)? }, diff --git a/src/traits.rs b/src/traits.rs index 74cb6966c067..813b19c3dadc 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -146,6 +146,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } + pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { + let drop_fn = self.memory.read_ptr(vtable)?; + + // just a sanity check + assert_eq!(drop_fn.offset, 0); + + // some values don't need to call a drop impl, so the value is null + if drop_fn == Pointer::from_int(0) { + Ok(None) + } else { + self.memory.get_fn(drop_fn.alloc_id).map(Some) + } + } + pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_usize(vtable.offset(pointer_size))?; From 3ef0b0de2c8018037d2efcba547bab1b84e25419 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:51:43 +0100 Subject: [PATCH 08/31] Dropping slices works again --- src/terminator/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 5b4215dec17e..a7dcb614bbb9 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -116,7 +116,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); ::eval_context::MirRef::clone(&self.seq_drop_glue) }, - ty::TySlice(ref elem) => unimplemented!(), + ty::TySlice(elem) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + if let Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } = lval { + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + } else { + panic!("slice without length: {:?}", lval); + } + }, _ => { let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByVal(PrimVal::Ptr(src_ptr)); From caed365dbe29f31ec477eb82128c98a4771c6983 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 18:31:41 +0100 Subject: [PATCH 09/31] Refactor drop into its own module and fix Vec --- src/terminator/drop.rs | 82 +++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 87 ++++++++++-------------------------------- 2 files changed, 102 insertions(+), 67 deletions(-) create mode 100644 src/terminator/drop.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs new file mode 100644 index 000000000000..bc37730b7143 --- /dev/null +++ b/src/terminator/drop.rs @@ -0,0 +1,82 @@ +use rustc::mir; +use rustc::ty::{self, Ty}; +use rustc::ty::subst::Kind; +use syntax::codemap::Span; + +use error::EvalResult; +use eval_context::{EvalContext, StackPopCleanup}; +use lvalue::{Lvalue, LvalueExtra}; +use memory::Pointer; +use value::PrimVal; +use value::Value; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { + trace!("drop_lvalue: {:#?}", lval); + let val = match self.force_allocation(lval)? { + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(PrimVal::Ptr(ptr)), + _ => bug!("force_allocation broken"), + }; + self.drop(val, instance, ty, span) + } + pub(crate) fn drop(&mut self, mut arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { + trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def); + + if let ty::InstanceDef::DropGlue(_, None) = instance.def { + trace!("nothing to do, aborting"); + // we don't actually need to drop anything + return Ok(()); + } + let mir = match ty.sty { + ty::TyDynamic(..) => { + let vtable = match arg { + Value::ByValPair(_, PrimVal::Ptr(vtable)) => vtable, + _ => bug!("expected fat ptr, got {:?}", arg), + }; + match self.read_drop_type_from_vtable(vtable)? { + Some(func) => { + instance = func; + self.load_mir(func.def)? + }, + // no drop fn -> bail out + None => return Ok(()), + } + }, + ty::TyArray(elem, n) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + let ptr = match arg { + Value::ByVal(PrimVal::Ptr(src_ptr)) => src_ptr, + _ => bug!("expected thin ptr, got {:?}", arg), + }; + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + ty::TySlice(elem) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + _ => self.load_mir(instance.def)?, + }; + + self.push_stack_frame( + instance, + span, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + + let mut arg_locals = self.frame().mir.args_iter(); + assert_eq!(self.frame().mir.arg_count, 1); + let arg_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let arg_ty = self.tcx.mk_mut_ptr(ty); + self.write_value(arg, dest, arg_ty) + } +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a7dcb614bbb9..6a615689ae7e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,18 +2,18 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; -use rustc::ty::subst::Kind; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; -use lvalue::{Lvalue, LvalueExtra}; +use lvalue::Lvalue; use memory::Pointer; use value::PrimVal; use value::Value; +mod drop; mod intrinsic; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -79,75 +79,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Drop { ref location, target, .. } => { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; - trace!("drop lval: {:#?}", lval); let ty = self.lvalue_ty(location); self.goto_block(target); - let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); - let mut instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); - - if let ty::InstanceDef::DropGlue(_, None) = instance.def { - // we don't actually need to drop anything - return Ok(()); - } - let arg; - let mir = match ty.sty { - ty::TyDynamic(..) => { - if let Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } = lval { - arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)); - match self.read_drop_type_from_vtable(vtable)? { - Some(func) => { - instance = func; - self.load_mir(func.def)? - }, - // no drop fn -> bail out - None => return Ok(()), - } - } else { - panic!("expected fat lvalue, got {:?}", lval); - } - }, - ty::TyArray(elem, n) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - let src_ptr = self.force_allocation(lval)?.to_ptr(); - arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); - ::eval_context::MirRef::clone(&self.seq_drop_glue) - }, - ty::TySlice(elem) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - if let Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } = lval { - arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)); - ::eval_context::MirRef::clone(&self.seq_drop_glue) - } else { - panic!("slice without length: {:?}", lval); - } - }, - _ => { - let src_ptr = self.force_allocation(lval)?.to_ptr(); - arg = Value::ByVal(PrimVal::Ptr(src_ptr)); - self.load_mir(instance.def)? - }, - }; - - self.push_stack_frame( - instance, - terminator.source_info.span, - mir, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - )?; - - let mut arg_locals = self.frame().mir.args_iter(); - assert_eq!(self.frame().mir.arg_count, 1); - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - let arg_ty = self.tcx.mk_mut_ptr(ty); - self.write_value(arg, dest, arg_ty)?; + let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + self.drop_lvalue(lval, instance, ty, terminator.source_info.span)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -246,6 +183,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, + ty::InstanceDef::DropGlue(..) => { + assert_eq!(arg_operands.len(), 1); + assert_eq!(sig.abi, Abi::Rust); + let val = self.eval_operand(&arg_operands[0])?; + let ty = self.operand_ty(&arg_operands[0]); + let (_, target) = destination.expect("diverging drop glue"); + self.goto_block(target); + // FIXME: deduplicate these matches + let pointee_type = match ty.sty { + ty::TyRawPtr(ref tam) | + ty::TyRef(_, ref tam) => tam.ty, + ty::TyAdt(ref def, _) if def.is_box() => ty.boxed_ty(), + _ => bug!("can only deref pointer types"), + }; + self.drop(val, instance, pointee_type, span) + } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } } From 0255a5146874f4e340d470af0f6dd96aa5afe8a8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 10:04:08 +0100 Subject: [PATCH 10/31] Fix function pointer calls --- src/terminator/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6a615689ae7e..157fc12a8ce0 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -198,6 +198,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("can only deref pointer types"), }; self.drop(val, instance, pointee_type, span) + }, + ty::InstanceDef::FnPtrShim(..) => { + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + match sig.abi { + Abi::Rust => { + args.remove(0); + }, + Abi::RustCall => {}, + _ => unimplemented!(), + } + trace!("ABI: {}", sig.abi); + self.eval_fn_call_inner( + instance, + destination, + args, + span, + ) } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } From 70ea218d2be5024f9ff2978facb20f9ba9aad6c9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 13:35:19 +0100 Subject: [PATCH 11/31] Reuse more rustc code instead of copying it into miri --- src/traits.rs | 231 +++----------------------------------------------- 1 file changed, 12 insertions(+), 219 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index 813b19c3dadc..77541a5b70fb 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,12 +1,11 @@ -use rustc::traits::{self, Reveal, SelectionContext}; +use rustc::traits::{self, Reveal}; use eval_context::EvalContext; use memory::Pointer; use rustc::hir::def_id::DefId; -use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty}; use syntax::codemap::DUMMY_SP; use syntax::ast; @@ -43,101 +42,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { - let tcx = self.tcx; - debug!("get_vtable(trait_ref={:?})", trait_ref); - let methods: Vec<_> = traits::supertraits(tcx, trait_ref).flat_map(|trait_ref| { - match self.fulfill_obligation(trait_ref) { - // Should default trait error here? - traits::VtableDefaultImpl(_) | - traits::VtableBuiltin(_) => { - Vec::new().into_iter() - } - - traits::VtableImpl(traits::VtableImplData { impl_def_id: id, substs, .. }) => { - self.get_vtable_methods(id, substs) - .into_iter() - .map(|opt_mth| opt_mth.map(|mth| { - self.memory.create_fn_ptr(mth.method.def_id, mth.substs) - })) - .collect::>() - .into_iter() - } - - traits::VtableClosure( - traits::VtableClosureData { - closure_def_id, - substs, - .. - } - ) => { - let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce); - vec![Some(self.memory.create_fn_alloc(instance))].into_iter() - } - - // turn a function definition into a Fn trait object - traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { - match fn_ty.sty { - ty::TyFnDef(did, substs, _) => { - let instance = ty::Instance { - def: ty::InstanceDef::FnPtrShim(did, fn_ty), - substs, - }; - vec![Some(self.memory.create_fn_alloc(instance))].into_iter() - }, - ty::TyFnPtr(_) => { - unimplemented!(); - }, - _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), - } - } - - traits::VtableObject(ref data) => { - // this would imply that the Self type being erased is - // an object type; this cannot happen because we - // cannot cast an unsized type into a trait object - bug!("cannot get vtable for an object type: {:?}", - data); - } - - vtable @ traits::VtableParam(..) => { - bug!("resolved vtable for {:?} to bad vtable {:?} in trans", - trait_ref, - vtable); - } - } - }).collect(); - let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type"); let align = self.type_align(trait_ref.self_ty())?; let ptr_size = self.memory.pointer_size(); - let vtable = self.memory.allocate(ptr_size * (3 + methods.len() as u64), ptr_size)?; + let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref); + let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size)?; - // in case there is no drop function to be called, this still needs to be initialized - self.memory.write_usize(vtable, 0)?; - let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); - if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if adt_def.has_dtor(self.tcx) { - let env = self.tcx.empty_parameter_environment(); - let def = if self.tcx.type_needs_drop_given_env(ty, &env) { - ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) - } else { - ty::InstanceDef::DropGlue(drop_in_place, None) - }; - let instance = ty::Instance { substs, def }; - let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable, fn_ptr)?; - } - } + let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let drop = self.memory.create_fn_alloc(drop); + self.memory.write_ptr(vtable, drop)?; self.memory.write_usize(vtable.offset(ptr_size), size)?; self.memory.write_usize(vtable.offset(ptr_size * 2), align)?; - for (i, method) in methods.into_iter().enumerate() { - if let Some(method) = method { - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), method)?; + for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { + if let Some((def_id, substs)) = method { + let instance = ::eval_context::resolve(self.tcx, def_id, substs); + let fn_ptr = self.memory.create_fn_alloc(instance); + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), fn_ptr)?; } } @@ -167,94 +92,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((size, align)) } - fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec>> { - debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); - - let trait_id = match self.tcx.impl_trait_ref(impl_id) { - Some(t_id) => t_id.def_id, - None => bug!("make_impl_vtable: don't know how to \ - make a vtable for a type impl!") - }; - - self.tcx.populate_implementations_for_trait_if_necessary(trait_id); - - self.tcx - .associated_items(trait_id) - // Filter out non-method items. - .filter_map(|trait_method_type| { - if trait_method_type.kind != ty::AssociatedKind::Method { - return None; - } - debug!("get_vtable_methods: trait_method_type={:?}", - trait_method_type); - - let name = trait_method_type.name; - - // Some methods cannot be called on an object; skip those. - if !self.tcx.is_vtable_safe_method(trait_id, &trait_method_type) { - debug!("get_vtable_methods: not vtable safe"); - return Some(None); - } - - debug!("get_vtable_methods: trait_method_type={:?}", - trait_method_type); - - // the method may have some early-bound lifetimes, add - // regions for those - let method_substs = Substs::for_item(self.tcx, trait_method_type.def_id, - |_, _| self.tcx.mk_region(ty::ReErased), - |_, _| self.tcx.types.err); - - // The substitutions we have are on the impl, so we grab - // the method type from the impl to substitute into. - let mth = get_impl_method(self.tcx, method_substs, impl_id, substs, name); - - debug!("get_vtable_methods: mth={:?}", mth); - - // If this is a default method, it's possible that it - // relies on where clauses that do not hold for this - // particular set of type parameters. Note that this - // method could then never be called, so we do not want to - // try and trans it, in that case. Issue #23435. - if mth.is_provided { - let predicates = self.tcx.item_predicates(trait_method_type.def_id).instantiate_own(self.tcx, mth.substs); - if !self.normalize_and_test_predicates(predicates.predicates) { - debug!("get_vtable_methods: predicates do not hold"); - return Some(None); - } - } - - Some(Some(mth)) - }) - .collect() - } - - /// Normalizes the predicates and checks whether they hold. If this - /// returns false, then either normalize encountered an error or one - /// of the predicates did not hold. Used when creating vtables to - /// check for unsatisfiable methods. - fn normalize_and_test_predicates(&mut self, predicates: Vec>) -> bool { - debug!("normalize_and_test_predicates(predicates={:?})", - predicates); - - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let mut fulfill_cx = traits::FulfillmentContext::new(); - let cause = traits::ObligationCause::dummy(); - let traits::Normalized { value: predicates, obligations } = - traits::normalize(&mut selcx, cause.clone(), &predicates); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - for predicate in predicates { - let obligation = traits::Obligation::new(cause.clone(), predicate); - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - - fulfill_cx.select_all_or_error(&infcx).is_ok() - }) - } - pub(crate) fn resolve_associated_const( &self, def_id: DefId, @@ -275,47 +112,3 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::Instance::new(def_id, substs) } } - -#[derive(Debug)] -pub(super) struct ImplMethod<'tcx> { - pub(super) method: ty::AssociatedItem, - pub(super) substs: &'tcx Substs<'tcx>, - pub(super) is_provided: bool, -} - -/// Locates the applicable definition of a method, given its name. -pub(super) fn get_impl_method<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name, -) -> ImplMethod<'tcx> { - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, - substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("trans::meth::get_impl_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - ImplMethod { - method: node_item.item, - substs, - is_provided: node_item.node.is_from_trait(), - } - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} From d70b79c778843ef115103156ccbeed8d2ff4d91f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 13:36:13 +0100 Subject: [PATCH 12/31] Refactor function calls --- src/terminator/mod.rs | 147 +++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 157fc12a8ce0..84c76dd1204f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -12,6 +12,7 @@ use lvalue::Lvalue; use memory::Pointer; use value::PrimVal; use value::Value; +use rustc_data_structures::indexed_vec::Idx; mod drop; mod intrinsic; @@ -148,21 +149,38 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - assert_eq!(sig.abi, Abi::RustCall); self.eval_fn_call_inner( instance, destination, - args, span, - ) + )?; + let mut arg_locals = self.frame().mir.args_iter(); + match sig.abi { + // closure as closure once + Abi::RustCall => { + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; + } + }, + // non capture closure as fn ptr + // need to inject zst ptr for closure object (aka do nothing) + // and need to pack arguments + Abi::Rust => { + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); + let local = arg_locals.nth(1).unwrap(); + for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { + let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?; + self.write_value(arg_val, dest, arg_ty)?; + } + + }, + _ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi), + } + Ok(()) } ty::InstanceDef::Item(_) => { - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } match sig.abi { Abi::C => { let ty = sig.output(); @@ -172,16 +190,59 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(target); return Ok(()); }, - Abi::Rust => {}, - Abi::RustCall => self.unpack_fn_args(&mut args)?, + Abi::Rust | Abi::RustCall => {}, _ => unimplemented!(), } + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + self.eval_fn_call_inner( instance, destination, - args, span, - ) + )?; + + let mut arg_locals = self.frame().mir.args_iter(); + match sig.abi { + Abi::Rust => { + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; + } + } + Abi::RustCall => { + assert_eq!(args.len(), 2); + + { // write first argument + let first_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; + let (arg_val, arg_ty) = args.remove(0); + self.write_value(arg_val, dest, arg_ty)?; + } + + // unpack and write all other args + let (arg_val, arg_ty) = args.remove(0); + let layout = self.type_layout(arg_ty)?; + if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } + } + } else { + bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); + } + } + _ => unimplemented!(), + } + Ok(()) }, ty::InstanceDef::DropGlue(..) => { assert_eq!(arg_operands.len(), 1); @@ -200,26 +261,31 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.drop(val, instance, pointee_type, span) }, ty::InstanceDef::FnPtrShim(..) => { + trace!("ABI: {}", sig.abi); let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } + self.eval_fn_call_inner( + instance, + destination, + span, + )?; + let arg_locals = self.frame().mir.args_iter(); match sig.abi { Abi::Rust => { args.remove(0); }, Abi::RustCall => {}, _ => unimplemented!(), + }; + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; } - trace!("ABI: {}", sig.abi); - self.eval_fn_call_inner( - instance, - destination, - args, - span, - ) + Ok(()) } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } @@ -229,10 +295,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, - args: Vec<(Value, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", instance, args, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); // Only trait methods can have a Self parameter. @@ -281,13 +346,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, )?; - let arg_locals = self.frame().mir.args_iter(); - assert_eq!(self.frame().mir.arg_count, args.len()); - for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg_val, dest, arg_ty)?; - } - Ok(()) } @@ -457,34 +515,5 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // as if the call just completed and it's returning to the // current frame. Ok(()) - } - - pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { - if let Some((last, last_ty)) = args.pop() { - let last_layout = self.type_layout(last_ty)?; - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields, _), - &Layout::Univariant { ref variant, .. }) => { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - match last { - Value::ByRef(last_ptr) => { - for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset)); - args.push((arg, ty)); - } - }, - // propagate undefs - undef @ Value::ByVal(PrimVal::Undef) => { - for field_ty in fields { - args.push((undef, field_ty)); - } - }, - _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - Ok(()) - } + } } From ad4f6b920a47ab112fa249693c819c32988add32 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 14:24:02 +0100 Subject: [PATCH 13/31] Fix virtual function calls --- src/terminator/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 84c76dd1204f..a6e0e53c2ded 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -174,7 +174,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?; self.write_value(arg_val, dest, arg_ty)?; } - }, _ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi), } @@ -286,8 +285,60 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(arg_val, dest, arg_ty)?; } Ok(()) - } - _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), + }, + ty::InstanceDef::Virtual(_, idx) => { + trace!("ABI: {:?}", sig.abi); + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + let ptr_size = self.memory.pointer_size(); + let (_, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; + // FIXME: do we need to rewrite args[0] to be a thin ptr? + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; + let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + self.eval_fn_call_inner( + instance, + destination, + span, + )?; + match sig.abi { + Abi::RustCall => { + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); + trace!("args: {:#?}", args); + + assert_eq!(args.len(), 2); + + { // write first argument + let first_local = self.frame().mir.args_iter().next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; + let (arg_val, arg_ty) = args.remove(0); + self.write_value(arg_val, dest, arg_ty)?; + } + + // unpack and write all other args + let (arg_val, arg_ty) = args.remove(0); + let layout = self.type_layout(arg_ty)?; + if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(self.frame().mir.args_iter().skip(1)) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } + } + } else { + bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); + } + }, + _ => unimplemented!(), + } + Ok(()) + }, } } From d71f24c00f64cdf35f7b3cd5fd5635185b8b8a0b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 14:57:11 +0100 Subject: [PATCH 14/31] Fix virtual function calls --- src/terminator/mod.rs | 51 +++++-------------------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a6e0e53c2ded..fa74e5713203 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -287,57 +287,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) }, ty::InstanceDef::Virtual(_, idx) => { - trace!("ABI: {:?}", sig.abi); - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } let ptr_size = self.memory.pointer_size(); - let (_, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; - // FIXME: do we need to rewrite args[0] to be a thin ptr? + let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; - self.eval_fn_call_inner( + // recurse with concrete function + self.eval_fn_call( instance, destination, + arg_operands, span, - )?; - match sig.abi { - Abi::RustCall => { - trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); - trace!("arg_operands: {:?}", arg_operands); - trace!("args: {:#?}", args); - - assert_eq!(args.len(), 2); - - { // write first argument - let first_local = self.frame().mir.args_iter().next().unwrap(); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; - let (arg_val, arg_ty) = args.remove(0); - self.write_value(arg_val, dest, arg_ty)?; - } - - // unpack and write all other args - let (arg_val, arg_ty) = args.remove(0); - let layout = self.type_layout(arg_ty)?; - if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(self.frame().mir.args_iter().skip(1)) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; - } - } - } else { - bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); - } - }, - _ => unimplemented!(), - } - Ok(()) + sig, + ) }, } } From 4e83659b1de9dad88a034fdc07e30921701761af Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:07:33 +0100 Subject: [PATCH 15/31] Fix manual rust-call impls --- src/terminator/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index fa74e5713203..de3742caf131 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -227,13 +227,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (arg_val, arg_ty) = args.remove(0); let layout = self.type_layout(arg_ty)?; if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; + if self.frame().mir.args_iter().count() == fields.len() + 1 { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } } + } else { + // called a manual impl of a rust-call function + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; + self.write_value(arg_val, dest, arg_ty)?; } } else { bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); From 1391c5a10abf89b0e5eb92dbc3f0c4f997525e98 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:17:02 +0100 Subject: [PATCH 16/31] Reintroduce fn ptr transmute check --- src/terminator/mod.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index de3742caf131..4ea2c4d95878 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,7 +65,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - (self.memory.get_fn(fn_ptr.alloc_id)?, sig) + let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + match instance.def.def_ty(self.tcx).sty { + ty::TyFnDef(_, _, real_sig) => { + let sig = self.erase_lifetimes(&sig); + let real_sig = self.erase_lifetimes(&real_sig); + if sig.abi != real_sig.abi || + sig.variadic != real_sig.variadic || + sig.inputs_and_output != real_sig.inputs_and_output { + return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + } + }, + ref other => bug!("instance def ty: {:?}", other), + } + (instance, sig) }, ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig), _ => { From 1c9f5ac669ab3e709df43b453aa1e80d488e3416 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:23:53 +0100 Subject: [PATCH 17/31] Skip the transmute checks for closure glue --- src/terminator/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4ea2c4d95878..e49c4aa7387b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -70,10 +70,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, real_sig) => { let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); - if sig.abi != real_sig.abi || - sig.variadic != real_sig.variadic || - sig.inputs_and_output != real_sig.inputs_and_output { - return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + match instance.def { + // FIXME: this needs checks for weird transmutes + // we need to bail here, because noncapturing closures as fn ptrs fail the checks + ty::InstanceDef::ClosureOnceShim{..} => {} + _ => if sig.abi != real_sig.abi || + sig.variadic != real_sig.variadic || + sig.inputs_and_output != real_sig.inputs_and_output { + return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + }, } }, ref other => bug!("instance def ty: {:?}", other), From eecc727e877630c7809e08f854c96233991ca89c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:52:02 +0100 Subject: [PATCH 18/31] Reduce noisyness --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index bf847051d8ab..f81f352c8aa0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -2130,7 +2130,7 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }); let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + debug!("Cache miss: {:?} => {:?}", trait_ref, vtable); vtable }) } From 197226aa2a5eac9a580c15fd35f1bb98723c3249 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:52:11 +0100 Subject: [PATCH 19/31] Try to fix travis --- .travis.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05fe244d04ff..e6450622b2e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,18 +2,16 @@ language: rust rust: - nightly before_script: -- | - pip install 'travis-cargo<0.2' --user && - export PATH=$HOME/.local/bin:$PATH -- sh ~/rust-installer/rustup.sh --add-target=i686-unknown-linux-gnu --prefix=/home/travis/rust -y --disable-sudo -- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-gnu --prefix=/home/travis/rust -y --disable-sudo -- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust -y --disable-sudo +- export PATH=$HOME/.local/bin:$PATH +- rustup target add i686-unknown-linux-gnu +- rustup target add i686-pc-windows-gnu +- rustup target add i686-pc-windows-msvc script: - | export RUST_SYSROOT=$HOME/rust && - travis-cargo build && - travis-cargo test && - travis-cargo install && + cargo build && + cargo test && + cargo install && cd cargo-miri-test && cargo miri && cargo miri test && From fb7cc3c164f457e8724bb483b19e40aecb31329b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 16:09:36 +0100 Subject: [PATCH 20/31] Fix single field by val tuples --- src/terminator/mod.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e49c4aa7387b..1712a0cc1262 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -224,6 +224,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; let mut arg_locals = self.frame().mir.args_iter(); + trace!("ABI: {:?}", sig.abi); + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); match sig.abi { Abi::Rust => { for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { @@ -245,16 +248,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (arg_val, arg_ty) = args.remove(0); let layout = self.type_layout(arg_ty)?; if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + trace!("fields: {:?}", fields); if self.frame().mir.args_iter().count() == fields.len() + 1 { let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; + match arg_val { + Value::ByRef(ptr) => { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); + self.write_value(arg, dest, ty)?; + } + }, + Value::ByVal(PrimVal::Undef) => {}, + other => { + assert_eq!(fields.len(), 1); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; + self.write_value(other, dest, fields[0])?; } } } else { + trace!("manual impl of rust-call ABI"); // called a manual impl of a rust-call function let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; self.write_value(arg_val, dest, arg_ty)?; From 0927829e14686684f96799dd5944190b817145a5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 16:13:01 +0100 Subject: [PATCH 21/31] Add regression test for single field by val tuples --- tests/run-pass/issue-20575.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/run-pass/issue-20575.rs diff --git a/tests/run-pass/issue-20575.rs b/tests/run-pass/issue-20575.rs new file mode 100644 index 000000000000..7db7e3b28e8e --- /dev/null +++ b/tests/run-pass/issue-20575.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that overloaded calls work with zero arity closures + +// pretty-expanded FIXME #23616 + +fn main() { + let functions: [Box Option<()>>; 1] = [Box::new(|| None)]; + + let _: Option> = functions.iter().map(|f| (*f)()).collect(); +} From f4ed482c4d153eb61e451129ae48fa9cc39bbeff Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 17:36:10 +0100 Subject: [PATCH 22/31] `print` doesn't add a stack frame, so don't write arguments --- src/terminator/mod.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1712a0cc1262..14f0652a2b57 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -167,11 +167,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let mut arg_locals = self.frame().mir.args_iter(); match sig.abi { // closure as closure once @@ -217,11 +219,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let mut arg_locals = self.frame().mir.args_iter(); trace!("ABI: {:?}", sig.abi); @@ -305,11 +309,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let arg_locals = self.frame().mir.args_iter(); match sig.abi { Abi::Rust => { @@ -341,12 +347,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + /// Returns Ok(true) when the function was handled completely due to mir not being available fn eval_fn_call_inner( &mut self, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, span: Span, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx, bool> { trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); // Only trait methods can have a Self parameter. @@ -358,7 +365,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // let's just ignore all output for now "std::io::_print" => { self.goto_block(destination.unwrap().1); - return Ok(()); + return Ok(true); }, "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), @@ -371,7 +378,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let bool = self.tcx.types.bool; self.write_primval(lval, PrimVal::from_bool(false), bool)?; self.goto_block(block); - return Ok(()); + return Ok(true); } _ => {}, } @@ -396,7 +403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, )?; - Ok(()) + Ok(false) } pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { From cb867d250ac4f1a1306d3835d461275ea037c065 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 17:57:40 +0100 Subject: [PATCH 23/31] Fix casting generic functions to concrete function pointers --- src/eval_context.rs | 3 ++- src/memory.rs | 7 ------- src/terminator/mod.rs | 4 +++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index f81f352c8aa0..5cd790797b47 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -825,7 +825,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, _) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs); + let instance = resolve(self.tcx, def_id, substs); + let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), diff --git a/src/memory.rs b/src/memory.rs index 058f8b478cac..3d46d710d3dd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,9 +2,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet}; use std::{fmt, iter, ptr, mem, io}; -use rustc::hir::def_id::DefId; use rustc::ty; -use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; @@ -176,11 +174,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { - let instance = ty::Instance::new(def_id, substs); - self.create_fn_alloc(instance) - } - pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { return Pointer::new(alloc_id, 0); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 14f0652a2b57..060299983ed4 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -66,7 +66,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; - match instance.def.def_ty(self.tcx).sty { + let instance_ty = instance.def.def_ty(self.tcx); + let instance_ty = self.monomorphize(instance_ty, instance.substs); + match instance_ty.sty { ty::TyFnDef(_, _, real_sig) => { let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); From bbeb7216e0985b21c6288acd4f09de1bb0c250cc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 18:32:57 +0100 Subject: [PATCH 24/31] Thinify the fat pointer on virtual function calls --- src/lvalue.rs | 6 ++++++ src/terminator/mod.rs | 9 +++++++- tests/run-pass/issue-3794.rs | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/issue-3794.rs diff --git a/src/lvalue.rs b/src/lvalue.rs index 62855c045057..b78824ea5c9e 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -200,6 +200,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Size::from_bytes(field * elem_size), false) } + FatPointer { .. } => { + let bytes = field_index as u64 * self.memory.pointer_size(); + let offset = Size::from_bytes(bytes); + (offset, false) + } + _ => bug!("field access on non-product type: {:?}", base_layout), }; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 060299983ed4..03804ccd0bd5 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -337,11 +337,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + let mut arg_operands = arg_operands.to_vec(); + let ty = self.operand_ty(&arg_operands[0]); + let ty = self.get_field_ty(ty, 0)?; + match arg_operands[0] { + mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty), + _ => bug!("virtual call first arg cannot be a constant"), + } // recurse with concrete function self.eval_fn_call( instance, destination, - arg_operands, + &arg_operands, span, sig, ) diff --git a/tests/run-pass/issue-3794.rs b/tests/run-pass/issue-3794.rs new file mode 100644 index 000000000000..badb833ee800 --- /dev/null +++ b/tests/run-pass/issue-3794.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +trait T { + fn print(&self); +} + +#[derive(Debug)] +struct S { + s: isize, +} + +impl T for S { + fn print(&self) { + println!("{:?}", self); + } +} + +fn print_t(t: &T) { + t.print(); +} + +fn print_s(s: &S) { + s.print(); +} + +pub fn main() { + let s: Box = box S { s: 5 }; + print_s(&*s); + let t: Box = s as Box; + print_t(&*t); +} From 6706d8fdec0cc24a9d866eba8d2b1bc60ccf4286 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 24 Mar 2017 09:03:19 +0100 Subject: [PATCH 25/31] Add regression test for bad substs --- tests/run-pass/bad_substs.rs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/run-pass/bad_substs.rs diff --git a/tests/run-pass/bad_substs.rs b/tests/run-pass/bad_substs.rs new file mode 100644 index 000000000000..d8da2de5d6df --- /dev/null +++ b/tests/run-pass/bad_substs.rs @@ -0,0 +1,4 @@ +fn main() { + let f: fn(i32) -> Option = Some::; + f(42); +} From 065e9593b4f63889fad8b8cf29a52a4791fddc3f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 27 Mar 2017 10:13:21 +0200 Subject: [PATCH 26/31] Rustup to `rustc 1.17.0-nightly (7846dbe0c 2017-03-26)` --- src/eval_context.rs | 18 +++++++----------- src/step.rs | 8 +------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 5cd790797b47..bbee49bae84c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -342,7 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } - pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { + pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::ConstFloat; @@ -364,7 +364,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), - Function(_, _) => unimplemented!(), + // function items are zero sized and thus have no readable value + Function(..) => PrimVal::Undef, Array(_) => unimplemented!(), Repeat(_, _) => unimplemented!(), }; @@ -995,20 +996,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), - Constant(mir::Constant { ref literal, ty, .. }) => { + Constant(mir::Constant { ref literal, .. }) => { use rustc::mir::Literal; let value = match *literal { Literal::Value { ref value } => self.const_to_value(value)?, Literal::Item { def_id, substs } => { - if let ty::TyFnDef(..) = ty.sty { - // function items are zero sized - Value::ByRef(self.memory.allocate(0, 0)?) - } else { - let instance = self.resolve_associated_const(def_id, substs); - let cid = GlobalId { instance, promoted: None }; - self.globals.get(&cid).expect("static/const not cached").value - } + let instance = self.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; + self.globals.get(&cid).expect("static/const not cached").value } Literal::Promoted { index } => { diff --git a/src/step.rs b/src/step.rs index 8a0cbc4a8f1c..79e940bee533 100644 --- a/src/step.rs +++ b/src/step.rs @@ -195,13 +195,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - if let ty::TyFnDef(..) = constant.ty.sty { - // No need to do anything here, - // because the type is the actual function, not the signature of the function. - // Thus we can simply create a zero sized allocation in `evaluate_operand` - } else { - self.global_item(def_id, substs, constant.span, true); - } + self.global_item(def_id, substs, constant.span, true); }, mir::Literal::Promoted { index } => { let cid = GlobalId { From fdeee8fb594f05319dafacb90233fc8a03aa7352 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 29 Mar 2017 09:10:05 +0200 Subject: [PATCH 27/31] Cleanup the diff --- src/terminator/intrinsic.rs | 45 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a84b87f49a73..bf5623e485bf 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let f32 = self.tcx.types.f32; let f64 = self.tcx.types.f64; + let substs = instance.substs; let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { @@ -58,7 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -67,7 +68,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_relaxed" | "atomic_store_rel" | "volatile_store" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -77,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_xchg") => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -91,7 +92,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_cxchg") => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; @@ -113,7 +114,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" | "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -142,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy" | "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs - let elem_ty = instance.substs.type_at(0); + let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; @@ -155,7 +156,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "cttz" | "ctlz" | "bswap" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let num = self.value_to_primval(arg_vals[0], ty)?; let kind = self.ty_to_primval_kind(ty)?; let num = numeric_intrinsic(intrinsic_name, num, kind)?; @@ -163,7 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; @@ -216,7 +217,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[1], ty)?; @@ -248,7 +249,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { - let ptr = this.alloc_ptr_with_substs(dest_ty, instance.substs)?; + let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) } @@ -268,14 +269,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "min_align_of" => { - let elem_ty = instance.substs.type_at(0); + let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty)?; let align_val = PrimVal::from_u128(elem_align as u128); self.write_primval(dest, align_val, dest_ty)?; } "pref_align_of" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); let align_val = PrimVal::from_u128(align as u128); @@ -283,20 +284,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "move_val_init" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } "needs_drop" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } "offset" => { - let pointee_ty = instance.substs.type_at(0); + let pointee_ty = substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; @@ -357,7 +358,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") @@ -367,32 +368,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of_val" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; } "type_name" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ty_name = ty.to_string(); let s = self.str_to_value(&ty_name)?; self.write_value(s, dest, dest_ty)?; } "type_id" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } "transmute" => { - let dest_ty = instance.substs.type_at(1); + let dest_ty = substs.type_at(1); self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -418,7 +419,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write_bytes" => { let u8 = self.tcx.types.u8; - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); From f95cc529fcc8293e236ec92012acfd5684e99b9d Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 20 Apr 2017 09:46:43 +0300 Subject: [PATCH 28/31] Update byteorder commit hash in Cargo.lock. --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e3708fd1bc66..7263e2beeebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "byteorder" version = "1.0.0" -source = "git+https://github.com/quininer/byteorder.git?branch=i128#ef51df297aa833d0b6639aae328a95597fc07d75" +source = "git+https://github.com/quininer/byteorder.git?branch=i128#9bab6d7783f81da50feb234a120c918d9eabba6e" [[package]] name = "cargo_metadata" From cb3799b44bc50fc5b92555cd853bbbc6ff377efd Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 13:27:09 +0300 Subject: [PATCH 29/31] Update for changes in LocalDecl on nightly. --- src/eval_context.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index bbee49bae84c..3ddecc43373d 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -259,31 +259,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mutability: mir::Mutability::Mut, ty: tcx.mk_nil(), name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))), name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.usize, name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.usize, name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.bool, name: None, - source_info: None, + source_info, + is_user_variable: false, }, ]; let seq_drop_glue = mir::Mir::new( From 738c7d262afc644fb54c09df5517a845f1dfe726 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 13:56:44 +0300 Subject: [PATCH 30/31] Handle Use of ! as Unreachable is not emitted nowadays. --- src/lvalue.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index b78824ea5c9e..9b6bfd2ddf41 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -108,18 +108,24 @@ 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> { + let ty = self.lvalue_ty(lvalue); let lvalue = self.eval_lvalue(lvalue)?; - Ok(self.read_lvalue(lvalue)) - } - pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> Value { + if ty.is_never() { + return Err(EvalError::Unreachable); + } + match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Value::ByRef(ptr) + Ok(Value::ByRef(ptr)) + } + Lvalue::Local { frame, local, field } => { + Ok(self.stack[frame].get_local(local, field.map(|(i, _)| i))) + } + Lvalue::Global(cid) => { + Ok(self.globals.get(&cid).expect("global not cached").value) } - 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, } } From b9bd747b6c8f10b4430b96fba16c0359e7c1b271 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 14:02:12 +0300 Subject: [PATCH 31/31] Import EvalError in lvalue. --- src/lvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 9b6bfd2ddf41..45d9501458d7 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -3,7 +3,7 @@ use rustc::ty::layout::{Size, Align}; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; -use error::EvalResult; +use error::{EvalError, EvalResult}; use eval_context::{EvalContext}; use memory::Pointer; use value::{PrimVal, Value};