Commit 897e783763

Andrew Kelley <superjoe30@gmail.com>
2018-03-25 00:25:53
add promise->T syntax parsing
closes #857
1 parent 18af2f9
doc/langref.html.in
@@ -5863,7 +5863,9 @@ StructLiteralField = "." Symbol "=" Expression
 
 PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
 
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
+
+PromiseType = "promise" option("-&gt;" TypeExpr)
 
 ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
 
src/all_types.hpp
@@ -409,6 +409,7 @@ enum NodeType {
     NodeTypeResume,
     NodeTypeAwaitExpr,
     NodeTypeSuspend,
+    NodeTypePromiseType,
 };
 
 struct AstNodeRoot {
@@ -879,6 +880,10 @@ struct AstNodeSuspend {
     AstNode *promise_symbol;
 };
 
+struct AstNodePromiseType {
+    AstNode *payload_type; // can be NULL
+};
+
 struct AstNode {
     enum NodeType type;
     size_t line;
@@ -939,6 +944,7 @@ struct AstNode {
         AstNodeResumeExpr resume_expr;
         AstNodeAwaitExpr await_expr;
         AstNodeSuspend suspend;
+        AstNodePromiseType promise_type;
     } data;
 };
 
@@ -1947,6 +1953,7 @@ enum IrInstructionId {
     IrInstructionIdSetRuntimeSafety,
     IrInstructionIdSetFloatMode,
     IrInstructionIdArrayType,
+    IrInstructionIdPromiseType,
     IrInstructionIdSliceType,
     IrInstructionIdAsm,
     IrInstructionIdSizeOf,
@@ -2365,6 +2372,12 @@ struct IrInstructionArrayType {
     IrInstruction *child_type;
 };
 
+struct IrInstructionPromiseType {
+    IrInstruction base;
+
+    IrInstruction *payload_type;
+};
+
 struct IrInstructionSliceType {
     IrInstruction base;
 
src/analyze.cpp
@@ -3254,6 +3254,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeResume:
         case NodeTypeAwaitExpr:
         case NodeTypeSuspend:
+        case NodeTypePromiseType:
             zig_unreachable();
     }
 }
src/ast_render.cpp
@@ -250,6 +250,8 @@ static const char *node_type_str(NodeType node_type) {
             return "AwaitExpr";
         case NodeTypeSuspend:
             return "Suspend";
+        case NodeTypePromiseType:
+            return "PromiseType";
     }
     zig_unreachable();
 }
@@ -781,6 +783,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 render_node_ungrouped(ar, node->data.array_type.child_type);
                 break;
             }
+        case NodeTypePromiseType:
+            {
+                fprintf(ar->f, "promise");
+                if (node->data.promise_type.payload_type != nullptr) {
+                    fprintf(ar->f, "->");
+                    render_node_grouped(ar, node->data.promise_type.payload_type);
+                }
+                break;
+            }
         case NodeTypeErrorType:
             fprintf(ar->f, "error");
             break;
src/codegen.cpp
@@ -4205,6 +4205,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSetRuntimeSafety:
         case IrInstructionIdSetFloatMode:
         case IrInstructionIdArrayType:
+        case IrInstructionIdPromiseType:
         case IrInstructionIdSliceType:
         case IrInstructionIdSizeOf:
         case IrInstructionIdSwitchTarget:
src/ir.cpp
@@ -349,6 +349,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
     return IrInstructionIdArrayType;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) {
+    return IrInstructionIdPromiseType;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
     return IrInstructionIdSliceType;
 }
@@ -1469,6 +1473,17 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *payload_type)
+{
+    IrInstructionPromiseType *instruction = ir_build_instruction<IrInstructionPromiseType>(irb, scope, source_node);
+    instruction->payload_type = payload_type;
+
+    if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
         IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value)
 {
@@ -5074,6 +5089,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n
     }
 }
 
+static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypePromiseType);
+
+    AstNode *payload_type_node = node->data.promise_type.payload_type;
+    IrInstruction *payload_type_value = nullptr;
+
+    if (payload_type_node != nullptr) {
+        payload_type_value = ir_gen_node(irb, payload_type_node, scope);
+        if (payload_type_value == irb->codegen->invalid_instruction)
+            return payload_type_value;
+
+    }
+
+    return ir_build_promise_type(irb, scope, node, payload_type_value);
+}
+
 static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypeUndefinedLiteral);
     return ir_build_const_undefined(irb, scope, node);
@@ -6282,6 +6313,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval);
         case NodeTypeArrayType:
             return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval);
+        case NodeTypePromiseType:
+            return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval);
         case NodeTypeStringLiteral:
             return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval);
         case NodeTypeUndefinedLiteral:
