Skip to content

libsyntax/librustc: Allow calling variadic foreign functions. #10064

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
Nov 5, 2013
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
1 change: 1 addition & 0 deletions src/librustc/lib/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub static False: Bool = 0 as Bool;

// Consts for the LLVM CallConv type, pre-cast to uint.

#[deriving(Eq)]
pub enum CallConv {
CCallConv = 0,
FastCallConv = 8,
Expand Down
13 changes: 10 additions & 3 deletions src/librustc/metadata/tydecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,17 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
inputs.push(parse_ty(st, |x,y| conv(x,y)));
}
st.pos += 1u; // eat the ']'
let variadic = if peek(st) == 'A' {
st.pos += 1; // eat the 'A'
true
} else { false };
let ret_ty = parse_ty(st, conv);
ty::FnSig {bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
inputs: inputs,
output: ret_ty}
ty::FnSig {
bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
inputs: inputs,
output: ret_ty,
variadic: variadic
}
}

// Rust metadata parsing
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ fn enc_fn_sig(w: @mut MemWriter, cx: @ctxt, fsig: &ty::FnSig) {
enc_ty(w, cx, *ty);
}
mywrite!(w, "]");
if fsig.variadic {
mywrite!(w, "A");
}
enc_ty(w, cx, fsig.output);
}

Expand Down
16 changes: 14 additions & 2 deletions src/librustc/middle/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,8 +750,12 @@ pub fn trans_call_inner(in_cx: @mut Block,
let mut llargs = ~[];
bcx = trans_args(bcx, args, callee_ty,
autoref_arg, &mut llargs);
let arg_tys = match args {
ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(),
ArgVals(_) => fail!("expected arg exprs.")
};
bcx = foreign::trans_native_call(bcx, callee_ty,
llfn, opt_llretslot.unwrap(), llargs);
llfn, opt_llretslot.unwrap(), llargs, arg_tys);
}

