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:
bors 2013-11-04 21:02:07 -08:00
commit 1c56652640
34 changed files with 527 additions and 103 deletions

View File

@ -29,6 +29,7 @@
// Consts for the LLVM CallConv type, pre-cast to uint.
#[deriving(Eq)]
pub enum CallConv {
CCallConv = 0,
FastCallConv = 8,

View File

@ -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

View File

@ -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);
}

View File

@ -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,

View File

@ -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 {

View File

@ -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);
}

View File

@ -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)
}

View File

@ -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" })
}
}
}

View File

@ -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
}
}
}

View File

@ -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();

View File

@ -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,

View File

@ -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
})
}
}
}

View File

@ -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
}
})
}

View File

@ -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
}
});

View File

@ -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) {

View File

@ -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) {

View File

@ -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 }

View File

@ -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;

View File

@ -892,6 +892,7 @@ pub struct fn_decl {
inputs: ~[arg],
output: Ty,
cf: ret_style,
variadic: bool
}
#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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; }

View File

@ -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(),

View File

@ -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,

View File

@ -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 => ~":",

View File

@ -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,

View 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() {}

View 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() {}

View 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() {}

View 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() {}

View 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
}
}

View File

@ -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,

View File

@ -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 }

View 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);
}
}