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 std::fmt;
use rustc::mir; use rustc::mir;
use rustc::ty::{FnSig, Ty, layout}; use rustc::ty::{FnSig, Ty, layout};
use memory::{Pointer, Function}; use memory::Pointer;
use rustc_const_math::ConstMathErr; use rustc_const_math::ConstMathErr;
use syntax::codemap::Span; use syntax::codemap::Span;
@ -52,9 +52,6 @@ pub enum EvalError<'tcx> {
DeallocatedStaticMemory, DeallocatedStaticMemory,
Layout(layout::LayoutError<'tcx>), Layout(layout::LayoutError<'tcx>),
Unreachable, Unreachable,
ExpectedConcreteFunction(Function<'tcx>),
ExpectedDropGlue(Function<'tcx>),
ManuallyCalledDropGlue,
Panic, 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", "attempted to get length of a null terminated string, but no null found before end of allocation",
EvalError::Unreachable => EvalError::Unreachable =>
"entered unreachable code", "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 => EvalError::Panic =>
"the evaluated program panicked", "the evaluated program panicked",
} }

View file

@ -8,7 +8,7 @@ use rustc::middle::const_val::ConstVal;
use rustc::mir; use rustc::mir;
use rustc::traits::Reveal; use rustc::traits::Reveal;
use rustc::ty::layout::{self, Layout, Size}; 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::ty::{self, Ty, TyCtxt, TypeFoldable, Binder};
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
use syntax::codemap::{self, DUMMY_SP}; use syntax::codemap::{self, DUMMY_SP};
@ -52,11 +52,8 @@ pub struct Frame<'tcx> {
/// The MIR for the function called on this frame. /// The MIR for the function called on this frame.
pub mir: MirRef<'tcx>, pub mir: MirRef<'tcx>,
/// The def_id of the current function. /// The def_id and substs of the current function
pub def_id: DefId, pub instance: ty::Instance<'tcx>,
/// type substitutions for the current function invocation.
pub substs: &'tcx Substs<'tcx>,
/// The span of the call site. /// The span of the call site.
pub span: codemap::Span, pub span: codemap::Span,
@ -78,12 +75,6 @@ pub struct Frame<'tcx> {
/// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`. /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`.
pub locals: Vec<Value>, 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 // 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) ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP)
} }
pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, MirRef<'tcx>> { pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, MirRef<'tcx>> {
trace!("load mir {:?}", def_id); trace!("load mir {:?}", instance);
if def_id.is_local() || self.tcx.sess.cstore.is_item_mir_available(def_id) { match instance {
Ok(self.tcx.item_mir(def_id)) ty::InstanceDef::Item(def_id) => self.tcx.maybe_item_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))),
} else { _ => Ok(self.tcx.instance_mir(instance)),
Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id)))
} }
} }
@ -272,13 +262,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn push_stack_frame( pub fn push_stack_frame(
&mut self, &mut self,
def_id: DefId, instance: ty::Instance<'tcx>,
span: codemap::Span, span: codemap::Span,
mir: MirRef<'tcx>, mir: MirRef<'tcx>,
substs: &'tcx Substs<'tcx>,
return_lvalue: Lvalue<'tcx>, return_lvalue: Lvalue<'tcx>,
return_to_block: StackPopCleanup, return_to_block: StackPopCleanup,
temporaries: Vec<(Pointer, Ty<'tcx>)>,
) -> EvalResult<'tcx> { ) -> EvalResult<'tcx> {
::log_settings::settings().indentation += 1; ::log_settings::settings().indentation += 1;
@ -293,10 +281,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
return_to_block, return_to_block,
return_lvalue, return_lvalue,
locals, locals,
interpreter_temporaries: temporaries,
span, span,
def_id, instance,
substs,
stmt: 0, 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(()) Ok(())
} }
@ -665,8 +645,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
ReifyFnPointer => match self.operand_ty(operand).sty { ReifyFnPointer => match self.operand_ty(operand).sty {
ty::TyFnDef(def_id, substs, sig) => { ty::TyFnDef(def_id, substs, _) => {
let fn_ptr = self.memory.create_fn_ptr(def_id, substs, sig); let fn_ptr = self.memory.create_fn_ptr(def_id, substs);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
}, },
ref other => bug!("reify fn pointer on {:?}", other), 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 { ClosureFnPointer => match self.operand_ty(operand).sty {
ty::TyClosure(def_id, substs) => { ty::TyClosure(def_id, substs) => {
let fn_ty = self.tcx.closure_type(def_id); let instance = resolve_closure(self.tcx, def_id, substs, ty::ClosureKind::FnOnce);
let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(def_id, substs, fn_ty); let fn_ptr = self.memory.create_fn_alloc(instance);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
}, },
ref other => bug!("reify fn pointer on {:?}", other), ref other => bug!("reify fn pointer on {:?}", other),
@ -845,16 +825,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// function items are zero sized // function items are zero sized
Value::ByRef(self.memory.allocate(0, 0)?) Value::ByRef(self.memory.allocate(0, 0)?)
} else { } else {
let (def_id, substs) = self.resolve_associated_const(def_id, substs); let instance = self.resolve_associated_const(def_id, substs);
let cid = GlobalId { def_id, substs, promoted: None }; let cid = GlobalId { instance, promoted: None };
self.globals.get(&cid).expect("static/const not cached").value self.globals.get(&cid).expect("static/const not cached").value
} }
} }
Literal::Promoted { index } => { Literal::Promoted { index } => {
let cid = GlobalId { let cid = GlobalId {
def_id: self.frame().def_id, instance: self.frame().instance,
substs: self.substs(),
promoted: Some(index), promoted: Some(index),
}; };
self.globals.get(&cid).expect("promoted not cached").value self.globals.get(&cid).expect("promoted not cached").value
@ -891,8 +870,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}, },
val => { val => {
let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.stack[frame].mir.local_decls[local].ty;
let ty = self.monomorphize(ty, self.stack[frame].substs); let ty = self.monomorphize(ty, self.stack[frame].instance.substs);
let substs = self.stack[frame].substs; let substs = self.stack[frame].instance.substs;
let ptr = self.alloc_ptr_with_substs(ty, substs)?; let ptr = self.alloc_ptr_with_substs(ty, substs)?;
self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr);
self.write_value_to_ptr(val, ptr, ty)?; self.write_value_to_ptr(val, ptr, ty)?;
@ -911,7 +890,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match global_val.value { match global_val.value {
Value::ByRef(ptr) => Lvalue::from_ptr(ptr), 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.memory.mark_static(ptr.alloc_id);
self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?;
// see comment on `initialized` field // see comment on `initialized` field
@ -1289,7 +1268,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
pub(super) fn substs(&self) -> &'tcx Substs<'tcx> { pub(super) fn substs(&self) -> &'tcx Substs<'tcx> {
self.frame().substs self.frame().instance.substs
} }
fn unsize_into_ptr( fn unsize_into_ptr(
@ -1320,7 +1299,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
(_, &ty::TyDynamic(ref data, _)) => { (_, &ty::TyDynamic(ref data, _)) => {
let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty);
let trait_ref = self.tcx.erase_regions(&trait_ref); 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 = src.read_ptr(&self.memory)?;
let ptr = PrimVal::Ptr(ptr); let ptr = PrimVal::Ptr(ptr);
let extra = PrimVal::Ptr(vtable); let extra = PrimVal::Ptr(vtable);
@ -1519,7 +1498,8 @@ pub fn eval_main<'a, 'tcx: 'a>(
limits: ResourceLimits, limits: ResourceLimits,
) { ) {
let mut ecx = EvalContext::new(tcx, limits); 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 { if !mir.return_ty.is_nil() || mir.arg_count != 0 {
let msg = "miri does not support main functions without `fn()` type signatures"; 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( ecx.push_stack_frame(
def_id, instance,
DUMMY_SP, DUMMY_SP,
mir, mir,
tcx.intern_substs(&[]),
Lvalue::from_ptr(Pointer::zst_ptr()), Lvalue::from_ptr(Pointer::zst_ptr()),
StackPopCleanup::None, StackPopCleanup::None,
Vec::new(),
).expect("could not allocate first stack frame"); ).expect("could not allocate first stack frame");
loop { loop {
@ -1564,23 +1542,12 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
block.terminator().source_info.span block.terminator().source_info.span
}; };
let mut err = tcx.sess.struct_span_err(span, &e.to_string()); let mut err = tcx.sess.struct_span_err(span, &e.to_string());
for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { for &Frame { instance, span, .. } in ecx.stack().iter().rev() {
if tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
err.span_note(span, "inside call to closure"); err.span_note(span, "inside call to closure");
continue; continue;
} }
// FIXME(solson): Find a way to do this without this Display impl hack. err.span_note(span, &format!("inside call to {}", instance));
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.emit(); err.emit();
} }
@ -1657,3 +1624,80 @@ impl<'b, 'tcx: 'b> IntoValTyPair<'tcx> for &'b mir::Operand<'tcx> {
Ok((value, value_ty)) 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::mir;
use rustc::ty::layout::{Size, Align}; use rustc::ty::layout::{Size, Align};
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
@ -42,15 +40,9 @@ pub enum LvalueExtra {
/// Uniquely identifies a specific constant or static. /// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalId<'tcx> { pub struct GlobalId<'tcx> {
/// For a constant or static, the `DefId` of the item itself. /// For a constant or static, the `Instance` of the item itself.
/// For a promoted global, the `DefId` of the function they belong to. /// For a promoted global, the `Instance` of the function they belong to.
pub(super) def_id: DefId, pub(super) instance: ty::Instance<'tcx>,
/// 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>,
/// The index for promoted globals within their function's `Mir`. /// The index for promoted globals within their function's `Mir`.
pub(super) promoted: Option<mir::Promoted>, 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 }, Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None },
Static(ref static_) => { Static(ref static_) => {
let substs = self.tcx.intern_substs(&[]); let instance = ty::Instance::mono(self.tcx, static_.def_id);
Lvalue::Global(GlobalId { def_id: static_.def_id, substs, promoted: None }) Lvalue::Global(GlobalId { instance, promoted: None })
} }
Projection(ref proj) => return self.eval_lvalue_projection(proj), 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 std::{fmt, iter, ptr, mem, io};
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::{self, PolyFnSig, ClosureSubsts}; use rustc::ty;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::ty::layout::{self, TargetDataLayout}; 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 // 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 /// Function "allocations". They exist solely so pointers have something to point to, and
/// we can figure out what they point to. /// 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 /// 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. /// Target machine data layout to emulate.
pub layout: &'a TargetDataLayout, pub layout: &'a TargetDataLayout,
@ -214,55 +176,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
self.alloc_map.iter() self.alloc_map.iter()
} }
pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer {
self.create_fn_alloc(Function::Closure(FunctionDefinition { let instance = ty::Instance::new(def_id, substs);
def_id, self.create_fn_alloc(instance)
substs: substs.substs,
sig,
}))
} }
pub fn create_fn_ptr_from_noncapture_closure(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer {
self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition { if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) {
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) {
return Pointer::new(alloc_id, 0); return Pointer::new(alloc_id, 0);
} }
let id = self.next_id; let id = self.next_id;
debug!("creating fn ptr: {}", id); debug!("creating fn ptr: {}", id);
self.next_id.0 += 1; self.next_id.0 += 1;
self.functions.insert(id, def); self.functions.insert(id, instance);
self.function_alloc_cache.insert(def, id); self.function_alloc_cache.insert(instance, id);
Pointer::new(id, 0) 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); debug!("reading fn ptr: {}", id);
match self.functions.get(&id) { match self.functions.get(&id) {
Some(&fndef) => Ok(fndef), 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)) { let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) {
(Some(a), None) => a, (Some(a), None) => a,
(None, Some(&Function::Concrete(fn_def))) => { (None, Some(instance)) => {
trace!("{} {}", msg, dump_fn_def(fn_def)); trace!("{} {}", msg, instance);
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));
continue; continue;
}, },
(None, None) => { (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 /// Byte accessors
impl<'a, 'tcx> Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> {
fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { 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); let mut new = Ok(0);
ConstantExtractor { ConstantExtractor {
span: stmt.source_info.span, span: stmt.source_info.span,
substs: self.substs(), instance: self.frame().instance,
def_id: self.frame().def_id,
ecx: self, ecx: self,
mir: Ref::clone(&mir), mir: Ref::clone(&mir),
new_constants: &mut new, new_constants: &mut new,
@ -63,8 +62,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let mut new = Ok(0); let mut new = Ok(0);
ConstantExtractor { ConstantExtractor {
span: terminator.source_info.span, span: terminator.source_info.span,
substs: self.substs(), instance: self.frame().instance,
def_id: self.frame().def_id,
ecx: self, ecx: self,
mir: Ref::clone(&mir), mir: Ref::clone(&mir),
new_constants: &mut new, new_constants: &mut new,
@ -145,8 +143,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
span: Span, span: Span,
ecx: &'a mut EvalContext<'b, 'tcx>, ecx: &'a mut EvalContext<'b, 'tcx>,
mir: MirRef<'tcx>, mir: MirRef<'tcx>,
def_id: DefId, instance: ty::Instance<'tcx>,
substs: &'tcx subst::Substs<'tcx>,
new_constants: &'a mut EvalResult<'tcx, u64>, new_constants: &'a mut EvalResult<'tcx, u64>,
} }
@ -158,26 +155,24 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
span: Span, span: Span,
shared: bool, shared: bool,
) { ) {
let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs); let instance = self.ecx.resolve_associated_const(def_id, substs);
let cid = GlobalId { def_id, substs, promoted: None }; let cid = GlobalId { instance, promoted: None };
if self.ecx.globals.contains_key(&cid) { if self.ecx.globals.contains_key(&cid) {
return; return;
} }
self.try(|this| { 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)); this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe(); let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe();
let cleanup = StackPopCleanup::MarkStatic(mutable); let cleanup = StackPopCleanup::MarkStatic(mutable);
let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
trace!("pushing stack frame for global: {}", name); trace!("pushing stack frame for global: {}", name);
this.ecx.push_stack_frame( this.ecx.push_stack_frame(
def_id, instance,
span, span,
mir, mir,
substs,
Lvalue::Global(cid), Lvalue::Global(cid),
cleanup, cleanup,
Vec::new(),
) )
}); });
} }
@ -210,8 +205,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
}, },
mir::Literal::Promoted { index } => { mir::Literal::Promoted { index } => {
let cid = GlobalId { let cid = GlobalId {
def_id: self.def_id, instance: self.instance,
substs: self.substs,
promoted: Some(index), promoted: Some(index),
}; };
if self.ecx.globals.contains_key(&cid) { 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::clone(&self.mir);
let mir = Ref::map(mir, |mir| &mir.promoted[index]); let mir = Ref::map(mir, |mir| &mir.promoted[index]);
self.try(|this| { 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)); this.ecx.globals.insert(cid, Global::uninitialized(ty));
trace!("pushing stack frame for {:?}", index); trace!("pushing stack frame for {:?}", index);
this.ecx.push_stack_frame(this.def_id, this.ecx.push_stack_frame(this.instance,
constant.span, constant.span,
mir, mir,
this.substs,
Lvalue::Global(cid), Lvalue::Global(cid),
StackPopCleanup::MarkStatic(false), StackPopCleanup::MarkStatic(false))
Vec::new())
}); });
} }
} }

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::mir;
use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::layout::{Layout, Size, Align};
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
@ -13,8 +12,7 @@ use value::{PrimVal, PrimValKind, Value};
impl<'a, 'tcx> EvalContext<'a, 'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn call_intrinsic( pub(super) fn call_intrinsic(
&mut self, &mut self,
def_id: DefId, instance: ty::Instance<'tcx>,
substs: &'tcx Substs<'tcx>,
args: &[mir::Operand<'tcx>], args: &[mir::Operand<'tcx>],
dest: Lvalue<'tcx>, dest: Lvalue<'tcx>,
dest_ty: Ty<'tcx>, dest_ty: Ty<'tcx>,
@ -31,7 +29,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let f32 = self.tcx.types.f32; let f32 = self.tcx.types.f32;
let f64 = self.tcx.types.f64; 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 { match intrinsic_name {
"add_with_overflow" => "add_with_overflow" =>
self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?, 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_relaxed" |
"atomic_load_acq" | "atomic_load_acq" |
"volatile_load" => { "volatile_load" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let ptr = arg_vals[0].read_ptr(&self.memory)?; let ptr = arg_vals[0].read_ptr(&self.memory)?;
self.write_value(Value::ByRef(ptr), dest, ty)?; self.write_value(Value::ByRef(ptr), dest, ty)?;
} }
@ -69,7 +67,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"atomic_store_relaxed" | "atomic_store_relaxed" |
"atomic_store_rel" | "atomic_store_rel" |
"volatile_store" => { "volatile_store" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let dest = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], dest, ty)?; 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") => { _ 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 ptr = arg_vals[0].read_ptr(&self.memory)?;
let change = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, 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") => { _ 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 ptr = arg_vals[0].read_ptr(&self.memory)?;
let expect_old = self.value_to_primval(arg_vals[1], ty)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?;
let change = self.value_to_primval(arg_vals[2], 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_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_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" => { "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 ptr = arg_vals[0].read_ptr(&self.memory)?;
let change = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, ty)?; let old = self.read_value(ptr, ty)?;
@ -144,7 +142,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"copy" | "copy" |
"copy_nonoverlapping" => { "copy_nonoverlapping" => {
// FIXME: check whether overlapping occurs // 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_size = self.type_size(elem_ty)?.expect("cannot copy unsized value");
let elem_align = self.type_align(elem_ty)?; let elem_align = self.type_align(elem_ty)?;
let src = arg_vals[0].read_ptr(&self.memory)?; let src = arg_vals[0].read_ptr(&self.memory)?;
@ -157,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"cttz" | "cttz" |
"ctlz" | "ctlz" |
"bswap" => { "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 num = self.value_to_primval(arg_vals[0], ty)?;
let kind = self.ty_to_primval_kind(ty)?; let kind = self.ty_to_primval_kind(ty)?;
let num = numeric_intrinsic(intrinsic_name, num, kind)?; let num = numeric_intrinsic(intrinsic_name, num, kind)?;
@ -165,41 +163,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
"discriminant_value" => { "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 adt_ptr = arg_vals[0].read_ptr(&self.memory)?;
let discr_val = self.read_discriminant_value(adt_ptr, ty)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
self.write_primval(dest, PrimVal::Bytes(discr_val), dest_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" | "sinf32" | "fabsf32" | "cosf32" |
"sqrtf32" | "expf32" | "exp2f32" | "sqrtf32" | "expf32" | "exp2f32" |
"logf32" | "log10f32" | "log2f32" | "logf32" | "log10f32" | "log2f32" |
@ -247,7 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { "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 kind = self.ty_to_primval_kind(ty)?;
let a = self.value_to_primval(arg_vals[0], ty)?; let a = self.value_to_primval(arg_vals[0], ty)?;
let b = self.value_to_primval(arg_vals[1], 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) { Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) {
Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Ok(_) => Value::ByVal(PrimVal::Bytes(0)),
Err(_) => { 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)?; this.memory.write_repeat(ptr, 0, size)?;
Value::ByRef(ptr) Value::ByRef(ptr)
} }
@ -299,14 +268,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
"min_align_of" => { "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 elem_align = self.type_align(elem_ty)?;
let align_val = PrimVal::from_u128(elem_align as u128); let align_val = PrimVal::from_u128(elem_align as u128);
self.write_primval(dest, align_val, dest_ty)?; self.write_primval(dest, align_val, dest_ty)?;
} }
"pref_align_of" => { "pref_align_of" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let layout = self.type_layout(ty)?; let layout = self.type_layout(ty)?;
let align = layout.align(&self.tcx.data_layout).pref(); let align = layout.align(&self.tcx.data_layout).pref();
let align_val = PrimVal::from_u128(align as u128); let align_val = PrimVal::from_u128(align as u128);
@ -314,20 +283,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
"move_val_init" => { "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)?; let ptr = arg_vals[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], ptr, ty)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
} }
"needs_drop" => { "needs_drop" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let env = self.tcx.empty_parameter_environment(); let env = self.tcx.empty_parameter_environment();
let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env);
self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?;
} }
"offset" => { "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() // 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 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; 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" => { "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 // 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 // `size_of_val` intrinsic, then change this back to
// .expect("size_of intrinsic called on unsized value") // .expect("size_of intrinsic called on unsized value")
@ -398,32 +367,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
"size_of_val" => { "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])?; let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?;
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
} }
"min_align_of_val" | "min_align_of_val" |
"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])?; let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?;
self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?;
} }
"type_name" => { "type_name" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let ty_name = ty.to_string(); let ty_name = ty.to_string();
let s = self.str_to_value(&ty_name)?; let s = self.str_to_value(&ty_name)?;
self.write_value(s, dest, dest_ty)?; self.write_value(s, dest, dest_ty)?;
} }
"type_id" => { "type_id" => {
let ty = substs.type_at(0); let ty = instance.substs.type_at(0);
let n = self.tcx.type_id_hash(ty); let n = self.tcx.type_id_hash(ty);
self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
} }
"transmute" => { "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)?; self.write_value(arg_vals[0], dest, dest_ty)?;
} }
@ -449,7 +418,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"write_bytes" => { "write_bytes" => {
let u8 = self.tcx.types.u8; 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 ty_align = self.type_align(ty)?;
let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; 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"); 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::hir::def_id::DefId;
use rustc::mir; use rustc::mir;
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc_const_math::ConstInt;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::attr; use syntax::attr;
use syntax::abi::Abi; use syntax::abi::Abi;
@ -11,12 +8,11 @@ use syntax::abi::Abi;
use error::{EvalError, EvalResult}; use error::{EvalError, EvalResult};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
use lvalue::Lvalue; use lvalue::Lvalue;
use memory::{Pointer, FunctionDefinition, Function}; use memory::Pointer;
use value::PrimVal; use value::PrimVal;
use value::Value; use value::Value;
mod intrinsic; mod intrinsic;
mod drop;
impl<'a, 'tcx> EvalContext<'a, 'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { 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 func_ty = self.operand_ty(func);
let fn_def = match func_ty.sty { let fn_def = match func_ty.sty {
ty::TyFnPtr(bare_sig) => { ty::TyFnPtr(_) => {
let bare_sig = self.erase_lifetimes(&bare_sig);
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; 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)? self.memory.get_fn(fn_ptr.alloc_id)?
}, },
ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition { ty::TyFnDef(def_id, substs, _) => ty::Instance::new(def_id, substs),
def_id,
substs,
sig: fn_ty,
}),
_ => { _ => {
let msg = format!("can't handle callee of type {:?}", func_ty); let msg = format!("can't handle callee of type {:?}", func_ty);
return Err(EvalError::Unimplemented(msg)); 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)?; self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?;
} }
Drop { ref location, target, .. } => { Drop { .. } => unreachable!(),
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)?;
}
Assert { ref cond, expected, ref msg, target, .. } => { Assert { ref cond, expected, ref msg, target, .. } => {
let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; 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( fn eval_fn_call(
&mut self, &mut self,
fn_def: Function<'tcx>, instance: ty::Instance<'tcx>,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
arg_operands: &[mir::Operand<'tcx>], arg_operands: &[mir::Operand<'tcx>],
span: Span, span: Span,
) -> EvalResult<'tcx> { ) -> EvalResult<'tcx> {
use syntax::abi::Abi; let sig = match instance.def.def_ty(self.tcx).sty {
match fn_def { ty::TyFnPtr(bare_sig) => bare_sig,
// Intrinsics can only be addressed directly ty::TyFnDef(_, _, fn_ty) => fn_ty,
Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { ref other => bug!("expected function or pointer, got {:?}", other),
};
match sig.abi() {
Abi::RustIntrinsic => {
let sig = self.erase_lifetimes(&sig); let sig = self.erase_lifetimes(&sig);
let ty = sig.output(); let ty = sig.output();
let layout = self.type_layout(ty)?; 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, Some(dest) if is_inhabited(self.tcx, ty) => dest,
_ => return Err(EvalError::Unreachable), _ => 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); self.dump_local(ret);
Ok(()) Ok(())
}, },
// C functions can only be addressed directly Abi::C => {
Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => {
let sig = self.erase_lifetimes(&sig); let sig = self.erase_lifetimes(&sig);
let ty = sig.output(); let ty = sig.output();
let (ret, target) = destination.unwrap(); 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.dump_local(ret);
self.goto_block(target); self.goto_block(target);
Ok(()) Ok(())
}, },
Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), Abi::Rust | Abi::RustCall => {
Function::Concrete(FunctionDefinition { def_id, sig, substs }) if sig.abi() == Abi::Rust || sig.abi() == Abi::RustCall => {
let mut args = Vec::new(); let mut args = Vec::new();
for arg in arg_operands { for arg in arg_operands {
let arg_val = self.eval_operand(arg)?; let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg); let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty)); 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( self.eval_fn_call_inner(
resolved_def_id, instance,
resolved_substs,
destination, destination,
args, args,
temporaries,
span, span,
) )
}, },
Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { other => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", other))),
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))),
} }
} }
fn eval_fn_call_inner( fn eval_fn_call_inner(
&mut self, &mut self,
resolved_def_id: DefId, instance: ty::Instance<'tcx>,
resolved_substs: &'tcx Substs,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
args: Vec<(Value, Ty<'tcx>)>, args: Vec<(Value, Ty<'tcx>)>,
temporaries: Vec<(Pointer, Ty<'tcx>)>,
span: Span, span: Span,
) -> EvalResult<'tcx> { ) -> 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, Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => { Err(EvalError::NoMirFor(path)) => {
match &path[..] { match &path[..] {
@ -344,13 +208,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}; };
self.push_stack_frame( self.push_stack_frame(
resolved_def_id, instance,
span, span,
mir, mir,
resolved_substs,
return_lvalue, return_lvalue,
return_to_block, return_to_block,
temporaries,
)?; )?;
let arg_locals = self.frame().mir.args_iter(); let arg_locals = self.frame().mir.args_iter();
@ -530,33 +392,4 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// current frame. // current frame.
Ok(()) 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::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use syntax::codemap::DUMMY_SP; use syntax::codemap::DUMMY_SP;
use syntax::{ast, abi}; use syntax::ast;
use error::{EvalError, EvalResult}; use error::EvalResult;
use memory::Function;
use value::PrimVal;
use value::Value;
impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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, ()> { 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 // 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 /// 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 /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
/// `trait_ref` would map `T:Trait`. /// `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; let tcx = self.tcx;
debug!("get_vtable(trait_ref={:?})", trait_ref); debug!("get_vtable(trait_ref={:?})", trait_ref);
@ -219,13 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.get_vtable_methods(id, substs) self.get_vtable_methods(id, substs)
.into_iter() .into_iter()
.map(|opt_mth| opt_mth.map(|mth| { .map(|opt_mth| opt_mth.map(|mth| {
let fn_ty = self.tcx.item_type(mth.method.def_id); self.memory.create_fn_ptr(mth.method.def_id, mth.substs)
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)
})) }))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
@ -238,18 +72,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
.. ..
} }
) => { ) => {
let closure_type = self.tcx.closure_type(closure_def_id); let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce);
vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() vec![Some(self.memory.create_fn_alloc(instance))].into_iter()
} }
// turn a function definition into a Fn trait object // turn a function definition into a Fn trait object
traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => {
match fn_ty.sty { match fn_ty.sty {
ty::TyFnDef(did, substs, bare_fn_ty) => { ty::TyFnDef(did, substs, _) => {
vec![Some(self.memory.create_fn_as_trait_glue(did, substs, bare_fn_ty))].into_iter() 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) => { ty::TyFnPtr(_) => {
vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter() unimplemented!();
}, },
_ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), _ => 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 // in case there is no drop function to be called, this still needs to be initialized
self.memory.write_usize(vtable, 0)?; 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 ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty {
if let Some(destructor) = adt_def.destructor(self.tcx) { if adt_def.has_dtor(self.tcx) {
let fn_ty = match self.tcx.item_type(destructor.did).sty { let env = self.tcx.empty_parameter_environment();
ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), let def = if self.tcx.type_needs_drop_given_env(ty, &env) {
_ => bug!("drop method is not a TyFnDef"), ty::InstanceDef::DropGlue(drop_in_place, Some(ty))
} else {
ty::InstanceDef::DropGlue(drop_in_place, None)
}; };
let fn_ty = self.erase_lifetimes(&fn_ty); let instance = ty::Instance { substs, def };
// The real type is taken from the self argument in `fn drop(&mut self)` let fn_ptr = self.memory.create_fn_alloc(instance);
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);
self.memory.write_ptr(vtable, fn_ptr)?; self.memory.write_ptr(vtable, fn_ptr)?;
} }
} }
@ -310,20 +146,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(vtable) 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)> { pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> {
let pointer_size = self.memory.pointer_size(); let pointer_size = self.memory.pointer_size();
let size = self.memory.read_usize(vtable.offset(pointer_size))?; let size = self.memory.read_usize(vtable.offset(pointer_size))?;
@ -423,7 +245,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&self, &self,
def_id: DefId, def_id: DefId,
substs: &'tcx Substs<'tcx>, substs: &'tcx Substs<'tcx>,
) -> (DefId, &'tcx Substs<'tcx>) { ) -> ty::Instance<'tcx> {
if let Some(trait_id) = self.tcx.trait_of_item(def_id) { if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
let vtable = self.fulfill_obligation(trait_ref); 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) let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id)
.find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
if let Some(assoc_const) = assoc_const_opt { 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)
}
}
}