Commit 7a99d63c76

Andrew Kelley <superjoe30@gmail.com>
2018-03-22 21:56:03
ability to use async function pointers
closes #817
1 parent 53588f4
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("&lt;" Expression "&gt;"))) "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;
+}