// If the caller doesn't care about the result of this fn call,
Expand Down Expand Up @@ -789,6 +793,7 @@ pub fn trans_args(cx: @mut Block,
let _icx = push_ctxt("trans_args");
let mut temp_cleanups = ~[];
let arg_tys = ty::ty_fn_args(fn_ty);
let variadic = ty::fn_is_variadic(fn_ty);

let mut bcx = cx;

Expand All @@ -797,10 +802,17 @@ pub fn trans_args(cx: @mut Block,
// to cast her view of the arguments to the caller's view.
match args {
ArgExprs(arg_exprs) => {
let num_formal_args = arg_tys.len();
for (i, arg_expr) in arg_exprs.iter().enumerate() {
let arg_ty = if i >= num_formal_args {
assert!(variadic);
expr_ty_adjusted(cx, *arg_expr)
} else {
arg_tys[i]
};
let arg_val = unpack_result!(bcx, {
trans_arg_expr(bcx,
arg_tys[i],
arg_ty,
ty::ByCopy,
*arg_expr,
&mut temp_cleanups,
Expand Down
48 changes: 38 additions & 10 deletions src/librustc/middle/trans/foreign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ pub fn register_foreign_item_fn(ccx: @mut CrateContext,
let cc = match llvm_calling_convention(ccx, abis) {
Some(cc) => cc,
None => {
// FIXME(#8357) We really ought to report a span here
ccx.sess.fatal(
ccx.sess.span_fatal(foreign_item.span,
format!("ABI `{}` has no suitable ABI \
for target architecture \
in module {}",
Expand All @@ -135,6 +134,12 @@ pub fn register_foreign_item_fn(ccx: @mut CrateContext,
let lname = link_name(ccx, foreign_item);
let tys = foreign_types_for_id(ccx, foreign_item.id);

// Make sure the calling convention is right for variadic functions
// (should've been caught if not in typeck)
if tys.fn_sig.variadic {
assert!(cc == lib::llvm::CCallConv);
}

// Create the LLVM value for the C extern fn
let llfn_ty = lltype_for_fn_from_foreign_types(&tys);
let llfn = base::get_extern_fn(&mut ccx.externs, ccx.llmod,
Expand All @@ -148,7 +153,8 @@ pub fn trans_native_call(bcx: @mut Block,
callee_ty: ty::t,
llfn: ValueRef,
llretptr: ValueRef,
llargs_rust: &[ValueRef]) -> @mut Block {
llargs_rust: &[ValueRef],
passed_arg_tys: ~[ty::t]) -> @mut Block {
/*!
* Prepares a call to a native function. This requires adapting
* from the Rust argument passing rules to the native rules.
Expand All @@ -160,6 +166,10 @@ pub fn trans_native_call(bcx: @mut Block,
* - `llretptr`: where to store the return value of the function
* - `llargs_rust`: a list of the argument values, prepared
* as they would be if calling a Rust function
* - `passed_arg_tys`: Rust type for the arguments. Normally we
* can derive these from callee_ty but in the case of variadic
* functions passed_arg_tys will include the Rust type of all
* the arguments including the ones not specified in the fn's signature.
*/

let ccx = bcx.ccx();
Expand All @@ -176,7 +186,7 @@ pub fn trans_native_call(bcx: @mut Block,
ty::ty_bare_fn(ref fn_ty) => (fn_ty.abis, fn_ty.sig.clone()),
_ => ccx.sess.bug("trans_native_call called on non-function type")
};
let llsig = foreign_signature(ccx, &fn_sig);
let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys);
let ret_def = !ty::type_is_voidish(bcx.tcx(), fn_sig.output);
let fn_type = cabi::compute_abi_info(ccx,
llsig.llarg_tys,
Expand Down Expand Up @@ -208,7 +218,7 @@ pub fn trans_native_call(bcx: @mut Block,
let mut llarg_rust = llarg_rust;

// Does Rust pass this argument by pointer?
let rust_indirect = type_of::arg_is_indirect(ccx, fn_sig.inputs[i]);
let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]);

debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}",
i,
Expand All @@ -219,7 +229,7 @@ pub fn trans_native_call(bcx: @mut Block,
// Ensure that we always have the Rust value indirectly,
// because it makes bitcasting easier.
if !rust_indirect {
let scratch = base::alloca(bcx, type_of::type_of(ccx, fn_sig.inputs[i]), "__arg");
let scratch = base::alloca(bcx, type_of::type_of(ccx, passed_arg_tys[i]), "__arg");
Store(bcx, llarg_rust, scratch);
llarg_rust = scratch;
}
Expand Down Expand Up @@ -331,6 +341,20 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
foreign_mod: &ast::foreign_mod) {
let _icx = push_ctxt("foreign::trans_foreign_mod");
for &foreign_item in foreign_mod.items.iter() {
match foreign_item.node {
ast::foreign_item_fn(*) => {
let (abis, mut path) = match ccx.tcx.items.get_copy(&foreign_item.id) {
ast_map::node_foreign_item(_, abis, _, path) => (abis, (*path).clone()),
_ => fail!("Unable to find foreign item in tcx.items table.")
};
if !(abis.is_rust() || abis.is_intrinsic()) {
path.push(ast_map::path_name(foreign_item.ident));
register_foreign_item_fn(ccx, abis, &path, foreign_item);
}
}
_ => ()
}

let lname = link_name(ccx, foreign_item);
ccx.item_symbols.insert(foreign_item.id, lname.to_owned());
}
Expand Down Expand Up @@ -701,7 +725,7 @@ pub fn link_name(ccx: &CrateContext, i: @ast::foreign_item) -> @str {
}
}

fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig, arg_tys: &[ty::t])
-> LlvmSignature {
/*!
* The ForeignSignature is the LLVM types of the arguments/return type
Expand All @@ -711,7 +735,7 @@ fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
* values by pointer like we do.
*/

let llarg_tys = fn_sig.inputs.map(|&arg| type_of(ccx, arg));
let llarg_tys = arg_tys.map(|&arg| type_of(ccx, arg));
let llret_ty = type_of::type_of(ccx, fn_sig.output);
LlvmSignature {
llarg_tys: llarg_tys,
Expand All @@ -731,7 +755,7 @@ fn foreign_types_for_fn_ty(ccx: &mut CrateContext,
ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(),
_ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type")
};
let llsig = foreign_signature(ccx, &fn_sig);
let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs);
let ret_def = !ty::type_is_voidish(ccx.tcx, fn_sig.output);
let fn_ty = cabi::compute_abi_info(ccx,
llsig.llarg_tys,
Expand Down Expand Up @@ -790,7 +814,11 @@ fn lltype_for_fn_from_foreign_types(tys: &ForeignTypes) -> Type {
llargument_tys.push(llarg_ty);
}

Type::func(llargument_tys, &llreturn_ty)
if tys.fn_sig.variadic {
Type::variadic_func(llargument_tys, &llreturn_ty)
} else {
Type::func(llargument_tys, &llreturn_ty)
}
}

pub fn lltype_for_foreign_fn(ccx: &mut CrateContext, ty: ty::t) -> Type {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/trans/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ impl Reflector {
self.visit("fn_input", extra);
}
let extra = ~[self.c_uint(retval),
self.c_bool(sig.variadic),
self.c_tydesc(sig.output)];
self.visit("fn_output", extra);
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/middle/trans/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ impl Type {
args.len() as c_uint, False))
}

pub fn variadic_func(args: &[Type], ret: &Type) -> Type {
let vec : &[TypeRef] = unsafe { cast::transmute(args) };
ty!(llvm::LLVMFunctionType(ret.to_ref(), vec::raw::to_ptr(vec),
args.len() as c_uint, True))
}

pub fn func_pair(cx: &CrateContext, fn_ty: &Type) -> Type {
Type::struct_([fn_ty.ptr_to(), Type::opaque_cbox_ptr(cx)], false)
}
Expand Down
29 changes: 25 additions & 4 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,12 +433,15 @@ pub struct ClosureTy {
*
* - `lifetimes` is the list of region names bound in this fn.
* - `inputs` is the list of arguments and their modes.
* - `output` is the return type. */
* - `output` is the return type.
* - `variadic` indicates whether this is a varidic function. (only true for foreign fns)
*/
#[deriving(Clone, Eq, IterBytes)]
pub struct FnSig {
bound_lifetime_names: OptVec<ast::Ident>,
inputs: ~[t],
output: t
output: t,
variadic: bool
}

#[deriving(Clone, Eq, IterBytes)]
Expand Down Expand Up @@ -705,6 +708,7 @@ pub enum type_err {
terr_float_mismatch(expected_found<ast::float_ty>),
terr_traits(expected_found<ast::DefId>),
terr_builtin_bounds(expected_found<BuiltinBounds>),
terr_variadic_mismatch(expected_found<bool>)
}

#[deriving(Eq, IterBytes)]
Expand Down Expand Up @@ -1251,7 +1255,8 @@ pub fn mk_ctor_fn(cx: ctxt, input_tys: &[ty::t], output: ty::t) -> t {
sig: FnSig {
bound_lifetime_names: opt_vec::Empty,
inputs: input_args,
output: output
output: output,
variadic: false
}
})
}
Expand Down Expand Up @@ -1338,7 +1343,8 @@ pub fn fold_sig(sig: &FnSig, fldop: &fn(t) -> t) -> FnSig {
FnSig {
bound_lifetime_names: sig.bound_lifetime_names.clone(),
inputs: args,
output: fldop(sig.output)
output: fldop(sig.output),
variadic: sig.variadic
}
}

Expand Down Expand Up @@ -2816,6 +2822,16 @@ fn node_id_has_type_params(cx: ctxt, id: ast::NodeId) -> bool {
cx.node_type_substs.contains_key(&id)
}

pub fn fn_is_variadic(fty: t) -> bool {
match get(fty).sty {
ty_bare_fn(ref f) => f.sig.variadic,
ty_closure(ref f) => f.sig.variadic,
ref s => {
fail!("fn_is_variadic() called on non-fn type: {:?}", s)
}
}
}

pub fn ty_fn_sig(fty: t) -> FnSig {
match get(fty).sty {
ty_bare_fn(ref f) => f.sig.clone(),
Expand Down Expand Up @@ -3579,6 +3595,11 @@ pub fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
values.expected.to_str(),
values.found.to_str())
}
terr_variadic_mismatch(ref values) => {
format!("expected {} fn but found {} function",
if values.expected { "variadic" } else { "non-variadic" },
if values.found { "variadic" } else { "non-variadic" })
}
}
}

Expand Down
21 changes: 15 additions & 6 deletions src/librustc/middle/typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope + Clone + 'static>(
ty::mk_tup(tcx, flds)
}
ast::ty_bare_fn(ref bf) => {
if bf.decl.variadic && !bf.abis.is_c() {
tcx.sess.span_err(ast_ty.span, "variadic function must have C calling convention");
}
ty::mk_bare_fn(tcx, ty_of_bare_fn(this, rscope, bf.purity,
bf.abis, &bf.lifetimes, &bf.decl))
}
Expand Down Expand Up @@ -660,9 +663,12 @@ fn ty_of_method_or_bare_fn<AC:AstConv,RS:RegionScope + Clone + 'static>(
ty::BareFnTy {
purity: purity,
abis: abi,
sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
inputs: input_tys,
output: output_ty}
sig: ty::FnSig {
bound_lifetime_names: bound_lifetime_names,
inputs: input_tys,
output: output_ty,
variadic: decl.variadic
}
});

fn transform_self_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
Expand Down Expand Up @@ -770,9 +776,12 @@ pub fn ty_of_closure<AC:AstConv,RS:RegionScope + Clone + 'static>(
onceness: onceness,
region: bound_region,
bounds: bounds,
sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
inputs: input_tys,
output: output_ty}
sig: ty::FnSig {
bound_lifetime_names: bound_lifetime_names,
inputs: input_tys,
output: output_ty,
variadic: decl.variadic
}
}
}

Expand Down
Loading