| author | |
| committer | |
| log | 7a99d63c764f3d5d92370c90f932b1bf156269f6 |
| tree | e25d7c07e6cbb0ad10f4c21f6f461e7917f7e25b |
| parent | 53588f4f124bb713cc2c67c721d5f4e3586b7d79 |
closes #8176 files changed, 113 insertions(+), 61 deletions(-)
doc/langref.html.in+1-1| ... | @@ -5739,7 +5739,7 @@ UseDecl = "use" Expression ";" | ... | @@ -5739,7 +5739,7 @@ UseDecl = "use" Expression ";" |
| 5739 | 5739 | ||
| 5740 | ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" | 5740 | ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" |
| 5741 | 5741 | ||
| 5742 | FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") | 5742 | FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("<" Expression ">"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") |
| 5743 | 5743 | ||
| 5744 | FnDef = option("inline" | "export") FnProto Block | 5744 | FnDef = option("inline" | "export") FnProto Block |
| 5745 | 5745 |
src/all_types.hpp+1| ... | @@ -2673,6 +2673,7 @@ struct IrInstructionFnProto { | ... | @@ -2673,6 +2673,7 @@ struct IrInstructionFnProto { |
| 2673 | IrInstruction **param_types; | 2673 | IrInstruction **param_types; |
| 2674 | IrInstruction *align_value; | 2674 | IrInstruction *align_value; |
| 2675 | IrInstruction *return_type; | 2675 | IrInstruction *return_type; |
| 2676 | IrInstruction *async_allocator_type_value; | ||
| 2676 | bool is_var_args; | 2677 | bool is_var_args; |
| 2677 | }; | 2678 | }; |
| 2678 | 2679 |
src/analyze.cpp+2-1| ... | @@ -985,7 +985,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -985,7 +985,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 985 | // populate the name of the type | 985 | // populate the name of the type |
| 986 | buf_resize(&fn_type->name, 0); | 986 | buf_resize(&fn_type->name, 0); |
| 987 | if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { | 987 | if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { |
| 988 | buf_appendf(&fn_type->name, "async(%s) ", buf_ptr(&fn_type_id->async_allocator_type->name)); | 988 | assert(fn_type_id->async_allocator_type != nullptr); |
| 989 | buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name)); | ||
| 989 | } else { | 990 | } else { |
| 990 | const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); | 991 | const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); |
| 991 | buf_appendf(&fn_type->name, "%s", cc_str); | 992 | buf_appendf(&fn_type->name, "%s", cc_str); |
src/ir.cpp+20-2| ... | @@ -2141,12 +2141,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc | ... | @@ -2141,12 +2141,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc |
| 2141 | } | 2141 | } |
| 2142 | 2142 | ||
| 2143 | static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2143 | static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2144 | IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args) | 2144 | IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, |
| 2145 | IrInstruction *async_allocator_type_value, bool is_var_args) | ||
| 2145 | { | 2146 | { |
| 2146 | IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node); | 2147 | IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node); |
| 2147 | instruction->param_types = param_types; | 2148 | instruction->param_types = param_types; |
| 2148 | instruction->align_value = align_value; | 2149 | instruction->align_value = align_value; |
| 2149 | instruction->return_type = return_type; | 2150 | instruction->return_type = return_type; |
| 2151 | instruction->async_allocator_type_value = async_allocator_type_value; | ||
| 2150 | instruction->is_var_args = is_var_args; | 2152 | instruction->is_var_args = is_var_args; |
| 2151 | 2153 | ||
| 2152 | assert(source_node->type == NodeTypeFnProto); | 2154 | assert(source_node->type == NodeTypeFnProto); |
| ... | @@ -2156,6 +2158,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s | ... | @@ -2156,6 +2158,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s |
| 2156 | if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); | 2158 | if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); |
| 2157 | } | 2159 | } |
| 2158 | if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); | 2160 | if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); |
| 2161 | if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block); | ||
| 2159 | ir_ref_instruction(return_type, irb->current_basic_block); | 2162 | ir_ref_instruction(return_type, irb->current_basic_block); |
| 2160 | 2163 | ||
| 2161 | return &instruction->base; | 2164 | return &instruction->base; |
| ... | @@ -5989,7 +5992,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo | ... | @@ -5989,7 +5992,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo |
| 5989 | return_type = nullptr; | 5992 | return_type = nullptr; |
| 5990 | } | 5993 | } |
| 5991 | 5994 | ||
| 5992 | return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); | 5995 | IrInstruction *async_allocator_type_value = nullptr; |
| 5996 | if (node->data.fn_proto.async_allocator_type != nullptr) { | ||
| 5997 | async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope); | ||
| 5998 | if (async_allocator_type_value == irb->codegen->invalid_instruction) | ||
| 5999 | return irb->codegen->invalid_instruction; | ||
| 6000 | } | ||
| 6001 | |||
| 6002 | return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, | ||
| 6003 | async_allocator_type_value, is_var_args); | ||
| 5993 | } | 6004 | } |
| 5994 | 6005 | ||
| 5995 | static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | 6006 | 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 | ... | @@ -16561,6 +16572,13 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc |
| 16561 | if (type_is_invalid(fn_type_id.return_type)) | 16572 | if (type_is_invalid(fn_type_id.return_type)) |
| 16562 | return ira->codegen->builtin_types.entry_invalid; | 16573 | return ira->codegen->builtin_types.entry_invalid; |
| 16563 | 16574 | ||
| 16575 | if (fn_type_id.cc == CallingConventionAsync) { | ||
| 16576 | IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other; | ||
| 16577 | fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); | ||
| 16578 | if (type_is_invalid(fn_type_id.async_allocator_type)) | ||
| 16579 | return ira->codegen->builtin_types.entry_invalid; | ||
| 16580 | } | ||
| 16581 | |||
| 16564 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | 16582 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 16565 | out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id); | 16583 | out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id); |
| 16566 | return ira->codegen->builtin_types.entry_type; | 16584 | return ira->codegen->builtin_types.entry_type; |
src/parser.cpp+69-57| ... | @@ -955,6 +955,66 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde | ... | @@ -955,6 +955,66 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde |
| 955 | } | 955 | } |
| 956 | } | 956 | } |
| 957 | 957 | ||
| 958 | static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token, | ||
| 959 | AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod) | ||
| 960 | { | ||
| 961 | AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token); | ||
| 962 | node->data.fn_proto.visib_mod = visib_mod; | ||
| 963 | node->data.fn_proto.cc = cc; | ||
| 964 | node->data.fn_proto.is_extern = is_extern; | ||
| 965 | node->data.fn_proto.async_allocator_type = async_allocator_type_node; | ||
| 966 | |||
| 967 | Token *fn_name = &pc->tokens->at(*token_index); | ||
| 968 | |||
| 969 | if (fn_name->id == TokenIdSymbol) { | ||
| 970 | *token_index += 1; | ||
| 971 | node->data.fn_proto.name = token_buf(fn_name); | ||
| 972 | } else { | ||
| 973 | node->data.fn_proto.name = nullptr; | ||
| 974 | } | ||
| 975 | |||
| 976 | ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); | ||
| 977 | |||
| 978 | Token *next_token = &pc->tokens->at(*token_index); | ||
| 979 | if (next_token->id == TokenIdKeywordAlign) { | ||
| 980 | *token_index += 1; | ||
| 981 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 982 | |||
| 983 | node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true); | ||
| 984 | ast_eat_token(pc, token_index, TokenIdRParen); | ||
| 985 | next_token = &pc->tokens->at(*token_index); | ||
| 986 | } | ||
| 987 | if (next_token->id == TokenIdKeywordSection) { | ||
| 988 | *token_index += 1; | ||
| 989 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 990 | |||
| 991 | node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true); | ||
| 992 | ast_eat_token(pc, token_index, TokenIdRParen); | ||
| 993 | next_token = &pc->tokens->at(*token_index); | ||
| 994 | } | ||
| 995 | if (next_token->id == TokenIdKeywordVar) { | ||
| 996 | node->data.fn_proto.return_var_token = next_token; | ||
| 997 | *token_index += 1; | ||
| 998 | next_token = &pc->tokens->at(*token_index); | ||
| 999 | } else { | ||
| 1000 | if (next_token->id == TokenIdKeywordError) { | ||
| 1001 | Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1); | ||
| 1002 | if (maybe_lbrace_tok->id == TokenIdLBrace) { | ||
| 1003 | *token_index += 1; | ||
| 1004 | node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); | ||
| 1005 | return node; | ||
| 1006 | } | ||
| 1007 | } else if (next_token->id == TokenIdBang) { | ||
| 1008 | *token_index += 1; | ||
| 1009 | node->data.fn_proto.auto_err_set = true; | ||
| 1010 | next_token = &pc->tokens->at(*token_index); | ||
| 1011 | } | ||
| 1012 | node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | return node; | ||
| 1016 | } | ||
| 1017 | |||
| 958 | /* | 1018 | /* |
| 959 | SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | 1019 | SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) |
| 960 | FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) | 1020 | 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, | ... | @@ -979,6 +1039,11 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, |
| 979 | } | 1039 | } |
| 980 | 1040 | ||
| 981 | Token *fncall_token = &pc->tokens->at(*token_index); | 1041 | Token *fncall_token = &pc->tokens->at(*token_index); |
| 1042 | if (fncall_token->id == TokenIdKeywordFn) { | ||
| 1043 | *token_index += 1; | ||
| 1044 | return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync, | ||
| 1045 | false, VisibModPrivate); | ||
| 1046 | } | ||
| 982 | AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true); | 1047 | AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true); |
| 983 | if (node->type != NodeTypeFnCallExpr) { | 1048 | if (node->type != NodeTypeFnCallExpr) { |
| 984 | ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id)); | 1049 | 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 | ... | @@ -2434,9 +2499,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m |
| 2434 | } else if (first_token->id == TokenIdKeywordAsync) { | 2499 | } else if (first_token->id == TokenIdKeywordAsync) { |
| 2435 | *token_index += 1; | 2500 | *token_index += 1; |
| 2436 | Token *next_token = &pc->tokens->at(*token_index); | 2501 | Token *next_token = &pc->tokens->at(*token_index); |
| 2437 | if (next_token->id == TokenIdLParen) { | 2502 | if (next_token->id == TokenIdCmpLessThan) { |
| 2503 | *token_index += 1; | ||
| 2438 | async_allocator_type_node = ast_parse_type_expr(pc, token_index, true); | 2504 | async_allocator_type_node = ast_parse_type_expr(pc, token_index, true); |
| 2439 | ast_eat_token(pc, token_index, TokenIdRParen); | 2505 | ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); |
| 2440 | } | 2506 | } |
| 2441 | fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); | 2507 | fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); |
| 2442 | cc = CallingConventionAsync; | 2508 | cc = CallingConventionAsync; |
| ... | @@ -2470,61 +2536,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m | ... | @@ -2470,61 +2536,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m |
| 2470 | return nullptr; | 2536 | return nullptr; |
| 2471 | } | 2537 | } |
| 2472 | 2538 | ||
| 2473 | AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token); | 2539 | return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod); |
| 2474 | node->data.fn_proto.visib_mod = visib_mod; | ||
| 2475 | node->data.fn_proto.cc = cc; | ||
| 2476 | node->data.fn_proto.is_extern = is_extern; | ||
| 2477 | node->data.fn_proto.async_allocator_type = async_allocator_type_node; | ||
| 2478 | |||
| 2479 | Token *fn_name = &pc->tokens->at(*token_index); | ||
| 2480 | |||
| 2481 | if (fn_name->id == TokenIdSymbol) { | ||
| 2482 | *token_index += 1; | ||
| 2483 | node->data.fn_proto.name = token_buf(fn_name); | ||
| 2484 | } else { | ||
| 2485 | node->data.fn_proto.name = nullptr; | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); | ||
| 2489 | |||
| 2490 | Token *next_token = &pc->tokens->at(*token_index); | ||
| 2491 | if (next_token->id == TokenIdKeywordAlign) { | ||
| 2492 | *token_index += 1; | ||
| 2493 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 2494 | |||
| 2495 | node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true); | ||
| 2496 | ast_eat_token(pc, token_index, TokenIdRParen); | ||
| 2497 | next_token = &pc->tokens->at(*token_index); | ||
| 2498 | } | ||
| 2499 | if (next_token->id == TokenIdKeywordSection) { | ||
| 2500 | *token_index += 1; | ||
| 2501 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 2502 | |||
| 2503 | node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true); | ||
| 2504 | ast_eat_token(pc, token_index, TokenIdRParen); | ||
| 2505 | next_token = &pc->tokens->at(*token_index); | ||
| 2506 | } | ||
| 2507 | if (next_token->id == TokenIdKeywordVar) { | ||
| 2508 | node->data.fn_proto.return_var_token = next_token; | ||
| 2509 | *token_index += 1; | ||
| 2510 | next_token = &pc->tokens->at(*token_index); | ||
| 2511 | } else { | ||
| 2512 | if (next_token->id == TokenIdKeywordError) { | ||
| 2513 | Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1); | ||
| 2514 | if (maybe_lbrace_tok->id == TokenIdLBrace) { | ||
| 2515 | *token_index += 1; | ||
| 2516 | node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); | ||
| 2517 | return node; | ||
| 2518 | } | ||
| 2519 | } else if (next_token->id == TokenIdBang) { | ||
| 2520 | *token_index += 1; | ||
| 2521 | node->data.fn_proto.auto_err_set = true; | ||
| 2522 | next_token = &pc->tokens->at(*token_index); | ||
| 2523 | } | ||
| 2524 | node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true); | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | return node; | ||
| 2528 | } | 2540 | } |
| 2529 | 2541 | ||
| 2530 | /* | 2542 | /* |
test/cases/coroutines.zig+20| ... | @@ -156,3 +156,23 @@ test "async function with dot syntax" { | ... | @@ -156,3 +156,23 @@ test "async function with dot syntax" { |
| 156 | cancel p; | 156 | cancel p; |
| 157 | assert(S.y == 2); | 157 | assert(S.y == 2); |
| 158 | } | 158 | } |
| 159 | |||
| 160 | test "async fn pointer in a struct field" { | ||
| 161 | var data: i32 = 1; | ||
| 162 | const Foo = struct { | ||
| 163 | bar: async<&std.mem.Allocator> fn(&i32) void, | ||
| 164 | }; | ||
| 165 | var foo = Foo { | ||
| 166 | .bar = simpleAsyncFn2, | ||
| 167 | }; | ||
| 168 | const p = (async<std.debug.global_allocator> foo.bar(&data)) catch unreachable; | ||
| 169 | assert(data == 2); | ||
| 170 | cancel p; | ||
| 171 | assert(data == 4); | ||
| 172 | } | ||
| 173 | |||
| 174 | async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { | ||
| 175 | defer *y += 2; | ||
| 176 | *y += 1; | ||
| 177 | suspend; | ||
| 178 | } |