From 6b1370ba76cf6bf70946ca7eadf17cd7d45a5071 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 25 Aug 2022 18:10:50 +0200 Subject: [PATCH] Support varargs for macros. Bugfix member access. Support macro varargs. --- src/compiler/compiler_internal.h | 118 ++--- src/compiler/context.c | 4 +- src/compiler/copying.c | 22 +- src/compiler/enums.h | 4 +- src/compiler/llvm_codegen.c | 5 +- src/compiler/llvm_codegen_debug_info.c | 2 +- src/compiler/llvm_codegen_expr.c | 17 +- src/compiler/llvm_codegen_function.c | 6 +- src/compiler/parse_expr.c | 9 +- src/compiler/parse_global.c | 63 +-- src/compiler/parse_stmt.c | 2 - src/compiler/parser_internal.h | 2 +- src/compiler/sema_decls.c | 437 ++++++++--------- src/compiler/sema_expr.c | 429 ++++++++-------- src/compiler/sema_internal.h | 1 + src/compiler/sema_name_resolution.c | 13 +- src/compiler/sema_stmts.c | 35 +- src/compiler/sema_types.c | 15 + src/compiler/semantic_analyser.c | 4 +- src/compiler/tokens.c | 2 +- src/compiler/types.c | 20 +- test/test_suite/compile_time/ct_for.c3t | 2 +- test/test_suite/functions/macro_arguments.c3 | 4 +- test/test_suite/macros/macro_calls_prefix.c3 | 6 +- .../test_suite/macros/macro_typed_varargs.c3t | 210 ++++++++ .../macros/macro_untyped_varargs.c3 | 25 + .../macros/macro_untyped_varargs_2.c3t | 463 ++++++++++++++++++ test/test_suite2/compile_time/ct_for.c3t | 2 +- test/test_suite2/functions/macro_arguments.c3 | 4 +- test/test_suite2/macros/macro_calls_prefix.c3 | 6 +- .../macros/macro_typed_varargs.c3t | 191 ++++++++ .../macros/macro_untyped_varargs.c3 | 25 + .../macros/macro_untyped_varargs_2.c3t | 421 ++++++++++++++++ 33 files changed, 1959 insertions(+), 610 deletions(-) create mode 100644 test/test_suite/macros/macro_typed_varargs.c3t create mode 100644 test/test_suite/macros/macro_untyped_varargs.c3 create mode 100644 test/test_suite/macros/macro_untyped_varargs_2.c3t create mode 100644 test/test_suite2/macros/macro_typed_varargs.c3t create mode 100644 test/test_suite2/macros/macro_untyped_varargs.c3 create mode 100644 test/test_suite2/macros/macro_untyped_varargs_2.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d43a88555..c8e3986dc 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -46,7 +46,9 @@ typedef struct Expr_ Expr; typedef struct Module_ Module; typedef struct Type_ Type; typedef Type CanonicalType; - +typedef struct Signature_ Signature; +typedef struct ConstInitializer_ ConstInitializer; +typedef struct CompilationUnit_ CompilationUnit; typedef unsigned AstId; typedef unsigned ExprId; typedef unsigned DeclId; @@ -88,32 +90,32 @@ typedef enum -typedef struct ConstInitializer_ +struct ConstInitializer_ { ConstInitType kind; // Type initialized Type *type; union { - struct ConstInitializer_ **init_struct; + ConstInitializer **init_struct; Expr *init_value; struct { - struct ConstInitializer_ *element; + ConstInitializer *element; MemberIndex index; } init_union; struct { - struct ConstInitializer_ **elements; + ConstInitializer **elements; } init_array; - struct ConstInitializer_ **init_array_full; + ConstInitializer **init_array_full; struct { - struct ConstInitializer_ *element; + ConstInitializer *element; MemberIndex index; } init_array_value; }; -} ConstInitializer; +}; typedef struct { @@ -169,8 +171,6 @@ typedef union static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes"); - - typedef struct { const char *key; @@ -225,14 +225,15 @@ typedef struct typedef struct { bool nodiscard : 1; - bool maydiscard: 1; -} FunctionAttributes; + bool maydiscard : 1; + bool is_pure : 1; + bool noreturn : 1; +} CalleeAttributes; typedef struct { - FunctionAttributes attrs; Module *module; - Decl** params; + Signature *signature; struct FunctionPrototype_ *prototype; } TypeFunction; @@ -443,36 +444,53 @@ typedef enum -typedef struct FunctionSignature_ +struct Signature_ { - FunctionAttributes attrs; + CalleeAttributes attrs; + bool is_macro : 1; + bool is_at_macro : 1; Variadic variadic : 3; - unsigned vararg_index : 10; - bool use_win64 : 1; - bool is_pure : 1; CallABI abi : 8; - TypeInfoId returntype; + unsigned vararg_index; + TypeInfoId rtype; Decl** params; -} FunctionSignature; +}; typedef struct { - struct - { - bool attr_noreturn : 1; - bool attr_inline : 1; - bool attr_noinline : 1; - bool attr_extname : 1; - bool attr_naked : 1; - }; TypeInfoId type_parent; - FunctionSignature function_signature; + Signature signature; AstId body; AstId docs; + union + { + struct + { + bool attr_inline : 1; + bool attr_noinline : 1; + bool attr_extname : 1; + bool attr_naked : 1; + }; + struct + { + DeclId body_param; + CompilationUnit *unit; + }; + }; } FuncDecl; +typedef struct +{ + TypeInfoId type_parent; // May be 0 + Signature signature; + AstId body; + DeclId body_param; + CompilationUnit *unit; + AstId docs; +} MacroDecl; + typedef struct { Decl **params; @@ -485,7 +503,7 @@ typedef struct bool is_distinct : 1; union { - FunctionSignature function_signature; + Signature function_signature; TypeInfo *type_info; }; } TypedefDecl; @@ -500,33 +518,7 @@ typedef struct }; } DistinctDecl; -typedef struct -{ - struct - { - unsigned vararg_index : 10; - Variadic variadic : 3; - bool attr_noreturn : 1; - bool attr_nodiscard : 1; - bool attr_maydiscard : 1; - }; - TypeInfoId type_parent; // May be 0 - TypeInfoId rtype; // May be 0 - AstId body; - DeclId body_param; - Decl **parameters; - struct CompilationUnit_ *unit; - AstId docs; -} MacroDecl; -typedef struct -{ - struct Ast_ **cases; - Decl **parameters; - Decl **body_parameters; - TypeInfoId rtype; // May be 0! - Path *path; // For redefinition -} GenericDecl; typedef enum { @@ -645,8 +637,6 @@ typedef struct Decl_ FuncDecl func_decl; AttrDecl attr_decl; TypedefDecl typedef_decl; - MacroDecl macro_decl; - GenericDecl generic_decl; DefineDecl define_decl; CtIfDecl ct_if_decl; CtIfDecl ct_elif_decl; @@ -845,7 +835,7 @@ typedef struct typedef struct { - TokenType type : 8; + TokenType type : 16; ExprId arg; } ExprCtArg; @@ -883,7 +873,6 @@ typedef struct typedef struct { AstId first_stmt; - Expr **args; Decl **params; BlockExit **block_exit; } ExprMacroBlock; @@ -1418,7 +1407,7 @@ typedef struct DeclId *entries; } DeclTable; -typedef struct CompilationUnit_ +struct CompilationUnit_ { Module *module; File* file; @@ -1449,7 +1438,7 @@ typedef struct CompilationUnit_ void *debug_file; void *debug_compile_unit; } llvm; -} CompilationUnit; +}; typedef struct ParseContext_ { @@ -1494,6 +1483,7 @@ typedef struct SemaContext_ // Reusable returns cache. Ast **returns_cache; Expr **macro_varargs; + Decl **macro_params; }; Type *rtype; struct SemaContext_ *yield_context; @@ -2137,7 +2127,7 @@ void type_func_prototype_init(uint32_t capacity); bool type_is_subtype(Type *type, Type *possible_subtype); bool type_is_abi_aggregate(Type *type); bool type_is_int128(Type *type); -Type *type_get_func(FunctionSignature *signature, CallABI abi); +Type *type_get_func(Signature *signature, CallABI abi); Type *type_from_token(TokenType type); bool type_is_user_defined(Type *type); bool type_is_structurally_equivalent(Type *type1, Type *type); diff --git a/src/compiler/context.c b/src/compiler/context.c index b362c58fe..76358ebff 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -175,7 +175,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) break; case DECL_GENERIC: assert(decl->name); - if (decl->macro_decl.type_parent) + if (decl->func_decl.type_parent) { vec_add(unit->generic_methods, decl); return; @@ -189,7 +189,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) break; case DECL_MACRO: assert(decl->name); - if (decl->macro_decl.type_parent) + if (decl->func_decl.type_parent) { vec_add(unit->macro_methods, decl); return; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index ac319b0c6..ceffa8fed 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -545,6 +545,7 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source) return copy; case TYPE_INFO_EVALTYPE: case TYPE_INFO_EXPRESSION: + case TYPE_INFO_VAARG_TYPE: assert(source->resolve_status == RESOLVE_NOT_DONE); copy->unresolved_type_expr = copy_expr(c, source->unresolved_type_expr); return copy; @@ -566,10 +567,10 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source) UNREACHABLE } -static void copy_function_signature_deep(CopyStruct *c, FunctionSignature *signature) +static void copy_signature_deep(CopyStruct *c, Signature *signature) { MACRO_COPY_DECL_LIST(signature->params); - MACRO_COPY_TYPEID(signature->returntype); + MACRO_COPY_TYPEID(signature->rtype); } void copy_decl_type(Decl *decl) { @@ -647,7 +648,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) copy_decl_type(copy); MACRO_COPY_TYPEID(copy->func_decl.type_parent); MACRO_COPY_ASTID(copy->func_decl.docs); - copy_function_signature_deep(c, ©->func_decl.function_signature); + copy_signature_deep(c, ©->func_decl.signature); MACRO_COPY_ASTID(copy->func_decl.body); break; case DECL_VAR: @@ -673,7 +674,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) copy_decl_type(copy); if (copy->typedef_decl.is_func) { - copy_function_signature_deep(c, ©->typedef_decl.function_signature); + copy_signature_deep(c, ©->typedef_decl.function_signature); break; } MACRO_COPY_TYPE(copy->typedef_decl.type_info); @@ -683,7 +684,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) MACRO_COPY_DECL_LIST(copy->methods); if (copy->distinct_decl.typedef_decl.is_func) { - copy_function_signature_deep(c, ©->distinct_decl.typedef_decl.function_signature); + copy_signature_deep(c, ©->distinct_decl.typedef_decl.function_signature); break; } MACRO_COPY_TYPE(copy->distinct_decl.typedef_decl.type_info); @@ -708,12 +709,11 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) break; case DECL_GENERIC: case DECL_MACRO: - MACRO_COPY_ASTID(copy->macro_decl.docs); - MACRO_COPY_TYPEID(decl->macro_decl.type_parent); - MACRO_COPY_DECL_LIST(decl->macro_decl.parameters); - MACRO_COPY_ASTID(decl->macro_decl.body); - MACRO_COPY_TYPEID(decl->macro_decl.rtype); - MACRO_COPY_DECLID(decl->macro_decl.body_param); + MACRO_COPY_ASTID(copy->func_decl.docs); + MACRO_COPY_TYPEID(decl->func_decl.type_parent); + copy_signature_deep(c, ©->func_decl.signature); + MACRO_COPY_ASTID(decl->func_decl.body); + MACRO_COPY_DECLID(decl->func_decl.body_param); break; case DECL_CT_SWITCH: MACRO_COPY_DECL_LIST(decl->ct_switch_decl.cases); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 13158d6b4..d76d9ca83 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -324,6 +324,7 @@ typedef enum TYPE_INFO_IDENTIFIER, TYPE_INFO_CT_IDENTIFIER, TYPE_INFO_EXPRESSION, + TYPE_INFO_VAARG_TYPE, TYPE_INFO_EVALTYPE, TYPE_INFO_ARRAY, TYPE_INFO_VECTOR, @@ -554,7 +555,8 @@ typedef enum case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_VARIANT #define TYPE_TOKENS NON_VOID_TYPE_TOKENS: case TOKEN_VOID #define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: \ - case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF + case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF: case TOKEN_CT_EVALTYPE: \ + case TOKEN_CT_VAARG_GET_TYPE // Note that ordering matters here. If ordering is changed, // So must type_find_max_type and friends. diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index dabc7d771..82990d751 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -321,10 +321,11 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) LLVMValueRef init_value; Type *var_type = type_lowering(decl->type); - AlignSize alignment = type_alloca_alignment(var_type); Expr *init_expr = decl->var.init_expr; - if (init_expr && init_expr->expr_kind == EXPR_IDENTIFIER && init_expr->identifier_expr.is_const) + while (init_expr && init_expr->expr_kind == EXPR_IDENTIFIER + && init_expr->identifier_expr.decl->decl_kind == DECL_VAR + && init_expr->identifier_expr.decl->var.kind == VARDECL_CONST) { init_expr = init_expr->identifier_expr.decl->var.init_expr; } diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 8bcc5122e..74fe70398 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -124,7 +124,7 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) UNREACHABLE } flags |= LLVMDIFlagPrototyped; - if (decl->func_decl.attr_noreturn) flags |= LLVMDIFlagNoReturn; + if (decl->func_decl.signature.attrs.noreturn) flags |= LLVMDIFlagNoReturn; uint32_t row = decl->span.row; if (!row) row = 1; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 999afd658..7b0e40136 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5163,11 +5163,12 @@ static inline void llvm_emit_expr_block(GenContext *context, BEValue *be_value, static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, Expr *expr) { - VECEACH(expr->macro_block.params, i) + foreach(Decl *, expr->macro_block.params) { + // Skip vararg + if (!val) continue; // In case we have a constant, we never do an emit. The value is already folded. - Decl *decl = expr->macro_block.params[i]; - switch (decl->var.kind) + switch (val->var.kind) { case VARDECL_CONST: case VARDECL_GLOBAL: @@ -5183,8 +5184,8 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, case VARDECL_PARAM_REF: { BEValue addr; - llvm_emit_expr(context, &addr, decl->var.init_expr); - decl->backend_ref = addr.value; + llvm_emit_expr(context, &addr, val->var.init_expr); + val->backend_ref = addr.value; continue; } case VARDECL_PARAM_CT: @@ -5194,11 +5195,11 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, case VARDECL_PARAM: break; } - llvm_emit_and_set_decl_alloca(context, decl); + llvm_emit_and_set_decl_alloca(context, val); BEValue value; - llvm_emit_expr(context, &value, expr->macro_block.args[i]); - llvm_store_decl(context, decl, &value); + llvm_emit_expr(context, &value, val->var.init_expr); + llvm_store_decl(context, val, &value); } llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt, expr->macro_block.block_exit); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 28c6114ed..498504ce1 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -506,9 +506,9 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) if (!decl->func_decl.attr_naked) { // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code - VECEACH(decl->func_decl.function_signature.params, i) + VECEACH(decl->func_decl.signature.params, i) { - llvm_emit_parameter(c, decl->func_decl.function_signature.params[i], prototype->abi_args[i], &arg, i); + llvm_emit_parameter(c, decl->func_decl.signature.params[i], prototype->abi_args[i], &arg, i); } } @@ -633,7 +633,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) { llvm_attribute_add(c, function, attribute_id.noinline, -1); } - if (decl->func_decl.attr_noreturn) + if (decl->func_decl.signature.attrs.noreturn) { llvm_attribute_add(c, function, attribute_id.noreturn, -1); } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index e0d815b57..ef0c02c9c 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -912,11 +912,13 @@ 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; + assert(type != TOKEN_CT_VAARG_GET_TYPE); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); if (type != TOKEN_CT_VAARG_COUNT) @@ -1753,5 +1755,10 @@ 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 } + [TOKEN_CT_VAARG_COUNT] = { parse_ct_arg, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_GET_ARG] = { parse_ct_arg, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_GET_REF] = { parse_ct_arg, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_GET_TYPE] = { parse_type_expr, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_GET_EXPR] = { parse_ct_arg, NULL, PREC_NONE }, + [TOKEN_CT_VAARG_GET_CONST] = { parse_ct_arg, NULL, PREC_NONE }, }; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 6381e2f17..7d414eb43 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -491,6 +491,15 @@ static inline TypeInfo *parse_base_type(ParseContext *c) RANGE_EXTEND_PREV(type_info); return type_info; } + if (try_consume(c, TOKEN_CT_VAARG_GET_TYPE)) + { + TypeInfo *type_info = type_info_new(TYPE_INFO_VAARG_TYPE, c->prev_span); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_type_info); + ASSIGN_EXPR_OR_RET(type_info->unresolved_type_expr, parse_expr(c), poisoned_type_info); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_type_info); + RANGE_EXTEND_PREV(type_info); + return type_info; + } if (try_consume(c, TOKEN_CT_EVALTYPE)) { TypeInfo *type_info = type_info_new(TYPE_INFO_EVALTYPE, c->prev_span); @@ -1066,11 +1075,10 @@ INLINE bool is_end_of_param_list(ParseContext *c) * | ELLIPSIS (CT_TYPE_IDENT | non_type_ident ('=' expr)?)? */ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params, - Variadic *variadic, unsigned *vararg_index_ref) + Variadic *variadic, int *vararg_index_ref) { Decl** params = NULL; bool var_arg_found = false; - while (!is_end_of_param_list(c)) { bool ellipsis = try_consume(c, TOKEN_ELLIPSIS); @@ -1280,16 +1288,15 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref * * parameter_type_list ::= '(' parameters ')' */ -static inline bool parse_fn_parameter_list(ParseContext *c, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +static inline bool parse_fn_parameter_list(ParseContext *c, Visibility parent_visibility, Signature *signature, bool is_interface) { Decl **decls = NULL; CONSUME_OR_RET(TOKEN_LPAREN, false); Variadic variadic = VARIADIC_NONE; - unsigned vararg_index = 0; + int vararg_index = -1; 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->vararg_index = vararg_index < 0 ? vec_size(decls) : vararg_index; signature->params = decls; signature->variadic = variadic; @@ -1529,12 +1536,12 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl * // Parse the regular parameters. Variadic variadic = VARIADIC_NONE; - unsigned vararg_index = 0; + int vararg_index = -1; 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; + macro->func_decl.signature.params = params; + macro->func_decl.signature.vararg_index = vararg_index < 0 ? vec_size(params) : vararg_index; + macro->func_decl.signature.variadic = variadic; // Do we have trailing block parameters? if (try_consume(c, TOKEN_EOS)) @@ -1547,11 +1554,11 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl * if (!parse_parameters(c, visibility, &body_param->body_params, NULL, NULL, NULL)) return false; CONSUME_OR_RET(TOKEN_RPAREN, false); } - macro->macro_decl.body_param = declid(body_param); + macro->func_decl.body_param = declid(body_param); } else { - macro->macro_decl.body_param = 0; + macro->func_decl.body_param = 0; } CONSUME_OR_RET(TOKEN_RPAREN, false); return true; @@ -1606,7 +1613,7 @@ static inline Decl *parse_define_type(ParseContext *c, Visibility visibility) decl->typedef_decl.is_func = true; 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); + decl->typedef_decl.function_signature.rtype = type_infoid(type_info); if (!parse_fn_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; @@ -1794,13 +1801,13 @@ static inline bool parse_is_macro_name(ParseContext *c) * func_header ::= type '!'? (type '.')? (IDENT | MACRO_IDENT) * macro_header ::= (type '!'?)? (type '.')? (IDENT | MACRO_IDENT) */ -static inline bool parse_func_macro_header(ParseContext *c, bool is_macro, - TypeInfoId *rtype_ref, TypeInfoId *method_type_ref, - const char **name_ref, SourceSpan *name_span) +static inline bool parse_func_macro_header(ParseContext *c, Decl *decl) { TypeInfo *rtype = NULL; TypeInfo *method_type = NULL; + bool is_macro = decl->decl_kind == DECL_MACRO; + // 1. If we have a macro and see the name, we're done. if (is_macro && parse_is_macro_name(c)) { @@ -1839,8 +1846,8 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro, return false; } RESULT: - *name_ref = symstr(c); - *name_span = c->span; + decl->name = symstr(c); + decl->span = c->span; if (is_macro && c->tok != TOKEN_IDENT && c->tok != TOKEN_AT_IDENT) { sema_error_at(c->span, "Expected a macro name here, e.g. '@someName' or 'someName'."); @@ -1852,8 +1859,10 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro, return false; } advance(c); - *rtype_ref = rtype ? type_infoid(rtype) : 0; - *method_type_ref = method_type ? type_infoid(method_type) : 0; + decl->func_decl.signature.rtype = rtype ? type_infoid(rtype) : 0; + decl->func_decl.signature.is_macro = is_macro; + decl->func_decl.signature.is_at_macro = decl->name[0] == '@'; + decl->func_decl.type_parent = method_type ? type_infoid(method_type) : 0; return true; } @@ -1869,15 +1878,13 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili Decl *decl = decl_calloc(); decl->decl_kind = kind; decl->visibility = visibility; - decl->macro_decl.docs = docs; - TypeInfoId *rtype_ref = &decl->macro_decl.rtype; - 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; + decl->func_decl.docs = docs; + if (!parse_func_macro_header(c, decl)) return poisoned_decl; const char *block_parameter = NULL; 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); + ASSIGN_ASTID_OR_RET(decl->func_decl.body, parse_stmt(c), poisoned_decl); return decl; } @@ -2073,15 +2080,13 @@ static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility func->decl_kind = DECL_FUNC; func->visibility = visibility; func->func_decl.docs = docs; - TypeInfoId *rtype_id_ref = &func->func_decl.function_signature.returntype; - TypeInfoId *method_type_ref = &func->func_decl.type_parent; - if (!parse_func_macro_header(c, false, rtype_id_ref, method_type_ref, &func->name, &func->span)) return poisoned_decl; + if (!parse_func_macro_header(c, func)) return poisoned_decl; if (func->name[0] == '@') { SEMA_ERROR(func, "Function names may not use '@'."); return false; } - if (!parse_fn_parameter_list(c, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; + if (!parse_fn_parameter_list(c, visibility, &(func->func_decl.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 726d5b5dc..5d236f1ef 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -856,7 +856,6 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_HASH_IDENT: case TOKEN_IDENT: case TOKEN_CONST_IDENT: - case TOKEN_CT_EVALTYPE: return parse_decl_or_expr_stmt(c); case TOKEN_VAR: return parse_var_stmt(c); @@ -964,7 +963,6 @@ Ast *parse_stmt(ParseContext *c) 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 fe578790c..e0e6fe369 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -46,7 +46,7 @@ Decl *parse_var_decl(ParseContext *c); bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params, Variadic *variadic, - unsigned *vararg_index_ref); + int *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); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index f262031e2..f38573229 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -69,7 +69,7 @@ static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *curr { if (name == decls[i]->name) { - SEMA_ERROR(current, "Duplicate parameter name %s.", name); + SEMA_ERROR(current, "Duplicate parameter name '%s'.", name); SEMA_NOTE(decls[i], "Previous use of the name was here."); decl_poison(decls[i]); decl_poison(current); @@ -581,51 +581,40 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl) } -static inline bool sema_analyse_function_param(SemaContext *context, Decl *param) +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) { - param->unit = context->unit; - assert(param->decl_kind == DECL_VAR); - // We need to check that the parameters are not typeless nor are of any macro parameter kind. - if (param->var.kind != VARDECL_PARAM) - { - SEMA_ERROR(param, "Only regular parameters are allowed for functions."); - return false; - } + Variadic variadic_type = sig->variadic; + Decl **params = sig->params; + unsigned param_count = vec_size(params); + unsigned vararg_index = sig->vararg_index; + bool is_fn = !sig->is_macro; + bool is_macro_at_name = sig->is_at_macro; - if (!sema_analyse_attributes_for_var(context, param)) return false; - if (!param->var.type_info) + // Check return type + assert(sig->rtype || sig->is_macro); + Type *rtype = NULL; + if (sig->rtype) { - SEMA_ERROR(param, "Only typed parameters are allowed for functions."); - return false; - } - if (!sema_resolve_type_info(context, param->var.type_info)) return false; - - if (param->var.vararg) - { - param->var.type_info->type = type_get_subarray(param->var.type_info->type); - } - param->type = param->var.type_info->type; - if (param->var.init_expr) - { - Expr *expr = param->var.init_expr; - - if (expr->expr_kind == EXPR_CONST) + TypeInfo *rtype_info = type_infoptr(sig->rtype); + if (!sema_resolve_type_info(context, type_infoptr(sig->rtype))) return false; + rtype = rtype_info->type; + if (sig->attrs.nodiscard) { - if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return false; + if (rtype == type_void) + { + SEMA_ERROR(rtype_info, "@nodiscard cannot be used on %s returning 'void'.", is_fn ? "functions" : "macros"); + return false; + } + } + if (sig->attrs.maydiscard) + { + if (!type_is_optional(rtype)) + { + SEMA_ERROR(rtype_info, "@maydiscard can only be used on %s returning optional values.", is_fn ? "functions" : "macros"); + return false; + } } } - param->alignment = type_abi_alignment(param->type); - return true; -} - -static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, FunctionSignature *signature, bool is_real_function) -{ - // Get param count and variadic type - Variadic variadic_type = signature->variadic; - Decl **params = signature->params; - unsigned param_count = vec_size(params); - - bool all_ok = sema_resolve_type_info(context, type_infoptr(signature->returntype)); // We don't support more than MAX_PARAMS number of params. This makes everything sane. if (param_count > MAX_PARAMS) @@ -633,39 +622,28 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Decl * if (variadic_type != VARIADIC_NONE) { SEMA_ERROR(params[MAX_PARAMS], "The number of params exceeded the max of %d.", MAX_PARAMS); - return NULL; + return false; } SEMA_ERROR(params[MAX_PARAMS], "The number of params exceeded the max of %d. To accept more arguments, consider using varargs.", MAX_PARAMS); - return NULL; + return false; } - Type **types = NULL; - int vararg_index = -1; + // Check parameters for (unsigned i = 0; i < param_count; i++) { Decl *param = params[i]; - // We might run into a raw vararg if (!param) { assert(variadic_type == VARIADIC_RAW); - vararg_index = i; + assert(i == vararg_index); continue; } - assert(param->resolve_status == RESOLVE_NOT_DONE && "The param shouldn't have been resolved yet."); - param->resolve_status = RESOLVE_RUNNING; - // Analyse the param - if (!sema_analyse_function_param(context, param)) + if (vararg_index < i) { - decl_poison(param); - all_ok = false; - continue; - } - if (vararg_index > -1) - { - if (variadic_type == VARIADIC_RAW) + if (is_fn && variadic_type == VARIADIC_RAW) { SEMA_ERROR(param, "C-style varargs cannot be followed by regular parameters."); - return NULL; + return decl_poison(param); } // If we already reached a vararg (as a previous argument) and we have a @@ -673,48 +651,149 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Decl * if (!param->name) { SEMA_ERROR(param, "A parameter name was expected, as parameters after varargs must always be named."); - decl_poison(param); - all_ok = false; - continue; + return decl_poison(param); } } - // 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)) + + assert(param->resolve_status == RESOLVE_NOT_DONE && "The param shouldn't have been resolved yet."); + param->resolve_status = RESOLVE_RUNNING; + param->unit = context->unit; + assert(param->decl_kind == DECL_VAR); + VarDeclKind var_kind = param->var.kind; + switch (var_kind) { - all_ok = false; + case VARDECL_PARAM_EXPR: + case VARDECL_PARAM_REF: + if (is_fn) + { + SEMA_ERROR(param, "Only regular parameters are allowed for functions."); + return decl_poison(param); + } + if (!is_macro_at_name) + { + SEMA_ERROR(param, "Ref and expression parameters are not allowed in function-like macros. Prefix the macro name with '@'."); + return decl_poison(param); + } + FALLTHROUGH; + case VARDECL_PARAM_CT: + if (is_fn) + { + SEMA_ERROR(param, "Only regular parameters are allowed for functions."); + return decl_poison(param); + } + FALLTHROUGH; + case VARDECL_PARAM: + if (param->var.type_info) + { + if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(param); + param->type = param->var.type_info->type; + } + else if (is_fn) + { + SEMA_ERROR(param, "Only typed parameters are allowed for functions."); + return false; + } + if (!sema_analyse_attributes_for_var(context, param)) return false; + break; + case VARDECL_PARAM_CT_TYPE: + if (is_fn) + { + SEMA_ERROR(param, "Only regular parameters are allowed for functions."); + return decl_poison(param); + } + if (param->var.type_info) + { + SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself."); + return decl_poison(param); + } + break; + case VARDECL_CONST: + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_MEMBER: + case VARDECL_BITMEMBER: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: + UNREACHABLE } - // Update whether this was a vararg, and update "default" for the signature. if (param->var.vararg) { - if (vararg_index > -1) + if (var_kind != VARDECL_PARAM) { - SEMA_ERROR(param, "A function may not have more than one vararg."); - all_ok = false; + SEMA_ERROR(param, "Only regular parameters may be vararg."); + return decl_poison(param); } - vararg_index = i; + if (!param->var.type_info) + { + SEMA_ERROR(param, "Only typed parameters may be vararg."); + return decl_poison(param); + } + // Update whether this was a vararg, and update "default" for the signature. + if (param->var.vararg) + { + if (vararg_index != i) + { + SEMA_ERROR(param, "A %s may not have more than one vararg.", is_fn ? "function" : "macro"); + return decl_poison(param); + } + } + param->var.type_info->type = type_get_subarray(param->var.type_info->type); } - // Resolution is done. + + if (param->var.type_info) + { + param->type = param->var.type_info->type; + param->alignment = type_abi_alignment(param->type); + } + if (param->var.init_expr) + { + Expr *expr = param->var.init_expr; + if (expr->expr_kind == EXPR_CONST) + { + if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return decl_poison(param); + } + } + if (!sema_check_param_uniqueness_and_type(params, param, i, param_count)) return decl_poison(param); param->resolve_status = RESOLVE_DONE; - // Add it to the type for the type signature. - vec_add(types, param->type); } + return true; +} + + +static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, Signature *signature, bool is_real_function) +{ + // Get param count and variadic type + Decl **params = signature->params; + + if (!sema_analyse_signature(context, signature)) return NULL; + + Variadic variadic_type = signature->variadic; // Remove the last empty value. if (variadic_type == VARIADIC_RAW) { - assert(vararg_index >= 0); - assert(!params[vararg_index] && "The last parameter must have been a raw variadic."); - assert(vararg_index == param_count - 1); + assert(!params[signature->vararg_index] && "The last parameter must have been a raw variadic."); + assert(signature->vararg_index == vec_size(params) - 1); vec_pop(params); } + Type **types = NULL; + bool all_ok = true; + unsigned param_count = vec_size(params); + + for (unsigned i = 0; i < param_count; i++) + { + vec_add(types, params[i]->type); + } + if (!all_ok) return NULL; Type *raw_type = type_get_func(signature, abi); Type *type = type_new(TYPE_FUNC, parent->name); type->canonical = type; - type->function.attrs = signature->attrs; + type->function.signature = signature; type->function.module = parent->unit->module; - type->function.params = signature->params; type->function.prototype = raw_type->function.prototype; return type; } @@ -1007,33 +1086,11 @@ static inline const char *name_by_decl(Decl *method_like) } -static inline void find_operator_parameters(Decl *method, TypeInfoId *rtype_ptr, Decl ***params_ptr) -{ - switch (method->decl_kind) - { - case DECL_GENERIC: - *params_ptr = method->generic_decl.parameters; - *rtype_ptr = method->generic_decl.rtype; - break; - case DECL_MACRO: - *params_ptr = method->macro_decl.parameters; - *rtype_ptr = method->macro_decl.rtype; - break; - case DECL_FUNC: - *params_ptr = method->func_decl.function_signature.params; - *rtype_ptr = method->func_decl.function_signature.returntype; - break; - default: - UNREACHABLE - } - -} static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr, uint32_t parameters) { - TypeInfoId rtype_id; - find_operator_parameters(method, &rtype_id, params_ptr); - Decl **params = *params_ptr; + Signature *signature = &method->func_decl.signature; + Decl **params = *params_ptr = signature->params; uint32_t param_count = vec_size(params); if (param_count > parameters) { @@ -1046,7 +1103,7 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec return false; } - if (!rtype_id) + if (!signature->rtype) { SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name); return false; @@ -1060,7 +1117,7 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec return false; } } - *rtype_ptr = type_infoptr(rtype_id); + *rtype_ptr = type_infoptr(signature->rtype); return true; } @@ -1233,7 +1290,7 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) type_to_error_string(parent_type->type)); return false; } - Decl **params = decl->func_decl.function_signature.params; + Decl **params = decl->func_decl.signature.params; if (!vec_size(params)) { SEMA_ERROR(decl, "A method must start with a parameter of type %s or %s.", @@ -1330,21 +1387,21 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, switch (type) { case ATTRIBUTE_CDECL: - decl->func_decl.function_signature.abi = CALL_C; + decl->func_decl.signature.abi = CALL_C; break; case ATTRIBUTE_VECCALL: switch (platform_target.arch) { case ARCH_TYPE_X86_64: case ARCH_TYPE_X86: - decl->func_decl.function_signature.abi = CALL_X86_VECTOR; + decl->func_decl.signature.abi = CALL_X86_VECTOR; break; case ARCH_TYPE_ARM: case ARCH_TYPE_ARMB: case ARCH_TYPE_AARCH64: case ARCH_TYPE_AARCH64_32: case ARCH_TYPE_AARCH64_BE: - decl->func_decl.function_signature.abi = CALL_AAPCS_VFP; + decl->func_decl.signature.abi = CALL_AAPCS_VFP; break; default: break; @@ -1354,25 +1411,25 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, assert(decl->decl_kind == DECL_FUNC); if (platform_target.arch == ARCH_TYPE_X86 || platform_target.arch == ARCH_TYPE_X86_64) { - decl->func_decl.function_signature.abi = CALL_X86_STD; + decl->func_decl.signature.abi = CALL_X86_STD; } else if (platform_target.arch == ARCH_TYPE_ARM || platform_target.arch == ARCH_TYPE_ARMB) { - decl->func_decl.function_signature.abi = CALL_AAPCS; + decl->func_decl.signature.abi = CALL_AAPCS; } break; // Check args case ATTRIBUTE_FASTCALL: if (platform_target.arch == ARCH_TYPE_X86 || (platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32)) { - decl->func_decl.function_signature.abi = CALL_X86_FAST; + decl->func_decl.signature.abi = CALL_X86_FAST; } break; case ATTRIBUTE_REGCALL: if (platform_target.arch == ARCH_TYPE_X86 || (platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32)) { - decl->func_decl.function_signature.abi = CALL_X86_REG; + decl->func_decl.signature.abi = CALL_X86_REG; } break; case ATTRIBUTE_OPERATOR: @@ -1476,33 +1533,17 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, decl->func_decl.attr_inline = false; break; case ATTRIBUTE_NODISCARD: - if (domain == ATTR_MACRO) - { - decl->macro_decl.attr_nodiscard = true; - break; - } - decl->func_decl.function_signature.attrs.nodiscard = true; + decl->func_decl.signature.attrs.nodiscard = true; break; case ATTRIBUTE_MAYDISCARD: - if (domain == ATTR_MACRO) - { - decl->macro_decl.attr_maydiscard = true; - break; - } - decl->func_decl.function_signature.attrs.maydiscard = true; + decl->func_decl.signature.attrs.maydiscard = true; break; case ATTRIBUTE_INLINE: decl->func_decl.attr_inline = true; decl->func_decl.attr_noinline = false; break; case ATTRIBUTE_NORETURN: - if (domain == ATTR_MACRO) - { - decl->macro_decl.attr_noreturn = true; - break; - } - assert(domain == ATTR_FUNC); - decl->func_decl.attr_noreturn = true; + decl->func_decl.signature.attrs.noreturn = true; break; case ATTRIBUTE_WEAK: decl->is_weak = true; @@ -1736,8 +1777,8 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) SEMA_ERROR(decl, "A main function may not have local visibility."); return false; } - FunctionSignature *signature = &decl->func_decl.function_signature; - TypeInfo *rtype_info = type_infoptr(signature->returntype); + Signature *signature = &decl->func_decl.signature; + TypeInfo *rtype_info = type_infoptr(signature->rtype); Type *rtype = type_flatten_distinct(rtype_info->type); bool is_int_return = true; bool is_err_return = false; @@ -1807,13 +1848,14 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) function->unit = decl->unit; function->extname = kw_main; function->has_extname = true; - function->func_decl.function_signature.returntype = type_infoid(type_info_new_base(type_cint, decl->span)); + function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span)); + function->func_decl.signature.vararg_index = 2; Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span); Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span); Decl **main_params = NULL; vec_add(main_params, param1); vec_add(main_params, param2); - function->func_decl.function_signature.params = main_params; + function->func_decl.signature.params = main_params; Ast *body = new_ast(AST_COMPOUND_STMT, decl->span); AstId *next = &body->compound_stmt.first_stmt; Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span); @@ -1877,19 +1919,24 @@ REGISTER_MAIN: return true; } +static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, bool is_func) +{ + if (!sema_analyse_attributes(context, decl, decl->attributes, is_func ? ATTR_FUNC : ATTR_MACRO)) return decl_poison(decl); + return true; +} static inline bool sema_analyse_func(SemaContext *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); - if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_FUNC)) return decl_poison(decl); + if (!sema_analyse_func_macro(context, decl, true)) return false; - Type *func_type = sema_analyse_function_signature(context, decl, decl->func_decl.function_signature.abi, &decl->func_decl.function_signature, true); + Type *func_type = sema_analyse_function_signature(context, decl, decl->func_decl.signature.abi, &decl->func_decl.signature, true); decl->type = func_type; if (!func_type) return decl_poison(decl); - TypeInfo *rtype_info = type_infoptr(decl->func_decl.function_signature.returntype); + TypeInfo *rtype_info = type_infoptr(decl->func_decl.signature.rtype); assert(rtype_info); Type *rtype = rtype_info->type->canonical; - if (decl->func_decl.function_signature.attrs.nodiscard) + if (decl->func_decl.signature.attrs.nodiscard) { if (rtype == type_void) { @@ -1897,7 +1944,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) return decl_poison(decl); } } - if (decl->func_decl.function_signature.attrs.maydiscard) + if (decl->func_decl.signature.attrs.maydiscard) { if (!type_is_optional(rtype)) { @@ -1918,8 +1965,8 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) decl_set_external_name(decl); } bool pure = false; - if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.function_signature.params, NULL, &pure)) return decl_poison(decl); - decl->func_decl.function_signature.is_pure = pure; + if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, NULL, &pure)) return decl_poison(decl); + decl->func_decl.signature.attrs.is_pure = pure; decl->alignment = type_alloca_alignment(decl->type); DEBUG_LOG("Function analysis done."); return true; @@ -1945,7 +1992,7 @@ ERROR: static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) { - TypeInfo *parent_type_info = type_infoptr(decl->macro_decl.type_parent); + TypeInfo *parent_type_info = type_infoptr(decl->func_decl.type_parent); if (!sema_resolve_type_info(context, parent_type_info)) return false; Type *parent_type = parent_type_info->type; if (!type_may_have_sub_elements(parent_type)) @@ -1955,12 +2002,12 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) type_to_error_string(parent_type)); return false; } - if (!vec_size(decl->macro_decl.parameters)) + if (!vec_size(decl->func_decl.signature.params)) { SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_to_error_string(parent_type)); return false; } - Decl *first_param = decl->macro_decl.parameters[0]; + Decl *first_param = decl->func_decl.signature.params[0]; if (!sema_is_valid_method_param(context, first_param, parent_type->canonical)) return false; @@ -1975,91 +2022,21 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) { bool is_generic = decl->decl_kind == DECL_GENERIC; + decl->func_decl.unit = context->unit; - if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_MACRO)) return decl_poison(decl); + if (!sema_analyse_func_macro(context, decl, false)) return false; + if (!sema_analyse_signature(context, &decl->func_decl.signature)) return decl_poison(decl); - if (decl->macro_decl.rtype) + if (!decl->func_decl.signature.is_at_macro && decl->func_decl.body_param) { - TypeInfo *rtype_info = type_infoptr(decl->macro_decl.rtype); - if (!sema_resolve_type_info(context, rtype_info)) return decl_poison(decl); - Type *rtype = rtype_info->type; - if (decl->macro_decl.attr_nodiscard) - { - if (rtype == type_void) - { - SEMA_ERROR(rtype_info, "@nodiscard cannot be used on macros returning 'void'."); - return decl_poison(decl); - } - } - if (decl->macro_decl.attr_maydiscard) - { - if (!type_is_optional(rtype)) - { - SEMA_ERROR(rtype_info, "@maydiscard can only be used on macros returning optional values."); - return decl_poison(decl); - } - } - } - decl->macro_decl.unit = context->unit; - Decl **parameters = decl->macro_decl.parameters; - unsigned param_count = vec_size(parameters); - bool is_function_like = true; - for (unsigned i = 0; i < param_count; i++) - { - Decl *param = parameters[i]; - param->unit = context->unit; - param->resolve_status = RESOLVE_RUNNING; - assert(param->decl_kind == DECL_VAR); - switch (param->var.kind) - { - case VARDECL_PARAM_EXPR: - case VARDECL_PARAM_REF: - is_function_like = false; - FALLTHROUGH; - case VARDECL_PARAM_CT: - if (is_generic) - { - SEMA_ERROR(param, "Only regular parameters and type parameters are allowed for generic functions."); - return decl_poison(decl); - } - FALLTHROUGH; - case VARDECL_PARAM: - if (param->var.type_info) - { - if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl); - param->type = param->var.type_info->type; - } - break; - case VARDECL_PARAM_CT_TYPE: - if (param->var.type_info) - { - SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself."); - return decl_poison(decl); - } - break; - case VARDECL_CONST: - case VARDECL_GLOBAL: - case VARDECL_LOCAL: - case VARDECL_MEMBER: - case VARDECL_BITMEMBER: - case VARDECL_LOCAL_CT: - case VARDECL_LOCAL_CT_TYPE: - case VARDECL_UNWRAPPED: - case VARDECL_REWRAPPED: - case VARDECL_ERASE: - UNREACHABLE - } - if (!sema_check_param_uniqueness_and_type(parameters, param, i, param_count)) return decl_poison(decl); - param->resolve_status = RESOLVE_DONE; - } - DeclId body_param = decl->macro_decl.body_param; - if (body_param) is_function_like = false; - if (is_generic && body_param) - { - SEMA_ERROR(declptr(body_param), "Trailing block syntax is not allowed for generic functions."); + SEMA_ERROR(decl, "Names of macros with a trailing body must start with '@'."); return decl_poison(decl); } + Decl **parameters = decl->func_decl.signature.params; + unsigned param_count = vec_size(parameters); + DeclId body_param = decl->func_decl.body_param; + Decl **body_parameters = body_param ? declptr(body_param)->body_params : NULL; unsigned body_param_count = vec_size(body_parameters); for (unsigned i = 0; i < body_param_count; i++) @@ -2094,13 +2071,9 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) param->resolve_status = RESOLVE_DONE; } bool pure = false; - if (!sema_analyse_doc_header(decl->macro_decl.docs, decl->macro_decl.parameters, body_parameters, &pure)) return decl_poison(decl); - if (decl->name[0] != '@' && !is_function_like) - { - SEMA_ERROR(decl, "Names of non-function like macros (i.e. a macro with a trailing body, ref or expression parameters, must start with '@'."); - return false; - } - if (decl->macro_decl.type_parent) + if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, body_parameters, &pure)) return decl_poison(decl); + + if (decl->func_decl.type_parent) { if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl); } @@ -2520,7 +2493,7 @@ static inline bool sema_analyse_attribute_decl(SemaContext *c, Decl *decl) { if (param[j].name == param->name) { - SEMA_ERROR(param, "Duplicate parameter name."); + SEMA_ERROR(param, "Duplicate parameter name '%s'.", param->name); return false; } } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a7ca8f9c1..d130d1fa4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -71,6 +71,37 @@ void expr_rewrite_to_builtin_access(SemaContext *context, Expr *expr, Expr *pare expr->resolve_status = RESOLVE_DONE; } +Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr) +{ + unsigned args = vec_size(context->macro_varargs); + uint64_t index; + Decl *param = NULL; + if (!sema_analyse_expr(context, index_expr)) return poisoned_expr; + if (!type_is_integer(index_expr->type)) + { + SEMA_ERROR(index_expr, "Expected the argument index here, but found a value of type %s.", type_quoted_error_string(index_expr->type)); + return poisoned_expr; + } + if (!expr_is_const(index_expr)) + { + SEMA_ERROR(index_expr, "Vararg functions need a constant argument, but this is a runtime value."); + return poisoned_expr; + } + Int index_val = index_expr->const_expr.ixx; + if (int_is_neg(index_val)) + { + SEMA_ERROR(index_expr, "The index cannot be negative."); + return poisoned_expr; + } + Int int_max = { .i = { .low = args }, .type = TYPE_U32 }; + if (int_comp(index_val, int_max, BINARYOP_GE)) + { + SEMA_ERROR(index_expr, "Only %u vararg%s exist.", args, args == 1 ? "" : "s"); + return poisoned_expr; + } + return context->macro_varargs[(size_t)index_val.i.low]; +} + const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing) { if (!sema_analyse_expr(c, inner)) return false; @@ -149,7 +180,7 @@ void expr_insert_addr(Expr *original) original->expr_kind = EXPR_UNARY; Type *inner_type = inner->type; bool failable = type_is_optional(inner->type); - original->type = type_add_optional(type_get_ptr(type_no_optional(inner->type)), failable); + original->type = type_add_optional(type_get_ptr(type_no_optional(inner_type)), failable); original->unary_expr.operator = UNARYOP_ADDR; original->unary_expr.expr = inner; } @@ -380,7 +411,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) return false; case EXPR_IDENTIFIER: if (expr->identifier_expr.decl->decl_kind != DECL_VAR) return true; - if (expr->identifier_expr.is_const) + if (expr->identifier_expr.decl->var.kind == VARDECL_CONST) { expr = expr->identifier_expr.decl->var.init_expr; goto RETRY; @@ -570,17 +601,17 @@ static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr) return true; case EXPR_IDENTIFIER: { - if (expr->identifier_expr.is_const) - { - SEMA_ERROR(top_expr, "You cannot assign to a constant."); - return false; - } Decl *decl = expr->identifier_expr.decl; if (decl->decl_kind != DECL_VAR) { SEMA_ERROR(top_expr, "You cannot assign a value to %s.", decl_to_a_name(decl)); return false; } + if (decl->var.kind == VARDECL_CONST) + { + SEMA_ERROR(top_expr, "You cannot assign to a constant."); + return false; + } decl = decl_raw(decl); switch (decl->var.kind) { @@ -1100,8 +1131,9 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, assert(expr && expr->identifier_expr.ident); DEBUG_LOG("Resolving identifier '%s'", expr->identifier_expr.ident); + assert(expr->resolve_status != RESOLVE_DONE); DeclId body_param; - if (!expr->identifier_expr.path && context->current_macro && (body_param = context->current_macro->macro_decl.body_param)) + if (!expr->identifier_expr.path && context->current_macro && (body_param = context->current_macro->func_decl.body_param)) { if (expr->identifier_expr.ident == declptr(body_param)->name) { @@ -1375,15 +1407,11 @@ static inline bool expr_may_splat_as_vararg(Expr *expr, Type *variadic_base_type 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; + Signature *signature; } CalledDecl; static inline bool expr_promote_vararg(SemaContext *context, Expr *arg) @@ -1463,7 +1491,6 @@ INLINE bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, { unsigned num_args = vec_size(args); Decl **params = callee->params; - bool is_func_ptr = callee->func_pointer; Expr **actual_args = VECNEW(Expr*, func_param_count); for (unsigned i = 0; i < func_param_count; i++) vec_add(actual_args, NULL); @@ -1628,8 +1655,10 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr // 2. Pick out all the arguments and parameters. Expr **args = call->call_expr.arguments; + Signature *sig = callee.signature; + unsigned vararg_index = sig->vararg_index; + Variadic variadic = sig->variadic; Decl **decl_params = callee.params; - Type **param_types = callee.param_types; unsigned num_args = vec_size(args); // 3. If this is a type call, then we have an implicit first argument. @@ -1655,7 +1684,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr if (splat) { // 4a. Is this *not* a variadic function/macro? - Then that's an error. - if (callee.variadic == VARIADIC_NONE) + if (variadic == VARIADIC_NONE) { SEMA_ERROR(call->call_expr.arguments[num_args - 1], "Using the splat operator is only allowed on vararg parameters."); @@ -1664,15 +1693,15 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr } // 5. Zero out all argument slots. - unsigned func_param_count = callee.param_count; + unsigned param_count = vec_size(decl_params); // 6. We might have a typed variadic call e.g. foo(int, double...) // get that type. Type *variadic_type = NULL; - if (callee.variadic == VARIADIC_TYPED || callee.variadic == VARIADIC_ANY) + if (variadic == VARIADIC_TYPED || variadic == VARIADIC_ANY) { // 7a. The parameter type is [], so we get the - Type *vararg_slot_type = callee.macro ? callee.params[callee.vararg_index]->type : callee.param_types[callee.vararg_index]; + Type *vararg_slot_type = decl_params[vararg_index]->type; assert(vararg_slot_type->type_kind == TYPE_SUBARRAY); variadic_type = vararg_slot_type->array.base; } @@ -1683,9 +1712,9 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr &callee, call, args, - func_param_count, - callee.variadic, - callee.vararg_index, + param_count, + variadic, + vararg_index, failable, &varargs, &vararg_splat)) return false; args = call->call_expr.arguments; @@ -1694,18 +1723,22 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr call->call_expr.varargs = NULL; if (varargs) { - if (callee.variadic == VARIADIC_RAW) + if (variadic == VARIADIC_RAW) { foreach(Expr*, varargs) { // 12a. Analyse the expression. - 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 (callee.macro) { + // Just keep as lvalues + if (!sema_analyse_expr_lvalue_fold_const(context, val)) return false; + } + else + { + if (!sema_analyse_expr(context, val)) return false; if (!expr_promote_vararg(context, val)) return false; } + // Set the argument at the location. *failable |= IS_OPTIONAL(val); } @@ -1746,27 +1779,15 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr // 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) + if (i == vararg_index && variadic != VARIADIC_NONE) { assert(arg == NULL); continue; } - Decl *param; - VarDeclKind kind; - Type *type; - if (decl_params) - { - param = decl_params[i]; - kind = param->var.kind; - type = param->type; - } - else - { - param = NULL; - kind = VARDECL_PARAM; - type = param_types[i]; - } + Decl *param = decl_params[i]; + VarDeclKind kind = param->var.kind; + Type *type = param->type; // 16. Analyse a regular argument. switch (kind) @@ -1801,8 +1822,9 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr SEMA_ERROR(arg, "An untyped list can only be passed as a compile time parameter."); return false; } - if (param && !param->alignment) + if (!param->alignment) { + assert(callee.macro && "Only in the macro case should we need to insert the alignment."); param->alignment = type_alloca_alignment(arg->type); } break; @@ -1846,23 +1868,19 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr } return true; } -static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type *type, - FunctionSignature *sig, Expr *expr, Decl *decl, - Expr *struct_var, bool failable, const char *name) +static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var, + bool failable, const char *name) { + Signature *sig = type->function.signature; CalledDecl callee = { .macro = false, .name = name, - .func_pointer = sig ? 0 : 1, .block_parameter = NULL, .struct_var = struct_var, - .params = type->function.params, - .param_types = type->function.prototype->param_types, - .param_count = vec_size(type->function.prototype->param_types), - .variadic = type->function.prototype->variadic, - .vararg_index = type->function.prototype->vararg_index + .params = sig->params, + .signature = sig, }; - if (context->current_function_pure && (!sig || !sig->is_pure) && !expr->call_expr.attr_pure) + if (context->current_function_pure && !sig->attrs.is_pure && !expr->call_expr.attr_pure) { SEMA_ERROR(expr, "Only '@pure' functions may be called, you can override this with an attribute."); return false; @@ -1873,16 +1891,16 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type Type *rtype = type->function.prototype->rtype; - if (is_unused && rtype != type_void && decl && decl->decl_kind == DECL_FUNC) + if (is_unused && rtype != type_void) { - if (decl->func_decl.function_signature.attrs.nodiscard) + if (sig->attrs.nodiscard) { SEMA_ERROR(expr, "The result of the function must be used."); return false; } - if (type_is_optional(rtype) && !decl->func_decl.function_signature.attrs.maydiscard) + if (type_is_optional(rtype) && !sig->attrs.maydiscard) { - SEMA_ERROR(expr, "The optional result of the macro must be used."); + SEMA_ERROR(expr, "The optional result of the function must be used."); return false; } } @@ -1895,20 +1913,14 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool failable) { Decl *decl = NULL; - if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) goto ERR; + if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) + { + SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(func_ptr_type)); + return false; + } Type *pointee = func_ptr_type->pointer; expr->call_expr.is_pointer_call = true; - return sema_expr_analyse_func_invocation(context, - pointee, - NULL, - expr, - decl, - NULL, - failable, - func_ptr_type->pointer->name); -ERR: - SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(func_ptr_type)); - return false; + return sema_expr_analyse_func_invocation(context, pointee, expr, NULL, failable, func_ptr_type->pointer->name); } // Unify returns in a macro or expression block. @@ -2008,9 +2020,7 @@ static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, expr->call_expr.is_pointer_call = false; return sema_expr_analyse_func_invocation(context, decl->type, - &decl->func_decl.function_signature, - expr, - decl, + expr, struct_var, failable, decl->name); @@ -2068,24 +2078,43 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s { assert(decl->decl_kind == DECL_MACRO); - Decl **params = decl_copy_list(decl->macro_decl.parameters); + Decl **params = decl_copy_list(decl->func_decl.signature.params); 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), + .block_parameter = decl->func_decl.body_param ? declptr(decl->func_decl.body_param)->name : NULL, + .signature = &decl->func_decl.signature, .struct_var = struct_var }; if (!sema_expr_analyse_call_invocation(context, call_expr, callee, &failable)) return false; + unsigned vararg_index = decl->func_decl.signature.vararg_index; Expr **args = call_expr->call_expr.arguments; VECEACH(params, i) { Decl *param = params[i]; + if (i == vararg_index) + { + if (!param) continue; + // Splat? That's the simple case. + if (call_expr->call_expr.splat_vararg) + { + + if (!sema_analyse_expr(context, args[i] = call_expr->call_expr.splat)) return false; + } + else + { + Expr **exprs = call_expr->call_expr.varargs; + Expr *literal = expr_new(EXPR_COMPOUND_LITERAL, exprs ? exprs[0]->span : param->span); + Expr *initializer_list = expr_new_expr(EXPR_INITIALIZER_LIST, literal); + initializer_list->initializer_list = exprs; + literal->expr_compound_literal.type_info = param->var.type_info; + literal->expr_compound_literal.initializer = initializer_list; + if (!sema_analyse_expr(context, args[i] = literal)) return false; + } + } param->var.init_expr = args[i]; VarDeclKind kind = param->var.kind; if (kind == VARDECL_PARAM_CT_TYPE || kind == VARDECL_PARAM_CT) @@ -2096,7 +2125,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Decl **body_params = call_expr->call_expr.body_arguments; unsigned body_params_count = vec_size(body_params); - Decl **macro_body_params = decl->macro_decl.body_param ? declptr(decl->macro_decl.body_param)->body_params : NULL; + Decl **macro_body_params = decl->func_decl.body_param ? declptr(decl->func_decl.body_param)->body_params : NULL; unsigned expected_body_params = vec_size(macro_body_params); if (expected_body_params > body_params_count) { @@ -2134,7 +2163,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type); } - Ast *body = ast_macro_copy(astptr(decl->macro_decl.body)); + Ast *body = ast_macro_copy(astptr(decl->func_decl.body)); DynamicScope old_scope = context->active_scope; context_change_scope_with_flags(context, SCOPE_NONE); @@ -2142,10 +2171,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s SemaContext macro_context; Type *rtype = NULL; - sema_context_init(¯o_context, decl->macro_decl.unit); + sema_context_init(¯o_context, decl->unit); macro_context.compilation_unit = context->unit; macro_context.current_function = context->current_function; - rtype = decl->macro_decl.rtype ? type_infoptr(decl->macro_decl.rtype)->type : NULL; + rtype = decl->func_decl.signature.rtype ? type_infoptr(decl->func_decl.signature.rtype)->type : NULL; macro_context.expected_block_type = rtype; bool may_failable = true; if (rtype) @@ -2170,26 +2199,30 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s macro_context.yield_body = body_id ? astptr(body_id) : NULL; macro_context.yield_params = body_params; macro_context.yield_context = context; + macro_context.macro_varargs = call_expr->call_expr.varargs; macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : call_expr->span.row; - + macro_context.macro_params = params; BlockExit** block_exit_ref = MALLOCS(BlockExit*); macro_context.block_exit_ref = block_exit_ref; VECEACH(params, i) { Decl *param = params[i]; + // Skip raw vararg + if (!param) continue; if (!sema_add_local(¯o_context, param)) goto EXIT_FAIL; } AstId assert_first = 0; AstId* next = &assert_first; - if (!sema_analyse_contracts(¯o_context, decl->macro_decl.docs, &next)) return false; + if (!sema_analyse_contracts(¯o_context, decl->func_decl.docs, &next)) return false; sema_append_contract_asserts(assert_first, body); if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL; - bool is_no_return = decl->macro_decl.attr_noreturn; + params = macro_context.macro_params; + bool is_no_return = decl->func_decl.signature.attrs.noreturn; if (!vec_size(macro_context.returns)) { @@ -2249,12 +2282,12 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Type *type = call_expr->type; if (type != type_void) { - if (decl->macro_decl.attr_nodiscard) + if (decl->func_decl.signature.attrs.nodiscard) { SEMA_ERROR(call_expr, "The result of the macro must be used."); return SCOPE_POP_ERROR(); } - if (type_is_optional(type) && !decl->macro_decl.attr_maydiscard) + if (type_is_optional(type) && !decl->func_decl.signature.attrs.maydiscard) { SEMA_ERROR(call_expr, "The optional result of the macro must be used."); return SCOPE_POP_ERROR(); @@ -2285,7 +2318,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s call_expr->expr_kind = EXPR_MACRO_BLOCK; call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt; call_expr->macro_block.params = params; - call_expr->macro_block.args = args; call_expr->macro_block.block_exit = block_exit_ref; EXIT: assert(context->active_scope.defer_last == context->active_scope.defer_start); @@ -2301,102 +2333,11 @@ static inline Decl *sema_generate_generic_function(SemaContext *context, Expr *c return NULL; } -static inline bool sema_expr_analyse_generic_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable) -{ - assert(decl->decl_kind == DECL_GENERIC); - - Expr **args = call_expr->call_expr.arguments; - Decl **func_params = decl->macro_decl.parameters; - - unsigned explicit_args = vec_size(args); - unsigned total_args = explicit_args; - - scratch_buffer_clear(); - scratch_buffer_append(decl->extname); - - // TODO revisit name mangling - if (struct_var) - { - total_args++; - scratch_buffer_append_char('@'); - scratch_buffer_append(struct_var->type->canonical->name); - } - - if (total_args != vec_size(func_params)) - { - // TODO HANDLE VARARGS - SEMA_ERROR(call_expr, "Mismatch on number of arguments."); - return false; - } - - unsigned offset = total_args - explicit_args; - for (unsigned i = 0; i < explicit_args; i++) - { - Expr *arg = args[i]; - Decl *param = func_params[i + offset]; - if (param->var.kind == VARDECL_PARAM_CT_TYPE) - { - if (!sema_analyse_expr_lvalue_fold_const(context, arg)) return false; - if (arg->expr_kind != EXPR_TYPEINFO) - { - SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); - return false; - } - scratch_buffer_append_char(','); - scratch_buffer_append(arg->type_expr->type->canonical->name); - continue; - } - if (param->var.type_info) - { - if (!sema_analyse_expr_rhs(context, param->var.type_info->type, arg, true)) return false; - } - else - { - if (!sema_analyse_expr(context, arg)) return false; - } - scratch_buffer_append_char(','); - scratch_buffer_append(arg->type->canonical->name); - } - const char *mangled_name = scratch_buffer_interned(); - Decl **generic_cache = decl->unit->module->generic_cache; - Decl *found = NULL; - VECEACH(generic_cache, i) - { - TODO - if (generic_cache[i]->extname == mangled_name) - { - found = generic_cache[i]; - break; - } - } - if (!found) - { - found = sema_generate_generic_function(context, call_expr, struct_var, decl, mangled_name); - } - // Remove type parameters from call. - for (unsigned i = 0; i < explicit_args; i++) - { - Decl *param = func_params[i + offset]; - if (param->var.kind == VARDECL_PARAM_CT_TYPE) - { - for (unsigned j = i + 1; j < explicit_args; j++) - { - args[j - 1] = args[j]; - } - explicit_args--; - i--; - } - } - vec_resize(args, explicit_args); - // Perform the normal func call on the found declaration. - return sema_expr_analyse_func_call(context, call_expr, found, struct_var, failable); -} - static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) { Decl *macro = macro_context->current_macro; assert(macro); - DeclId body_param = macro->macro_decl.body_param; + DeclId body_param = macro->func_decl.body_param; assert(body_param); ExprCall *call_expr = &call->call_expr; @@ -2431,7 +2372,6 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) Ast *first_defer = NULL; SemaContext *context = macro_context->yield_context; Decl **params = macro_context->yield_params; - Expr *func_expr = exprptr(call_expr->function); assert(func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION); expr_replace(call, func_expr); @@ -2499,7 +2439,7 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl case DECL_GENERIC: expr->call_expr.func_ref = declid(decl); expr->call_expr.is_func_ref = true; - return sema_expr_analyse_generic_call(context, expr, struct_var, decl, failable); + TODO // Maybe generics won't happen case DECL_POISONED: return false; default: @@ -2820,14 +2760,8 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr) switch (decl->decl_kind) { case DECL_MACRO: - if (decl->macro_decl.parameters[0]->type->type_kind == TYPE_POINTER) - { - expr_insert_addr(func_expr->access_expr.parent); - } - struct_var = func_expr->access_expr.parent; - break; case DECL_FUNC: - if (decl->func_decl.function_signature.params[0]->type->type_kind == TYPE_POINTER) + if (decl->func_decl.signature.params[0]->type->type_kind == TYPE_POINTER) { expr_insert_addr(func_expr->access_expr.parent); } @@ -3512,6 +3446,7 @@ static void add_members_to_context(SemaContext *context, Decl *decl) static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child, bool *missing) { RETRY: + assert(child->resolve_status != RESOLVE_DONE); switch (child->expr_kind) { case EXPR_IDENTIFIER: @@ -4272,15 +4207,18 @@ CHECK_DEEPER: return false; } - // Transform bitstruct access to expr_bitaccess. - if (member->var.kind == VARDECL_BITMEMBER) + if (member->decl_kind == DECL_VAR) { - expr->expr_kind = EXPR_BITACCESS; - } - if (member->var.kind == VARDECL_MEMBER && expr_is_const_list(current_parent)) - { - sema_expr_fold_to_member(expr, current_parent, member); - return true; + if (member->var.kind == VARDECL_BITMEMBER) + { + // Transform bitstruct access to expr_bitaccess. + expr->expr_kind = EXPR_BITACCESS; + } + else if (member->var.kind == VARDECL_MEMBER && expr_is_const_list(current_parent)) + { + sema_expr_fold_to_member(expr, current_parent, member); + return true; + } } // 13. Copy properties. expr->access_expr.parent = current_parent; @@ -7615,6 +7553,9 @@ RETRY: if (!decl_ok(decl)) return poisoned_type; return decl->type->canonical; } + case TYPE_INFO_VAARG_TYPE: + if (!sema_resolve_type_info(context, type_info)) return poisoned_type; + return type_info->type->canonical; case TYPE_INFO_EXPRESSION: if (!sema_resolve_type_info(context, type_info)) return poisoned_type; return type_info->type; @@ -7771,18 +7712,104 @@ static inline bool sema_expr_analyse_variant(SemaContext *context, Expr *expr) static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) { + assert(expr->resolve_status == RESOLVE_RUNNING); + TokenType type = expr->ct_arg_expr.type; 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)); + SEMA_ERROR(expr, "'%s' can only be used inside of a macro.", token_type_to_string(type)); return false; } - switch (expr->ct_arg_expr.type) + switch (type) { case TOKEN_CT_VAARG_COUNT: expr_rewrite_const_int(expr, type_usize, vec_size(context->macro_varargs), true); return true; + case TOKEN_CT_VAARG_GET_ARG: + { + // A normal argument, this means we only evaluate it once. + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false); + + Decl *decl = NULL; + // Try to find the original param. + foreach(Decl*, context->macro_params) + { + if (!val) continue; + if (val->var.init_expr == arg_expr) + { + decl = val; + break; + } + } + // Not found, so generate a new. + if (!decl) + { + decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM, arg_expr->span); + decl->var.init_expr = arg_expr; + vec_add(context->macro_params, decl); + } + // Replace with the identifier. + expr->expr_kind = EXPR_IDENTIFIER; + expr->identifier_expr = (ExprIdentifier) { .decl = decl }; + expr->resolve_status = RESOLVE_DONE; + expr->type = decl->type; + return true; + } + case TOKEN_CT_VAARG_GET_EXPR: + { + // An expr argument, this means we copy and evaluate. + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false); + expr_replace(expr, expr_macro_copy(arg_expr)); + return true; + } + case TOKEN_CT_VAARG_GET_CONST: + { + // An expr argument, this means we copy and evaluate. + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false); + arg_expr = expr_macro_copy(arg_expr); + if (!expr_is_constant_eval(arg_expr, CONSTANT_EVAL_CONSTANT_VALUE)) + { + SEMA_ERROR(arg_expr, "This argument needs to be a compile time constant."); + return false; + } + expr_replace(expr, arg_expr); + return true; + } + case TOKEN_CT_VAARG_GET_REF: + { + // A normal argument, this means we only evaluate it once. + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false); + + if (!sema_check_expr_lvalue(arg_expr, arg_expr)) return false; + + Decl *decl = NULL; + // Try to find the original param. + foreach(Decl*, context->macro_params) + { + if (!val) continue; + if (val->var.init_expr == arg_expr) + { + decl = val; + decl->var.kind = VARDECL_PARAM_REF; + break; + } + } + // Not found, so generate a new. + if (!decl) + { + decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM_REF, arg_expr->span); + decl->var.init_expr = arg_expr; + vec_add(context->macro_params, decl); + } + // Replace with the identifier. + expr->expr_kind = EXPR_IDENTIFIER; + expr->identifier_expr = (ExprIdentifier) { .decl = decl }; + expr->resolve_status = RESOLVE_DONE; + expr->type = decl->type; + return true; + } + case TOKEN_CT_VAARG_GET_TYPE: default: - TODO; + UNREACHABLE; } } @@ -8133,7 +8160,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) case EXPR_MACRO_BODY_EXPANSION: if (!expr->body_expansion_expr.ast) { - SEMA_ERROR(expr, "'@%s' must be followed by ().", declptr(context->current_macro->macro_decl.body_param)->name); + SEMA_ERROR(expr, "'@%s' must be followed by ().", declptr(context->current_macro->func_decl.body_param)->name); return false; } break; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index b77637fb1..fb4831dba 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -77,6 +77,7 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_analyse_expr_lvalue_fold_const(SemaContext *context, Expr *expr); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable); +Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr); void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string); const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing); extern const char *ct_eval_error; diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 8645cdda8..6b98144ea 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -482,18 +482,7 @@ Decl *sema_find_extension_method_in_module(Module *module, Type *type, const cha { Decl *extension = extensions[i]; if (extension->name != method_name) continue; - switch (extension->decl_kind) - { - case DECL_FUNC: - if (type_infoptr(extension->func_decl.type_parent)->type->canonical == type) return extension; - break; - case DECL_MACRO: - case DECL_GENERIC: - if (type_infoptr(extension->macro_decl.type_parent)->type->canonical == type) return extension; - break; - default: - UNREACHABLE - } + if (type_infoptr(extension->func_decl.type_parent)->type->canonical == type) return extension; } return NULL; } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9bdc90ff7..0231fe0e3 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -324,6 +324,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) return false; } + assert(ident->resolve_status != RESOLVE_DONE); if (ident->identifier_expr.path) { SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); @@ -445,6 +446,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) return false; } + assert(ident->resolve_status != RESOLVE_DONE); if (ident->identifier_expr.path) { SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); @@ -1048,8 +1050,8 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen return false; } index_macro = value_by_ref ? by_ref : by_val; - index_type = index_macro->macro_decl.parameters[1]->type; - TypeInfoId rtype = index_macro->macro_decl.rtype; + index_type = index_macro->func_decl.signature.params[1]->type; + TypeInfoId rtype = index_macro->func_decl.signature.rtype; value_type = rtype ? type_infoptr(rtype)->type : NULL; } @@ -2244,8 +2246,6 @@ static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement) static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement) { bool success = false; - // Enter for scope - SCOPE_OUTER_START ExprId init; if ((init = statement->for_stmt.init)) @@ -2262,16 +2262,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE)) { SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in a '$for'"); - goto EXIT_ERROR; + return false; } - if (!sema_analyse_var_decl_ct(context, decl)) goto EXIT_ERROR; + if (!sema_analyse_var_decl_ct(context, decl)) return false; continue; } - if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR; + if (!sema_analyse_expr(context, expr)) return false; if (!expr_is_constant_eval(expr, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(expr, "Only constant expressions are allowed."); - goto EXIT_ERROR; + return false; } } } @@ -2284,16 +2284,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement for (int i = 0; i < MAX_MACRO_ITERATIONS; i++) { Expr *copy = expr_macro_copy(exprptr(condition)); - if (!sema_analyse_cond_expr(context, copy)) goto EXIT_ERROR; + if (!sema_analyse_cond_expr(context, copy)) return false; if (copy->expr_kind != EXPR_CONST) { SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time."); - goto EXIT_ERROR; + return false; } if (!copy->const_expr.b) break; Ast *compound_stmt = ast_macro_copy(body); - if (!sema_analyse_compound_stmt(context, compound_stmt)) goto EXIT_ERROR; + if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) return false; *current = astid(compound_stmt); current = &compound_stmt->next; @@ -2303,21 +2303,18 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement VECEACH(exprs, j) { copy = expr_macro_copy(exprs[j]); - if (!sema_analyse_expr(context, copy)) goto EXIT_ERROR; + if (!sema_analyse_expr(context, copy)) return false; if (copy->expr_kind != EXPR_CONST) { SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time."); - goto EXIT_ERROR; + return false; } } } } statement->ast_kind = AST_COMPOUND_STMT; statement->compound_stmt.first_stmt = start; - success = true; - EXIT_ERROR: - SCOPE_OUTER_END; - return success; + return true; } @@ -2544,10 +2541,10 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts) bool sema_analyse_function_body(SemaContext *context, Decl *func) { if (!decl_ok(func)) return false; - FunctionSignature *signature = &func->func_decl.function_signature; + Signature *signature = &func->func_decl.signature; FunctionPrototype *prototype = func->type->function.prototype; context->current_function = func; - context->current_function_pure = func->func_decl.function_signature.is_pure; + context->current_function_pure = func->func_decl.signature.attrs.is_pure; context->rtype = prototype->rtype; context->active_scope = (DynamicScope) { .scope_id = 0, diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 4a717ed09..4e8ac0e69 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -254,6 +254,21 @@ RETRY: { case TYPE_INFO_POISON: UNREACHABLE + case TYPE_INFO_VAARG_TYPE: + if (context->current_macro) + { + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, type_info->unresolved_type_expr), false); + if (arg_expr->expr_kind != EXPR_TYPEINFO) + { + SEMA_ERROR(arg_expr, "The argument was not a type."); + return false; + } + *type_info = *arg_expr->type_expr; + assert(type_info->resolve_status == RESOLVE_DONE); + return true; + } + SEMA_ERROR(type_info, "'%s' can only be used inside of a macro.", token_type_to_string(TOKEN_CT_VAARG_GET_TYPE)); + return false; case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: if (!sema_resolve_type_identifier(context, type_info)) return false; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 020cc68c1..3add80463 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -321,12 +321,12 @@ void sema_analysis_run(void) { error_exit("'%s::%s' is not a function.", path->module, ident); } - Decl **params = decl->func_decl.function_signature.params; + Decl **params = decl->func_decl.signature.params; if (vec_size(params) != 4 || params[0]->type != type_get_ptr(type_char) || params[1]->type != type_get_ptr(type_char) || params[2]->type != type_get_ptr(type_char) || params[3]->type != type_uint - || typeinfotype(decl->func_decl.function_signature.returntype) != type_void) + || typeinfotype(decl->func_decl.signature.rtype) != type_void) { error_exit("Expected panic function to have the signature fn void(char*, char*, uint, uint)."); } diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 5b812fe6f..4f32ac281 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -401,6 +401,6 @@ const char *token_type_to_string(TokenType type) bool token_is_any_type(TokenType type) { - return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT; + return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT || type == TOKEN_CT_VAARG_GET_TYPE; } diff --git a/src/compiler/types.c b/src/compiler/types.c index 7fd555fb5..0e826b3d6 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1080,10 +1080,10 @@ void type_func_prototype_init(uint32_t capacity) map.max_load = (uint32_t)(TABLE_MAX_LOAD * capacity); } -static uint32_t hash_function(FunctionSignature *sig) +static uint32_t hash_function(Signature *sig) { uintptr_t hash = (unsigned)sig->variadic; - hash = hash * 31 + (uintptr_t)type_infoptr(sig->returntype)->type->canonical; + hash = hash * 31 + (uintptr_t)type_infoptr(sig->rtype)->type->canonical; Decl **params = sig->params; VECEACH(params, i) { @@ -1093,14 +1093,14 @@ static uint32_t hash_function(FunctionSignature *sig) return (uint32_t)((hash >> 16) ^ hash); } -static int compare_function(FunctionSignature *sig, FunctionPrototype *proto) +static int compare_function(Signature *sig, FunctionPrototype *proto) { if (sig->variadic != proto->variadic) return -1; Decl **params = sig->params; Type **other_params = proto->param_types; unsigned param_count = vec_size(params); if (param_count != vec_size(other_params)) return -1; - if (type_infoptr(sig->returntype)->type->canonical != proto->rtype->canonical) return -1; + if (type_infoptr(sig->rtype)->type->canonical != proto->rtype->canonical) return -1; VECEACH(params, i) { Decl *param = params[i]; @@ -1111,13 +1111,13 @@ static int compare_function(FunctionSignature *sig, FunctionPrototype *proto) } -static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI abi, uint32_t hash, FuncTypeEntry *entry) +static inline Type *func_create_new_func_proto(Signature *sig, CallABI abi, uint32_t hash, FuncTypeEntry *entry) { 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; + Type *rtype = type_infoptr(sig->rtype)->type; proto->rtype = rtype; if (type_is_optional(rtype)) { @@ -1163,10 +1163,14 @@ static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI a } scratch_buffer_append_char(')'); Type *type = type_new(TYPE_FUNC, scratch_buffer_interned()); + Signature *copy_sig = CALLOCS(Signature); + *copy_sig = *sig; + copy_sig->attrs = (CalleeAttributes) { .nodiscard = false }; + copy_sig->params = proto->param_copy; proto->raw_type = type; type->function.prototype = proto; type->function.module = NULL; - type->function.params = proto->param_copy; + type->function.signature = copy_sig; type->canonical = type; entry->key = hash; entry->value = type; @@ -1202,7 +1206,7 @@ static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI a return type; } -Type *type_get_func(FunctionSignature *signature, CallABI abi) +Type *type_get_func(Signature *signature, CallABI abi) { uint32_t hash = hash_function(signature); uint32_t mask = map.capacity - 1; diff --git a/test/test_suite/compile_time/ct_for.c3t b/test/test_suite/compile_time/ct_for.c3t index 0d86c654a..7d8994ea8 100644 --- a/test/test_suite/compile_time/ct_for.c3t +++ b/test/test_suite/compile_time/ct_for.c3t @@ -11,7 +11,7 @@ fn void main() printf("Foo %d\n", $i); $endfor; - $for (var $i = 0, var $j = 100; $i < 4;): + $for ($i = 0, var $j = 100; $i < 4;): printf("Foo %d %d\n", $i++, $j--); $endfor; diff --git a/test/test_suite/functions/macro_arguments.c3 b/test/test_suite/functions/macro_arguments.c3 index 88ca22ee6..b893f9081 100644 --- a/test/test_suite/functions/macro_arguments.c3 +++ b/test/test_suite/functions/macro_arguments.c3 @@ -7,4 +7,6 @@ fn void foo3(bar) { } // #error: Only typed parameters are allowed for functions fn void foo4($Type) { } // #error: Only regular parameters are allowed for functions. -fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions. \ No newline at end of file +fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions. + +fn void foo9(int x, int x) {} // #error: Duplicate parameter name 'x'. \ No newline at end of file diff --git a/test/test_suite/macros/macro_calls_prefix.c3 b/test/test_suite/macros/macro_calls_prefix.c3 index 316bdca8b..6d93d08af 100644 --- a/test/test_suite/macros/macro_calls_prefix.c3 +++ b/test/test_suite/macros/macro_calls_prefix.c3 @@ -2,10 +2,10 @@ macro foo(a, $b, $Type) {} macro @foo2(a, $b, $Type) {} -macro bar(&x) // #error: non-function +macro bar(&x) // #error: Ref and expression parameters {} -macro baz(#y) {} // #error: non-function +macro baz(#y) {} // #error: Ref and expression parameters -macro baz2(a; @body()) {} // #error: non-function +macro baz2(a; @body()) {} // #error: Names of macros diff --git a/test/test_suite/macros/macro_typed_varargs.c3t b/test/test_suite/macros/macro_typed_varargs.c3t new file mode 100644 index 000000000..5d27a6511 --- /dev/null +++ b/test/test_suite/macros/macro_typed_varargs.c3t @@ -0,0 +1,210 @@ +// #target: macos-x64 + +module test; + +import std::io; + +macro foo(int... x) +{ + foreach (i : x) + { + io::printfln("%d", i); + } +} + +macro foo2(x...) +{ + foreach (i : x) + { + io::printfln("%d", *(int*)i); + } +} + +fn void main() +{ + foo(1, -1, 3141, 999 + 1); + foo2(1, -1, 3141, 999 + 1); +} + +/* #expect: test.ll + + +define void @test_main() #0 { +entry: + %x = alloca %"int[]", align 8 + %literal = alloca [4 x i32], align 16 + %.anon = alloca i64, align 8 + %.anon1 = alloca i64, align 8 + %i = alloca i32, align 4 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %x4 = alloca %"variant[]", align 8 + %literal5 = alloca [4 x %variant], align 16 + %taddr6 = alloca i32, align 4 + %taddr7 = alloca i32, align 4 + %taddr8 = alloca i32, align 4 + %taddr9 = alloca i32, align 4 + %.anon10 = alloca i64, align 8 + %.anon11 = alloca i64, align 8 + %i15 = alloca %variant, align 8 + %retparam17 = alloca i64, align 8 + %taddr18 = alloca %"char[]", align 8 + %vararg21 = alloca %"variant[]", align 8 + %varargslots22 = alloca [1 x %variant], align 16 + %0 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 0 + store i32 1, i32* %0, align 4 + %1 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 1 + store i32 -1, i32* %1, align 4 + %2 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 2 + store i32 3141, i32* %2, align 4 + %3 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 3 + store i32 1000, i32* %3, align 4 + %4 = bitcast [4 x i32]* %literal to i32* + %5 = insertvalue %"int[]" undef, i32* %4, 0 + %6 = insertvalue %"int[]" %5, i64 4, 1 + store %"int[]" %6, %"int[]"* %x, align 8 + %7 = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 + %8 = load i64, i64* %7, align 8 + store i64 %8, i64* %.anon, align 8 + store i64 0, i64* %.anon1, align 8 + br label %loop.cond + +loop.cond: ; preds = %voiderr, %entry + %9 = load i64, i64* %.anon1, align 8 + %10 = load i64, i64* %.anon, align 8 + %lt = icmp ult i64 %9, %10 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %11 = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0 + %12 = load i32*, i32** %11, align 8 + %13 = load i64, i64* %.anon1, align 8 + %ptroffset = getelementptr inbounds i32, i32* %12, i64 %13 + %14 = load i32, i32* %ptroffset, align 4 + store i32 %14, i32* %i, align 4 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8 + %15 = bitcast %"char[]"* %taddr to { i8*, i64 }* + %16 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %15, i32 0, i32 0 + %lo = load i8*, i8** %16, align 8 + %17 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %15, i32 0, i32 1 + %hi = load i64, i64* %17, align 8 + %18 = bitcast i32* %i to i8* + %19 = insertvalue %variant undef, i8* %18, 0 + %20 = insertvalue %variant %19, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %21 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0 + store %variant %20, %variant* %21, align 16 + %22 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + store i64 1, i64* %22, align 8 + %23 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %24 = bitcast [1 x %variant]* %varargslots to %variant* + store %variant* %24, %variant** %23, align 8 + %25 = bitcast %"variant[]"* %vararg to { i8*, i64 }* + %26 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 0 + %lo2 = load i8*, i8** %26, align 8 + %27 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 1 + %hi3 = load i64, i64* %27, align 8 + %28 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3) + %not_err = icmp eq i64 %28, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %loop.body + br label %voiderr + +voiderr: ; preds = %after_check, %loop.body + %29 = load i64, i64* %.anon1, align 8 + %add = add i64 %29, 1 + store i64 %add, i64* %.anon1, align 8 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + %30 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 0 + store i32 1, i32* %taddr6, align 4 + %31 = bitcast i32* %taddr6 to i8* + %32 = insertvalue %variant undef, i8* %31, 0 + %33 = insertvalue %variant %32, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + store %variant %33, %variant* %30, align 8 + %34 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 1 + store i32 -1, i32* %taddr7, align 4 + %35 = bitcast i32* %taddr7 to i8* + %36 = insertvalue %variant undef, i8* %35, 0 + %37 = insertvalue %variant %36, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + store %variant %37, %variant* %34, align 8 + %38 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 2 + store i32 3141, i32* %taddr8, align 4 + %39 = bitcast i32* %taddr8 to i8* + %40 = insertvalue %variant undef, i8* %39, 0 + %41 = insertvalue %variant %40, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + store %variant %41, %variant* %38, align 8 + %42 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 3 + store i32 1000, i32* %taddr9, align 4 + %43 = bitcast i32* %taddr9 to i8* + %44 = insertvalue %variant undef, i8* %43, 0 + %45 = insertvalue %variant %44, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + store %variant %45, %variant* %42, align 8 + %46 = bitcast [4 x %variant]* %literal5 to %variant* + %47 = insertvalue %"variant[]" undef, %variant* %46, 0 + %48 = insertvalue %"variant[]" %47, i64 4, 1 + store %"variant[]" %48, %"variant[]"* %x4, align 8 + %49 = getelementptr inbounds %"variant[]", %"variant[]"* %x4, i32 0, i32 1 + %50 = load i64, i64* %49, align 8 + store i64 %50, i64* %.anon10, align 8 + store i64 0, i64* %.anon11, align 8 + br label %loop.cond12 + +loop.cond12: ; preds = %voiderr27, %loop.exit + %51 = load i64, i64* %.anon11, align 8 + %52 = load i64, i64* %.anon10, align 8 + %lt13 = icmp ult i64 %51, %52 + br i1 %lt13, label %loop.body14, label %loop.exit29 + +loop.body14: ; preds = %loop.cond12 + %53 = getelementptr inbounds %"variant[]", %"variant[]"* %x4, i32 0, i32 0 + %54 = load %variant*, %variant** %53, align 8 + %55 = load i64, i64* %.anon11, align 8 + %ptroffset16 = getelementptr inbounds %variant, %variant* %54, i64 %55 + %56 = bitcast %variant* %i15 to i8* + %57 = bitcast %variant* %ptroffset16 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %56, i8* align 8 %57, i32 16, i1 false) + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0), i64 2 }, %"char[]"* %taddr18, align 8 + %58 = bitcast %"char[]"* %taddr18 to { i8*, i64 }* + %59 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %58, i32 0, i32 0 + %lo19 = load i8*, i8** %59, align 8 + %60 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %58, i32 0, i32 1 + %hi20 = load i64, i64* %60, align 8 + %61 = getelementptr inbounds %variant, %variant* %i15, i32 0, i32 0 + %62 = bitcast i8** %61 to i32** + %63 = load i32*, i32** %62, align 8 + %64 = bitcast i32* %63 to i8* + %65 = insertvalue %variant undef, i8* %64, 0 + %66 = insertvalue %variant %65, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %67 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots22, i64 0, i64 0 + store %variant %66, %variant* %67, align 16 + %68 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg21, i32 0, i32 1 + store i64 1, i64* %68, align 8 + %69 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg21, i32 0, i32 0 + %70 = bitcast [1 x %variant]* %varargslots22 to %variant* + store %variant* %70, %variant** %69, align 8 + %71 = bitcast %"variant[]"* %vararg21 to { i8*, i64 }* + %72 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %71, i32 0, i32 0 + %lo23 = load i8*, i8** %72, align 8 + %73 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %71, i32 0, i32 1 + %hi24 = load i64, i64* %73, align 8 + %74 = call i64 @std_io_printfln(i64* %retparam17, i8* %lo19, i64 %hi20, i8* %lo23, i64 %hi24) + %not_err25 = icmp eq i64 %74, 0 + br i1 %not_err25, label %after_check26, label %voiderr27 + +after_check26: ; preds = %loop.body14 + br label %voiderr27 + +voiderr27: ; preds = %after_check26, %loop.body14 + %75 = load i64, i64* %.anon11, align 8 + %add28 = add i64 %75, 1 + store i64 %add28, i64* %.anon11, align 8 + br label %loop.cond12 + +loop.exit29: ; preds = %loop.cond12 + ret void +} + diff --git a/test/test_suite/macros/macro_untyped_varargs.c3 b/test/test_suite/macros/macro_untyped_varargs.c3 new file mode 100644 index 000000000..6360af3c1 --- /dev/null +++ b/test/test_suite/macros/macro_untyped_varargs.c3 @@ -0,0 +1,25 @@ +macro foo(...) +{ + $vaarg_get_arg("hello"); // #error: Expected the argument index here + int x; + $vaarg_get_arg(x); // #error: Vararg functions need a constant argument + $vaarg_get_arg(-1); // #error: negative + $vaarg_get_arg(100); // #error: varargs exist +} + +macro foo2(...) +{ + $vaarg_get_const(0); +} + +macro foo3(...) +{ + $vaarg_get_type(0) a; +} +fn void main() +{ + foo(1, -1, 3141, 999 + 1); + int x; + foo2(x); // #error: This argument needs to be a compile time constant + foo3(3); // #error: The argument was not a type. +} diff --git a/test/test_suite/macros/macro_untyped_varargs_2.c3t b/test/test_suite/macros/macro_untyped_varargs_2.c3t new file mode 100644 index 000000000..7f736d08d --- /dev/null +++ b/test/test_suite/macros/macro_untyped_varargs_2.c3t @@ -0,0 +1,463 @@ +// #target: macos-x64 + +module test; + +import std::io; + +macro @foo(...) +{ + int i = $vaarg_get_arg(1) + $vaarg_get_arg(1); + int j = $vaarg_get_expr(2) + $vaarg_get_expr(2); + $for (var $i = 0; $i < $vaarg_count(); $i++): + io::printfln("%d", $vaarg_get_arg($i)); + $endfor; +} + +macro foo2(...) +{ + $for (var $i = 0; $i < $vaarg_count(); $i++): + { + $vaarg_get_type($i) x; + } + io::printfln("%s", $nameof($vaarg_get_type($i))); + $endfor; +} + +macro foo3(...) +{ + var $x = 0; + $for (var $i = 0; $i < $vaarg_count(); $i++): + $x += $vaarg_get_const($i); + $endfor; + return $x; +} + +macro @foo4(...) +{ + $typeof($vaarg_get_ref(0)) a = $vaarg_get_ref(0); + $vaarg_get_ref(0) = $vaarg_get_ref(1); + $vaarg_get_ref(1) = a; +} +fn int ping(int val) +{ + io::printfln("Ping[%d]", val); + return val; +} + +fn void main() +{ + @foo(ping(1), ping(-1), ping(3141), ping(999 + 1)); + foo2(int, double); + var $x = foo3(1, 4, 100); + io::printfln("%d", $x); + int x = 123; + int y = 33; + @foo4(x, y); + io::printfln("%d, %d", x, y); + @foo4(x, y); + io::printfln("%d, %d", x, y); +} + +/* #expect: test.ll + +define i32 @test_ping(i32 %0) #0 { +entry: + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %taddr1 = alloca i32, align 4 + store %"char[]" { i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i64 8 }, %"char[]"* %taddr, align 8 + %1 = bitcast %"char[]"* %taddr to { i8*, i64 }* + %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0 + %lo = load i8*, i8** %2, align 8 + %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1 + %hi = load i64, i64* %3, align 8 + store i32 %0, i32* %taddr1, align 4 + %4 = bitcast i32* %taddr1 to i8* + %5 = insertvalue %variant undef, i8* %4, 0 + %6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %7 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0 + store %variant %6, %variant* %7, align 16 + %8 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + store i64 1, i64* %8, align 8 + %9 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %10 = bitcast [1 x %variant]* %varargslots to %variant* + store %variant* %10, %variant** %9, align 8 + %11 = bitcast %"variant[]"* %vararg to { i8*, i64 }* + %12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 0 + %lo2 = load i8*, i8** %12, align 8 + %13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 1 + %hi3 = load i64, i64* %13, align 8 + %14 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3) + %not_err = icmp eq i64 %14, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %entry + br label %voiderr + +voiderr: ; preds = %after_check, %entry + ret i32 %0 +} + +define void @test_main() #0 { +entry: + %.anon = alloca i32, align 4 + %.anon1 = alloca i32, align 4 + %.anon2 = alloca i32, align 4 + %.anon3 = alloca i32, align 4 + %i = alloca i32, align 4 + %j = alloca i32, align 4 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %retparam7 = alloca i64, align 8 + %taddr8 = alloca %"char[]", align 8 + %vararg11 = alloca %"variant[]", align 8 + %varargslots12 = alloca [1 x %variant], align 16 + %retparam18 = alloca i64, align 8 + %taddr19 = alloca %"char[]", align 8 + %vararg22 = alloca %"variant[]", align 8 + %varargslots23 = alloca [1 x %variant], align 16 + %retparam29 = alloca i64, align 8 + %taddr30 = alloca %"char[]", align 8 + %vararg33 = alloca %"variant[]", align 8 + %varargslots34 = alloca [1 x %variant], align 16 + %x = alloca i32, align 4 + %retparam40 = alloca i64, align 8 + %taddr41 = alloca %"char[]", align 8 + %vararg44 = alloca %"variant[]", align 8 + %varargslots45 = alloca [1 x %variant], align 16 + %taddr46 = alloca [3 x i8]*, align 8 + %x52 = alloca double, align 8 + %retparam53 = alloca i64, align 8 + %taddr54 = alloca %"char[]", align 8 + %vararg57 = alloca %"variant[]", align 8 + %varargslots58 = alloca [1 x %variant], align 16 + %taddr59 = alloca [6 x i8]*, align 8 + %retparam65 = alloca i64, align 8 + %taddr66 = alloca %"char[]", align 8 + %vararg69 = alloca %"variant[]", align 8 + %varargslots70 = alloca [1 x %variant], align 16 + %taddr71 = alloca i32, align 4 + %x77 = alloca i32, align 4 + %y = alloca i32, align 4 + %a = alloca i32, align 4 + %retparam78 = alloca i64, align 8 + %taddr79 = alloca %"char[]", align 8 + %vararg82 = alloca %"variant[]", align 8 + %varargslots83 = alloca [2 x %variant], align 16 + %a89 = alloca i32, align 4 + %retparam90 = alloca i64, align 8 + %taddr91 = alloca %"char[]", align 8 + %vararg94 = alloca %"variant[]", align 8 + %varargslots95 = alloca [2 x %variant], align 16 + %0 = call i32 @test_ping(i32 -1) + store i32 %0, i32* %.anon, align 4 + %1 = call i32 @test_ping(i32 1) + store i32 %1, i32* %.anon1, align 4 + %2 = call i32 @test_ping(i32 3141) + store i32 %2, i32* %.anon2, align 4 + %3 = call i32 @test_ping(i32 1000) + store i32 %3, i32* %.anon3, align 4 + %4 = load i32, i32* %.anon, align 4 + %5 = load i32, i32* %.anon, align 4 + %add = add i32 %4, %5 + store i32 %add, i32* %i, align 4 + %6 = call i32 @test_ping(i32 3141) + %7 = call i32 @test_ping(i32 3141) + %add4 = add i32 %6, %7 + store i32 %add4, i32* %j, align 4 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8 + %8 = bitcast %"char[]"* %taddr to { i8*, i64 }* + %9 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %8, i32 0, i32 0 + %lo = load i8*, i8** %9, align 8 + %10 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %8, i32 0, i32 1 + %hi = load i64, i64* %10, align 8 + %11 = bitcast i32* %.anon1 to i8* + %12 = insertvalue %variant undef, i8* %11, 0 + %13 = insertvalue %variant %12, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %14 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0 + store %variant %13, %variant* %14, align 16 + %15 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + store i64 1, i64* %15, align 8 + %16 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %17 = bitcast [1 x %variant]* %varargslots to %variant* + store %variant* %17, %variant** %16, align 8 + %18 = bitcast %"variant[]"* %vararg to { i8*, i64 }* + %19 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %18, i32 0, i32 0 + %lo5 = load i8*, i8** %19, align 8 + %20 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %18, i32 0, i32 1 + %hi6 = load i64, i64* %20, align 8 + %21 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo5, i64 %hi6) + %not_err = icmp eq i64 %21, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %entry + br label %voiderr + +voiderr: ; preds = %after_check, %entry + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2, i32 0, i32 0), i64 2 }, %"char[]"* %taddr8, align 8 + %22 = bitcast %"char[]"* %taddr8 to { i8*, i64 }* + %23 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %22, i32 0, i32 0 + %lo9 = load i8*, i8** %23, align 8 + %24 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %22, i32 0, i32 1 + %hi10 = load i64, i64* %24, align 8 + %25 = bitcast i32* %.anon to i8* + %26 = insertvalue %variant undef, i8* %25, 0 + %27 = insertvalue %variant %26, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %28 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots12, i64 0, i64 0 + store %variant %27, %variant* %28, align 16 + %29 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg11, i32 0, i32 1 + store i64 1, i64* %29, align 8 + %30 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg11, i32 0, i32 0 + %31 = bitcast [1 x %variant]* %varargslots12 to %variant* + store %variant* %31, %variant** %30, align 8 + %32 = bitcast %"variant[]"* %vararg11 to { i8*, i64 }* + %33 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %32, i32 0, i32 0 + %lo13 = load i8*, i8** %33, align 8 + %34 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %32, i32 0, i32 1 + %hi14 = load i64, i64* %34, align 8 + %35 = call i64 @std_io_printfln(i64* %retparam7, i8* %lo9, i64 %hi10, i8* %lo13, i64 %hi14) + %not_err15 = icmp eq i64 %35, 0 + br i1 %not_err15, label %after_check16, label %voiderr17 + +after_check16: ; preds = %voiderr + br label %voiderr17 + +voiderr17: ; preds = %after_check16, %voiderr + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.3, i32 0, i32 0), i64 2 }, %"char[]"* %taddr19, align 8 + %36 = bitcast %"char[]"* %taddr19 to { i8*, i64 }* + %37 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %36, i32 0, i32 0 + %lo20 = load i8*, i8** %37, align 8 + %38 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %36, i32 0, i32 1 + %hi21 = load i64, i64* %38, align 8 + %39 = bitcast i32* %.anon2 to i8* + %40 = insertvalue %variant undef, i8* %39, 0 + %41 = insertvalue %variant %40, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %42 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots23, i64 0, i64 0 + store %variant %41, %variant* %42, align 16 + %43 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg22, i32 0, i32 1 + store i64 1, i64* %43, align 8 + %44 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg22, i32 0, i32 0 + %45 = bitcast [1 x %variant]* %varargslots23 to %variant* + store %variant* %45, %variant** %44, align 8 + %46 = bitcast %"variant[]"* %vararg22 to { i8*, i64 }* + %47 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %46, i32 0, i32 0 + %lo24 = load i8*, i8** %47, align 8 + %48 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %46, i32 0, i32 1 + %hi25 = load i64, i64* %48, align 8 + %49 = call i64 @std_io_printfln(i64* %retparam18, i8* %lo20, i64 %hi21, i8* %lo24, i64 %hi25) + %not_err26 = icmp eq i64 %49, 0 + br i1 %not_err26, label %after_check27, label %voiderr28 + +after_check27: ; preds = %voiderr17 + br label %voiderr28 + +voiderr28: ; preds = %after_check27, %voiderr17 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.4, i32 0, i32 0), i64 2 }, %"char[]"* %taddr30, align 8 + %50 = bitcast %"char[]"* %taddr30 to { i8*, i64 }* + %51 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %50, i32 0, i32 0 + %lo31 = load i8*, i8** %51, align 8 + %52 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %50, i32 0, i32 1 + %hi32 = load i64, i64* %52, align 8 + %53 = bitcast i32* %.anon3 to i8* + %54 = insertvalue %variant undef, i8* %53, 0 + %55 = insertvalue %variant %54, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %56 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots34, i64 0, i64 0 + store %variant %55, %variant* %56, align 16 + %57 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg33, i32 0, i32 1 + store i64 1, i64* %57, align 8 + %58 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg33, i32 0, i32 0 + %59 = bitcast [1 x %variant]* %varargslots34 to %variant* + store %variant* %59, %variant** %58, align 8 + %60 = bitcast %"variant[]"* %vararg33 to { i8*, i64 }* + %61 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %60, i32 0, i32 0 + %lo35 = load i8*, i8** %61, align 8 + %62 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %60, i32 0, i32 1 + %hi36 = load i64, i64* %62, align 8 + %63 = call i64 @std_io_printfln(i64* %retparam29, i8* %lo31, i64 %hi32, i8* %lo35, i64 %hi36) + %not_err37 = icmp eq i64 %63, 0 + br i1 %not_err37, label %after_check38, label %voiderr39 + +after_check38: ; preds = %voiderr28 + br label %voiderr39 + +voiderr39: ; preds = %after_check38, %voiderr28 + store i32 0, i32* %x, align 4 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.5, i32 0, i32 0), i64 2 }, %"char[]"* %taddr41, align 8 + %64 = bitcast %"char[]"* %taddr41 to { i8*, i64 }* + %65 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %64, i32 0, i32 0 + %lo42 = load i8*, i8** %65, align 8 + %66 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %64, i32 0, i32 1 + %hi43 = load i64, i64* %66, align 8 + store [3 x i8]* bitcast ([4 x i8]* @.str.6 to [3 x i8]*), [3 x i8]** %taddr46, align 8 + %67 = bitcast [3 x i8]** %taddr46 to i8* + %68 = insertvalue %variant undef, i8* %67, 0 + %69 = insertvalue %variant %68, i64 ptrtoint (%.introspect* @"ct$p$a3$char" to i64), 1 + %70 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots45, i64 0, i64 0 + store %variant %69, %variant* %70, align 16 + %71 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg44, i32 0, i32 1 + store i64 1, i64* %71, align 8 + %72 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg44, i32 0, i32 0 + %73 = bitcast [1 x %variant]* %varargslots45 to %variant* + store %variant* %73, %variant** %72, align 8 + %74 = bitcast %"variant[]"* %vararg44 to { i8*, i64 }* + %75 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %74, i32 0, i32 0 + %lo47 = load i8*, i8** %75, align 8 + %76 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %74, i32 0, i32 1 + %hi48 = load i64, i64* %76, align 8 + %77 = call i64 @std_io_printfln(i64* %retparam40, i8* %lo42, i64 %hi43, i8* %lo47, i64 %hi48) + %not_err49 = icmp eq i64 %77, 0 + br i1 %not_err49, label %after_check50, label %voiderr51 + +after_check50: ; preds = %voiderr39 + br label %voiderr51 + +voiderr51: ; preds = %after_check50, %voiderr39 + store double 0.000000e+00, double* %x52, align 8 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.7, i32 0, i32 0), i64 2 }, %"char[]"* %taddr54, align 8 + %78 = bitcast %"char[]"* %taddr54 to { i8*, i64 }* + %79 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %78, i32 0, i32 0 + %lo55 = load i8*, i8** %79, align 8 + %80 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %78, i32 0, i32 1 + %hi56 = load i64, i64* %80, align 8 + store [6 x i8]* bitcast ([7 x i8]* @.str.8 to [6 x i8]*), [6 x i8]** %taddr59, align 8 + %81 = bitcast [6 x i8]** %taddr59 to i8* + %82 = insertvalue %variant undef, i8* %81, 0 + %83 = insertvalue %variant %82, i64 ptrtoint (%.introspect* @"ct$p$a6$char" to i64), 1 + %84 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots58, i64 0, i64 0 + store %variant %83, %variant* %84, align 16 + %85 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg57, i32 0, i32 1 + store i64 1, i64* %85, align 8 + %86 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg57, i32 0, i32 0 + %87 = bitcast [1 x %variant]* %varargslots58 to %variant* + store %variant* %87, %variant** %86, align 8 + %88 = bitcast %"variant[]"* %vararg57 to { i8*, i64 }* + %89 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %88, i32 0, i32 0 + %lo60 = load i8*, i8** %89, align 8 + %90 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %88, i32 0, i32 1 + %hi61 = load i64, i64* %90, align 8 + %91 = call i64 @std_io_printfln(i64* %retparam53, i8* %lo55, i64 %hi56, i8* %lo60, i64 %hi61) + %not_err62 = icmp eq i64 %91, 0 + br i1 %not_err62, label %after_check63, label %voiderr64 + +after_check63: ; preds = %voiderr51 + br label %voiderr64 + +voiderr64: ; preds = %after_check63, %voiderr51 + store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0), i64 2 }, %"char[]"* %taddr66, align 8 + %92 = bitcast %"char[]"* %taddr66 to { i8*, i64 }* + %93 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %92, i32 0, i32 0 + %lo67 = load i8*, i8** %93, align 8 + %94 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %92, i32 0, i32 1 + %hi68 = load i64, i64* %94, align 8 + store i32 105, i32* %taddr71, align 4 + %95 = bitcast i32* %taddr71 to i8* + %96 = insertvalue %variant undef, i8* %95, 0 + %97 = insertvalue %variant %96, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %98 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots70, i64 0, i64 0 + store %variant %97, %variant* %98, align 16 + %99 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg69, i32 0, i32 1 + store i64 1, i64* %99, align 8 + %100 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg69, i32 0, i32 0 + %101 = bitcast [1 x %variant]* %varargslots70 to %variant* + store %variant* %101, %variant** %100, align 8 + %102 = bitcast %"variant[]"* %vararg69 to { i8*, i64 }* + %103 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %102, i32 0, i32 0 + %lo72 = load i8*, i8** %103, align 8 + %104 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %102, i32 0, i32 1 + %hi73 = load i64, i64* %104, align 8 + %105 = call i64 @std_io_printfln(i64* %retparam65, i8* %lo67, i64 %hi68, i8* %lo72, i64 %hi73) + %not_err74 = icmp eq i64 %105, 0 + br i1 %not_err74, label %after_check75, label %voiderr76 + +after_check75: ; preds = %voiderr64 + br label %voiderr76 + +voiderr76: ; preds = %after_check75, %voiderr64 + store i32 123, i32* %x77, align 4 + store i32 33, i32* %y, align 4 + %106 = load i32, i32* %x77, align 4 + store i32 %106, i32* %a, align 4 + %107 = load i32, i32* %y, align 4 + store i32 %107, i32* %x77, align 4 + %108 = load i32, i32* %a, align 4 + store i32 %108, i32* %y, align 4 + store %"char[]" { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.10, i32 0, i32 0), i64 6 }, %"char[]"* %taddr79, align 8 + %109 = bitcast %"char[]"* %taddr79 to { i8*, i64 }* + %110 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 0 + %lo80 = load i8*, i8** %110, align 8 + %111 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 1 + %hi81 = load i64, i64* %111, align 8 + %112 = bitcast i32* %x77 to i8* + %113 = insertvalue %variant undef, i8* %112, 0 + %114 = insertvalue %variant %113, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %115 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots83, i64 0, i64 0 + store %variant %114, %variant* %115, align 16 + %116 = bitcast i32* %y to i8* + %117 = insertvalue %variant undef, i8* %116, 0 + %118 = insertvalue %variant %117, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %119 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots83, i64 0, i64 1 + store %variant %118, %variant* %119, align 16 + %120 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg82, i32 0, i32 1 + store i64 2, i64* %120, align 8 + %121 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg82, i32 0, i32 0 + %122 = bitcast [2 x %variant]* %varargslots83 to %variant* + store %variant* %122, %variant** %121, align 8 + %123 = bitcast %"variant[]"* %vararg82 to { i8*, i64 }* + %124 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %123, i32 0, i32 0 + %lo84 = load i8*, i8** %124, align 8 + %125 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %123, i32 0, i32 1 + %hi85 = load i64, i64* %125, align 8 + %126 = call i64 @std_io_printfln(i64* %retparam78, i8* %lo80, i64 %hi81, i8* %lo84, i64 %hi85) + %not_err86 = icmp eq i64 %126, 0 + br i1 %not_err86, label %after_check87, label %voiderr88 + +after_check87: ; preds = %voiderr76 + br label %voiderr88 + +voiderr88: ; preds = %after_check87, %voiderr76 + %127 = load i32, i32* %x77, align 4 + store i32 %127, i32* %a89, align 4 + %128 = load i32, i32* %y, align 4 + store i32 %128, i32* %x77, align 4 + %129 = load i32, i32* %a89, align 4 + store i32 %129, i32* %y, align 4 + store %"char[]" { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.11, i32 0, i32 0), i64 6 }, %"char[]"* %taddr91, align 8 + %130 = bitcast %"char[]"* %taddr91 to { i8*, i64 }* + %131 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %130, i32 0, i32 0 + %lo92 = load i8*, i8** %131, align 8 + %132 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %130, i32 0, i32 1 + %hi93 = load i64, i64* %132, align 8 + %133 = bitcast i32* %x77 to i8* + %134 = insertvalue %variant undef, i8* %133, 0 + %135 = insertvalue %variant %134, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %136 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots95, i64 0, i64 0 + store %variant %135, %variant* %136, align 16 + %137 = bitcast i32* %y to i8* + %138 = insertvalue %variant undef, i8* %137, 0 + %139 = insertvalue %variant %138, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + %140 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots95, i64 0, i64 1 + store %variant %139, %variant* %140, align 16 + %141 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg94, i32 0, i32 1 + store i64 2, i64* %141, align 8 + %142 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg94, i32 0, i32 0 + %143 = bitcast [2 x %variant]* %varargslots95 to %variant* + store %variant* %143, %variant** %142, align 8 + %144 = bitcast %"variant[]"* %vararg94 to { i8*, i64 }* + %145 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %144, i32 0, i32 0 + %lo96 = load i8*, i8** %145, align 8 + %146 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %144, i32 0, i32 1 + %hi97 = load i64, i64* %146, align 8 + %147 = call i64 @std_io_printfln(i64* %retparam90, i8* %lo92, i64 %hi93, i8* %lo96, i64 %hi97) + %not_err98 = icmp eq i64 %147, 0 + br i1 %not_err98, label %after_check99, label %voiderr100 + +after_check99: ; preds = %voiderr88 + br label %voiderr100 + +voiderr100: ; preds = %after_check99, %voiderr88 + ret void +} diff --git a/test/test_suite2/compile_time/ct_for.c3t b/test/test_suite2/compile_time/ct_for.c3t index 90a5a5fff..34d6b921b 100644 --- a/test/test_suite2/compile_time/ct_for.c3t +++ b/test/test_suite2/compile_time/ct_for.c3t @@ -11,7 +11,7 @@ fn void main() printf("Foo %d\n", $i); $endfor; - $for (var $i = 0, var $j = 100; $i < 4;): + $for ($i = 0, var $j = 100; $i < 4;): printf("Foo %d %d\n", $i++, $j--); $endfor; diff --git a/test/test_suite2/functions/macro_arguments.c3 b/test/test_suite2/functions/macro_arguments.c3 index 88ca22ee6..b893f9081 100644 --- a/test/test_suite2/functions/macro_arguments.c3 +++ b/test/test_suite2/functions/macro_arguments.c3 @@ -7,4 +7,6 @@ fn void foo3(bar) { } // #error: Only typed parameters are allowed for functions fn void foo4($Type) { } // #error: Only regular parameters are allowed for functions. -fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions. \ No newline at end of file +fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions. + +fn void foo9(int x, int x) {} // #error: Duplicate parameter name 'x'. \ No newline at end of file diff --git a/test/test_suite2/macros/macro_calls_prefix.c3 b/test/test_suite2/macros/macro_calls_prefix.c3 index 316bdca8b..6d93d08af 100644 --- a/test/test_suite2/macros/macro_calls_prefix.c3 +++ b/test/test_suite2/macros/macro_calls_prefix.c3 @@ -2,10 +2,10 @@ macro foo(a, $b, $Type) {} macro @foo2(a, $b, $Type) {} -macro bar(&x) // #error: non-function +macro bar(&x) // #error: Ref and expression parameters {} -macro baz(#y) {} // #error: non-function +macro baz(#y) {} // #error: Ref and expression parameters -macro baz2(a; @body()) {} // #error: non-function +macro baz2(a; @body()) {} // #error: Names of macros diff --git a/test/test_suite2/macros/macro_typed_varargs.c3t b/test/test_suite2/macros/macro_typed_varargs.c3t new file mode 100644 index 000000000..11733b4e1 --- /dev/null +++ b/test/test_suite2/macros/macro_typed_varargs.c3t @@ -0,0 +1,191 @@ +// #target: macos-x64 + +module test; + +import std::io; + +macro foo(int... x) +{ + foreach (i : x) + { + io::printfln("%d", i); + } +} + +macro foo2(x...) +{ + foreach (i : x) + { + io::printfln("%d", *(int*)i); + } +} + +fn void main() +{ + foo(1, -1, 3141, 999 + 1); + foo2(1, -1, 3141, 999 + 1); +} + +/* #expect: test.ll + +define void @test_main() #0 { +entry: + %x = alloca %"int[]", align 8 + %literal = alloca [4 x i32], align 16 + %.anon = alloca i64, align 8 + %.anon1 = alloca i64, align 8 + %i = alloca i32, align 4 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %x4 = alloca %"variant[]", align 8 + %literal5 = alloca [4 x %variant], align 16 + %taddr6 = alloca i32, align 4 + %taddr7 = alloca i32, align 4 + %taddr8 = alloca i32, align 4 + %taddr9 = alloca i32, align 4 + %.anon10 = alloca i64, align 8 + %.anon11 = alloca i64, align 8 + %i15 = alloca %variant, align 8 + %retparam17 = alloca i64, align 8 + %taddr18 = alloca %"char[]", align 8 + %vararg21 = alloca %"variant[]", align 8 + %varargslots22 = alloca [1 x %variant], align 16 + %0 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 0 + store i32 1, ptr %0, align 4 + %1 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 1 + store i32 -1, ptr %1, align 4 + %2 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 2 + store i32 3141, ptr %2, align 4 + %3 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 3 + store i32 1000, ptr %3, align 4 + %4 = insertvalue %"int[]" undef, ptr %literal, 0 + %5 = insertvalue %"int[]" %4, i64 4, 1 + store %"int[]" %5, ptr %x, align 8 + %6 = getelementptr inbounds %"int[]", ptr %x, i32 0, i32 1 + %7 = load i64, ptr %6, align 8 + store i64 %7, ptr %.anon, align 8 + store i64 0, ptr %.anon1, align 8 + br label %loop.cond + +loop.cond: ; preds = %voiderr, %entry + %8 = load i64, ptr %.anon1, align 8 + %9 = load i64, ptr %.anon, align 8 + %lt = icmp ult i64 %8, %9 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %10 = getelementptr inbounds %"int[]", ptr %x, i32 0, i32 0 + %11 = load ptr, ptr %10, align 8 + %12 = load i64, ptr %.anon1, align 8 + %ptroffset = getelementptr inbounds i32, ptr %11, i64 %12 + %13 = load i32, ptr %ptroffset, align 4 + store i32 %13, ptr %i, align 4 + store %"char[]" { ptr @.str, i64 2 }, ptr %taddr, align 8 + %14 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0 + %lo = load ptr, ptr %14, align 8 + %15 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1 + %hi = load i64, ptr %15, align 8 + %16 = insertvalue %variant undef, ptr %i, 0 + %17 = insertvalue %variant %16, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %18 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0 + store %variant %17, ptr %18, align 16 + %19 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1 + store i64 1, ptr %19, align 8 + %20 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0 + store ptr %varargslots, ptr %20, align 8 + %21 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0 + %lo2 = load ptr, ptr %21, align 8 + %22 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1 + %hi3 = load i64, ptr %22, align 8 + %23 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3) + %not_err = icmp eq i64 %23, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %loop.body + br label %voiderr + +voiderr: ; preds = %after_check, %loop.body + %24 = load i64, ptr %.anon1, align 8 + %add = add i64 %24, 1 + store i64 %add, ptr %.anon1, align 8 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + %25 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 0 + store i32 1, ptr %taddr6, align 4 + %26 = insertvalue %variant undef, ptr %taddr6, 0 + %27 = insertvalue %variant %26, i64 ptrtoint (ptr @"ct$int" to i64), 1 + store %variant %27, ptr %25, align 8 + %28 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 1 + store i32 -1, ptr %taddr7, align 4 + %29 = insertvalue %variant undef, ptr %taddr7, 0 + %30 = insertvalue %variant %29, i64 ptrtoint (ptr @"ct$int" to i64), 1 + store %variant %30, ptr %28, align 8 + %31 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 2 + store i32 3141, ptr %taddr8, align 4 + %32 = insertvalue %variant undef, ptr %taddr8, 0 + %33 = insertvalue %variant %32, i64 ptrtoint (ptr @"ct$int" to i64), 1 + store %variant %33, ptr %31, align 8 + %34 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 3 + store i32 1000, ptr %taddr9, align 4 + %35 = insertvalue %variant undef, ptr %taddr9, 0 + %36 = insertvalue %variant %35, i64 ptrtoint (ptr @"ct$int" to i64), 1 + store %variant %36, ptr %34, align 8 + %37 = insertvalue %"variant[]" undef, ptr %literal5, 0 + %38 = insertvalue %"variant[]" %37, i64 4, 1 + store %"variant[]" %38, ptr %x4, align 8 + %39 = getelementptr inbounds %"variant[]", ptr %x4, i32 0, i32 1 + %40 = load i64, ptr %39, align 8 + store i64 %40, ptr %.anon10, align 8 + store i64 0, ptr %.anon11, align 8 + br label %loop.cond12 + +loop.cond12: ; preds = %voiderr27, %loop.exit + %41 = load i64, ptr %.anon11, align 8 + %42 = load i64, ptr %.anon10, align 8 + %lt13 = icmp ult i64 %41, %42 + br i1 %lt13, label %loop.body14, label %loop.exit29 + +loop.body14: ; preds = %loop.cond12 + %43 = getelementptr inbounds %"variant[]", ptr %x4, i32 0, i32 0 + %44 = load ptr, ptr %43, align 8 + %45 = load i64, ptr %.anon11, align 8 + %ptroffset16 = getelementptr inbounds %variant, ptr %44, i64 %45 + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %i15, ptr align 8 %ptroffset16, i32 16, i1 false) + store %"char[]" { ptr @.str.1, i64 2 }, ptr %taddr18, align 8 + %46 = getelementptr inbounds { ptr, i64 }, ptr %taddr18, i32 0, i32 0 + %lo19 = load ptr, ptr %46, align 8 + %47 = getelementptr inbounds { ptr, i64 }, ptr %taddr18, i32 0, i32 1 + %hi20 = load i64, ptr %47, align 8 + %48 = getelementptr inbounds %variant, ptr %i15, i32 0, i32 0 + %49 = load ptr, ptr %48, align 8 + %50 = insertvalue %variant undef, ptr %49, 0 + %51 = insertvalue %variant %50, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %52 = getelementptr inbounds [1 x %variant], ptr %varargslots22, i64 0, i64 0 + store %variant %51, ptr %52, align 16 + %53 = getelementptr inbounds %"variant[]", ptr %vararg21, i32 0, i32 1 + store i64 1, ptr %53, align 8 + %54 = getelementptr inbounds %"variant[]", ptr %vararg21, i32 0, i32 0 + store ptr %varargslots22, ptr %54, align 8 + %55 = getelementptr inbounds { ptr, i64 }, ptr %vararg21, i32 0, i32 0 + %lo23 = load ptr, ptr %55, align 8 + %56 = getelementptr inbounds { ptr, i64 }, ptr %vararg21, i32 0, i32 1 + %hi24 = load i64, ptr %56, align 8 + %57 = call i64 @std_io_printfln(ptr %retparam17, ptr %lo19, i64 %hi20, ptr %lo23, i64 %hi24) + %not_err25 = icmp eq i64 %57, 0 + br i1 %not_err25, label %after_check26, label %voiderr27 + +after_check26: ; preds = %loop.body14 + br label %voiderr27 + +voiderr27: ; preds = %after_check26, %loop.body14 + %58 = load i64, ptr %.anon11, align 8 + %add28 = add i64 %58, 1 + store i64 %add28, ptr %.anon11, align 8 + br label %loop.cond12 + +loop.exit29: ; preds = %loop.cond12 + ret void +} diff --git a/test/test_suite2/macros/macro_untyped_varargs.c3 b/test/test_suite2/macros/macro_untyped_varargs.c3 new file mode 100644 index 000000000..6360af3c1 --- /dev/null +++ b/test/test_suite2/macros/macro_untyped_varargs.c3 @@ -0,0 +1,25 @@ +macro foo(...) +{ + $vaarg_get_arg("hello"); // #error: Expected the argument index here + int x; + $vaarg_get_arg(x); // #error: Vararg functions need a constant argument + $vaarg_get_arg(-1); // #error: negative + $vaarg_get_arg(100); // #error: varargs exist +} + +macro foo2(...) +{ + $vaarg_get_const(0); +} + +macro foo3(...) +{ + $vaarg_get_type(0) a; +} +fn void main() +{ + foo(1, -1, 3141, 999 + 1); + int x; + foo2(x); // #error: This argument needs to be a compile time constant + foo3(3); // #error: The argument was not a type. +} diff --git a/test/test_suite2/macros/macro_untyped_varargs_2.c3t b/test/test_suite2/macros/macro_untyped_varargs_2.c3t new file mode 100644 index 000000000..74aa8cb33 --- /dev/null +++ b/test/test_suite2/macros/macro_untyped_varargs_2.c3t @@ -0,0 +1,421 @@ +// #target: macos-x64 + +module test; + +import std::io; + +macro @foo(...) +{ + int i = $vaarg_get_arg(1) + $vaarg_get_arg(1); + int j = $vaarg_get_expr(2) + $vaarg_get_expr(2); + $for (var $i = 0; $i < $vaarg_count(); $i++): + io::printfln("%d", $vaarg_get_arg($i)); + $endfor; +} + +macro foo2(...) +{ + $for (var $i = 0; $i < $vaarg_count(); $i++): + { + $vaarg_get_type($i) x; + } + io::printfln("%s", $nameof($vaarg_get_type($i))); + $endfor; +} + +macro foo3(...) +{ + var $x = 0; + $for (var $i = 0; $i < $vaarg_count(); $i++): + $x += $vaarg_get_const($i); + $endfor; + return $x; +} + +macro @foo4(...) +{ + $typeof($vaarg_get_ref(0)) a = $vaarg_get_ref(0); + $vaarg_get_ref(0) = $vaarg_get_ref(1); + $vaarg_get_ref(1) = a; +} +fn int ping(int val) +{ + io::printfln("Ping[%d]", val); + return val; +} + +fn void main() +{ + @foo(ping(1), ping(-1), ping(3141), ping(999 + 1)); + foo2(int, double); + var $x = foo3(1, 4, 100); + io::printfln("%d", $x); + int x = 123; + int y = 33; + @foo4(x, y); + io::printfln("%d, %d", x, y); + @foo4(x, y); + io::printfln("%d, %d", x, y); +} + +/* #expect: test.ll + +define i32 @test_ping(i32 %0) #0 { +entry: + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %taddr1 = alloca i32, align 4 + store %"char[]" { ptr @.str, i64 8 }, ptr %taddr, align 8 + %1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0 + %lo = load ptr, ptr %1, align 8 + %2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1 + %hi = load i64, ptr %2, align 8 + store i32 %0, ptr %taddr1, align 4 + %3 = insertvalue %variant undef, ptr %taddr1, 0 + %4 = insertvalue %variant %3, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %5 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0 + store %variant %4, ptr %5, align 16 + %6 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1 + store i64 1, ptr %6, align 8 + %7 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0 + store ptr %varargslots, ptr %7, align 8 + %8 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0 + %lo2 = load ptr, ptr %8, align 8 + %9 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1 + %hi3 = load i64, ptr %9, align 8 + %10 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3) + %not_err = icmp eq i64 %10, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %entry + br label %voiderr + +voiderr: ; preds = %after_check, %entry + ret i32 %0 +} + +define void @test_main() #0 { +entry: + %.anon = alloca i32, align 4 + %.anon1 = alloca i32, align 4 + %.anon2 = alloca i32, align 4 + %.anon3 = alloca i32, align 4 + %i = alloca i32, align 4 + %j = alloca i32, align 4 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %retparam7 = alloca i64, align 8 + %taddr8 = alloca %"char[]", align 8 + %vararg11 = alloca %"variant[]", align 8 + %varargslots12 = alloca [1 x %variant], align 16 + %retparam18 = alloca i64, align 8 + %taddr19 = alloca %"char[]", align 8 + %vararg22 = alloca %"variant[]", align 8 + %varargslots23 = alloca [1 x %variant], align 16 + %retparam29 = alloca i64, align 8 + %taddr30 = alloca %"char[]", align 8 + %vararg33 = alloca %"variant[]", align 8 + %varargslots34 = alloca [1 x %variant], align 16 + %x = alloca i32, align 4 + %retparam40 = alloca i64, align 8 + %taddr41 = alloca %"char[]", align 8 + %vararg44 = alloca %"variant[]", align 8 + %varargslots45 = alloca [1 x %variant], align 16 + %taddr46 = alloca ptr, align 8 + %x52 = alloca double, align 8 + %retparam53 = alloca i64, align 8 + %taddr54 = alloca %"char[]", align 8 + %vararg57 = alloca %"variant[]", align 8 + %varargslots58 = alloca [1 x %variant], align 16 + %taddr59 = alloca ptr, align 8 + %retparam65 = alloca i64, align 8 + %taddr66 = alloca %"char[]", align 8 + %vararg69 = alloca %"variant[]", align 8 + %varargslots70 = alloca [1 x %variant], align 16 + %taddr71 = alloca i32, align 4 + %x77 = alloca i32, align 4 + %y = alloca i32, align 4 + %a = alloca i32, align 4 + %retparam78 = alloca i64, align 8 + %taddr79 = alloca %"char[]", align 8 + %vararg82 = alloca %"variant[]", align 8 + %varargslots83 = alloca [2 x %variant], align 16 + %a89 = alloca i32, align 4 + %retparam90 = alloca i64, align 8 + %taddr91 = alloca %"char[]", align 8 + %vararg94 = alloca %"variant[]", align 8 + %varargslots95 = alloca [2 x %variant], align 16 + %0 = call i32 @test_ping(i32 -1) + store i32 %0, ptr %.anon, align 4 + %1 = call i32 @test_ping(i32 1) + store i32 %1, ptr %.anon1, align 4 + %2 = call i32 @test_ping(i32 3141) + store i32 %2, ptr %.anon2, align 4 + %3 = call i32 @test_ping(i32 1000) + store i32 %3, ptr %.anon3, align 4 + %4 = load i32, ptr %.anon, align 4 + %5 = load i32, ptr %.anon, align 4 + %add = add i32 %4, %5 + store i32 %add, ptr %i, align 4 + %6 = call i32 @test_ping(i32 3141) + %7 = call i32 @test_ping(i32 3141) + %add4 = add i32 %6, %7 + store i32 %add4, ptr %j, align 4 + store %"char[]" { ptr @.str.1, i64 2 }, ptr %taddr, align 8 + %8 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0 + %lo = load ptr, ptr %8, align 8 + %9 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1 + %hi = load i64, ptr %9, align 8 + %10 = insertvalue %variant undef, ptr %.anon1, 0 + %11 = insertvalue %variant %10, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %12 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0 + store %variant %11, ptr %12, align 16 + %13 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1 + store i64 1, ptr %13, align 8 + %14 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0 + store ptr %varargslots, ptr %14, align 8 + %15 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0 + %lo5 = load ptr, ptr %15, align 8 + %16 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1 + %hi6 = load i64, ptr %16, align 8 + %17 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo5, i64 %hi6) + %not_err = icmp eq i64 %17, 0 + br i1 %not_err, label %after_check, label %voiderr + +after_check: ; preds = %entry + br label %voiderr + +voiderr: ; preds = %after_check, %entry + store %"char[]" { ptr @.str.2, i64 2 }, ptr %taddr8, align 8 + %18 = getelementptr inbounds { ptr, i64 }, ptr %taddr8, i32 0, i32 0 + %lo9 = load ptr, ptr %18, align 8 + %19 = getelementptr inbounds { ptr, i64 }, ptr %taddr8, i32 0, i32 1 + %hi10 = load i64, ptr %19, align 8 + %20 = insertvalue %variant undef, ptr %.anon, 0 + %21 = insertvalue %variant %20, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %22 = getelementptr inbounds [1 x %variant], ptr %varargslots12, i64 0, i64 0 + store %variant %21, ptr %22, align 16 + %23 = getelementptr inbounds %"variant[]", ptr %vararg11, i32 0, i32 1 + store i64 1, ptr %23, align 8 + %24 = getelementptr inbounds %"variant[]", ptr %vararg11, i32 0, i32 0 + store ptr %varargslots12, ptr %24, align 8 + %25 = getelementptr inbounds { ptr, i64 }, ptr %vararg11, i32 0, i32 0 + %lo13 = load ptr, ptr %25, align 8 + %26 = getelementptr inbounds { ptr, i64 }, ptr %vararg11, i32 0, i32 1 + %hi14 = load i64, ptr %26, align 8 + %27 = call i64 @std_io_printfln(ptr %retparam7, ptr %lo9, i64 %hi10, ptr %lo13, i64 %hi14) + %not_err15 = icmp eq i64 %27, 0 + br i1 %not_err15, label %after_check16, label %voiderr17 + +after_check16: ; preds = %voiderr + br label %voiderr17 + +voiderr17: ; preds = %after_check16, %voiderr + store %"char[]" { ptr @.str.3, i64 2 }, ptr %taddr19, align 8 + %28 = getelementptr inbounds { ptr, i64 }, ptr %taddr19, i32 0, i32 0 + %lo20 = load ptr, ptr %28, align 8 + %29 = getelementptr inbounds { ptr, i64 }, ptr %taddr19, i32 0, i32 1 + %hi21 = load i64, ptr %29, align 8 + %30 = insertvalue %variant undef, ptr %.anon2, 0 + %31 = insertvalue %variant %30, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %32 = getelementptr inbounds [1 x %variant], ptr %varargslots23, i64 0, i64 0 + store %variant %31, ptr %32, align 16 + %33 = getelementptr inbounds %"variant[]", ptr %vararg22, i32 0, i32 1 + store i64 1, ptr %33, align 8 + %34 = getelementptr inbounds %"variant[]", ptr %vararg22, i32 0, i32 0 + store ptr %varargslots23, ptr %34, align 8 + %35 = getelementptr inbounds { ptr, i64 }, ptr %vararg22, i32 0, i32 0 + %lo24 = load ptr, ptr %35, align 8 + %36 = getelementptr inbounds { ptr, i64 }, ptr %vararg22, i32 0, i32 1 + %hi25 = load i64, ptr %36, align 8 + %37 = call i64 @std_io_printfln(ptr %retparam18, ptr %lo20, i64 %hi21, ptr %lo24, i64 %hi25) + %not_err26 = icmp eq i64 %37, 0 + br i1 %not_err26, label %after_check27, label %voiderr28 + +after_check27: ; preds = %voiderr17 + br label %voiderr28 + +voiderr28: ; preds = %after_check27, %voiderr17 + store %"char[]" { ptr @.str.4, i64 2 }, ptr %taddr30, align 8 + %38 = getelementptr inbounds { ptr, i64 }, ptr %taddr30, i32 0, i32 0 + %lo31 = load ptr, ptr %38, align 8 + %39 = getelementptr inbounds { ptr, i64 }, ptr %taddr30, i32 0, i32 1 + %hi32 = load i64, ptr %39, align 8 + %40 = insertvalue %variant undef, ptr %.anon3, 0 + %41 = insertvalue %variant %40, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %42 = getelementptr inbounds [1 x %variant], ptr %varargslots34, i64 0, i64 0 + store %variant %41, ptr %42, align 16 + %43 = getelementptr inbounds %"variant[]", ptr %vararg33, i32 0, i32 1 + store i64 1, ptr %43, align 8 + %44 = getelementptr inbounds %"variant[]", ptr %vararg33, i32 0, i32 0 + store ptr %varargslots34, ptr %44, align 8 + %45 = getelementptr inbounds { ptr, i64 }, ptr %vararg33, i32 0, i32 0 + %lo35 = load ptr, ptr %45, align 8 + %46 = getelementptr inbounds { ptr, i64 }, ptr %vararg33, i32 0, i32 1 + %hi36 = load i64, ptr %46, align 8 + %47 = call i64 @std_io_printfln(ptr %retparam29, ptr %lo31, i64 %hi32, ptr %lo35, i64 %hi36) + %not_err37 = icmp eq i64 %47, 0 + br i1 %not_err37, label %after_check38, label %voiderr39 + +after_check38: ; preds = %voiderr28 + br label %voiderr39 + +voiderr39: ; preds = %after_check38, %voiderr28 + store i32 0, ptr %x, align 4 + store %"char[]" { ptr @.str.5, i64 2 }, ptr %taddr41, align 8 + %48 = getelementptr inbounds { ptr, i64 }, ptr %taddr41, i32 0, i32 0 + %lo42 = load ptr, ptr %48, align 8 + %49 = getelementptr inbounds { ptr, i64 }, ptr %taddr41, i32 0, i32 1 + %hi43 = load i64, ptr %49, align 8 + store ptr @.str.6, ptr %taddr46, align 8 + %50 = insertvalue %variant undef, ptr %taddr46, 0 + %51 = insertvalue %variant %50, i64 ptrtoint (ptr @"ct$p$a3$char" to i64), 1 + %52 = getelementptr inbounds [1 x %variant], ptr %varargslots45, i64 0, i64 0 + store %variant %51, ptr %52, align 16 + %53 = getelementptr inbounds %"variant[]", ptr %vararg44, i32 0, i32 1 + store i64 1, ptr %53, align 8 + %54 = getelementptr inbounds %"variant[]", ptr %vararg44, i32 0, i32 0 + store ptr %varargslots45, ptr %54, align 8 + %55 = getelementptr inbounds { ptr, i64 }, ptr %vararg44, i32 0, i32 0 + %lo47 = load ptr, ptr %55, align 8 + %56 = getelementptr inbounds { ptr, i64 }, ptr %vararg44, i32 0, i32 1 + %hi48 = load i64, ptr %56, align 8 + %57 = call i64 @std_io_printfln(ptr %retparam40, ptr %lo42, i64 %hi43, ptr %lo47, i64 %hi48) + %not_err49 = icmp eq i64 %57, 0 + br i1 %not_err49, label %after_check50, label %voiderr51 + +after_check50: ; preds = %voiderr39 + br label %voiderr51 + +voiderr51: ; preds = %after_check50, %voiderr39 + store double 0.000000e+00, ptr %x52, align 8 + store %"char[]" { ptr @.str.7, i64 2 }, ptr %taddr54, align 8 + %58 = getelementptr inbounds { ptr, i64 }, ptr %taddr54, i32 0, i32 0 + %lo55 = load ptr, ptr %58, align 8 + %59 = getelementptr inbounds { ptr, i64 }, ptr %taddr54, i32 0, i32 1 + %hi56 = load i64, ptr %59, align 8 + store ptr @.str.8, ptr %taddr59, align 8 + %60 = insertvalue %variant undef, ptr %taddr59, 0 + %61 = insertvalue %variant %60, i64 ptrtoint (ptr @"ct$p$a6$char" to i64), 1 + %62 = getelementptr inbounds [1 x %variant], ptr %varargslots58, i64 0, i64 0 + store %variant %61, ptr %62, align 16 + %63 = getelementptr inbounds %"variant[]", ptr %vararg57, i32 0, i32 1 + store i64 1, ptr %63, align 8 + %64 = getelementptr inbounds %"variant[]", ptr %vararg57, i32 0, i32 0 + store ptr %varargslots58, ptr %64, align 8 + %65 = getelementptr inbounds { ptr, i64 }, ptr %vararg57, i32 0, i32 0 + %lo60 = load ptr, ptr %65, align 8 + %66 = getelementptr inbounds { ptr, i64 }, ptr %vararg57, i32 0, i32 1 + %hi61 = load i64, ptr %66, align 8 + %67 = call i64 @std_io_printfln(ptr %retparam53, ptr %lo55, i64 %hi56, ptr %lo60, i64 %hi61) + %not_err62 = icmp eq i64 %67, 0 + br i1 %not_err62, label %after_check63, label %voiderr64 + +after_check63: ; preds = %voiderr51 + br label %voiderr64 + +voiderr64: ; preds = %after_check63, %voiderr51 + store %"char[]" { ptr @.str.9, i64 2 }, ptr %taddr66, align 8 + %68 = getelementptr inbounds { ptr, i64 }, ptr %taddr66, i32 0, i32 0 + %lo67 = load ptr, ptr %68, align 8 + %69 = getelementptr inbounds { ptr, i64 }, ptr %taddr66, i32 0, i32 1 + %hi68 = load i64, ptr %69, align 8 + store i32 105, ptr %taddr71, align 4 + %70 = insertvalue %variant undef, ptr %taddr71, 0 + %71 = insertvalue %variant %70, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %72 = getelementptr inbounds [1 x %variant], ptr %varargslots70, i64 0, i64 0 + store %variant %71, ptr %72, align 16 + %73 = getelementptr inbounds %"variant[]", ptr %vararg69, i32 0, i32 1 + store i64 1, ptr %73, align 8 + %74 = getelementptr inbounds %"variant[]", ptr %vararg69, i32 0, i32 0 + store ptr %varargslots70, ptr %74, align 8 + %75 = getelementptr inbounds { ptr, i64 }, ptr %vararg69, i32 0, i32 0 + %lo72 = load ptr, ptr %75, align 8 + %76 = getelementptr inbounds { ptr, i64 }, ptr %vararg69, i32 0, i32 1 + %hi73 = load i64, ptr %76, align 8 + %77 = call i64 @std_io_printfln(ptr %retparam65, ptr %lo67, i64 %hi68, ptr %lo72, i64 %hi73) + %not_err74 = icmp eq i64 %77, 0 + br i1 %not_err74, label %after_check75, label %voiderr76 + +after_check75: ; preds = %voiderr64 + br label %voiderr76 + +voiderr76: ; preds = %after_check75, %voiderr64 + store i32 123, ptr %x77, align 4 + store i32 33, ptr %y, align 4 + %78 = load i32, ptr %x77, align 4 + store i32 %78, ptr %a, align 4 + %79 = load i32, ptr %y, align 4 + store i32 %79, ptr %x77, align 4 + %80 = load i32, ptr %a, align 4 + store i32 %80, ptr %y, align 4 + store %"char[]" { ptr @.str.10, i64 6 }, ptr %taddr79, align 8 + %81 = getelementptr inbounds { ptr, i64 }, ptr %taddr79, i32 0, i32 0 + %lo80 = load ptr, ptr %81, align 8 + %82 = getelementptr inbounds { ptr, i64 }, ptr %taddr79, i32 0, i32 1 + %hi81 = load i64, ptr %82, align 8 + %83 = insertvalue %variant undef, ptr %x77, 0 + %84 = insertvalue %variant %83, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %85 = getelementptr inbounds [2 x %variant], ptr %varargslots83, i64 0, i64 0 + store %variant %84, ptr %85, align 16 + %86 = insertvalue %variant undef, ptr %y, 0 + %87 = insertvalue %variant %86, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %88 = getelementptr inbounds [2 x %variant], ptr %varargslots83, i64 0, i64 1 + store %variant %87, ptr %88, align 16 + %89 = getelementptr inbounds %"variant[]", ptr %vararg82, i32 0, i32 1 + store i64 2, ptr %89, align 8 + %90 = getelementptr inbounds %"variant[]", ptr %vararg82, i32 0, i32 0 + store ptr %varargslots83, ptr %90, align 8 + %91 = getelementptr inbounds { ptr, i64 }, ptr %vararg82, i32 0, i32 0 + %lo84 = load ptr, ptr %91, align 8 + %92 = getelementptr inbounds { ptr, i64 }, ptr %vararg82, i32 0, i32 1 + %hi85 = load i64, ptr %92, align 8 + %93 = call i64 @std_io_printfln(ptr %retparam78, ptr %lo80, i64 %hi81, ptr %lo84, i64 %hi85) + %not_err86 = icmp eq i64 %93, 0 + br i1 %not_err86, label %after_check87, label %voiderr88 + +after_check87: ; preds = %voiderr76 + br label %voiderr88 + +voiderr88: ; preds = %after_check87, %voiderr76 + %94 = load i32, ptr %x77, align 4 + store i32 %94, ptr %a89, align 4 + %95 = load i32, ptr %y, align 4 + store i32 %95, ptr %x77, align 4 + %96 = load i32, ptr %a89, align 4 + store i32 %96, ptr %y, align 4 + store %"char[]" { ptr @.str.11, i64 6 }, ptr %taddr91, align 8 + %97 = getelementptr inbounds { ptr, i64 }, ptr %taddr91, i32 0, i32 0 + %lo92 = load ptr, ptr %97, align 8 + %98 = getelementptr inbounds { ptr, i64 }, ptr %taddr91, i32 0, i32 1 + %hi93 = load i64, ptr %98, align 8 + %99 = insertvalue %variant undef, ptr %x77, 0 + %100 = insertvalue %variant %99, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %101 = getelementptr inbounds [2 x %variant], ptr %varargslots95, i64 0, i64 0 + store %variant %100, ptr %101, align 16 + %102 = insertvalue %variant undef, ptr %y, 0 + %103 = insertvalue %variant %102, i64 ptrtoint (ptr @"ct$int" to i64), 1 + %104 = getelementptr inbounds [2 x %variant], ptr %varargslots95, i64 0, i64 1 + store %variant %103, ptr %104, align 16 + %105 = getelementptr inbounds %"variant[]", ptr %vararg94, i32 0, i32 1 + store i64 2, ptr %105, align 8 + %106 = getelementptr inbounds %"variant[]", ptr %vararg94, i32 0, i32 0 + store ptr %varargslots95, ptr %106, align 8 + %107 = getelementptr inbounds { ptr, i64 }, ptr %vararg94, i32 0, i32 0 + %lo96 = load ptr, ptr %107, align 8 + %108 = getelementptr inbounds { ptr, i64 }, ptr %vararg94, i32 0, i32 1 + %hi97 = load i64, ptr %108, align 8 + %109 = call i64 @std_io_printfln(ptr %retparam90, ptr %lo92, i64 %hi93, ptr %lo96, i64 %hi97) + %not_err98 = icmp eq i64 %109, 0 + br i1 %not_err98, label %after_check99, label %voiderr100 + +after_check99: ; preds = %voiderr88 + br label %voiderr100 + +voiderr100: ; preds = %after_check99, %voiderr88 + ret void +}