Commit 2151f84d59

Vexu <15308111+Vexu@users.noreply.github.com>
2019-08-16 11:24:06
implement new async syntax in self-hosted compiler
1 parent 1e3b681
doc/langref.html.in
@@ -9987,7 +9987,7 @@ TypeExpr &lt;- PrefixTypeOp* ErrorUnionExpr
 ErrorUnionExpr &lt;- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
 
 SuffixExpr
-    &lt;- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArguments
+    &lt;- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
      / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
 
 PrimaryTypeExpr
@@ -10063,7 +10063,7 @@ FnCC
     &lt;- KEYWORD_nakedcc
      / KEYWORD_stdcallcc
      / KEYWORD_extern
-     / KEYWORD_async (LARROW TypeExpr RARROW)?
+     / KEYWORD_async
 
 ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
 
@@ -10168,8 +10168,6 @@ SuffixOp
      / DOTASTERISK
      / DOTQUESTIONMARK
 
-AsyncPrefix &lt;- KEYWORD_async (LARROW PrefixExpr RARROW)?
-
 FnCallArguments &lt;- LPAREN ExprList RPAREN
 
 # Ptr specific
src/parser.cpp
@@ -113,7 +113,6 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc);
 static AstNode *ast_parse_prefix_op(ParseContext *pc);
 static AstNode *ast_parse_prefix_type_op(ParseContext *pc);
 static AstNode *ast_parse_suffix_op(ParseContext *pc);
-static AstNode *ast_parse_async_prefix(ParseContext *pc);
 static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc);
 static AstNode *ast_parse_array_type_start(ParseContext *pc);
 static AstNode *ast_parse_ptr_type_start(ParseContext *pc);
@@ -1389,22 +1388,18 @@ static AstNode *ast_parse_error_union_expr(ParseContext *pc) {
 }
 
 // SuffixExpr
-//     <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArguments
+//     <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
 //      / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
 static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
-    AstNode *async_call = ast_parse_async_prefix(pc);
-    if (async_call != nullptr) {
+    Token *async_token = eat_token_if(pc, TokenIdKeywordAsync);
+    if (async_token != nullptr) {
         if (eat_token_if(pc, TokenIdKeywordFn) != nullptr) {
             // HACK: If we see the keyword `fn`, then we assume that
             //       we are parsing an async fn proto, and not a call.
             //       We therefore put back all tokens consumed by the async
             //       prefix...
-            // HACK: This loop is not actually enough to put back all the
-            //       tokens. Let's hope this is fine for most code right now
-            //       and wait till we get the async rework for a syntax update.
-            do {
-                put_back_token(pc);
-            } while (peek_token(pc)->id != TokenIdKeywordAsync);
+            put_back_token(pc);
+            put_back_token(pc);
 
             return ast_parse_primary_type_expr(pc);
         }
@@ -1446,10 +1441,14 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
             ast_invalid_token_error(pc, peek_token(pc));
 
         assert(args->type == NodeTypeFnCallExpr);
-        async_call->data.fn_call_expr.fn_ref_expr = child;
-        async_call->data.fn_call_expr.params = args->data.fn_call_expr.params;
-        async_call->data.fn_call_expr.is_builtin = false;
-        return async_call;
+
+        AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token);
+        res->data.fn_call_expr.is_async = true;
+        res->data.fn_call_expr.seen = false;
+        res->data.fn_call_expr.fn_ref_expr = child;
+        res->data.fn_call_expr.params = args->data.fn_call_expr.params;
+        res->data.fn_call_expr.is_builtin = false;
+        return res;
     }
 
     AstNode *res = ast_parse_primary_type_expr(pc);
@@ -1501,7 +1500,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
 //     <- BUILTINIDENTIFIER FnCallArguments
 //      / CHAR_LITERAL
 //      / ContainerDecl
-//     / DOT IDENTIFIER
+//      / DOT IDENTIFIER
 //      / ErrorSetDecl
 //      / FLOAT
 //      / FnProto
