implement calling methods through trait objects
This commit is contained in:
parent
168d9e7745
commit
00c551c5f0
6 changed files with 347 additions and 47 deletions
|
@ -40,6 +40,8 @@ pub enum EvalError<'tcx> {
|
|||
required: usize,
|
||||
has: usize,
|
||||
},
|
||||
CalledClosureAsFunction,
|
||||
VtableForArgumentlessMethod,
|
||||
}
|
||||
|
||||
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
||||
|
@ -88,6 +90,10 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"reached the configured maximum number of stack frames",
|
||||
EvalError::AlignmentCheckFailed{..} =>
|
||||
"tried to execute a misaligned read or write",
|
||||
EvalError::CalledClosureAsFunction =>
|
||||
"tried to call a closure through a function pointer",
|
||||
EvalError::VtableForArgumentlessMethod =>
|
||||
"tried to call a vtable function without arguments",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ use std::collections::HashMap;
|
|||
mod step;
|
||||
mod terminator;
|
||||
mod cast;
|
||||
mod vtable;
|
||||
|
||||
pub struct EvalContext<'a, 'tcx: 'a> {
|
||||
/// The results of the type checker, from rustc.
|
||||
|
@ -108,7 +109,7 @@ struct Lvalue {
|
|||
enum LvalueExtra {
|
||||
None,
|
||||
Length(u64),
|
||||
// TODO(solson): Vtable(memory::AllocId),
|
||||
Vtable(Pointer),
|
||||
DowncastVariant(usize),
|
||||
}
|
||||
|
||||
|
@ -569,6 +570,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
LvalueExtra::Length(len) => {
|
||||
self.memory.write_usize(extra, len)?;
|
||||
}
|
||||
LvalueExtra::Vtable(ptr) => {
|
||||
self.memory.write_ptr(extra, ptr)?;
|
||||
},
|
||||
LvalueExtra::DowncastVariant(..) =>
|
||||
bug!("attempted to take a reference to an enum downcast lvalue"),
|
||||
}
|
||||
|
@ -587,6 +591,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Unsize => {
|
||||
let src = self.eval_operand(operand)?;
|
||||
let src_ty = self.operand_ty(operand);
|
||||
let dest_ty = self.monomorphize(dest_ty, self.substs());
|
||||
assert!(self.type_is_fat_ptr(dest_ty));
|
||||
let (ptr, extra) = self.get_fat_ptr(dest);
|
||||
self.move_(src, ptr, src_ty)?;
|
||||
let src_pointee_ty = pointee_type(src_ty).unwrap();
|
||||
|
@ -596,8 +602,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
self.memory.write_usize(extra, length as u64)?;
|
||||
}
|
||||
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
|
||||
// For now, upcasts are limited to changes in marker
|
||||
// traits, and hence never actually require an actual
|
||||
// change to the vtable.
|
||||
let (_, src_extra) = self.get_fat_ptr(src);
|
||||
let src_extra = self.memory.read_ptr(src_extra)?;
|
||||
self.memory.write_ptr(extra, src_extra)?;
|
||||
},
|
||||
(_, &ty::TyTrait(ref data)) => {
|
||||
let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty);
|
||||
let trait_ref = self.tcx.erase_regions(&trait_ref);
|
||||
let vtable = self.get_vtable(trait_ref)?;
|
||||
self.memory.write_ptr(extra, vtable)?;
|
||||
},
|
||||
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
|
||||
_ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,8 +658,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty::TyFnPtr(unsafe_fn_ty) => {
|
||||
let src = self.eval_operand(operand)?;
|
||||
let ptr = self.memory.read_ptr(src)?;
|
||||
let fn_def = self.memory.get_fn(ptr.alloc_id)?;
|
||||
let fn_ptr = self.memory.create_fn_ptr(fn_def.def_id, fn_def.substs, unsafe_fn_ty);
|
||||
let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?;
|
||||
let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty);
|
||||
self.memory.write_ptr(dest, fn_ptr)?;
|
||||
},
|
||||
ref other => bug!("fn to unsafe fn cast on {:?}", other),
|
||||
|
@ -827,7 +847,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
Deref => {
|
||||
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
|
||||
self.memory.dump(base.ptr.alloc_id);
|
||||
let ptr = self.memory.read_ptr(base.ptr)?;
|
||||
let extra = match pointee_ty.sty {
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
|
@ -835,7 +854,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let len = self.memory.read_usize(extra)?;
|
||||
LvalueExtra::Length(len)
|
||||
}
|
||||
ty::TyTrait(_) => unimplemented!(),
|
||||
ty::TyTrait(_) => {
|
||||
let (_, extra) = self.get_fat_ptr(base.ptr);
|
||||
let vtable = self.memory.read_ptr(extra)?;
|
||||
LvalueExtra::Vtable(vtable)
|
||||
},
|
||||
_ => LvalueExtra::None,
|
||||
};
|
||||
return Ok(Lvalue { ptr: ptr, extra: extra });
|
||||
|
|
|
@ -12,7 +12,7 @@ use syntax::codemap::{DUMMY_SP, Span};
|
|||
|
||||
use super::{EvalContext, IntegerExt};
|
||||
use error::{EvalError, EvalResult};
|
||||
use memory::{Pointer, FunctionDefinition};
|
||||
use memory::Pointer;
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
|
@ -90,7 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty::TyFnPtr(bare_fn_ty) => {
|
||||
let ptr = self.eval_operand(func)?;
|
||||
let fn_ptr = self.memory.read_ptr(ptr)?;
|
||||
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
|
||||
let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?;
|
||||
if fn_ty != bare_fn_ty {
|
||||
return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty));
|
||||
}
|
||||
|
@ -172,14 +172,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// TODO(solson): Adjust the first argument when calling a Fn or
|
||||
// FnMut closure via FnOnce::call_once.
|
||||
|
||||
// Only trait methods can have a Self parameter.
|
||||
let (resolved_def_id, resolved_substs) =
|
||||
if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
|
||||
self.trait_method(trait_id, def_id, substs)
|
||||
} else {
|
||||
(def_id, substs)
|
||||
};
|
||||
|
||||
let mut arg_srcs = Vec::new();
|
||||
for arg in args {
|
||||
let src = self.eval_operand(arg)?;
|
||||
|
@ -187,6 +179,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
arg_srcs.push((src, src_ty));
|
||||
}
|
||||
|
||||
// Only trait methods can have a Self parameter.
|
||||
let (resolved_def_id, resolved_substs) =
|
||||
if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
|
||||
self.trait_method(trait_id, def_id, substs, arg_srcs.get_mut(0))?
|
||||
} else {
|
||||
(def_id, substs)
|
||||
};
|
||||
|
||||
if fn_ty.abi == Abi::RustCall && !args.is_empty() {
|
||||
arg_srcs.pop();
|
||||
let last_arg = args.last().unwrap();
|
||||
|
@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> {
|
||||
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(None, None, Reveal::All).enter(|infcx| {
|
||||
|
@ -491,8 +491,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
&self,
|
||||
trait_id: DefId,
|
||||
def_id: DefId,
|
||||
substs: &'tcx Substs<'tcx>
|
||||
) -> (DefId, &'tcx Substs<'tcx>) {
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
first_arg: Option<&mut (Pointer, Ty<'tcx>)>,
|
||||
) -> EvalResult<'tcx, (DefId, &'tcx Substs<'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));
|
||||
|
||||
|
@ -504,11 +505,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// and those from the method:
|
||||
let mth = get_impl_method(self.tcx, substs, impl_did, vtable_impl.substs, mname);
|
||||
|
||||
(mth.method.def_id, mth.substs)
|
||||
Ok((mth.method.def_id, mth.substs))
|
||||
}
|
||||
|
||||
traits::VtableClosure(vtable_closure) =>
|
||||
(vtable_closure.closure_def_id, vtable_closure.substs.func_substs),
|
||||
Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)),
|
||||
|
||||
traits::VtableFnPointer(_fn_ty) => {
|
||||
let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap();
|
||||
|
@ -524,14 +525,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
|
||||
}
|
||||
|
||||
traits::VtableObject(ref _data) => {
|
||||
unimplemented!()
|
||||
// Callee {
|
||||
// data: Virtual(traits::get_vtable_index_of_object_method(
|
||||
// tcx, data, def_id)),
|
||||
// ty: def_ty(tcx, def_id, substs)
|
||||
// }
|
||||
}
|
||||
traits::VtableObject(ref data) => {
|
||||
let idx = self.tcx.get_vtable_index_of_object_method(data, def_id);
|
||||
if let Some(&mut(first_arg, ref mut first_ty)) = first_arg {
|
||||
self.memory.dump(first_arg.alloc_id);
|
||||
println!("idx: {}", idx);
|
||||
let (_, vtable) = self.get_fat_ptr(first_arg);
|
||||
let vtable = self.memory.read_ptr(vtable)?;
|
||||
let idx = idx + 3;
|
||||
let offset = idx * self.memory.pointer_size();
|
||||
let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?;
|
||||
let (def_id, substs, ty) = self.memory.get_fn(fn_ptr.alloc_id)?;
|
||||
// FIXME: skip_binder is wrong for HKL
|
||||
*first_ty = ty.sig.skip_binder().inputs[0];
|
||||
Ok((def_id, substs))
|
||||
} else {
|
||||
Err(EvalError::VtableForArgumentlessMethod)
|
||||
}
|
||||
},
|
||||
vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable),
|
||||
}
|
||||
}
|
||||
|
@ -566,14 +577,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ImplMethod<'tcx> {
|
||||
method: Rc<ty::Method<'tcx>>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
is_provided: bool,
|
||||
pub (super) struct ImplMethod<'tcx> {
|
||||
pub (super) method: Rc<ty::Method<'tcx>>,
|
||||
pub (super) substs: &'tcx Substs<'tcx>,
|
||||
pub (super) is_provided: bool,
|
||||
}
|
||||
|
||||
/// Locates the applicable definition of a method, given its name.
|
||||
fn get_impl_method<'a, 'tcx>(
|
||||
pub (super) fn get_impl_method<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
|
|
195
src/interpreter/vtable.rs
Normal file
195
src/interpreter/vtable.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::traits::{self, Reveal, SelectionContext};
|
||||
use rustc::ty::subst::{Substs, Subst};
|
||||
use rustc::ty;
|
||||
|
||||
use super::EvalContext;
|
||||
use error::EvalResult;
|
||||
use memory::Pointer;
|
||||
use super::terminator::{get_impl_method, ImplMethod};
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
/// Creates a returns a dynamic vtable for the given type and vtable origin.
|
||||
/// This is used only for objects.
|
||||
///
|
||||
/// The `trait_ref` encodes the erased self type. Hence if we are
|
||||
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
|
||||
/// `trait_ref` would map `T:Trait`.
|
||||
pub fn get_vtable(&mut self, 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.clone()).flat_map(|trait_ref| {
|
||||
match self.fulfill_obligation(trait_ref.clone()) {
|
||||
// Should default trait error here?
|
||||
traits::VtableDefaultImpl(_) |
|
||||
traits::VtableBuiltin(_) => {
|
||||
Vec::new().into_iter()
|
||||
}
|
||||
traits::VtableImpl(
|
||||
traits::VtableImplData {
|
||||
impl_def_id: id,
|
||||
substs,
|
||||
nested: _ }) => {
|
||||
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, mth.method.fty)
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
}
|
||||
traits::VtableClosure(
|
||||
traits::VtableClosureData {
|
||||
closure_def_id,
|
||||
substs,
|
||||
nested: _ }) => {
|
||||
let closure_type = self.tcx.closure_type(closure_def_id, substs);
|
||||
vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter()
|
||||
}
|
||||
traits::VtableFnPointer(
|
||||
traits::VtableFnPointerData {
|
||||
fn_ty: bare_fn_ty,
|
||||
nested: _ }) => {
|
||||
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
|
||||
//vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter()
|
||||
unimplemented!()
|
||||
}
|
||||
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());
|
||||
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()), ptr_size)?;
|
||||
|
||||
// FIXME: generate a destructor for the vtable.
|
||||
// trans does this with glue::get_drop_glue(ccx, trait_ref.self_ty())
|
||||
|
||||
self.memory.write_usize(vtable.offset(ptr_size as isize), size as u64)?;
|
||||
self.memory.write_usize(vtable.offset((ptr_size * 2) as isize), align as u64)?;
|
||||
|
||||
for (i, method) in methods.into_iter().enumerate() {
|
||||
if let Some(method) = method {
|
||||
self.memory.write_ptr(vtable.offset(ptr_size as isize * (3 + i as isize)), method)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vtable)
|
||||
}
|
||||
|
||||
fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec<Option<ImplMethod<'tcx>>> {
|
||||
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);
|
||||
|
||||
let trait_item_def_ids = self.tcx.trait_item_def_ids(trait_id);
|
||||
trait_item_def_ids
|
||||
.iter()
|
||||
|
||||
// Filter out non-method items.
|
||||
.filter_map(|item_def_id| {
|
||||
match *item_def_id {
|
||||
ty::MethodTraitItemId(def_id) => Some(def_id),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
||||
// Now produce pointers for each remaining method. If the
|
||||
// method could never be called from this object, just supply
|
||||
// null.
|
||||
.map(|trait_method_def_id| {
|
||||
debug!("get_vtable_methods: trait_method_def_id={:?}",
|
||||
trait_method_def_id);
|
||||
|
||||
let trait_method_type = match self.tcx.impl_or_trait_item(trait_method_def_id) {
|
||||
ty::MethodTraitItem(m) => m,
|
||||
_ => bug!("should be a method, not other assoc item"),
|
||||
};
|
||||
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 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_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 = mth.method.predicates.predicates.subst(self.tcx, &mth.substs);
|
||||
if !self.normalize_and_test_predicates(predicates) {
|
||||
debug!("get_vtable_methods: predicates do not hold");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
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<ty::Predicate<'tcx>>) -> bool {
|
||||
debug!("normalize_and_test_predicates(predicates={:?})",
|
||||
predicates);
|
||||
|
||||
self.tcx.infer_ctxt(None, None, 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()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
|
|||
use std::{fmt, iter, ptr};
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::BareFnTy;
|
||||
use rustc::ty::{BareFnTy, ClosureTy, ClosureSubsts};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::layout::{self, TargetDataLayout};
|
||||
|
||||
|
@ -53,11 +53,22 @@ impl Pointer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct FunctionDefinition<'tcx> {
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
struct FunctionDefinition<'tcx> {
|
||||
pub def_id: DefId,
|
||||
pub substs: &'tcx Substs<'tcx>,
|
||||
pub fn_ty: &'tcx BareFnTy<'tcx>,
|
||||
pub kind: FunctionKind<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
enum FunctionKind<'tcx> {
|
||||
Closure {
|
||||
substs: ClosureSubsts<'tcx>,
|
||||
ty: ClosureTy<'tcx>,
|
||||
},
|
||||
Function {
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
ty: &'tcx BareFnTy<'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -112,12 +123,27 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
self.alloc_map.iter()
|
||||
}
|
||||
|
||||
pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
|
||||
let def = FunctionDefinition {
|
||||
pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer {
|
||||
self.create_fn_alloc(FunctionDefinition {
|
||||
def_id: def_id,
|
||||
substs: substs,
|
||||
fn_ty: fn_ty,
|
||||
};
|
||||
kind: FunctionKind::Closure {
|
||||
substs: substs,
|
||||
ty: fn_ty,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
|
||||
self.create_fn_alloc(FunctionDefinition {
|
||||
def_id: def_id,
|
||||
kind: FunctionKind::Function {
|
||||
substs: substs,
|
||||
ty: fn_ty,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer {
|
||||
if let Some(&alloc_id) = self.function_alloc_cache.get(&def) {
|
||||
return Pointer {
|
||||
alloc_id: alloc_id,
|
||||
|
@ -127,7 +153,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
let id = self.next_id;
|
||||
debug!("creating fn ptr: {}", id);
|
||||
self.next_id.0 += 1;
|
||||
self.functions.insert(id, def);
|
||||
self.functions.insert(id, def.clone());
|
||||
self.function_alloc_cache.insert(def, id);
|
||||
Pointer {
|
||||
alloc_id: id,
|
||||
|
@ -269,10 +295,33 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, FunctionDefinition<'tcx>> {
|
||||
pub fn get_closure(&self, id: AllocId) -> EvalResult<'tcx, (DefId, ClosureSubsts<'tcx>, ClosureTy<'tcx>)> {
|
||||
debug!("reading closure fn ptr: {}", id);
|
||||
match self.functions.get(&id) {
|
||||
Some(&FunctionDefinition {
|
||||
def_id,
|
||||
kind: FunctionKind::Closure { ref substs, ref ty }
|
||||
}) => Ok((def_id, substs.clone(), ty.clone())),
|
||||
Some(&FunctionDefinition {
|
||||
kind: FunctionKind::Function { .. }, ..
|
||||
}) => Err(EvalError::CalledClosureAsFunction),
|
||||
None => match self.alloc_map.get(&id) {
|
||||
Some(_) => Err(EvalError::ExecuteMemory),
|
||||
None => Err(EvalError::InvalidFunctionPointer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, &'tcx BareFnTy<'tcx>)> {
|
||||
debug!("reading fn ptr: {}", id);
|
||||
match self.functions.get(&id) {
|
||||
Some(&fn_id) => Ok(fn_id),
|
||||
Some(&FunctionDefinition {
|
||||
def_id,
|
||||
kind: FunctionKind::Function { substs, ty }
|
||||
}) => Ok((def_id, substs, ty)),
|
||||
Some(&FunctionDefinition {
|
||||
kind: FunctionKind::Closure { .. }, ..
|
||||
}) => Err(EvalError::CalledClosureAsFunction),
|
||||
None => match self.alloc_map.get(&id) {
|
||||
Some(_) => Err(EvalError::ExecuteMemory),
|
||||
None => Err(EvalError::InvalidFunctionPointer),
|
||||
|
|
16
tests/run-pass/traits.rs
Normal file
16
tests/run-pass/traits.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
struct Struct(i32);
|
||||
|
||||
trait Trait {
|
||||
fn method(&self);
|
||||
}
|
||||
|
||||
impl Trait for Struct {
|
||||
fn method(&self) {
|
||||
assert_eq!(self.0, 42);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let y: &Trait = &Struct(42);
|
||||
y.method();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue