From c669ccf3d30da3eb505832d0872bf03607eb98eb Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Fri, 25 Oct 2013 01:56:34 -0400 Subject: [PATCH 1/2] libsyntax/librustc: Allow calling variadic foreign functions. --- src/librustc/lib/llvm.rs | 1 + src/librustc/metadata/tydecode.rs | 13 +- src/librustc/metadata/tyencode.rs | 3 + src/librustc/middle/trans/callee.rs | 16 ++- src/librustc/middle/trans/foreign.rs | 48 ++++++-- src/librustc/middle/trans/reflect.rs | 1 + src/librustc/middle/trans/type_.rs | 6 + src/librustc/middle/ty.rs | 29 ++++- src/librustc/middle/typeck/astconv.rs | 21 +++- src/librustc/middle/typeck/check/mod.rs | 124 ++++++++++++++------ src/librustc/middle/typeck/collect.rs | 15 ++- src/librustc/middle/typeck/infer/combine.rs | 16 ++- src/librustc/middle/typeck/infer/test.rs | 7 +- src/librustc/middle/typeck/mod.rs | 6 +- src/librustc/util/ppaux.rs | 4 + src/libstd/reflect.rs | 7 ++ src/libstd/repr.rs | 15 +++ src/libstd/unstable/intrinsics.rs | 3 + src/libsyntax/ast.rs | 1 + src/libsyntax/ext/build.rs | 1 + src/libsyntax/fold.rs | 2 + src/libsyntax/parse/lexer.rs | 18 ++- src/libsyntax/parse/mod.rs | 3 +- src/libsyntax/parse/parser.rs | 81 ++++++++++--- src/libsyntax/parse/token.rs | 2 + src/libsyntax/print/pprust.rs | 9 +- src/test/run-pass/reflect-visit-data.rs | 6 +- src/test/run-pass/reflect-visit-type.rs | 2 +- 28 files changed, 357 insertions(+), 103 deletions(-) diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index e152d009050c8..043b3dcc7aa57 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -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, diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 62de991ce9631..b365e7a48795e 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -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 diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 7fb33c881156b..5397bf0e768d8 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -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); } diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 7a3a67b35ee1e..ecb53efbad788 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -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, diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 7f8f1daebc48a..ef843b56f4bf0 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -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 { diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 7e875243fd083..c56d88355495f 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -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); } diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index de41548895df2..9e95a9a6449ac 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -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) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c3c0c5ef20ff6..373395e91cc05 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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, 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), terr_traits(expected_found), terr_builtin_bounds(expected_found), + terr_variadic_mismatch(expected_found) } #[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 @@ 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" }) + } } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index dd0c6c12a69aa..45a6d709b04fb 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -396,6 +396,9 @@ pub fn ast_ty_to_ty( 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( 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( @@ -770,9 +776,12 @@ pub fn ty_of_closure( 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 + } } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 011d39af7a8c8..16f1052d90187 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, } } - 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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 @@ pub fn check_expr_with_unifier(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 @@ pub fn check_expr_with_unifier(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 @@ pub fn check_expr_with_unifier(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 { @@ -3897,9 +3952,12 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { 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(); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 9c69e6fd85cc7..353639051097a 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -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, diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 2d810f1da746f..5d0f44ae7e356 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -414,8 +414,7 @@ pub fn eq_opt_regions( } } -pub fn super_fn_sigs( - this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres { +pub fn super_fn_sigs(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres { fn argvecs(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty::t]> { if a_args.len() == b_args.len() { @@ -426,12 +425,19 @@ pub fn super_fn_sigs( } } + 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 + }) } } } diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index 48660ab64e591..d7a00ebf0e940 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -190,8 +190,11 @@ impl Env { onceness: ast::Many, region: ty::re_static, bounds: @~[]}, - sig: FnSig {inputs: inputs, - output: output_ty} + sig: FnSig { + inputs: inputs, + output: output_ty, + variadic: false + } }) } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 10005bfb2cb68..75c7adfb03e40 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -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 } }); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index ab1ca420afab1..006e14232f368 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -410,7 +410,11 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { 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) { diff --git a/src/libstd/reflect.rs b/src/libstd/reflect.rs index 19fa9abc0da55..c841750d7ce7a 100644 --- a/src/libstd/reflect.rs +++ b/src/libstd/reflect.rs @@ -382,11 +382,18 @@ impl TyVisitor for MovePtrAdaptor { 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) { diff --git a/src/libstd/repr.rs b/src/libstd/repr.rs index dd68c57e37e97..97b42a1ebc1ca 100644 --- a/src/libstd/repr.rs +++ b/src/libstd/repr.rs @@ -572,6 +572,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> { 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 @@ impl<'self> TyVisitor for ReprVisitor<'self> { 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 } diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs index c086e13d74024..5189fdce115d1 100644 --- a/src/libstd/unstable/intrinsics.rs +++ b/src/libstd/unstable/intrinsics.rs @@ -160,7 +160,10 @@ pub trait TyVisitor { 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; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cf72455a83a06..6aec7380f4015 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -892,6 +892,7 @@ pub struct fn_decl { inputs: ~[arg], output: Ty, cf: ret_style, + variadic: bool } #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 76d9f755d3c9f..0a5e20fc2b29a 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -728,6 +728,7 @@ impl AstBuilder for @ExtCtxt { inputs: inputs, output: output, cf: ast::return_val, + variadic: false } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d579e7dd2c7e4..388de29b45680 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -99,6 +99,7 @@ pub trait ast_fold { 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(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 } } diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 7ac999c46a400..49445312a12f6 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -664,12 +664,18 @@ fn next_token_inner(rdr: @mut StringReader) -> 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; } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index fbe711b5efe9e..6c81784b5dee4 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -701,7 +701,8 @@ mod test { 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(), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 56254704e28d5..7c98d8d1c85b3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -863,7 +863,7 @@ impl Parser { 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 @@ impl Parser { // 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 @@ impl Parser { // 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 @@ impl Parser { inputs: inputs, output: output, cf: return_style, + variadic: false }; (BorrowedSigil, decl, lifetimes, bounds) @@ -994,7 +995,7 @@ impl Parser { } // parse a function type (following the 'fn') - pub fn parse_ty_fn_decl(&self) -> (fn_decl, OptVec) { + pub fn parse_ty_fn_decl(&self, allow_variadic: bool) -> (fn_decl, OptVec) { /* (fn) <'lt> (S) -> T @@ -1013,17 +1014,13 @@ impl Parser { 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 @@ impl Parser { node: ty_infer, span: *self.span }, - cf: return_val + cf: return_val, + variadic: false } } } @@ -3526,21 +3524,63 @@ impl Parser { (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] = 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 @@ impl Parser { 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 @@ impl Parser { inputs: inputs_captures, output: output, cf: return_val, + variadic: false } } @@ -3784,6 +3826,7 @@ impl Parser { inputs: inputs, output: output, cf: return_val, + variadic: false } } @@ -3808,7 +3851,7 @@ impl Parser { // 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 @@ impl Parser { } 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, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 3d8fa1b672811..63f4f97889c6d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -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 => ~":", diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 0eb1045efe95f..08c2ae88e4fab 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1774,6 +1774,9 @@ pub fn print_fn_args_and_ret(s: @ps, decl: &ast::fn_decl, opt_explicit_self: Option) { 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 @@ mod test { 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, diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs index de8d9470f1027..42614f7653928 100644 --- a/src/test/run-pass/reflect-visit-data.rs +++ b/src/test/run-pass/reflect-visit-data.rs @@ -366,8 +366,8 @@ impl TyVisitor for ptr_visit_adaptor { 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 @@ impl TyVisitor for my_visitor { 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, diff --git a/src/test/run-pass/reflect-visit-type.rs b/src/test/run-pass/reflect-visit-type.rs index e77cb432c3ad0..03722cd0fbf2c 100644 --- a/src/test/run-pass/reflect-visit-type.rs +++ b/src/test/run-pass/reflect-visit-type.rs @@ -135,7 +135,7 @@ impl TyVisitor for MyVisitor { 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 } From 77e0235983ba3ad16f99123604dbbd1401ba8d6c Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Mon, 4 Nov 2013 16:34:07 -0500 Subject: [PATCH 2/2] Add tests for variadic foreign functions. --- src/test/compile-fail/variadic-ffi-1.rs | 16 ++++++ src/test/compile-fail/variadic-ffi-2.rs | 16 ++++++ src/test/compile-fail/variadic-ffi-3.rs | 15 ++++++ src/test/compile-fail/variadic-ffi-4.rs | 15 ++++++ src/test/compile-fail/variadic-ffi.rs | 40 +++++++++++++++ src/test/run-pass/variadic-ffi.rs | 68 +++++++++++++++++++++++++ 6 files changed, 170 insertions(+) create mode 100644 src/test/compile-fail/variadic-ffi-1.rs create mode 100644 src/test/compile-fail/variadic-ffi-2.rs create mode 100644 src/test/compile-fail/variadic-ffi-3.rs create mode 100644 src/test/compile-fail/variadic-ffi-4.rs create mode 100644 src/test/compile-fail/variadic-ffi.rs create mode 100644 src/test/run-pass/variadic-ffi.rs diff --git a/src/test/compile-fail/variadic-ffi-1.rs b/src/test/compile-fail/variadic-ffi-1.rs new file mode 100644 index 0000000000000..93f702d8052de --- /dev/null +++ b/src/test/compile-fail/variadic-ffi-1.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/variadic-ffi-2.rs b/src/test/compile-fail/variadic-ffi-2.rs new file mode 100644 index 0000000000000..9e2b015f33d96 --- /dev/null +++ b/src/test/compile-fail/variadic-ffi-2.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/variadic-ffi-3.rs b/src/test/compile-fail/variadic-ffi-3.rs new file mode 100644 index 0000000000000..f143e715450ff --- /dev/null +++ b/src/test/compile-fail/variadic-ffi-3.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/variadic-ffi-4.rs b/src/test/compile-fail/variadic-ffi-4.rs new file mode 100644 index 0000000000000..d4c54dfffe0a3 --- /dev/null +++ b/src/test/compile-fail/variadic-ffi-4.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/variadic-ffi.rs b/src/test/compile-fail/variadic-ffi.rs new file mode 100644 index 0000000000000..4133f008cbfba --- /dev/null +++ b/src/test/compile-fail/variadic-ffi.rs @@ -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 or the MIT license +// , 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 + } +} diff --git a/src/test/run-pass/variadic-ffi.rs b/src/test/run-pass/variadic-ffi.rs new file mode 100644 index 0000000000000..6f8867afef324 --- /dev/null +++ b/src/test/run-pass/variadic-ffi.rs @@ -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 or the MIT license +// , 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(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); + } + +}