Skip to content

Commit 4e3dbf9

Browse files
committed
auto merge of #8666 : nikomatsakis/rust/issue-3678-extern-fn-types, r=pcwalton
Change the type of crust fns like this one: extern fn foo() { ... } from `*u8` to `extern "C" fn()`. r? @pcwalton (or whomever)
2 parents 3f6f79b + ffb6404 commit 4e3dbf9

29 files changed

+385
-131
lines changed

doc/rust.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,20 +1006,25 @@ code_. They are defined in the same way as any other Rust function,
10061006
except that they have the `extern` modifier.
10071007

10081008
~~~
1009+
// Declares an extern fn, the ABI defaults to "C"
10091010
extern fn new_vec() -> ~[int] { ~[] }
1011+
1012+
// Declares an extern fn with "stdcall" ABI
1013+
extern "stdcall" fn new_vec_stdcall() -> ~[int] { ~[] }
10101014
~~~
10111015

1012-
Extern functions may not be called from Rust code,
1013-
but Rust code may take their value as a raw `u8` pointer.
1016+
Unlike normal functions, extern fns have an `extern "ABI" fn()`.
1017+
This is the same type as the functions declared in an extern
1018+
block.
10141019

10151020
~~~
10161021
# extern fn new_vec() -> ~[int] { ~[] }
1017-
let fptr: *u8 = new_vec;
1022+
let fptr: extern "C" fn() -> ~[int] = new_vec;
10181023
~~~
10191024

1020-
The primary motivation for extern functions is
1021-
to create callbacks for foreign functions that expect to receive function
1022-
pointers.
1025+
Extern functions may be called from Rust code, but
1026+
caution must be taken with respect to the size of the stack
1027+
segment, just as when calling an extern function normally.
10231028

10241029
### Type definitions
10251030

@@ -1384,14 +1389,13 @@ between the Rust ABI and the foreign ABI.
13841389
A number of [attributes](#attributes) control the behavior of external
13851390
blocks.
13861391

1387-
By default external blocks assume
1388-
that the library they are calling uses the standard C "cdecl" ABI.
1389-
Other ABIs may be specified using the `abi` attribute as in
1392+
By default external blocks assume that the library they are calling
1393+
uses the standard C "cdecl" ABI. Other ABIs may be specified using
1394+
an `abi` string, as shown here:
13901395

13911396
~~~{.xfail-test}
13921397
// Interface to the Windows API
1393-
#[abi = "stdcall"]
1394-
extern { }
1398+
extern "stdcall" { }
13951399
~~~
13961400

13971401
The `link_name` attribute allows the name of the library to be specified.
@@ -1407,6 +1411,12 @@ This is particularly useful for creating external blocks for libc,
14071411
which tends to not follow standard library naming conventions
14081412
and is linked to all Rust programs anyway.
14091413

1414+
The type of a function
1415+
declared in an extern block
1416+
is `extern "abi" fn(A1, ..., An) -> R`,
1417+
where `A1...An` are the declared types of its arguments
1418+
and `R` is the decalred return type.
1419+
14101420
## Attributes
14111421

14121422
~~~~~~~~{.ebnf .gram}

src/libextra/rl.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,31 @@ use std::libc::{c_char, c_int};
1616
use std::local_data;
1717
use std::str;
1818

19+
#[cfg(stage0)]
1920
pub mod rustrt {
2021
use std::libc::{c_char, c_int};
2122

22-
#[cfg(stage0)]
23-
mod macro_hack {
24-
#[macro_escape];
25-
macro_rules! externfn(
26-
(fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => (
27-
extern {
28-
fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*;
29-
}
30-
)
31-
)
23+
extern {
24+
fn linenoise(prompt: *c_char) -> *c_char;
25+
fn linenoiseHistoryAdd(line: *c_char) -> c_int;
26+
fn linenoiseHistorySetMaxLen(len: c_int) -> c_int;
27+
fn linenoiseHistorySave(file: *c_char) -> c_int;
28+
fn linenoiseHistoryLoad(file: *c_char) -> c_int;
29+
fn linenoiseSetCompletionCallback(callback: *u8);
30+
fn linenoiseAddCompletion(completions: *(), line: *c_char);
3231
}
32+
}
33+
34+
#[cfg(not(stage0))]
35+
pub mod rustrt {
36+
use std::libc::{c_char, c_int};
3337

3438
externfn!(fn linenoise(prompt: *c_char) -> *c_char)
3539
externfn!(fn linenoiseHistoryAdd(line: *c_char) -> c_int)
3640
externfn!(fn linenoiseHistorySetMaxLen(len: c_int) -> c_int)
3741
externfn!(fn linenoiseHistorySave(file: *c_char) -> c_int)
3842
externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int)
39-
externfn!(fn linenoiseSetCompletionCallback(callback: *u8))
43+
externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *())))
4044
externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char))
4145
}
4246

