Commit ee64a22045

Andrew Kelley <andrew@ziglang.org>
2019-07-27 01:52:35
add the `anyframe` and `anyframe->T` types
1 parent 018a89c
src/all_types.hpp
@@ -479,6 +479,7 @@ enum NodeType {
     NodeTypeResume,
     NodeTypeAwaitExpr,
     NodeTypeSuspend,
+    NodeTypeAnyFrameType,
     NodeTypeEnumLiteral,
 };
 
@@ -936,6 +937,10 @@ struct AstNodeSuspend {
     AstNode *block;
 };
 
+struct AstNodeAnyFrameType {
+    AstNode *payload_type; // can be NULL
+};
+
 struct AstNodeEnumLiteral {
     Token *period;
     Token *identifier;
@@ -1001,6 +1006,7 @@ struct AstNode {
         AstNodeResumeExpr resume_expr;
         AstNodeAwaitExpr await_expr;
         AstNodeSuspend suspend;
+        AstNodeAnyFrameType anyframe_type;
         AstNodeEnumLiteral enum_literal;
     } data;
 };
@@ -1253,6 +1259,7 @@ enum ZigTypeId {
     ZigTypeIdArgTuple,
     ZigTypeIdOpaque,
     ZigTypeIdCoroFrame,
+    ZigTypeIdAnyFrame,
     ZigTypeIdVector,
     ZigTypeIdEnumLiteral,
 };
@@ -1272,6 +1279,10 @@ struct ZigTypeCoroFrame {
     ZigType *locals_struct;
 };
 
+struct ZigTypeAnyFrame {
+    ZigType *result_type; // null if `anyframe` instead of `anyframe->T`
+};
+
 struct ZigType {
     ZigTypeId id;
     Buf name;
@@ -1298,11 +1309,13 @@ struct ZigType {
         ZigTypeVector vector;
         ZigTypeOpaque opaque;
         ZigTypeCoroFrame frame;
+        ZigTypeAnyFrame any_frame;
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
     ZigType *pointer_parent[2]; // [0 - mut, 1 - const]
     ZigType *optional_parent;
+    ZigType *any_frame_parent;
     // If we generate a constant name value for this type, we memoize it here.
     // The type of this is array
     ConstExprValue *cached_const_name_val;
@@ -1781,6 +1794,7 @@ struct CodeGen {
         ZigType *entry_arg_tuple;
         ZigType *entry_enum_literal;
         ZigType *entry_frame_header;
+        ZigType *entry_any_frame;
     } builtin_types;
     ZigType *align_amt_type;
     ZigType *stack_trace_type;
@@ -2208,6 +2222,7 @@ enum IrInstructionId {
     IrInstructionIdSetRuntimeSafety,
     IrInstructionIdSetFloatMode,
     IrInstructionIdArrayType,
+    IrInstructionIdAnyFrameType,
     IrInstructionIdSliceType,
     IrInstructionIdGlobalAsm,
     IrInstructionIdAsm,
@@ -2709,6 +2724,12 @@ struct IrInstructionPtrType {
     bool is_allow_zero;
 };
 
+struct IrInstructionAnyFrameType {
+    IrInstruction base;
+
+    IrInstruction *payload_type;
+};
+
 struct IrInstructionSliceType {
     IrInstruction base;
 
src/analyze.cpp
@@ -256,6 +256,7 @@ AstNode *type_decl_node(ZigType *type_entry) {
         case ZigTypeIdBoundFn:
         case ZigTypeIdArgTuple:
         case ZigTypeIdVector:
+        case ZigTypeIdAnyFrame:
             return nullptr;
     }
     zig_unreachable();
@@ -322,6 +323,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
         case ZigTypeIdBoundFn:
         case ZigTypeIdArgTuple:
         case ZigTypeIdVector:
+        case ZigTypeIdAnyFrame:
             return true;
     }
     zig_unreachable();
@@ -354,6 +356,31 @@ ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
     return get_int_type(g, false, bits_needed_for_unsigned(x));
 }
 
+ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) {
+    if (result_type != nullptr && result_type->any_frame_parent != nullptr) {
+        return result_type->any_frame_parent;
+    } else if (result_type == nullptr && g->builtin_types.entry_any_frame != nullptr) {
+        return g->builtin_types.entry_any_frame;
+    }
+
+    ZigType *entry = new_type_table_entry(ZigTypeIdAnyFrame);
+    entry->abi_size = g->builtin_types.entry_usize->abi_size;
+    entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
+    entry->abi_align = g->builtin_types.entry_usize->abi_align;
+    entry->data.any_frame.result_type = result_type;
+    buf_init_from_str(&entry->name, "anyframe");
+    if (result_type != nullptr) {
+        buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name));
+    }
+
+    if (result_type != nullptr) {
+        result_type->any_frame_parent = entry;
+    } else if (result_type == nullptr) {
+        g->builtin_types.entry_any_frame = entry;
+    }
+    return entry;
+}
+
 static const char *ptr_len_to_star_str(PtrLen ptr_len) {
     switch (ptr_len) {
         case PtrLenSingle:
@@ -1080,6 +1107,7 @@ static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType
         case ZigTypeIdArgTuple:
         case ZigTypeIdOpaque:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             add_node_error(g, source_node,
                     buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
                         buf_ptr(&type_entry->name)));
@@ -1169,6 +1197,7 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdArgTuple:
         case ZigTypeIdVoid:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return false;
         case ZigTypeIdOpaque:
         case ZigTypeIdUnreachable:
@@ -1340,6 +1369,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
             case ZigTypeIdFn:
             case ZigTypeIdVector:
             case ZigTypeIdCoroFrame:
+            case ZigTypeIdAnyFrame:
                 switch (type_requires_comptime(g, type_entry)) {
                     case ReqCompTimeNo:
                         break;
@@ -1436,6 +1466,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
         case ZigTypeIdFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             switch (type_requires_comptime(g, fn_type_id.return_type)) {
                 case ReqCompTimeInvalid:
                     return g->builtin_types.entry_invalid;
@@ -2997,6 +3028,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeAwaitExpr:
         case NodeTypeSuspend:
         case NodeTypeEnumLiteral:
+        case NodeTypeAnyFrameType:
             zig_unreachable();
     }
 }
@@ -3049,6 +3081,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
         case ZigTypeIdBoundFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return type_entry;
     }
     zig_unreachable();
@@ -3550,6 +3583,7 @@ bool is_container(ZigType *type_entry) {
         case ZigTypeIdOpaque:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return false;
     }
     zig_unreachable();
@@ -3607,6 +3641,7 @@ Error resolve_container_type(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdOpaque:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             zig_unreachable();
     }
     zig_unreachable();
@@ -3615,11 +3650,13 @@ Error resolve_container_type(CodeGen *g, ZigType *type_entry) {
 ZigType *get_src_ptr_type(ZigType *type) {
     if (type->id == ZigTypeIdPointer) return type;
     if (type->id == ZigTypeIdFn) return type;
+    if (type->id == ZigTypeIdAnyFrame) return type;
     if (type->id == ZigTypeIdOptional) {
         if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
             return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
         }
         if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
+        if (type->data.maybe.child_type->id == ZigTypeIdAnyFrame) return type->data.maybe.child_type;
     }
     return nullptr;
 }
@@ -3635,6 +3672,13 @@ bool type_is_nonnull_ptr(ZigType *type) {
     return get_codegen_ptr_type(type) == type && !ptr_allows_addr_zero(type);
 }
 
+static uint32_t get_coro_frame_align_bytes(CodeGen *g) {
+    uint32_t a = g->pointer_size_bytes * 2;
+    // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
+    if (a < 8) a = 8;
+    return a;
+}
+
 uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
     ZigType *ptr_type = get_src_ptr_type(type);
     if (ptr_type->id == ZigTypeIdPointer) {
@@ -3646,6 +3690,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
         // when getting the alignment of `?extern fn() void`.
         // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
         return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
+    } else if (ptr_type->id == ZigTypeIdAnyFrame) {
+        return get_coro_frame_align_bytes(g);
     } else {
         zig_unreachable();
     }
@@ -3657,6 +3703,8 @@ bool get_ptr_const(ZigType *type) {
         return ptr_type->data.pointer.is_const;
     } else if (ptr_type->id == ZigTypeIdFn) {
         return true;
+    } else if (ptr_type->id == ZigTypeIdAnyFrame) {
+        return true;
     } else {
         zig_unreachable();
     }
@@ -4153,6 +4201,7 @@ bool handle_is_ptr(ZigType *type_entry) {
         case ZigTypeIdFn:
         case ZigTypeIdEnum:
         case ZigTypeIdVector:
+        case ZigTypeIdAnyFrame:
              return false;
         case ZigTypeIdArray:
         case ZigTypeIdStruct:
@@ -4404,6 +4453,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
         case ZigTypeIdCoroFrame:
             // TODO better hashing algorithm
             return 675741936;
+        case ZigTypeIdAnyFrame:
+            // TODO better hashing algorithm
+            return 3747294894;
         case ZigTypeIdBoundFn:
         case ZigTypeIdInvalid:
         case ZigTypeIdUnreachable:
@@ -4469,6 +4521,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
         case ZigTypeIdErrorSet:
         case ZigTypeIdEnum:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return false;
 
         case ZigTypeIdPointer:
@@ -4541,6 +4594,7 @@ static bool return_type_is_cacheable(ZigType *return_type) {
         case ZigTypeIdPointer:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return true;
 
         case ZigTypeIdArray:
@@ -4673,6 +4727,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdFloat:
         case ZigTypeIdErrorUnion:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return OnePossibleValueNo;
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
@@ -4761,6 +4816,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdVoid:
         case ZigTypeIdUnreachable:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return ReqCompTimeNo;
     }
     zig_unreachable();
@@ -5433,6 +5489,8 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
             return true;
         case ZigTypeIdCoroFrame:
             zig_panic("TODO");
+        case ZigTypeIdAnyFrame:
+            zig_panic("TODO");
         case ZigTypeIdUndefined:
             zig_panic("TODO");
         case ZigTypeIdNull:
@@ -5786,7 +5844,11 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
                 return;
             }
         case ZigTypeIdCoroFrame:
