diff --git a/doc/langref.html.in b/doc/langref.html.in index 2b09ca81bd..9123b1df74 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5663,7 +5663,7 @@ ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression BlockOrExpression = Block | Expression -Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression +Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression | CancelExpression AsmExpression = "asm" option("volatile") "(" String option(AsmOutput) ")" @@ -5707,6 +5707,8 @@ TryExpression = "try" Expression BreakExpression = "break" option(":" Symbol) option(Expression) +CancelExpression = "cancel" Expression; + Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) @@ -5745,7 +5747,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) FieldAccessExpression = "." Symbol diff --git a/src/all_types.hpp b/src/all_types.hpp index 04b781a598..694a2f64d9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -393,6 +393,7 @@ enum NodeType { NodeTypeIfErrorExpr, NodeTypeTestExpr, NodeTypeErrorSetDecl, + NodeTypeCancel, }; struct AstNodeRoot { @@ -567,6 +568,8 @@ struct AstNodeFnCallExpr { AstNode *fn_ref_expr; ZigList params; bool is_builtin; + bool is_async; + AstNode *async_allocator; }; struct AstNodeArrayAccessExpr { @@ -829,6 +832,10 @@ struct AstNodeBreakExpr { AstNode *expr; // may be null }; +struct AstNodeCancelExpr { + AstNode *expr; +}; + struct AstNodeContinueExpr { Buf *name; }; @@ -900,6 +907,7 @@ struct AstNode { AstNodeErrorType error_type; AstNodeVarLiteral var_literal; AstNodeErrorSetDecl err_set_decl; + AstNodeCancelExpr cancel_expr; } data; }; @@ -1495,6 +1503,7 @@ struct CodeGen { TypeTableEntry *entry_var; TypeTableEntry *entry_global_error_set; TypeTableEntry *entry_arg_tuple; + TypeTableEntry *entry_promise; } builtin_types; EmitFileType emit_file_type; @@ -1939,6 +1948,7 @@ enum IrInstructionId { IrInstructionIdExport, IrInstructionIdErrorReturnTrace, IrInstructionIdErrorUnion, + IrInstructionIdCancel, }; struct IrInstruction { @@ -2776,6 +2786,12 @@ struct IrInstructionErrorUnion { IrInstruction *payload; }; +struct IrInstructionCancel { + IrInstruction base; + + IrInstruction *target; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index c16a5d462a..4545db9d78 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3117,6 +3117,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeIfErrorExpr: case NodeTypeTestExpr: case NodeTypeErrorSetDecl: + case NodeTypeCancel: zig_unreachable(); } } diff --git a/src/ast_render.cpp b/src/ast_render.cpp index aed4b3e6db..eec4b996a0 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -244,6 +244,8 @@ static const char *node_type_str(NodeType node_type) { return "TestExpr"; case NodeTypeErrorSetDecl: return "ErrorSetDecl"; + case NodeTypeCancel: + return "Cancel"; } zig_unreachable(); } @@ -1037,6 +1039,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "}"); break; } + case NodeTypeCancel: + { + fprintf(ar->f, "cancel "); + render_node_grouped(ar, node->data.cancel_expr.expr); + break; + } case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeTestDecl: diff --git a/src/codegen.cpp b/src/codegen.cpp index 15648cbdec..4d9b0279d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3088,6 +3088,10 @@ static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *execu return g->cur_err_ret_trace_val; } +static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrInstructionCancel *instruction) { + zig_panic("TODO ir_render_cancel"); +} + static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { switch (atomic_order) { case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; @@ -3862,6 +3866,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction); case IrInstructionIdErrorReturnTrace: return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction); + case IrInstructionIdCancel: + return ir_render_cancel(g, executable, (IrInstructionCancel *)instruction); } zig_unreachable(); } @@ -5271,6 +5277,16 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } + { + TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); + entry->type_ref = u8_ptr_type->type_ref; + entry->zero_bits = false; + buf_init_from_str(&entry->name, "promise"); + entry->di_type = u8_ptr_type->di_type; + g->builtin_types.entry_promise = entry; + g->primitive_type_table.put(&entry->name, entry); + } } diff --git a/src/ir.cpp b/src/ir.cpp index 7eac9e4d23..d220efde79 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -637,6 +637,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) { return IrInstructionIdErrorUnion; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCancel *) { + return IrInstructionIdCancel; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2396,6 +2400,17 @@ static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_cancel(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionCancel *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3873,6 +3888,10 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return args[i]; } + if (node->data.fn_call_expr.is_async) { + zig_panic("TODO ir_gen_fn_call for async fn calls"); + } + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto); } @@ -5598,6 +5617,16 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); } +static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeCancel); + + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope); + if (target_inst == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_build_cancel(irb, parent_scope, node, target_inst); +} + static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { @@ -5694,6 +5723,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); case NodeTypeErrorSetDecl: return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval); + case NodeTypeCancel: + return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval); } zig_unreachable(); } @@ -16459,6 +16490,17 @@ static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruc } } +static TypeTableEntry *ir_analyze_instruction_cancel(IrAnalyze *ira, IrInstructionCancel *instruction) { + IrInstruction *casted_target = ir_implicit_cast(ira, instruction->target->other, ira->codegen->builtin_types.entry_promise); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_cancel(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_target); + result->value.type = casted_target->value.type; + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -16661,6 +16703,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); case IrInstructionIdErrorUnion: return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction); + case IrInstructionIdCancel: + return ir_analyze_instruction_cancel(ira, (IrInstructionCancel *)instruction); } zig_unreachable(); } @@ -16774,7 +16818,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrTypeOf: case IrInstructionIdSetAlignStack: case IrInstructionIdExport: + case IrInstructionIdCancel: return true; + case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f2c0d6a5b4..5c0c3bab0d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1010,6 +1010,11 @@ static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruct ir_print_other_instruction(irp, instruction->payload); } +static void ir_print_cancel(IrPrint *irp, IrInstructionCancel *instruction) { + fprintf(irp->f, "cancel "); + ir_print_other_instruction(irp, instruction->target); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1330,6 +1335,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrorUnion: ir_print_error_union(irp, (IrInstructionErrorUnion *)instruction); break; + case IrInstructionIdCancel: + ir_print_cancel(irp, (IrInstructionCancel *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 6ce9e25221..fc682ee62a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -920,7 +920,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde } /* -SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" @@ -928,9 +928,34 @@ FieldAccessExpression : token(Dot) token(Symbol) StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); - if (!primary_expr) - return nullptr; + AstNode *primary_expr; + + Token *async_token = &pc->tokens->at(*token_index); + if (async_token->id == TokenIdKeywordAsync) { + *token_index += 1; + + AstNode *allocator_expr_node = nullptr; + Token *async_lparen_tok = &pc->tokens->at(*token_index); + if (async_lparen_tok->id == TokenIdLParen) { + *token_index += 1; + allocator_expr_node = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + } + + AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true); + Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen); + AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok); + node->data.fn_call_expr.is_async = true; + node->data.fn_call_expr.async_allocator = allocator_expr_node; + node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node; + ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); + + primary_expr = node; + } else { + primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); + if (!primary_expr) + return nullptr; + } while (true) { Token *first_token = &pc->tokens->at(*token_index); @@ -1535,6 +1560,24 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) { return node; } +/* +CancelExpression = "cancel" Expression; +*/ +static AstNode *ast_parse_cancel_expr(ParseContext *pc, size_t *token_index) { + Token *token = &pc->tokens->at(*token_index); + + if (token->id != TokenIdKeywordCancel) { + return nullptr; + } + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypeCancel, token); + + node->data.cancel_expr.expr = ast_parse_expression(pc, token_index, false); + + return node; +} + /* Defer(body) = ("defer" | "errdefer") body */ @@ -2159,7 +2202,7 @@ static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_in } /* -Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression +Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression | CancelExpression */ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -2176,6 +2219,10 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool if (break_expr) return break_expr; + AstNode *cancel_expr = ast_parse_cancel_expr(pc, token_index); + if (cancel_expr) + return cancel_expr; + AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false); if (ass_expr) return ass_expr; @@ -2809,6 +2856,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeFnCallExpr: visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context); visit_node_list(&node->data.fn_call_expr.params, visit, context); + visit_field(&node->data.fn_call_expr.async_allocator, visit, context); break; case NodeTypeArrayAccessExpr: visit_field(&node->data.array_access_expr.array_ref_expr, visit, context); @@ -2931,5 +2979,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeErrorSetDecl: visit_node_list(&node->data.err_set_decl.decls, visit, context); break; + case NodeTypeCancel: + visit_field(&node->data.cancel_expr.expr, visit, context); + break; } } diff --git a/std/mem.zig b/std/mem.zig index 07521bfcb8..1dfef86a8f 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -116,6 +116,22 @@ pub const Allocator = struct { const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } + + pub const AsyncAllocator = struct { + allocator: &Allocator, + + fn alloc(self: &const AsyncAllocator, byte_count: usize, alignment: u29) Error![]u8 { + return self.allocator.allocFn(self.allocator, byte_count, alignment); + } + + fn free(self: &const AsyncAllocator, old_mem: []u8) { + return self.allocator.freeFn(self.allocator, old_mem); + } + }; + + fn toAsync(self: &Allocator) AsyncAllocator { + return AsyncAllocator { .allocator = self }; + } }; /// Copy all of source into dest at position 0.