From 28428fcf308730b3214848758882f790787e96ad Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 6 Sep 2024 10:43:03 +0200 Subject: [PATCH] Handle "splice splat" in the vararg slot as an expression. --- src/compiler/compiler_internal.h | 10 +++-- src/compiler/copying.c | 11 ++--- src/compiler/enums.h | 5 ++- src/compiler/expr.c | 2 + src/compiler/llvm_codegen_expr.c | 4 +- src/compiler/parse_expr.c | 29 ++++++------- src/compiler/parse_global.c | 2 +- src/compiler/parser_internal.h | 2 +- src/compiler/sema_expr.c | 72 ++++++++++++++++++-------------- src/compiler/sema_liveness.c | 4 +- src/compiler/sema_stmts.c | 1 + 11 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 75872b149..d765d2517 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index db2350c64..0837fc9e5 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -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 { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 0966e88c4..bbeb25cbd 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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"); diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 202c51410..1614cfefd 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -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: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ca5c09c90..c8ee6bd6c 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -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 { diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index ead200fbb..6239cd1ce 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -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, ¶ms, TOKEN_RPAREN, &splat, true)) return poisoned_expr; + params = VECNEW(Expr*, 8); + if (!parse_arg_list(c, ¶ms, 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)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index ae212831b..8ada930e7 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -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); } } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index d3f41061b..8af704687 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -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) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 626af63ee..1e55676c8 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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."); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index decd2dc68..2b87f3628 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -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 { diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 95f424ff5..6b14bd01f 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -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: