Skip to content

Commit e6e6ef2

Browse files
committed
Allow generic foreign functions.
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.
1 parent 2a47fa7 commit e6e6ef2

File tree

11 files changed

+198
-63
lines changed

11 files changed

+198
-63
lines changed

src/librustc/metadata/encoder.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1755,7 +1755,8 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
17551755
match ecx.tcx.map.find(*id) {
17561756
Some(ast_map::NodeItem(i)) => {
17571757
match i.node {
1758-
ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => {
1758+
ast::ItemFn(_, _, abi, ref generics, _)
1759+
if abi != abi::Rust && !generics.is_type_parameterized() => {
17591760
rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
17601761
}
17611762
_ => {}

src/librustc/middle/trans/base.rs

+21-14
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'a> Drop for StatRecorder<'a> {
171171
}
172172

173173
// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
174-
fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
174+
pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
175175
ty: Type, output: ty::t) -> ValueRef {
176176

177177
let llfn: ValueRef = name.with_c_str(|buf| {
@@ -1922,20 +1922,27 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
19221922
let _icx = push_ctxt("trans_item");
19231923
match item.node {
19241924
ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
1925-
if abi != Rust {
1926-
let llfndecl = get_item_val(ccx, item.id);
1927-
foreign::trans_rust_fn_with_foreign_abi(
1928-
ccx, &**decl, &**body, item.attrs.as_slice(), llfndecl, item.id);
1929-
} else if !generics.is_type_parameterized() {
1925+
if !generics.is_type_parameterized() {
19301926
let llfn = get_item_val(ccx, item.id);
1931-
trans_fn(ccx,
1932-
&**decl,
1933-
&**body,
1934-
llfn,
1935-
&param_substs::empty(),
1936-
item.id,
1937-
item.attrs.as_slice(),
1938-
TranslateItems);
1927+
if abi != Rust {
1928+
foreign::trans_rust_fn_with_foreign_abi(ccx,
1929+
&**decl,
1930+
&**body,
1931+
item.attrs.as_slice(),
1932+
llfn,
1933+
&param_substs::empty(),
1934+
item.id,
1935+
None);
1936+
} else {
1937+
trans_fn(ccx,
1938+
&**decl,
1939+
&**body,
1940+
llfn,
1941+
&param_substs::empty(),
1942+
item.id,
1943+
item.attrs.as_slice(),
1944+
TranslateItems);
1945+
}
19391946
} else {
19401947
// Be sure to travel more than just one layer deep to catch nested
19411948
// items in blocks and such.

src/librustc/middle/trans/foreign.rs

+39-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -24,6 +24,7 @@ use middle::trans::type_of::*;
2424
use middle::trans::type_of;
2525
use middle::ty::FnSig;
2626
use middle::ty;
27+
use middle::subst::Subst;
2728
use std::cmp;
2829
use libc::c_uint;
2930
use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi};
@@ -525,6 +526,26 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) {
525526
// inline the one into the other. Of course we could just generate the
526527
// correct code in the first place, but this is much simpler.
527528

529+
pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext,
530+
t: ty::t,
531+
name: &str)
532+
-> ValueRef {
533+
let tys = foreign_types_for_fn_ty(ccx, t);
534+
let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
535+
let cconv = match ty::get(t).sty {
536+
ty::ty_bare_fn(ref fn_ty) => {
537+
let c = llvm_calling_convention(ccx, fn_ty.abi);
538+
c.unwrap_or(llvm::CCallConv)
539+
}
540+
_ => fail!("expected bare fn in decl_rust_fn_with_foreign_abi")
541+
};
542+
let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::mk_nil());
543+
add_argument_attributes(&tys, llfn);
544+
debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})",
545+
ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn));
546+
llfn
547+
}
548+
528549
pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext,
529550
sp: Span,
530551
sym: String,
@@ -554,31 +575,39 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
554575
body: &ast::Block,
555576
attrs: &[ast::Attribute],
556577
llwrapfn: ValueRef,
557-
id: ast::NodeId) {
578+
param_substs: &param_substs,
579+
id: ast::NodeId,
580+
hash: Option<&str>) {
558581
let _icx = push_ctxt("foreign::build_foreign_fn");
559-
let tys = foreign_types_for_id(ccx, id);
582+
583+
let fnty = ty::node_id_to_type(ccx.tcx(), id);
584+
let mty = fnty.subst(ccx.tcx(), &param_substs.substs);
585+
let tys = foreign_types_for_fn_ty(ccx, mty);
560586

561587
unsafe { // unsafe because we call LLVM operations
562588
// Build up the Rust function (`foo0` above).
563-
let llrustfn = build_rust_fn(ccx, decl, body, attrs, id);
589+
let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);
564590

565591
// Build up the foreign wrapper (`foo` above).
566-
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, id);
592+
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty);
567593
}
568594

