diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 3336d47aa..be42ea801 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -288,6 +288,7 @@ bool expr_is_pure(Expr *expr) case EXPR_CT_IDENT: case EXPR_CT_CALL: case EXPR_TYPEID: + case EXPR_CT_ARG: return true; case EXPR_ARGV_TO_SUBARRAY: case EXPR_BITASSIGN: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 84e48e6ec..73868db76 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -336,7 +336,6 @@ typedef struct VarDecl_ bool unwrap : 1; bool shadow : 1; bool vararg : 1; - bool vararg_implicit : 1; bool is_static : 1; bool is_read : 1; bool not_null : 1; @@ -438,6 +437,7 @@ typedef enum typedef struct FunctionSignature_ { Variadic variadic : 3; + unsigned vararg_index : 10; bool has_default : 1; bool use_win64 : 1; bool is_pure : 1; @@ -497,6 +497,8 @@ typedef struct { struct { + unsigned vararg_index : 10; + Variadic variadic : 3; bool attr_noreturn : 1; bool attr_nodiscard : 1; bool attr_maydiscard : 1; @@ -679,13 +681,11 @@ typedef struct - - typedef struct { bool is_type_method : 1; bool is_pointer_call : 1; - bool unsplat_last : 1; + bool splat_vararg : 1; bool attr_force_inline : 1; bool attr_force_noinline : 1; bool is_builtin : 1; @@ -699,6 +699,11 @@ typedef struct DeclId func_ref; }; Expr **arguments; + union + { + Expr **varargs; + Expr *splat; + }; Decl **body_arguments; } ExprCall; @@ -831,6 +836,12 @@ typedef struct }; } ExprCtCall; +typedef struct +{ + TokenType type : 8; + ExprId arg; +} ExprCtArg; + typedef struct { CastKind kind : 8; @@ -1006,6 +1017,7 @@ struct Expr_ ExprIdentifier identifier_expr; // 24 ExprIdentifierRaw ct_ident_expr; // 24 ExprCtCall ct_call_expr; // 24 + ExprCtArg ct_arg_expr; ExprIdentifierRaw ct_macro_ident_expr; // 24 ExprIdentifierRaw hash_ident_expr; // 24 TypeInfo *typeid_expr; // 8 @@ -1474,6 +1486,7 @@ typedef struct SemaContext_ Ast **returns; // Reusable returns cache. Ast **returns_cache; + Expr **macro_varargs; }; Type *rtype; struct SemaContext_ *yield_context; @@ -1591,6 +1604,7 @@ typedef struct FunctionPrototype_ bool use_win64 : 1; bool is_failable : 1; bool ret_by_ref : 1; + unsigned short vararg_index; Type *rtype; Type **params; Type **varargs; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 22eeea3be..ac319b0c6 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -184,6 +184,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_BUILTIN: case EXPR_RETVAL: return expr; + case EXPR_CT_ARG: + MACRO_COPY_EXPRID(expr->ct_arg_expr.arg); + return expr; case EXPR_POINTER_OFFSET: MACRO_COPY_EXPRID(expr->pointer_offset_expr.offset); MACRO_COPY_EXPRID(expr->pointer_offset_expr.ptr); @@ -305,6 +308,17 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_ASTID(expr->call_expr.body); MACRO_COPY_DECL_LIST(expr->call_expr.body_arguments); MACRO_COPY_EXPR_LIST(expr->call_expr.arguments); + if (expr->call_expr.varargs) + { + if (expr->call_expr.splat_vararg) + { + MACRO_COPY_EXPR(expr->call_expr.splat); + } + else + { + MACRO_COPY_EXPR_LIST(expr->call_expr.varargs); + } + } return expr; case EXPR_SUBSCRIPT: case EXPR_SUBSCRIPT_ADDR: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 458d8ea5e..13158d6b4 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -213,6 +213,7 @@ typedef enum EXPR_CT_CONV, EXPR_CT_IDENT, EXPR_CT_EVAL, + EXPR_CT_ARG, EXPR_COND, EXPR_DECL, EXPR_DESIGNATOR, @@ -503,33 +504,38 @@ typedef enum TOKEN_VAR, TOKEN_WHILE, - TOKEN_CT_ALIGNOF, // $alignof - TOKEN_CT_ASSERT, // $assert - TOKEN_CT_CASE, // $case - TOKEN_CT_DEFAULT, // $default - TOKEN_CT_DEFINED, // $defined - TOKEN_CT_FOR, // $for - TOKEN_CT_FOREACH, // $foreach - TOKEN_CT_ELIF, // $elif - TOKEN_CT_ELSE, // $else - TOKEN_CT_EVAL, // $eval - TOKEN_CT_EVALTYPE, // $evaltype - TOKEN_CT_ENDIF, // $endif - TOKEN_CT_ENDSWITCH, // $endswitch - TOKEN_CT_ENDFOR, // $endfor - TOKEN_CT_ENDFOREACH, // $endforeach - TOKEN_CT_EXTNAMEOF, // $extnameof - TOKEN_CT_IF, // $if - TOKEN_CT_NAMEOF, // $nameof - TOKEN_CT_OFFSETOF, // $offsetof - TOKEN_CT_QNAMEOF, // $qnameof - TOKEN_CT_SIZEOF, // $sizeof - TOKEN_CT_STRINGIFY, // $stringify - TOKEN_CT_SWITCH, // $switch - TOKEN_CT_TYPEOF, // $typeof - TOKEN_CT_CONVERTIBLE, // $convertible - TOKEN_CT_CASTABLE, // $castable - + TOKEN_CT_ALIGNOF, // $alignof + TOKEN_CT_ASSERT, // $assert + TOKEN_CT_CASE, // $case + TOKEN_CT_DEFAULT, // $default + TOKEN_CT_DEFINED, // $defined + TOKEN_CT_FOR, // $for + TOKEN_CT_FOREACH, // $foreach + TOKEN_CT_ELIF, // $elif + TOKEN_CT_ELSE, // $else + TOKEN_CT_EVAL, // $eval + TOKEN_CT_EVALTYPE, // $evaltype + TOKEN_CT_ENDIF, // $endif + TOKEN_CT_ENDSWITCH, // $endswitch + TOKEN_CT_ENDFOR, // $endfor + TOKEN_CT_ENDFOREACH, // $endforeach + TOKEN_CT_EXTNAMEOF, // $extnameof + TOKEN_CT_IF, // $if + TOKEN_CT_NAMEOF, // $nameof + TOKEN_CT_OFFSETOF, // $offsetof + TOKEN_CT_QNAMEOF, // $qnameof + TOKEN_CT_SIZEOF, // $sizeof + TOKEN_CT_STRINGIFY, // $stringify + TOKEN_CT_SWITCH, // $switch + TOKEN_CT_TYPEOF, // $typeof + TOKEN_CT_CONVERTIBLE, // $convertible + TOKEN_CT_CASTABLE, // $castable + TOKEN_CT_VAARG_COUNT, // $vaarg_count + TOKEN_CT_VAARG_GET_TYPE, // $vaarg_get_type + TOKEN_CT_VAARG_GET_CONST, // $vaarg_get_const, + TOKEN_CT_VAARG_GET_REF, // $vaarg_get_ref, + TOKEN_CT_VAARG_GET_ARG, // $vaarg_get_arg, + TOKEN_CT_VAARG_GET_EXPR, // $vaarg_get_expr, TOKEN_DOCS_START, // /** TOKEN_DOCS_END, // */ (may start with an arbitrary number of `*` TOKEN_DOC_DIRECTIVE, // Any doc directive diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3f2c53058..dabc7d771 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -901,7 +901,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) switch (decl->decl_kind) { case DECL_VAR: - if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) + if (decl->var.kind == VARDECL_UNWRAPPED) { return decl->backend_ref = llvm_get_ref(c, decl->var.alias); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ac9de430f..675fa4739 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4286,7 +4286,7 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B } } -static void llvm_emit_unpacked_variadic_arg(GenContext *c, Expr *expr, BEValue *subarray) +static void llvm_emit_splatted_variadic_arg(GenContext *c, Expr *expr, BEValue *subarray) { BEValue value; llvm_emit_expr(c, &value, expr); @@ -4563,6 +4563,55 @@ void llvm_add_abi_call_attributes(GenContext *c, LLVMValueRef call_value, int co } } + +static inline void llvm_emit_vararg_parameter(GenContext *c, BEValue *value, Type *vararg_type, ABIArgInfo *abi_info, Expr **varargs, Expr *vararg_splat) +{ + REMINDER("All varargs should be called with non-alias!"); + + llvm_value_set_address_abi_aligned(value, llvm_emit_alloca_aligned(c, vararg_type, "vararg"), vararg_type); + // 9a. Special case, empty argument + if (!vararg_splat && !varargs) + { + // Just set the size to zero. + BEValue len_addr; + llvm_emit_subarray_len(c, value, &len_addr); + llvm_store_raw(c, &len_addr, llvm_get_zero(c, type_usize)); + return; + } + if (vararg_splat) + { + // 9b. We splat the last type which is either a slice, an array or a dynamic array. + llvm_emit_splatted_variadic_arg(c, vararg_splat, value); + return; + } + BEValue temp_value; + // 9b. Otherwise we also need to allocate memory for the arguments: + Type *pointee_type = vararg_type->array.base; + unsigned elements = vec_size(varargs); + Type *array = type_get_array(pointee_type, elements); + LLVMTypeRef llvm_array_type = llvm_get_type(c, array); + AlignSize alignment = type_alloca_alignment(array); + LLVMValueRef array_ref = llvm_emit_alloca(c, llvm_array_type, alignment, "varargslots"); + foreach(Expr*, varargs) + { + llvm_emit_expr(c, &temp_value, val); + AlignSize store_alignment; + LLVMValueRef slot = llvm_emit_array_gep_raw(c, + array_ref, + llvm_array_type, + foreach_index, + alignment, + &store_alignment); + llvm_store_to_ptr_aligned(c, slot, &temp_value, store_alignment); + } + BEValue len_addr; + llvm_emit_subarray_len(c, value, &len_addr); + llvm_store_raw(c, &len_addr, llvm_const_int(c, type_usize, elements)); + BEValue pointer_addr; + llvm_emit_subarray_pointer(c, value, &pointer_addr); + llvm_store_raw(c, &pointer_addr, llvm_emit_bitcast_ptr(c, array_ref, pointee_type)); +} + void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) { @@ -4626,21 +4675,33 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) Type **params = prototype->params; ABIArgInfo **abi_args = prototype->abi_args; unsigned param_count = vec_size(params); - unsigned non_variadic_params = param_count; Expr **args = expr->call_expr.arguments; unsigned arguments = vec_size(args); - - if (prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY) non_variadic_params--; + Expr **varargs = NULL; + Expr *vararg_splat = NULL; + if (prototype->variadic != VARIADIC_NONE) + { + if (expr->call_expr.splat_vararg) + { + vararg_splat = expr->call_expr.splat; + } + else + { + varargs = expr->call_expr.varargs; + } + } FunctionPrototype copy; if (prototype->variadic == VARIADIC_RAW) { - if (arguments > non_variadic_params) + if (varargs || vararg_splat) { + assert(!vararg_splat); copy = *prototype; copy.varargs = NULL; - for (unsigned i = non_variadic_params; i < arguments; i++) + + foreach(Expr*, varargs) { - vec_add(copy.varargs, type_flatten(args[i]->type)); + vec_add(copy.varargs, type_flatten(val->type)); } copy.ret_abi_info = NULL; copy.ret_by_ref_abi_info = NULL; @@ -4709,79 +4770,39 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 8. Add all other arguments. - assert(arguments >= non_variadic_params); - for (unsigned i = 0; i < non_variadic_params; i++) + for (unsigned i = 0; i < param_count; i++) { // 8a. Evaluate the expression. Expr *arg_expr = args[i]; - llvm_emit_expr(c, &temp_value, arg_expr); - - // 8b. Emit the parameter according to ABI rules. Type *param = params[i]; ABIArgInfo *info = abi_args[i]; + + if (arg_expr) + { + llvm_emit_expr(c, &temp_value, arg_expr); + } + else + { + assert(prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY); + llvm_emit_vararg_parameter(c, &temp_value, param, info, varargs, vararg_splat); + } + + // 8b. Emit the parameter according to ABI rules. llvm_emit_parameter(c, &values, info, &temp_value, param); } // 9. Typed varargs - if (prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY) - { - REMINDER("All varargs should be called with non-alias!"); - Type *vararg_param = params[non_variadic_params]; - ABIArgInfo *vararg_info = abi_args[non_variadic_params]; - BEValue subarray; - - llvm_value_set_address_abi_aligned(&subarray, llvm_emit_alloca_aligned(c, vararg_param, "vararg"), vararg_param); - - // 9a. Special case, empty argument - if (arguments == non_variadic_params) - { - // Just set the size to zero. - BEValue len_addr; - llvm_emit_subarray_len(c, &subarray, &len_addr); - llvm_store_raw(c, &len_addr, llvm_get_zero(c, type_usize)); - } - else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last) - { - // 9b. We unpack the last type which is either a slice, an array or a dynamic array. - llvm_emit_unpacked_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); - } - else - { - // 9b. Otherwise we also need to allocate memory for the arguments: - Type *pointee_type = vararg_param->array.base; - Type *array = type_get_array(pointee_type, arguments - non_variadic_params); - LLVMTypeRef llvm_array_type = llvm_get_type(c, array); - AlignSize alignment = type_alloca_alignment(array); - LLVMValueRef array_ref = llvm_emit_alloca(c, llvm_array_type, alignment, "varargslots"); - for (unsigned i = non_variadic_params; i < arguments; i++) - { - Expr *arg_expr = expr->call_expr.arguments[i]; - llvm_emit_expr(c, &temp_value, arg_expr); - AlignSize store_alignment; - LLVMValueRef slot = llvm_emit_array_gep_raw(c, array_ref, llvm_array_type, i - non_variadic_params, alignment, &store_alignment); - llvm_store_to_ptr_aligned(c, slot, &temp_value, store_alignment); - } - BEValue len_addr; - llvm_emit_subarray_len(c, &subarray, &len_addr); - llvm_store_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params)); - BEValue pointer_addr; - llvm_emit_subarray_pointer(c, &subarray, &pointer_addr); - llvm_store_raw(c, &pointer_addr, llvm_emit_bitcast_ptr(c, array_ref, pointee_type)); - } - llvm_emit_parameter(c, &values, vararg_info, &subarray, vararg_param); - } - else + if (prototype->variadic == VARIADIC_RAW) { if (prototype->abi_varargs) { // 9. Emit varargs. unsigned index = 0; ABIArgInfo **abi_varargs = prototype->abi_varargs; - for (unsigned i = param_count; i < arguments; i++) + foreach(Expr*, varargs) { - Expr *arg_expr = args[i]; - llvm_emit_expr(c, &temp_value, arg_expr); + llvm_emit_expr(c, &temp_value, val); ABIArgInfo *info = abi_varargs[index]; llvm_emit_parameter(c, &values, info, &temp_value, prototype->varargs[index]); index++; @@ -4790,10 +4811,9 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) else { // 9. Emit varargs. - for (unsigned i = param_count; i < arguments; i++) + foreach(Expr*, varargs) { - Expr *arg_expr = args[i]; - llvm_emit_expr(c, &temp_value, arg_expr); + llvm_emit_expr(c, &temp_value, val); REMINDER("Varargs should be expanded correctly"); vec_add(values, llvm_load_value_store(c, &temp_value)); } @@ -4821,7 +4841,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) } assert(!prototype->ret_by_ref || prototype->ret_by_ref_abi_info->kind != ABI_ARG_INDIRECT); - llvm_add_abi_call_attributes(c, call_value, non_variadic_params, abi_args); + llvm_add_abi_call_attributes(c, call_value, param_count, abi_args); if (prototype->abi_varargs) { llvm_add_abi_call_attributes(c, @@ -5300,14 +5320,15 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr * { Decl **declarations = body_expr->body_expansion_expr.declarations; Expr **values = body_expr->body_expansion_expr.values; + // Create backend refs on demand. - foreach(declarations, i) + VECEACH(declarations, i) { Decl *decl = declarations[i]; if (!decl->backend_ref) llvm_emit_local_var_alloca(c, decl); } // Set the values - foreach(values, i) + VECEACH(values, i) { Expr *expr = values[i]; llvm_emit_expr(c, value, expr); @@ -5766,6 +5787,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case NON_RUNTIME_EXPR: case EXPR_COND: case EXPR_CT_CONV: + case EXPR_CT_ARG: UNREACHABLE case EXPR_BUILTIN_ACCESS: llvm_emit_builtin_access(c, value, expr); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index a5315ecbd..e0d815b57 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -298,10 +298,10 @@ static bool parse_param_path(ParseContext *c, DesignatorElement ***path) * * parameter ::= (param_path '=')? expr */ -bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *unsplat) +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat) { *result = NULL; - if (unsplat) *unsplat = false; + if (splat) *splat = false; while (1) { Expr *expr = NULL; @@ -324,9 +324,9 @@ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool * } else { - if (unsplat) + if (splat) { - *unsplat = try_consume(c, TOKEN_ELLIPSIS); + *splat = try_consume(c, TOKEN_ELLIPSIS); } ASSIGN_EXPR_OR_RET(expr, parse_expr_or_initializer_list(c), false); } @@ -336,7 +336,7 @@ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool * return true; } if (tok_is(c, param_end)) return true; - if (unsplat && *unsplat) + if (splat && *splat) { SEMA_ERROR_HERE("'...' is only allowed on the last argument in a call."); return false; @@ -657,13 +657,13 @@ static Expr *parse_call_expr(ParseContext *c, Expr *left) Expr **params = NULL; advance_and_verify(c, TOKEN_LPAREN); - bool unsplat = false; + bool splat = false; Decl **body_args = NULL; if (!tok_is(c, TOKEN_RPAREN)) { // Pick a modest guess. params = VECNEW(Expr*, 4); - if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; + if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &splat)) return poisoned_expr; } if (try_consume(c, TOKEN_EOS) && !tok_is(c, TOKEN_RPAREN)) { @@ -672,7 +672,7 @@ static Expr *parse_call_expr(ParseContext *c, Expr *left) SEMA_ERROR_LAST("Expected an ending ')'. Did you forget a ')' before this ';'?"); return poisoned_expr; } - if (!parse_parameters(c, VISIBLE_LOCAL, &body_args)) return poisoned_expr; + if (!parse_parameters(c, VISIBLE_LOCAL, &body_args, NULL, NULL, NULL)) return poisoned_expr; } if (!tok_is(c, TOKEN_RPAREN)) { @@ -684,7 +684,7 @@ 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.unsplat_last = unsplat; + call->call_expr.splat_vararg = splat; call->call_expr.body_arguments = body_args; RANGE_EXTEND_PREV(call); if (body_args && !tok_is(c, TOKEN_LBRACE)) @@ -912,6 +912,22 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left) return expr; } +static Expr *parse_ct_arg(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_ARG); + TokenType type = expr->ct_arg_expr.type = c->tok; + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + if (type != TOKEN_CT_VAARG_COUNT) + { + ASSIGN_EXPRID_OR_RET(expr->ct_arg_expr.arg, parse_expr(c), poisoned_expr); + } + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + static Expr *parse_ct_conv(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -1737,4 +1753,5 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_CONVERTIBLE] = { parse_ct_conv, NULL, PREC_NONE }, [TOKEN_CT_CASTABLE] = { parse_ct_conv, NULL, PREC_NONE }, [TOKEN_LBRACE] = { parse_initializer_list, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_COUNT] = { parse_ct_arg, NULL, PREC_NONE } }; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 93535f4e8..6381e2f17 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1055,84 +1055,153 @@ bool parse_next_is_typed_parameter(ParseContext *c) } } +INLINE bool is_end_of_param_list(ParseContext *c) +{ + return tok_is(c, TOKEN_EOS) || tok_is(c, TOKEN_RPAREN); +} /** * parameters ::= (parameter (',' parameter)*)? * non_type_ident = IDENT | HASH_IDENT | CT_IDENT * parameter ::= type ELLIPSIS? (non_type_ident ('=' expr))? * | ELLIPSIS (CT_TYPE_IDENT | non_type_ident ('=' expr)?)? */ -bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref) +bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params, + Variadic *variadic, unsigned *vararg_index_ref) { Decl** params = NULL; bool var_arg_found = false; - while (!tok_is(c, TOKEN_EOS) && !tok_is(c, TOKEN_RPAREN)) + while (!is_end_of_param_list(c)) { - TypeInfo *type = NULL; - bool ellipsis = try_consume(c, TOKEN_ELLIPSIS); - if (!ellipsis && parse_next_is_typed_parameter(c)) + // Check for "raw" variadic arguments. This is allowed on C functions and macros. + if (ellipsis) { + // In the future maybe this + if (!is_end_of_param_list(c) && !tok_is(c, TOKEN_COMMA)) + { + SEMA_ERROR_HERE("Expected ')' here."); + return false; + } + // Variadics might not be allowed + if (!variadic) + { + SEMA_ERROR_LAST("Variadic parameters are not allowed."); + return false; + } + // Check that we only have one variadic parameter. + if (var_arg_found) + { + SEMA_ERROR_LAST("Only a single variadic parameter is allowed."); + return false; + } + // Set the variadic type and insert a dummy argument. + *variadic = VARIADIC_RAW; + *vararg_index_ref = vec_size(params); + var_arg_found = true; + vec_add(params, NULL); + if (!try_consume(c, TOKEN_COMMA)) break; + continue; + } + + // Now we have the following possibilities: "foo", "Foo foo", "Foo... foo", "foo...", "Foo" + TypeInfo *type = NULL; + if (parse_next_is_typed_parameter(c)) + { + // Parse the type, ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false); ellipsis = try_consume(c, TOKEN_ELLIPSIS); + // We might have Foo... + if (ellipsis) + { + if (!variadic) + { + SEMA_ERROR_HERE("Variadic arguments are not allowed."); + return false; + } + if (var_arg_found) + { + sema_error_at(extend_span_with_token(type->span, c->prev_span), "Only a single variadic parameter is allowed."); + return false; + } + *variadic = VARIADIC_TYPED; + } } - if (ellipsis && var_arg_found) - { - SEMA_ERROR_LAST("Only a single vararg parameter is allowed."); - return false; - } - + // We have parsed the optional type, next get the optional variable name VarDeclKind param_kind; const char *name = NULL; SourceSpan span = c->span; bool no_name = false; - bool vararg_implicit = false; switch (c->tok) { case TOKEN_CONST_IDENT: case TOKEN_CT_CONST_IDENT: + // We reserve upper case constants for globals. SEMA_ERROR_HERE("Parameter names may not be all uppercase."); return false; case TOKEN_IDENT: - // normal foo + // normal "foo" name = symstr(c); param_kind = VARDECL_PARAM; + advance_and_verify(c, TOKEN_IDENT); // Check for "foo..." which defines an implicit "any" vararg - if (peek(c) == TOKEN_ELLIPSIS) + if (try_consume(c, TOKEN_ELLIPSIS)) { + // Did we get Foo... foo... if (ellipsis) { - SEMA_ERROR_HERE("Unexpected '...' here."); + SEMA_ERROR_HERE("Unexpected '...' following a vararg declaration."); return false; } - advance(c); + ellipsis = true; + if (!variadic) + { + sema_error_at(extend_span_with_token(span, c->span), "Variadic parameters are not allowed."); + return false; + } + // Did we get Foo foo..., then that's an error. if (type) { - SEMA_ERROR_HERE("The '...' should appear after the type."); + SEMA_ERROR_HERE("For typed varargs '...', needs to appear after the type."); return false; } + // This is "foo..." + *variadic = VARIADIC_ANY; + // We generate the type as type_any type = type_info_new_base(type_any, c->span); - ellipsis = true; - vararg_implicit = true; } 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 || peek(c) == TOKEN_ELLIPSIS) + { + SEMA_ERROR_HERE("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(c); + advance_and_verify(c, TOKEN_AMP); name = symstr(c); - span = extend_span_with_token(span, c->span); - if (!tok_is(c, TOKEN_IDENT)) + if (!try_consume(c, TOKEN_IDENT)) { - SEMA_ERROR_HERE("Only normal variables may be passed by reference."); + SEMA_ERROR_HERE("A regular variable name, e.g. 'foo' was expected after the '&'."); return false; } + // This will catch Type... &foo and &foo..., neighter is allowed. + if (ellipsis || try_consume(c, TOKEN_ELLIPSIS)) + { + SEMA_ERROR_HERE("Reference parameters may not be varargs, use untyped macro varargs '...' instead."); + return false; + } + // Span includes the "&" + span = extend_span_with_token(span, c->span); param_kind = VARDECL_PARAM_REF; break; case TOKEN_HASH_TYPE_IDENT: @@ -1142,19 +1211,32 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref case TOKEN_HASH_IDENT: // expression #foo name = symstr(c); + advance_and_verify(c, TOKEN_HASH_IDENT); + if (ellipsis || try_consume(c, TOKEN_ELLIPSIS)) + { + SEMA_ERROR_HERE("Expression parameters may not be varargs, use untyped macro varargs '...' instead."); + return false; + } param_kind = VARDECL_PARAM_EXPR; break; // Compile time type $Type case TOKEN_CT_TYPE_IDENT: name = symstr(c); + advance_and_verify(c, TOKEN_CT_TYPE_IDENT); + if (ellipsis || try_consume(c, TOKEN_ELLIPSIS)) + { + SEMA_ERROR_HERE("Expression parameters may not be varargs, use untyped macro varargs '...' instead."); + return false; + } param_kind = VARDECL_PARAM_CT_TYPE; break; case TOKEN_COMMA: case TOKEN_EOS: case TOKEN_RPAREN: + // Handle "Type..." and "Type" if (!type && !ellipsis) { - SEMA_ERROR_HERE("Expected a parameter."); + sema_error_at_after(c->prev_span, "Expected a parameter."); return false; } no_name = true; @@ -1162,14 +1244,6 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref param_kind = VARDECL_PARAM; break; default: - if (!type && parse_next_may_be_type(c)) - { - ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false); - param_kind = VARDECL_PARAM; - no_name = true; - span = type->span; - break; - } SEMA_ERROR_HERE("Expected a parameter."); return false; } @@ -1182,16 +1256,18 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref param->var.type_info = type; if (!no_name) { - advance(c); if (try_consume(c, TOKEN_EQ)) { if (!parse_decl_initializer(c, param, false)) return poisoned_decl; } } if (!parse_attributes(c, ¶m->attributes)) return false; - var_arg_found |= ellipsis; - param->var.vararg = ellipsis; - param->var.vararg_implicit = vararg_implicit; + if (ellipsis) + { + var_arg_found = true; + param->var.vararg = ellipsis; + *vararg_index_ref = vec_size(params); + } vec_add(params, param); if (!try_consume(c, TOKEN_COMMA)) break; } @@ -1204,31 +1280,19 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref * * parameter_type_list ::= '(' parameters ')' */ -static inline bool parse_parameter_list(ParseContext *c, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +static inline bool parse_fn_parameter_list(ParseContext *c, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) { Decl **decls = NULL; CONSUME_OR_RET(TOKEN_LPAREN, false); - if (!parse_parameters(c, parent_visibility, &decls)) return false; + Variadic variadic = VARIADIC_NONE; + unsigned vararg_index = 0; + if (!parse_parameters(c, parent_visibility, &decls, NULL, &variadic, &vararg_index)) return false; CONSUME_OR_RET(TOKEN_RPAREN, false); + signature->vararg_index = vararg_index; signature->params = decls; - Decl *last = VECLAST(decls); + signature->variadic = variadic; - // The last parameter may hold a vararg - if (last && last->var.vararg) - { - // Is it "(foo...)" (any) or "(int... foo)" (typed)? - if (last->var.type_info) - { - signature->variadic = last->var.vararg_implicit ? VARIADIC_ANY : VARIADIC_TYPED; - } - else - { - // Remove the last element if it's a raw variant, i.e. "(...)" - vec_pop(decls); - signature->variadic = VARIADIC_RAW; - } - } return true; } @@ -1459,14 +1523,18 @@ static inline Decl *parse_top_level_const_declaration(ParseContext *c, Visibilit * * trailing_block_parameter ::= '@' IDENT ( '(' parameters ')' )? */ -static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl ***params_ref, DeclId *body_param_ref) +static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl *macro) { CONSUME_OR_RET(TOKEN_LPAREN, false); - *params_ref = NULL; - *body_param_ref = 0; // Parse the regular parameters. - if (!parse_parameters(c, visibility, params_ref)) return false; + Variadic variadic = VARIADIC_NONE; + unsigned vararg_index = 0; + Decl **params = NULL; + if (!parse_parameters(c, visibility, ¶ms, NULL, &variadic, &vararg_index)) return false; + macro->macro_decl.parameters = params; + macro->macro_decl.vararg_index = vararg_index; + macro->macro_decl.variadic = variadic; // Do we have trailing block parameters? if (try_consume(c, TOKEN_EOS)) @@ -1476,10 +1544,14 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl * TRY_CONSUME_OR_RET(TOKEN_AT_IDENT, "Expected an ending ')' or a block parameter on the format '@block(...).", false); if (try_consume(c, TOKEN_LPAREN)) { - if (!parse_parameters(c, visibility, &body_param->body_params)) return false; + if (!parse_parameters(c, visibility, &body_param->body_params, NULL, NULL, NULL)) return false; CONSUME_OR_RET(TOKEN_RPAREN, false); } - *body_param_ref = declid(body_param); + macro->macro_decl.body_param = declid(body_param); + } + else + { + macro->macro_decl.body_param = 0; } CONSUME_OR_RET(TOKEN_RPAREN, false); return true; @@ -1535,7 +1607,7 @@ static inline Decl *parse_define_type(ParseContext *c, Visibility visibility) decl->typedef_decl.is_distinct = distinct; ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_optional_type(c), poisoned_decl); decl->typedef_decl.function_signature.returntype = type_infoid(type_info); - if (!parse_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true)) + if (!parse_fn_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; } @@ -1676,7 +1748,7 @@ static inline Decl *parse_define_attribute(ParseContext *c, Visibility visibilit if (try_consume(c, TOKEN_LPAREN)) { - if (!parse_parameters(c, visibility, &decl->attr_decl.params)) return poisoned_decl; + if (!parse_parameters(c, visibility, &decl->attr_decl.params, NULL, NULL, NULL)) return poisoned_decl; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl); } @@ -1802,7 +1874,7 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili TypeInfoId *method_type_ref = &decl->macro_decl.type_parent; if (!parse_func_macro_header(c, true, rtype_ref, method_type_ref, &decl->name, &decl->span)) return poisoned_decl; const char *block_parameter = NULL; - if (!parse_macro_arguments(c, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_param)) return poisoned_decl; + if (!parse_macro_arguments(c, visibility, decl)) return poisoned_decl; if (!parse_attributes(c, &decl->attributes)) return poisoned_decl; ASSIGN_ASTID_OR_RET(decl->macro_decl.body, parse_stmt(c), poisoned_decl); @@ -1988,7 +2060,7 @@ static inline Decl *parse_enum_declaration(ParseContext *c, Visibility visibilit * ; * * func_declaration - * : FUNC failable_type func_name '(' opt_parameter_type_list ')' opt_attributes + * : FN failable_type func_name '(' opt_parameter_type_list ')' opt_attributes * ; * * @param visibility @@ -2009,7 +2081,7 @@ static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility SEMA_ERROR(func, "Function names may not use '@'."); return false; } - if (!parse_parameter_list(c, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; + if (!parse_fn_parameter_list(c, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; if (!parse_attributes(c, &func->attributes)) return poisoned_decl; // TODO remove diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index dc429861e..726d5b5dc 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -959,6 +959,12 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_CATCH: case TOKEN_BYTES: case TOKEN_BUILTIN: + case TOKEN_CT_VAARG_COUNT: + case TOKEN_CT_VAARG_GET_ARG: + case TOKEN_CT_VAARG_GET_EXPR: + case TOKEN_CT_VAARG_GET_CONST: + case TOKEN_CT_VAARG_GET_REF: + case TOKEN_CT_VAARG_GET_TYPE: return parse_expr_stmt(c); case TOKEN_ASSERT: return parse_assert_stmt(c); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 0efe2f86a..fe578790c 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -43,8 +43,11 @@ Expr *parse_ct_expression_list(ParseContext *c, bool allow_decl); Expr *parse_expression_list(ParseContext *c, bool allow_decls); Decl *parse_decl_after_type(ParseContext *c, TypeInfo *type); Decl *parse_var_decl(ParseContext *c); -bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref); -bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *unsplat); + +bool +parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params, Variadic *variadic, + unsigned *vararg_index_ref); +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat); Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info); bool parse_module(ParseContext *c); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 9d963a793..563b42cc5 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -825,6 +825,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_VARIANT: case EXPR_CT_CONV: case EXPR_POINTER_OFFSET: + case EXPR_CT_ARG: UNREACHABLE case EXPR_BUILTIN_ACCESS: @@ -992,6 +993,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_VARIANT: case EXPR_CT_CONV: case EXPR_POINTER_OFFSET: + case EXPR_CT_ARG: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 0a39f525f..9589c387b 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -629,36 +629,79 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, CallAB { bool all_ok = true; all_ok = sema_resolve_type_info(context, type_infoptr(signature->returntype)) && all_ok; + + // We don't support more than MAX_PARAMS number of params. This makes everything sane. if (vec_size(signature->params) > MAX_PARAMS) { SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); return NULL; } + + // Get param count and variadic type + Variadic variadic_type = signature->variadic; unsigned param_count = vec_size(signature->params); Decl **params = signature->params; Type **types = NULL; + bool reached_vararg = false; + for (unsigned i = 0; i < param_count; i++) { Decl *param = params[i]; - assert(param->resolve_status == RESOLVE_NOT_DONE); + // We might run into a raw vararg + if (!param) + { + // Just skip, we'll remove this parameter later. + assert(variadic_type == VARIADIC_RAW); + reached_vararg = true; + continue; + } + assert(param->resolve_status == RESOLVE_NOT_DONE && "The param shouldn't have been resolved yet."); param->resolve_status = RESOLVE_RUNNING; bool has_default; + // Analyse the param if (!sema_analyse_function_param(context, param, is_real_function, &has_default)) { decl_poison(param); all_ok = false; continue; } + if (reached_vararg) + { + // If we already reached a vararg (as a previous argument) and we have a + // parameter without a name. + if (!param->name) + { + SEMA_ERROR(param, "A parameter name was expected, as parameters after varargs must be named."); + decl_poison(param); + return NULL; + } + if (variadic_type == VARIADIC_RAW) + { + SEMA_ERROR(param, "C-style varargs cannot be followed by regular parameters."); + return NULL; + } + } + // Disallow "void" args and repeating the same parameter name. + // This must be done after checking raw varargs if (!sema_check_param_uniqueness_and_type(params, param, i, param_count)) { all_ok = false; continue; } + // Update whether this was a vararg, and update "default" for the signature. + reached_vararg |= param->var.vararg; signature->has_default = signature->has_default || has_default; + // Resolution is done. param->resolve_status = RESOLVE_DONE; + // Add it to the type for the type signature. vec_add(types, param->type); } - + // Remove the last empty value. + if (variadic_type == VARIADIC_RAW) + { + assert(VECLAST(params) == NULL && "The last parameter must have been a raw variadic."); + vec_pop(signature->params); + } if (!all_ok) return NULL; return type_get_func(signature, abi); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a5d7b16ee..b54d089ed 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -482,6 +482,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_COMPILER_CONST: case EXPR_POISONED: case EXPR_ARGV_TO_SUBARRAY: + case EXPR_CT_ARG: UNREACHABLE case EXPR_NOP: return true; @@ -674,6 +675,7 @@ static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr) case EXPR_VARIANT: case EXPR_BUILTIN_ACCESS: case EXPR_POINTER_OFFSET: + case EXPR_CT_ARG: goto ERR; } UNREACHABLE @@ -772,6 +774,7 @@ bool expr_may_addr(Expr *expr) case EXPR_VARIANT: case EXPR_BUILTIN_ACCESS: case EXPR_POINTER_OFFSET: + case EXPR_CT_ARG: return false; } UNREACHABLE @@ -1352,7 +1355,7 @@ static inline bool sema_expr_analyse_intrinsic_fp_invocation(SemaContext *contex } -static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_type) +static inline bool expr_may_splat_as_vararg(Expr *expr, Type *variadic_base_type) { Type *base_type = variadic_base_type->canonical; Type *canonical = expr->type->canonical; @@ -1373,12 +1376,14 @@ typedef struct { bool macro; bool func_pointer; + const char *name; const char *block_parameter; Decl **params; Type **param_types; Expr *struct_var; unsigned param_count; Variadic variadic; + unsigned vararg_index; } CalledDecl; static inline bool expr_promote_vararg(SemaContext *context, Expr *arg) @@ -1450,20 +1455,21 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr } -static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, Variadic variadic, bool *failable) +INLINE bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, + unsigned func_param_count, Variadic variadic, unsigned vararg_index, + bool *failable, + Expr ***varargs_ref, + Expr **vararg_splat_ref) { unsigned num_args = vec_size(args); Decl **params = callee->params; bool is_func_ptr = callee->func_pointer; - // 1. We need at least as many function locations as we have parameters. - unsigned entries_needed = func_param_count > num_args ? func_param_count : num_args; - Expr **actual_args = VECNEW(Expr*, entries_needed); - for (unsigned i = 0; i < entries_needed; i++) vec_add(actual_args, NULL); - + Expr **actual_args = VECNEW(Expr*, func_param_count); + for (unsigned i = 0; i < func_param_count; i++) vec_add(actual_args, NULL); // 2. Loop through the parameters. - bool uses_named_parameters = false; + bool has_named = false; for (unsigned i = 0; i < num_args; i++) { Expr *arg = args[i]; @@ -1476,15 +1482,16 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * SEMA_ERROR(arg, "Named parameters are not allowed with function pointer calls."); return false; } - // 8a. We have named parameters, that will add some restrictions. - uses_named_parameters = true; - // 8b. Find the location of the parameter. + // Find the location of the parameter. int index = find_index_of_named_parameter(params, arg); - // 8c. If it's not found then this is an error. + // If it's not found then this is an error. if (index < 0) return false; + // We have named parameters, that will add some restrictions. + has_named = true; + // 8d. We might actually be finding the typed vararg at the end, // this is an error. if (params[index]->var.vararg) @@ -1506,52 +1513,55 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * continue; } - // 9. Check for previous use of named parameters, this is not allowed - // like foo(.a = 1, 3) => error. - if (uses_named_parameters) + if (has_named) { - SEMA_ERROR(arg, "A regular parameter cannot follow a named parameter."); + SEMA_ERROR(args[i - 1], "Named arguments must be placed after positional arguments."); + return false; + } + + // 11. We might have a typed variadic argument. + if (variadic == VARIADIC_NONE && i >= func_param_count) + { + // 15. We have too many parameters... + SEMA_ERROR(arg, "This argument would exceed the number of parameters, did you add too many arguments?"); return false; } // 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 (i >= func_param_count) + if (variadic != VARIADIC_NONE && i >= vararg_index) { - // 11. We might have a typed variadic argument. - if (variadic == VARIADIC_NONE) - { - // 15. We have too many parameters... - SEMA_ERROR(arg, "This argument would exceed the number of parameters, did you add too many arguments?"); - return false; - } - // 11a. Look if we did an unsplat - if (call->call_expr.unsplat_last) + // 11a. Look if we did a splat + if (call->call_expr.splat_vararg) { - // 11b. Is this the last argument, or did we get some before the unpack? + // 11b. Is this the last argument, or did we get some before the splat? if (i < num_args - 1) { SEMA_ERROR(arg, - "This looks like a variable argument before an unpacked variable which isn't allowed. Did you add too many arguments?"); + "This looks like a variable argument before an splatted variable which isn't allowed. Did you add too many arguments?"); return false; } + *vararg_splat_ref = arg; + continue; } else if (variadic == VARIADIC_ANY) { if (!sema_analyse_expr(context, arg)) return false; expr_insert_addr(arg); } + vec_add(*varargs_ref, arg); + continue; } actual_args[i] = arg; } // 17. Set default values. - for (unsigned i = 0; i < entries_needed; i++) + for (unsigned i = 0; i < func_param_count; i++) { // 17a. Assigned a value - skip if (actual_args[i]) continue; - + if (i == vararg_index && variadic != VARIADIC_NONE) continue; if (is_func_ptr) goto FAIL_MISSING; // 17b. Set the init expression. Decl *param = params[i]; @@ -1579,26 +1589,31 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * FAIL_MISSING: // 17c. Vararg not set? That's fine. - if (params && params[i]->var.vararg) continue; + if (param && params[i]->var.vararg) continue; // 17d. Argument missing, that's bad. - if (!uses_named_parameters || is_func_ptr || !params[i]->name) + if (!has_named || is_func_ptr || !params[i]->name) { - if (entries_needed == 1) + if (func_param_count == 1) { - SEMA_ERROR(call, "A parameter was expected for the call."); + SEMA_ERROR(call, "This call expected a parameter of type %s, did you forget it?", type_quoted_error_string(params[i]->type)); + return false; + } + if (variadic != VARIADIC_NONE && i > vararg_index) + { + sema_error_at_after(args[num_args - 1]->span, "Expected '.%s = ...' after this argument.", params[i]->name); return false; } if (num_args) { - unsigned needed = entries_needed - num_args; + unsigned needed = func_param_count - num_args; SEMA_ERROR(args[num_args - 1], "Expected %d more %s after this one, did you forget %s?", - needed, needed > 1 ? "arguments" : "argument", needed > 1 ? "them" : "it"); + needed, needed == 1 ? "argument" : "arguments", needed == 1 ? "it" : "them"); } else { - SEMA_ERROR(call, "The call needs %d parameters, please provide them.", entries_needed); + SEMA_ERROR(call, "'%s' expects %d parameters, but none was provided.", callee->name, func_param_count); } return false; } @@ -1638,16 +1653,15 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr assert(!call->call_expr.is_pointer_call); } - // 4. Check for unsplat of the last argument. - bool unsplat = call->call_expr.unsplat_last; - if (unsplat) + // 4. Check for splat of the variadic argument. + bool splat = call->call_expr.splat_vararg; + if (splat) { // 4a. Is this *not* a variadic function/macro? - Then that's an error. if (callee.variadic == VARIADIC_NONE) { SEMA_ERROR(call->call_expr.arguments[num_args - 1], - "Unpacking is only allowed for %s with variable parameters.", - callee.macro ? "macros" : "functions"); + "Using the splat operator is only allowed on vararg parameters."); return false; } } @@ -1661,71 +1675,84 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr if (callee.variadic == VARIADIC_TYPED || callee.variadic == VARIADIC_ANY) { // 7a. The parameter type is [], so we get the - Type *last_type = callee.macro ? callee.params[func_param_count - 1]->type : callee.param_types[func_param_count - 1]; - assert(last_type->type_kind == TYPE_SUBARRAY); - variadic_type = last_type->array.base; - // 7b. The last function parameter is implicit so we will pretend it's not there. - func_param_count--; + Type *vararg_slot_type = callee.macro ? callee.params[callee.vararg_index]->type : callee.param_types[callee.vararg_index]; + assert(vararg_slot_type->type_kind == TYPE_SUBARRAY); + variadic_type = vararg_slot_type->array.base; } - if (!sema_expand_call_arguments(context, &callee, call, args, func_param_count, callee.variadic, failable)) return false; + Expr **varargs = NULL; + Expr *vararg_splat = NULL; + if (!sema_expand_call_arguments(context, + &callee, + call, + args, + func_param_count, + callee.variadic, + callee.vararg_index, + failable, &varargs, &vararg_splat)) return false; args = call->call_expr.arguments; num_args = vec_size(args); - // 7. Loop through the parameters. - for (unsigned i = 0; i < num_args; i++) + call->call_expr.varargs = NULL; + if (varargs) { - Expr *arg = args[i]; - - // 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 (i >= func_param_count) + if (callee.variadic == VARIADIC_RAW) { - // 11. We might have a typed variadic argument. - if (variadic_type) - { - // 11a. Look if we did an unsplat - if (call->call_expr.unsplat_last) - { - // 11c. Analyse the expression. We don't use any type inference here since - // foo(...{ 1, 2, 3 }) is a fairly worthless thing. - if (!sema_analyse_expr(context, arg)) return false; - - // 11d. If it is allowed. - if (!expr_may_unpack_as_vararg(arg, variadic_type)) - { - SEMA_ERROR(arg, "It's not possible to unpack %s as vararg of type %s", - type_quoted_error_string(arg->type), - type_quoted_error_string(variadic_type)); - return false; - } - } - else - { - // 11e. A simple variadic value: - if (!sema_analyse_expr_rhs(context, variadic_type, arg, true)) return false; - } - // Set the argument at the location. - *failable |= IS_OPTIONAL(arg); - continue; - } - // 12. We might have a naked variadic argument - if (callee.variadic == VARIADIC_RAW) + foreach(Expr*, varargs) { // 12a. Analyse the expression. - if (!sema_analyse_expr(context, arg)) return false; + if (!sema_analyse_expr(context, val)) return false; // 12b. In the case of a compile time variable non macros we cast to c_int / double. if (!callee.macro) { - if (!expr_promote_vararg(context, arg)) return false; + if (!expr_promote_vararg(context, val)) return false; } // Set the argument at the location. - *failable |= IS_OPTIONAL(arg); - continue; + *failable |= IS_OPTIONAL(val); } - UNREACHABLE + } + else + { + foreach(Expr*, varargs) + { + // 11e. A simple variadic value: + if (!sema_analyse_expr_rhs(context, variadic_type, val, true)) return false; + *failable |= IS_OPTIONAL(val); + } + } + call->call_expr.varargs = varargs; + } + if (vararg_splat) + { + // 11c. Analyse the expression. We don't use any type inference here since + // foo(...{ 1, 2, 3 }) is a fairly worthless thing. + if (!sema_analyse_expr(context, vararg_splat)) return false; + + // 11d. If it is allowed. + if (!expr_may_splat_as_vararg(vararg_splat, variadic_type)) + { + SEMA_ERROR(vararg_splat, "It's not possible to splat %s as vararg of type %s", + type_quoted_error_string(vararg_splat->type), + type_quoted_error_string(variadic_type)); + return false; + } + *failable |= IS_OPTIONAL(vararg_splat); + call->call_expr.splat = vararg_splat; + } + // 7. Loop through the parameters. + for (unsigned i = 0; i < num_args; i++) + { + + Expr *arg = args[i]; + + // 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 (i == callee.vararg_index && callee.variadic != VARIADIC_NONE) + { + assert(arg == NULL); + continue; } Decl *param; @@ -1822,11 +1849,13 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr } return true; } -static inline bool sema_expr_analyse_func_invocation(SemaContext *context, FunctionPrototype *prototype, FunctionSignature *sig, Expr *expr, Decl *decl, - Expr *struct_var, bool failable) +static inline bool sema_expr_analyse_func_invocation(SemaContext *context, FunctionPrototype *prototype, + FunctionSignature *sig, Expr *expr, Decl *decl, + Expr *struct_var, bool failable, const char *name) { CalledDecl callee = { .macro = false, + .name = name, .func_pointer = sig ? 0 : 1, .block_parameter = NULL, .struct_var = struct_var, @@ -1834,6 +1863,7 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Funct .param_types = prototype->params, .param_count = vec_size(prototype->params), .variadic = prototype->variadic, + .vararg_index = prototype->vararg_index }; if (context->current_function_pure && (!sig || !sig->is_pure) && !expr->call_expr.attr_pure) { @@ -1877,7 +1907,7 @@ static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, func_ptr_type->pointer->func.prototype, NULL, expr, - NULL, NULL, failable); + NULL, NULL, failable, func_ptr_type->pointer->name); } @@ -1976,7 +2006,14 @@ static inline Type *unify_returns(SemaContext *context) static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool failable) { expr->call_expr.is_pointer_call = false; - return sema_expr_analyse_func_invocation(context, decl->type->func.prototype, &decl->func_decl.function_signature, expr, decl, struct_var, failable); + return sema_expr_analyse_func_invocation(context, + decl->type->func.prototype, + &decl->func_decl.function_signature, + expr, + decl, + struct_var, + failable, + decl->name); } @@ -2034,8 +2071,11 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Decl **params = decl_copy_list(decl->macro_decl.parameters); CalledDecl callee = { .macro = true, + .name = decl->name, .block_parameter = decl->macro_decl.body_param ? declptr(decl->macro_decl.body_param)->name : NULL, .params = params, + .variadic = decl->macro_decl.variadic, + .vararg_index = decl->macro_decl.vararg_index, .param_count = vec_size(params), .struct_var = struct_var }; @@ -2365,7 +2405,7 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) SEMA_ERROR(call, "Nested expansion is not possible."); return false; } - if (call_expr->unsplat_last) + if (call_expr->splat_vararg) { SEMA_ERROR(call, "Expanding parameters is not allowed for macro invocations."); return false; @@ -7725,6 +7765,23 @@ static inline bool sema_expr_analyse_variant(SemaContext *context, Expr *expr) return true; } +static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) +{ + if (!context->current_macro) + { + SEMA_ERROR(expr, "'%s' can only be used inside of a macro.", token_type_to_string(expr->ct_arg_expr.type)); + return false; + } + switch (expr->ct_arg_expr.type) + { + case TOKEN_CT_VAARG_COUNT: + expr_rewrite_const_int(expr, type_usize, vec_size(context->macro_varargs), true); + return true; + default: + TODO; + } +} + static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr) { Expr *inner = expr->inner_expr; @@ -7920,6 +7977,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_VARIANTSWITCH: case EXPR_TYPEID_INFO: UNREACHABLE + case EXPR_CT_ARG: + return sema_expr_analyse_ct_arg(context, expr); case EXPR_VARIANT: return sema_expr_analyse_variant(context, expr); case EXPR_STRINGIFY: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 74d24a771..6924c966e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -909,7 +909,7 @@ static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span len_call->call_expr.arguments = arguments; len_call->call_expr.body = 0; len_call->call_expr.is_func_ref = true; - len_call->call_expr.unsplat_last = false; + len_call->call_expr.splat_vararg = false; len_call->call_expr.is_type_method = true; bool is_macro = method_decl->decl_kind == DECL_MACRO; if (!is_macro) diff --git a/src/compiler/tilde_codegen_expr.c b/src/compiler/tilde_codegen_expr.c index 429c7befa..73850e176 100644 --- a/src/compiler/tilde_codegen_expr.c +++ b/src/compiler/tilde_codegen_expr.c @@ -361,11 +361,11 @@ void tilde_emit_call_expr(TbContext *c, TBEValue *result_value, Expr *expr) // llvm_emit_subarray_len(c, &subarray, &len_addr); // llvm_store_value_raw(c, &len_addr, llvm_get_zero(c, type_usize)); } - else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last) + else if (arguments == non_variadic_params + 1 && expr->call_expr.splat_last) { - // 9b. We unpack the last type which is either a slice, an array or a dynamic array. + // 9b. We splat the last type which is either a slice, an array or a dynamic array. TODO - // llvm_emit_unpacked_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); + // llvm_emit_splatted_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); } else { diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 405430408..5b812fe6f 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -362,6 +362,18 @@ const char *token_type_to_string(TokenType type) return "$extnameof"; case TOKEN_CT_IF: return "$if"; + case TOKEN_CT_VAARG_COUNT: + return "$vaarg_count"; + case TOKEN_CT_VAARG_GET_TYPE: + return "$vaarg_get_type"; + case TOKEN_CT_VAARG_GET_CONST: + return "$vaarg_get_const"; + case TOKEN_CT_VAARG_GET_ARG: + return "$vaarg_get_arg"; + case TOKEN_CT_VAARG_GET_REF: + return "$vaarg_get_ref"; + case TOKEN_CT_VAARG_GET_EXPR: + return "$vaarg_get_expr"; case TOKEN_CT_NAMEOF: return "$nameof"; case TOKEN_CT_OFFSETOF: diff --git a/src/compiler/types.c b/src/compiler/types.c index 7eec504cf..ce8bcfc4b 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1087,6 +1087,7 @@ static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI a unsigned param_count = vec_size(sig->params); FunctionPrototype *proto = CALLOCS(FunctionPrototype); proto->variadic = sig->variadic; + proto->vararg_index = sig->vararg_index; Type *rtype = type_infoptr(sig->returntype)->type; proto->rtype = rtype; if (type_is_optional(rtype)) diff --git a/src/utils/lib.h b/src/utils/lib.h index cf8add180..d2b97ec60 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -270,9 +270,10 @@ static inline void* expand_(void *vec, size_t element_size) #define CONCAT(a, b) CONCAT_INNER(a, b) #define VECEACH(_vec, _index) \ for (unsigned (_index) = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); (_index) < CONCAT(__vecsize_, __LINE__); (_index)++) -#define foreach(_vec, _index) \ - for (unsigned (_index) = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); (_index) < CONCAT(__vecsize_, __LINE__); (_index)++) - +#define foreach(type__, vec__) \ + type__* foreach_vec__ = vec__; unsigned foreach_len__ = vec_size(foreach_vec__); \ + type__ val; if (foreach_vec__) val = foreach_vec__[0]; \ + for (unsigned foreach_index = 0; foreach_index < foreach_len__; val = foreach_vec__[++foreach_index]) #define VECNEW(_type, _capacity) ((_type *)(vec_new_(sizeof(_type), _capacity) + 1)) #define vec_add(vec_, value_) do { \ diff --git a/src/version.h b/src/version.h index fa4fbfd63..71925510f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.25" \ No newline at end of file +#define COMPILER_VERSION "0.3.26" \ No newline at end of file diff --git a/test/test_suite/functions/after_vararg.c3 b/test/test_suite/functions/after_vararg.c3 new file mode 100644 index 000000000..8fee2a86b --- /dev/null +++ b/test/test_suite/functions/after_vararg.c3 @@ -0,0 +1,3 @@ +fn void test1(int... x, int) { } // #error: A parameter name was expected +fn void test2(x..., int) { } // #error: A parameter name was expected +extern fn void test3(int a, ..., int y); // #error: C-style varargs cannot be followed by regular parameters. \ No newline at end of file diff --git a/test/test_suite/functions/splat_mingw.c3t b/test/test_suite/functions/splat_mingw.c3t index f4f4fc83f..a88610122 100644 --- a/test/test_suite/functions/splat_mingw.c3t +++ b/test/test_suite/functions/splat_mingw.c3t @@ -40,7 +40,7 @@ fn void test() %6 = bitcast %"int[]"* %indirectarg to i8* %7 = bitcast %"int[]"* %vararg to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %6, i8* align 8 %7, i32 16, i1 false) - %8 = call i32 @sum_us(%"int[]"* %indirectarg) + %8 = call i32 @sum_us(%"int[]"* align 8 %indirectarg) %9 = bitcast [3 x i32]* %x to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %9, i8* align 4 bitcast ([3 x i32]* @.__const to i8*), i32 12, i1 false) %10 = bitcast [3 x i32]* %x to i32* @@ -55,17 +55,17 @@ fn void test() %16 = bitcast %"int[]"* %indirectarg2 to i8* %17 = bitcast %"int[]"* %vararg1 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %16, i8* align 8 %17, i32 16, i1 false) - %18 = call i32 @sum_us(%"int[]"* %indirectarg2) + %18 = call i32 @sum_us(%"int[]"* align 8 %indirectarg2) %19 = getelementptr inbounds %"int[]", %"int[]"* %vararg3, i32 0, i32 1 %20 = getelementptr inbounds %"int[]", %"int[]"* %vararg3, i32 0, i32 0 %21 = bitcast %"int[]"* %indirectarg4 to i8* %22 = bitcast %"int[]"* %z to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %21, i8* align 8 %22, i32 16, i1 false) - %23 = call i32 @sum_us(%"int[]"* %indirectarg4) + %23 = call i32 @sum_us(%"int[]"* align 8 %indirectarg4) %24 = getelementptr inbounds %"int[]", %"int[]"* %vararg5, i32 0, i32 1 store i64 0, i64* %24, align 8 %25 = bitcast %"int[]"* %indirectarg6 to i8* %26 = bitcast %"int[]"* %vararg5 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %25, i8* align 8 %26, i32 16, i1 false) - %27 = call i32 @sum_us(%"int[]"* %indirectarg6) + %27 = call i32 @sum_us(%"int[]"* align 8 %indirectarg6) ret void diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 8c706a992..ad5341fbe 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -156,12 +156,12 @@ fn void main() Foo ddx; int fro = 3; int[4] x = { 1, 2, 3, 3 }; - fro += printf("1Vararg4unsplatA: %d\n", sum_us(...x)); + fro += printf("1Vararg4splatA: %d\n", sum_us(...x)); printf("%d\n", fro); int[] z = &x; int[3] de = { 1, 2, 3 }; - printf("Vararg4unsplatB: %d\n", sum_us(...&x)); - printf("Vararg4unsplatC: %d\n", sum_us(...z)); + printf("Vararg4splatB: %d\n", sum_us(...&x)); + printf("Vararg4splatC: %d\n", sum_us(...z)); printf("Vararg4: %d\n", sum_us(1, 2, 4, 5)); printf("Vararg1: %d\n", sum_us(1)); printf("Vararg0: %d\n", sum_us()); @@ -561,7 +561,7 @@ loop.exit8: ; preds = %loop.cond2 %46 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %44, i32 0, i32 1 %hi = load i64, i64* %46, align 8 %47 = call i32 @test_sum_us(i8* %lo, i64 %hi) - %48 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.13, i32 0, i32 0), i32 %47) + %48 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.13, i32 0, i32 0), i32 %47) %add9 = add i32 %40, %48 store i32 %add9, i32* %fro, align 4 %49 = load i32, i32* %fro, align 4 @@ -583,7 +583,7 @@ loop.exit8: ; preds = %loop.cond2 %60 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %58, i32 0, i32 1 %hi12 = load i64, i64* %60, align 8 %61 = call i32 @test_sum_us(i8* %lo11, i64 %hi12) - %62 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.16, i32 0, i32 0), i32 %61) + %62 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.16, i32 0, i32 0), i32 %61) %63 = getelementptr inbounds %"int[]", %"int[]"* %vararg13, i32 0, i32 1 %64 = getelementptr inbounds %"int[]", %"int[]"* %vararg13, i32 0, i32 0 %65 = bitcast %"int[]"* %z to { i8*, i64 }* @@ -592,7 +592,7 @@ loop.exit8: ; preds = %loop.cond2 %67 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %65, i32 0, i32 1 %hi15 = load i64, i64* %67, align 8 %68 = call i32 @test_sum_us(i8* %lo14, i64 %hi15) - %69 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.17, i32 0, i32 0), i32 %68) + %69 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.17, i32 0, i32 0), i32 %68) %70 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 0 store i32 1, i32* %70, align 4 %71 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 1 diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index 1f572b7f5..d09250f0f 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -158,12 +158,12 @@ fn void main() Foo ddx; int fro = 3; int[4] x = { 1, 2, 3, 3 }; - fro += printf("1Vararg4unsplatA: %d\n", sum_us(...x)); + fro += printf("1Vararg4splatA: %d\n", sum_us(...x)); printf("%d\n", fro); int[] z = &x; int[3] de = { 1, 2, 3 }; - printf("Vararg4unsplatB: %d\n", sum_us(...&x)); - printf("Vararg4unsplatC: %d\n", sum_us(...z)); + printf("Vararg4splatB: %d\n", sum_us(...&x)); + printf("Vararg4splatC: %d\n", sum_us(...z)); printf("Vararg4: %d\n", sum_us(1, 2, 4, 5)); printf("Vararg1: %d\n", sum_us(1)); printf("Vararg0: %d\n", sum_us()); @@ -282,11 +282,11 @@ $"ct$test_MyEnum" = comdat any @.str.10 = private unnamed_addr constant [17 x i8] c"Mult int was %d\0A\00", align 1 @.str.11 = private unnamed_addr constant [20 x i8] c"Mult double was %f\0A\00", align 1 @.__const.12 = private unnamed_addr constant [4 x i32] [i32 1, i32 2, i32 3, i32 3], align 16 -@.str.13 = private unnamed_addr constant [22 x i8] c"1Vararg4unsplatA: %d\0A\00", align 1 +@.str.13 = private unnamed_addr constant [20 x i8] c"1Vararg4splatA: %d\0A\00", align 1 @.str.14 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @.__const.15 = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 3], align 4 -@.str.16 = private unnamed_addr constant [21 x i8] c"Vararg4unsplatB: %d\0A\00", align 1 -@.str.17 = private unnamed_addr constant [21 x i8] c"Vararg4unsplatC: %d\0A\00", align 1 +@.str.16 = private unnamed_addr constant [19 x i8] c"Vararg4splatB: %d\0A\00", align 1 +@.str.17 = private unnamed_addr constant [19 x i8] c"Vararg4splatC: %d\0A\00", align 1 @.str.18 = private unnamed_addr constant [13 x i8] c"Vararg4: %d\0A\00", align 1 @.str.19 = private unnamed_addr constant [13 x i8] c"Vararg1: %d\0A\00", align 1 @.str.20 = private unnamed_addr constant [13 x i8] c"Vararg0: %d\0A\00", align 1 @@ -414,7 +414,7 @@ if.exit: ; preds = %entry %15 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 1 %16 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 store %"int[]" %14, %"int[]"* %indirectarg, align 8 - %17 = call i32 @test_sum_us(%"int[]"* %indirectarg) + %17 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg) %add = add i32 %8, %17 %add2 = add i32 %5, %add store i32 %add2, i32* %sum, align 4 @@ -598,8 +598,8 @@ loop.exit8: ; preds = %loop.cond2 %44 = bitcast %"int[]"* %indirectarg to i8* %45 = bitcast %"int[]"* %vararg to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %44, i8* align 8 %45, i32 16, i1 false) - %46 = call i32 @test_sum_us(%"int[]"* %indirectarg) - %47 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.13, i32 0, i32 0), i32 %46) + %46 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg) + %47 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.13, i32 0, i32 0), i32 %46) %add9 = add i32 %40, %47 store i32 %add9, i32* %fro, align 4 %48 = load i32, i32* %fro, align 4 @@ -618,15 +618,15 @@ loop.exit8: ; preds = %loop.cond2 %57 = bitcast %"int[]"* %indirectarg11 to i8* %58 = bitcast %"int[]"* %vararg10 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %57, i8* align 8 %58, i32 16, i1 false) - %59 = call i32 @test_sum_us(%"int[]"* %indirectarg11) - %60 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.16, i32 0, i32 0), i32 %59) + %59 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg11) + %60 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.16, i32 0, i32 0), i32 %59) %61 = getelementptr inbounds %"int[]", %"int[]"* %vararg12, i32 0, i32 1 %62 = getelementptr inbounds %"int[]", %"int[]"* %vararg12, i32 0, i32 0 %63 = bitcast %"int[]"* %indirectarg13 to i8* %64 = bitcast %"int[]"* %z to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %63, i8* align 8 %64, i32 16, i1 false) - %65 = call i32 @test_sum_us(%"int[]"* %indirectarg13) - %66 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.17, i32 0, i32 0), i32 %65) + %65 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg13) + %66 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.17, i32 0, i32 0), i32 %65) %67 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 0 store i32 1, i32* %67, align 4 %68 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 1 @@ -643,7 +643,7 @@ loop.exit8: ; preds = %loop.cond2 %74 = bitcast %"int[]"* %indirectarg15 to i8* %75 = bitcast %"int[]"* %vararg14 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %74, i8* align 8 %75, i32 16, i1 false) - %76 = call i32 @test_sum_us(%"int[]"* %indirectarg15) + %76 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg15) %77 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.18, i32 0, i32 0), i32 %76) %78 = getelementptr inbounds [1 x i32], [1 x i32]* %varargslots17, i64 0, i64 0 store i32 1, i32* %78, align 4 @@ -655,14 +655,14 @@ loop.exit8: ; preds = %loop.cond2 %82 = bitcast %"int[]"* %indirectarg18 to i8* %83 = bitcast %"int[]"* %vararg16 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %82, i8* align 8 %83, i32 16, i1 false) - %84 = call i32 @test_sum_us(%"int[]"* %indirectarg18) + %84 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg18) %85 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.19, i32 0, i32 0), i32 %84) %86 = getelementptr inbounds %"int[]", %"int[]"* %vararg19, i32 0, i32 1 store i64 0, i64* %86, align 8 %87 = bitcast %"int[]"* %indirectarg20 to i8* %88 = bitcast %"int[]"* %vararg19 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %87, i8* align 8 %88, i32 16, i1 false) - %89 = call i32 @test_sum_us(%"int[]"* %indirectarg20) + %89 = call i32 @test_sum_us(%"int[]"* align 8 %indirectarg20) %90 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.20, i32 0, i32 0), i32 %89) store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %a1, align 8 store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %b2, align 8 diff --git a/test/test_suite/functions/vararg_and_named_tests.c3 b/test/test_suite/functions/vararg_and_named_tests.c3 new file mode 100644 index 000000000..1c4ee62c7 --- /dev/null +++ b/test/test_suite/functions/vararg_and_named_tests.c3 @@ -0,0 +1,53 @@ +// #target: macos-x64 +module foo; + +extern fn void printf(char* format, ...); + +fn void test(int x, int... y, int z = 2) +{ +} + +fn void test2(int x, int... y, int z) +{ +} + +fn void a() +{ + test(.z = 32, 3); // #error: Named arguments must be placed after positional arguments +} + +fn void b() +{ + test(1, .x = 3); // #error: The parameter 'x' was already set. +} + +fn void c() +{ + test(1); +} + +fn void d() +{ + test2(1, .z = 3); + test2(1, 2, 3); // #error: Expected '.z = ...' after this argument +} + + +fn void single(int x) {} + +fn void e() +{ + single(); // #error: This call expected a parameter of type 'int' +} + +fn void multiple(int x, int y) {} + +fn void f() +{ + multiple(1); // #error: Expected 1 more argument after this one +} + +fn void g() +{ + multiple(); // #error: 'multiple' expects 2 parameters, but none was provided +} \ No newline at end of file diff --git a/test/test_suite/functions/vararg_argument_fails.c3 b/test/test_suite/functions/vararg_argument_fails.c3 index 9e865b473..1a7916389 100644 --- a/test/test_suite/functions/vararg_argument_fails.c3 +++ b/test/test_suite/functions/vararg_argument_fails.c3 @@ -1,5 +1,5 @@ -fn void foo5(..., ...) {} // #error: Only a single vararg parameter is allowed +fn void foo5(..., ...) {} // #error: Only a single variadic parameter is allowed -fn void foo6(int ..., int ...) {} // #error: Only a single vararg parameter is allowed +fn void foo6(int ..., int ...) {} // #error: Only a single variadic parameter is allowed -fn void foo7(int... x, int ... y) {} // #error: Only a single vararg parameter is allowed \ No newline at end of file +fn void foo7(int... x, int ... y) {} // #error: Only a single variadic parameter is allowed \ No newline at end of file diff --git a/test/test_suite/functions/varargs_followed_by_named.c3t b/test/test_suite/functions/varargs_followed_by_named.c3t new file mode 100644 index 000000000..a5515e295 --- /dev/null +++ b/test/test_suite/functions/varargs_followed_by_named.c3t @@ -0,0 +1,153 @@ +// #target: macos-x64 +module foo; + +extern fn void printf(char* format, ...); + +fn void test(int x, int... y, int z = 2) +{ + printf("Got %d %d %d %d\n", x, (int)y.len, z, y[0]); +} +fn void test2(int x, y..., int z = 2) +{ + printf("Got %d %d %d\n", x, (int)y.len, z); +} + +fn void main() +{ + test(3, 4, 5, .z = 123); + test2(3, 4, 5, .z = 123); + test(3, 4, 5); + test2(3, 4, 5); +} + +/* #expect: foo.ll + + +define void @foo_test(i32 %0, i8* %1, i64 %2, i32 %3) #0 { +entry: + %y = alloca %"int[]", align 8 + %pair = bitcast %"int[]"* %y to { i8*, i64 }* + %4 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %1, i8** %4, align 8 + %5 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %2, i64* %5, align 8 + %6 = getelementptr inbounds %"int[]", %"int[]"* %y, i32 0, i32 1 + %7 = load i64, i64* %6, align 8 + %uisitrunc = trunc i64 %7 to i32 + %8 = getelementptr inbounds %"int[]", %"int[]"* %y, i32 0, i32 0 + %9 = load i32*, i32** %8, align 8 + %ptroffset = getelementptr inbounds i32, i32* %9, i64 0 + %10 = load i32, i32* %ptroffset, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str, i32 0, i32 0), i32 %0, i32 %uisitrunc, i32 %3, i32 %10) + ret void +} + +define void @foo_test2(i32 %0, i8* %1, i64 %2, i32 %3) #0 { +entry: + %y = alloca %"variant[]", align 8 + %pair = bitcast %"variant[]"* %y to { i8*, i64 }* + %4 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %1, i8** %4, align 8 + %5 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %2, i64* %5, align 8 + %6 = getelementptr inbounds %"variant[]", %"variant[]"* %y, i32 0, i32 1 + %7 = load i64, i64* %6, align 8 + %uisitrunc = trunc i64 %7 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.1, i32 0, i32 0), i32 %0, i32 %uisitrunc, i32 %3) + ret void +} + +define void @foo_main() #0 { +entry: + %vararg = alloca %"int[]", align 8 + %varargslots = alloca [2 x i32], align 4 + %vararg1 = alloca %"variant[]", align 8 + %varargslots2 = alloca [2 x %variant], align 16 + %taddr = alloca i32, align 4 + %taddr3 = alloca i32, align 4 + %vararg6 = alloca %"int[]", align 8 + %varargslots7 = alloca [2 x i32], align 4 + %vararg10 = alloca %"variant[]", align 8 + %varargslots11 = alloca [2 x %variant], align 16 + %taddr12 = alloca i32, align 4 + %taddr13 = alloca i32, align 4 + %0 = getelementptr inbounds [2 x i32], [2 x i32]* %varargslots, i64 0, i64 0 + store i32 4, i32* %0, align 4 + %1 = getelementptr inbounds [2 x i32], [2 x i32]* %varargslots, i64 0, i64 1 + store i32 5, i32* %1, align 4 + %2 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 1 + store i64 2, i64* %2, align 8 + %3 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 + %4 = bitcast [2 x i32]* %varargslots to i32* + store i32* %4, i32** %3, align 8 + %5 = bitcast %"int[]"* %vararg to { i8*, i64 }* + %6 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %5, i32 0, i32 0 + %lo = load i8*, i8** %6, align 8 + %7 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %5, i32 0, i32 1 + %hi = load i64, i64* %7, align 8 + call void @foo_test(i32 3, i8* %lo, i64 %hi, i32 123) + store i32 4, i32* %taddr, align 4 + %8 = bitcast i32* %taddr to i8* + %9 = insertvalue %variant undef, i8* %8, 0 + %10 = insertvalue %variant %9, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %11 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots2, i64 0, i64 0 + store %variant %10, %variant* %11, align 16 + store i32 5, i32* %taddr3, align 4 + %12 = bitcast i32* %taddr3 to i8* + %13 = insertvalue %variant undef, i8* %12, 0 + %14 = insertvalue %variant %13, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %15 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots2, i64 0, i64 1 + store %variant %14, %variant* %15, align 16 + %16 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg1, i32 0, i32 1 + store i64 2, i64* %16, align 8 + %17 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg1, i32 0, i32 0 + %18 = bitcast [2 x %variant]* %varargslots2 to %variant* + store %variant* %18, %variant** %17, align 8 + %19 = bitcast %"variant[]"* %vararg1 to { i8*, i64 }* + %20 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %19, i32 0, i32 0 + %lo4 = load i8*, i8** %20, align 8 + %21 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %19, i32 0, i32 1 + %hi5 = load i64, i64* %21, align 8 + call void @foo_test2(i32 3, i8* %lo4, i64 %hi5, i32 123) + %22 = getelementptr inbounds [2 x i32], [2 x i32]* %varargslots7, i64 0, i64 0 + store i32 4, i32* %22, align 4 + %23 = getelementptr inbounds [2 x i32], [2 x i32]* %varargslots7, i64 0, i64 1 + store i32 5, i32* %23, align 4 + %24 = getelementptr inbounds %"int[]", %"int[]"* %vararg6, i32 0, i32 1 + store i64 2, i64* %24, align 8 + %25 = getelementptr inbounds %"int[]", %"int[]"* %vararg6, i32 0, i32 0 + %26 = bitcast [2 x i32]* %varargslots7 to i32* + store i32* %26, i32** %25, align 8 + %27 = bitcast %"int[]"* %vararg6 to { i8*, i64 }* + %28 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %27, i32 0, i32 0 + %lo8 = load i8*, i8** %28, align 8 + %29 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %27, i32 0, i32 1 + %hi9 = load i64, i64* %29, align 8 + call void @foo_test(i32 3, i8* %lo8, i64 %hi9, i32 2) + store i32 4, i32* %taddr12, align 4 + %30 = bitcast i32* %taddr12 to i8* + %31 = insertvalue %variant undef, i8* %30, 0 + %32 = insertvalue %variant %31, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %33 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots11, i64 0, i64 0 + store %variant %32, %variant* %33, align 16 + store i32 5, i32* %taddr13, align 4 + %34 = bitcast i32* %taddr13 to i8* + %35 = insertvalue %variant undef, i8* %34, 0 + %36 = insertvalue %variant %35, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %37 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots11, i64 0, i64 1 + store %variant %36, %variant* %37, align 16 + %38 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg10, i32 0, i32 1 + store i64 2, i64* %38, align 8 + %39 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg10, i32 0, i32 0 + %40 = bitcast [2 x %variant]* %varargslots11 to %variant* + store %variant* %40, %variant** %39, align 8 + %41 = bitcast %"variant[]"* %vararg10 to { i8*, i64 }* + %42 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %41, i32 0, i32 0 + %lo14 = load i8*, i8** %42, align 8 + %43 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %41, i32 0, i32 1 + %hi15 = load i64, i64* %43, align 8 + call void @foo_test2(i32 3, i8* %lo14, i64 %hi15, i32 2) + ret void +} + + diff --git a/test/test_suite2/functions/after_vararg.c3 b/test/test_suite2/functions/after_vararg.c3 new file mode 100644 index 000000000..8fee2a86b --- /dev/null +++ b/test/test_suite2/functions/after_vararg.c3 @@ -0,0 +1,3 @@ +fn void test1(int... x, int) { } // #error: A parameter name was expected +fn void test2(x..., int) { } // #error: A parameter name was expected +extern fn void test3(int a, ..., int y); // #error: C-style varargs cannot be followed by regular parameters. \ No newline at end of file diff --git a/test/test_suite2/functions/splat_mingw.c3t b/test/test_suite2/functions/splat_mingw.c3t index 7c0d91360..dccf70884 100644 --- a/test/test_suite2/functions/splat_mingw.c3t +++ b/test/test_suite2/functions/splat_mingw.c3t @@ -39,7 +39,7 @@ entry: %4 = getelementptr inbounds %"int[]", ptr %vararg, i32 0, i32 0 store ptr %varargslots, ptr %4, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg, ptr align 8 %vararg, i32 16, i1 false) - %5 = call i32 @sum_us(ptr %indirectarg) + %5 = call i32 @sum_us(ptr align 8 %indirectarg) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %x, ptr align 4 @.__const, i32 12, i1 false) %6 = insertvalue %"int[]" undef, ptr %x, 0 %7 = insertvalue %"int[]" %6, i64 3, 1 @@ -49,13 +49,13 @@ entry: store i64 3, ptr %8, align 8 store ptr %x, ptr %9, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg2, ptr align 8 %vararg1, i32 16, i1 false) - %10 = call i32 @sum_us(ptr %indirectarg2) + %10 = call i32 @sum_us(ptr align 8 %indirectarg2) %11 = getelementptr inbounds %"int[]", ptr %vararg3, i32 0, i32 1 %12 = getelementptr inbounds %"int[]", ptr %vararg3, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg4, ptr align 8 %z, i32 16, i1 false) - %13 = call i32 @sum_us(ptr %indirectarg4) + %13 = call i32 @sum_us(ptr align 8 %indirectarg4) %14 = getelementptr inbounds %"int[]", ptr %vararg5, i32 0, i32 1 store i64 0, ptr %14, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg6, ptr align 8 %vararg5, i32 16, i1 false) - %15 = call i32 @sum_us(ptr %indirectarg6) + %15 = call i32 @sum_us(ptr align 8 %indirectarg6) ret void diff --git a/test/test_suite2/functions/test_regression.c3t b/test/test_suite2/functions/test_regression.c3t index 7b6ea6f8b..7681ad6ee 100644 --- a/test/test_suite2/functions/test_regression.c3t +++ b/test/test_suite2/functions/test_regression.c3t @@ -156,12 +156,12 @@ fn void main() Foo ddx; int fro = 3; int[4] x = { 1, 2, 3, 3 }; - fro += printf("1Vararg4unsplatA: %d\n", sum_us(...x)); + fro += printf("1Vararg4splatA: %d\n", sum_us(...x)); printf("%d\n", fro); int[] z = &x; int[3] de = { 1, 2, 3 }; - printf("Vararg4unsplatB: %d\n", sum_us(...&x)); - printf("Vararg4unsplatC: %d\n", sum_us(...z)); + printf("Vararg4splatB: %d\n", sum_us(...&x)); + printf("Vararg4splatC: %d\n", sum_us(...z)); printf("Vararg4: %d\n", sum_us(1, 2, 4, 5)); printf("Vararg1: %d\n", sum_us(1)); printf("Vararg0: %d\n", sum_us()); diff --git a/test/test_suite2/functions/test_regression_mingw.c3t b/test/test_suite2/functions/test_regression_mingw.c3t index 96ada348c..d8bebbc7e 100644 --- a/test/test_suite2/functions/test_regression_mingw.c3t +++ b/test/test_suite2/functions/test_regression_mingw.c3t @@ -158,12 +158,12 @@ fn void main() Foo ddx; int fro = 3; int[4] x = { 1, 2, 3, 3 }; - fro += printf("1Vararg4unsplatA: %d\n", sum_us(...x)); + fro += printf("1Vararg4splatA: %d\n", sum_us(...x)); printf("%d\n", fro); int[] z = &x; int[3] de = { 1, 2, 3 }; - printf("Vararg4unsplatB: %d\n", sum_us(...&x)); - printf("Vararg4unsplatC: %d\n", sum_us(...z)); + printf("Vararg4splatB: %d\n", sum_us(...&x)); + printf("Vararg4splatC: %d\n", sum_us(...z)); printf("Vararg4: %d\n", sum_us(1, 2, 4, 5)); printf("Vararg1: %d\n", sum_us(1)); printf("Vararg0: %d\n", sum_us()); @@ -281,11 +281,11 @@ $"ct$test_MyEnum" = comdat any @.str.10 = private unnamed_addr constant [17 x i8] c"Mult int was %d\0A\00", align 1 @.str.11 = private unnamed_addr constant [20 x i8] c"Mult double was %f\0A\00", align 1 @.__const.12 = private unnamed_addr constant [4 x i32] [i32 1, i32 2, i32 3, i32 3], align 16 -@.str.13 = private unnamed_addr constant [22 x i8] c"1Vararg4unsplatA: %d\0A\00", align 1 +@.str.13 = private unnamed_addr constant [20 x i8] c"1Vararg4splatA: %d\0A\00", align 1 @.str.14 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @.__const.15 = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 3], align 4 -@.str.16 = private unnamed_addr constant [21 x i8] c"Vararg4unsplatB: %d\0A\00", align 1 -@.str.17 = private unnamed_addr constant [21 x i8] c"Vararg4unsplatC: %d\0A\00", align 1 +@.str.16 = private unnamed_addr constant [19 x i8] c"Vararg4splatB: %d\0A\00", align 1 +@.str.17 = private unnamed_addr constant [19 x i8] c"Vararg4splatC: %d\0A\00", align 1 @.str.18 = private unnamed_addr constant [13 x i8] c"Vararg4: %d\0A\00", align 1 @.str.19 = private unnamed_addr constant [13 x i8] c"Vararg1: %d\0A\00", align 1 @.str.20 = private unnamed_addr constant [13 x i8] c"Vararg0: %d\0A\00", align 1 @@ -404,7 +404,7 @@ if.exit: ; preds = %entry %13 = getelementptr inbounds %"int[]", ptr %vararg, i32 0, i32 1 %14 = getelementptr inbounds %"int[]", ptr %vararg, i32 0, i32 0 store %"int[]" %12, ptr %indirectarg, align 8 - %15 = call i32 @test_sum_us(ptr %indirectarg) + %15 = call i32 @test_sum_us(ptr align 8 %indirectarg) %add = add i32 %6, %15 %add2 = add i32 %3, %add store i32 %add2, ptr %sum, align 4 @@ -576,7 +576,7 @@ loop.exit8: ; preds = %loop.cond2 store i64 4, ptr %34, align 8 store ptr %x, ptr %35, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg, ptr align 8 %vararg, i32 16, i1 false) - %36 = call i32 @test_sum_us(ptr %indirectarg) + %36 = call i32 @test_sum_us(ptr align 8 %indirectarg) %37 = call i32 (ptr, ...) @printf(ptr @.str.13, i32 %36) %add9 = add i32 %33, %37 store i32 %add9, ptr %fro, align 4 @@ -591,12 +591,12 @@ loop.exit8: ; preds = %loop.cond2 store i64 4, ptr %42, align 8 store ptr %x, ptr %43, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg11, ptr align 8 %vararg10, i32 16, i1 false) - %44 = call i32 @test_sum_us(ptr %indirectarg11) + %44 = call i32 @test_sum_us(ptr align 8 %indirectarg11) %45 = call i32 (ptr, ...) @printf(ptr @.str.16, i32 %44) %46 = getelementptr inbounds %"int[]", ptr %vararg12, i32 0, i32 1 %47 = getelementptr inbounds %"int[]", ptr %vararg12, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg13, ptr align 8 %z, i32 16, i1 false) - %48 = call i32 @test_sum_us(ptr %indirectarg13) + %48 = call i32 @test_sum_us(ptr align 8 %indirectarg13) %49 = call i32 (ptr, ...) @printf(ptr @.str.17, i32 %48) %50 = getelementptr inbounds [4 x i32], ptr %varargslots, i64 0, i64 0 store i32 1, ptr %50, align 4 @@ -611,7 +611,7 @@ loop.exit8: ; preds = %loop.cond2 %55 = getelementptr inbounds %"int[]", ptr %vararg14, i32 0, i32 0 store ptr %varargslots, ptr %55, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg15, ptr align 8 %vararg14, i32 16, i1 false) - %56 = call i32 @test_sum_us(ptr %indirectarg15) + %56 = call i32 @test_sum_us(ptr align 8 %indirectarg15) %57 = call i32 (ptr, ...) @printf(ptr @.str.18, i32 %56) %58 = getelementptr inbounds [1 x i32], ptr %varargslots17, i64 0, i64 0 store i32 1, ptr %58, align 4 @@ -620,12 +620,12 @@ loop.exit8: ; preds = %loop.cond2 %60 = getelementptr inbounds %"int[]", ptr %vararg16, i32 0, i32 0 store ptr %varargslots17, ptr %60, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg18, ptr align 8 %vararg16, i32 16, i1 false) - %61 = call i32 @test_sum_us(ptr %indirectarg18) + %61 = call i32 @test_sum_us(ptr align 8 %indirectarg18) %62 = call i32 (ptr, ...) @printf(ptr @.str.19, i32 %61) %63 = getelementptr inbounds %"int[]", ptr %vararg19, i32 0, i32 1 store i64 0, ptr %63, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %indirectarg20, ptr align 8 %vararg19, i32 16, i1 false) - %64 = call i32 @test_sum_us(ptr %indirectarg20) + %64 = call i32 @test_sum_us(ptr align 8 %indirectarg20) %65 = call i32 (ptr, ...) @printf(ptr @.str.20, i32 %64) store ptr null, ptr %a1, align 8 store ptr null, ptr %b2, align 8 diff --git a/test/test_suite2/functions/vararg_and_named_tests.c3 b/test/test_suite2/functions/vararg_and_named_tests.c3 new file mode 100644 index 000000000..1c4ee62c7 --- /dev/null +++ b/test/test_suite2/functions/vararg_and_named_tests.c3 @@ -0,0 +1,53 @@ +// #target: macos-x64 +module foo; + +extern fn void printf(char* format, ...); + +fn void test(int x, int... y, int z = 2) +{ +} + +fn void test2(int x, int... y, int z) +{ +} + +fn void a() +{ + test(.z = 32, 3); // #error: Named arguments must be placed after positional arguments +} + +fn void b() +{ + test(1, .x = 3); // #error: The parameter 'x' was already set. +} + +fn void c() +{ + test(1); +} + +fn void d() +{ + test2(1, .z = 3); + test2(1, 2, 3); // #error: Expected '.z = ...' after this argument +} + + +fn void single(int x) {} + +fn void e() +{ + single(); // #error: This call expected a parameter of type 'int' +} + +fn void multiple(int x, int y) {} + +fn void f() +{ + multiple(1); // #error: Expected 1 more argument after this one +} + +fn void g() +{ + multiple(); // #error: 'multiple' expects 2 parameters, but none was provided +} \ No newline at end of file diff --git a/test/test_suite2/functions/vararg_argument_fails.c3 b/test/test_suite2/functions/vararg_argument_fails.c3 index 9e865b473..1a7916389 100644 --- a/test/test_suite2/functions/vararg_argument_fails.c3 +++ b/test/test_suite2/functions/vararg_argument_fails.c3 @@ -1,5 +1,5 @@ -fn void foo5(..., ...) {} // #error: Only a single vararg parameter is allowed +fn void foo5(..., ...) {} // #error: Only a single variadic parameter is allowed -fn void foo6(int ..., int ...) {} // #error: Only a single vararg parameter is allowed +fn void foo6(int ..., int ...) {} // #error: Only a single variadic parameter is allowed -fn void foo7(int... x, int ... y) {} // #error: Only a single vararg parameter is allowed \ No newline at end of file +fn void foo7(int... x, int ... y) {} // #error: Only a single variadic parameter is allowed \ No newline at end of file diff --git a/test/test_suite2/functions/varargs_followed_by_named.c3t b/test/test_suite2/functions/varargs_followed_by_named.c3t new file mode 100644 index 000000000..7d9fdc324 --- /dev/null +++ b/test/test_suite2/functions/varargs_followed_by_named.c3t @@ -0,0 +1,136 @@ +// #target: macos-x64 +module foo; + +extern fn void printf(char* format, ...); + +fn void test(int x, int... y, int z = 2) +{ + printf("Got %d %d %d %d\n", x, (int)y.len, z, y[0]); +} +fn void test2(int x, y..., int z = 2) +{ + printf("Got %d %d %d\n", x, (int)y.len, z); +} + +fn void main() +{ + test(3, 4, 5, .z = 123); + test2(3, 4, 5, .z = 123); + test(3, 4, 5); + test2(3, 4, 5); +} + +/* #expect: foo.ll + +define void @foo_test(i32 %0, ptr %1, i64 %2, i32 %3) #0 { +entry: + %y = alloca %"int[]", align 8 + %4 = getelementptr inbounds { ptr, i64 }, ptr %y, i32 0, i32 0 + store ptr %1, ptr %4, align 8 + %5 = getelementptr inbounds { ptr, i64 }, ptr %y, i32 0, i32 1 + store i64 %2, ptr %5, align 8 + %6 = getelementptr inbounds %"int[]", ptr %y, i32 0, i32 1 + %7 = load i64, ptr %6, align 8 + %uisitrunc = trunc i64 %7 to i32 + %8 = getelementptr inbounds %"int[]", ptr %y, i32 0, i32 0 + %9 = load ptr, ptr %8, align 8 + %ptroffset = getelementptr inbounds i32, ptr %9, i64 0 + %10 = load i32, ptr %ptroffset, align 4 + call void (ptr, ...) @printf(ptr @.str, i32 %0, i32 %uisitrunc, i32 %3, i32 %10) + ret void +} + +define void @foo_test2(i32 %0, ptr %1, i64 %2, i32 %3) #0 { +entry: + %y = alloca %"variant[]", align 8 + %4 = getelementptr inbounds { ptr, i64 }, ptr %y, i32 0, i32 0 + store ptr %1, ptr %4, align 8 + %5 = getelementptr inbounds { ptr, i64 }, ptr %y, i32 0, i32 1 + store i64 %2, ptr %5, align 8 + %6 = getelementptr inbounds %"variant[]", ptr %y, i32 0, i32 1 + %7 = load i64, ptr %6, align 8 + %uisitrunc = trunc i64 %7 to i32 + call void (ptr, ...) @printf(ptr @.str.1, i32 %0, i32 %uisitrunc, i32 %3) + ret void +} + +define void @foo_main() #0 { +entry: + %vararg = alloca %"int[]", align 8 + %varargslots = alloca [2 x i32], align 4 + %vararg1 = alloca %"variant[]", align 8 + %varargslots2 = alloca [2 x %variant], align 16 + %taddr = alloca i32, align 4 + %taddr3 = alloca i32, align 4 + %vararg6 = alloca %"int[]", align 8 + %varargslots7 = alloca [2 x i32], align 4 + %vararg10 = alloca %"variant[]", align 8 + %varargslots11 = alloca [2 x %variant], align 16 + %taddr12 = alloca i32, align 4 + %taddr13 = alloca i32, align 4 + %0 = getelementptr inbounds [2 x i32], ptr %varargslots, i64 0, i64 0 + store i32 4, ptr %0, align 4 + %1 = getelementptr inbounds [2 x i32], ptr %varargslots, i64 0, i64 1 + store i32 5, ptr %1, align 4 + %2 = getelementptr inbounds %"int[]", ptr %vararg, i32 0, i32 1 + store i64 2, ptr %2, align 8 + %3 = getelementptr inbounds %"int[]", ptr %vararg, i32 0, i32 0 + store ptr %varargslots, ptr %3, align 8 + %4 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0 + %lo = load ptr, ptr %4, align 8 + %5 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1 + %hi = load i64, ptr %5, align 8 + call void @foo_test(i32 3, ptr %lo, i64 %hi, i32 123) + store i32 4, ptr %taddr, align 4 + %6 = insertvalue %variant undef, ptr %taddr, 0 + %7 = insertvalue %variant %6, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %8 = getelementptr inbounds [2 x %variant], ptr %varargslots2, i64 0, i64 0 + store %variant %7, ptr %8, align 16 + store i32 5, ptr %taddr3, align 4 + %9 = insertvalue %variant undef, ptr %taddr3, 0 + %10 = insertvalue %variant %9, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %11 = getelementptr inbounds [2 x %variant], ptr %varargslots2, i64 0, i64 1 + store %variant %10, ptr %11, align 16 + %12 = getelementptr inbounds %"variant[]", ptr %vararg1, i32 0, i32 1 + store i64 2, ptr %12, align 8 + %13 = getelementptr inbounds %"variant[]", ptr %vararg1, i32 0, i32 0 + store ptr %varargslots2, ptr %13, align 8 + %14 = getelementptr inbounds { ptr, i64 }, ptr %vararg1, i32 0, i32 0 + %lo4 = load ptr, ptr %14, align 8 + %15 = getelementptr inbounds { ptr, i64 }, ptr %vararg1, i32 0, i32 1 + %hi5 = load i64, ptr %15, align 8 + call void @foo_test2(i32 3, ptr %lo4, i64 %hi5, i32 123) + %16 = getelementptr inbounds [2 x i32], ptr %varargslots7, i64 0, i64 0 + store i32 4, ptr %16, align 4 + %17 = getelementptr inbounds [2 x i32], ptr %varargslots7, i64 0, i64 1 + store i32 5, ptr %17, align 4 + %18 = getelementptr inbounds %"int[]", ptr %vararg6, i32 0, i32 1 + store i64 2, ptr %18, align 8 + %19 = getelementptr inbounds %"int[]", ptr %vararg6, i32 0, i32 0 + store ptr %varargslots7, ptr %19, align 8 + %20 = getelementptr inbounds { ptr, i64 }, ptr %vararg6, i32 0, i32 0 + %lo8 = load ptr, ptr %20, align 8 + %21 = getelementptr inbounds { ptr, i64 }, ptr %vararg6, i32 0, i32 1 + %hi9 = load i64, ptr %21, align 8 + call void @foo_test(i32 3, ptr %lo8, i64 %hi9, i32 2) + store i32 4, ptr %taddr12, align 4 + %22 = insertvalue %variant undef, ptr %taddr12, 0 + %23 = insertvalue %variant %22, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %24 = getelementptr inbounds [2 x %variant], ptr %varargslots11, i64 0, i64 0 + store %variant %23, ptr %24, align 16 + store i32 5, ptr %taddr13, align 4 + %25 = insertvalue %variant undef, ptr %taddr13, 0 + %26 = insertvalue %variant %25, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %27 = getelementptr inbounds [2 x %variant], ptr %varargslots11, i64 0, i64 1 + store %variant %26, ptr %27, align 16 + %28 = getelementptr inbounds %"variant[]", ptr %vararg10, i32 0, i32 1 + store i64 2, ptr %28, align 8 + %29 = getelementptr inbounds %"variant[]", ptr %vararg10, i32 0, i32 0 + store ptr %varargslots11, ptr %29, align 8 + %30 = getelementptr inbounds { ptr, i64 }, ptr %vararg10, i32 0, i32 0 + %lo14 = load ptr, ptr %30, align 8 + %31 = getelementptr inbounds { ptr, i64 }, ptr %vararg10, i32 0, i32 1 + %hi15 = load i64, ptr %31, align 8 + call void @foo_test2(i32 3, ptr %lo14, i64 %hi15, i32 2) + ret void +} \ No newline at end of file