From 63cc7fc9e84c2332f2712c041e21bb748dfe9575 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 12:51:48 +0200 Subject: [PATCH 1/7] fix miri backtrace panic --- src/interpreter/mod.rs | 8 +------- src/interpreter/terminator.rs | 2 +- tests/run-pass/try-operator-custom.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 tests/run-pass/try-operator-custom.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f15755533034..095b8c0c5340 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1188,13 +1188,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) } } - let inst = Instance(def_id, substs); - match ::std::panic::catch_unwind(|| { - format!("inside call to {}", inst) - }) { - Ok(msg) => err.span_note(span, &msg), - Err(_) => err.span_note(span, &format!("ppaux::parameterized failed: {:?}, {:?}", def_id, substs)), - }; + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); } err.emit(); } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 6636e2b854fe..851dc82adc50 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -218,7 +218,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), None => (None, StackPopCleanup::None), }; - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; + self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; diff --git a/tests/run-pass/try-operator-custom.rs b/tests/run-pass/try-operator-custom.rs new file mode 100644 index 000000000000..3b447f36ece1 --- /dev/null +++ b/tests/run-pass/try-operator-custom.rs @@ -0,0 +1,13 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert!(Ok::(42) == Ok(42)); +} From 1e0b3b207d5c337ed27a567f5cc27f69e7d86bb7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 12:52:01 +0200 Subject: [PATCH 2/7] prep for eddyb's find_method --- src/interpreter/terminator.rs | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 851dc82adc50..478c0232c22a 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -527,9 +527,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mname = self.tcx.item_name(def_id); // Create a concatenated set of substitutions which includes those from the impl // and those from the method: - let mth = get_impl_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); + let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - Ok((mth.method.def_id, mth.substs)) + Ok((did, substs)) } traits::VtableClosure(vtable_closure) => @@ -636,3 +636,35 @@ pub(super) fn get_impl_method<'a, 'tcx>( } } } + +/// Locates the applicable definition of a method, given its name. +pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + impl_def_id: DefId, + impl_substs: &'tcx Substs<'tcx>, + name: ast::Name) + -> (DefId, &'tcx Substs<'tcx>) +{ + assert!(!substs.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); + let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("find_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); + (node_item.item.def_id, substs) + } + None => { + bug!("method {:?} not found in {:?}", name, impl_def_id) + } + } +} From 477d1c20f46d122b4010c099834ff9b9938a4fd6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 13:34:53 +0200 Subject: [PATCH 3/7] fix enum variant downcasting --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 095b8c0c5340..9338044e5341 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -907,9 +907,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Downcast(_, variant) => { use rustc::ty::layout::Layout::*; match *base_layout { - General { discr, .. } => { + General { ref variants, .. } => { return Ok(Lvalue { - ptr: base.ptr.offset(discr.size().bytes() as isize), + ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), extra: LvalueExtra::DowncastVariant(variant), }); } From 8df6e7275ab3530eb5c35768a3a82c7bf7fef515 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 13:35:19 +0200 Subject: [PATCH 4/7] export `StackPopCleanup` (needed by priroda) --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index dba1b81b70f1..8598a8e7c571 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub use interpreter::{ Frame, eval_main, run_mir_passes, + StackPopCleanup, }; pub use memory::{ From 4ab704c57df52569eaf6feec22a38d80d1b85bea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 16:05:30 +0200 Subject: [PATCH 5/7] implement more intrinsics --- src/interpreter/terminator/intrinsics.rs | 251 ++++++++++++++++++ .../{terminator.rs => terminator/mod.rs} | 148 +---------- 2 files changed, 253 insertions(+), 146 deletions(-) create mode 100644 src/interpreter/terminator/intrinsics.rs rename src/interpreter/{terminator.rs => terminator/mod.rs} (77%) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs new file mode 100644 index 000000000000..e2381d59e2cd --- /dev/null +++ b/src/interpreter/terminator/intrinsics.rs @@ -0,0 +1,251 @@ +use rustc::hir::def_id::DefId; +use rustc::mir::repr as mir; +use rustc::ty::layout::Layout; +use rustc::ty::subst::Substs; +use rustc::ty; + +use error::{EvalError, EvalResult}; +use memory::Pointer; +use interpreter::EvalContext; +use primval; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(super) fn call_intrinsic( + &mut self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + args: &[mir::Operand<'tcx>], + dest: Pointer, + dest_layout: &'tcx Layout, + ) -> EvalResult<'tcx, ()> { + // TODO(solson): We can probably remove this _to_ptr easily. + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand_to_ptr(arg)) + .collect(); + let args_ptrs = args_res?; + let pointer_size = self.memory.pointer_size(); + + match &self.tcx.item_name(def_id).as_str()[..] { + "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, + "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, + "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, + + "arith_offset" => { + let ptr = self.memory.read_ptr(args_ptrs[0])?; + let offset = self.memory.read_int(args_ptrs[1], pointer_size)?; + let new_ptr = ptr.offset(offset as isize); + self.memory.write_ptr(dest, new_ptr)?; + } + + "assume" => { + if !self.memory.read_bool(args_ptrs[0])? { + return Err(EvalError::AssumptionNotHeld); + } + } + + "breakpoint" => unimplemented!(), // halt miri + + "copy" | + "copy_nonoverlapping" => { + // FIXME: check whether overlapping occurs + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let elem_align = self.type_align(elem_ty); + let src = self.memory.read_ptr(args_ptrs[0])?; + let dest = self.memory.read_ptr(args_ptrs[1])?; + let count = self.memory.read_isize(args_ptrs[2])?; + self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; + } + + "ctpop" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "ctlz" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "discriminant_value" => { + let ty = substs.type_at(0); + let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; + let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + self.memory.write_uint(dest, discr_val, 8)?; + } + + "fabsf32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + self.memory.write_f32(dest, f.abs())?; + } + + "fabsf64" => { + let f = self.memory.read_f64(args_ptrs[0])?; + self.memory.write_f64(dest, f.abs())?; + } + + "fadd_fast" => { + let ty = substs.type_at(0); + let a = self.read_primval(args_ptrs[0], ty)?; + let b = self.read_primval(args_ptrs[0], ty)?; + let result = primval::binary_op(mir::BinOp::Add, a, b)?; + self.memory.write_primval(dest, result.0)?; + } + + "likely" | + "unlikely" | + "forget" => {} + + "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, + + "min_align_of" => { + let elem_ty = substs.type_at(0); + let elem_align = self.type_align(elem_ty); + self.memory.write_uint(dest, elem_align as u64, pointer_size)?; + } + + "pref_align_of" => { + let ty = substs.type_at(0); + let layout = self.type_layout(ty); + let align = layout.align(&self.tcx.data_layout).pref(); + self.memory.write_uint(dest, align, pointer_size)?; + } + + "move_val_init" => { + let ty = substs.type_at(0); + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], ptr, ty)?; + } + + "needs_drop" => { + let ty = substs.type_at(0); + self.memory.write_bool(dest, self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()))?; + } + + "offset" => { + let pointee_ty = substs.type_at(0); + let pointee_size = self.type_size(pointee_ty) as isize; + let ptr_arg = args_ptrs[0]; + let offset = self.memory.read_isize(args_ptrs[1])?; + + match self.memory.read_ptr(ptr_arg) { + Ok(ptr) => { + let result_ptr = ptr.offset(offset as isize * pointee_size); + self.memory.write_ptr(dest, result_ptr)?; + } + Err(EvalError::ReadBytesAsPointer) => { + let addr = self.memory.read_isize(ptr_arg)?; + let result_addr = addr + offset * pointee_size as i64; + self.memory.write_isize(dest, result_addr)?; + } + Err(e) => return Err(e), + } + } + + "overflowing_sub" => { + self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; + } + + "overflowing_mul" => { + self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; + } + + "overflowing_add" => { + self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; + } + + "powif32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + let i = self.memory.read_int(args_ptrs[1], 4)?; + self.memory.write_f32(dest, f.powi(i as i32))?; + } + + "powif64" => { + let f = self.memory.read_f32(args_ptrs[0])?; + let i = self.memory.read_int(args_ptrs[1], 4)?; + self.memory.write_f32(dest, f.powi(i as i32))?; + } + + "sqrtf32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + self.memory.write_f32(dest, f.sqrt())?; + } + + "sqrtf64" => { + let f = self.memory.read_f64(args_ptrs[0])?; + self.memory.write_f64(dest, f.sqrt())?; + } + + "size_of" => { + let ty = substs.type_at(0); + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } + + "size_of_val" => { + let ty = substs.type_at(0); + if self.type_is_sized(ty) { + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } else { + match ty.sty { + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty) as u64; + let ptr_size = self.memory.pointer_size() as isize; + let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; + self.memory.write_uint(dest, n * elem_size, pointer_size)?; + } + + _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), + } + } + } + // FIXME: wait for eval_operand_to_ptr to be gone + /* + "type_name" => { + let ty = substs.type_at(0); + let ty_name = ty.to_string(); + let s = self.str_to_value(&ty_name)?; + self.memory.write_ptr(dest, s)?; + }*/ + "type_id" => { + let ty = substs.type_at(0); + let n = self.tcx.type_id_hash(ty); + self.memory.write_uint(dest, n, 8)?; + } + + "transmute" => { + let ty = substs.type_at(0); + self.move_(args_ptrs[0], dest, ty)?; + } + + "try" => unimplemented!(), + + "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, + + "volatile_load" => { + let ty = substs.type_at(0); + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(ptr, dest, ty)?; + } + + "volatile_store" => { + let ty = substs.type_at(0); + let dest = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], dest, ty)?; + } + + name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(()) + } +} diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator/mod.rs similarity index 77% rename from src/interpreter/terminator.rs rename to src/interpreter/terminator/mod.rs index 478c0232c22a..600bccfa404c 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator/mod.rs @@ -15,6 +15,8 @@ use error::{EvalError, EvalResult}; use memory::Pointer; use super::{EvalContext, IntegerExt, StackPopCleanup}; +mod intrinsics; + impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { @@ -270,152 +272,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(if not_null { nndiscr } else { 1 - nndiscr }) } - fn call_intrinsic( - &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: &[mir::Operand<'tcx>], - dest: Pointer, - dest_layout: &'tcx Layout, - ) -> EvalResult<'tcx, ()> { - // TODO(solson): We can probably remove this _to_ptr easily. - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand_to_ptr(arg)) - .collect(); - let args_ptrs = args_res?; - let pointer_size = self.memory.pointer_size(); - - match &self.tcx.item_name(def_id).as_str()[..] { - "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, - "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, - "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, - - "assume" => { - if !self.memory.read_bool(args_ptrs[0])? { - return Err(EvalError::AssumptionNotHeld); - } - } - - "copy_nonoverlapping" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let elem_align = self.type_align(elem_ty); - let src = self.memory.read_ptr(args_ptrs[0])?; - let dest = self.memory.read_ptr(args_ptrs[1])?; - let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; - } - - "ctpop" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); - self.memory.write_uint(dest, num.into(), elem_size)?; - } - - "ctlz" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); - self.memory.write_uint(dest, num.into(), elem_size)?; - } - - "discriminant_value" => { - let ty = substs.type_at(0); - let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; - let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.memory.write_uint(dest, discr_val, 8)?; - } - - "forget" => {} - - "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, - - "min_align_of" => { - let elem_ty = substs.type_at(0); - let elem_align = self.type_align(elem_ty); - self.memory.write_uint(dest, elem_align as u64, pointer_size)?; - } - - "move_val_init" => { - let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], ptr, ty)?; - } - - "offset" => { - let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args_ptrs[0]; - let offset = self.memory.read_isize(args_ptrs[1])?; - - match self.memory.read_ptr(ptr_arg) { - Ok(ptr) => { - let result_ptr = ptr.offset(offset as isize * pointee_size); - self.memory.write_ptr(dest, result_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let addr = self.memory.read_isize(ptr_arg)?; - let result_addr = addr + offset * pointee_size as i64; - self.memory.write_isize(dest, result_addr)?; - } - Err(e) => return Err(e), - } - } - - "overflowing_sub" => { - self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; - } - - "overflowing_mul" => { - self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; - } - - "overflowing_add" => { - self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; - } - - "size_of" => { - let ty = substs.type_at(0); - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } - - "size_of_val" => { - let ty = substs.type_at(0); - if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } else { - match ty.sty { - ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size() as isize; - let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; - self.memory.write_uint(dest, n * elem_size, pointer_size)?; - } - - _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), - } - } - } - - "transmute" => { - let ty = substs.type_at(0); - self.move_(args_ptrs[0], dest, ty)?; - } - "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, - - name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), - } - - // Since we pushed no stack frame, the main loop will act - // as if the call just completed and it's returning to the - // current frame. - Ok(()) - } - fn call_c_abi( &mut self, def_id: DefId, From 16f6ae39336b61315b743d0b1721ecd0491fe402 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 18:31:55 +0200 Subject: [PATCH 6/7] fix calling Fn closures as FnOnce closures --- src/interpreter/terminator/mod.rs | 39 ++++++++++++++++++++++++++++--- tests/run-pass/closures.rs | 30 ++++++++++++++++-------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 600bccfa404c..df1277d7a212 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Trait method, which has to be resolved to an impl method. fn trait_method( - &self, + &mut self, trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx>, @@ -388,8 +388,41 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((did, substs)) } - traits::VtableClosure(vtable_closure) => - Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), + traits::VtableClosure(vtable_closure) => { + let trait_closure_kind = self.tcx + .lang_items + .fn_trait_kind(trait_id) + .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); + let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); + trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); + match (closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. + // We want a `fn(self, ...)`. + // We can produce this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + + // interpreter magic: insert an intermediate pointer, so we can skip the intermediate function call + // FIXME: this is a memory leak, should probably add the pointer to the current stack + let ptr_size = self.memory.pointer_size(); + let first = self.memory.allocate(ptr_size, ptr_size)?; + self.memory.copy(args[0].0, first, ptr_size, ptr_size)?; + self.memory.write_ptr(args[0].0, first)?; + self.memory.dump(args[0].0.alloc_id); + } + _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), + } + Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)) + } traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { diff --git a/tests/run-pass/closures.rs b/tests/run-pass/closures.rs index e0b2c24c8357..9b379051eb77 100644 --- a/tests/run-pass/closures.rs +++ b/tests/run-pass/closures.rs @@ -21,18 +21,28 @@ fn crazy_closure() -> (i32, i32, i32) { inner(10) } -// TODO(solson): Implement closure argument adjustment and uncomment this test. -// fn closure_arg_adjustment_problem() -> i64 { -// fn once(f: F) { f(2); } -// let mut y = 1; -// { -// let f = |x| y += x; -// once(f); -// } -// y -// } +fn closure_arg_adjustment_problem() -> i64 { + fn once(f: F) { f(2); } + let mut y = 1; + { + let f = |x| y += x; + once(f); + } + y +} + +fn fn_once_closure_with_multiple_args() -> i64 { + fn once i64>(f: F) -> i64 { f(2, 3) } + let y = 1; + { + let f = |x, z| x + y + z; + once(f) + } +} fn main() { assert_eq!(simple(), 12); assert_eq!(crazy_closure(), (84, 10, 10)); + assert_eq!(closure_arg_adjustment_problem(), 3); + assert_eq!(fn_once_closure_with_multiple_args(), 6); } From 75ccfd57a508807af636a399a6fae5da1894134f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Sep 2016 11:03:41 +0200 Subject: [PATCH 7/7] remove leftover debug print --- src/interpreter/terminator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index df1277d7a212..ae7eaf2c8b66 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -417,7 +417,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let first = self.memory.allocate(ptr_size, ptr_size)?; self.memory.copy(args[0].0, first, ptr_size, ptr_size)?; self.memory.write_ptr(args[0].0, first)?; - self.memory.dump(args[0].0.alloc_id); } _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), }