569595
fn build_rust_fn(ccx: &CrateContext,
570596
decl: &ast::FnDecl,
571597
body: &ast::Block,
598+
param_substs: &param_substs,
572599
attrs: &[ast::Attribute],
573-
id: ast::NodeId)
600+
id: ast::NodeId,
601+
hash: Option<&str>)
574602
-> ValueRef {
575603
let _icx = push_ctxt("foreign::foreign::build_rust_fn");
576604
let tcx = ccx.tcx();
577-
let t = ty::node_id_to_type(tcx, id);
605+
let t = ty::node_id_to_type(tcx, id).subst(
606+
ccx.tcx(), &param_substs.substs);
578607

579608
let ps = ccx.tcx.map.with_path(id, |path| {
580609
let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));
581-
link::mangle(path.chain(abi.move_iter()), None)
610+
link::mangle(path.chain(abi.move_iter()), hash)
582611
});
583612

584613
// Compute the type that the function would have if it were just a
@@ -601,22 +630,19 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
601630

602631
let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice());
603632
base::set_llvm_fn_attrs(attrs, llfn);
604-
base::trans_fn(ccx, decl, body, llfn, &param_substs::empty(), id, [],
605-
TranslateItems);
633+
base::trans_fn(ccx, decl, body, llfn, param_substs, id, [], TranslateItems);
606634
llfn
607635
}
608636

609637
unsafe fn build_wrap_fn(ccx: &CrateContext,
610638
llrustfn: ValueRef,
611639
llwrapfn: ValueRef,
612640
tys: &ForeignTypes,
613-
id: ast::NodeId) {
641+
t: ty::t) {
614642
let _icx = push_ctxt(
615643
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
616644
let tcx = ccx.tcx();
617645

618-
let t = ty::node_id_to_type(tcx, id);
619-
620646
debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})",
621647
ccx.tn.val_to_string(llrustfn),
622648
ccx.tn.val_to_string(llwrapfn),

src/librustc/middle/trans/monomorphize.rs

+34-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -18,6 +18,7 @@ use middle::trans::base::{trans_enum_variant, push_ctxt, get_item_val};
1818
use middle::trans::base::{trans_fn, decl_internal_rust_fn};
1919
use middle::trans::base;
2020
use middle::trans::common::*;
21+
use middle::trans::foreign;
2122
use middle::ty;
2223
use middle::typeck;
2324
use util::ppaux::Repr;
@@ -123,19 +124,29 @@ pub fn monomorphic_fn(ccx: &CrateContext,
123124
monomorphizing.insert(fn_id, depth + 1);
124125
}
125126

126-
let s = ccx.tcx.map.with_path(fn_id.node, |path| {
127+
let hash;
128+
let s = {
127129
let mut state = sip::SipState::new();
128130
hash_id.hash(&mut state);
129131
mono_ty.hash(&mut state);
130132

131-
exported_name(path, format!("h{}", state.result()).as_slice())
132-
});
133+
hash = format!("h{}", state.result());
134+
ccx.tcx.map.with_path(fn_id.node, |path| {
135+
exported_name(path, hash.as_slice())
136+
})
137+
};
138+
133139
debug!("monomorphize_fn mangled to {}", s);
134140

