Generic extern functions written in Rust have their names mangled, as well as their internal clownshoe __rust_abi functions. This allows e.g. specific monomorphizations of these functions to be used as callbacks.
Closes #12502.
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);
}
_ => {}
}
// 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| {
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.
-// 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.
//
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};
// 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,
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
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
}
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),
-// 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.
//
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;
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
};
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
}
_ => {
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) => {
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);
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);
}
}
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(),
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
}
}
-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);
},
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
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-extern "C" fn foo<T>() {} //~ERROR foreign functions may not use type parameters
+extern {
+ fn foo<T>(); //~ ERROR foreign items may not have type parameters
+}
fn main() {
- let _ = foo::<int>;
+ foo::<i32>();
}
--- /dev/null
+-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
--- /dev/null
+#include <stdint.h>
+
+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);
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate testcrate;
+
+extern "C" fn bar<T>(ts: testcrate::TestStruct<T>) -> T { ts.y }
+
+#[link(name = "test")]
+extern {
+ fn call(c: extern "C" fn(testcrate::TestStruct<i32>) -> i32) -> i32;
+}
+
+fn main() {
+ // Let's test calling it cross crate
+ let back = unsafe {
+ testcrate::call(testcrate::foo::<i32>)
+ };
+ assert_eq!(3, back);
+
+ // And just within this crate
+ let back = unsafe {
+ call(bar::<i32>)
+ };
+ assert_eq!(3, back);
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
+ pub x: u8,
+ pub y: T
+}
+
+pub extern "C" fn foo<T>(ts: TestStruct<T>) -> T { ts.y }
+
+#[link(name = "test")]
+extern {
+ pub fn call(c: extern "C" fn(TestStruct<i32>) -> i32) -> i32;
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern "C" fn foo<T: Int>(a: T, b: T) -> T { a + b }
+
+fn main() {
+ assert_eq!(99u8, foo(255u8, 100u8));
+ assert_eq!(99u16, foo(65535u16, 100u16));
+}