diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index f607dfdd17f..3bfd03da283 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -281,8 +281,32 @@ pub fn trans_unboxing_shim(bcx: Block, }; let boxed_function_type = ty::mk_bare_fn(tcx, boxed_function_type).subst(tcx, &substs); - let function_type = - ty::mk_bare_fn(tcx, (*fty).clone()).subst(tcx, &substs); + let function_type = match fty.abi { + synabi::RustCall => { + // We're passing through to a RustCall ABI function, but + // because the shim will already perform untupling, we + // need to pretend the shimmed function does not use + // RustCall so the untupled arguments can be passed + // through verbatim. This is kind of ugly. + let fake_ty = ty::FnSig { + binder_id: fty.sig.binder_id, + inputs: type_of::untuple_arguments_if_necessary(ccx, + fty.sig.inputs.as_slice(), + fty.abi), + output: fty.sig.output, + variadic: false, + }; + let fake_ty = ty::BareFnTy { + fn_style: fty.fn_style, + abi: synabi::Rust, + sig: fake_ty, + }; + ty::mk_bare_fn(tcx, fake_ty).subst(tcx, &substs) + } + _ => { + ty::mk_bare_fn(tcx, (*fty).clone()).subst(tcx, &substs) + } + }; let function_name = ty::with_path(tcx, method_id, |path| { link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim") diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index b5f2ef419a0..989ac63cd5c 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -58,10 +58,10 @@ pub fn type_of_explicit_arg(ccx: &CrateContext, arg_ty: ty::t) -> Type { /// Yields the types of the "real" arguments for this function. For most /// functions, these are simply the types of the arguments. For functions with /// the `RustCall` ABI, however, this untuples the arguments of the function. -fn untuple_arguments_if_necessary(ccx: &CrateContext, - inputs: &[ty::t], - abi: abi::Abi) - -> Vec { +pub fn untuple_arguments_if_necessary(ccx: &CrateContext, + inputs: &[ty::t], + abi: abi::Abi) + -> Vec { if abi != abi::RustCall { return inputs.iter().map(|x| (*x).clone()).collect() } diff --git a/src/test/run-pass/issue-16739.rs b/src/test/run-pass/issue-16739.rs new file mode 100644 index 00000000000..80a7ae72e18 --- /dev/null +++ b/src/test/run-pass/issue-16739.rs @@ -0,0 +1,42 @@ +// Copyright 2014 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(unboxed_closures)] + +// Test that unboxing shim for calling rust-call ABI methods through a +// trait box works and does not cause an ICE + +struct Foo { foo: uint } + +impl FnOnce<(), uint> for Foo { + #[rust_call_abi_hack] + fn call_once(self, _: ()) -> uint { self.foo } +} + +impl FnOnce<(uint,), uint> for Foo { + #[rust_call_abi_hack] + fn call_once(self, (x,): (uint,)) -> uint { self.foo + x } +} + +impl FnOnce<(uint, uint), uint> for Foo { + #[rust_call_abi_hack] + fn call_once(self, (x, y): (uint, uint)) -> uint { self.foo + x + y } +} + +fn main() { + let f = box Foo { foo: 42 } as Box>; + assert_eq!(f.call_once(()), 42); + + let f = box Foo { foo: 40 } as Box>; + assert_eq!(f.call_once((2,)), 42); + + let f = box Foo { foo: 40 } as Box>; + assert_eq!(f.call_once((1, 1)), 42); +}