135141
// This shouldn't need to option dance.
136142
let mut hash_id = Some(hash_id);
137-
let mk_lldecl = || {
138-
let lldecl = decl_internal_rust_fn(ccx, mono_ty, s.as_slice());
143+
let mk_lldecl = |abi: abi::Abi| {
144+
let lldecl = if abi != abi::Rust {
145+
foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, s.as_slice())
146+
} else {
147+
decl_internal_rust_fn(ccx, mono_ty, s.as_slice())
148+
};
149+
139150
ccx.monomorphized.borrow_mut().insert(hash_id.take_unwrap(), lldecl);
140151
lldecl
141152
};
@@ -144,13 +155,21 @@ pub fn monomorphic_fn(ccx: &CrateContext,
144155
ast_map::NodeItem(i) => {
145156
match *i {
146157
ast::Item {
147-
node: ast::ItemFn(ref decl, _, _, _, ref body),
158+
node: ast::ItemFn(ref decl, _, abi, _, ref body),
148159
..
149160
} => {
150-
let d = mk_lldecl();
161+
let d = mk_lldecl(abi);
151162
set_llvm_fn_attrs(i.attrs.as_slice(), d);
152-
trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, [],
153-
IgnoreItems);
163+
164+
if abi != abi::Rust {
165+
foreign::trans_rust_fn_with_foreign_abi(
166+
ccx, &**decl, &**body, [], d, &psubsts, fn_id.node,
167+
Some(hash.as_slice()));
168+
} else {
169+
trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, [],
170+
IgnoreItems);
171+
}
172+
154173
d
155174
}
156175
_ => {
@@ -162,7 +181,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
162181
let parent = ccx.tcx.map.get_parent(fn_id.node);
163182
let tvs = ty::enum_variants(ccx.tcx(), local_def(parent));
164183
let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap();
165-
let d = mk_lldecl();
184+
let d = mk_lldecl(abi::Rust);
166185
set_inline_hint(d);
167186
match v.node.kind {
168187
ast::TupleVariantKind(ref args) => {
@@ -180,7 +199,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
180199
d
181200
}
182201
ast_map::NodeMethod(mth) => {
183-
let d = mk_lldecl();
202+
let d = mk_lldecl(abi::Rust);
184203
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
185204
trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d, &psubsts, mth.id, [],
186205
IgnoreItems);
@@ -189,7 +208,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
189208
ast_map::NodeTraitMethod(method) => {
190209
match *method {
191210
ast::Provided(mth) => {
192-
let d = mk_lldecl();
211+
let d = mk_lldecl(abi::Rust);
193212
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
194213
trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d,
195214
&psubsts, mth.id, [], IgnoreItems);
@@ -202,7 +221,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
202221
}
203222
}
204223
ast_map::NodeStructCtor(struct_def) => {
205-
let d = mk_lldecl();
224+
let d = mk_lldecl(abi::Rust);
206225
set_inline_hint(d);
207226
base::trans_tuple_struct(ccx,
208227
struct_def.fields.as_slice(),
@@ -230,7 +249,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
230249
ccx.monomorphizing.borrow_mut().insert(fn_id, depth);
231250

232251
debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id));
233-
(lldecl, false)
252+
(lldecl, true)
234253
}
235254

236255
// Used to identify cached monomorphized functions and vtables

src/librustc/middle/typeck/collect.rs

+2-18
Original file line numberDiff line numberDiff line change
@@ -444,17 +444,6 @@ pub fn ensure_no_ty_param_bounds(ccx: &CrateCtxt,
444444
}
445445
}
446446

447-
fn ensure_generics_abi(ccx: &CrateCtxt,
448-
span: Span,
449-
abi: abi::Abi,
450-
generics: &ast::Generics) {
451-
if generics.ty_params.len() > 0 &&
452-
!(abi == abi::Rust || abi == abi::RustIntrinsic) {
453-
span_err!(ccx.tcx.sess, span, E0123,
454-
"foreign functions may not use type parameters");
455-
}
456-
}
457-
458447
pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
459448
let tcx = ccx.tcx;
460449
debug!("convert: item {} with id {}", token::get_ident(it.ident), it.id);
@@ -572,13 +561,8 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
572561
},
573562
ast::ItemTy(_, ref generics) => {
574563
ensure_no_ty_param_bounds(ccx, it.span, generics, "type");
575-
let pty = ty_of_item(ccx, it);
576-
write_ty_to_tcx(tcx, it.id, pty.ty);
577-
},
578-
ast::ItemFn(_, _, abi, ref generics, _) => {
579-
ensure_generics_abi(ccx, it.span, abi, generics);
580-
let pty = ty_of_item(ccx, it);
581-
write_ty_to_tcx(tcx, it.id, pty.ty);
564+
let tpt = ty_of_item(ccx, it);
565+
write_ty_to_tcx(tcx, it.id, tpt.ty);
582566
},
583567
_ => {
584568
// This call populates the type cache with the converted type

src/test/compile-fail/generic-extern.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
extern "C" fn foo<T>() {} //~ERROR foreign functions may not use type parameters
11+
extern {
12+
fn foo<T>(); //~ ERROR foreign items may not have type parameters
13+
}
1214

1315
fn main() {
14-
let _ = foo::<int>;
16+
foo::<i32>();
1517
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-include ../tools.mk
2+
3+
all:
4+
$(CC) -std=c99 test.c -c -o $(TMPDIR)/test.o
5+
$(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o
6+
$(RUSTC) testcrate.rs -L $(TMPDIR)
7+
$(RUSTC) test.rs -L $(TMPDIR)
8+
$(call RUN,test) || exit 1
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdint.h>
2+
3+
typedef struct TestStruct {
4+
uint8_t x;
5+
int32_t y;
6+
} TestStruct;
7+
8+
typedef int callback(TestStruct s);
9+
10+
uint32_t call(callback *c) {
11+
TestStruct s;
12+
s.x = 'a';
13+
s.y = 3;
14+
15+
return c(s);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern crate testcrate;
12+
13+
extern "C" fn bar<T>(ts: testcrate::TestStruct<T>) -> T { ts.y }
14+
15+
#[link(name = "test")]
16+
extern {
17+
fn call(c: extern "C" fn(testcrate::TestStruct<i32>) -> i32) -> i32;
18+
}
19+
20+
fn main() {
21+
// Let's test calling it cross crate
22+
let back = unsafe {
23+
testcrate::call(testcrate::foo::<i32>)
24+
};
25+
assert_eq!(3, back);
26+
27+
// And just within this crate
28+
let back = unsafe {
29+
call(bar::<i32>)
30+
};
31+
assert_eq!(3, back);
32+
}

0 commit comments

Comments
 (0)