diff --git a/releasenotes.md b/releasenotes.md index 2509af20a..77c30a867 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -6,7 +6,7 @@ - Addition of $append and $concat functions. - Added $$str_hash, $$str_upper, $$str_lower, $$str_find builtins. - Improved error notes when call expressions have errors. - +- Trailing body arguments may now be `&ref`, `#hash`, `$const` and `$Type` arguments. ### Fixes - Error with unsigned compare in `@ensure` when early returning 0 #1207. - Prevent Mach-O from removing `@init` and `@dynamic` in a more reliable way #1200. diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ae8397776..d8dfb1aa8 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -6373,6 +6373,7 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr * VECEACH(declarations, i) { Decl *decl = declarations[i]; + if (!values[i]) continue; if (!decl->backend_ref) llvm_emit_local_var_alloca(c, decl); } @@ -6382,6 +6383,7 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr * VECEACH(values, i) { Expr *expr = values[i]; + if (!expr) continue; llvm_emit_expr(c, value, expr); llvm_store_to_ptr_aligned(c, declarations[i]->backend_ref, value, declarations[i]->alignment); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 1157b281e..c2b4c3940 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1166,9 +1166,20 @@ static bool parse_next_is_typed_parameter(ParseContext *c, ParameterParseKind pa case TOKEN_CT_TYPEOF: case TOKEN_CT_TYPEFROM: return true; - case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_VATYPE: return parse_kind == PARAM_PARSE_LAMBDA || parse_kind == PARAM_PARSE_CALL; + case TOKEN_CT_TYPE_IDENT: + if (parse_kind == PARAM_PARSE_LAMBDA) return true; + if (parse_kind != PARAM_PARSE_CALL) return false; + switch (peek(c)) + { + case TOKEN_IDENT: + case TOKEN_HASH_IDENT: + case TOKEN_CT_IDENT: + return true; + default: + return false; + } default: return false; } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index f150ce06d..c63a962c0 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -163,7 +163,7 @@ static inline bool parse_next_may_be_type_or_ident(ParseContext *c) case TOKEN_CONST_IDENT: case TOKEN_IDENT: case TOKEN_HASH_CONST_IDENT: - case TOKEN_HASH: + case TOKEN_HASH_IDENT: case TOKEN_CT_IDENT: case TOKEN_CT_CONST_IDENT: case TYPELIKE_TOKENS: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7e1716975..6b0c218c0 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3237,16 +3237,19 @@ INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters param->resolve_status = RESOLVE_RUNNING; assert(param->decl_kind == DECL_VAR); TypeInfo *type_info = type_infoptrzero(param->var.type_info); - switch (param->var.kind) + VarDeclKind kind = param->var.kind; + switch (kind) { case VARDECL_PARAM: - if (type_info && !sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; - break; case VARDECL_PARAM_EXPR: case VARDECL_PARAM_CT: case VARDECL_PARAM_REF: case VARDECL_PARAM_CT_TYPE: - RETURN_SEMA_ERROR(param, "Only plain variables are allowed as body parameters."); + if (!type_info) break; + if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; + if (kind != VARDECL_PARAM_REF || type_is_pointer(type_info->type)) break; + RETURN_SEMA_ERROR(type_info, "A pointer type was expected for a ref argument, did you mean %s?", + type_quoted_error_string(type_get_ptr(type_info->type))); case VARDECL_CONST: case VARDECL_GLOBAL: case VARDECL_LOCAL: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 65bff9021..96c162b2a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1527,6 +1527,125 @@ INLINE bool sema_arg_is_pass_through_ref(Expr *expr) return decl->var.kind == VARDECL_PARAM_REF; } +static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, Decl *definition, bool *optional_ref, bool *no_match_ref, bool macro) +{ + VarDeclKind kind = param->var.kind; + Type *type = param->type; + + // 16. Analyse a regular argument. + switch (kind) + { + case VARDECL_PARAM_REF: + // &foo + if (!sema_analyse_expr_lvalue(context, arg)) return false; + if (sema_arg_is_pass_through_ref(arg) && !sema_expr_check_assign(context, arg)) + { + SEMA_NOTE(definition, "The definition is here."); + return false; + } + expr_insert_addr(arg); + *optional_ref |= IS_OPTIONAL(arg); + if (!sema_call_check_contract_param_match(context, param, arg)) + { + SEMA_NOTE(definition, "The definition is here."); + return false; + } + if (type_storage_type(type) != STORAGE_NORMAL) + { + RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type)); + } + if (type && type->canonical != arg->type->canonical) + { + SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type)); + SEMA_NOTE(definition, "The definition is here."); + return false; + } + if (!param->alignment) + { + if (arg->expr_kind == EXPR_IDENTIFIER) + { + param->alignment = arg->identifier_expr.decl->alignment; + } + else + { + if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; + } + } + break; + case VARDECL_PARAM: + // foo + if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false; + if (IS_OPTIONAL(arg)) *optional_ref = true; + switch (type_storage_type(arg->type)) + { + case STORAGE_NORMAL: + break; + case STORAGE_VOID: + case STORAGE_WILDCARD: + RETURN_SEMA_ERROR(arg, "A 'void' value cannot be passed as a parameter."); + case STORAGE_COMPILE_TIME: + RETURN_SEMA_ERROR(arg, "It is only possible to use %s as a compile time parameter.", + type_invalid_storage_type_name(arg->type)); + case STORAGE_UNKNOWN: + RETURN_SEMA_ERROR(arg, "A value of type '%s' has no known size so cannot be " + "passed as a parameter, you can pass a pointer to it though.", + type_quoted_error_string(arg->type)); + } + if (!sema_call_check_contract_param_match(context, param, arg)) + { + SEMA_NOTE(definition, "The definition was here."); + return false; + } + if (!param->alignment) + { + assert(macro && "Only in the macro case should we need to insert the alignment."); + if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; + } + break; + case VARDECL_PARAM_EXPR: + // #foo + param->var.hash_var.context = context; + param->var.hash_var.span = arg->span; + break; + case VARDECL_PARAM_CT: + // $foo + assert(macro); + if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) + { + SEMA_NOTE(definition, "The definition is here."); + return false; + } + if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE)) + { + RETURN_SEMA_FUNC_ERROR(definition, arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); + } + break; + case VARDECL_PARAM_CT_TYPE: + // $Foo + if (!sema_analyse_expr_lvalue_fold_const(context, arg)) return false; + if (arg->expr_kind != EXPR_TYPEINFO) + { + RETURN_SEMA_FUNC_ERROR(definition, arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); + } + 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 (param && type_len_is_inferred(type)) + { + param->type = type_no_optional(arg->type); + } + return true; +} static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional, bool *no_match_ref) { @@ -1670,123 +1789,8 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call assert(arg == NULL); continue; } - - Decl *param = decl_params[i]; - VarDeclKind kind = param->var.kind; - Type *type = param->type; - - // 16. Analyse a regular argument. - switch (kind) - { - case VARDECL_PARAM_REF: - // &foo - if (!sema_analyse_expr_lvalue(context, arg)) return false; - if (sema_arg_is_pass_through_ref(arg) && !sema_expr_check_assign(context, arg)) - { - SEMA_NOTE(callee.definition, "The definition is here."); - return false; - } - expr_insert_addr(arg); - *optional |= IS_OPTIONAL(arg); - if (!sema_call_check_contract_param_match(context, param, arg)) - { - SEMA_NOTE(callee.definition, "The definition is here."); - return false; - } - if (type_storage_type(type) != STORAGE_NORMAL) - { - RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type)); - } - if (type && type->canonical != arg->type->canonical) - { - SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type)); - SEMA_NOTE(callee.definition, "The definition is here."); - return false; - } - if (!param->alignment) - { - if (arg->expr_kind == EXPR_IDENTIFIER) - { - param->alignment = arg->identifier_expr.decl->alignment; - } - else - { - if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; - } - } - break; - case VARDECL_PARAM: - // foo - if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false; - if (IS_OPTIONAL(arg)) *optional = true; - switch (type_storage_type(arg->type)) - { - case STORAGE_NORMAL: - break; - case STORAGE_VOID: - case STORAGE_WILDCARD: - RETURN_SEMA_ERROR(arg, "A 'void' value cannot be passed as a parameter."); - case STORAGE_COMPILE_TIME: - RETURN_SEMA_ERROR(arg, "It is only possible to use %s as a compile time parameter.", - type_invalid_storage_type_name(arg->type)); - case STORAGE_UNKNOWN: - RETURN_SEMA_ERROR(arg, "A value of type '%s' has no known size so cannot be " - "passed as a parameter, you can pass a pointer to it though.", - type_quoted_error_string(arg->type)); - } - if (!sema_call_check_contract_param_match(context, param, arg)) - { - SEMA_NOTE(callee.definition, "The definition was here."); - return false; - } - if (!param->alignment) - { - assert(callee.macro && "Only in the macro case should we need to insert the alignment."); - if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false; - } - break; - case VARDECL_PARAM_EXPR: - // #foo - param->var.hash_var.context = context; - param->var.hash_var.span = arg->span; - break; - case VARDECL_PARAM_CT: - // $foo - assert(callee.macro); - if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) - { - SEMA_NOTE(callee.definition, "The definition is here."); - return false; - } - if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE)) - { - RETURN_SEMA_FUNC_ERROR(callee.definition, arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); - } - break; - case VARDECL_PARAM_CT_TYPE: - // $Foo - if (!sema_analyse_expr_lvalue_fold_const(context, arg)) return false; - if (arg->expr_kind != EXPR_TYPEINFO) - { - RETURN_SEMA_FUNC_ERROR(callee.definition, arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); - } - 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 (param && type_len_is_inferred(type)) - { - param->type = type_no_optional(arg->type); - } + if (!sema_analyse_parameter(context, arg, decl_params[i], callee.definition, + optional, no_match_ref, callee.macro)) return false; } return true; } @@ -2050,25 +2054,63 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Decl *body_param = macro_body_params[i]; assert(body_param->resolve_status == RESOLVE_DONE); Decl *body_arg = body_params[i]; - if (!body_arg->var.type_info) + VarDeclKind kind_of_expected = body_param->var.kind; + if (kind_of_expected != body_arg->var.kind) { - RETURN_SEMA_ERROR(body_arg, "Expected a type parameter before this variable name."); - } - TypeInfo *type_info = vartype(body_arg); - if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; - body_arg->type = type_info->type; - if (type_info) - { - Type *declare_type = type_info->type->canonical; - if (declare_type != body_arg->type->canonical) + switch (kind_of_expected) { - if (no_match_ref) goto NO_MATCH_REF; - RETURN_SEMA_ERROR(type_info, "This parameter should be %s but was %s", - type_quoted_error_string(declare_type), - type_quoted_error_string(body_arg->type)); + case VARDECL_PARAM_CT_TYPE: + RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a type argument."); + case VARDECL_PARAM_EXPR: + RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected an expression argument."); + case VARDECL_PARAM_REF: + RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a reference ('&') argument."); + case VARDECL_PARAM_CT: + RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a compile time ('$') argument."); + case VARDECL_PARAM: + RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a regular argument."); + default: + UNREACHABLE } } - if (!body_arg->alignment) + // No type checking + switch (kind_of_expected) + { + case VARDECL_PARAM_CT_TYPE: + continue; + case VARDECL_PARAM_CT: + case VARDECL_PARAM_EXPR: + case VARDECL_PARAM_REF: + // Optional typing + break; + case VARDECL_PARAM: + // Mandatory typing + if (!body_arg->var.type_info) + { + RETURN_SEMA_ERROR(body_arg, "Expected a type parameter before this variable name."); + } + break; + default: + UNREACHABLE + } + TypeInfo *expected_type_info = vartype(body_param); + TypeInfo *type_info = vartype(body_arg); + if (type_info && !sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; + Type *type = type_info ? type_info->type : NULL; + if (type && expected_type_info && type->canonical != expected_type_info->type->canonical) + { + if (no_match_ref) goto NO_MATCH_REF; + RETURN_SEMA_ERROR(type_info, "This parameter should be %s but was %s", + type_quoted_error_string(expected_type_info->type), + type_quoted_error_string(type)); + } + if (type && kind_of_expected == VARDECL_PARAM_REF && !type_is_pointer(type_info->type)) + { + RETURN_SEMA_ERROR(type_info, "A pointer type was expected for a ref argument, did you mean %s?", + type_quoted_error_string(type_get_ptr(type_info->type))); + } + body_arg->type = type; + if (type_info && type_storage_type(type_info->type)) { if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false; } @@ -2290,10 +2332,8 @@ NO_MATCH_REF: static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call) { Decl *macro = macro_context->current_macro; - assert(macro); - DeclId body_param = macro->func_decl.body_param; - assert(body_param); - + assert(macro && macro->func_decl.body_param); + Decl *body_decl = declptr(macro->func_decl.body_param); ExprCall *call_expr = &call->call_expr; Expr *macro_body = exprptrzero(call_expr->macro_body); if (macro_body && vec_size(macro_body->macro_body_expr.body_arguments)) @@ -2307,19 +2347,42 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c } // Theoretically we could support named arguments, but that's unnecessary. unsigned expressions = vec_size(call_expr->arguments); - Decl **body_parameters = declptr(body_param)->body_params; + Decl **body_parameters = body_decl->body_params; if (expressions != vec_size(body_parameters)) { - PRINT_ERROR_AT(call, "Expected %d parameter(s).", vec_size(body_parameters)); + PRINT_ERROR_AT(call, "Expected %d parameter(s) to %s.", vec_size(body_parameters), body_decl->name); } Expr **args = call_expr->arguments; Decl **params = macro_context->yield_params; - // Evaluate the expressions. TODO hash expressions + + bool has_optional_arg = false; + + // Evaluate the expressions. for (unsigned i = 0; i < expressions; i++) { + Decl *param = params[i]; Expr *expr = args[i]; - if (!sema_analyse_expr_rhs(macro_context, params[i]->type, expr, false, NULL)) return false; + if (!sema_analyse_parameter(macro_context, expr, param, body_decl, &has_optional_arg, NULL, true)) + { + return false; + } + if (has_optional_arg) + { + sema_error_at(macro_context, expr->span, "Optional arguments are not permitted in a body invocation."); + return false; + } + switch (param->var.kind) + { + case VARDECL_PARAM_EXPR: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + param->var.init_expr = args[i]; + args[i] = NULL; + break; + default: + break; + } } AstId macro_defer = macro_context->active_scope.defer_last; @@ -2405,10 +2468,6 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl } } - - - - static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref) { if (no_match_ref) *no_match_ref = true; @@ -4354,11 +4413,10 @@ CHECK_DEEPER: return true; } - // 10. Dump all members and methods into the scope. + // 10. Dump all members and methods into a decl stack. Decl *decl = type->decl; Decl *member = sema_decl_stack_find_decl_member(decl, kw); - if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && expr_is_const(parent)) { if (!sema_analyse_decl(context, decl)) return false; @@ -9023,8 +9081,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) } break; case EXPR_TYPEINFO: - SEMA_ERROR(expr, "A type must be followed by either (...) or '.'."); - return false; + RETURN_SEMA_ERROR(expr, "A type must be followed by either (...) or '.'."); case EXPR_CT_IDENT: if (!sema_cast_ct_ident_rvalue(context, expr)) return false; break; diff --git a/test/test_suite/macros/macro_body_ref_hash_constant_type.c3t b/test/test_suite/macros/macro_body_ref_hash_constant_type.c3t new file mode 100644 index 000000000..b5c4df429 --- /dev/null +++ b/test/test_suite/macros/macro_body_ref_hash_constant_type.c3t @@ -0,0 +1,195 @@ +// #target: macos-x64 +module test; +import std::io; + +fn void main() +{ + @boba(;#hash_val, &foo, int $value, $Type) + { + io::printn("Now invoking hash"); + #hash_val; + #hash_val; + *foo += $value; + io::printfn("The type was: %s", $Type.nameof); + }; +} + +macro void @boba(;@body(#hash, &foo, $val, $Type)) +{ + io::printn("Boba"); + int a = 0; + int b = 0; + @body({| io::printfn("Send %d", a); a++; |}, b, 3, int); + io::printfn("%d", b); +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %retparam = alloca i64, align 8 + %error_var2 = alloca i64, align 8 + %error_var8 = alloca i64, align 8 + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %foo = alloca ptr, align 8 + %len14 = alloca i64, align 8 + %error_var15 = alloca i64, align 8 + %retparam17 = alloca i64, align 8 + %error_var23 = alloca i64, align 8 + %error_var29 = alloca i64, align 8 + %varargslots = alloca [1 x %any], align 16 + %retparam37 = alloca i64, align 8 + %varargslots41 = alloca [1 x %any], align 16 + %retparam42 = alloca i64, align 8 + %varargslots47 = alloca [1 x %any], align 16 + %taddr = alloca %"char[]", align 8 + %retparam48 = alloca i64, align 8 + %varargslots51 = alloca [1 x %any], align 16 + %retparam52 = alloca i64, align 8 + %0 = call ptr @std.io.stdout() + %1 = call i64 @std.io.File.write(ptr %retparam, ptr %0, ptr @.str, i64 4) + %not_err = icmp eq i64 %1, 0 + %2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %2, label %after_check, label %assign_optional + +assign_optional: ; preds = %entry + store i64 %1, ptr %error_var, align 8 + br label %guard_block + +after_check: ; preds = %entry + br label %noerr_block + +guard_block: ; preds = %assign_optional + br label %voiderr + +noerr_block: ; preds = %after_check + %3 = load i64, ptr %retparam, align 8 + store i64 %3, ptr %len, align 8 + %4 = call i64 @std.io.File.write_byte(ptr %0, i8 zeroext 10) + %not_err3 = icmp eq i64 %4, 0 + %5 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) + br i1 %5, label %after_check5, label %assign_optional4 + +assign_optional4: ; preds = %noerr_block + store i64 %4, ptr %error_var2, align 8 + br label %guard_block6 + +after_check5: ; preds = %noerr_block + br label %noerr_block7 + +guard_block6: ; preds = %assign_optional4 + br label %voiderr + +noerr_block7: ; preds = %after_check5 + %6 = call i64 @std.io.File.flush(ptr %0) + %not_err9 = icmp eq i64 %6, 0 + %7 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true) + br i1 %7, label %after_check11, label %assign_optional10 + +assign_optional10: ; preds = %noerr_block7 + store i64 %6, ptr %error_var8, align 8 + br label %guard_block12 + +after_check11: ; preds = %noerr_block7 + br label %noerr_block13 + +guard_block12: ; preds = %assign_optional10 + br label %voiderr + +noerr_block13: ; preds = %after_check11 + %8 = load i64, ptr %len, align 8 + %add = add i64 %8, 1 + br label %voiderr + +voiderr: ; preds = %noerr_block13, %guard_block12, %guard_block6, %guard_block + store i32 0, ptr %a, align 4 + store i32 0, ptr %b, align 4 + store ptr %b, ptr %foo, align 8 + %9 = call ptr @std.io.stdout() + %10 = call i64 @std.io.File.write(ptr %retparam17, ptr %9, ptr @.str.1, i64 17) + %not_err18 = icmp eq i64 %10, 0 + %11 = call i1 @llvm.expect.i1(i1 %not_err18, i1 true) + br i1 %11, label %after_check20, label %assign_optional19 + +assign_optional19: ; preds = %voiderr + store i64 %10, ptr %error_var15, align 8 + br label %guard_block21 + +after_check20: ; preds = %voiderr + br label %noerr_block22 + +guard_block21: ; preds = %assign_optional19 + br label %voiderr36 + +noerr_block22: ; preds = %after_check20 + %12 = load i64, ptr %retparam17, align 8 + store i64 %12, ptr %len14, align 8 + %13 = call i64 @std.io.File.write_byte(ptr %9, i8 zeroext 10) + %not_err24 = icmp eq i64 %13, 0 + %14 = call i1 @llvm.expect.i1(i1 %not_err24, i1 true) + br i1 %14, label %after_check26, label %assign_optional25 + +assign_optional25: ; preds = %noerr_block22 + store i64 %13, ptr %error_var23, align 8 + br label %guard_block27 + +after_check26: ; preds = %noerr_block22 + br label %noerr_block28 + +guard_block27: ; preds = %assign_optional25 + br label %voiderr36 + +noerr_block28: ; preds = %after_check26 + %15 = call i64 @std.io.File.flush(ptr %9) + %not_err30 = icmp eq i64 %15, 0 + %16 = call i1 @llvm.expect.i1(i1 %not_err30, i1 true) + br i1 %16, label %after_check32, label %assign_optional31 + +assign_optional31: ; preds = %noerr_block28 + store i64 %15, ptr %error_var29, align 8 + br label %guard_block33 + +after_check32: ; preds = %noerr_block28 + br label %noerr_block34 + +guard_block33: ; preds = %assign_optional31 + br label %voiderr36 + +noerr_block34: ; preds = %after_check32 + %17 = load i64, ptr %len14, align 8 + %add35 = add i64 %17, 1 + br label %voiderr36 + +voiderr36: ; preds = %noerr_block34, %guard_block33, %guard_block27, %guard_block21 + %18 = insertvalue %any undef, ptr %a, 0 + %19 = insertvalue %any %18, i64 ptrtoint (ptr @"$ct.int" to i64), 1 + store %any %19, ptr %varargslots, align 16 + %20 = call i64 @std.io.printfn(ptr %retparam37, ptr @.str.2, i64 7, ptr %varargslots, i64 1) + %21 = load i32, ptr %a, align 4 + %add40 = add i32 %21, 1 + store i32 %add40, ptr %a, align 4 + %22 = insertvalue %any undef, ptr %a, 0 + %23 = insertvalue %any %22, i64 ptrtoint (ptr @"$ct.int" to i64), 1 + store %any %23, ptr %varargslots41, align 16 + %24 = call i64 @std.io.printfn(ptr %retparam42, ptr @.str.3, i64 7, ptr %varargslots41, i64 1) + %25 = load i32, ptr %a, align 4 + %add45 = add i32 %25, 1 + store i32 %add45, ptr %a, align 4 + %26 = load ptr, ptr %foo, align 8 + %27 = load i32, ptr %26, align 4 + %add46 = add i32 %27, 3 + store i32 %add46, ptr %26, align 4 + store %"char[]" { ptr @.str.5, i64 3 }, ptr %taddr, align 8 + %28 = insertvalue %any undef, ptr %taddr, 0 + %29 = insertvalue %any %28, i64 ptrtoint (ptr @"$ct.String" to i64), 1 + store %any %29, ptr %varargslots47, align 16 + %30 = call i64 @std.io.printfn(ptr %retparam48, ptr @.str.4, i64 16, ptr %varargslots47, i64 1) + %31 = insertvalue %any undef, ptr %b, 0 + %32 = insertvalue %any %31, i64 ptrtoint (ptr @"$ct.int" to i64), 1 + store %any %32, ptr %varargslots51, align 16 + %33 = call i64 @std.io.printfn(ptr %retparam52, ptr @.str.6, i64 2, ptr %varargslots51, i64 1) + ret void +} diff --git a/test/test_suite/macros/macro_ref_body_err1.c3 b/test/test_suite/macros/macro_ref_body_err1.c3 new file mode 100644 index 000000000..7c88a6dfc --- /dev/null +++ b/test/test_suite/macros/macro_ref_body_err1.c3 @@ -0,0 +1,4 @@ + +macro void @bar(;@body(int &foo)) // #error: did you mean 'int*' +{} + diff --git a/test/test_suite/macros/macro_ref_body_err2.c3 b/test/test_suite/macros/macro_ref_body_err2.c3 new file mode 100644 index 000000000..1ba1fd8e3 --- /dev/null +++ b/test/test_suite/macros/macro_ref_body_err2.c3 @@ -0,0 +1,11 @@ +fn void main() +{ + @foo(;int &foo) // #error: did you mean 'int*' + { + }; +} + +macro void @foo(;@body(&foo)) +{ +} +