Handle "splice splat" in the vararg slot as an expression.

This commit is contained in:
Christoffer Lerno
2024-09-06 10:43:03 +02:00
parent 1e570bf506
commit 28428fcf30
11 changed files with 77 additions and 65 deletions

View File

@@ -693,7 +693,6 @@ typedef struct
ExprId macro_body;
bool is_type_method : 1;
bool is_pointer_call : 1;
bool splat_vararg : 1;
bool attr_force_inline : 1;
bool attr_force_noinline : 1;
bool is_builtin : 1;
@@ -704,11 +703,11 @@ typedef struct
bool has_optional_arg : 1;
bool must_use : 1;
bool is_optional_return : 1;
bool va_is_splat : 1;
Expr **arguments;
union
{
union {
Expr **varargs;
Expr *splat;
Expr *vasplat;
};
} ExprCall;
@@ -3217,6 +3216,9 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc)
case EXPR_DESIGNATED_INITIALIZER_LIST:
expr_list_set_span(expr->designated_init_list, loc);
return;
case EXPR_SPLAT:
expr_set_span(expr->inner_expr, loc);
return;
case EXPR_EXPRESSION_LIST:
case EXPR_ACCESS:
case EXPR_BITACCESS:

View File

@@ -447,11 +447,12 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
case EXPR_CT_AND_OR:
MACRO_COPY_EXPR_LIST(expr->ct_and_or_expr.args);
return expr;
case EXPR_FORCE_UNWRAP:
case EXPR_OPTIONAL:
case EXPR_STRINGIFY:
case EXPR_CT_EVAL:
case EXPR_CT_IS_CONST:
case EXPR_FORCE_UNWRAP:
case EXPR_OPTIONAL:
case EXPR_SPLAT:
case EXPR_STRINGIFY:
MACRO_COPY_EXPR(expr->inner_expr);
return expr;
case EXPR_DEFAULT_ARG:
@@ -516,9 +517,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_EXPR_LIST(expr->call_expr.arguments);
if (expr->call_expr.varargs)
{
if (expr->call_expr.splat_vararg)
if (expr->call_expr.va_is_splat)
{
MACRO_COPY_EXPR(expr->call_expr.splat);
MACRO_COPY_EXPR(expr->call_expr.vasplat);
}
else
{

View File

@@ -806,6 +806,7 @@ typedef enum
EXPR_SLICE,
EXPR_SLICE_ASSIGN,
EXPR_SLICE_COPY,
EXPR_SPLAT,
EXPR_STRINGIFY,
EXPR_SUBSCRIPT,
EXPR_SUBSCRIPT_ADDR,
@@ -1603,8 +1604,8 @@ typedef enum
case EXPR_CT_DEFINED: case EXPR_CT_AND_OR:\
case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: \
case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \
case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \
case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_TAGOF: \
case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \
case EXPR_SPLAT: case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_TAGOF: \
case EXPR_CT_EVAL: case EXPR_CT_CONCAT: case EXPR_CT_APPEND
static_assert(EXPR_LAST < 128, "Too many expression types");

View File

@@ -226,6 +226,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
case EXPR_POST_UNARY:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_MACRO_BLOCK:
case EXPR_RETHROW:
case EXPR_MEMBER_GET:
@@ -765,6 +766,7 @@ bool expr_is_pure(Expr *expr)
case EXPR_POST_UNARY:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_TRY_UNWRAP:
case EXPR_TRY_UNWRAP_CHAIN:
case EXPR_FORCE_UNWRAP:

View File

@@ -6091,9 +6091,9 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr
Expr *vararg_splat = NULL;
Expr **varargs = NULL;
if (expr->call_expr.splat_vararg)
if (expr->call_expr.va_is_splat)
{
vararg_splat = expr->call_expr.splat;
vararg_splat = expr->call_expr.vasplat;
}
else
{

View File

@@ -484,10 +484,10 @@ END:
*
* parameter ::= ((param_path '=')? expr) | param_path
*/
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat)
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool vasplat)
{
*result = NULL;
if (splat) *splat = false;
bool has_splat = false;
while (1)
{
Expr *expr = NULL;
@@ -528,16 +528,16 @@ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *
ASSIGN_EXPR_OR_RET(expr, parse_vasplat(c), false);
goto DONE;
}
if (splat)
if (try_consume(c, TOKEN_ELLIPSIS))
{
if (*splat)
{
PRINT_ERROR_HERE("'...' is only allowed on the last argument in a call.");
return false;
}
*splat = try_consume(c, TOKEN_ELLIPSIS);
expr = expr_new(EXPR_SPLAT, start_span);
ASSIGN_EXPR_OR_RET(expr->inner_expr, parse_expr(c), false);
RANGE_EXTEND_PREV(expr);
}
else
{
ASSIGN_EXPR_OR_RET(expr, parse_expr(c), false);
}
ASSIGN_EXPR_OR_RET(expr, parse_expr(c), false);
DONE:
vec_add(*result, expr);
if (!try_consume(c, TOKEN_COMMA))
@@ -545,9 +545,6 @@ DONE:
return true;
}
if (tok_is(c, param_end)) return true;
if (splat && *splat)
{
}
}
}
@@ -971,13 +968,12 @@ static Expr *parse_call_expr(ParseContext *c, Expr *left)
Expr **params = NULL;
advance_and_verify(c, TOKEN_LPAREN);
bool splat = false;
Decl **body_args = NULL;
if (!tok_is(c, TOKEN_RPAREN) && !tok_is(c, TOKEN_EOS))
{
// Pick a modest guess.
params = VECNEW(Expr*, 4);
if (!parse_arg_list(c, &params, TOKEN_RPAREN, &splat, true)) return poisoned_expr;
params = VECNEW(Expr*, 8);
if (!parse_arg_list(c, &params, TOKEN_RPAREN, true)) return poisoned_expr;
}
if (try_consume(c, TOKEN_EOS))
{
@@ -998,7 +994,6 @@ static Expr *parse_call_expr(ParseContext *c, Expr *left)
Expr *call = expr_new_expr(EXPR_CALL, left);
call->call_expr.function = exprid(left);
call->call_expr.arguments = params;
call->call_expr.splat_vararg = splat;
RANGE_EXTEND_PREV(call);
if (body_args && !tok_is(c, TOKEN_LBRACE))
{

View File

@@ -2213,7 +2213,7 @@ static inline Decl *parse_enum_declaration(ParseContext *c)
else
{
CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl);
if (!parse_arg_list(c, &enum_const->enum_constant.args, TOKEN_RBRACE, NULL, false)) return poisoned_decl;
if (!parse_arg_list(c, &enum_const->enum_constant.args, TOKEN_RBRACE, 0)) return poisoned_decl;
CONSUME_OR_RET(TOKEN_RBRACE, poisoned_decl);
}
}