src/librustc/middle/trans/base.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use middle::trans::expr;
5252
use middle::trans::foreign;
5353
use middle::trans::glue;
5454
use middle::trans::inline;
55+
use middle::trans::llrepr::LlvmRepr;
5556
use middle::trans::machine;
5657
use middle::trans::machine::{llalign_of_min, llsize_of};
5758
use middle::trans::meth;
@@ -1739,6 +1740,10 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext,
17391740
args: &[ast::arg],
17401741
raw_llargs: &[ValueRef],
17411742
arg_tys: &[ty::t]) -> @mut Block {
1743+
debug!("copy_args_to_allocas: raw_llargs=%s arg_tys=%s",
1744+
raw_llargs.llrepr(fcx.ccx),
1745+
arg_tys.repr(fcx.ccx.tcx));
1746+
17421747
let _icx = push_ctxt("copy_args_to_allocas");
17431748
let mut bcx = bcx;
17441749

src/librustc/middle/trans/builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::hashmap::HashMap;
2222
use std::libc::{c_uint, c_ulonglong, c_char};
2323
use std::vec;
2424
use syntax::codemap::span;
25+
use std::ptr::is_not_null;
2526

2627
pub struct Builder {
2728
llbuilder: BuilderRef,
@@ -483,6 +484,7 @@ impl Builder {
483484
debug!("Store %s -> %s",
484485
self.ccx.tn.val_to_str(val),
485486
self.ccx.tn.val_to_str(ptr));
487+
assert!(is_not_null(self.llbuilder));
486488
self.count_insn("store");
487489
unsafe {
488490
llvm::LLVMBuildStore(self.llbuilder, val, ptr);

src/librustc/middle/trans/expr.rs

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -824,56 +824,30 @@ fn trans_def_datum_unadjusted(bcx: @mut Block,
824824
{
825825
let _icx = push_ctxt("trans_def_datum_unadjusted");
826826

827-
match def {
827+
let fn_data = match def {
828828
ast::def_fn(did, _) | ast::def_static_method(did, None, _) => {
829-
let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id);
830-
return fn_data_to_datum(bcx, ref_expr, did, fn_data);
829+
callee::trans_fn_ref(bcx, did, ref_expr.id)
831830
}
832831
ast::def_static_method(impl_did, Some(trait_did), _) => {
833-
let fn_data = meth::trans_static_method_callee(bcx, impl_did,
834-
trait_did,
835-
ref_expr.id);
836-
return fn_data_to_datum(bcx, ref_expr, impl_did, fn_data);
832+
meth::trans_static_method_callee(bcx, impl_did,
833+
trait_did,
834+
ref_expr.id)
837835
}
838836
_ => {
839837
bcx.tcx().sess.span_bug(ref_expr.span, fmt!(
840838
"Non-DPS def %? referened by %s",
841839
def, bcx.node_id_to_str(ref_expr.id)));
842840
}
843-
}
841+
};
844842

845-
fn fn_data_to_datum(bcx: @mut Block,
846-
ref_expr: &ast::expr,
847-
def_id: ast::def_id,
848-
fn_data: callee::FnData) -> DatumBlock {
849-
/*!
850-
*
851-
* Translates a reference to a top-level fn item into a rust
852-
* value. This is just a fn pointer.
853-
*/
854-
855-
let is_extern = {
856-
let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id);
857-
ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn
858-
};
859-
let (rust_ty, llval) = if is_extern {
860-
let rust_ty = ty::mk_ptr(
861-
bcx.tcx(),
862-
ty::mt {
863-
ty: ty::mk_mach_uint(ast::ty_u8),
864-
mutbl: ast::m_imm
865-
}); // *u8
866-
(rust_ty, PointerCast(bcx, fn_data.llfn, Type::i8p()))
867-
} else {
868-
let fn_ty = expr_ty(bcx, ref_expr);
869-
(fn_ty, fn_data.llfn)
870-
};
871-
return DatumBlock {
872-
bcx: bcx,
873-
datum: Datum {val: llval,
874-
ty: rust_ty,
875-
mode: ByValue}
876-
};
843+
let fn_ty = expr_ty(bcx, ref_expr);
844+
DatumBlock {
845+
bcx: bcx,
846+
datum: Datum {
847+
val: fn_data.llfn,
848+
ty: fn_ty,
849+
mode: ByValue
850+
}
877851
}
878852
}
879853

@@ -1657,6 +1631,7 @@ pub fn cast_type_kind(t: ty::t) -> cast_kind {
16571631
ty::ty_float(*) => cast_float,
16581632
ty::ty_ptr(*) => cast_pointer,
16591633
ty::ty_rptr(*) => cast_pointer,
1634+
ty::ty_bare_fn(*) => cast_pointer,
16601635
ty::ty_int(*) => cast_integral,
16611636
ty::ty_uint(*) => cast_integral,
16621637
ty::ty_bool => cast_integral,
@@ -1719,10 +1694,16 @@ fn trans_imm_cast(bcx: @mut Block, expr: @ast::expr,
17191694
val_ty(lldiscrim_a),
17201695
lldiscrim_a, true),
17211696
cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out),
1722-
_ => ccx.sess.bug("translating unsupported cast.")
1697+
_ => ccx.sess.bug(fmt!("translating unsupported cast: \
1698+
%s (%?) -> %s (%?)",
1699+
t_in.repr(ccx.tcx), k_in,
1700+
t_out.repr(ccx.tcx), k_out))
17231701
}
17241702
}
1725-
_ => ccx.sess.bug("translating unsupported cast.")
1703+
_ => ccx.sess.bug(fmt!("translating unsupported cast: \
1704+
%s (%?) -> %s (%?)",
1705+
t_in.repr(ccx.tcx), k_in,
1706+
t_out.repr(ccx.tcx), k_out))
17261707
};
17271708
return immediate_rvalue_bcx(bcx, newval, t_out);
17281709
}

