harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 700c8a23413f6912baf026c09582a043eaf19c2d
parent 33055d8f156b925960da869b286a703105859d06
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 20 Mar 2022 10:14:41 +0100

Implement improved C-style variadic arguments

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Minclude/ast.h | 5+++++
Minclude/expr.h | 8++++++++
Minclude/lex.h | 4++++
Minclude/qbe.h | 4++++
Minclude/types.h | 4+++-
Msrc/check.c | 96++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/emit.c | 14++++++++++++--
Msrc/eval.c | 8++++++++
Msrc/gen.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lex.c | 5+++++
Msrc/parse.c | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/qbe.c | 4++++
Msrc/qinstr.c | 2++
Msrc/qtype.c | 4++++
Msrc/type_store.c | 3+++
Msrc/typedef.c | 4++++
Msrc/types.c | 14++++++++++++++
Mtests/09-funcs.ha | 20++++++++++++++++++++
18 files changed, 272 insertions(+), 16 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -319,6 +319,10 @@ struct ast_expression_unarithm { struct ast_expression *operand; }; +struct ast_expression_vaarg { + struct ast_expression *ap; +}; + struct ast_expression { struct location loc; enum expr_type type; @@ -349,6 +353,7 @@ struct ast_expression { struct ast_expression_switch _switch; struct ast_expression_tuple tuple; struct ast_expression_unarithm unarithm; + struct ast_expression_vaarg vaarg; }; }; diff --git a/include/expr.h b/include/expr.h @@ -37,6 +37,9 @@ enum expr_type { EXPR_SWITCH, EXPR_TUPLE, EXPR_UNARITHM, + EXPR_VAARG, + EXPR_VAEND, + EXPR_VASTART, EXPR_YIELD, }; @@ -332,6 +335,10 @@ struct expression_unarithm { struct expression *operand; }; +struct expression_vaarg { + struct expression *ap; +}; + struct expression { const struct type *result; enum expr_type type; @@ -364,6 +371,7 @@ struct expression { struct expression_slice slice; struct expression_tuple tuple; struct expression_unarithm unarithm; + struct expression_vaarg vaarg; void *user; }; }; diff --git a/include/lex.h b/include/lex.h @@ -68,6 +68,10 @@ enum lexical_token { T_UINTPTR, T_UNION, T_USE, + T_VAARG, + T_VAEND, + T_VALIST, + T_VASTART, T_VOID, T_YIELD, T_LAST_KEYWORD = T_YIELD, diff --git a/include/qbe.h b/include/qbe.h @@ -50,6 +50,7 @@ enum qbe_value_kind { QV_GLOBAL, QV_LABEL, QV_TEMPORARY, + QV_VARIADIC, }; struct qbe_value { @@ -154,6 +155,8 @@ enum qbe_instr { Q_ULTOF, Q_UREM, Q_UWTOF, + Q_VAARG, + Q_VASTART, Q_XOR, Q_LAST_INSTR, @@ -199,6 +202,7 @@ struct qbe_statements { struct qbe_func { const struct qbe_type *returns; struct qbe_func_param *params; + bool variadic; struct qbe_statements prelude, body; }; diff --git a/include/types.h b/include/types.h @@ -38,6 +38,7 @@ enum type_storage { STORAGE_TAGGED, STORAGE_TUPLE, STORAGE_UNION, + STORAGE_VALIST, STORAGE_FCONST, STORAGE_ICONST, STORAGE_RCONST, @@ -247,6 +248,7 @@ extern struct type // etc builtin_type_ptr_const_char, builtin_type_str, - builtin_type_const_str; + builtin_type_const_str, + builtin_type_valist; #endif diff --git a/src/check.c b/src/check.c @@ -787,6 +787,7 @@ type_promote(struct type_store *store, case STORAGE_TUPLE: case STORAGE_UINTPTR: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: return NULL; // Handled above @@ -1129,11 +1130,12 @@ check_expr_call(struct context *ctx, struct call_argument *arg, **next = &expr->call.args; struct ast_call_argument *aarg = aexpr->call.args; struct type_func_param *param = fntype->func.params; - while (param && aarg) { + while ((param || fntype->func.variadism == VARIADISM_C) && aarg) { arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = xcalloc(1, sizeof(struct expression)); - if (!param->next && fntype->func.variadism == VARIADISM_HARE + if (param && !param->next + && fntype->func.variadism == VARIADISM_HARE && !aarg->variadic) { lower_vaargs(ctx, aarg, arg->value, param->type->array.members); @@ -1143,18 +1145,26 @@ check_expr_call(struct context *ctx, break; } - check_expression(ctx, aarg->value, arg->value, param->type); + const struct type *ptype = NULL; + if (param) { + ptype = param->type; + } + check_expression(ctx, aarg->value, arg->value, ptype); - if (!type_is_assignable(param->type, arg->value->result)) { - error(ctx, aarg->value->loc, expr, - "Argument type %s is not assignable to parameter type %s", gen_typename(arg->value->result), gen_typename(param->type)); - return; + if (param) { + if (!type_is_assignable(ptype, arg->value->result)) { + error(ctx, aarg->value->loc, expr, + "Argument type %s is not assignable to parameter type %s", gen_typename(arg->value->result), gen_typename(param->type)); + return; + } + arg->value = lower_implicit_cast(ptype, arg->value); } - arg->value = lower_implicit_cast(param->type, arg->value); aarg = aarg->next; - param = param->next; next = &arg->next; + if (param) { + param = param->next; + } } if (param && fntype->func.variadism == VARIADISM_HARE) { @@ -1464,6 +1474,7 @@ check_expr_constant(struct context *ctx, case STORAGE_TUPLE: case STORAGE_STRUCT: case STORAGE_UNION: + case STORAGE_VALIST: assert(0); // Invariant } } @@ -2680,6 +2691,60 @@ check_expr_unarithm(struct context *ctx, } } +static void +check_expr_vastart(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr, + const struct type *hint) +{ + if (ctx->fntype->func.variadism != VARIADISM_C) { + error(ctx, aexpr->loc, expr, + "Cannot use vastart within function which does not use C-style variadism"); + return; + } + expr->type = EXPR_VASTART; + expr->result = &builtin_type_valist; +} + +static void +check_expr_vaarg(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr, + const struct type *hint) +{ + expr->type = EXPR_VAARG; + if (hint == NULL) { + error(ctx, aexpr->loc, expr, + "Cannot infer type of vaarg without hint"); + return; + } + expr->vaarg.ap = xcalloc(1, sizeof(struct expression)); + check_expression(ctx, aexpr->vaarg.ap, expr->vaarg.ap, &builtin_type_valist); + if (type_dealias(expr->vaarg.ap->result)->storage != STORAGE_VALIST) { + error(ctx, aexpr->loc, expr, + "Expected vaarg operand to be valist"); + return; + } + expr->result = hint; +} + +static void +check_expr_vaend(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr, + const struct type *hint) +{ + expr->type = EXPR_VAEND; + expr->vaarg.ap = xcalloc(1, sizeof(struct expression)); + check_expression(ctx, aexpr->vaarg.ap, expr->vaarg.ap, &builtin_type_valist); + if (type_dealias(expr->vaarg.ap->result)->storage != STORAGE_VALIST) { + error(ctx, aexpr->loc, expr, + "Expected vaend operand to be valist"); + return; + } + expr->result = &builtin_type_void; +} + void check_expression(struct context *ctx, const struct ast_expression *aexpr, @@ -2772,6 +2837,15 @@ check_expression(struct context *ctx, case EXPR_UNARITHM: check_expr_unarithm(ctx, aexpr, expr, hint); break; + case EXPR_VAARG: + check_expr_vaarg(ctx, aexpr, expr, hint); + break; + case EXPR_VAEND: + check_expr_vaend(ctx, aexpr, expr, hint); + break; + case EXPR_VASTART: + check_expr_vastart(ctx, aexpr, expr, hint); + break; } assert(expr->result); const_refer(expr->result, &expr->result); @@ -2845,10 +2919,6 @@ check_function(struct context *ctx, return decl; // Prototype } - expect(&adecl->loc, - fntype->func.variadism != VARIADISM_C, - "C-style variadism is not allowed for function declarations"); - decl->func.scope = scope_push(&ctx->scope, SCOPE_FUNC); struct ast_function_parameters *params = afndecl->prototype.params; while (params) { diff --git a/src/emit.c b/src/emit.c @@ -126,6 +126,9 @@ emit_value(struct qbe_value *val, FILE *out) case QV_TEMPORARY: fprintf(out, "%%%s", val->name); break; + case QV_VARIADIC: + fprintf(out, "..."); + break; } } @@ -143,8 +146,10 @@ emit_call(struct qbe_statement *stmt, FILE *out) bool comma = false; while (arg) { fprintf(out, "%s", comma ? ", " : ""); - emit_qtype(arg->value.type, true, out); - fprintf(out, " "); + if (arg->value.kind != QV_VARIADIC) { + emit_qtype(arg->value.type, true, out); + fprintf(out, " "); + } emit_value(&arg->value, out); arg = arg->next; comma = true; @@ -215,6 +220,9 @@ emit_func(struct qbe_def *def, FILE *out) } param = param->next; } + if (def->func.variadic) { + fprintf(out, ", ..."); + } fprintf(out, ") {\n"); for (size_t i = 0; i < def->func.prelude.ln; ++i) { @@ -274,6 +282,8 @@ is_zeroes(struct qbe_data_item *data) case QV_LABEL: case QV_TEMPORARY: return false; + case QV_VARIADIC: + abort(); } break; case QD_STRING: diff --git a/src/eval.c b/src/eval.c @@ -91,6 +91,7 @@ itrunc(const struct type *type, uintmax_t val) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: assert(0); } @@ -355,6 +356,8 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) case STORAGE_VOID: out->constant = in->constant; break; + case STORAGE_VALIST: + abort(); // Invariant } return EVAL_OK; } @@ -487,6 +490,7 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out) case STORAGE_STRUCT: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: assert(0); // Invariant case STORAGE_VOID: break; // no-op @@ -576,6 +580,7 @@ constant_default(struct context *ctx, struct expression *v) assert(0); // TODO case STORAGE_ALIAS: case STORAGE_FUNCTION: + case STORAGE_VALIST: assert(0); // Invariant case STORAGE_VOID: break; // no-op @@ -808,6 +813,9 @@ eval_expr(struct context *ctx, struct expression *in, struct expression *out) case EXPR_PROPAGATE: case EXPR_RETURN: case EXPR_SWITCH: + case EXPR_VAARG: + case EXPR_VAEND: + case EXPR_VASTART: case EXPR_YIELD: // Excluded from translation-compatible subset return EVAL_INVALID; diff --git a/src/gen.c b/src/gen.c @@ -144,6 +144,7 @@ gen_store(struct gen_context *ctx, case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: + case STORAGE_VALIST: gen_copy_aligned(ctx, object, value); return; case STORAGE_UNION: @@ -176,6 +177,7 @@ gen_load(struct gen_context *ctx, struct gen_value object) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: return object; case STORAGE_ENUM: object.type = builtin_type_for_storage(ty->_enum.storage, @@ -1074,6 +1076,8 @@ gen_expr_call(struct gen_context *ctx, const struct expression *expr) call.out->type = qtype_lookup(ctx, rtype->func.result, false); } + bool cvar = false; + struct type_func_param *param = rtype->func.params; struct qbe_arguments *args, **next = &call.args; args = *next = xcalloc(1, sizeof(struct qbe_arguments)); args->value = mkqval(ctx, &lvalue); @@ -1085,6 +1089,15 @@ gen_expr_call(struct gen_context *ctx, const struct expression *expr) args->value = mkqval(ctx, &arg); args->value.type = qtype_lookup(ctx, carg->value->result, false); next = &args->next; + if (param) { + param = param->next; + } + if (!param && !cvar && rtype->func.variadism == VARIADISM_C) { + cvar = true; + args = *next = xcalloc(1, sizeof(struct qbe_arguments)); + args->value.kind = QV_VARIADIC; + next = &args->next; + } } push(&ctx->current->body, &call); @@ -1583,6 +1596,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: abort(); // Invariant } @@ -2775,6 +2789,28 @@ gen_expr_unarithm(struct gen_context *ctx, } static struct gen_value +gen_expr_vaarg(struct gen_context *ctx, + const struct expression *expr) +{ + // XXX: qbe only supports variadic base types, should check for this + struct gen_value result = mktemp(ctx, expr->result, ".%d"); + struct qbe_value qresult = mkqval(ctx, &result); + struct gen_value ap = gen_expr(ctx, expr->vaarg.ap); + struct qbe_value qap = mkqval(ctx, &ap); + pushi(ctx->current, &qresult, Q_VAARG, &qap, NULL); + return result; +} + +static void +gen_expr_vastart_at(struct gen_context *ctx, + const struct expression *expr, + struct gen_value out) +{ + struct qbe_value base = mklval(ctx, &out); + pushi(ctx->current, NULL, Q_VASTART, &base, NULL); +} + +static struct gen_value gen_expr(struct gen_context *ctx, const struct expression *expr) { switch ((int)expr->type) { @@ -2828,9 +2864,14 @@ gen_expr(struct gen_context *ctx, const struct expression *expr) return gen_expr_switch_with(ctx, expr, NULL); case EXPR_UNARITHM: return gen_expr_unarithm(ctx, expr); + case EXPR_VAARG: + return gen_expr_vaarg(ctx, expr); + case EXPR_VAEND: + return gv_void; // no-op case EXPR_SLICE: case EXPR_STRUCT: case EXPR_TUPLE: + case EXPR_VASTART: break; // Prefers -at style // gen-specific psuedo-expressions case EXPR_GEN_VALUE: @@ -2884,6 +2925,9 @@ gen_expr_at(struct gen_context *ctx, case EXPR_TUPLE: gen_expr_tuple_at(ctx, expr, out); return; + case EXPR_VASTART: + gen_expr_vastart_at(ctx, expr, out); + return; default: break; // Prefers non-at style } @@ -2944,6 +2988,9 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl) } else { qdef->func.returns = &qbe_void; } + if (fntype->func.variadism == VARIADISM_C) { + qdef->func.variadic = true; + } struct qbe_func_param *param, **next = &qdef->func.params; for (struct scope_object *obj = decl->func.scope->objects; @@ -3372,6 +3419,7 @@ gen_data_item(struct gen_context *ctx, struct expression *expr, case STORAGE_ICONST: case STORAGE_RCONST: case STORAGE_NULL: + case STORAGE_VALIST: case STORAGE_VOID: assert(0); // Invariant } diff --git a/src/lex.c b/src/lex.c @@ -76,6 +76,10 @@ static const char *tokens[] = { [T_UINTPTR] = "uintptr", [T_UNION] = "union", [T_USE] = "use", + [T_VAARG] = "vaarg", + [T_VAEND] = "vaend", + [T_VALIST] = "valist", + [T_VASTART] = "vastart", [T_VOID] = "void", [T_YIELD] = "yield", @@ -1144,6 +1148,7 @@ token_str(const struct token *tok) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: assert(0); } diff --git a/src/parse.c b/src/parse.c @@ -396,6 +396,9 @@ parse_primitive_type(struct lexer *lexer) case T_VOID: type->storage = STORAGE_VOID; break; + case T_VALIST: + type->storage = STORAGE_VALIST; + break; default: assert(0); } @@ -656,6 +659,7 @@ parse_type(struct lexer *lexer) case T_U8: case T_UINT: case T_UINTPTR: + case T_VALIST: case T_VOID: unlex(lexer, &tok); type = parse_primitive_type(lexer); @@ -842,6 +846,7 @@ parse_constant(struct lexer *lexer) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: assert(0); // Handled in a different nonterminal } return exp; @@ -1461,6 +1466,37 @@ parse_object_selector(struct lexer *lexer) } static struct ast_expression * +parse_va_expression(struct lexer *lexer) +{ + struct ast_expression *expr; + struct token tok; + switch (lex(lexer, &tok)) { + case T_VASTART: + expr = mkexpr(&lexer->loc); + expr->type = EXPR_VASTART; + want(lexer, T_LPAREN, NULL); + want(lexer, T_RPAREN, NULL); + return expr; + case T_VAARG: + expr = mkexpr(&lexer->loc); + expr->type = EXPR_VAARG; + want(lexer, T_LPAREN, NULL); + expr->vaarg.ap = parse_object_selector(lexer); + want(lexer, T_RPAREN, NULL); + return expr; + case T_VAEND: + expr = mkexpr(&lexer->loc); + expr->type = EXPR_VAEND; + want(lexer, T_LPAREN, NULL); + expr->vaarg.ap = parse_object_selector(lexer); + want(lexer, T_RPAREN, NULL); + return expr; + default: + assert(0); + } +} + +static struct ast_expression * parse_builtin_expression(struct lexer *lexer) { struct token tok; @@ -1499,6 +1535,11 @@ parse_builtin_expression(struct lexer *lexer) case T_OFFSET: unlex(lexer, &tok); return parse_measurement_expression(lexer); + case T_VAARG: + case T_VAEND: + case T_VASTART: + unlex(lexer, &tok); + return parse_va_expression(lexer); default: unlex(lexer, &tok); break; diff --git a/src/qbe.c b/src/qbe.c @@ -39,6 +39,8 @@ qbe_aggregate = { .stype = Q__AGGREGATE, }; +const struct qbe_value variadic_sigil = {0}; + const char *qbe_instr[Q_LAST_INSTR] = { [Q_ADD] = "add", [Q_ALLOC16] = "alloc16", @@ -129,6 +131,8 @@ const char *qbe_instr[Q_LAST_INSTR] = { [Q_ULTOF] = "ultof", [Q_UREM] = "urem", [Q_UWTOF] = "uwtof", + [Q_VAARG] = "vaarg", + [Q_VASTART] = "vastart", [Q_XOR] = "xor", }; diff --git a/src/qinstr.c b/src/qinstr.c @@ -81,6 +81,7 @@ store_for_type(struct gen_context *ctx, const struct type *type) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: abort(); // Invariant } @@ -149,6 +150,7 @@ load_for_type(struct gen_context *ctx, const struct type *type) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: case STORAGE_VOID: abort(); // Invariant } diff --git a/src/qtype.c b/src/qtype.c @@ -180,6 +180,7 @@ aggregate_lookup(struct gen_context *ctx, const struct type *type) case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: + case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_FUNCTION: abort(); // Invariant @@ -237,6 +238,8 @@ qtype_lookup(struct gen_context *ctx, return aggregate_lookup(ctx, type); case STORAGE_FUNCTION: return ctx->arch.ptr; + case STORAGE_VALIST: + return ctx->arch.ptr; case STORAGE_VOID: abort(); // Invariant case STORAGE_FCONST: @@ -285,6 +288,7 @@ type_is_aggregate(const struct type *type) case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: + case STORAGE_VALIST: return true; case STORAGE_FCONST: case STORAGE_ICONST: diff --git a/src/type_store.c b/src/type_store.c @@ -105,6 +105,8 @@ builtin_type_for_storage(enum type_storage storage, bool is_const) return is_const ? &builtin_type_const_uint : &builtin_type_uint; case STORAGE_UINTPTR: return is_const ? &builtin_type_const_uintptr : &builtin_type_uintptr; + case STORAGE_VALIST: + return &builtin_type_valist; case STORAGE_VOID: return is_const ? &builtin_type_const_void : &builtin_type_void; case STORAGE_NULL: @@ -629,6 +631,7 @@ type_init_from_atype(struct type_store *store, case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: + case STORAGE_VALIST: case STORAGE_VOID: builtin = builtin_type_for_storage(type->storage, false); type->size = builtin->size; diff --git a/src/typedef.c b/src/typedef.c @@ -48,6 +48,8 @@ storage_to_suffix(enum type_storage storage) return "u"; case STORAGE_UINTPTR: return "u64: uintptr"; + case STORAGE_VALIST: + return "valist"; default: assert(0); } @@ -144,6 +146,7 @@ emit_const(const struct expression *expr, FILE *out) case STORAGE_FUNCTION: case STORAGE_POINTER: case STORAGE_TAGGED: + case STORAGE_VALIST: assert(0); // Invariant } } @@ -226,6 +229,7 @@ emit_type(const struct type *type, FILE *out) case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: + case STORAGE_VALIST: case STORAGE_VOID: fprintf(out, "%s", type_storage_unparse(type->storage)); break; diff --git a/src/types.c b/src/types.c @@ -168,6 +168,8 @@ type_storage_unparse(enum type_storage storage) return "uintptr"; case STORAGE_UNION: return "union"; + case STORAGE_VALIST: + return "valist"; case STORAGE_VOID: return "void"; } @@ -195,6 +197,7 @@ type_is_integer(const struct type *type) case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: + case STORAGE_VALIST: return false; case STORAGE_CHAR: case STORAGE_ENUM: @@ -237,6 +240,7 @@ type_is_numeric(const struct type *type) case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_NULL: + case STORAGE_VALIST: return false; case STORAGE_ENUM: case STORAGE_I8: @@ -301,6 +305,7 @@ type_is_signed(const struct type *type) case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: + case STORAGE_VALIST: return false; case STORAGE_I8: case STORAGE_I16: @@ -353,6 +358,7 @@ type_hash(const struct type *type) case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: + case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_STRING: break; // built-ins @@ -851,6 +857,7 @@ type_is_assignable(const struct type *to, const struct type *from) case STORAGE_TUPLE: case STORAGE_UINTPTR: case STORAGE_UNION: + case STORAGE_VALIST: return false; } @@ -957,6 +964,7 @@ type_is_castable(const struct type *to, const struct type *from) case STORAGE_TUPLE: case STORAGE_STRUCT: case STORAGE_UNION: + case STORAGE_VALIST: return false; case STORAGE_TAGGED: case STORAGE_ALIAS: @@ -987,6 +995,7 @@ builtin_types_init() &builtin_type_const_rune, &builtin_type_const_size, &builtin_type_const_void, &builtin_type_ptr_const_char, &builtin_type_str, &builtin_type_const_str, + &builtin_type_valist, }; for (size_t i = 0; i < sizeof(builtins) / sizeof(builtins[0]); ++i) { builtins[i]->id = type_hash(builtins[i]); @@ -1217,4 +1226,9 @@ builtin_type_const_str = { .flags = TYPE_CONST, .size = 24, // XXX: ARCH .align = 8, +}, +builtin_type_valist = { + .storage = STORAGE_VALIST, + .size = 32, // XXX: ARCH + .align = 8, }; diff --git a/tests/09-funcs.ha b/tests/09-funcs.ha @@ -50,9 +50,29 @@ let x: int = 42; void; // Should be allowed to have the same name }; +fn cvafn(n: int, ...) void = { + let ap = vastart(); + defer vaend(ap); + let ap2 = ap; + defer vaend(ap2); + for (let i = 0; i < n; i += 1) { + let arg: int = vaarg(ap); + assert(arg == i + 1); + }; + for (let i = 0; i < n; i += 1) { + let arg: int = vaarg(ap2); + assert(arg == i + 1); + }; +}; + +fn cvaargs() void = { + cvafn(3, 1, 2, 3); +}; + export fn main() void = { assert(simple() == 69); pointers(); vaargs(); + cvaargs(); assert(x == 1337); };