View File

@@ -60,7 +60,7 @@ bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref);
bool parse_parameters(ParseContext *c, Decl ***params_ref, Decl **body_params,
Variadic *variadic, int *vararg_index_ref, ParameterParseKind parse_kind);
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat);
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool vasplat);
Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info);
INLINE void add_decl_to_list(Decl ***list, Decl *decl)

View File

@@ -139,7 +139,7 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Decl
static inline bool sema_call_check_invalid_body_arguments(SemaContext *context, Expr *call, CalledDecl *callee);
INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call,
Expr **args, unsigned func_param_count,
Variadic variadic, unsigned vararg_index, bool *optional,
Variadic variadic, unsigned vaarg_index, bool *optional,
Expr ***varargs_ref, Expr **vararg_splat_ref, bool *no_match_ref);
static inline bool sema_call_check_contract_param_match(SemaContext *context, Decl *param, Expr *expr);
static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call);
@@ -550,6 +550,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp
case EXPR_RETVAL:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_STRINGIFY:
case EXPR_TAGOF:
case EXPR_TERNARY:
@@ -686,6 +687,7 @@ static bool expr_may_ref(Expr *expr)
case EXPR_RETVAL:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_STRINGIFY:
case EXPR_TERNARY:
case EXPR_TEST_HOOK:
@@ -1227,7 +1229,7 @@ static inline bool sema_call_check_invalid_body_arguments(SemaContext *context,
INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args,
unsigned func_param_count, Variadic variadic, unsigned vararg_index,
unsigned func_param_count, Variadic variadic, unsigned vaarg_index,
bool *optional, Expr ***varargs_ref, Expr **vararg_splat_ref,
bool *no_match_ref)
{
@@ -1251,6 +1253,20 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
Expr *arg = args[i];
assert(expr_ok(arg));
if (arg->expr_kind == EXPR_SPLAT)
{
if (variadic == VARIADIC_NONE)
{
RETURN_SEMA_ERROR(arg, "Splat is only possible with variadic functions.");
}
if (i != vaarg_index)
{
RETURN_SEMA_ERROR(arg, "Expected a splat only in the vaarg slot.");
}
call->call_expr.va_is_splat = true;
*vararg_splat_ref = args[i] = arg->inner_expr;
continue;
}
if (arg->expr_kind == EXPR_NAMED_ARGUMENT)
{
// Find the location of the parameter.
@@ -1278,7 +1294,6 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
if (last_index > index)
{
SEMA_ERROR(arg, "Named arguments must always be declared in order.");
SEMA_NOTE(last_named_arg, "Place it before this argument.");
return false;
@@ -1313,16 +1328,9 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
// 10. If we exceed the function parameter count (remember we reduced this by one
// in the case of typed vararg) we're now in a variadic list.
if (variadic != VARIADIC_NONE && i >= vararg_index)
if (variadic != VARIADIC_NONE && i >= vaarg_index)
{
// 11a. Look if we did a splat
if (call->call_expr.splat_vararg)
{
*vararg_splat_ref = arg;
continue;
}
else if (variadic == VARIADIC_ANY)
if (variadic == VARIADIC_ANY)
{
if (!sema_analyse_expr(context, arg)) return false;
Type *type = arg->type;
@@ -1344,7 +1352,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
{
// 17a. Assigned a value - skip
if (actual_args[i]) continue;
if (i == vararg_index && variadic != VARIADIC_NONE) continue;
if (i == vaarg_index && variadic != VARIADIC_NONE) continue;
// 17b. Set the init expression.
Decl *param = params[i];
@@ -1408,7 +1416,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
}
RETURN_SEMA_FUNC_ERROR(callee->definition, call, "This call expected a parameter, did you forget it?");
}
if (variadic != VARIADIC_NONE && i > vararg_index)
if (variadic != VARIADIC_NONE && i > vaarg_index)
{
if (!param)
{
@@ -1646,7 +1654,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
}
// 4. Check for splat of the variadic argument.
bool splat = call->call_expr.splat_vararg;
bool splat = call->call_expr.va_is_splat;
if (splat)
{
// 4a. Is this *not* a variadic function/macro? - Then that's an error.
@@ -1739,7 +1747,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
return false;
}
*optional |= IS_OPTIONAL(vararg_splat);
call->call_expr.splat = vararg_splat;
call->call_expr.vasplat = vararg_splat;
}
// 7. Loop through the parameters.
for (unsigned i = 0; i < num_args; i++)
@@ -1979,9 +1987,9 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
{
if (!param) continue;
// Splat? That's the simple case.
if (call_expr->call_expr.splat_vararg)
if (call_expr->call_expr.va_is_splat)
{
if (!sema_analyse_expr(context, args[i] = call_expr->call_expr.splat)) return false;
if (!sema_analyse_expr(context, args[i] = call_expr->call_expr.vasplat)) return false;
}
else
{
@@ -2309,7 +2317,7 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c
PRINT_ERROR_AT(call, "Nested expansion is not possible.");
return false;
}
if (call_expr->splat_vararg)
if (call_expr->va_is_splat)
{
PRINT_ERROR_AT(call, "Expanding parameters is not allowed for macro invocations.");
}
@@ -8383,6 +8391,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
case EXPR_SLICE:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_SWIZZLE:
case EXPR_SUBSCRIPT_ADDR:
case EXPR_SUBSCRIPT_ASSIGN:
@@ -8844,24 +8853,25 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr,
{
switch (expr->expr_kind)
{
case EXPR_COND:
case EXPR_DESIGNATOR:
case EXPR_MACRO_BODY_EXPANSION:
case EXPR_NOP:
case EXPR_TRY_UNWRAP_CHAIN:
case EXPR_TRY_UNWRAP:
case EXPR_CATCH_UNWRAP:
case EXPR_ANYSWITCH:
case EXPR_TYPEID_INFO:
case EXPR_ASM:
case EXPR_OPERATOR_CHARS:
case EXPR_BENCHMARK_HOOK:
case EXPR_TEST_HOOK:
case EXPR_SWIZZLE:
case EXPR_MACRO_BODY:
case EXPR_CATCH_UNWRAP:
case EXPR_COND:
case EXPR_DEFAULT_ARG:
case EXPR_DESIGNATOR:
case EXPR_MACRO_BODY:
case EXPR_MACRO_BODY_EXPANSION:
case EXPR_MEMBER_GET:
case EXPR_NAMED_ARGUMENT:
case EXPR_NOP:
case EXPR_OPERATOR_CHARS:
case EXPR_SPLAT:
case EXPR_SWIZZLE:
case EXPR_TEST_HOOK:
case EXPR_TRY_UNWRAP:
case EXPR_TRY_UNWRAP_CHAIN:
case EXPR_TYPEID_INFO:
UNREACHABLE
case EXPR_TAGOF:
RETURN_SEMA_ERROR(expr, "Expected '()' after this.");

View File

@@ -299,9 +299,9 @@ RETRY:
sema_trace_expr_list_liveness(expr->call_expr.arguments);
if (expr->call_expr.varargs)
{
if (expr->call_expr.splat_vararg)
if (expr->call_expr.va_is_splat)
{
sema_trace_expr_liveness(expr->call_expr.splat);
sema_trace_expr_liveness(expr->call_expr.vasplat);
}
else
{

View File

@@ -698,6 +698,7 @@ static inline bool sema_expr_valid_try_expression(Expr *expr)
case EXPR_SLICE:
case EXPR_SLICE_ASSIGN:
case EXPR_SLICE_COPY:
case EXPR_SPLAT:
case EXPR_STRINGIFY:
case EXPR_SUBSCRIPT:
case EXPR_SWIZZLE: