Skip to content

Commit

Permalink
Move relevant code into "traits" module.
Browse files Browse the repository at this point in the history
  • Loading branch information
solson committed Feb 10, 2017
1 parent d8d813c commit 68daab9
Show file tree
Hide file tree
Showing 2 changed files with 250 additions and 246 deletions.
246 changes: 5 additions & 241 deletions src/terminator/mod.rs
@@ -1,25 +1,22 @@
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;

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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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<Pointer>)> {
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)
}
}
}

0 comments on commit 68daab9

Please sign in to comment.