Skip to content

Commit

Permalink
Merge pull request rust-lang#21 from oli-obk/function_pointers2
Browse files Browse the repository at this point in the history
Function pointers
  • Loading branch information
solson authored Jun 13, 2016
2 parents 2f2219b + fe9b455 commit d69ffa0
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 95 deletions.
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use memory::Pointer;
#[derive(Clone, Debug)]
pub enum EvalError {
DanglingPointerDeref,
InvalidFunctionPointer,
InvalidBool,
InvalidDiscriminant,
PointerOutOfBounds {
Expand All @@ -19,6 +20,8 @@ pub enum EvalError {
ReadUndefBytes,
InvalidBoolOp(mir::BinOp),
Unimplemented(String),
DerefFunctionPointer,
ExecuteMemory,
}

pub type EvalResult<T> = Result<T, EvalError>;
Expand All @@ -28,6 +31,8 @@ impl Error for EvalError {
match *self {
EvalError::DanglingPointerDeref =>
"dangling pointer was dereferenced",
EvalError::InvalidFunctionPointer =>
"tried to use a pointer as a function pointer",
EvalError::InvalidBool =>
"invalid boolean value read",
EvalError::InvalidDiscriminant =>
Expand All @@ -45,6 +50,10 @@ impl Error for EvalError {
EvalError::InvalidBoolOp(_) =>
"invalid boolean operation",
EvalError::Unimplemented(ref msg) => msg,
EvalError::DerefFunctionPointer =>
"tried to dereference a function pointer",
EvalError::ExecuteMemory =>
"tried to treat a memory pointer as a function pointer",
}
}

Expand Down
201 changes: 116 additions & 85 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc::traits::{self, ProjectionMode};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::layout::{self, Layout, Size};
use rustc::ty::subst::{self, Subst, Substs};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::{self, Ty, TyCtxt, BareFnTy};
use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::indexed_vec::Idx;
use std::cell::RefCell;
Expand All @@ -15,7 +15,7 @@ use std::rc::Rc;
use std::{iter, mem};
use syntax::ast;
use syntax::attr;
use syntax::codemap::{self, DUMMY_SP};
use syntax::codemap::{self, DUMMY_SP, Span};

use error::{EvalError, EvalResult};
use memory::{Memory, Pointer};
Expand All @@ -40,7 +40,7 @@ pub struct EvalContext<'a, 'tcx: 'a> {
mir_cache: RefCell<DefIdMap<Rc<mir::Mir<'tcx>>>>,

/// The virtual memory system.
memory: Memory,
memory: Memory<'tcx>,

/// Precomputed statics, constants and promoteds
statics: HashMap<ConstantId<'tcx>, Pointer>,
Expand Down Expand Up @@ -283,6 +283,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> {
use rustc_trans::back::symbol_names::def_id_to_string;
match self.tcx.map.as_local_node_id(def_id) {
Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()),
None => {
Expand All @@ -293,7 +294,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {

let cs = &self.tcx.sess.cstore;
let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| {
panic!("no mir for {:?}", def_id);
panic!("no mir for `{}`", def_id_to_string(self.tcx, def_id));
});
let cached = Rc::new(mir);
mir_cache.insert(def_id, cached.clone());
Expand Down Expand Up @@ -429,84 +430,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {

let func_ty = self.operand_ty(func);
match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
let ptr = self.eval_operand(func)?;
assert_eq!(ptr.offset, 0);
let fn_ptr = self.memory.read_ptr(ptr)?;
let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?;
self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args,
terminator.source_info.span)?
},
ty::TyFnDef(def_id, substs, fn_ty) => {
use syntax::abi::Abi;
match fn_ty.abi {
Abi::RustIntrinsic => {
let name = self.tcx.item_name(def_id).as_str();
match fn_ty.sig.0.output {
ty::FnConverging(ty) => {
let size = self.type_size(ty);
let ret = return_ptr.unwrap();
self.call_intrinsic(&name, substs, args, ret, size)?
}
ty::FnDiverging => unimplemented!(),
}
}

Abi::C => {
match fn_ty.sig.0.output {
ty::FnConverging(ty) => {
let size = self.type_size(ty);
self.call_c_abi(def_id, args, return_ptr.unwrap(), size)?
}
ty::FnDiverging => unimplemented!(),
}
}

Abi::Rust | Abi::RustCall => {
// TODO(solson): Adjust the first argument when calling a Fn or
// FnMut closure via FnOnce::call_once.

// Only trait methods can have a Self parameter.
let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() {
self.trait_method(def_id, substs)
} else {
(def_id, substs)
};

let mut arg_srcs = Vec::new();
for arg in args {
let src = self.eval_operand(arg)?;
let src_ty = self.operand_ty(arg);
arg_srcs.push((src, src_ty));
}

if fn_ty.abi == Abi::RustCall && !args.is_empty() {
arg_srcs.pop();
let last_arg = args.last().unwrap();
let last = self.eval_operand(last_arg)?;
let last_ty = self.operand_ty(last_arg);
let last_layout = self.type_layout(last_ty);
match (&last_ty.sty, last_layout) {
(&ty::TyTuple(fields),
&Layout::Univariant { ref variant, .. }) => {
let offsets = iter::once(0)
.chain(variant.offset_after_field.iter()
.map(|s| s.bytes()));
for (offset, ty) in offsets.zip(fields) {
let src = last.offset(offset as isize);
arg_srcs.push((src, ty));
}
}
ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty),
}
}

let mir = self.load_mir(resolved_def_id);
self.push_stack_frame(
def_id, terminator.source_info.span, mir, resolved_substs,
return_ptr
);

for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() {
let dest = self.frame().locals[i];
self.move_(src, dest, src_ty)?;
}
}

abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
}
self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args,
terminator.source_info.span)?
}

_ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))),
Expand Down Expand Up @@ -538,6 +472,93 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(())
}

