Skip to content

Commit b2b8141

Browse files
authored
Merge pull request rust-lang#115 from oli-obk/master
fix turning function items into closure trait objects
2 parents 9f7ca35 + 74d8c8a commit b2b8141

12 files changed

+358
-53
lines changed

src/error.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33
use rustc::mir;
44
use rustc::ty::{BareFnTy, Ty, FnSig, layout};
55
use syntax::abi::Abi;
6-
use memory::Pointer;
6+
use memory::{Pointer, Function};
77
use rustc_const_math::ConstMathErr;
88
use syntax::codemap::Span;
99

@@ -53,6 +53,9 @@ pub enum EvalError<'tcx> {
5353
DeallocatedFrozenMemory,
5454
Layout(layout::LayoutError<'tcx>),
5555
Unreachable,
56+
ExpectedConcreteFunction(Function<'tcx>),
57+
ExpectedDropGlue(Function<'tcx>),
58+
ManuallyCalledDropGlue,
5659
}
5760

5861
pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
@@ -125,6 +128,12 @@ impl<'tcx> Error for EvalError<'tcx> {
125128
"attempted to get length of a null terminated string, but no null found before end of allocation",
126129
EvalError::Unreachable =>
127130
"entered unreachable code",
131+
EvalError::ExpectedConcreteFunction(_) =>
132+
"tried to use glue function as function",
133+
EvalError::ExpectedDropGlue(_) =>
134+
"tried to use non-drop-glue function as drop glue",
135+
EvalError::ManuallyCalledDropGlue =>
136+
"tried to manually invoke drop glue",
128137
}
129138
}
130139

src/eval_context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
659659
ty::TyFnPtr(unsafe_fn_ty) => {
660660
let src = self.eval_operand(operand)?;
661661
let ptr = src.read_ptr(&self.memory)?;
662-
let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?;
662+
let fn_def = self.memory.get_fn(ptr.alloc_id)?.expect_concrete()?;
663663
let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty);
664-
let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty);
664+
let fn_ptr = self.memory.create_fn_ptr(self.tcx, fn_def.def_id, fn_def.substs, unsafe_fn_ty);
665665
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
666666
},
667667
ref other => bug!("fn to unsafe fn cast on {:?}", other),

src/memory.rs

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,47 @@ impl Pointer {
9494
}
9595
}
9696

97-
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
98-
struct FunctionDefinition<'tcx> {
97+
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
98+
/// Identifies a specific monomorphized function
99+
pub struct FunctionDefinition<'tcx> {
99100
pub def_id: DefId,
100101
pub substs: &'tcx Substs<'tcx>,
101102
pub abi: Abi,
102103
pub sig: &'tcx ty::FnSig<'tcx>,
103104
}
104105

106+
/// Either a concrete function, or a glue function
107+
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
108+
pub enum Function<'tcx> {
109+
/// A function or method created by compiling code
110+
Concrete(FunctionDefinition<'tcx>),
111+
/// Glue required to call a regular function through a Fn(Mut|Once) trait object
112+
FnDefAsTraitObject(FunctionDefinition<'tcx>),
113+
/// Glue required to call the actual drop impl's `drop` method.
114+
/// Drop glue takes the `self` by value, while `Drop::drop` take `&mut self`
115+
DropGlue(FunctionDefinition<'tcx>),
116+
/// Glue required to treat the ptr part of a fat pointer
117+
/// as a function pointer
118+
FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>),
119+
/// Glue for Closures
120+
Closure(FunctionDefinition<'tcx>),
121+
}
122+
123+
impl<'tcx> Function<'tcx> {
124+
pub fn expect_concrete(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> {
125+
match self {
126+
Function::Concrete(fn_def) => Ok(fn_def),
127+
other => Err(EvalError::ExpectedConcreteFunction(other)),
128+
}
129+
}
130+
pub fn expect_drop_glue(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> {
131+
match self {
132+
Function::DropGlue(fn_def) => Ok(fn_def),
133+
other => Err(EvalError::ExpectedDropGlue(other)),
134+
}
135+
}
136+
}
137+
105138
////////////////////////////////////////////////////////////////////////////////
106139
// Top-level interpreter memory
107140
////////////////////////////////////////////////////////////////////////////////
@@ -115,9 +148,9 @@ pub struct Memory<'a, 'tcx> {
115148
memory_size: u64,
116149
/// Function "allocations". They exist solely so pointers have something to point to, and
117150
/// we can figure out what they point to.
118-
functions: HashMap<AllocId, FunctionDefinition<'tcx>>,
151+
functions: HashMap<AllocId, Function<'tcx>>,
119152
/// Inverse map of `functions` so we don't allocate a new pointer every time we need one
120-
function_alloc_cache: HashMap<FunctionDefinition<'tcx>, AllocId>,
153+
function_alloc_cache: HashMap<Function<'tcx>, AllocId>,
121154
next_id: AllocId,
122155
pub layout: &'a TargetDataLayout,
123156
/// List of memory regions containing packed structures
@@ -160,35 +193,61 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
160193
abi: fn_ty.abi,
161194
sig: fn_ty.sig,
162195
});
163-
self.create_fn_alloc(FunctionDefinition {
196+
self.create_fn_alloc(Function::Closure(FunctionDefinition {
164197
def_id,
165198
substs: substs.substs,
166199
abi: fn_ty.abi,
167200
// FIXME: why doesn't this compile?
168201
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
169202
sig: fn_ty.sig.skip_binder(),
170-
})
203+
}))
204+
}
205+
206+
pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
207+
self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition {
208+
def_id,
209+
substs,
210+
abi: fn_ty.abi,
211+
// FIXME: why doesn't this compile?
212+
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
213+
sig: fn_ty.sig.skip_binder(),
214+
}))
215+
}
216+
217+
pub fn create_fn_ptr_as_trait_glue(&mut self, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
218+
self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder()))
219+
}
220+
221+
pub fn create_drop_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
222+
self.create_fn_alloc(Function::DropGlue(FunctionDefinition {
223+
def_id,
224+
substs,
225+
abi: fn_ty.abi,
226+
// FIXME: why doesn't this compile?
227+
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
228+
sig: fn_ty.sig.skip_binder(),
229+
}))
171230
}
172231