src/librustc/middle/trans/foreign.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use middle::trans::cabi;
2121
use middle::trans::build::*;
2222
use middle::trans::builder::noname;
2323
use middle::trans::common::*;
24+
use middle::trans::llrepr::LlvmRepr;
2425
use middle::trans::type_of::*;
2526
use middle::trans::type_of;
2627
use middle::ty;
@@ -399,7 +400,29 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
399400
ccx, vec::append_one((*path).clone(), ast_map::path_name(
400401
special_idents::clownshoe_abi
401402
)));
402-
let llty = type_of_fn_from_ty(ccx, t);
403+
404+
// Compute the LLVM type that the function would have if it
405+
// were just a normal Rust function. This will be the type of
406+
// the wrappee fn.
407+
let llty = match ty::get(t).sty {
408+
ty::ty_bare_fn(ref f) => {
409+
assert!(!f.abis.is_rust() && !f.abis.is_intrinsic());
410+
type_of_rust_fn(ccx, f.sig.inputs, f.sig.output)
411+
}
412+
_ => {
413+
ccx.sess.bug(fmt!("build_rust_fn: extern fn %s has ty %s, \
414+
expected a bare fn ty",
415+
path.repr(tcx),
416+
t.repr(tcx)));
417+
}
418+
};
419+
420+
debug!("build_rust_fn: path=%s id=%? t=%s llty=%s",
421+
path.repr(tcx),
422+
id,
423+
t.repr(tcx),
424+
llty.llrepr(ccx));
425+
403426
let llfndecl = base::decl_internal_cdecl_fn(ccx.llmod, ps, llty);
404427
base::trans_fn(ccx,
405428
(*path).clone(),

src/librustc/middle/trans/llrepr.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2012 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+
use middle::trans::context::CrateContext;
12+
use middle::trans::type_::Type;
13+
use lib::llvm::ValueRef;
14+
15+
pub trait LlvmRepr {
16+
fn llrepr(&self, ccx: &CrateContext) -> ~str;
17+
}
18+
19+
impl<'self, T:LlvmRepr> LlvmRepr for &'self [T] {
20+
fn llrepr(&self, ccx: &CrateContext) -> ~str {
21+
let reprs = self.map(|t| t.llrepr(ccx));
22+
fmt!("[%s]", reprs.connect(","))
23+
}
24+
}
25+
26+
impl LlvmRepr for Type {
27+
fn llrepr(&self, ccx: &CrateContext) -> ~str {
28+
ccx.tn.type_to_str(*self)
29+
}
30+
}
31+
32+
impl LlvmRepr for ValueRef {
33+
fn llrepr(&self, ccx: &CrateContext) -> ~str {
34+
ccx.tn.val_to_str(*self)
35+
}
36+
}
37+
38+

src/librustc/middle/trans/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ pub mod asm;
4545
pub mod type_;
4646
pub mod value;
4747
pub mod basic_block;
48+
pub mod llrepr;

src/librustc/middle/typeck/check/mod.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,22 +3099,6 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: @mut FnCtxt,
30993099
let typ = fcx.local_ty(sp, nid);
31003100
return no_params(typ);
31013101
}
3102-
ast::def_fn(_, ast::extern_fn) => {
3103-
// extern functions are just u8 pointers
3104-
return ty_param_bounds_and_ty {
3105-
generics: ty::Generics {
3106-
type_param_defs: @~[],
3107-
region_param: None
3108-
},
3109-
ty: ty::mk_ptr(
3110-
fcx.ccx.tcx,
3111-
ty::mt {
3112-
ty: ty::mk_mach_uint(ast::ty_u8),
3113-
mutbl: ast::m_imm
3114-
})
3115-
};
3116-
}
3117-
31183102
ast::def_fn(id, _) | ast::def_static_method(id, _, _) |
31193103
ast::def_static(id, _) | ast::def_variant(_, id) |
31203104
ast::def_struct(id) => {

src/librustc/middle/typeck/collect.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,13 +1087,13 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item)
10871087
tcx.tcache.insert(local_def(it.id), tpt);
10881088
return tpt;
10891089
}
1090-
ast::item_fn(ref decl, purity, _, ref generics, _) => {
1090+
ast::item_fn(ref decl, purity, abi, ref generics, _) => {
10911091
assert!(rp.is_none());
10921092
let ty_generics = ty_generics(ccx, None, generics, 0);
10931093
let tofd = astconv::ty_of_bare_fn(ccx,
10941094
&empty_rscope,
10951095
purity,
1096-
AbiSet::Rust(),
1096+
abi,
10971097
&generics.lifetimes,
10981098
decl);
10991099
let tpt = ty_param_bounds_and_ty {

0 commit comments

Comments
 (0)