Skip to content

Commit d69ffa0

Browse files
authored
Merge pull request #21 from oli-obk/function_pointers2
Function pointers
2 parents 2f2219b + fe9b455 commit d69ffa0

File tree

10 files changed

+247
-95
lines changed

10 files changed

+247
-95
lines changed

src/error.rs

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use memory::Pointer;
66
#[derive(Clone, Debug)]
77
pub enum EvalError {
88
DanglingPointerDeref,
9+
InvalidFunctionPointer,
910
InvalidBool,
1011
InvalidDiscriminant,
1112
PointerOutOfBounds {
@@ -19,6 +20,8 @@ pub enum EvalError {
1920
ReadUndefBytes,
2021
InvalidBoolOp(mir::BinOp),
2122
Unimplemented(String),
23+
DerefFunctionPointer,
24+
ExecuteMemory,
2225
}
2326

2427
pub type EvalResult<T> = Result<T, EvalError>;
@@ -28,6 +31,8 @@ impl Error for EvalError {
2831
match *self {
2932
EvalError::DanglingPointerDeref =>
3033
"dangling pointer was dereferenced",
34+
EvalError::InvalidFunctionPointer =>
35+
"tried to use a pointer as a function pointer",
3136
EvalError::InvalidBool =>
3237
"invalid boolean value read",
3338
EvalError::InvalidDiscriminant =>
@@ -45,6 +50,10 @@ impl Error for EvalError {
4550
EvalError::InvalidBoolOp(_) =>
4651
"invalid boolean operation",
4752
EvalError::Unimplemented(ref msg) => msg,
53+
EvalError::DerefFunctionPointer =>
54+
"tried to dereference a function pointer",
55+
EvalError::ExecuteMemory =>
56+
"tried to treat a memory pointer as a function pointer",
4857
}
4958
}
5059

src/interpreter/mod.rs

+116-85
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc::traits::{self, ProjectionMode};
66
use rustc::ty::fold::TypeFoldable;
77
use rustc::ty::layout::{self, Layout, Size};
88
use rustc::ty::subst::{self, Subst, Substs};
9-
use rustc::ty::{self, Ty, TyCtxt};
9+
use rustc::ty::{self, Ty, TyCtxt, BareFnTy};
1010
use rustc::util::nodemap::DefIdMap;
1111
use rustc_data_structures::indexed_vec::Idx;
1212
use std::cell::RefCell;
@@ -15,7 +15,7 @@ use std::rc::Rc;
1515
use std::{iter, mem};
1616
use syntax::ast;
1717
use syntax::attr;
18-
use syntax::codemap::{self, DUMMY_SP};
18+
use syntax::codemap::{self, DUMMY_SP, Span};
1919

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

4242
/// The virtual memory system.
43-
memory: Memory,
43+
memory: Memory<'tcx>,
4444

4545
/// Precomputed statics, constants and promoteds
4646
statics: HashMap<ConstantId<'tcx>, Pointer>,
@@ -283,6 +283,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
283283
}
284284

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

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

430431
let func_ty = self.operand_ty(func);
431432
match func_ty.sty {
433+
ty::TyFnPtr(bare_fn_ty) => {
434+
let ptr = self.eval_operand(func)?;
435+
assert_eq!(ptr.offset, 0);
436+
let fn_ptr = self.memory.read_ptr(ptr)?;
437+
let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?;
438+
self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args,
439+
terminator.source_info.span)?
440+
},
432441
ty::TyFnDef(def_id, substs, fn_ty) => {
433-
use syntax::abi::Abi;
434-
match fn_ty.abi {
435-
Abi::RustIntrinsic => {
436-
let name = self.tcx.item_name(def_id).as_str();
437-
match fn_ty.sig.0.output {
438-
ty::FnConverging(ty) => {
439-
let size = self.type_size(ty);
440-
let ret = return_ptr.unwrap();
441-
self.call_intrinsic(&name, substs, args, ret, size)?
442-
}
443-
ty::FnDiverging => unimplemented!(),
444-
}
445-
}
446-
447-
Abi::C => {
448-
match fn_ty.sig.0.output {
449-
ty::FnConverging(ty) => {
450-
let size = self.type_size(ty);
451-
self.call_c_abi(def_id, args, return_ptr.unwrap(), size)?
452-
}
453-
ty::FnDiverging => unimplemented!(),
454-
}
455-
}
456-
457-
Abi::Rust | Abi::RustCall => {
458-
// TODO(solson): Adjust the first argument when calling a Fn or
459-
// FnMut closure via FnOnce::call_once.
460-
461-
// Only trait methods can have a Self parameter.
462-
let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() {
463-
self.trait_method(def_id, substs)
464-
} else {
465-
(def_id, substs)
466-
};
467-
468-
let mut arg_srcs = Vec::new();
469-
for arg in args {
470-
let src = self.eval_operand(arg)?;
471-
let src_ty = self.operand_ty(arg);
472-
arg_srcs.push((src, src_ty));
473-
}
474-
475-
if fn_ty.abi == Abi::RustCall && !args.is_empty() {
476-
arg_srcs.pop();
477-
let last_arg = args.last().unwrap();
478-
let last = self.eval_operand(last_arg)?;
479-
let last_ty = self.operand_ty(last_arg);
480-
let last_layout = self.type_layout(last_ty);
481-
match (&last_ty.sty, last_layout) {
482-
(&ty::TyTuple(fields),
483-
&Layout::Univariant { ref variant, .. }) => {
484-
let offsets = iter::once(0)
485-
.chain(variant.offset_after_field.iter()
486-
.map(|s| s.bytes()));
487-
for (offset, ty) in offsets.zip(fields) {
488-
let src = last.offset(offset as isize);
489-
arg_srcs.push((src, ty));
490-
}
491-
}
492-
ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty),
493-
}
494-
}
495-
496-
let mir = self.load_mir(resolved_def_id);
497-
self.push_stack_frame(
498-
def_id, terminator.source_info.span, mir, resolved_substs,
499-
return_ptr
500-
);
501-
502-
for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() {
503-
let dest = self.frame().locals[i];
504-
self.move_(src, dest, src_ty)?;
505-
}
506-
}
507-
508-
abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
509-
}
442+
self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args,
443+
terminator.source_info.span)?
510444
}
511445

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

475+
pub fn eval_fn_call(
476+
&mut self,
477+
def_id: DefId,
478+
substs: &'tcx Substs<'tcx>,
479+
fn_ty: &'tcx BareFnTy,
480+
return_ptr: Option<Pointer>,
481+
args: &[mir::Operand<'tcx>],
482+
span: Span,
483+
) -> EvalResult<()> {
484+
use syntax::abi::Abi;
485+
match fn_ty.abi {
486+
Abi::RustIntrinsic => {
487+
let name = self.tcx.item_name(def_id).as_str();
488+
match fn_ty.sig.0.output {
489+
ty::FnConverging(ty) => {
490+
let size = self.type_size(ty);
491+
let ret = return_ptr.unwrap();
492+
self.call_intrinsic(&name, substs, args, ret, size)
493+
}
494+
ty::FnDiverging => unimplemented!(),
495+
}
496+
}
497+
498+
Abi::C => {
499+
match fn_ty.sig.0.output {
500+
ty::FnConverging(ty) => {
501+
let size = self.type_size(ty);
502+
self.call_c_abi(def_id, args, return_ptr.unwrap(), size)
503+
}
504+
ty::FnDiverging => unimplemented!(),
505+
}
506+
}
507+
508+
Abi::Rust | Abi::RustCall => {
509+
// TODO(solson): Adjust the first argument when calling a Fn or
510+
// FnMut closure via FnOnce::call_once.
511+
512+
// Only trait methods can have a Self parameter.
513+
let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() {
514+
self.trait_method(def_id, substs)
515+
} else {
516+
(def_id, substs)
517+
};
518+
519+
let mut arg_srcs = Vec::new();
520+
for arg in args {
521+
let src = self.eval_operand(arg)?;
522+
let src_ty = self.operand_ty(arg);
523+
arg_srcs.push((src, src_ty));
524+
}
525+
526+
if fn_ty.abi == Abi::RustCall && !args.is_empty() {
527+
arg_srcs.pop();
528+
let last_arg = args.last().unwrap();
529+
let last = self.eval_operand(last_arg)?;
530+
let last_ty = self.operand_ty(last_arg);
531+
let last_layout = self.type_layout(last_ty);
532+
match (&last_ty.sty, last_layout) {
533+
(&ty::TyTuple(fields),
534+
&Layout::Univariant { ref variant, .. }) => {
535+
let offsets = iter::once(0)
536+
.chain(variant.offset_after_field.iter()
537+
.map(|s| s.bytes()));
538+
for (offset, ty) in offsets.zip(fields) {
539+
let src = last.offset(offset as isize);
540+
arg_srcs.push((src, ty));
541+
}
542+
}
543+
ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty),
544+
}
545+
}
546+
547+
let mir = self.load_mir(resolved_def_id);
548+
self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr);
549+
550+
for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() {
551+
let dest = self.frame().locals[i];
552+
self.move_(src, dest, src_ty)?;
553+
}
554+
555+
Ok(())
556+
}
557+
558+
abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
559+
}
560+
}
561+
541562
fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> {
542563
if !self.type_needs_drop(ty) {
543564
debug!("no need to drop {:?}", ty);
@@ -1033,12 +1054,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10331054
}
10341055

10351056
Cast(kind, ref operand, dest_ty) => {
1036-
let src = self.eval_operand(operand)?;
1037-
let src_ty = self.operand_ty(operand);
1038-
10391057
use rustc::mir::repr::CastKind::*;
10401058
match kind {
10411059
Unsize => {
1060+
let src = self.eval_operand(operand)?;
1061+
let src_ty = self.operand_ty(operand);
10421062
self.move_(src, dest, src_ty)?;
10431063
let src_pointee_ty = pointee_type(src_ty).unwrap();
10441064
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
@@ -1054,6 +1074,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10541074
}
10551075

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

1097+
ReifyFnPointer => match self.operand_ty(operand).sty {
1098+
ty::TyFnDef(def_id, substs, _) => {
1099+
let fn_ptr = self.memory.create_fn_ptr(def_id, substs);
1100+
self.memory.write_ptr(dest, fn_ptr)?;
1101+
},
1102+
ref other => panic!("reify fn pointer on {:?}", other),
1103+
},
1104+
10751105
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
10761106
}
10771107
}
@@ -1159,7 +1189,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11591189
Value { ref value } => Ok(self.const_to_ptr(value)?),
11601190
Item { def_id, substs } => {
11611191
if let ty::TyFnDef(..) = ty.sty {
1162-
Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string()))
1192+
// function items are zero sized
1193+
Ok(self.memory.allocate(0))
11631194
} else {
11641195
let cid = ConstantId {
11651196
def_id: def_id,

src/interpreter/stepper.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
129129
mir::Literal::Value { .. } => {}
130130
mir::Literal::Item { def_id, substs } => {
131131
if let ty::TyFnDef(..) = constant.ty.sty {
132-
// No need to do anything here, even if function pointers are implemented,
132+
// No need to do anything here,
133133
// because the type is the actual function, not the signature of the function.
134134
// Thus we can simply create a zero sized allocation in `evaluate_operand`
135135
} else {

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#[macro_use] extern crate rustc;
1414
extern crate rustc_data_structures;
1515
extern crate rustc_mir;
16+
extern crate rustc_trans;
1617
extern crate syntax;
1718
#[macro_use] extern crate log;
1819
extern crate log_settings;

0 commit comments

Comments
 (0)