@@ -14069,6 +14102,24 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
     zig_unreachable();
 }
 
+static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) {
+    TypeTableEntry *promise_type;
+
+    if (instruction->payload_type == nullptr) {
+        promise_type = ira->codegen->builtin_types.entry_promise;
+    } else {
+        TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload_type->other);
+        if (type_is_invalid(payload_type))
+            return ira->codegen->builtin_types.entry_invalid;
+
+        promise_type = get_promise_type(ira->codegen, payload_type);
+    }
+
+    ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+    out_val->data.x_type = promise_type;
+    return ira->codegen->builtin_types.entry_type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
         IrInstructionSizeOf *size_of_instruction)
 {
@@ -17907,6 +17958,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
         case IrInstructionIdArrayType:
             return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
+        case IrInstructionIdPromiseType:
+            return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)instruction);
         case IrInstructionIdSizeOf:
             return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
         case IrInstructionIdTestNonNull:
@@ -18232,6 +18285,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdUnionFieldPtr:
         case IrInstructionIdArrayType:
+        case IrInstructionIdPromiseType:
         case IrInstructionIdSliceType:
         case IrInstructionIdSizeOf:
         case IrInstructionIdTestNonNull:
src/ir_print.cpp
@@ -404,6 +404,14 @@ static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instructio
     ir_print_other_instruction(irp, instruction->child_type);
 }
 
+static void ir_print_promise_type(IrPrint *irp, IrInstructionPromiseType *instruction) {
+    fprintf(irp->f, "promise");
+    if (instruction->payload_type != nullptr) {
+        fprintf(irp->f, "->");
+        ir_print_other_instruction(irp, instruction->payload_type);
+    }
+}
+
 static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instruction) {
     const char *const_kw = instruction->is_const ? "const " : "";
     fprintf(irp->f, "[]%s", const_kw);
@@ -1263,6 +1271,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdArrayType:
             ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
             break;
+        case IrInstructionIdPromiseType:
+            ir_print_promise_type(irp, (IrInstructionPromiseType *)instruction);
+            break;
         case IrInstructionIdSliceType:
             ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
             break;
src/parser.cpp
@@ -705,7 +705,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
 }
 
 /*
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
 KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
 ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
 */
@@ -774,6 +774,15 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
         AstNode *node = ast_create_node(pc, NodeTypeSuspend, token);
         *token_index += 1;
         return node;
+    } else if (token->id == TokenIdKeywordPromise) {
+        AstNode *node = ast_create_node(pc, NodeTypePromiseType, token);
+        *token_index += 1;
+        Token *arrow_tok = &pc->tokens->at(*token_index);
+        if (arrow_tok->id == TokenIdArrow) {
+            *token_index += 1;
+            node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true);
+        }
+        return node;
     } else if (token->id == TokenIdKeywordError) {
         Token *next_token = &pc->tokens->at(*token_index + 1);
         if (next_token->id == TokenIdLBrace) {
@@ -3081,6 +3090,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
             visit_field(&node->data.array_type.child_type, visit, context);
             visit_field(&node->data.array_type.align_expr, visit, context);
             break;
+        case NodeTypePromiseType:
+            visit_field(&node->data.promise_type.payload_type, visit, context);
+            break;
         case NodeTypeErrorType:
             // none
             break;
src/tokenizer.cpp
@@ -135,6 +135,7 @@ static const struct ZigKeyword zig_keywords[] = {
     {"null", TokenIdKeywordNull},
     {"or", TokenIdKeywordOr},
     {"packed", TokenIdKeywordPacked},
+    {"promise", TokenIdKeywordPromise},
     {"pub", TokenIdKeywordPub},
     {"resume", TokenIdKeywordResume},
     {"return", TokenIdKeywordReturn},
@@ -1558,6 +1559,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordNull: return "null";
         case TokenIdKeywordOr: return "or";
         case TokenIdKeywordPacked: return "packed";
+        case TokenIdKeywordPromise: return "promise";
         case TokenIdKeywordPub: return "pub";
         case TokenIdKeywordReturn: return "return";
         case TokenIdKeywordSection: return "section";
src/tokenizer.hpp
@@ -76,6 +76,7 @@ enum TokenId {
     TokenIdKeywordNull,
     TokenIdKeywordOr,
     TokenIdKeywordPacked,
+    TokenIdKeywordPromise,
     TokenIdKeywordPub,
     TokenIdKeywordResume,
     TokenIdKeywordReturn,
test/cases/coroutines.zig
@@ -5,6 +5,7 @@ var x: i32 = 1;
 
 test "create a coroutine and cancel it" {
     const p = try async<std.debug.global_allocator> simpleAsyncFn();
+    comptime assert(@typeOf(p) == promise->void);
     cancel p;
     assert(x == 2);
 }