- Support int $foo... arguments. #2601

This commit is contained in:
Christoffer Lerno
2025-11-26 23:54:18 +01:00
parent 8ec3a52ef7
commit 463c6957fc
9 changed files with 120 additions and 17 deletions

View File

@@ -17,6 +17,7 @@
- Deprecate `--test-nocapture` in favour of `--test-show-output` #2588.
- Xtensa target no longer enabled by default on LLVM 22, Compile with `-DXTENSA_ENABLE` to enable it instead
- Add `float[<3>] x = { .xy = 1.2, .z = 3.3 }` swizzle initialization for vectors. #2599
- Support `int $foo...` arguments. #2601
### Fixes
- `Foo.is_eq` would return false if the type was a `typedef` and had an overload, but the underlying type was not comparable.

View File

@@ -1583,12 +1583,20 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
// We reserve upper case constants for globals.
PRINT_ERROR_HERE("Parameter names may not be all uppercase.");
return false;
case TOKEN_CT_IDENT:
// ct_var $foo
name = symstr(c);
advance_and_verify(c, TOKEN_CT_IDENT);
param_kind = VARDECL_PARAM_CT;
goto CHECK_ELLIPSIS;
break;
case TOKEN_IDENT:
// normal "foo"
name = symstr(c);
param_kind = VARDECL_PARAM;
advance_and_verify(c, TOKEN_IDENT);
// Check for "foo..." which defines an implicit "any" vararg
CHECK_ELLIPSIS:
if (try_consume(c, TOKEN_ELLIPSIS))
{
// Did we get Foo... foo...
@@ -1606,7 +1614,12 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
// Did we get Foo foo...? If so then that's an error.
if (type)
{
PRINT_ERROR_LAST("For typed varargs '...', needs to appear after the type.");
PRINT_ERROR_LAST("For typed vaargs '...', needs to appear after the type.");
return false;
}
else if (param_kind == VARDECL_PARAM_CT)
{
PRINT_ERROR_LAST("Untyped constant vaargs are not supported. Use raw macro vaargs instead.");
return false;
}
// This is "foo..."
@@ -1615,18 +1628,6 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
type = type_info_new_base(type_any, c->span);
}
break;
case TOKEN_CT_IDENT:
// ct_var $foo
name = symstr(c);
advance_and_verify(c, TOKEN_CT_IDENT);
// This will catch Type... $foo and $foo..., neither is allowed.
if (ellipsis || tok_is(c, TOKEN_ELLIPSIS))
{
PRINT_ERROR_LAST("Compile time parameters may not be varargs, use untyped macro varargs '...' instead.");
return false;
}
param_kind = VARDECL_PARAM_CT;
break;
case TOKEN_AMP:
// reference &foo
advance_and_verify(c, TOKEN_AMP);
@@ -1659,7 +1660,7 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
advance_and_verify(c, TOKEN_HASH_IDENT);
if (ellipsis || tok_is(c, TOKEN_ELLIPSIS))
{
PRINT_ERROR_LAST("Expression parameters may not be varargs, use untyped macro varargs '...' instead.");
PRINT_ERROR_LAST("Expression parameters may not be vaargs, use untyped macro vaargs '...' instead.");
return false;
}
param_kind = VARDECL_PARAM_EXPR;
@@ -1670,7 +1671,7 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
advance_and_verify(c, TOKEN_CT_TYPE_IDENT);
if (ellipsis || tok_is(c, TOKEN_ELLIPSIS))
{
PRINT_ERROR_LAST("Expression parameters may not be varargs, use untyped macro varargs '...' instead.");
PRINT_ERROR_LAST("Expression parameters may not be vaargs, use untyped macro vaargs '...' instead.");
return false;
}
param_kind = VARDECL_PARAM_CT_TYPE;

View File

