Trailing body arguments may now be &ref, #hash, $const and $Type arguments.

This commit is contained in:
Christoffer Lerno
2024-06-22 20:43:32 +02:00
parent 41db9c43e5
commit e02f73417c
9 changed files with 438 additions and 155 deletions

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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, &param->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, &param->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, &param->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, &param->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;

View File

@@ -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
}

View File

@@ -0,0 +1,4 @@
macro void @bar(;@body(int &foo)) // #error: did you mean 'int*'
{}

View File

@@ -0,0 +1,11 @@
fn void main()
{
@foo(;int &foo) // #error: did you mean 'int*'
{
};
}
macro void @foo(;@body(&foo))
{
}