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