Commit 70bced5dcf

Andrew Kelley <andrew@ziglang.org>
2019-07-25 07:47:56
implement `@frame` and `@Frame`
1 parent ead2d32
src/all_types.hpp
@@ -1435,8 +1435,6 @@ enum BuiltinFnId {
     BuiltinFnIdErrName,
     BuiltinFnIdBreakpoint,
     BuiltinFnIdReturnAddress,
-    BuiltinFnIdFrameAddress,
-    BuiltinFnIdHandle,
     BuiltinFnIdEmbedFile,
     BuiltinFnIdCmpxchgWeak,
     BuiltinFnIdCmpxchgStrong,
@@ -1507,6 +1505,9 @@ enum BuiltinFnId {
     BuiltinFnIdAtomicLoad,
     BuiltinFnIdHasDecl,
     BuiltinFnIdUnionInit,
+    BuiltinFnIdFrameAddress,
+    BuiltinFnIdFrameType,
+    BuiltinFnIdFrameHandle,
 };
 
 struct BuiltinFnEntry {
@@ -2252,7 +2253,8 @@ enum IrInstructionId {
     IrInstructionIdBreakpoint,
     IrInstructionIdReturnAddress,
     IrInstructionIdFrameAddress,
-    IrInstructionIdHandle,
+    IrInstructionIdFrameHandle,
+    IrInstructionIdFrameType,
     IrInstructionIdAlignOf,
     IrInstructionIdOverflowOp,
     IrInstructionIdTestErrSrc,
@@ -3038,8 +3040,14 @@ struct IrInstructionFrameAddress {
     IrInstruction base;
 };
 
-struct IrInstructionHandle {
+struct IrInstructionFrameHandle {
+    IrInstruction base;
+};
+
+struct IrInstructionFrameType {
     IrInstruction base;
+
+    IrInstruction *fn;
 };
 
 enum IrOverflowOp {
src/codegen.cpp
@@ -4457,10 +4457,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
     return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, "");
 }
 
-static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable,
-        IrInstructionHandle *instruction)
-{
-    zig_panic("TODO @handle() codegen");
+static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionFrameHandle *instruction) {
+    return g->cur_ret_ptr;
 }
 
 static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
@@ -5008,6 +5006,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdBitCastSrc:
         case IrInstructionIdTestErrSrc:
         case IrInstructionIdUnionInitNamedField:
+        case IrInstructionIdFrameType:
             zig_unreachable();
 
         case IrInstructionIdDeclVarGen:
@@ -5086,8 +5085,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
         case IrInstructionIdFrameAddress:
             return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
-        case IrInstructionIdHandle:
-            return ir_render_handle(g, executable, (IrInstructionHandle *)instruction);
+        case IrInstructionIdFrameHandle:
+            return ir_render_handle(g, executable, (IrInstructionFrameHandle *)instruction);
         case IrInstructionIdOverflowOp:
             return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
         case IrInstructionIdTestErrGen:
@@ -6754,8 +6753,6 @@ static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char
 static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
     create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
-    create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
-    create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0);
     create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
     create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
     create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
@@ -6856,6 +6853,9 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdThis, "This", 0);
     create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2);
     create_builtin_fn(g, BuiltinFnIdUnionInit, "unionInit", 3);
+    create_builtin_fn(g, BuiltinFnIdFrameHandle, "frame", 0);
+    create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1);
+    create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
 }
 
 static const char *bool_to_str(bool b) {
src/ir.cpp
@@ -755,8 +755,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)
     return IrInstructionIdFrameAddress;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) {
-    return IrInstructionIdHandle;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameHandle *) {
+    return IrInstructionIdFrameHandle;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameType *) {
+    return IrInstructionIdFrameType;
 }
 
 static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
@@ -2362,7 +2366,16 @@ static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNo
 }
 
 static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
-    IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
+    IrInstructionFrameHandle *instruction = ir_build_instruction<IrInstructionFrameHandle>(irb, scope, source_node);
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_frame_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) {
+    IrInstructionFrameType *instruction = ir_build_instruction<IrInstructionFrameType>(irb, scope, source_node);
+    instruction->fn = fn;
+
+    ir_ref_instruction(fn, irb->current_basic_block);
+
     return &instruction->base;
 }
 
@@ -3358,11 +3371,6 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
     return nullptr;
 }
 
-static bool exec_is_async(IrExecutable *exec) {
-    ZigFn *fn_entry = exec_fn_entry(exec);
-    return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
-}
-
 static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
     bool is_generated_code)
 {
@@ -4278,8 +4286,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
         return irb->codegen->invalid_instruction;
     }
 
-    bool is_async = exec_is_async(irb->exec);
-
     switch (builtin_fn->id) {
         case BuiltinFnIdInvalid:
             zig_unreachable();
@@ -4902,16 +4908,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
             return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval, result_loc);
         case BuiltinFnIdFrameAddress:
             return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdHandle:
+        case BuiltinFnIdFrameHandle:
             if (!irb->exec->fn_entry) {
                 add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
                 return irb->codegen->invalid_instruction;
             }
-            if (!is_async) {
-                add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
-                return irb->codegen->invalid_instruction;
-            }
             return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdFrameType: {
+            AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+            IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+            if (arg0_value == irb->codegen->invalid_instruction)
+                return arg0_value;
+
+            IrInstruction *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
+            return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
+        }
         case BuiltinFnIdAlignOf:
             {
                 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -21726,8 +21737,25 @@ static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIns
     return result;
 }
 
-static IrInstruction *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
-    zig_panic("TODO anlayze @handle()");
+static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstructionFrameHandle *instruction) {
+    ZigFn *fn = exec_fn_entry(ira->new_irb.exec);
+    ir_assert(fn != nullptr, &instruction->base);
+
+    ZigType *frame_type = get_coro_frame_type(ira->codegen, fn);
+    ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false);
+
+    IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
+    result->value.type = ptr_frame_type;
+    return result;
+}
+
+static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstructionFrameType *instruction) {
+    ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child);
+    if (fn == nullptr)
+        return ira->codegen->invalid_instruction;
+
+    ZigType *ty = get_coro_frame_type(ira->codegen, fn);
+    return ir_const_type(ira, &instruction->base, ty);
 }
 
 static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
@@ -24355,8 +24383,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
             return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
         case IrInstructionIdFrameAddress:
             return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
-        case IrInstructionIdHandle:
-            return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
+        case IrInstructionIdFrameHandle:
+            return ir_analyze_instruction_frame_handle(ira, (IrInstructionFrameHandle *)instruction);
+        case IrInstructionIdFrameType:
+            return ir_analyze_instruction_frame_type(ira, (IrInstructionFrameType *)instruction);
         case IrInstructionIdAlignOf:
             return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
         case IrInstructionIdOverflowOp:
@@ -24650,7 +24680,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdAlignOf:
         case IrInstructionIdReturnAddress:
         case IrInstructionIdFrameAddress:
-        case IrInstructionIdHandle:
+        case IrInstructionIdFrameHandle:
+        case IrInstructionIdFrameType:
         case IrInstructionIdTestErrSrc:
         case IrInstructionIdTestErrGen:
         case IrInstructionIdFnProto:
src/ir_print.cpp
@@ -906,8 +906,14 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
     fprintf(irp->f, "@frameAddress()");
 }
 
-static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
-    fprintf(irp->f, "@handle()");
+static void ir_print_handle(IrPrint *irp, IrInstructionFrameHandle *instruction) {
+    fprintf(irp->f, "@frame()");
+}
+
+static void ir_print_frame_type(IrPrint *irp, IrInstructionFrameType *instruction) {
+    fprintf(irp->f, "@Frame(");
+    ir_print_other_instruction(irp, instruction->fn);
+    fprintf(irp->f, ")");
 }
 
 static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
@@ -1764,8 +1770,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdFrameAddress:
             ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
             break;
-        case IrInstructionIdHandle:
-            ir_print_handle(irp, (IrInstructionHandle *)instruction);
+        case IrInstructionIdFrameHandle:
+            ir_print_handle(irp, (IrInstructionFrameHandle *)instruction);
+            break;
+        case IrInstructionIdFrameType:
+            ir_print_frame_type(irp, (IrInstructionFrameType *)instruction);
             break;
         case IrInstructionIdAlignOf:
             ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
test/stage1/behavior/coroutines.zig
@@ -79,15 +79,23 @@ test "local variable in async function" {
 
 test "calling an inferred async function" {
     const S = struct {
+        var x: i32 = 1;
+        var other_frame: *@Frame(other) = undefined;
+
         fn doTheTest() void {
             const p = async first();
+            expect(x == 1);
+            resume other_frame.*;
+            expect(x == 2);
         }
 
         fn first() void {
             other();
         }
         fn other() void {
+            other_frame = @frame();
             suspend;
+            x += 1;
         }
     };
     S.doTheTest();
BRANCH_TODO
@@ -0,0 +1,4 @@
+ * await
+ * await of a non async function
+ * async call on a non async function
+ * safety for resuming when it is awaiting