From 21e924975d8236572d3abcf8e0b997138389c37f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Sep 2016 15:57:13 +0200 Subject: [PATCH] only split the Fn* arguments in case of closures and function pointers --- src/interpreter/terminator/mod.rs | 43 +++++++++++------------ tests/run-pass/function_pointers.rs | 11 ++++++ tests/run-pass/overloaded-calls-simple.rs | 33 +++++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 tests/run-pass/overloaded-calls-simple.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index ae7eaf2c8b66..8f0375936ccf 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -178,9 +178,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Abi::Rust | Abi::RustCall => { - // TODO(solson): Adjust the first argument when calling a Fn or - // FnMut closure via FnOnce::call_once. - let mut arg_srcs = Vec::new(); for arg in args { let src = self.eval_operand_to_ptr(arg)?; @@ -196,25 +193,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs) }; - if fn_ty.abi == Abi::RustCall { - if let Some((last, last_ty)) = arg_srcs.pop() { - let last_layout = self.type_layout(last_ty); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - } - let mir = self.load_mir(resolved_def_id); let (return_ptr, return_to_block) = match destination { Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), @@ -366,6 +344,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } + fn unpack_fn_args(&self, args: &mut Vec<(Pointer, Ty<'tcx>)>) { + if let Some((last, last_ty)) = args.pop() { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + args.push((src, ty)); + } + } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + } + /// Trait method, which has to be resolved to an impl method. fn trait_method( &mut self, @@ -395,6 +392,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .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); + self.unpack_fn_args(args); match (closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | @@ -426,6 +424,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); + self.unpack_fn_args(args); Ok((did, substs)) } else { bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index e7368004069f..4f597d4a2e94 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -6,6 +6,10 @@ fn g(i: i32) -> i32 { i*42 } +fn h(i: i32, j: i32) -> i32 { + j * i * 7 +} + fn return_fn_ptr() -> fn() -> i32 { f } @@ -22,6 +26,10 @@ fn indirect2 i32>(f: F) -> i32 { f(10) } fn indirect_mut2 i32>(mut f: F) -> i32 { f(10) } fn indirect_once2 i32>(f: F) -> i32 { f(10) } +fn indirect3 i32>(f: F) -> i32 { f(10, 3) } +fn indirect_mut3 i32>(mut f: F) -> i32 { f(10, 3) } +fn indirect_once3 i32>(f: F) -> i32 { f(10, 3) } + fn main() { assert_eq!(call_fn_ptr(), 42); assert_eq!(indirect(f), 42); @@ -30,6 +38,9 @@ fn main() { assert_eq!(indirect2(g), 420); assert_eq!(indirect_mut2(g), 420); assert_eq!(indirect_once2(g), 420); + assert_eq!(indirect3(h), 210); + assert_eq!(indirect_mut3(h), 210); + assert_eq!(indirect_once3(h), 210); assert!(return_fn_ptr() == f); assert!(return_fn_ptr() as unsafe fn() -> i32 == f as fn() -> i32 as unsafe fn() -> i32); } diff --git a/tests/run-pass/overloaded-calls-simple.rs b/tests/run-pass/overloaded-calls-simple.rs new file mode 100644 index 000000000000..1eeda12ca06f --- /dev/null +++ b/tests/run-pass/overloaded-calls-simple.rs @@ -0,0 +1,33 @@ +// Copyright 2012 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. + + +#![feature(lang_items, unboxed_closures, fn_traits)] + +struct S3 { + x: i32, + y: i32, +} + +impl FnOnce<(i32,i32)> for S3 { + type Output = i32; + extern "rust-call" fn call_once(self, (z,zz): (i32,i32)) -> i32 { + self.x * self.y * z * zz + } +} + +fn main() { + let s = S3 { + x: 3, + y: 3, + }; + let ans = s(3, 1); + assert_eq!(ans, 27); +}