auto merge of #10064 : luqmana/rust/vvv, r=nikomatsakis
Fixes #2057. Example: ```Rust #[no_std]; type c_char = u8; type c_int = i32; type size_t = uint; extern { fn printf(format: *c_char, ...) -> c_int; } #[lang="fail_bounds_check"] fn fail_bounds_check(_: *c_char, _: size_t, _: size_t, _: size_t) {} #[start] #[fixed_stack_segment] fn main(_: int, _: **u8) -> int { unsafe { let msg = bytes!("Hello World!
This commit is contained in:
commit
1c56652640
@ -29,6 +29,7 @@
|
||||
|
||||
// Consts for the LLVM CallConv type, pre-cast to uint.
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum CallConv {
|
||||
CCallConv = 0,
|
||||
FastCallConv = 8,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
@ -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 {}",
|
||||
@ -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,
|
||||
@ -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.
|
||||
@ -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();
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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;
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
@ -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
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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 {
|
||||
|
@ -375,6 +375,7 @@ pub fn visit_sig(&mut self, retval: uint, sig: &ty::FnSig) {
|
||||
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);
|
||||
}
|
||||
|
@ -154,6 +154,12 @@ pub fn func(args: &[Type], ret: &Type) -> 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)
|
||||
}
|
||||
|
@ -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)]
|
||||
@ -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)]
|
||||
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
@ -3579,6 +3595,11 @@ fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~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" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,6 +396,9 @@ fn check_path_args(tcx: ty::ctxt,
|
||||
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))
|
||||
}
|
||||
@ -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>(
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,9 +643,17 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
|
||||
for item in m.items.iter() {
|
||||
let tpt = ty::lookup_item_type(ccx.tcx, local_def(item.id));
|
||||
if tpt.generics.has_type_params() {
|
||||
ccx.tcx.sess.span_err(
|
||||
item.span,
|
||||
format!("foreign items may not have type parameters"));
|
||||
ccx.tcx.sess.span_err(item.span, "foreign items may not have type parameters");
|
||||
}
|
||||
|
||||
match item.node {
|
||||
ast::foreign_item_fn(ref fn_decl, _) => {
|
||||
if fn_decl.variadic && !m.abis.is_c() {
|
||||
ccx.tcx.sess.span_err(
|
||||
item.span, "variadic function must have C calling convention");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1321,13 +1329,13 @@ fn check_method_argument_types(
|
||||
if ty::type_is_error(method_fn_ty) {
|
||||
let err_inputs = err_args(args.len());
|
||||
check_argument_types(fcx, sp, err_inputs, callee_expr,
|
||||
args, sugar, deref_args);
|
||||
args, sugar, deref_args, false);
|
||||
method_fn_ty
|
||||
} else {
|
||||
match ty::get(method_fn_ty).sty {
|
||||
ty::ty_bare_fn(ref fty) => {
|
||||
check_argument_types(fcx, sp, fty.sig.inputs, callee_expr,
|
||||
args, sugar, deref_args);
|
||||
args, sugar, deref_args, fty.sig.variadic);
|
||||
fty.sig.output
|
||||
}
|
||||
_ => {
|
||||
@ -1339,15 +1347,14 @@ fn check_method_argument_types(
|
||||
}
|
||||
}
|
||||
|
||||
fn check_argument_types(
|
||||
fcx: @mut FnCtxt,
|
||||
sp: Span,
|
||||
fn_inputs: &[ty::t],
|
||||
callee_expr: @ast::Expr,
|
||||
args: &[@ast::Expr],
|
||||
sugar: ast::CallSugar,
|
||||
deref_args: DerefArgs)
|
||||
{
|
||||
fn check_argument_types(fcx: @mut FnCtxt,
|
||||
sp: Span,
|
||||
fn_inputs: &[ty::t],
|
||||
callee_expr: @ast::Expr,
|
||||
args: &[@ast::Expr],
|
||||
sugar: ast::CallSugar,
|
||||
deref_args: DerefArgs,
|
||||
variadic: bool) {
|
||||
/*!
|
||||
*
|
||||
* Generic function that factors out common logic from
|
||||
@ -1362,6 +1369,19 @@ fn check_argument_types(
|
||||
let expected_arg_count = fn_inputs.len();
|
||||
let formal_tys = if expected_arg_count == supplied_arg_count {
|
||||
fn_inputs.map(|a| *a)
|
||||
} else if variadic {
|
||||
if supplied_arg_count >= expected_arg_count {
|
||||
fn_inputs.map(|a| *a)
|
||||
} else {
|
||||
let msg = format!(
|
||||
"this function takes at least {0, plural, =1{# parameter} \
|
||||
other{# parameters}} but {1, plural, =1{# parameter was} \
|
||||
other{# parameters were}} supplied", expected_arg_count, supplied_arg_count);
|
||||
|
||||
tcx.sess.span_err(sp, msg);
|
||||
|
||||
err_args(supplied_arg_count)
|
||||
}
|
||||
} else {
|
||||
let suffix = match sugar {
|
||||
ast::NoSugar => "",
|
||||
@ -1370,19 +1390,15 @@ fn check_argument_types(
|
||||
ast::ForSugar => " (including the closure passed by \
|
||||
the `for` keyword)"
|
||||
};
|
||||
let msg = format!("this function takes {} parameter{} but \
|
||||
{} parameter{} supplied{}",
|
||||
expected_arg_count,
|
||||
if expected_arg_count == 1 {""}
|
||||
else {"s"},
|
||||
supplied_arg_count,
|
||||
if supplied_arg_count == 1 {" was"}
|
||||
else {"s were"},
|
||||
suffix);
|
||||
let msg = format!(
|
||||
"this function takes {0, plural, =1{# parameter} \
|
||||
other{# parameters}} but {1, plural, =1{# parameter was} \
|
||||
other{# parameters were}} supplied{2}",
|
||||
expected_arg_count, supplied_arg_count, suffix);
|
||||
|
||||
tcx.sess.span_err(sp, msg);
|
||||
|
||||
vec::from_elem(supplied_arg_count, ty::mk_err())
|
||||
err_args(supplied_arg_count)
|
||||
};
|
||||
|
||||
debug!("check_argument_types: formal_tys={:?}",
|
||||
@ -1406,7 +1422,15 @@ fn check_argument_types(
|
||||
vtable::early_resolve_expr(callee_expr, fcx, true);
|
||||
}
|
||||
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
// For variadic functions, we don't have a declared type for all of
|
||||
// the arguments hence we only do our usual type checking with
|
||||
// the arguments who's types we do know.
|
||||
let t = if variadic {
|
||||
expected_arg_count
|
||||
} else {
|
||||
supplied_arg_count
|
||||
};
|
||||
for (i, arg) in args.iter().take(t).enumerate() {
|
||||
let is_block = match arg.node {
|
||||
ast::ExprFnBlock(*) |
|
||||
ast::ExprProc(*) |
|
||||
@ -1431,12 +1455,41 @@ fn check_argument_types(
|
||||
DontDerefArgs => {}
|
||||
}
|
||||
|
||||
check_expr_coercable_to_type(
|
||||
fcx, *arg, formal_ty);
|
||||
check_expr_coercable_to_type(fcx, *arg, formal_ty);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to make sure we at least write the ty of the other
|
||||
// arguments which we skipped above.
|
||||
if variadic {
|
||||
for arg in args.iter().skip(expected_arg_count) {
|
||||
check_expr(fcx, *arg);
|
||||
|
||||
// There are a few types which get autopromoted when passed via varargs
|
||||
// in C but we just error out instead and require explicit casts.
|
||||
let arg_ty = structurally_resolved_type(fcx, arg.span, fcx.expr_ty(*arg));
|
||||
match ty::get(arg_ty).sty {
|
||||
ty::ty_float(ast::ty_f32) => {
|
||||
fcx.type_error_message(arg.span,
|
||||
|t| format!("can't pass an {} to variadic function, \
|
||||
cast to c_double", t), arg_ty, None);
|
||||
}
|
||||
ty::ty_int(ast::ty_i8) | ty::ty_int(ast::ty_i16) | ty::ty_bool => {
|
||||
fcx.type_error_message(arg.span,
|
||||
|t| format!("can't pass {} to variadic function, cast to c_int",
|
||||
t), arg_ty, None);
|
||||
}
|
||||
ty::ty_uint(ast::ty_u8) | ty::ty_uint(ast::ty_u16) => {
|
||||
fcx.type_error_message(arg.span,
|
||||
|t| format!("can't pass {} to variadic function, cast to c_uint",
|
||||
t), arg_ty, None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn err_args(len: uint) -> ~[ty::t] {
|
||||
@ -1505,7 +1558,8 @@ fn check_call(fcx: @mut FnCtxt,
|
||||
let error_fn_sig = FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: err_args(args.len()),
|
||||
output: ty::mk_err()
|
||||
output: ty::mk_err(),
|
||||
variadic: false
|
||||
};
|
||||
|
||||
let fn_sig = match *fn_sty {
|
||||
@ -1532,7 +1586,7 @@ fn check_call(fcx: @mut FnCtxt,
|
||||
|
||||
// Call the generic checker.
|
||||
check_argument_types(fcx, call_expr.span, fn_sig.inputs, f,
|
||||
args, sugar, DontDerefArgs);
|
||||
args, sugar, DontDerefArgs, fn_sig.variadic);
|
||||
|
||||
write_call(fcx, call_expr, fn_sig.output, sugar);
|
||||
}
|
||||
@ -1914,7 +1968,8 @@ fn check_expr_fn(fcx: @mut FnCtxt,
|
||||
fty_sig = FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: fn_ty.sig.inputs.map(|_| ty::mk_err()),
|
||||
output: ty::mk_err()
|
||||
output: ty::mk_err(),
|
||||
variadic: false
|
||||
};
|
||||
ty::mk_err()
|
||||
} else {
|
||||
@ -3898,9 +3953,12 @@ fn param(ccx: @mut CrateCtxt, n: uint) -> ty::t {
|
||||
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
|
||||
purity: ast::unsafe_fn,
|
||||
abis: AbiSet::Intrinsic(),
|
||||
sig: FnSig {bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: inputs,
|
||||
output: output}
|
||||
sig: FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
variadic: false
|
||||
}
|
||||
});
|
||||
let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
|
||||
let i_n_tps = i_ty.generics.type_param_defs.len();
|
||||
|
@ -605,7 +605,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
|
||||
.bound_lifetime_names
|
||||
.clone(),
|
||||
inputs: trait_fn_args,
|
||||
output: trait_m.fty.sig.output
|
||||
output: trait_m.fty.sig.output,
|
||||
variadic: false
|
||||
}
|
||||
});
|
||||
let impl_fty =
|
||||
@ -620,7 +621,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
|
||||
.bound_lifetime_names
|
||||
.clone(),
|
||||
inputs: impl_fn_args,
|
||||
output: impl_m.fty.sig.output
|
||||
output: impl_m.fty.sig.output,
|
||||
variadic: false
|
||||
}
|
||||
});
|
||||
|
||||
@ -1291,9 +1293,12 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
|
||||
ty::BareFnTy {
|
||||
abis: abis,
|
||||
purity: ast::unsafe_fn,
|
||||
sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: input_tys,
|
||||
output: output_ty}
|
||||
sig: ty::FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: input_tys,
|
||||
output: output_ty,
|
||||
variadic: decl.variadic
|
||||
}
|
||||
});
|
||||
let tpt = ty_param_bounds_and_ty {
|
||||
generics: ty_generics,
|
||||
|
@ -414,8 +414,7 @@ pub fn eq_opt_regions<C:Combine>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn super_fn_sigs<C:Combine>(
|
||||
this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||
pub fn super_fn_sigs<C:Combine>(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||
|
||||
fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty::t]> {
|
||||
if a_args.len() == b_args.len() {
|
||||
@ -426,12 +425,19 @@ fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty
|
||||
}
|
||||
}
|
||||
|
||||
if a.variadic != b.variadic {
|
||||
return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
|
||||
}
|
||||
|
||||
do argvecs(this, a.inputs, b.inputs)
|
||||
.and_then |inputs| {
|
||||
do this.tys(a.output, b.output).and_then |output| {
|
||||
Ok(FnSig {bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
|
||||
inputs: inputs.clone(),
|
||||
output: output})
|
||||
Ok(FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
|
||||
inputs: inputs.clone(),
|
||||
output: output,
|
||||
variadic: a.variadic
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,8 +190,11 @@ pub fn t_fn(&self, input_tys: &[ty::t], output_ty: ty::t) -> ty::t {
|
||||
onceness: ast::Many,
|
||||
region: ty::re_static,
|
||||
bounds: @~[]},
|
||||
sig: FnSig {inputs: inputs,
|
||||
output: output_ty}
|
||||
sig: FnSig {
|
||||
inputs: inputs,
|
||||
output: output_ty,
|
||||
variadic: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -356,7 +356,8 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
|
||||
sig: ty::FnSig {
|
||||
bound_lifetime_names: opt_vec::Empty,
|
||||
inputs: ~[],
|
||||
output: ty::mk_nil()
|
||||
output: ty::mk_nil(),
|
||||
variadic: false
|
||||
}
|
||||
});
|
||||
|
||||
@ -404,7 +405,8 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
|
||||
ty::mk_int(),
|
||||
ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
|
||||
],
|
||||
output: ty::mk_int()
|
||||
output: ty::mk_int(),
|
||||
variadic: false
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -410,7 +410,11 @@ fn push_sig_to_str(cx: ctxt,
|
||||
s.push_char(bra);
|
||||
let strs = sig.inputs.map(|a| fn_input_to_str(cx, *a));
|
||||
s.push_str(strs.connect(", "));
|
||||
if sig.variadic {
|
||||
s.push_str(", ...");
|
||||
}
|
||||
s.push_char(ket);
|
||||
|
||||
if ty::get(sig.output).sty != ty_nil {
|
||||
s.push_str(" -> ");
|
||||
if ty::type_is_bot(sig.output) {
|
||||
|
@ -382,11 +382,18 @@ fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(stage0)]
|
||||
fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool {
|
||||
if ! self.inner.visit_fn_output(retstyle, inner) { return false; }
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
|
||||
if ! self.inner.visit_fn_output(retstyle, variadic, inner) { return false; }
|
||||
true
|
||||
}
|
||||
|
||||
fn visit_leave_fn(&mut self, purity: uint, proto: uint,
|
||||
n_inputs: uint, retstyle: uint) -> bool {
|
||||
if ! self.inner.visit_leave_fn(purity, proto, n_inputs, retstyle) {
|
||||
|
@ -572,6 +572,7 @@ fn visit_fn_input(&mut self, i: uint, _mode: uint, inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(stage0)]
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, inner: *TyDesc) -> bool {
|
||||
self.writer.write(")".as_bytes());
|
||||
let name = unsafe { (*inner).name };
|
||||
@ -582,6 +583,20 @@ fn visit_fn_output(&mut self, _retstyle: uint, inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
|
||||
if variadic {
|
||||
self.writer.write(", ...".as_bytes());
|
||||
}
|
||||
self.writer.write(")".as_bytes());
|
||||
let name = unsafe { (*inner).name };
|
||||
if name != "()" {
|
||||
self.writer.write(" -> ".as_bytes());
|
||||
self.writer.write(name.as_bytes());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
|
||||
_n_inputs: uint, _retstyle: uint) -> bool { true }
|
||||
|
||||
|
@ -160,7 +160,10 @@ fn visit_leave_enum(&mut self, n_variants: uint,
|
||||
fn visit_enter_fn(&mut self, purity: uint, proto: uint,
|
||||
n_inputs: uint, retstyle: uint) -> bool;
|
||||
fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool;
|
||||
#[cfg(stage0)]
|
||||
fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool;
|
||||
#[cfg(not(stage0))]
|
||||
fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool;
|
||||
fn visit_leave_fn(&mut self, purity: uint, proto: uint,
|
||||
n_inputs: uint, retstyle: uint) -> bool;
|
||||
|
||||
|
@ -892,6 +892,7 @@ pub struct fn_decl {
|
||||
inputs: ~[arg],
|
||||
output: Ty,
|
||||
cf: ret_style,
|
||||
variadic: bool
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
|
||||
|
@ -728,6 +728,7 @@ fn fn_decl(&self, inputs: ~[ast::arg], output: ast::Ty) -> ast::fn_decl {
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
cf: ast::return_val,
|
||||
variadic: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,7 @@ fn fold_foreign_item(&self, ni: @foreign_item) -> @foreign_item {
|
||||
self)),
|
||||
output: self.fold_ty(&fdec.output),
|
||||
cf: fdec.cf,
|
||||
variadic: fdec.variadic
|
||||
},
|
||||
fold_generics(generics, self))
|
||||
}
|
||||
@ -466,6 +467,7 @@ pub fn fold_fn_decl<T:ast_fold>(decl: &ast::fn_decl, fld: &T)
|
||||
inputs: decl.inputs.map(|x| fold_arg_(x, fld)), // bad copy
|
||||
output: fld.fold_ty(&decl.output),
|
||||
cf: decl.cf,
|
||||
variadic: decl.variadic
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -664,12 +664,18 @@ fn binop(rdr: @mut StringReader, op: token::binop) -> token::Token {
|
||||
';' => { bump(rdr); return token::SEMI; }
|
||||
',' => { bump(rdr); return token::COMMA; }
|
||||
'.' => {
|
||||
bump(rdr);
|
||||
if rdr.curr == '.' && nextch(rdr) != '.' {
|
||||
bump(rdr);
|
||||
return token::DOTDOT;
|
||||
}
|
||||
return token::DOT;
|
||||
bump(rdr);
|
||||
return if rdr.curr == '.' {
|
||||
bump(rdr);
|
||||
if rdr.curr == '.' {
|
||||
bump(rdr);
|
||||
token::DOTDOTDOT
|
||||
} else {
|
||||
token::DOTDOT
|
||||
}
|
||||
} else {
|
||||
token::DOT
|
||||
};
|
||||
}
|
||||
'(' => { bump(rdr); return token::LPAREN; }
|
||||
')' => { bump(rdr); return token::RPAREN; }
|
||||
|
@ -701,7 +701,8 @@ fn parser_done(p: Parser){
|
||||
output: ast::Ty{id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ty_nil,
|
||||
span:sp(15,15)}, // not sure
|
||||
cf: ast::return_val
|
||||
cf: ast::return_val,
|
||||
variadic: false
|
||||
},
|
||||
ast::impure_fn,
|
||||
abi::AbiSet::Rust(),
|
||||
|
@ -863,7 +863,7 @@ pub fn parse_ty_bare_fn(&self) -> ty_ {
|
||||
let abis = opt_abis.unwrap_or(AbiSet::Rust());
|
||||
let purity = self.parse_unsafety();
|
||||
self.expect_keyword(keywords::Fn);
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl();
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl(true);
|
||||
return ty_bare_fn(@TyBareFn {
|
||||
abis: abis,
|
||||
purity: purity,
|
||||
@ -875,7 +875,7 @@ pub fn parse_ty_bare_fn(&self) -> ty_ {
|
||||
// Parses a procedure type (`proc`). The initial `proc` keyword must
|
||||
// already have been parsed.
|
||||
pub fn parse_proc_type(&self) -> ty_ {
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl();
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl(false);
|
||||
ty_closure(@TyClosure {
|
||||
sigil: OwnedSigil,
|
||||
region: None,
|
||||
@ -919,7 +919,7 @@ pub fn parse_ty_closure(&self,
|
||||
// Old-style closure syntax (`fn(A)->B`).
|
||||
self.expect_keyword(keywords::Fn);
|
||||
let bounds = self.parse_optional_ty_param_bounds();
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl();
|
||||
let (decl, lifetimes) = self.parse_ty_fn_decl(false);
|
||||
(sigil, decl, lifetimes, bounds)
|
||||
}
|
||||
None => {
|
||||
@ -960,6 +960,7 @@ pub fn parse_ty_closure(&self,
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
cf: return_style,
|
||||
variadic: false
|
||||
};
|
||||
|
||||
(BorrowedSigil, decl, lifetimes, bounds)
|
||||
@ -994,7 +995,7 @@ pub fn parse_unsafety(&self) -> purity {
|
||||
}
|
||||
|
||||
// parse a function type (following the 'fn')
|
||||
pub fn parse_ty_fn_decl(&self) -> (fn_decl, OptVec<ast::Lifetime>) {
|
||||
pub fn parse_ty_fn_decl(&self, allow_variadic: bool) -> (fn_decl, OptVec<ast::Lifetime>) {
|
||||
/*
|
||||
|
||||
(fn) <'lt> (S) -> T
|
||||
@ -1013,17 +1014,13 @@ pub fn parse_ty_fn_decl(&self) -> (fn_decl, OptVec<ast::Lifetime>) {
|
||||
opt_vec::Empty
|
||||
};
|
||||
|
||||
let inputs = self.parse_unspanned_seq(
|
||||
&token::LPAREN,
|
||||
&token::RPAREN,
|
||||
seq_sep_trailing_disallowed(token::COMMA),
|
||||
|p| p.parse_arg_general(false)
|
||||
);
|
||||
let (inputs, variadic) = self.parse_fn_args(false, allow_variadic);
|
||||
let (ret_style, ret_ty) = self.parse_ret_ty();
|
||||
let decl = ast::fn_decl {
|
||||
inputs: inputs,
|
||||
output: ret_ty,
|
||||
cf: ret_style
|
||||
cf: ret_style,
|
||||
variadic: variadic
|
||||
};
|
||||
(decl, lifetimes)
|
||||
}
|
||||
@ -2475,7 +2472,8 @@ pub fn parse_lambda_block_expr(&self) -> @Expr {
|
||||
node: ty_infer,
|
||||
span: *self.span
|
||||
},
|
||||
cf: return_val
|
||||
cf: return_val,
|
||||
variadic: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3526,21 +3524,63 @@ fn parse_generic_values_after_lt(&self) -> (OptVec<ast::Lifetime>, ~[Ty]) {
|
||||
(lifetimes, opt_vec::take_vec(result))
|
||||
}
|
||||
|
||||
// parse the argument list and result type of a function declaration
|
||||
pub fn parse_fn_decl(&self) -> fn_decl {
|
||||
let args: ~[arg] =
|
||||
fn parse_fn_args(&self, named_args: bool, allow_variadic: bool) -> (~[arg], bool) {
|
||||
let sp = *self.span;
|
||||
let mut args: ~[Option<arg>] =
|
||||
self.parse_unspanned_seq(
|
||||
&token::LPAREN,
|
||||
&token::RPAREN,
|
||||
seq_sep_trailing_disallowed(token::COMMA),
|
||||
|p| p.parse_arg()
|
||||
|p| {
|
||||
if *p.token == token::DOTDOTDOT {
|
||||
p.bump();
|
||||
if allow_variadic {
|
||||
if *p.token != token::RPAREN {
|
||||
p.span_fatal(*p.span,
|
||||
"`...` must be last in argument list for variadic function");
|
||||
}
|
||||
} else {
|
||||
p.span_fatal(*p.span,
|
||||
"only foreign functions are allowed to be variadic");
|
||||
}
|
||||
None
|
||||
} else {
|
||||
Some(p.parse_arg_general(named_args))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let variadic = match args.pop_opt() {
|
||||
Some(None) => true,
|
||||
Some(x) => {
|
||||
// Need to put back that last arg
|
||||
args.push(x);
|
||||
false
|
||||
}
|
||||
None => false
|
||||
};
|
||||
|
||||
if variadic && args.is_empty() {
|
||||
self.span_err(sp,
|
||||
"variadic function must be declared with at least one named argument");
|
||||
}
|
||||
|
||||
let args = args.move_iter().map(|x| x.unwrap()).collect();
|
||||
|
||||
(args, variadic)
|
||||
}
|
||||
|
||||
// parse the argument list and result type of a function declaration
|
||||
pub fn parse_fn_decl(&self, allow_variadic: bool) -> fn_decl {
|
||||
|
||||
let (args, variadic) = self.parse_fn_args(true, allow_variadic);
|
||||
let (ret_style, ret_ty) = self.parse_ret_ty();
|
||||
|
||||
ast::fn_decl {
|
||||
inputs: args,
|
||||
output: ret_ty,
|
||||
cf: ret_style,
|
||||
variadic: variadic
|
||||
}
|
||||
}
|
||||
|
||||
@ -3729,7 +3769,8 @@ fn maybe_parse_borrowed_explicit_self(this: &Parser) -> ast::explicit_self_ {
|
||||
let fn_decl = ast::fn_decl {
|
||||
inputs: fn_inputs,
|
||||
output: ret_ty,
|
||||
cf: ret_style
|
||||
cf: ret_style,
|
||||
variadic: false
|
||||
};
|
||||
|
||||
(spanned(lo, hi, explicit_self), fn_decl)
|
||||
@ -3759,6 +3800,7 @@ fn parse_fn_block_decl(&self) -> fn_decl {
|
||||
inputs: inputs_captures,
|
||||
output: output,
|
||||
cf: return_val,
|
||||
variadic: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3784,6 +3826,7 @@ fn parse_proc_decl(&self) -> fn_decl {
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
cf: return_val,
|
||||
variadic: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3808,7 +3851,7 @@ fn mk_item(&self, lo: BytePos, hi: BytePos, ident: Ident,
|
||||
// parse an item-position function declaration.
|
||||
fn parse_item_fn(&self, purity: purity, abis: AbiSet) -> item_info {
|
||||
let (ident, generics) = self.parse_fn_header();
|
||||
let decl = self.parse_fn_decl();
|
||||
let decl = self.parse_fn_decl(false);
|
||||
let (inner_attrs, body) = self.parse_inner_attrs_and_block();
|
||||
(ident,
|
||||
item_fn(decl, purity, abis, generics, body),
|
||||
@ -4239,7 +4282,7 @@ fn parse_item_foreign_fn(&self, vis: ast::visibility,
|
||||
}
|
||||
|
||||
let (ident, generics) = self.parse_fn_header();
|
||||
let decl = self.parse_fn_decl();
|
||||
let decl = self.parse_fn_decl(true);
|
||||
let hi = self.span.hi;
|
||||
self.expect(&token::SEMI);
|
||||
@ast::foreign_item { ident: ident,
|
||||
|
@ -54,6 +54,7 @@ pub enum Token {
|
||||
AT,
|
||||
DOT,
|
||||
DOTDOT,
|
||||
DOTDOTDOT,
|
||||
COMMA,
|
||||
SEMI,
|
||||
COLON,
|
||||
@ -147,6 +148,7 @@ pub fn to_str(input: @ident_interner, t: &Token) -> ~str {
|
||||
AT => ~"@",
|
||||
DOT => ~".",
|
||||
DOTDOT => ~"..",
|
||||
DOTDOTDOT => ~"...",
|
||||
COMMA => ~",",
|
||||
SEMI => ~";",
|
||||
COLON => ~":",
|
||||
|
@ -1774,6 +1774,9 @@ pub fn print_fn_args_and_ret(s: @ps, decl: &ast::fn_decl,
|
||||
opt_explicit_self: Option<ast::explicit_self_>) {
|
||||
popen(s);
|
||||
print_fn_args(s, decl, opt_explicit_self);
|
||||
if decl.variadic {
|
||||
word(s.s, ", ...");
|
||||
}
|
||||
pclose(s);
|
||||
|
||||
maybe_print_comment(s, decl.output.span.lo);
|
||||
@ -2066,6 +2069,9 @@ pub fn print_ty_fn(s: @ps,
|
||||
|
||||
opt_bounds.as_ref().map(|bounds| print_bounds(s, bounds, true));
|
||||
} else {
|
||||
if decl.variadic {
|
||||
word(s.s, ", ...");
|
||||
}
|
||||
pclose(s);
|
||||
}
|
||||
|
||||
@ -2408,7 +2414,8 @@ fn test_fun_to_str() {
|
||||
output: ast::Ty {id: 0,
|
||||
node: ast::ty_nil,
|
||||
span: codemap::dummy_sp()},
|
||||
cf: ast::return_val
|
||||
cf: ast::return_val,
|
||||
variadic: false
|
||||
};
|
||||
let generics = ast_util::empty_generics();
|
||||
assert_eq!(&fun_to_str(&decl, ast::impure_fn, abba_ident,
|
||||
|
16
src/test/compile-fail/variadic-ffi-1.rs
Normal file
16
src/test/compile-fail/variadic-ffi-1.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2013 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 {
|
||||
fn printf(...); //~ ERROR: variadic function must be declared with at least one named argument
|
||||
fn printf(..., foo: int); //~ ERROR: `...` must be last in argument list for variadic function
|
||||
}
|
||||
|
||||
fn main() {}
|
16
src/test/compile-fail/variadic-ffi-2.rs
Normal file
16
src/test/compile-fail/variadic-ffi-2.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
fn baz(f: extern "stdcall" fn(uint, ...)) {
|
||||
//~^ ERROR: variadic function must have C calling convention
|
||||
f(22, 44);
|
||||
}
|
||||
|
||||
fn main() {}
|
15
src/test/compile-fail/variadic-ffi-3.rs
Normal file
15
src/test/compile-fail/variadic-ffi-3.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
fn foo(x: int, ...) {
|
||||
//~^ ERROR: only foreign functions are allowed to be variadic
|
||||
}
|
||||
|
||||
fn main() {}
|
15
src/test/compile-fail/variadic-ffi-4.rs
Normal file
15
src/test/compile-fail/variadic-ffi-4.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2013 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(x: int, ...) {
|
||||
//~^ ERROR: only foreign functions are allowed to be variadic
|
||||
}
|
||||
|
||||
fn main() {}
|
40
src/test/compile-fail/variadic-ffi.rs
Normal file
40
src/test/compile-fail/variadic-ffi.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2013 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 "stdcall" {
|
||||
fn printf(_: *u8, ...); //~ ERROR: variadic function must have C calling convention
|
||||
}
|
||||
|
||||
extern {
|
||||
fn foo(f: int, x: u8, ...);
|
||||
}
|
||||
|
||||
extern "C" fn bar(f: int, x: u8) {}
|
||||
|
||||
#[fixed_stack_segment]
|
||||
fn main() {
|
||||
unsafe {
|
||||
foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
|
||||
foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
|
||||
|
||||
let x: extern "C" unsafe fn(f: int, x: u8) = foo;
|
||||
//~^ ERROR: mismatched types: expected `extern "C" unsafe fn(int, u8)` but found `extern "C" unsafe fn(int, u8, ...)` (expected non-variadic fn but found variadic function)
|
||||
|
||||
let y: extern "C" unsafe fn(f: int, x: u8, ...) = bar;
|
||||
//~^ ERROR: mismatched types: expected `extern "C" unsafe fn(int, u8, ...)` but found `extern "C" extern fn(int, u8)` (expected variadic fn but found non-variadic function)
|
||||
|
||||
foo(1, 2, 3f32); //~ ERROR: can't pass an f32 to variadic function, cast to c_double
|
||||
foo(1, 2, true); //~ ERROR: can't pass bool to variadic function, cast to c_int
|
||||
foo(1, 2, 1i8); //~ ERROR: can't pass i8 to variadic function, cast to c_int
|
||||
foo(1, 2, 1u8); //~ ERROR: can't pass u8 to variadic function, cast to c_uint
|
||||
foo(1, 2, 1i16); //~ ERROR: can't pass i16 to variadic function, cast to c_int
|
||||
foo(1, 2, 1u16); //~ ERROR: can't pass u16 to variadic function, cast to c_uint
|
||||
}
|
||||
}
|
@ -366,8 +366,8 @@ fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool {
|
||||
if ! self.inner.visit_fn_output(retstyle, inner) { return false; }
|
||||
fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
|
||||
if ! self.inner.visit_fn_output(retstyle, variadic, inner) { return false; }
|
||||
true
|
||||
}
|
||||
|
||||
@ -603,7 +603,7 @@ fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
||||
fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, _inner: *TyDesc) -> bool {
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool {
|
||||
true
|
||||
}
|
||||
fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
|
||||
|
@ -135,7 +135,7 @@ fn visit_leave_enum(&mut self,
|
||||
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
||||
_n_inputs: uint, _retstyle: uint) -> bool { true }
|
||||
fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { true }
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, _inner: *TyDesc) -> bool { true }
|
||||
fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool { true }
|
||||
fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
|
||||
_n_inputs: uint, _retstyle: uint) -> bool { true }
|
||||
|
||||
|
68
src/test/run-pass/variadic-ffi.rs
Normal file
68
src/test/run-pass/variadic-ffi.rs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
use std::c_str::CString;
|
||||
use std::libc::{c_char, c_int};
|
||||
|
||||
extern {
|
||||
fn sprintf(s: *mut c_char, format: *c_char, ...) -> c_int;
|
||||
}
|
||||
|
||||
unsafe fn check<T>(expected: &str, f: &fn(*mut c_char) -> T) {
|
||||
let mut x = [0i8, ..50];
|
||||
f(&mut x[0] as *mut c_char);
|
||||
let res = CString::new(&x[0], false);
|
||||
assert_eq!(expected, res.as_str().unwrap());
|
||||
}
|
||||
|
||||
#[fixed_stack_segment]
|
||||
pub fn main() {
|
||||
|
||||
unsafe {
|
||||
// Call with just the named parameter
|
||||
do "Hello World\n".with_c_str |c| {
|
||||
check("Hello World\n", |s| sprintf(s, c));
|
||||
}
|
||||
|
||||
// Call with variable number of arguments
|
||||
do "%d %f %c %s\n".with_c_str |c| {
|
||||
do check("42 42.500000 a %d %f %c %s\n\n") |s| {
|
||||
sprintf(s, c, 42i, 42.5f64, 'a' as c_int, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Make a function pointer
|
||||
let x: extern "C" unsafe fn(*mut c_char, *c_char, ...) -> c_int = sprintf;
|
||||
|
||||
// A function that takes a function pointer
|
||||
unsafe fn call(p: extern "C" unsafe fn(*mut c_char, *c_char, ...) -> c_int) {
|
||||
#[fixed_stack_segment];
|
||||
|
||||
// Call with just the named parameter via fn pointer
|
||||
do "Hello World\n".with_c_str |c| {
|
||||
check("Hello World\n", |s| p(s, c));
|
||||
}
|
||||
|
||||
// Call with variable number of arguments
|
||||
do "%d %f %c %s\n".with_c_str |c| {
|
||||
do check("42 42.500000 a %d %f %c %s\n\n") |s| {
|
||||
p(s, c, 42i, 42.5f64, 'a' as c_int, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass sprintf directly
|
||||
call(sprintf);
|
||||
|
||||
// Pass sprintf indirectly
|
||||
call(x);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user