1
Fork 0

Compiles again

This commit is contained in:
Oliver Schneider 2017-03-21 13:53:55 +01:00
parent c6a18cead8
commit dc1b0fb436
9 changed files with 212 additions and 923 deletions

View file

@ -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",
}

View file

@ -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<Value>,
/// 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<bool, ()>
{
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(()),
}
}

View file

@ -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<mir::Promoted>,
@ -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),

View file

@ -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<AllocId, Function<'tcx>>,
functions: HashMap<AllocId, ty::Instance<'tcx>>,
/// Inverse map of `functions` so we don't allocate a new pointer every time we need one
function_alloc_cache: HashMap<Function<'tcx>, AllocId>,
function_alloc_cache: HashMap<ty::Instance<'tcx>, 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]> {

View file

@ -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))
});
}
}

View file

@ -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<T>(ptr: *mut T)
// is the same as
// fn drop(&mut self) if Self is Box<T>
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<I>(
&mut self,
fields: I,
lval: Lvalue<'tcx>,
ty: Ty<'tcx>,
drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>,
) -> EvalResult<'tcx>
where I: Iterator<Item=Ty<'tcx>>,
{
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())
}
}

View file

@ -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");

View file

@ -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(())
}
}

View file

@ -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<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> {
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::<Vec<_>>()
.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<Ty<'tcx>>> {
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)
}
}
}