// Consts for the LLVM CallConv type, pre-cast to uint.
+#[deriving(Eq)]
pub enum CallConv {
CCallConv = 0,
FastCallConv = 8,
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
enc_ty(w, cx, *ty);
}
mywrite!(w, "]");
+ if fsig.variadic {
+ mywrite!(w, "A");
+ }
enc_ty(w, cx, fsig.output);
}
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,
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;
// 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,
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 {}",
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,
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.
* - `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();
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,
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,
// 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;
}
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());
}
}
}
-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
* 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,
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,
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 {
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);
}
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)
}
*
* - `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)]
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)]
sig: FnSig {
bound_lifetime_names: opt_vec::Empty,
inputs: input_args,
- output: output
+ output: output,
+ variadic: false
}
})
}
FnSig {
bound_lifetime_names: sig.bound_lifetime_names.clone(),
inputs: args,
- output: fldop(sig.output)
+ output: fldop(sig.output),
+ variadic: sig.variadic
}
}
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(),
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" })
+ }
}
}
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))
}
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>(
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
+ }
}
}
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");
+ }
+ }
+ _ => {}
}
}
}
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
}
_ => {
}
}
- 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
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 => "",
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={:?}",
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(*) |
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] {
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 {
// 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);
}
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 {
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();
.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 =
.bound_lifetime_names
.clone(),
inputs: impl_fn_args,
- output: impl_m.fty.sig.output
+ output: impl_m.fty.sig.output,
+ variadic: false
}
});
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,
}
}
-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() {
}
}
+ 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
+ })
}
}
}
onceness: ast::Many,
region: ty::re_static,
bounds: @~[]},
- sig: FnSig {inputs: inputs,
- output: output_ty}
+ sig: FnSig {
+ inputs: inputs,
+ output: output_ty,
+ variadic: false
+ }
})
}
sig: ty::FnSig {
bound_lifetime_names: opt_vec::Empty,
inputs: ~[],
- output: ty::mk_nil()
+ output: ty::mk_nil(),
+ variadic: false
}
});
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
}
});
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) {
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) {
true
}
+ #[cfg(stage0)]
fn visit_fn_output(&mut self, _retstyle: uint, inner: *TyDesc) -> bool {
self.writer.write(")".as_bytes());
let name = unsafe { (*inner).name };
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 }
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;
inputs: ~[arg],
output: Ty,
cf: ret_style,
+ variadic: bool
}
#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
inputs: inputs,
output: output,
cf: ast::return_val,
+ variadic: false
}
}
self)),
output: self.fold_ty(&fdec.output),
cf: fdec.cf,
+ variadic: fdec.variadic
},
fold_generics(generics, self))
}
inputs: decl.inputs.map(|x| fold_arg_(x, fld)), // bad copy
output: fld.fold_ty(&decl.output),
cf: decl.cf,
+ variadic: decl.variadic
}
}
';' => { 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; }
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(),
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,
// 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,
// 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 => {
inputs: inputs,
output: output,
cf: return_style,
+ variadic: false
};
(BorrowedSigil, decl, lifetimes, bounds)
}
// 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
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)
}
node: ty_infer,
span: *self.span
},
- cf: return_val
+ cf: return_val,
+ variadic: false
}
}
}
(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
}
}
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)
inputs: inputs_captures,
output: output,
cf: return_val,
+ variadic: false
}
}
inputs: inputs,
output: output,
cf: return_val,
+ variadic: false
}
}
// 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),
}
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,
AT,
DOT,
DOTDOT,
+ DOTDOTDOT,
COMMA,
SEMI,
COLON,
AT => ~"@",
DOT => ~".",
DOTDOT => ~"..",
+ DOTDOTDOT => ~"...",
COMMA => ~",",
SEMI => ~";",
COLON => ~":",
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);
opt_bounds.as_ref().map(|bounds| print_bounds(s, bounds, true));
} else {
+ if decl.variadic {
+ word(s.s, ", ...");
+ }
pclose(s);
}
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,
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
}
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,
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 }