diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e6ed715280e0a..a3b47f533f4be 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,17 +1,15 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::traits::{self, Reveal}; -use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{Layout, Size}; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use syntax::codemap::{DUMMY_SP, Span}; -use syntax::{ast, attr, abi}; +use rustc::ty::{self, Ty, BareFnTy}; +use syntax::codemap::Span; +use syntax::attr; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, FunctionDefinition, Function}; +use memory::{Pointer, FunctionDefinition}; use value::PrimVal; use value::Value; @@ -19,7 +17,6 @@ mod intrinsic; mod drop; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { self.frame_mut().block = target; self.frame_mut().stmt = 0; @@ -497,29 +494,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub(super) 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 - // looking for -- that is, what specific impl. - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // 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| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) - }) - } - - fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { + 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) { @@ -540,215 +515,4 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ok(()) } - - /// Trait method, which has to be resolved to an impl method. - 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)> { - 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)?; - temporaries.push(ptr); - ptr - }, - Value::ByValPair(a, b) => { - let ptr = self.alloc_ptr(args[0].1)?; - self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - temporaries.push(ptr); - ptr - }, - }; - 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.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) => { - trace!("sig: {:#?}", fn_def.sig); - args[0] = ( - Value::ByVal(PrimVal::Ptr(self_ptr)), - fn_def.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) => { - 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 = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; - assert_eq!(sig, fn_def.sig); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - } - }, - vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), - } - } -} - -#[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) - } - } -} - -/// 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) - } - } } diff --git a/src/traits.rs b/src/traits.rs index a40e5a95542e9..b892efbf569bb 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,14 +1,178 @@ -use rustc::hir::def_id::DefId; use rustc::traits::{self, Reveal, SelectionContext}; -use rustc::ty::subst::Substs; -use rustc::ty; -use error::EvalResult; use eval_context::EvalContext; use memory::Pointer; -use terminator::{get_impl_method, ImplMethod}; + +use rustc::hir::def_id::DefId; +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 error::{EvalError, EvalResult}; +use memory::Function; +use value::PrimVal; +use value::Value; 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)> { + 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)?; + temporaries.push(ptr); + ptr + }, + Value::ByValPair(a, b) => { + let ptr = self.alloc_ptr(args[0].1)?; + self.write_pair_to_ptr(a, b, ptr, args[0].1)?; + temporaries.push(ptr); + ptr + }, + }; + 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.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) => { + trace!("sig: {:#?}", fn_def.sig); + args[0] = ( + Value::ByVal(PrimVal::Ptr(self_ptr)), + fn_def.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) => { + 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 = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; + assert_eq!(sig, fn_def.sig); + 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 + // looking for -- that is, what specific impl. + self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // 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| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) + }) + } + /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -230,3 +394,79 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (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) + } + } +} + +/// 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) + } + } +}