Compiles again
This commit is contained in:
parent
c6a18cead8
commit
dc1b0fb436
9 changed files with 212 additions and 923 deletions
11
src/error.rs
11
src/error.rs
|
@ -2,7 +2,7 @@ use std::error::Error;
|
|||
use std::fmt;
|
||||
use rustc::mir;
|
||||
use rustc::ty::{FnSig, Ty, layout};
|
||||
use memory::{Pointer, Function};
|
||||
use memory::Pointer;
|
||||
use rustc_const_math::ConstMathErr;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
|
@ -52,9 +52,6 @@ pub enum EvalError<'tcx> {
|
|||
DeallocatedStaticMemory,
|
||||
Layout(layout::LayoutError<'tcx>),
|
||||
Unreachable,
|
||||
ExpectedConcreteFunction(Function<'tcx>),
|
||||
ExpectedDropGlue(Function<'tcx>),
|
||||
ManuallyCalledDropGlue,
|
||||
Panic,
|
||||
}
|
||||
|
||||
|
@ -128,12 +125,6 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"attempted to get length of a null terminated string, but no null found before end of allocation",
|
||||
EvalError::Unreachable =>
|
||||
"entered unreachable code",
|
||||
EvalError::ExpectedConcreteFunction(_) =>
|
||||
"tried to use glue function as function",
|
||||
EvalError::ExpectedDropGlue(_) =>
|
||||
"tried to use non-drop-glue function as drop glue",
|
||||
EvalError::ManuallyCalledDropGlue =>
|
||||
"tried to manually invoke drop glue",
|
||||
EvalError::Panic =>
|
||||
"the evaluated program panicked",
|
||||
}
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
124
src/memory.rs
124
src/memory.rs
|
@ -3,7 +3,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet
|
|||
use std::{fmt, iter, ptr, mem, io};
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::{self, PolyFnSig, ClosureSubsts};
|
||||
use rustc::ty;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::layout::{self, TargetDataLayout};
|
||||
|
||||
|
@ -102,44 +102,6 @@ impl Pointer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
||||
/// Identifies a specific monomorphized function
|
||||
pub struct FunctionDefinition<'tcx> {
|
||||
pub def_id: DefId,
|
||||
pub substs: &'tcx Substs<'tcx>,
|
||||
pub sig: PolyFnSig<'tcx>,
|
||||
}
|
||||
|
||||
/// Either a concrete function, or a glue function
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
||||
pub enum Function<'tcx> {
|
||||
/// A function or method created by compiling code
|
||||
Concrete(FunctionDefinition<'tcx>),
|
||||
/// Glue required to call a regular function through a Fn(Mut|Once) trait object
|
||||
FnDefAsTraitObject(FunctionDefinition<'tcx>),
|
||||
/// A drop glue function only needs to know the real type, and then miri can extract
|
||||
/// that type from a vtable's drop pointer.
|
||||
/// Instead of storing some drop function, we act as if there are no trait objects, by
|
||||
/// mapping trait objects to their real types before acting on them.
|
||||
DropGlue(ty::Ty<'tcx>),
|
||||
/// Glue required to treat the ptr part of a fat pointer
|
||||
/// as a function pointer
|
||||
FnPtrAsTraitObject(PolyFnSig<'tcx>),
|
||||
/// Glue for Closures
|
||||
Closure(FunctionDefinition<'tcx>),
|
||||
/// Glue for noncapturing closures casted to function pointers
|
||||
NonCaptureClosureAsFnPtr(FunctionDefinition<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> Function<'tcx> {
|
||||
pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> {
|
||||
match self {
|
||||
Function::DropGlue(real_ty) => Ok(real_ty),
|
||||
other => Err(EvalError::ExpectedDropGlue(other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Top-level interpreter memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -165,10 +127,10 @@ pub struct Memory<'a, 'tcx> {
|
|||
|
||||
/// Function "allocations". They exist solely so pointers have something to point to, and
|
||||
/// we can figure out what they point to.
|
||||
functions: HashMap<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]> {
|
||||
|
|
30
src/step.rs
30
src/step.rs
|
@ -45,8 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let mut new = Ok(0);
|
||||
ConstantExtractor {
|
||||
span: stmt.source_info.span,
|
||||
substs: self.substs(),
|
||||
def_id: self.frame().def_id,
|
||||
instance: self.frame().instance,
|
||||
ecx: self,
|
||||
mir: Ref::clone(&mir),
|
||||
new_constants: &mut new,
|
||||
|
@ -63,8 +62,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let mut new = Ok(0);
|
||||
ConstantExtractor {
|
||||
span: terminator.source_info.span,
|
||||
substs: self.substs(),
|
||||
def_id: self.frame().def_id,
|
||||
instance: self.frame().instance,
|
||||
ecx: self,
|
||||
mir: Ref::clone(&mir),
|
||||
new_constants: &mut new,
|
||||
|
@ -145,8 +143,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
|
|||
span: Span,
|
||||
ecx: &'a mut EvalContext<'b, 'tcx>,
|
||||
mir: MirRef<'tcx>,
|
||||
def_id: DefId,
|
||||
substs: &'tcx subst::Substs<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
new_constants: &'a mut EvalResult<'tcx, u64>,
|
||||
}
|
||||
|
||||
|
@ -158,26 +155,24 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
|||
span: Span,
|
||||
shared: bool,
|
||||
) {
|
||||
let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs);
|
||||
let cid = GlobalId { def_id, substs, promoted: None };
|
||||
let instance = self.ecx.resolve_associated_const(def_id, substs);
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
if self.ecx.globals.contains_key(&cid) {
|
||||
return;
|
||||
}
|
||||
self.try(|this| {
|
||||
let mir = this.ecx.load_mir(def_id)?;
|
||||
let mir = this.ecx.load_mir(instance.def)?;
|
||||
this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
|
||||
let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe();
|
||||
let cleanup = StackPopCleanup::MarkStatic(mutable);
|
||||
let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
|
||||
trace!("pushing stack frame for global: {}", name);
|
||||
this.ecx.push_stack_frame(
|
||||
def_id,
|
||||
instance,
|
||||
span,
|
||||
mir,
|
||||
substs,
|
||||
Lvalue::Global(cid),
|
||||
cleanup,
|
||||
Vec::new(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
@ -210,8 +205,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
|
|||
},
|
||||
mir::Literal::Promoted { index } => {
|
||||
let cid = GlobalId {
|
||||
def_id: self.def_id,
|
||||
substs: self.substs,
|
||||
instance: self.instance,
|
||||
promoted: Some(index),
|
||||
};
|
||||
if self.ecx.globals.contains_key(&cid) {
|
||||
|
@ -220,16 +214,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
|
|||
let mir = Ref::clone(&self.mir);
|
||||
let mir = Ref::map(mir, |mir| &mir.promoted[index]);
|
||||
self.try(|this| {
|
||||
let ty = this.ecx.monomorphize(mir.return_ty, this.substs);
|
||||
let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
|
||||
this.ecx.globals.insert(cid, Global::uninitialized(ty));
|
||||
trace!("pushing stack frame for {:?}", index);
|
||||
this.ecx.push_stack_frame(this.def_id,
|
||||
this.ecx.push_stack_frame(this.instance,
|
||||
constant.span,
|
||||
mir,
|
||||
this.substs,
|
||||
Lvalue::Global(cid),
|
||||
StackPopCleanup::MarkStatic(false),
|
||||
Vec::new())
|
||||
StackPopCleanup::MarkStatic(false))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
262
src/traits.rs
262
src/traits.rs
|
@ -8,171 +8,11 @@ use rustc::ty::fold::TypeFoldable;
|
|||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
use syntax::{ast, abi};
|
||||
use syntax::ast;
|
||||
|
||||
use error::{EvalError, EvalResult};
|
||||
use memory::Function;
|
||||
use value::PrimVal;
|
||||
use value::Value;
|
||||
use error::EvalResult;
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
/// Trait method, which has to be resolved to an impl method.
|
||||
pub(crate) fn trait_method(
|
||||
&mut self,
|
||||
trait_id: DefId,
|
||||
def_id: DefId,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
args: &mut Vec<(Value, Ty<'tcx>)>,
|
||||
) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec<(Pointer, Ty<'tcx>)>)> {
|
||||
let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs);
|
||||
let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref));
|
||||
|
||||
match self.fulfill_obligation(trait_ref) {
|
||||
traits::VtableImpl(vtable_impl) => {
|
||||
let impl_did = vtable_impl.impl_def_id;
|
||||
let mname = self.tcx.item_name(def_id);
|
||||
// Create a concatenated set of substitutions which includes those from the impl
|
||||
// and those from the method:
|
||||
let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname);
|
||||
|
||||
Ok((did, substs, Vec::new()))
|
||||
}
|
||||
|
||||
traits::VtableClosure(vtable_closure) => {
|
||||
let trait_closure_kind = self.tcx
|
||||
.lang_items
|
||||
.fn_trait_kind(trait_id)
|
||||
.expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation");
|
||||
let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id);
|
||||
trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind);
|
||||
self.unpack_fn_args(args)?;
|
||||
let mut temporaries = Vec::new();
|
||||
match (closure_kind, trait_closure_kind) {
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
|
||||
(ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) |
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed.
|
||||
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
|
||||
// The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`.
|
||||
// We want a `fn(self, ...)`.
|
||||
// We can produce this by doing something like:
|
||||
//
|
||||
// fn call_once(self, ...) { call_mut(&self, ...) }
|
||||
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
|
||||
//
|
||||
// These are both the same at trans time.
|
||||
|
||||
// Interpreter magic: insert an intermediate pointer, so we can skip the
|
||||
// intermediate function call.
|
||||
let ptr = match args[0].0 {
|
||||
Value::ByRef(ptr) => ptr,
|
||||
Value::ByVal(primval) => {
|
||||
let ptr = self.alloc_ptr(args[0].1)?;
|
||||
let size = self.type_size(args[0].1)?.expect("closures are sized");
|
||||
self.memory.write_primval(ptr, primval, size)?;
|
||||
ptr
|
||||
},
|
||||
Value::ByValPair(a, b) => {
|
||||
let ptr = self.alloc_ptr(args[0].1)?;
|
||||
self.write_pair_to_ptr(a, b, ptr, args[0].1)?;
|
||||
ptr
|
||||
},
|
||||
};
|
||||
temporaries.push((ptr, args[0].1));
|
||||
args[0].0 = Value::ByVal(PrimVal::Ptr(ptr));
|
||||
args[0].1 = self.tcx.mk_mut_ptr(args[0].1);
|
||||
}
|
||||
|
||||
_ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind),
|
||||
}
|
||||
Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries))
|
||||
}
|
||||
|
||||
traits::VtableFnPointer(vtable_fn_ptr) => {
|
||||
if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty {
|
||||
args.remove(0);
|
||||
self.unpack_fn_args(args)?;
|
||||
Ok((did, substs, Vec::new()))
|
||||
} else {
|
||||
bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
traits::VtableObject(ref data) => {
|
||||
let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64;
|
||||
if args.is_empty() {
|
||||
return Err(EvalError::VtableForArgumentlessMethod);
|
||||
}
|
||||
let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?;
|
||||
let idx = idx + 3;
|
||||
let offset = idx * self.memory.pointer_size();
|
||||
let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?;
|
||||
trace!("args: {:#?}", args);
|
||||
match self.memory.get_fn(fn_ptr.alloc_id)? {
|
||||
Function::FnDefAsTraitObject(fn_def) => {
|
||||
trace!("sig: {:#?}", fn_def.sig);
|
||||
assert!(fn_def.sig.abi() != abi::Abi::RustCall);
|
||||
assert_eq!(args.len(), 2);
|
||||
// a function item turned into a closure trait object
|
||||
// the first arg is just there to give use the vtable
|
||||
args.remove(0);
|
||||
self.unpack_fn_args(args)?;
|
||||
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
|
||||
},
|
||||
Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue),
|
||||
Function::Concrete(fn_def) => {
|
||||
let sig = self.erase_lifetimes(&fn_def.sig);
|
||||
trace!("sig: {:#?}", sig);
|
||||
args[0] = (
|
||||
Value::ByVal(PrimVal::Ptr(self_ptr)),
|
||||
sig.inputs()[0],
|
||||
);
|
||||
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
|
||||
},
|
||||
Function::NonCaptureClosureAsFnPtr(fn_def) => {
|
||||
let sig = self.erase_lifetimes(&fn_def.sig);
|
||||
args.insert(0, (
|
||||
Value::ByVal(PrimVal::Undef),
|
||||
sig.inputs()[0],
|
||||
));
|
||||
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
|
||||
}
|
||||
Function::Closure(fn_def) => {
|
||||
self.unpack_fn_args(args)?;
|
||||
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
|
||||
}
|
||||
Function::FnPtrAsTraitObject(sig) => {
|
||||
let sig = self.erase_lifetimes(&sig);
|
||||
trace!("sig: {:#?}", sig);
|
||||
// the first argument was the fat ptr
|
||||
args.remove(0);
|
||||
self.unpack_fn_args(args)?;
|
||||
let fn_ptr = self.memory.read_ptr(self_ptr)?;
|
||||
let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? {
|
||||
Function::Concrete(fn_def) => {
|
||||
let fn_def_sig = self.erase_lifetimes(&fn_def.sig);
|
||||
assert_eq!(sig, fn_def_sig);
|
||||
fn_def
|
||||
},
|
||||
Function::NonCaptureClosureAsFnPtr(fn_def) => {
|
||||
let fn_def_sig = self.erase_lifetimes(&fn_def.sig);
|
||||
args.insert(0, (
|
||||
Value::ByVal(PrimVal::Undef),
|
||||
fn_def_sig.inputs()[0],
|
||||
));
|
||||
fn_def
|
||||
},
|
||||
other => bug!("FnPtrAsTraitObject for {:?}", other),
|
||||
};
|
||||
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
|
||||
}
|
||||
}
|
||||
},
|
||||
vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> {
|
||||
// Do the initial selection for the obligation. This yields the shallow result we are
|
||||
|
@ -202,7 +42,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
/// The `trait_ref` encodes the erased self type. Hence if we are
|
||||
/// making an object `Foo<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue