Skip to content

Commit 024b3d2

Browse files
committed
Merge remote-tracking branch 'oli/function_pointers2' into fixup-function_pointers2
2 parents 1c58b7c + 384623d commit 024b3d2

File tree

8 files changed

+190
-93
lines changed

8 files changed

+190
-93
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
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 {
@@ -28,6 +29,8 @@ impl Error for EvalError {
2829
match *self {
2930
EvalError::DanglingPointerDeref =>
3031
"dangling pointer was dereferenced",
32+
EvalError::InvalidFunctionPointer =>
33+
"tried to use a pointer as a function pointer",
3134
EvalError::InvalidBool =>
3235
"invalid boolean value read",
3336
EvalError::InvalidDiscriminant =>

src/interpreter/mod.rs

Lines changed: 115 additions & 85 deletions
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());
@@ -421,84 +422,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
421422

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

504438
_ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))),
@@ -530,6 +464,93 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
530464
Ok(())
531465
}
532466

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

10251046
Cast(kind, ref operand, dest_ty) => {
1026-
let src = self.eval_operand(operand)?;
1027-
let src_ty = self.operand_ty(operand);
1028-
10291047
use rustc::mir::repr::CastKind::*;
10301048
match kind {
10311049
Unsize => {
1050+
let src = self.eval_operand(operand)?;
1051+
let src_ty = self.operand_ty(operand);
10321052
self.move_(src, dest, src_ty)?;
10331053
let src_pointee_ty = pointee_type(src_ty).unwrap();
10341054
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
@@ -1044,11 +1064,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10441064
}
10451065

10461066
Misc => {
1067+
let src = self.eval_operand(operand)?;
10471068
// FIXME(solson): Wrong for almost everything.
10481069
let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize;
10491070
self.memory.copy(src, dest, size)?;
10501071
}
10511072

1073+
ReifyFnPointer => match self.operand_ty(operand).sty {
1074+
ty::TyFnDef(def_id, substs, _) => {
1075+
let fn_ptr = self.memory.create_fn_ptr(def_id, substs);
1076+
self.memory.write_ptr(dest, fn_ptr)?;
1077+
},
1078+
ref other => panic!("reify fn pointer on {:?}", other),
1079+
},
1080+
10521081
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
10531082
}
10541083
}
@@ -1136,7 +1165,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11361165
Value { ref value } => Ok(self.const_to_ptr(value)?),
11371166
Item { def_id, substs } => {
11381167
if let ty::TyFnDef(..) = ty.sty {
1139-
Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string()))
1168+
// function items are zero sized
1169+
Ok(self.memory.allocate(0))
11401170
} else {
11411171
let cid = ConstantId {
11421172
def_id: def_id,

src/interpreter/stepper.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 0 deletions
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;

src/memory.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use std::collections::Bound::{Included, Excluded};
33
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
44
use std::{fmt, iter, mem, ptr};
55

6+
use rustc::hir::def_id::DefId;
7+
use rustc::ty::subst::Substs;
8+
69
use error::{EvalError, EvalResult};
710
use primval::PrimVal;
811

@@ -42,22 +45,37 @@ impl Pointer {
4245
// Top-level interpreter memory
4346
////////////////////////////////////////////////////////////////////////////////
4447

45-
pub struct Memory {
48+
pub struct Memory<'tcx> {
4649
alloc_map: HashMap<AllocId, Allocation>,
50+
functions: HashMap<AllocId, (DefId, &'tcx Substs<'tcx>)>,
4751
next_id: AllocId,
4852
pub pointer_size: usize,
4953
}
5054

51-
impl Memory {
55+
impl<'tcx> Memory<'tcx> {
5256
// FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.)
5357
pub fn new(pointer_size: usize) -> Self {
5458
Memory {
5559
alloc_map: HashMap::new(),
60+
functions: HashMap::new(),
5661
next_id: AllocId(0),
5762
pointer_size: pointer_size,
5863
}
5964
}
6065

66+
// FIXME: never create two pointers to the same def_id + substs combination
67+
// maybe re-use the statics cache of the gecx?
68+
pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer {
69+
let id = self.next_id;
70+
debug!("creating fn ptr: {}", id);
71+
self.next_id.0 += 1;
72+
self.functions.insert(id, (def_id, substs));
73+
Pointer {
74+
alloc_id: id,
75+
offset: 0,
76+
}
77+
}
78+
6179
pub fn allocate(&mut self, size: usize) -> Pointer {
6280
let alloc = Allocation {
6381
bytes: vec![0; size],
@@ -125,6 +143,11 @@ impl Memory {
125143
self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref)
126144
}
127145

146+
pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> {
147+
debug!("reading fn ptr: {}", id);
148+
self.functions.get(&id).map(|&did| did).ok_or(EvalError::InvalidFunctionPointer)
149+
}
150+
128151
/// Print an allocation and all allocations it points to, recursively.
129152
pub fn dump(&self, id: AllocId) {
130153
let mut allocs_seen = HashSet::new();
@@ -137,12 +160,18 @@ impl Memory {
137160
print!("{}", prefix);
138161
let mut relocations = vec![];
139162

140-
let alloc = match self.alloc_map.get(&id) {
141-
Some(a) => a,
142-
None => {
163+
let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) {
164+
(Some(a), None) => a,
165+
(None, Some(_)) => {
166+
// FIXME: print function name
167+
println!("function pointer");
168+
continue;
169+
},
170+
(None, None) => {
143171
println!("(deallocated)");
144172
continue;
145-
}
173+
},
174+
(Some(_), Some(_)) => unreachable!(),
146175
};
147176

148177
for i in 0..alloc.bytes.len() {

0 commit comments

Comments
 (0)