-            buf_appendf(buf, "(TODO: coroutine frame value)");
+            buf_appendf(buf, "(TODO: async function frame value)");
+            return;
+
+        case ZigTypeIdAnyFrame:
+            buf_appendf(buf, "(TODO: anyframe value)");
             return;
 
     }
@@ -5836,6 +5898,7 @@ uint32_t type_id_hash(TypeId x) {
         case ZigTypeIdBoundFn:
         case ZigTypeIdArgTuple:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             zig_unreachable();
         case ZigTypeIdErrorUnion:
             return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
@@ -5885,6 +5948,7 @@ bool type_id_eql(TypeId a, TypeId b) {
         case ZigTypeIdArgTuple:
         case ZigTypeIdOpaque:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             zig_unreachable();
         case ZigTypeIdErrorUnion:
             return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
@@ -6051,6 +6115,7 @@ static const ZigTypeId all_type_ids[] = {
     ZigTypeIdArgTuple,
     ZigTypeIdOpaque,
     ZigTypeIdCoroFrame,
+    ZigTypeIdAnyFrame,
     ZigTypeIdVector,
     ZigTypeIdEnumLiteral,
 };
@@ -6116,10 +6181,12 @@ size_t type_id_index(ZigType *entry) {
             return 21;
         case ZigTypeIdCoroFrame:
             return 22;
-        case ZigTypeIdVector:
+        case ZigTypeIdAnyFrame:
             return 23;
-        case ZigTypeIdEnumLiteral:
+        case ZigTypeIdVector:
             return 24;
+        case ZigTypeIdEnumLiteral:
+            return 25;
     }
     zig_unreachable();
 }
@@ -6178,6 +6245,8 @@ const char *type_id_name(ZigTypeId id) {
             return "Vector";
         case ZigTypeIdCoroFrame:
             return "Frame";
+        case ZigTypeIdAnyFrame:
+            return "AnyFrame";
     }
     zig_unreachable();
 }
@@ -7398,6 +7467,40 @@ static void resolve_llvm_types_coro_frame(CodeGen *g, ZigType *frame_type, Resol
     frame_type->llvm_di_type = frame_type->data.frame.locals_struct->llvm_di_type;
 }
 
+static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, ResolveStatus wanted_resolve_status) {
+    if (any_frame_type->llvm_di_type != nullptr) return;
+
+    ZigType *result_type = any_frame_type->data.any_frame.result_type;
+    Buf *name = buf_sprintf("(%s header)", buf_ptr(&any_frame_type->name));
+
+    ZigType *frame_header_type;
+    if (result_type == nullptr || !type_has_bits(result_type)) {
+        const char *field_names[] = {"resume_index", "fn_ptr", "awaiter"};
+        ZigType *field_types[] = {
+            g->builtin_types.entry_usize,
+            g->builtin_types.entry_usize,
+            g->builtin_types.entry_usize,
+        };
+        frame_header_type = get_struct_type(g, buf_ptr(name), field_names, field_types, 3);
+    } else {
+        ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false);
+
+        const char *field_names[] = {"resume_index", "fn_ptr", "awaiter", "result_ptr", "result"};
+        ZigType *field_types[] = {
+            g->builtin_types.entry_usize,
+            g->builtin_types.entry_usize,
+            g->builtin_types.entry_usize,
+            ptr_result_type,
+            result_type,
+        };
+        frame_header_type = get_struct_type(g, buf_ptr(name), field_names, field_types, 5);
+    }
+
+    ZigType *ptr_type = get_pointer_to_type(g, frame_header_type, false);
+    any_frame_type->llvm_type = get_llvm_type(g, ptr_type);
+    any_frame_type->llvm_di_type = get_llvm_di_type(g, ptr_type);
+}
+
 static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
     assert(type->id == ZigTypeIdOpaque || type_is_resolved(type, ResolveStatusSizeKnown));
     assert(wanted_resolve_status > ResolveStatusSizeKnown);
@@ -7460,6 +7563,8 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r
         }
         case ZigTypeIdCoroFrame:
             return resolve_llvm_types_coro_frame(g, type, wanted_resolve_status);
+        case ZigTypeIdAnyFrame:
+            return resolve_llvm_types_any_frame(g, type, wanted_resolve_status);
     }
     zig_unreachable();
 }
src/analyze.hpp
@@ -41,6 +41,7 @@ ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const c
 ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
         ZigType *field_types[], size_t field_count);
 ZigType *get_test_fn_type(CodeGen *g);
+ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
 bool handle_is_ptr(ZigType *type_entry);
 
 bool type_has_bits(ZigType *type_entry);
