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:
commit
4879ca7924
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>();
|
||||
}
|
||||
|
18
src/test/compile-fail/generic-no-mangle.rs
Normal file
18
src/test/compile-fail/generic-no-mangle.rs
Normal 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
|
||||
|
8
src/test/run-make/extern-fn-generic/Makefile
Normal file
8
src/test/run-make/extern-fn-generic/Makefile
Normal 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
|
16
src/test/run-make/extern-fn-generic/test.c
Normal file
16
src/test/run-make/extern-fn-generic/test.c
Normal 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);
|
||||
}
|
32
src/test/run-make/extern-fn-generic/test.rs
Normal file
32
src/test/run-make/extern-fn-generic/test.rs
Normal 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);
|
||||
}
|
24
src/test/run-make/extern-fn-generic/testcrate.rs
Normal file
24
src/test/run-make/extern-fn-generic/testcrate.rs
Normal 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;
|
||||
}
|
7
src/test/run-make/extern-fn-mangle/Makefile
Normal file
7
src/test/run-make/extern-fn-mangle/Makefile
Normal 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
|
8
src/test/run-make/extern-fn-mangle/test.c
Normal file
8
src/test/run-make/extern-fn-mangle/test.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t foo();
|
||||
uint32_t bar();
|
||||
|
||||
uint32_t add() {
|
||||
return foo() + bar();
|
||||
}
|
25
src/test/run-make/extern-fn-mangle/test.rs
Normal file
25
src/test/run-make/extern-fn-mangle/test.rs
Normal 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);
|
||||
}
|
16
src/test/run-pass/generic-extern-mangle.rs
Normal file
16
src/test/run-pass/generic-extern-mangle.rs
Normal 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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user