diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6ede414e8375..9b31d3a1b646 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1755,7 +1755,8 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) { match ecx.tcx.map.find(*id) { Some(ast_map::NodeItem(i)) => { match i.node { - ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => { + ast::ItemFn(_, _, abi, ref generics, _) + if abi != abi::Rust && !generics.is_type_parameterized() => { rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id); } _ => {} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f1e84b8da810..51bba1085e49 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -171,7 +171,7 @@ impl<'a> Drop for StatRecorder<'a> { } // only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions -fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, +pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, ty: Type, output: ty::t) -> ValueRef { let llfn: ValueRef = name.with_c_str(|buf| { @@ -1922,20 +1922,27 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { let _icx = push_ctxt("trans_item"); match item.node { ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => { - if abi != Rust { - let llfndecl = get_item_val(ccx, item.id); - foreign::trans_rust_fn_with_foreign_abi( - ccx, &**decl, &**body, item.attrs.as_slice(), llfndecl, item.id); - } else if !generics.is_type_parameterized() { + if !generics.is_type_parameterized() { let llfn = get_item_val(ccx, item.id); - trans_fn(ccx, - &**decl, - &**body, - llfn, - ¶m_substs::empty(), - item.id, - item.attrs.as_slice(), - TranslateItems); + if abi != Rust { + foreign::trans_rust_fn_with_foreign_abi(ccx, + &**decl, + &**body, + item.attrs.as_slice(), + llfn, + ¶m_substs::empty(), + item.id, + None); + } else { + trans_fn(ccx, + &**decl, + &**body, + llfn, + ¶m_substs::empty(), + item.id, + item.attrs.as_slice(), + TranslateItems); + } } else { // Be sure to travel more than just one layer deep to catch nested // items in blocks and such. diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index cb46d62fca9e..93e357205484 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -24,6 +24,7 @@ use middle::trans::type_of::*; use middle::trans::type_of; use middle::ty::FnSig; use middle::ty; +use middle::subst::Subst; use std::cmp; use libc::c_uint; use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi}; @@ -525,6 +526,26 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { // inline the one into the other. Of course we could just generate the // correct code in the first place, but this is much simpler. +pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext, + t: ty::t, + name: &str) + -> ValueRef { + let tys = foreign_types_for_fn_ty(ccx, t); + let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); + let cconv = match ty::get(t).sty { + ty::ty_bare_fn(ref fn_ty) => { + let c = llvm_calling_convention(ccx, fn_ty.abi); + c.unwrap_or(llvm::CCallConv) + } + _ => fail!("expected bare fn in decl_rust_fn_with_foreign_abi") + }; + let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::mk_nil()); + add_argument_attributes(&tys, llfn); + debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", + ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn)); + llfn +} + pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext, sp: Span, sym: String, @@ -554,31 +575,39 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, body: &ast::Block, attrs: &[ast::Attribute], llwrapfn: ValueRef, - id: ast::NodeId) { + param_substs: ¶m_substs, + id: ast::NodeId, + hash: Option<&str>) { let _icx = push_ctxt("foreign::build_foreign_fn"); - let tys = foreign_types_for_id(ccx, id); + + let fnty = ty::node_id_to_type(ccx.tcx(), id); + let mty = fnty.subst(ccx.tcx(), ¶m_substs.substs); + let tys = foreign_types_for_fn_ty(ccx, mty); unsafe { // unsafe because we call LLVM operations // Build up the Rust function (`foo0` above). - let llrustfn = build_rust_fn(ccx, decl, body, attrs, id); + let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash); // Build up the foreign wrapper (`foo` above). - return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, id); + return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty); } fn build_rust_fn(ccx: &CrateContext, decl: &ast::FnDecl, body: &ast::Block, + param_substs: ¶m_substs, attrs: &[ast::Attribute], - id: ast::NodeId) + id: ast::NodeId, + hash: Option<&str>) -> ValueRef { let _icx = push_ctxt("foreign::foreign::build_rust_fn"); let tcx = ccx.tcx(); - let t = ty::node_id_to_type(tcx, id); + let t = ty::node_id_to_type(tcx, id).subst( + ccx.tcx(), ¶m_substs.substs); let ps = ccx.tcx.map.with_path(id, |path| { let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name)); - link::mangle(path.chain(abi.move_iter()), None) + link::mangle(path.chain(abi.move_iter()), hash) }); // Compute the type that the function would have if it were just a @@ -601,8 +630,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice()); base::set_llvm_fn_attrs(attrs, llfn); - base::trans_fn(ccx, decl, body, llfn, ¶m_substs::empty(), id, [], - TranslateItems); + base::trans_fn(ccx, decl, body, llfn, param_substs, id, [], TranslateItems); llfn } @@ -610,13 +638,11 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, llrustfn: ValueRef, llwrapfn: ValueRef, tys: &ForeignTypes, - id: ast::NodeId) { + t: ty::t) { let _icx = push_ctxt( "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn"); let tcx = ccx.tcx(); - let t = ty::node_id_to_type(tcx, id); - debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})", ccx.tn.val_to_string(llrustfn), ccx.tn.val_to_string(llwrapfn), diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 986d3328f2c3..97f0f1beeace 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -18,6 +18,7 @@ use middle::trans::base::{trans_enum_variant, push_ctxt, get_item_val}; use middle::trans::base::{trans_fn, decl_internal_rust_fn}; use middle::trans::base; use middle::trans::common::*; +use middle::trans::foreign; use middle::ty; use middle::typeck; use util::ppaux::Repr; @@ -123,19 +124,29 @@ pub fn monomorphic_fn(ccx: &CrateContext, monomorphizing.insert(fn_id, depth + 1); } - let s = ccx.tcx.map.with_path(fn_id.node, |path| { + let hash; + let s = { let mut state = sip::SipState::new(); hash_id.hash(&mut state); mono_ty.hash(&mut state); - exported_name(path, format!("h{}", state.result()).as_slice()) - }); + hash = format!("h{}", state.result()); + ccx.tcx.map.with_path(fn_id.node, |path| { + exported_name(path, hash.as_slice()) + }) + }; + debug!("monomorphize_fn mangled to {}", s); // This shouldn't need to option dance. let mut hash_id = Some(hash_id); - let mk_lldecl = || { - let lldecl = decl_internal_rust_fn(ccx, mono_ty, s.as_slice()); + let mk_lldecl = |abi: abi::Abi| { + let lldecl = if abi != abi::Rust { + foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, s.as_slice()) + } else { + decl_internal_rust_fn(ccx, mono_ty, s.as_slice()) + }; + ccx.monomorphized.borrow_mut().insert(hash_id.take_unwrap(), lldecl); lldecl }; @@ -144,13 +155,21 @@ pub fn monomorphic_fn(ccx: &CrateContext, ast_map::NodeItem(i) => { match *i { ast::Item { - node: ast::ItemFn(ref decl, _, _, _, ref body), + node: ast::ItemFn(ref decl, _, abi, _, ref body), .. } => { - let d = mk_lldecl(); + let d = mk_lldecl(abi); set_llvm_fn_attrs(i.attrs.as_slice(), d); - trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, [], - IgnoreItems); + + if abi != abi::Rust { + foreign::trans_rust_fn_with_foreign_abi( + ccx, &**decl, &**body, [], d, &psubsts, fn_id.node, + Some(hash.as_slice())); + } else { + trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, [], + IgnoreItems); + } + d } _ => { @@ -162,7 +181,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, let parent = ccx.tcx.map.get_parent(fn_id.node); let tvs = ty::enum_variants(ccx.tcx(), local_def(parent)); let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap(); - let d = mk_lldecl(); + let d = mk_lldecl(abi::Rust); set_inline_hint(d); match v.node.kind { ast::TupleVariantKind(ref args) => { @@ -180,7 +199,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, d } ast_map::NodeMethod(mth) => { - let d = mk_lldecl(); + let d = mk_lldecl(abi::Rust); set_llvm_fn_attrs(mth.attrs.as_slice(), d); trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, &psubsts, mth.id, [], IgnoreItems); @@ -189,7 +208,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, ast_map::NodeTraitMethod(method) => { match *method { ast::Provided(mth) => { - let d = mk_lldecl(); + let d = mk_lldecl(abi::Rust); set_llvm_fn_attrs(mth.attrs.as_slice(), d); trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, &psubsts, mth.id, [], IgnoreItems); @@ -202,7 +221,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, } } ast_map::NodeStructCtor(struct_def) => { - let d = mk_lldecl(); + let d = mk_lldecl(abi::Rust); set_inline_hint(d); base::trans_tuple_struct(ccx, struct_def.fields.as_slice(), @@ -230,7 +249,7 @@ pub fn monomorphic_fn(ccx: &CrateContext, ccx.monomorphizing.borrow_mut().insert(fn_id, depth); debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id)); - (lldecl, false) + (lldecl, true) } // Used to identify cached monomorphized functions and vtables diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 780faddb8863..6379166753ea 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -444,17 +444,6 @@ pub fn ensure_no_ty_param_bounds(ccx: &CrateCtxt, } } -fn ensure_generics_abi(ccx: &CrateCtxt, - span: Span, - abi: abi::Abi, - generics: &ast::Generics) { - if generics.ty_params.len() > 0 && - !(abi == abi::Rust || abi == abi::RustIntrinsic) { - span_err!(ccx.tcx.sess, span, E0123, - "foreign functions may not use type parameters"); - } -} - pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { let tcx = ccx.tcx; debug!("convert: item {} with id {}", token::get_ident(it.ident), it.id); @@ -572,13 +561,8 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { }, ast::ItemTy(_, ref generics) => { ensure_no_ty_param_bounds(ccx, it.span, generics, "type"); - let pty = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, pty.ty); - }, - ast::ItemFn(_, _, abi, ref generics, _) => { - ensure_generics_abi(ccx, it.span, abi, generics); - let pty = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, pty.ty); + let tpt = ty_of_item(ccx, it); + write_ty_to_tcx(tcx, it.id, tpt.ty); }, _ => { // This call populates the type cache with the converted type diff --git a/src/test/compile-fail/generic-extern.rs b/src/test/compile-fail/generic-extern.rs index ce8099d64a0f..a7140d1be87a 100644 --- a/src/test/compile-fail/generic-extern.rs +++ b/src/test/compile-fail/generic-extern.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern "C" fn foo() {} //~ERROR foreign functions may not use type parameters +extern { + fn foo(); //~ ERROR foreign items may not have type parameters +} fn main() { - let _ = foo::; + foo::(); } diff --git a/src/test/compile-fail/generic-no-mangle.rs b/src/test/compile-fail/generic-no-mangle.rs new file mode 100644 index 000000000000..f4ead18ee168 --- /dev/null +++ b/src/test/compile-fail/generic-no-mangle.rs @@ -0,0 +1,18 @@ +// 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. + +// ignore-test this should fail to compile (#15844) + +#[no_mangle] +fn foo() {} //~ ERROR generic functions must be mangled + +#[no_mangle] +extern fn foo() {} //~ ERROR generic functions must be mangled + diff --git a/src/test/run-make/extern-fn-generic/Makefile b/src/test/run-make/extern-fn-generic/Makefile new file mode 100644 index 000000000000..a325acbf687b --- /dev/null +++ b/src/test/run-make/extern-fn-generic/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +all: + $(CC) -std=c99 test.c -c -o $(TMPDIR)/test.o + $(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o + $(RUSTC) testcrate.rs -L $(TMPDIR) + $(RUSTC) test.rs -L $(TMPDIR) + $(call RUN,test) || exit 1 diff --git a/src/test/run-make/extern-fn-generic/test.c b/src/test/run-make/extern-fn-generic/test.c new file mode 100644 index 000000000000..f23dd1eb1462 --- /dev/null +++ b/src/test/run-make/extern-fn-generic/test.c @@ -0,0 +1,16 @@ +#include + +typedef struct TestStruct { + uint8_t x; + int32_t y; +} TestStruct; + +typedef int callback(TestStruct s); + +uint32_t call(callback *c) { + TestStruct s; + s.x = 'a'; + s.y = 3; + + return c(s); +} diff --git a/src/test/run-make/extern-fn-generic/test.rs b/src/test/run-make/extern-fn-generic/test.rs new file mode 100644 index 000000000000..ee0485683ecc --- /dev/null +++ b/src/test/run-make/extern-fn-generic/test.rs @@ -0,0 +1,32 @@ +// 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. + +extern crate testcrate; + +extern "C" fn bar(ts: testcrate::TestStruct) -> T { ts.y } + +#[link(name = "test")] +extern { + fn call(c: extern "C" fn(testcrate::TestStruct) -> i32) -> i32; +} + +fn main() { + // Let's test calling it cross crate + let back = unsafe { + testcrate::call(testcrate::foo::) + }; + assert_eq!(3, back); + + // And just within this crate + let back = unsafe { + call(bar::) + }; + assert_eq!(3, back); +} diff --git a/src/test/run-make/extern-fn-generic/testcrate.rs b/src/test/run-make/extern-fn-generic/testcrate.rs new file mode 100644 index 000000000000..5fd61bb419ca --- /dev/null +++ b/src/test/run-make/extern-fn-generic/testcrate.rs @@ -0,0 +1,24 @@ +// 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. + +#![crate_type = "lib"] + +#[repr(C)] +pub struct TestStruct { + pub x: u8, + pub y: T +} + +pub extern "C" fn foo(ts: TestStruct) -> T { ts.y } + +#[link(name = "test")] +extern { + pub fn call(c: extern "C" fn(TestStruct) -> i32) -> i32; +} diff --git a/src/test/run-make/extern-fn-mangle/Makefile b/src/test/run-make/extern-fn-mangle/Makefile new file mode 100644 index 000000000000..ea6971853fe9 --- /dev/null +++ b/src/test/run-make/extern-fn-mangle/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: + $(CC) -std=c99 test.c -c -o $(TMPDIR)/test.o + $(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o + $(RUSTC) test.rs -L $(TMPDIR) + $(call RUN,test) || exit 1 diff --git a/src/test/run-make/extern-fn-mangle/test.c b/src/test/run-make/extern-fn-mangle/test.c new file mode 100644 index 000000000000..8d93917ade03 --- /dev/null +++ b/src/test/run-make/extern-fn-mangle/test.c @@ -0,0 +1,8 @@ +#include + +uint32_t foo(); +uint32_t bar(); + +uint32_t add() { + return foo() + bar(); +} diff --git a/src/test/run-make/extern-fn-mangle/test.rs b/src/test/run-make/extern-fn-mangle/test.rs new file mode 100644 index 000000000000..35b5a9278a42 --- /dev/null +++ b/src/test/run-make/extern-fn-mangle/test.rs @@ -0,0 +1,25 @@ +// 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. + +#[no_mangle] +pub extern "C" fn foo() -> i32 { 3 } + +#[no_mangle] +pub extern "C" fn bar() -> i32 { 5 } + +#[link(name = "test", kind = "static")] +extern { + fn add() -> i32; +} + +fn main() { + let back = unsafe { add() }; + assert_eq!(8, back); +} diff --git a/src/test/run-pass/generic-extern-mangle.rs b/src/test/run-pass/generic-extern-mangle.rs new file mode 100644 index 000000000000..69846750afc0 --- /dev/null +++ b/src/test/run-pass/generic-extern-mangle.rs @@ -0,0 +1,16 @@ +// 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. + +extern "C" fn foo(a: T, b: T) -> T { a + b } + +fn main() { + assert_eq!(99u8, foo(255u8, 100u8)); + assert_eq!(99u16, foo(65535u16, 100u16)); +}