src/ast_render.cpp
@@ -259,6 +259,8 @@ static const char *node_type_str(NodeType node_type) {
             return "Suspend";
         case NodeTypePointerType:
             return "PointerType";
+        case NodeTypeAnyFrameType:
+            return "AnyFrameType";
         case NodeTypeEnumLiteral:
             return "EnumLiteral";
     }
@@ -847,6 +849,14 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 render_node_ungrouped(ar, node->data.inferred_array_type.child_type);
                 break;
             }
+        case NodeTypeAnyFrameType: {
+            fprintf(ar->f, "anyframe");
+            if (node->data.anyframe_type.payload_type != nullptr) {
+                fprintf(ar->f, "->");
+                render_node_grouped(ar, node->data.anyframe_type.payload_type);
+            }
+            break;
+        }
         case NodeTypeErrorType:
             fprintf(ar->f, "anyerror");
             break;
src/codegen.cpp
@@ -4947,6 +4947,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSetRuntimeSafety:
         case IrInstructionIdSetFloatMode:
         case IrInstructionIdArrayType:
+        case IrInstructionIdAnyFrameType:
         case IrInstructionIdSliceType:
         case IrInstructionIdSizeOf:
         case IrInstructionIdSwitchTarget:
@@ -5438,7 +5439,9 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
                 return val;
             }
         case ZigTypeIdCoroFrame:
-            zig_panic("TODO bit pack a coroutine frame");
+            zig_panic("TODO bit pack an async function frame");
+        case ZigTypeIdAnyFrame:
+            zig_panic("TODO bit pack an anyframe");
     }
     zig_unreachable();
 }
@@ -5961,6 +5964,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
             zig_unreachable();
         case ZigTypeIdCoroFrame:
             zig_panic("TODO");
+        case ZigTypeIdAnyFrame:
+            zig_panic("TODO");
     }
     zig_unreachable();
 }
@@ -7176,6 +7181,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
             "    ArgTuple: void,\n"
             "    Opaque: void,\n"
             "    Frame: void,\n"
+            "    AnyFrame: AnyFrame,\n"
             "    Vector: Vector,\n"
             "    EnumLiteral: void,\n"
             "\n\n"
@@ -7291,6 +7297,10 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
             "        args: []FnArg,\n"
             "    };\n"
             "\n"
+            "    pub const AnyFrame = struct {\n"
+            "        child: ?type,\n"
+            "    };\n"
+            "\n"
             "    pub const Vector = struct {\n"
             "        len: comptime_int,\n"
             "        child: type,\n"
@@ -8448,6 +8458,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
         case ZigTypeIdErrorUnion:
         case ZigTypeIdErrorSet:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             zig_unreachable();
         case ZigTypeIdVoid:
         case ZigTypeIdUnreachable:
@@ -8632,6 +8643,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
         case ZigTypeIdNull:
         case ZigTypeIdArgTuple:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             zig_unreachable();
     }
 }
@@ -8800,7 +8812,9 @@ static void gen_h_file(CodeGen *g) {
             case ZigTypeIdFn:
             case ZigTypeIdVector:
             case ZigTypeIdCoroFrame:
+            case ZigTypeIdAnyFrame:
                 zig_unreachable();
+
             case ZigTypeIdEnum:
                 if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
                     fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
src/ir.cpp
@@ -303,6 +303,7 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) {
         case ZigTypeIdBoundFn:
         case ZigTypeIdErrorSet:
         case ZigTypeIdOpaque:
+        case ZigTypeIdAnyFrame:
             return true;
         case ZigTypeIdFloat:
             return a->data.floating.bit_count == b->data.floating.bit_count;
@@ -563,6 +564,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
     return IrInstructionIdArrayType;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAnyFrameType *) {
+    return IrInstructionIdAnyFrameType;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
     return IrInstructionIdSliceType;
 }
@@ -1696,6 +1701,16 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *payload_type)
+{
+    IrInstructionAnyFrameType *instruction = ir_build_instruction<IrInstructionAnyFrameType>(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, bool is_allow_zero)
 {
@@ -6515,6 +6530,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n
     }
 }
 
+static IrInstruction *ir_gen_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeAnyFrameType);
+
+    AstNode *payload_type_node = node->data.anyframe_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_anyframe_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);
@@ -7884,6 +7915,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
         case NodeTypePointerType:
             return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
+        case NodeTypeAnyFrameType:
+            return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
         case NodeTypeStringLiteral:
             return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
         case NodeTypeUndefinedLiteral:
@@ -12775,6 +12808,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
         case ZigTypeIdArgTuple:
         case ZigTypeIdEnum:
         case ZigTypeIdEnumLiteral:
+        case ZigTypeIdAnyFrame:
             operator_allowed = is_equality_cmp;
             break;
 
@@ -14155,6 +14189,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                 case ZigTypeIdArgTuple:
                 case ZigTypeIdOpaque:
                 case ZigTypeIdCoroFrame:
+                case ZigTypeIdAnyFrame:
                     ir_add_error(ira, target,
                         buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
                     break;
@@ -14180,6 +14215,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
         case ZigTypeIdOpaque:
         case ZigTypeIdEnumLiteral:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             ir_add_error(ira, target,
                     buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name)));
             break;
@@ -15720,7 +15756,9 @@ static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp
         case ZigTypeIdBoundFn:
         case ZigTypeIdArgTuple:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             return ir_const_type(ira, &un_op_instruction->base, get_optional_type(ira->codegen, type_entry));
+
         case ZigTypeIdUnreachable:
         case ZigTypeIdOpaque:
             ir_add_error_node(ira, un_op_instruction->base.source_node,
@@ -17443,6 +17481,20 @@ static IrInstruction *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
     return ir_const_void(ira, &instruction->base);
 }
 
+static IrInstruction *ir_analyze_instruction_any_frame_type(IrAnalyze *ira,
+        IrInstructionAnyFrameType *instruction)
+{
+    ZigType *payload_type = nullptr;
+    if (instruction->payload_type != nullptr) {
+        payload_type = ir_resolve_type(ira, instruction->payload_type->child);
+        if (type_is_invalid(payload_type))
+            return ira->codegen->invalid_instruction;
+    }
+
+    ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type);
+    return ir_const_type(ira, &instruction->base, any_frame_type);
+}
+
 static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         IrInstructionSliceType *slice_type_instruction)
 {
@@ -17492,6 +17544,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         case ZigTypeIdBoundFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             {
                 ResolveStatus needed_status = (align_bytes == 0) ?
                     ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
@@ -17607,6 +17660,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
         case ZigTypeIdBoundFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             {
                 if ((err = ensure_complete_type(ira->codegen, child_type)))
                     return ira->codegen->invalid_instruction;
@@ -17658,6 +17712,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
         case ZigTypeIdFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             {
                 uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
                 return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
@@ -18222,6 +18277,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
         case ZigTypeIdOpaque:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             ir_add_error(ira, &switch_target_instruction->base,
                 buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
             return ira->codegen->invalid_instruction;
@@ -19656,6 +19712,22 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
 
                 break;
             }
+        case ZigTypeIdAnyFrame: {
+            result = create_const_vals(1);
+            result->special = ConstValSpecialStatic;
+            result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr);
+
+            ConstExprValue *fields = create_const_vals(1);
+            result->data.x_struct.fields = fields;
+
+            // child: ?type
+            ensure_field_index(result->type, "child", 0);
+            fields[0].special = ConstValSpecialStatic;
+            fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+            fields[0].data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr :
+                create_const_type(ira->codegen, type_entry->data.any_frame.result_type);
+            break;
+        }
         case ZigTypeIdEnum:
             {
                 result = create_const_vals(1);
@@ -20062,7 +20134,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
                 break;
             }
         case ZigTypeIdCoroFrame:
-            zig_panic("TODO @typeInfo for coro frames");
+            zig_panic("TODO @typeInfo for async function frames");
     }
 
     assert(result != nullptr);
@@ -21852,6 +21924,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
         case ZigTypeIdFn:
         case ZigTypeIdVector:
         case ZigTypeIdCoroFrame:
+        case ZigTypeIdAnyFrame:
             {
                 uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry);
                 return ir_const_unsigned(ira, &instruction->base, align_in_bytes);
@@ -23004,7 +23077,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
         case ZigTypeIdUnion:
             zig_panic("TODO buf_write_value_bytes union type");
         case ZigTypeIdCoroFrame:
-            zig_panic("TODO buf_write_value_bytes coro frame type");
+            zig_panic("TODO buf_write_value_bytes async fn frame type");
+        case ZigTypeIdAnyFrame:
+            zig_panic("TODO buf_write_value_bytes anyframe type");
     }
     zig_unreachable();
 }
@@ -23185,7 +23260,9 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
         case ZigTypeIdUnion:
             zig_panic("TODO buf_read_value_bytes union type");
         case ZigTypeIdCoroFrame:
-            zig_panic("TODO buf_read_value_bytes coro frame type");
+            zig_panic("TODO buf_read_value_bytes async fn frame type");
+        case ZigTypeIdAnyFrame:
+            zig_panic("TODO buf_read_value_bytes anyframe type");
     }
     zig_unreachable();
 }
@@ -24327,6 +24404,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
             return ir_analyze_instruction_set_runtime_safety(ira, (IrInstructionSetRuntimeSafety *)instruction);
         case IrInstructionIdSetFloatMode:
             return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
+        case IrInstructionIdAnyFrameType:
+            return ir_analyze_instruction_any_frame_type(ira, (IrInstructionAnyFrameType *)instruction);
         case IrInstructionIdSliceType:
             return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
         case IrInstructionIdGlobalAsm:
@@ -24707,6 +24786,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
+        case IrInstructionIdAnyFrameType:
         case IrInstructionIdSizeOf:
         case IrInstructionIdTestNonNull:
         case IrInstructionIdOptionalUnwrapPtr:
