Skip to content

Commit bfe1efc

Browse files
committed
stop leaking memory on closure calls
1 parent 360ef49 commit bfe1efc

File tree

3 files changed

+41
-37
lines changed

3 files changed

+41
-37
lines changed

Diff for: src/interpreter/mod.rs

+11-24
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ pub struct Frame<'tcx> {
8282
/// Before being initialized, a local is simply marked as None.
8383
pub locals: Vec<Option<Value>>,
8484

85+
/// Temporaries introduced to save stackframes
86+
/// This is pure interpreter magic and has nothing to do with how rustc does it
87+
/// An example is calling an FnMut closure that has been converted to a FnOnce closure
88+
/// If they are Value::ByRef, their memory will be freed when the stackframe finishes
89+
pub interpreter_temporaries: Vec<Value>,
90+
8591
////////////////////////////////////////////////////////////////////////////////
8692
// Current position within the function
8793
////////////////////////////////////////////////////////////////////////////////
@@ -327,6 +333,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
327333
substs: &'tcx Substs<'tcx>,
328334
return_lvalue: Lvalue<'tcx>,
329335
return_to_block: StackPopCleanup,
336+
temporaries: Vec<Value>,
330337
) -> EvalResult<'tcx, ()> {
331338
::log_settings::settings().indentation += 1;
332339

@@ -341,6 +348,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
341348
return_to_block: return_to_block,
342349
return_lvalue: return_lvalue,
343350
locals: locals,
351+
interpreter_temporaries: temporaries,
344352
span: span,
345353
def_id: def_id,
346354
substs: substs,
@@ -385,9 +393,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
385393
StackPopCleanup::None => {},
386394
}
387395
// deallocate all locals that are backed by an allocation
388-
for (i, local) in frame.locals.into_iter().enumerate() {
389-
if let Some(Value::ByRef(ptr)) = local {
390-
trace!("deallocating local {}: {:?}", i + 1, ptr);
396+
for local in frame.locals.into_iter().filter_map(|l| l).chain(frame.interpreter_temporaries) {
397+
if let Value::ByRef(ptr) = local {
391398
self.memory.dump(ptr.alloc_id);
392399
match self.memory.deallocate(ptr) {
393400
// Any frozen memory means that it belongs to a constant or something referenced
@@ -1131,27 +1138,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11311138
Ok(new_lvalue)
11321139
}
11331140

1134-
// FIXME(solson): This method unnecessarily allocates and should not be necessary. We can
1135-
// remove it as soon as PrimVal can represent fat pointers.
1136-
fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> {
1137-
match value {
1138-
Value::ByRef(ptr) => Ok(ptr),
1139-
1140-
Value::ByVal(primval) => {
1141-
let ptr = self.alloc_ptr(ty)?;
1142-
let kind = self.ty_to_primval_kind(ty)?;
1143-
self.memory.write_primval(ptr, primval, kind)?;
1144-
Ok(ptr)
1145-
}
1146-
1147-
Value::ByValPair(a, b) => {
1148-
let ptr = self.alloc_ptr(ty)?;
1149-
self.write_pair_to_ptr(a, b, ptr, ty)?;
1150-
Ok(ptr)
1151-
}
1152-
}
1153-
}
1154-
11551141
/// ensures this Value is not a ByRef
11561142
fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
11571143
match value {
@@ -1719,6 +1705,7 @@ pub fn eval_main<'a, 'tcx: 'a>(
17191705
tcx.intern_substs(&[]),
17201706
Lvalue::from_ptr(Pointer::zst_ptr()),
17211707
StackPopCleanup::None,
1708+
Vec::new(),
17221709
).expect("could not allocate first stack frame");
17231710

17241711
loop {

Diff for: src/interpreter/step.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
145145
} else {
146146
StackPopCleanup::None
147147
};
148-
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup)
148+
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new())
149149
});
150150
}
151151
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
@@ -194,7 +194,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
194194
mir,
195195
this.substs,
196196
Lvalue::Global(cid),
197-
StackPopCleanup::Freeze)
197+
StackPopCleanup::Freeze,
198+
Vec::new())
198199
});
199200
}
200201
}