173232
pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
174-
self.create_fn_alloc(FunctionDefinition {
233+
self.create_fn_alloc(Function::Concrete(FunctionDefinition {
175234
def_id,
176235
substs,
177236
abi: fn_ty.abi,
178237
// FIXME: why doesn't this compile?
179238
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
180239
sig: fn_ty.sig.skip_binder(),
181-
})
240+
}))
182241
}
183242

184-
fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer {
243+
fn create_fn_alloc(&mut self, def: Function<'tcx>) -> Pointer {
185244
if let Some(&alloc_id) = self.function_alloc_cache.get(&def) {
186245
return Pointer::new(alloc_id, 0);
187246
}
188247
let id = self.next_id;
189248
debug!("creating fn ptr: {}", id);
190249
self.next_id.0 += 1;
191-
self.functions.insert(id, def.clone());
250+
self.functions.insert(id, def);
192251
self.function_alloc_cache.insert(def, id);
193252
Pointer::new(id, 0)
194253
}
@@ -381,15 +440,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
381440
}
382441
}
383442

384-
pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Abi, &'tcx ty::FnSig<'tcx>)> {
443+
pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, Function<'tcx>> {
385444
debug!("reading fn ptr: {}", id);
386445
match self.functions.get(&id) {
387-
Some(&FunctionDefinition {
388-
def_id,
389-
substs,
390-
abi,
391-
sig,
392-
}) => Ok((def_id, substs, abi, sig)),
446+
Some(&fndef) => Ok(fndef),
393447
None => match self.alloc_map.get(&id) {
394448
Some(_) => Err(EvalError::ExecuteMemory),
395449
None => Err(EvalError::InvalidFunctionPointer),
@@ -418,14 +472,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
418472

419473
let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) {
420474
(Some(a), None) => a,
421-
(None, Some(fn_def)) => {
422-
let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id));
423-
let abi = if fn_def.abi == Abi::Rust {
424-
format!("")
425-
} else {
426-
format!("extern {} ", fn_def.abi)
427-
};
428-
trace!("{} function pointer: {}: {}{}", msg, name, abi, fn_def.sig);
475+
(None, Some(&Function::Concrete(fn_def))) => {
476+
trace!("{} {}", msg, dump_fn_def(fn_def));
477+
continue;
478+
},
479+
(None, Some(&Function::DropGlue(fn_def))) => {
480+
trace!("{} drop glue for {}", msg, dump_fn_def(fn_def));
481+
continue;
482+
},
483+
(None, Some(&Function::FnDefAsTraitObject(fn_def))) => {
484+
trace!("{} fn as Fn glue for {}", msg, dump_fn_def(fn_def));
485+
continue;
486+
},
487+
(None, Some(&Function::FnPtrAsTraitObject(fn_def))) => {
488+
trace!("{} fn ptr as Fn glue (signature: {:?})", msg, fn_def);
489+
continue;
490+
},
491+
(None, Some(&Function::Closure(fn_def))) => {
492+
trace!("{} closure glue for {}", msg, dump_fn_def(fn_def));
429493
continue;
430494
},
431495
(None, None) => {
@@ -476,6 +540,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
476540
}
477541
}
478542

543+
fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String {
544+
let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id));
545+
let abi = if fn_def.abi == Abi::Rust {
546+
format!("")
547+
} else {
548+
format!("extern {} ", fn_def.abi)
549+
};
550+
format!("function pointer: {}: {}{}", name, abi, fn_def.sig)
551+
}
552+
479553
/// Byte accessors
480554
impl<'a, 'tcx> Memory<'a, 'tcx> {
481555
fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {

src/terminator/mod.rs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ use rustc::ty::layout::{Layout, Size};
66
use rustc::ty::subst::{Substs, Kind};
77
use rustc::ty::{self, Ty, TyCtxt, BareFnTy};
88
use syntax::codemap::{DUMMY_SP, Span};
9-
use syntax::{ast, attr};
9+
use syntax::{ast, attr, abi};
1010

1111
use error::{EvalError, EvalResult};
1212
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited};
1313
use lvalue::{Lvalue, LvalueExtra};
14-
use memory::Pointer;
14+
use memory::{Pointer, FunctionDefinition, Function};
1515
use value::PrimVal;
1616
use value::Value;
1717

@@ -89,7 +89,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
8989
match func_ty.sty {
9090
ty::TyFnPtr(bare_fn_ty) => {
9191
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?;
92-
let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?;
92+
let FunctionDefinition {def_id, substs, abi, sig} = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?;
9393
let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig);
9494
let bare_sig = self.tcx.erase_regions(&bare_sig);
9595
// transmuting function pointers in miri is fine as long as the number of
@@ -313,6 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
313313
)?;
314314

315315
let arg_locals = self.frame().mir.args_iter();
316+
assert_eq!(self.frame().mir.arg_count, args.len());
316317
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
317318
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
318319
self.write_value(arg_val, dest, arg_ty)?;
@@ -624,17 +625,48 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
624625

625626
traits::VtableObject(ref data) => {
626627
let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64;
627-
if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) {
628-
let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?;
629-
*first_arg = Value::ByVal(PrimVal::Ptr(self_ptr));
630-
let idx = idx + 3;
631-
let offset = idx * self.memory.pointer_size();
632-
let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?;
633-
let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?;
634-
*first_ty = sig.inputs()[0];
635-
Ok((def_id, substs, Vec::new()))
636-
} else {
637-
Err(EvalError::VtableForArgumentlessMethod)
628+
if args.is_empty() {
629+
return Err(EvalError::VtableForArgumentlessMethod);
630+
}
631+
let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?;
632+
let idx = idx + 3;
633+
let offset = idx * self.memory.pointer_size();
634+
let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?;
635+
trace!("args: {:#?}", args);
636+
match self.memory.get_fn(fn_ptr.alloc_id)? {
637+
Function::FnDefAsTraitObject(fn_def) => {
638+
trace!("sig: {:#?}", fn_def.sig);
639+
assert!(fn_def.abi != abi::Abi::RustCall);
640+
assert_eq!(args.len(), 2);
641+
// a function item turned into a closure trait object
642+
// the first arg is just there to give use the vtable
643+
args.remove(0);
644+
self.unpack_fn_args(args)?;
645+
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
646+
},
647+
Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue),
648+
Function::Concrete(fn_def) => {
649+
trace!("sig: {:#?}", fn_def.sig);
650+
args[0] = (
651+
Value::ByVal(PrimVal::Ptr(self_ptr)),
652+
fn_def.sig.inputs()[0],
653+
);
654+
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
655+
},
656+
Function::Closure(fn_def) => {
657+
self.unpack_fn_args(args)?;
658+
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
659+
}
660+
Function::FnPtrAsTraitObject(sig) => {
661+
trace!("sig: {:#?}", sig);
662+
// the first argument was the fat ptr
663+
args.remove(0);
664+
self.unpack_fn_args(args)?;
665+
let fn_ptr = self.memory.read_ptr(self_ptr)?;
666+
let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?;
667+
assert_eq!(sig, fn_def.sig);
668+
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
669+
}
638670
}
639671
},
640672
vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable),
@@ -768,7 +800,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
768800
let drop_fn = self.memory.read_ptr(vtable)?;
769801
// some values don't need to call a drop impl, so the value is null
770802
if drop_fn != Pointer::from_int(0) {
771-
let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?;
803+
let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?;
772804
let real_ty = sig.inputs()[0];
773805
self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?;
774806
drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs));

src/vtable.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
5555
vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter()
5656
}
5757

58+
// turn a function definition into a Fn trait object
5859
traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => {
5960
match fn_ty.sty {
6061
ty::TyFnDef(did, substs, bare_fn_ty) => {
61-
vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter()
62+
vec![Some(self.memory.create_fn_as_trait_glue(self.tcx, did, substs, bare_fn_ty))].into_iter()
6263
},
63-
_ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty),
64+
ty::TyFnPtr(bare_fn_ty) => {
65+
vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter()
66+
},
67+
_ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty),
6468
}
6569
}
6670

@@ -94,7 +98,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
9498
ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty),
9599
_ => bug!("drop method is not a TyFnDef"),
96100
};
97-
let fn_ptr = self.memory.create_fn_ptr(self.tcx, drop_def_id, substs, fn_ty);
101+
let fn_ptr = self.memory.create_drop_glue(self.tcx, drop_def_id, substs, fn_ty);
98102
self.memory.write_ptr(vtable, fn_ptr)?;
99103
}
100104
}

0 commit comments

Comments
 (0)