src/ir_print.cpp
@@ -471,6 +471,15 @@ static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instructio
     ir_print_other_instruction(irp, instruction->child_type);
 }
 
+static void ir_print_any_frame_type(IrPrint *irp, IrInstructionAnyFrameType *instruction) {
+    if (instruction->payload_type == nullptr) {
+        fprintf(irp->f, "anyframe");
+    } else {
+        fprintf(irp->f, "anyframe->");
+        ir_print_other_instruction(irp, instruction->payload_type);
+    }
+}
+
 static void ir_print_global_asm(IrPrint *irp, IrInstructionGlobalAsm *instruction) {
     fprintf(irp->f, "asm(\"%s\")", buf_ptr(instruction->asm_code));
 }
@@ -1629,6 +1638,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdSliceType:
             ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
             break;
+        case IrInstructionIdAnyFrameType:
+            ir_print_any_frame_type(irp, (IrInstructionAnyFrameType *)instruction);
+            break;
         case IrInstructionIdGlobalAsm:
             ir_print_global_asm(irp, (IrInstructionGlobalAsm *)instruction);
             break;
src/parser.cpp
@@ -282,6 +282,9 @@ static AstNode *ast_parse_prefix_op_expr(
             case NodeTypeAwaitExpr:
                 right = &prefix->data.await_expr.expr;
                 break;
+            case NodeTypeAnyFrameType:
+                right = &prefix->data.anyframe_type.payload_type;
+                break;
             case NodeTypeArrayType:
                 right = &prefix->data.array_type.child_type;
                 break;
@@ -1640,6 +1643,10 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
     if (null != nullptr)
         return ast_create_node(pc, NodeTypeNullLiteral, null);
 
+    Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
+    if (anyframe != nullptr)
+        return ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
+
     Token *true_token = eat_token_if(pc, TokenIdKeywordTrue);
     if (true_token != nullptr) {
         AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, true_token);
@@ -2510,7 +2517,7 @@ static AstNode *ast_parse_prefix_op(ParseContext *pc) {
 
 // PrefixTypeOp
 //     <- QUESTIONMARK
-//      / KEYWORD_promise MINUSRARROW
+//      / KEYWORD_anyframe MINUSRARROW
 //      / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
 //      / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
 static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
@@ -2521,6 +2528,16 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
         return res;
     }
 
+    Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
+    if (anyframe != nullptr) {
+        if (eat_token_if(pc, TokenIdArrow) != nullptr) {
+            AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
+            return res;
+        }
+
+        put_back_token(pc);
+    }
+
     AstNode *array = ast_parse_array_type_start(pc);
     if (array != nullptr) {
         assert(array->type == NodeTypeArrayType);
@@ -3005,6 +3022,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
         case NodeTypeInferredArrayType:
             visit_field(&node->data.array_type.child_type, visit, context);
             break;
+        case NodeTypeAnyFrameType:
+            visit_field(&node->data.anyframe_type.payload_type, visit, context);
+            break;
         case NodeTypeErrorType:
             // none
             break;
src/tokenizer.cpp
@@ -109,6 +109,7 @@ static const struct ZigKeyword zig_keywords[] = {
     {"align", TokenIdKeywordAlign},
     {"allowzero", TokenIdKeywordAllowZero},
     {"and", TokenIdKeywordAnd},
+    {"anyframe", TokenIdKeywordAnyFrame},
     {"asm", TokenIdKeywordAsm},
     {"async", TokenIdKeywordAsync},
     {"await", TokenIdKeywordAwait},
@@ -1533,6 +1534,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordCancel: return "cancel";
         case TokenIdKeywordAlign: return "align";
         case TokenIdKeywordAnd: return "and";
+        case TokenIdKeywordAnyFrame: return "anyframe";
         case TokenIdKeywordAsm: return "asm";
         case TokenIdKeywordBreak: return "break";
         case TokenIdKeywordCatch: return "catch";
src/tokenizer.hpp
@@ -53,6 +53,7 @@ enum TokenId {
     TokenIdKeywordAlign,
     TokenIdKeywordAllowZero,
     TokenIdKeywordAnd,
+    TokenIdKeywordAnyFrame,
     TokenIdKeywordAsm,
     TokenIdKeywordAsync,
     TokenIdKeywordAwait,
std/zig/ast.zig
@@ -400,7 +400,7 @@ pub const Node = struct {
         VarType,
         ErrorType,
         FnProto,
-        PromiseType,
+        AnyFrameType,
 
         // Primary expressions
         IntegerLiteral,
@@ -952,9 +952,9 @@ pub const Node = struct {
         }
     };
 
-    pub const PromiseType = struct {
+    pub const AnyFrameType = struct {
         base: Node,
-        promise_token: TokenIndex,
+        anyframe_token: TokenIndex,
         result: ?Result,
 
         pub const Result = struct {
@@ -962,7 +962,7 @@ pub const Node = struct {
             return_type: *Node,
         };
 
-        pub fn iterate(self: *PromiseType, index: usize) ?*Node {
+        pub fn iterate(self: *AnyFrameType, index: usize) ?*Node {
             var i = index;
 
             if (self.result) |result| {
@@ -973,13 +973,13 @@ pub const Node = struct {
             return null;
         }
 
-        pub fn firstToken(self: *const PromiseType) TokenIndex {
-            return self.promise_token;
+        pub fn firstToken(self: *const AnyFrameType) TokenIndex {
+            return self.anyframe_token;
         }
 
-        pub fn lastToken(self: *const PromiseType) TokenIndex {
+        pub fn lastToken(self: *const AnyFrameType) TokenIndex {
             if (self.result) |result| return result.return_type.lastToken();
-            return self.promise_token;
+            return self.anyframe_token;
         }
     };
 
std/zig/parse.zig
@@ -1201,7 +1201,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
 ///      / KEYWORD_error DOT IDENTIFIER
 ///      / KEYWORD_false
 ///      / KEYWORD_null
-///      / KEYWORD_promise
+///      / KEYWORD_anyframe
 ///      / KEYWORD_true
 ///      / KEYWORD_undefined
 ///      / KEYWORD_unreachable
@@ -1256,11 +1256,11 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
     }
     if (eatToken(it, .Keyword_false)) |token| return createLiteral(arena, Node.BoolLiteral, token);
     if (eatToken(it, .Keyword_null)) |token| return createLiteral(arena, Node.NullLiteral, token);
-    if (eatToken(it, .Keyword_promise)) |token| {
-        const node = try arena.create(Node.PromiseType);
-        node.* = Node.PromiseType{
-            .base = Node{ .id = .PromiseType },
-            .promise_token = token,
+    if (eatToken(it, .Keyword_anyframe)) |token| {
+        const node = try arena.create(Node.AnyFrameType);
+        node.* = Node.AnyFrameType{
+            .base = Node{ .id = .AnyFrameType },
+            .anyframe_token = token,
             .result = null,
         };
         return &node.base;
@@ -2194,7 +2194,7 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
 
 /// PrefixTypeOp
 ///     <- QUESTIONMARK
-///      / KEYWORD_promise MINUSRARROW
+///      / KEYWORD_anyframe MINUSRARROW
 ///      / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
 ///      / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
 fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
@@ -2209,20 +2209,20 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
         return &node.base;
     }
 
-    // TODO: Returning a PromiseType instead of PrefixOp makes casting and setting .rhs or
+    // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or
     //       .return_type more difficult for the caller (see parsePrefixOpExpr helper).
-    //       Consider making the PromiseType a member of PrefixOp and add a
-    //       PrefixOp.PromiseType variant?
-    if (eatToken(it, .Keyword_promise)) |token| {
+    //       Consider making the AnyFrameType a member of PrefixOp and add a
+    //       PrefixOp.AnyFrameType variant?
+    if (eatToken(it, .Keyword_anyframe)) |token| {
         const arrow = eatToken(it, .Arrow) orelse {
             putBackToken(it, token);
             return null;
         };
-        const node = try arena.create(Node.PromiseType);
-        node.* = Node.PromiseType{
-            .base = Node{ .id = .PromiseType },
-            .promise_token = token,
-            .result = Node.PromiseType.Result{
+        const node = try arena.create(Node.AnyFrameType);
+        node.* = Node.AnyFrameType{
+            .base = Node{ .id = .AnyFrameType },
+            .anyframe_token = token,
+            .result = Node.AnyFrameType.Result{
                 .arrow_token = arrow,
                 .return_type = undefined, // set by caller
             },
@@ -2903,8 +2903,8 @@ fn parsePrefixOpExpr(
                         rightmost_op = rhs;
                     } else break;
                 },
-                .PromiseType => {
-                    const prom = rightmost_op.cast(Node.PromiseType).?;
+                .AnyFrameType => {
+                    const prom = rightmost_op.cast(Node.AnyFrameType).?;
                     if (try opParseFn(arena, it, tree)) |rhs| {
                         prom.result.?.return_type = rhs;
                         rightmost_op = rhs;
@@ -2922,8 +2922,8 @@ fn parsePrefixOpExpr(
                     .InvalidToken = AstError.InvalidToken{ .token = it.index },
                 });
             },
-            .PromiseType => {
-                const prom = rightmost_op.cast(Node.PromiseType).?;
+            .AnyFrameType => {
+                const prom = rightmost_op.cast(Node.AnyFrameType).?;
                 prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, AstError{
                     .InvalidToken = AstError.InvalidToken{ .token = it.index },
                 });
std/zig/parser_test.zig
@@ -2111,12 +2111,12 @@ test "zig fmt: coroutines" {
         \\    suspend;
         \\    x += 1;
         \\    suspend;
-        \\    const p: promise->void = async simpleAsyncFn() catch unreachable;
+        \\    const p: anyframe->void = async simpleAsyncFn() catch unreachable;
         \\    await p;
         \\}
         \\
         \\test "coroutine suspend, resume, cancel" {
-        \\    const p: promise = try async<std.debug.global_allocator> testAsyncSeq();
+        \\    const p: anyframe = try async<std.debug.global_allocator> testAsyncSeq();
         \\    resume p;
         \\    cancel p;
         \\}
std/zig/render.zig
@@ -1205,15 +1205,15 @@ fn renderExpression(
             }
         },
 
-        ast.Node.Id.PromiseType => {
-            const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
+        ast.Node.Id.AnyFrameType => {
+            const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
 
-            if (promise_type.result) |result| {
-                try renderToken(tree, stream, promise_type.promise_token, indent, start_col, Space.None); // promise
+            if (anyframe_type.result) |result| {
+                try renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, Space.None); // anyframe
                 try renderToken(tree, stream, result.arrow_token, indent, start_col, Space.None); // ->
                 return renderExpression(allocator, stream, tree, indent, start_col, result.return_type, space);
             } else {
-                return renderToken(tree, stream, promise_type.promise_token, indent, start_col, space); // promise
+                return renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, space); // anyframe
             }
         },
 
std/zig/tokenizer.zig
@@ -15,6 +15,7 @@ pub const Token = struct {
         Keyword{ .bytes = "align", .id = Id.Keyword_align },
         Keyword{ .bytes = "allowzero", .id = Id.Keyword_allowzero },
         Keyword{ .bytes = "and", .id = Id.Keyword_and },
+        Keyword{ .bytes = "anyframe", .id = Id.Keyword_anyframe },
         Keyword{ .bytes = "asm", .id = Id.Keyword_asm },
         Keyword{ .bytes = "async", .id = Id.Keyword_async },
         Keyword{ .bytes = "await", .id = Id.Keyword_await },
@@ -42,7 +43,6 @@ pub const Token = struct {
         Keyword{ .bytes = "or", .id = Id.Keyword_or },
         Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse },
         Keyword{ .bytes = "packed", .id = Id.Keyword_packed },
-        Keyword{ .bytes = "promise", .id = Id.Keyword_promise },
         Keyword{ .bytes = "pub", .id = Id.Keyword_pub },
         Keyword{ .bytes = "resume", .id = Id.Keyword_resume },
         Keyword{ .bytes = "return", .id = Id.Keyword_return },
@@ -174,7 +174,7 @@ pub const Token = struct {
         Keyword_or,
         Keyword_orelse,
         Keyword_packed,
-        Keyword_promise,
+        Keyword_anyframe,
         Keyword_pub,
         Keyword_resume,
         Keyword_return,
std/hash_map.zig
@@ -540,6 +540,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
         .Undefined,
         .ArgTuple,
         .Frame,
+        .AnyFrame,
         => @compileError("cannot hash this type"),
 
         .Void,
std/testing.zig
@@ -30,6 +30,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
         .ArgTuple,
         .Opaque,
         .Frame,
+        .AnyFrame,
         => @compileError("value of type " ++ @typeName(@typeOf(actual)) ++ " encountered"),
 
         .Undefined,
test/stage1/behavior/type_info.zig
@@ -177,11 +177,11 @@ fn testUnion() void {
     expect(TypeId(typeinfo_info) == TypeId.Union);
     expect(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
     expect(typeinfo_info.Union.tag_type.? == TypeId);
-    expect(typeinfo_info.Union.fields.len == 25);
+    expect(typeinfo_info.Union.fields.len == 26);
     expect(typeinfo_info.Union.fields[4].enum_field != null);
     expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
     expect(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
-    expect(typeinfo_info.Union.decls.len == 20);
+    expect(typeinfo_info.Union.decls.len == 21);
 
     const TestNoTagUnion = union {
         Foo: void,
@@ -280,6 +280,25 @@ fn testVector() void {
     expect(vec_info.Vector.child == i32);
 }
 
+test "type info: anyframe and anyframe->T" {
+    testAnyFrame();
+    comptime testAnyFrame();
+}
+
+fn testAnyFrame() void {
+    {
+        const anyframe_info = @typeInfo(anyframe->i32);
+        expect(TypeId(anyframe_info) == .AnyFrame);
+        expect(anyframe_info.AnyFrame.child.? == i32);
+    }
+
+    {
+        const anyframe_info = @typeInfo(anyframe);
+        expect(TypeId(anyframe_info) == .AnyFrame);
+        expect(anyframe_info.AnyFrame.child == null);
+    }
+}
+
 test "type info: optional field unwrapping" {
     const Struct = struct {
         cdOffset: u32,
BRANCH_TODO
@@ -1,6 +1,4 @@
- * reimplement @frameSize with Prefix Data
- * reimplement with function splitting rather than switch
- * add the `anyframe` type and `anyframe->T`
+ * make the anyframe type and anyframe->T type work with resume
  * await
  * await of a non async function
  * await in single-threaded mode
@@ -12,3 +10,5 @@
  * implicit cast of normal function to async function should be allowed when it is inferred to be async
  * go over the commented out tests
  * revive std.event.Loop
+ * reimplement with function splitting rather than switch
+ * @typeInfo for @Frame(func)