Skip to content

Allow generic foreign functions. #15831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 7, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
_ => {}
Expand Down
35 changes: 21 additions & 14 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down Expand Up @@ -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.
Expand Down
52 changes: 39 additions & 13 deletions src/librustc/middle/trans/foreign.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -601,22 +630,19 @@ 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
}

unsafe fn build_wrap_fn(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),
Expand Down
49 changes: 34 additions & 15 deletions src/librustc/middle/trans/monomorphize.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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;
Expand Down Expand Up @@ -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
};
Expand All @@ -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
}
_ => {
Expand All @@ -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) => {
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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(),
Expand Down Expand Up @@ -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
Expand Down
20 changes: 2 additions & 18 deletions src/librustc/middle/typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/test/compile-fail/generic-extern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 18 additions & 0 deletions src/test/compile-fail/generic-no-mangle.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions src/test/run-make/extern-fn-generic/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions src/test/run-make/extern-fn-generic/test.c
Original file line number Diff line number Diff line change
@@ -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);
}
Loading