auto merge of #15831 : rpjohnst/rust/generic-foreign-fns, r=alexcrichton

This allows for things like this:

    extern "C" fn callback<T>(t: T) { /* ... */ }
    extern "C" {
        fn take_callback(c: extern fn(i32));
    }

and later:

    take_callback(callback::<i32>);

Closes #12502.
This commit is contained in:
bors 2014-08-07 15:56:43 +00:00
commit 4879ca7924
15 changed files with 256 additions and 63 deletions

View File

@ -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);
}
_ => {}

View File

@ -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,
&param_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,
&param_substs::empty(),
item.id,
None);
} else {
trans_fn(ccx,
&**decl,
&**body,
llfn,
&param_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.

View File

@ -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: &param_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(), &param_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: &param_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(), &param_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, &param_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),

View File

@ -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

View File

@ -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

View File

@ -8,8 +8,10 @@
// 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>();
}

View File

@ -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 <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.
// ignore-test this should fail to compile (#15844)
#[no_mangle]
fn foo<T>() {} //~ ERROR generic functions must be mangled
#[no_mangle]
extern fn foo<T>() {} //~ ERROR generic functions must be mangled

View File

@ -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

View File

@ -0,0 +1,16 @@
#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);
}

View File

@ -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 <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);
}

View File

@ -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 <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;
}

View File

@ -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

View File

@ -0,0 +1,8 @@
#include <stdint.h>
uint32_t foo();
uint32_t bar();
uint32_t add() {
return foo() + bar();
}

View File

@ -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 <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.
#[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);
}

View File

@ -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 <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));
}