@@ -1411,7 +1411,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
}
if (param->var.vararg)
{
if (var_kind != VARDECL_PARAM)
if (var_kind != VARDECL_PARAM && var_kind != VARDECL_PARAM_CT)
{
SEMA_ERROR(param, "Only regular parameters may be vararg.");
return decl_poison(param);

View File

@@ -2025,7 +2025,18 @@ INLINE bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *calle
if (type_is_arraylike(inner->type))
{
inner_new = expr_copy(inner);
expr_insert_addr(inner_new);
if (sema_cast_const(inner_new) && expr_is_const_initializer(inner_new))
{
ConstInitializer *initializer = inner_new->const_expr.initializer;
inner_new->const_expr.const_kind = CONST_SLICE;
inner_new->type = type_get_slice(inner_new->type->array.base);
inner_new->const_expr.slice_init = initializer;
initializer->type = inner_new->type;
}
else
{
expr_insert_addr(inner_new);
}
}
if (!cast_implicit_silent(context, inner_new, variadic_slot_type, false)) goto SPLAT_NORMAL;
if (inner != inner_new) expr_replace(inner, inner_new);
@@ -2218,6 +2229,33 @@ SPLAT_NORMAL:;
if (!sema_analyse_parameter(context, arg, params[i], callee->definition, optional, no_match_ref, callee->macro, callee->struct_var && i == 0)) return false;
actual_args[i] = arg;
}
if (call->call_expr.varargs && variadic != VARIADIC_RAW)
{
Decl *param = params[vaarg_index];
if (param->var.kind == VARDECL_PARAM_CT)
{
if (call->call_expr.va_is_splat)
{
if (!expr_is_runtime_const(call->call_expr.vasplat))
{
SEMA_ERROR(call->call_expr.vasplat, "The splat must be a compile time value.");
RETURN_NOTE_FUNC_DEFINITION;
}
}
else
{
FOREACH(Expr *, ct_param, call->call_expr.varargs)
{
if (!expr_is_runtime_const(ct_param))
{
SEMA_ERROR(ct_param, "All vaargs must be contant values.");
RETURN_NOTE_FUNC_DEFINITION;
}
}
}
}
}
if (num_args) last = args[num_args - 1];
call->call_expr.arguments = args;
// 17. Set default values.

View File

@@ -0,0 +1,6 @@
macro foo2(int $a, $i...) // #error: Untyped constant vaargs are not supported. Use raw macro vaargs instead
{
$foreach $ab : $i:
$echo $ab;
$endforeach
}

View File

@@ -0,0 +1,4 @@
macro @foo3(int $a, int #i...) // #error: Expression parameters may not be vaargs, use untyped macro vaargs '...' instead
{
}

View File

@@ -1,3 +1,4 @@
// #target: macos-x64
fn void test1()
{
int x;

View File

@@ -0,0 +1,35 @@
// #target: macos-x64
module test;
macro foo(int $a, int... $i)
{
$foreach $ab : $i:
{
int x = $ab;
}
$endforeach
}
fn int main()
{
foo(3, 1, 2);
int[3] $x = { 1, 2, 3 };
foo(3, ...$x);
return 0;
}
/* #expect: test.ll
define i32 @main() #0 {
entry:
%x = alloca i32, align 4
%x1 = alloca i32, align 4
%x2 = alloca i32, align 4
%x3 = alloca i32, align 4
%x4 = alloca i32, align 4
store i32 1, ptr %x, align 4
store i32 2, ptr %x1, align 4
store i32 1, ptr %x2, align 4
store i32 2, ptr %x3, align 4
store i32 3, ptr %x4, align 4
ret i32 0
}

View File

@@ -0,0 +1,17 @@
module test;
macro foo(int $a, int... $i)
{
$foreach $ab : $i:
$echo $ab;
$endforeach
}
fn int main()
{
int a;
foo(3, 1, 2, a); // #error: All vaargs must be contant values
int[3] x = { 1, 2, 3 };
foo(3, ...x); // #error: The splat must be a compile time value
return 0;
}