pub fn eval_fn_call(
&mut self,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
fn_ty: &'tcx BareFnTy,
return_ptr: Option<Pointer>,
args: &[mir::Operand<'tcx>],
span: Span,
) -> EvalResult<()> {
use syntax::abi::Abi;
match fn_ty.abi {
Abi::RustIntrinsic => {
let name = self.tcx.item_name(def_id).as_str();
match fn_ty.sig.0.output {
ty::FnConverging(ty) => {
let size = self.type_size(ty);
let ret = return_ptr.unwrap();
self.call_intrinsic(&name, substs, args, ret, size)
}
ty::FnDiverging => unimplemented!(),
}
}

Abi::C => {
match fn_ty.sig.0.output {
ty::FnConverging(ty) => {
let size = self.type_size(ty);
self.call_c_abi(def_id, args, return_ptr.unwrap(), size)
}
ty::FnDiverging => unimplemented!(),
}
}

Abi::Rust | Abi::RustCall => {
// TODO(solson): Adjust the first argument when calling a Fn or
// FnMut closure via FnOnce::call_once.

// Only trait methods can have a Self parameter.
let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() {
self.trait_method(def_id, substs)
} else {
(def_id, substs)
};

let mut arg_srcs = Vec::new();
for arg in args {
let src = self.eval_operand(arg)?;
let src_ty = self.operand_ty(arg);
arg_srcs.push((src, src_ty));
}

if fn_ty.abi == Abi::RustCall && !args.is_empty() {
arg_srcs.pop();
let last_arg = args.last().unwrap();
let last = self.eval_operand(last_arg)?;
let last_ty = self.operand_ty(last_arg);
let last_layout = self.type_layout(last_ty);
match (&last_ty.sty, last_layout) {
(&ty::TyTuple(fields),
&Layout::Univariant { ref variant, .. }) => {
let offsets = iter::once(0)
.chain(variant.offset_after_field.iter()
.map(|s| s.bytes()));
for (offset, ty) in offsets.zip(fields) {
let src = last.offset(offset as isize);
arg_srcs.push((src, ty));
}
}
ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty),
}
}

let mir = self.load_mir(resolved_def_id);
self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr);

for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() {
let dest = self.frame().locals[i];
self.move_(src, dest, src_ty)?;
}

Ok(())
}

abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
}
}

fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> {
if !self.type_needs_drop(ty) {
debug!("no need to drop {:?}", ty);
Expand Down Expand Up @@ -1033,12 +1054,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

Cast(kind, ref operand, dest_ty) => {
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);

use rustc::mir::repr::CastKind::*;
match kind {
Unsize => {
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);
self.move_(src, dest, src_ty)?;
let src_pointee_ty = pointee_type(src_ty).unwrap();
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
Expand All @@ -1054,6 +1074,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

Misc => {
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);
// FIXME(solson): Wrong for almost everything.
warn!("misc cast from {:?} to {:?}", src_ty, dest_ty);
let dest_size = self.type_size(dest_ty);
Expand All @@ -1072,6 +1094,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}

ReifyFnPointer => match self.operand_ty(operand).sty {
ty::TyFnDef(def_id, substs, _) => {
let fn_ptr = self.memory.create_fn_ptr(def_id, substs);
self.memory.write_ptr(dest, fn_ptr)?;
},
ref other => panic!("reify fn pointer on {:?}", other),
},

_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
}
}
Expand Down Expand Up @@ -1159,7 +1189,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Value { ref value } => Ok(self.const_to_ptr(value)?),
Item { def_id, substs } => {
if let ty::TyFnDef(..) = ty.sty {
Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string()))
// function items are zero sized
Ok(self.memory.allocate(0))
} else {
let cid = ConstantId {
def_id: def_id,
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/stepper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
mir::Literal::Value { .. } => {}
mir::Literal::Item { def_id, substs } => {
if let ty::TyFnDef(..) = constant.ty.sty {
// No need to do anything here, even if function pointers are implemented,
// No need to do anything here,
// because the type is the actual function, not the signature of the function.
// Thus we can simply create a zero sized allocation in `evaluate_operand`
} else {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#[macro_use] extern crate rustc;
extern crate rustc_data_structures;
extern crate rustc_mir;
extern crate rustc_trans;
extern crate syntax;
#[macro_use] extern crate log;
extern crate log_settings;
Expand Down
Loading

0 comments on commit d69ffa0

Please sign in to comment.