]> git.lizzy.rs Git - rust.git/commitdiff
Allow generic foreign functions.
authorRussell <rpjohnst@gmail.com>
Sat, 2 Aug 2014 04:25:41 +0000 (22:25 -0600)
committerRussell <rpjohnst@gmail.com>
Wed, 6 Aug 2014 05:28:50 +0000 (23:28 -0600)
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.

src/librustc/metadata/encoder.rs
src/librustc/middle/trans/base.rs
src/librustc/middle/trans/foreign.rs
src/librustc/middle/trans/monomorphize.rs
src/librustc/middle/typeck/collect.rs
src/test/compile-fail/generic-extern.rs
src/test/run-make/extern-fn-generic/Makefile [new file with mode: 0644]
src/test/run-make/extern-fn-generic/test.c [new file with mode: 0644]
src/test/run-make/extern-fn-generic/test.rs [new file with mode: 0644]
src/test/run-make/extern-fn-generic/testcrate.rs [new file with mode: 0644]
src/test/run-pass/generic-extern-mangle.rs [new file with mode: 0644]

index 6ede414e83758b5f7b801716619e7e46c4df739e..9b31d3a1b6460f7fe55a7266268568f0479dfee2 100644 (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);
                     }
                     _ => {}
index f1e84b8da8105b17a008586050efb6d898a173ee..51bba1085e49cedcdf59653223ebb5e5610b7042 100644 (file)
@@ -171,7 +171,7 @@ fn drop(&mut self) {
 }
 
 // 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.
index cb46d62fca9e9f56b81fdff0e432981f16ab4cf0..93e357205484882469235feb5616aa8391fb20e5 100644 (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::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 @@ fn build_rust_fn(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 @@ 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),
index 986d3328f2c3a7a97f8ab94660909cfd54cb9530..97f0f1beeace6d2b0076ad10e08bf8af32a08211 100644 (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_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
index 780faddb88639c8e384bc46882da01caa7dbdb09..6379166753eafa37fbeb69d55794adb2f9becbf5 100644 (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
index ce8099d64a0f11dfe93a5d1af034945637b8c4d6..a7140d1be87ad180ec66aeddf39a6aff5a3233a3 100644 (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>();
 }
diff --git a/src/test/run-make/extern-fn-generic/Makefile b/src/test/run-make/extern-fn-generic/Makefile
new file mode 100644 (file)
index 0000000..a325acb
--- /dev/null
@@ -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
diff --git a/src/test/run-make/extern-fn-generic/test.c b/src/test/run-make/extern-fn-generic/test.c
new file mode 100644 (file)
index 0000000..f23dd1e
--- /dev/null
@@ -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);
+}
diff --git a/src/test/run-make/extern-fn-generic/test.rs b/src/test/run-make/extern-fn-generic/test.rs
new file mode 100644 (file)
index 0000000..ee04856
--- /dev/null
@@ -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);
+}
diff --git a/src/test/run-make/extern-fn-generic/testcrate.rs b/src/test/run-make/extern-fn-generic/testcrate.rs
new file mode 100644 (file)
index 0000000..5fd61bb
--- /dev/null
@@ -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;
+}
diff --git a/src/test/run-pass/generic-extern-mangle.rs b/src/test/run-pass/generic-extern-mangle.rs
new file mode 100644 (file)
index 0000000..6984675
--- /dev/null
@@ -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));
+}