Diff for: src/interpreter/terminator/mod.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
164164
substs,
165165
Lvalue::from_ptr(Pointer::zst_ptr()),
166166
StackPopCleanup::None,
167+
Vec::new(),
167168
)?;
168169
let mut arg_locals = self.frame().mir.args_iter();
169170
let first = arg_locals.next().expect("drop impl has self arg");
@@ -211,11 +212,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
211212
}
212213

213214
// Only trait methods can have a Self parameter.
214-
let (resolved_def_id, resolved_substs) =
215+
let (resolved_def_id, resolved_substs, temporaries) =
215216
if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
216217
self.trait_method(trait_id, def_id, substs, &mut args)?
217218
} else {
218-
(def_id, substs)
219+
(def_id, substs, Vec::new())
219220
};
220221

221222
let mir = self.load_mir(resolved_def_id)?;
@@ -235,6 +236,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
235236
resolved_substs,
236237
return_lvalue,
237238
return_to_block,
239+
temporaries,
238240
)?;
239241

240242
let arg_locals = self.frame().mir.args_iter();
@@ -430,7 +432,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
430432
def_id: DefId,
431433
substs: &'tcx Substs<'tcx>,
432434
args: &mut Vec<(Value, Ty<'tcx>)>,
433-
) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> {
435+
) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec<Value>)> {
434436
let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs);
435437
let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref));
436438

@@ -442,7 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
442444
// and those from the method:
443445
let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname);
444446

445-
Ok((did, substs))
447+
Ok((did, substs, Vec::new()))
446448
}
447449

448450
traits::VtableClosure(vtable_closure) => {
@@ -453,6 +455,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
453455
let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id);
454456
trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind);
455457
self.unpack_fn_args(args)?;
458+
let mut temporaries = Vec::new();
456459
match (closure_kind, trait_closure_kind) {
457460
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
458461
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
@@ -472,23 +475,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
472475

473476
// Interpreter magic: insert an intermediate pointer, so we can skip the
474477
// intermediate function call.
475-
// FIXME: this is a memory leak, should probably add the pointer to the
476-
// current stack.
477-
let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?;
478-
args[0].0 = Value::ByVal(PrimVal::from_ptr(first));
478+
let ptr = match args[0].0 {
479+
Value::ByRef(ptr) => ptr,
480+
Value::ByVal(primval) => {
481+
let ptr = self.alloc_ptr(args[0].1)?;
482+
let kind = self.ty_to_primval_kind(args[0].1)?;
483+
self.memory.write_primval(ptr, primval, kind)?;
484+
temporaries.push(Value::ByRef(ptr));
485+
ptr
486+
},
487+
Value::ByValPair(a, b) => {
488+
let ptr = self.alloc_ptr(args[0].1)?;
489+
self.write_pair_to_ptr(a, b, ptr, args[0].1)?;
490+
temporaries.push(Value::ByRef(ptr));
491+
ptr
492+
},
493+
};
494+
args[0].0 = Value::ByVal(PrimVal::from_ptr(ptr));
479495
args[0].1 = self.tcx.mk_mut_ptr(args[0].1);
480496
}
481497

482498
_ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind),
483499
}
484-
Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs))
500+
Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries))
485501
}
486502

487503
traits::VtableFnPointer(vtable_fn_ptr) => {
488504
if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty {
489505
args.remove(0);
490506
self.unpack_fn_args(args)?;
491-
Ok((did, substs))
507+
Ok((did, substs, Vec::new()))
492508
} else {
493509
bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr)
494510
}
@@ -504,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
504520
let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?;
505521
let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?;
506522
*first_ty = sig.inputs[0];
507-
Ok((def_id, substs))
523+
Ok((def_id, substs, Vec::new()))
508524
} else {
509525
Err(EvalError::VtableForArgumentlessMethod)
510526
}

0 commit comments

Comments
 (0)