Commit 7a99d63c76
Changed files (6)
doc/langref.html.in
@@ -5739,7 +5739,7 @@ UseDecl = "use" Expression ";"
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
-FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
+FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("<" Expression ">"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
FnDef = option("inline" | "export") FnProto Block
src/all_types.hpp
@@ -2673,6 +2673,7 @@ struct IrInstructionFnProto {
IrInstruction **param_types;
IrInstruction *align_value;
IrInstruction *return_type;
+ IrInstruction *async_allocator_type_value;
bool is_var_args;
};
src/analyze.cpp
@@ -985,7 +985,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
// populate the name of the type
buf_resize(&fn_type->name, 0);
if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
- buf_appendf(&fn_type->name, "async(%s) ", buf_ptr(&fn_type_id->async_allocator_type->name));
+ assert(fn_type_id->async_allocator_type != nullptr);
+ buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name));
} else {
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
buf_appendf(&fn_type->name, "%s", cc_str);
src/ir.cpp
@@ -2141,12 +2141,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
}
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args)
+ IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
+ IrInstruction *async_allocator_type_value, bool is_var_args)
{
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
instruction->param_types = param_types;
instruction->align_value = align_value;
instruction->return_type = return_type;
+ instruction->async_allocator_type_value = async_allocator_type_value;
instruction->is_var_args = is_var_args;
assert(source_node->type == NodeTypeFnProto);
@@ -2156,6 +2158,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
}
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+ if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block);
ir_ref_instruction(return_type, irb->current_basic_block);
return &instruction->base;
@@ -5989,7 +5992,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
return_type = nullptr;
}
- return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
+ IrInstruction *async_allocator_type_value = nullptr;
+ if (node->data.fn_proto.async_allocator_type != nullptr) {
+ async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
+ if (async_allocator_type_value == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+ }
+
+ return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
+ async_allocator_type_value, is_var_args);
}
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
@@ -16561,6 +16572,13 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
if (type_is_invalid(fn_type_id.return_type))
return ira->codegen->builtin_types.entry_invalid;
+ if (fn_type_id.cc == CallingConventionAsync) {
+ IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;
+ fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
+ if (type_is_invalid(fn_type_id.async_allocator_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id);
return ira->codegen->builtin_types.entry_type;
src/parser.cpp
@@ -955,6 +955,66 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
}
}
+static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token,
+ AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod)
+{
+ AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
+ node->data.fn_proto.visib_mod = visib_mod;
+ node->data.fn_proto.cc = cc;
+ node->data.fn_proto.is_extern = is_extern;
+ node->data.fn_proto.async_allocator_type = async_allocator_type_node;
+
+ Token *fn_name = &pc->tokens->at(*token_index);
+
+ if (fn_name->id == TokenIdSymbol) {
+ *token_index += 1;
+ node->data.fn_proto.name = token_buf(fn_name);
+ } else {
+ node->data.fn_proto.name = nullptr;
+ }
+
+ ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
+
+ Token *next_token = &pc->tokens->at(*token_index);
+ if (next_token->id == TokenIdKeywordAlign) {
+ *token_index += 1;
+ ast_eat_token(pc, token_index, TokenIdLParen);
+
+ node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true);
+ ast_eat_token(pc, token_index, TokenIdRParen);
+ next_token = &pc->tokens->at(*token_index);
+ }
+ if (next_token->id == TokenIdKeywordSection) {
+ *token_index += 1;
+ ast_eat_token(pc, token_index, TokenIdLParen);
+
+ node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
+ ast_eat_token(pc, token_index, TokenIdRParen);
+ next_token = &pc->tokens->at(*token_index);
+ }
+ if (next_token->id == TokenIdKeywordVar) {
+ node->data.fn_proto.return_var_token = next_token;
+ *token_index += 1;
+ next_token = &pc->tokens->at(*token_index);
+ } else {
+ if (next_token->id == TokenIdKeywordError) {
+ Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
+ if (maybe_lbrace_tok->id == TokenIdLBrace) {
+ *token_index += 1;
+ node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
+ return node;
+ }
+ } else if (next_token->id == TokenIdBang) {
+ *token_index += 1;
+ node->data.fn_proto.auto_err_set = true;
+ next_token = &pc->tokens->at(*token_index);
+ }
+ node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
+ }
+
+ return node;
+}
+
/*
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
@@ -979,6 +1039,11 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
}
Token *fncall_token = &pc->tokens->at(*token_index);
+ if (fncall_token->id == TokenIdKeywordFn) {
+ *token_index += 1;
+ return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync,
+ false, VisibModPrivate);
+ }
AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
if (node->type != NodeTypeFnCallExpr) {
ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
@@ -2434,9 +2499,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
} else if (first_token->id == TokenIdKeywordAsync) {
*token_index += 1;
Token *next_token = &pc->tokens->at(*token_index);
- if (next_token->id == TokenIdLParen) {
+ if (next_token->id == TokenIdCmpLessThan) {
+ *token_index += 1;
async_allocator_type_node = ast_parse_type_expr(pc, token_index, true);
- ast_eat_token(pc, token_index, TokenIdRParen);
+ ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
}
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
cc = CallingConventionAsync;
@@ -2470,61 +2536,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
return nullptr;
}
- AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
- node->data.fn_proto.visib_mod = visib_mod;
- node->data.fn_proto.cc = cc;
- node->data.fn_proto.is_extern = is_extern;
- node->data.fn_proto.async_allocator_type = async_allocator_type_node;
-
- Token *fn_name = &pc->tokens->at(*token_index);
-
- if (fn_name->id == TokenIdSymbol) {
- *token_index += 1;
- node->data.fn_proto.name = token_buf(fn_name);
- } else {
- node->data.fn_proto.name = nullptr;
- }
-
- ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
-
- Token *next_token = &pc->tokens->at(*token_index);
- if (next_token->id == TokenIdKeywordAlign) {
- *token_index += 1;
- ast_eat_token(pc, token_index, TokenIdLParen);
-
- node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true);
- ast_eat_token(pc, token_index, TokenIdRParen);
- next_token = &pc->tokens->at(*token_index);
- }
- if (next_token->id == TokenIdKeywordSection) {
- *token_index += 1;
- ast_eat_token(pc, token_index, TokenIdLParen);
-
- node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
- ast_eat_token(pc, token_index, TokenIdRParen);
- next_token = &pc->tokens->at(*token_index);
- }
- if (next_token->id == TokenIdKeywordVar) {
- node->data.fn_proto.return_var_token = next_token;
- *token_index += 1;
- next_token = &pc->tokens->at(*token_index);
- } else {
- if (next_token->id == TokenIdKeywordError) {
- Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
- if (maybe_lbrace_tok->id == TokenIdLBrace) {
- *token_index += 1;
- node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
- return node;
- }
- } else if (next_token->id == TokenIdBang) {
- *token_index += 1;
- node->data.fn_proto.auto_err_set = true;
- next_token = &pc->tokens->at(*token_index);
- }
- node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
- }
-
- return node;
+ return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod);
}
/*
test/cases/coroutines.zig
@@ -156,3 +156,23 @@ test "async function with dot syntax" {
cancel p;
assert(S.y == 2);
}
+
+test "async fn pointer in a struct field" {
+ var data: i32 = 1;
+ const Foo = struct {
+ bar: async<&std.mem.Allocator> fn(&i32) void,
+ };
+ var foo = Foo {
+ .bar = simpleAsyncFn2,
+ };
+ const p = (async<std.debug.global_allocator> foo.bar(&data)) catch unreachable;
+ assert(data == 2);
+ cancel p;
+ assert(data == 4);
+}
+
+async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
+ defer *y += 2;
+ *y += 1;
+ suspend;
+}