authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-22 16:56:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-22 16:56:03-04:00
log7a99d63c764f3d5d92370c90f932b1bf156269f6
treee25d7c07e6cbb0ad10f4c21f6f461e7917f7e25b
parent53588f4f124bb713cc2c67c721d5f4e3586b7d79

ability to use async function pointers

closes #817

6 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 ";"
57395739
5740ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"5740ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
57415741
5742FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")5742FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("&lt;" Expression "&gt;"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
57435743
5744FnDef = option("inline" | "export") FnProto Block5744FnDef = option("inline" | "export") FnProto Block
57455745
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};
26782679
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 type985 // 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}
21422142
2143static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,2143static 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;
21512153
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);
21602163
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 }
59915994
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}
59946005
5995static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6006static 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;
1656316574
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}
957957
958static 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/*
959SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)1019SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
960FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)1020FnCallExpression : 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 }
9801040
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 }
24722538
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}
25292541
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
160test "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
174async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
175 defer *y += 2;
176 *y += 1;
177 suspend;
178}