@@ -2016,7 +2015,7 @@ static AstNode *ast_parse_link_section(ParseContext *pc) {
 //     <- KEYWORD_nakedcc
 //      / KEYWORD_stdcallcc
 //      / KEYWORD_extern
-//      / KEYWORD_async (LARROW TypeExpr RARROW)?
+//      / KEYWORD_async
 static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) {
     AstNodeFnProto res = {};
     if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) {
@@ -2657,19 +2656,6 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
     return nullptr;
 }
 
-// AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
-static AstNode *ast_parse_async_prefix(ParseContext *pc) {
-    Token *async = eat_token_if(pc, TokenIdKeywordAsync);
-    if (async == nullptr)
-        return nullptr;
-
-    AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async);
-    res->data.fn_call_expr.is_async = true;
-    res->data.fn_call_expr.seen = false;
-
-    return res;
-}
-
 // FnCallArguments <- LPAREN ExprList RPAREN
 static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc) {
     Token *paren = eat_token_if(pc, TokenIdLParen);
src-self-hosted/ir.zig
@@ -1181,7 +1181,6 @@ pub const Builder = struct {
             ast.Node.Id.ErrorTag => return error.Unimplemented,
             ast.Node.Id.AsmInput => return error.Unimplemented,
             ast.Node.Id.AsmOutput => return error.Unimplemented,
-            ast.Node.Id.AsyncAttribute => return error.Unimplemented,
             ast.Node.Id.ParamDecl => return error.Unimplemented,
             ast.Node.Id.FieldInitializer => return error.Unimplemented,
             ast.Node.Id.EnumLiteral => return error.Unimplemented,
src-self-hosted/translate_c.zig
@@ -1037,7 +1037,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
         .op = ast.Node.SuffixOp.Op{
             .Call = ast.Node.SuffixOp.Op.Call{
                 .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
-                .async_attr = null,
+                .async_token = null,
             },
         },
         .rtoken = undefined, // set after appending args
@@ -1355,7 +1355,6 @@ fn finishTransFnProto(
         .var_args_token = null, // TODO this field is broken in the AST data model
         .extern_export_inline_token = extern_export_inline_tok,
         .cc_token = cc_tok,
-        .async_attr = null,
         .body_node = null,
         .lib_name = null,
         .align_expr = null,
std/zig/ast.zig
@@ -434,7 +434,6 @@ pub const Node = struct {
         ErrorTag,
         AsmInput,
         AsmOutput,
-        AsyncAttribute,
         ParamDecl,
         FieldInitializer,
     };
@@ -838,36 +837,6 @@ pub const Node = struct {
         }
     };
 
-    pub const AsyncAttribute = struct {
-        base: Node,
-        async_token: TokenIndex,
-        allocator_type: ?*Node,
-        rangle_bracket: ?TokenIndex,
-
-        pub fn iterate(self: *AsyncAttribute, index: usize) ?*Node {
-            var i = index;
-
-            if (self.allocator_type) |allocator_type| {
-                if (i < 1) return allocator_type;
-                i -= 1;
-            }
-
-            return null;
-        }
-
-        pub fn firstToken(self: *const AsyncAttribute) TokenIndex {
-            return self.async_token;
-        }
-
-        pub fn lastToken(self: *const AsyncAttribute) TokenIndex {
-            if (self.rangle_bracket) |rangle_bracket| {
-                return rangle_bracket;
-            }
-
-            return self.async_token;
-        }
-    };
-
     pub const FnProto = struct {
         base: Node,
         doc_comments: ?*DocComment,
@@ -879,7 +848,6 @@ pub const Node = struct {
         var_args_token: ?TokenIndex,
         extern_export_inline_token: ?TokenIndex,
         cc_token: ?TokenIndex,
-        async_attr: ?*AsyncAttribute,
         body_node: ?*Node,
         lib_name: ?*Node, // populated if this is an extern declaration
         align_expr: ?*Node, // populated if align(A) is present
@@ -935,7 +903,6 @@ pub const Node = struct {
 
         pub fn firstToken(self: *const FnProto) TokenIndex {
             if (self.visib_token) |visib_token| return visib_token;
-            if (self.async_attr) |async_attr| return async_attr.firstToken();
             if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
             assert(self.lib_name == null);
             if (self.cc_token) |cc_token| return cc_token;
@@ -1699,7 +1666,7 @@ pub const Node = struct {
 
             pub const Call = struct {
                 params: ParamList,
-                async_attr: ?*AsyncAttribute,
+                async_token: ?TokenIndex,
 
                 pub const ParamList = SegmentedList(*Node, 2);
             };
@@ -1752,7 +1719,7 @@ pub const Node = struct {
 
         pub fn firstToken(self: *const SuffixOp) TokenIndex {
             switch (self.op) {
-                .Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),
+                .Call => |*call_info| if (call_info.async_token) |async_token| return async_token,
                 else => {},
             }
             return self.lhs.firstToken();
std/zig/parse.zig
@@ -277,7 +277,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
 
 /// FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
 fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
-    const cc = try parseFnCC(arena, it, tree);
+    const cc = parseFnCC(arena, it, tree);
     const fn_token = eatToken(it, .Keyword_fn) orelse {
         if (cc == null) return null else return error.ParseError;
     };
@@ -320,7 +320,6 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
         .var_args_token = var_args_token,
         .extern_export_inline_token = null,
         .cc_token = null,
-        .async_attr = null,
         .body_node = null,
         .lib_name = null,
         .align_expr = align_expr,
@@ -331,7 +330,6 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
         switch (kind) {
             .CC => |token| fn_proto_node.cc_token = token,
             .Extern => |token| fn_proto_node.extern_export_inline_token = token,
-            .Async => |node| fn_proto_node.async_attr = node,
         }
     }
 
@@ -1092,10 +1090,19 @@ fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
 }
 
 /// SuffixExpr
-///     <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArguments
+///     <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
 ///      / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
 fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
-    if (try parseAsyncPrefix(arena, it, tree)) |async_node| {
+    if (eatToken(it, .Keyword_async)) |async_token| {
+        if (eatToken(it, .Keyword_fn)) |token_fn| {
+            // HACK: If we see the keyword `fn`, then we assume that
+            //       we are parsing an async fn proto, and not a call.
+            //       We therefore put back all tokens consumed by the async
+            //       prefix...
+            putBackToken(it, token_fn);
+            putBackToken(it, async_token);
+            return parsePrimaryTypeExpr(arena, it, tree);
+        }
         // TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr
         var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, AstError{
             .ExpectedPrimaryTypeExpr = AstError.ExpectedPrimaryTypeExpr{ .token = it.index },
@@ -1116,7 +1123,6 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
             });
             return null;
         };
-
         const node = try arena.create(Node.SuffixOp);
         node.* = Node.SuffixOp{
             .base = Node{ .id = .SuffixOp },
@@ -1124,14 +1130,13 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
             .op = Node.SuffixOp.Op{
                 .Call = Node.SuffixOp.Op.Call{
                     .params = params.list,
-                    .async_attr = async_node.cast(Node.AsyncAttribute).?,
+                    .async_token = async_token,
                 },
             },
             .rtoken = params.rparen,
         };
         return &node.base;
     }
-
     if (try parsePrimaryTypeExpr(arena, it, tree)) |expr| {
         var res = expr;
 
@@ -1153,7 +1158,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
                     .op = Node.SuffixOp.Op{
                         .Call = Node.SuffixOp.Op.Call{
                             .params = params.list,
-                            .async_attr = null,
+                            .async_token = null,
                         },
                     },
                     .rtoken = params.rparen,
@@ -1653,36 +1658,18 @@ fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
 ///     <- KEYWORD_nakedcc
 ///      / KEYWORD_stdcallcc
 ///      / KEYWORD_extern
-///      / KEYWORD_async (LARROW TypeExpr RARROW)?
-fn parseFnCC(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?FnCC {
+///      / KEYWORD_async
+fn parseFnCC(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?FnCC {
     if (eatToken(it, .Keyword_nakedcc)) |token| return FnCC{ .CC = token };
     if (eatToken(it, .Keyword_stdcallcc)) |token| return FnCC{ .CC = token };
     if (eatToken(it, .Keyword_extern)) |token| return FnCC{ .Extern = token };
-    if (eatToken(it, .Keyword_async)) |token| {
-        const node = try arena.create(Node.AsyncAttribute);
-        node.* = Node.AsyncAttribute{
-            .base = Node{ .id = .AsyncAttribute },
-            .async_token = token,
-            .allocator_type = null,
-            .rangle_bracket = null,
-        };
-        if (eatToken(it, .AngleBracketLeft)) |_| {
-            const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
-                .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
-            });
-            const rarrow = try expectToken(it, tree, .AngleBracketRight);
-            node.allocator_type = type_expr;
-            node.rangle_bracket = rarrow;
-        }
-        return FnCC{ .Async = node };
-    }
+    if (eatToken(it, .Keyword_async)) |token| return FnCC{ .CC = token };
     return null;
 }
 
 const FnCC = union(enum) {
     CC: TokenIndex,
     Extern: TokenIndex,
-    Async: *Node.AsyncAttribute,
 };
 
 /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
@@ -2409,28 +2396,6 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
     return &node.base;
 }
 
-/// AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
-fn parseAsyncPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
-    const async_token = eatToken(it, .Keyword_async) orelse return null;
-    var rangle_bracket: ?TokenIndex = null;
-    const expr_node = if (eatToken(it, .AngleBracketLeft)) |_| blk: {
-        const prefix_expr = try expectNode(arena, it, tree, parsePrefixExpr, AstError{
-            .ExpectedPrefixExpr = AstError.ExpectedPrefixExpr{ .token = it.index },
-        });
-        rangle_bracket = try expectToken(it, tree, .AngleBracketRight);
-        break :blk prefix_expr;
-    } else null;
-
-    const node = try arena.create(Node.AsyncAttribute);
-    node.* = Node.AsyncAttribute{
-        .base = Node{ .id = .AsyncAttribute },
-        .async_token = async_token,
-        .allocator_type = expr_node,
-        .rangle_bracket = rangle_bracket,
-    };
-    return &node.base;
-}
-
 /// FnCallArguments <- LPAREN ExprList RPAREN
 /// ExprList <- (Expr COMMA)* Expr?
 fn parseFnCallArguments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?AnnotatedParamList {
std/zig/parser_test.zig
@@ -210,7 +210,7 @@ test "zig fmt: spaces around slice operator" {
 test "zig fmt: async call in if condition" {
     try testCanonical(
         \\comptime {
-        \\    if (async<a> b()) {
+        \\    if (async b()) {
         \\        a();
         \\    }
         \\}
@@ -1118,7 +1118,7 @@ test "zig fmt: first line comment in struct initializer" {
         \\pub async fn acquire(self: *Self) HeldLock {
         \\    return HeldLock{
         \\        // guaranteed allocation elision
-        \\        .held = await (async self.lock.acquire() catch unreachable),
+        \\        .held = self.lock.acquire(),
         \\        .value = &self.private_data,
         \\    };
         \\}
std/zig/render.zig
@@ -284,20 +284,6 @@ fn renderExpression(
             return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space);
         },
 
-        ast.Node.Id.AsyncAttribute => {
-            const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
-
-            if (async_attr.allocator_type) |allocator_type| {
-                try renderToken(tree, stream, async_attr.async_token, indent, start_col, Space.None); // async
-
-                try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, start_col, Space.None); // <
-                try renderExpression(allocator, stream, tree, indent, start_col, allocator_type, Space.None); // allocator
-                return renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, start_col, space); // >
-            } else {
-                return renderToken(tree, stream, async_attr.async_token, indent, start_col, space); // async
-            }
-        },
-
         ast.Node.Id.Suspend => {
             const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
 
@@ -459,8 +445,8 @@ fn renderExpression(
 
             switch (suffix_op.op) {
                 @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
-                    if (call_info.async_attr) |async_attr| {
-                        try renderExpression(allocator, stream, tree, indent, start_col, &async_attr.base, Space.Space);
+                    if (call_info.async_token) |async_token| {
+                        try renderToken(tree, stream, async_token, indent, start_col, Space.Space);
                     }
 
                     try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
@@ -1121,10 +1107,6 @@ fn renderExpression(
                 try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc
             }
 
-            if (fn_proto.async_attr) |async_attr| {
-                try renderExpression(allocator, stream, tree, indent, start_col, &async_attr.base, Space.Space);
-            }
-
             const lparen = if (fn_proto.name_token) |name_token| blk: {
                 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
                 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name