Commit 12fcbecbf8

Andrew Kelley <superjoe30@gmail.com>
2016-12-17 22:06:48
IR: add more instructions
* MaybeWrap * TestErr * UnwrapErrCode * UnwrapErrPayload * ErrUnionTypeChild * ErrWrapCode * ErrWrapPayload
1 parent 3a3cc7b
src/all_types.hpp
@@ -115,14 +115,26 @@ enum ConstValSpecial {
     ConstValSpecialZeroes,
 };
 
+enum RuntimeHintErrorUnion {
+    RuntimeHintErrorUnionUnknown,
+    RuntimeHintErrorUnionError,
+    RuntimeHintErrorUnionNonError,
+};
+
+enum RuntimeHintMaybe {
+    RuntimeHintMaybeUnknown,
+    RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
+    RuntimeHintMaybeNonNull,
+};
+
 struct ConstExprValue {
     ConstValSpecial special;
     bool depends_on_compile_var;
     LLVMValueRef llvm_value;
     LLVMValueRef llvm_global;
 
-    // populated if val_type == ConstValTypeOk
     union {
+        // populated if special == ConstValSpecialStatic
         BigNum x_bignum;
         bool x_bool;
         FnTableEntry *x_fn;
@@ -137,6 +149,10 @@ struct ConstExprValue {
         ConstPtrValue x_ptr;
         ImportTableEntry *x_import;
         Scope *x_block;
+
+        // populated if special == ConstValSpecialRuntime
+        RuntimeHintErrorUnion rh_error_union;
+        RuntimeHintMaybe rh_maybe;
     } data;
 };
 
@@ -412,10 +428,6 @@ enum CastOp {
     CastOpIntToPtr,
     CastOpWidenOrShorten,
     CastOpToUnknownSizeArray,
-    CastOpMaybeWrap,
-    CastOpNullToMaybe,
-    CastOpErrorWrap,
-    CastOpPureErrorWrap,
     CastOpPointerReinterpret,
     CastOpErrToInt,
     CastOpIntToFloat,
@@ -1394,6 +1406,7 @@ enum IrInstructionId {
     IrInstructionIdSizeOf,
     IrInstructionIdTestNull,
     IrInstructionIdUnwrapMaybe,
+    IrInstructionIdMaybeWrap,
     IrInstructionIdEnumTag,
     IrInstructionIdClz,
     IrInstructionIdCtz,
@@ -1426,6 +1439,12 @@ enum IrInstructionId {
     IrInstructionIdFrameAddress,
     IrInstructionIdAlignOf,
     IrInstructionIdOverflowOp,
+    IrInstructionIdTestErr,
+    IrInstructionIdUnwrapErrCode,
+    IrInstructionIdUnwrapErrPayload,
+    IrInstructionIdErrUnionTypeChild,
+    IrInstructionIdErrWrapCode,
+    IrInstructionIdErrWrapPayload,
 };
 
 struct IrInstruction {
@@ -1439,7 +1458,6 @@ struct IrInstruction {
     // if ref_count is zero, instruction can be omitted in codegen
     size_t ref_count;
     IrInstruction *other;
-    ReturnKnowledge return_knowledge;
 };
 
 struct IrInstructionCondBr {
@@ -1504,10 +1522,6 @@ enum IrUnOp {
     IrUnOpDereference,
     IrUnOpError,
     IrUnOpMaybe,
-    IrUnOpUnwrapError,
-    IrUnOpUnwrapMaybe,
-    IrUnOpErrorReturn,
-    IrUnOpMaybeReturn,
 };
 
 struct IrInstructionUnOp {
@@ -2004,6 +2018,53 @@ struct IrInstructionAlignOf {
     IrInstruction *type_value;
 };
 
+// returns true if error, returns false if not error
+struct IrInstructionTestErr {
+    IrInstruction base;
+
+    IrInstruction *value;
+};
+
+struct IrInstructionUnwrapErrCode {
+    IrInstruction base;
+
+    IrInstruction *value;
+};
+
+struct IrInstructionUnwrapErrPayload {
+    IrInstruction base;
+
+    IrInstruction *value;
+    bool safety_check_on;
+};
+
+struct IrInstructionErrUnionTypeChild {
+    IrInstruction base;
+
+    IrInstruction *type_value;
+};
+
+struct IrInstructionMaybeWrap {
+    IrInstruction base;
+
+    IrInstruction *value;
+    LLVMValueRef tmp_ptr;
+};
+
+struct IrInstructionErrWrapPayload {
+    IrInstruction base;
+
+    IrInstruction *value;
+    LLVMValueRef tmp_ptr;
+};
+
+struct IrInstructionErrWrapCode {
+    IrInstruction base;
+
+    IrInstruction *value;
+    LLVMValueRef tmp_ptr;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
@@ -2019,4 +2080,7 @@ static const size_t maybe_null_index = 1;
 static const size_t enum_gen_tag_index = 0;
 static const size_t enum_gen_union_index = 1;
 
+static const size_t err_union_err_index = 0;
+static const size_t err_union_payload_index = 1;
+
 #endif
src/ast_render.cpp
@@ -862,10 +862,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                     fprintf(ar->f, "const");
                 break;
             }
+        case NodeTypeUnwrapErrorExpr:
+            {
+                render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
+                fprintf(ar->f, " %%%% ");
+                if (node->data.unwrap_err_expr.symbol) {
+                    Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
+                    fprintf(ar->f, "|%s| ", buf_ptr(var_name));
+                }
+                render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
+                break;
+            }
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeErrorValueDecl:
-        case NodeTypeUnwrapErrorExpr:
         case NodeTypeStructField:
         case NodeTypeUse:
         case NodeTypeZeroesLiteral:
src/codegen.cpp
@@ -969,63 +969,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
             } else {
                 zig_panic("TODO");
             }
-        case CastOpMaybeWrap:
-            {
-                assert(cast_instruction->tmp_ptr);
-                assert(wanted_type->id == TypeTableEntryIdMaybe);
-                assert(actual_type);
-
-                TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
-
-                if (child_type->id == TypeTableEntryIdPointer ||
-                    child_type->id == TypeTableEntryIdFn)
-                {
-                    return expr_val;
-                } else {
-                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
-                    gen_assign_raw(g, cast_instruction->base.source_node,
-                            val_ptr, expr_val, child_type, actual_type);
-
-                    LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
-                    LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
-                }
-
-                return cast_instruction->tmp_ptr;
-            }
-        case CastOpNullToMaybe:
-            // handled by constant expression evaluator
-            zig_unreachable();
-        case CastOpErrorWrap:
-            {
-                assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-                TypeTableEntry *child_type = wanted_type->data.error.child_type;
-                LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
-
-                if (!type_has_bits(child_type)) {
-                    return ok_err_val;
-                } else {
-                    assert(cast_instruction->tmp_ptr);
-                    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-                    assert(actual_type);
-
-                    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
-                    LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
-
-                    LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
-                    gen_assign_raw(g, cast_instruction->base.source_node,
-                            payload_ptr, expr_val, child_type, actual_type);
-
-                    return cast_instruction->tmp_ptr;
-                }
-            }
-        case CastOpPureErrorWrap:
-            assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-
-            if (!type_has_bits(wanted_type->data.error.child_type)) {
-                return expr_val;
-            } else {
-                zig_panic("TODO");
-            }
         case CastOpPtrToInt:
             return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
         case CastOpIntToPtr:
@@ -1257,78 +1200,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
             {
                 zig_panic("TODO codegen PrefixOpMaybe");
             }
-        case IrUnOpUnwrapError:
-            {
-                assert(expr_type->id == TypeTableEntryIdErrorUnion);
-                TypeTableEntry *child_type = expr_type->data.error.child_type;
-
-                if (ir_want_debug_safety(g, &un_op_instruction->base)) {
-                    LLVMValueRef err_val;
-                    if (type_has_bits(child_type)) {
-                        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr, 0, "");
-                        err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
-                    } else {
-                        err_val = expr;
-                    }
-                    LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
-                    LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
-                    LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError");
-                    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk");
-                    LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
-
-                    LLVMPositionBuilderAtEnd(g->builder, err_block);
-                    gen_debug_safety_crash(g);
-
-                    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-                }
-
-                if (type_has_bits(child_type)) {
-                    LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr, 1, "");
-                    return get_handle_value(g, child_val_ptr, child_type);
-                } else {
-                    return nullptr;
-                }
-            }
-        case IrUnOpUnwrapMaybe:
-            {
-                assert(expr_type->id == TypeTableEntryIdMaybe);
-                TypeTableEntry *child_type = expr_type->data.maybe.child_type;
-
-                if (ir_want_debug_safety(g, &un_op_instruction->base)) {
-                    LLVMValueRef cond_val;
-                    if (child_type->id == TypeTableEntryIdPointer ||
-                        child_type->id == TypeTableEntryIdFn)
-                    {
-                        cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, expr,
-                                LLVMConstNull(child_type->type_ref), "");
-                    } else {
-                        LLVMValueRef maybe_null_ptr = LLVMBuildStructGEP(g->builder, expr, 1, "");
-                        cond_val = LLVMBuildLoad(g->builder, maybe_null_ptr, "");
-                    }
-
-                    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk");
-                    LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeNull");
-                    LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block);
-
-                    LLVMPositionBuilderAtEnd(g->builder, null_block);
-                    gen_debug_safety_crash(g);
-
-                    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-                }
-
-
-                if (child_type->id == TypeTableEntryIdPointer ||
-                    child_type->id == TypeTableEntryIdFn)
-                {
-                    return expr;
-                } else {
-                    LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr, 0, "");
-                    return get_handle_value(g, maybe_field_ptr, child_type);
-                }
-            }
-        case IrUnOpErrorReturn:
-        case IrUnOpMaybeReturn:
-            zig_panic("TODO codegen more un ops");
     }
 
     zig_unreachable();
@@ -2132,6 +2003,143 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,
     return overflow_bit;
 }
 
+static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {
+    TypeTableEntry *ptr_type = get_underlying_type(instruction->value->type_entry);
+    TypeTableEntry *err_union_type = get_underlying_type(ptr_type->data.pointer.child_type);
+    TypeTableEntry *child_type = get_underlying_type(err_union_type->data.error.child_type);
+    LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
+    LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type);
+
+    LLVMValueRef err_val;
+    if (type_has_bits(child_type)) {
+        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
+        err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
+    } else {
+        err_val = err_union_handle;
+    }
+
+    LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
+    return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, "");
+}
+
+static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) {
+    TypeTableEntry *ptr_type = get_underlying_type(instruction->value->type_entry);
+    TypeTableEntry *err_union_type = get_underlying_type(ptr_type->data.pointer.child_type);
+    TypeTableEntry *child_type = get_underlying_type(err_union_type->data.error.child_type);
+    LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
+    LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type);
+
+    if (type_has_bits(child_type)) {
+        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
+        return LLVMBuildLoad(g->builder, err_val_ptr, "");
+    } else {
+        return err_union_handle;
+    }
+}
+
+static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) {
+    TypeTableEntry *ptr_type = get_underlying_type(instruction->value->type_entry);
+    TypeTableEntry *err_union_type = get_underlying_type(ptr_type->data.pointer.child_type);
+    TypeTableEntry *child_type = get_underlying_type(err_union_type->data.error.child_type);
+    LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
+    LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type);
+
+    if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on) {
+        LLVMValueRef err_val;
+        if (type_has_bits(child_type)) {
+            LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
+            err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
+        } else {
+            err_val = err_union_handle;
+        }
+        LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
+        LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
+        LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError");
+        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk");
+        LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
+
+        LLVMPositionBuilderAtEnd(g->builder, err_block);
+        gen_debug_safety_crash(g);
+
+        LLVMPositionBuilderAtEnd(g->builder, ok_block);
+    }
+
+    if (type_has_bits(child_type)) {
+        return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, "");
+    } else {
+        return nullptr;
+    }
+}
+
+static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) {
+    TypeTableEntry *wanted_type = instruction->base.type_entry;
+
+    assert(wanted_type->id == TypeTableEntryIdMaybe);
+
+    TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
+
+    LLVMValueRef payload_val = ir_llvm_value(g, instruction->value);
+    if (child_type->id == TypeTableEntryIdPointer ||
+        child_type->id == TypeTableEntryIdFn)
+    {
+        return payload_val;
+    }
+
+    assert(instruction->tmp_ptr);
+
+    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
+    gen_assign_raw(g, instruction->base.source_node, val_ptr, payload_val, child_type, instruction->value->type_entry);
+
+    LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
+    LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
+
+    return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) {
+    TypeTableEntry *wanted_type = instruction->base.type_entry;
+
+    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+    TypeTableEntry *child_type = wanted_type->data.error.child_type;
+    LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
+
+    if (!type_has_bits(child_type))
+        return err_val;
+
+    assert(instruction->tmp_ptr);
+
+    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, "");
+    LLVMBuildStore(g->builder, err_val, err_tag_ptr);
+
+    return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapPayload *instruction) {
+    TypeTableEntry *wanted_type = instruction->base.type_entry;
+
+    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+    TypeTableEntry *child_type = wanted_type->data.error.child_type;
+
+    LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
+
+    if (!type_has_bits(child_type))
+        return ok_err_val;
+
+    assert(instruction->tmp_ptr);
+
+    LLVMValueRef payload_val = ir_llvm_value(g, instruction->value);
+
+    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, "");
+    LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
+
+    LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, "");
+    gen_assign_raw(g, instruction->base.source_node, payload_ptr, payload_val, child_type, instruction->value->type_entry);
+
+    return instruction->tmp_ptr;
+}
+
 static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
     AstNode *source_node = instruction->source_node;
     Scope *scope = instruction->scope;
@@ -2175,6 +2183,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdIntType:
         case IrInstructionIdMemberCount:
         case IrInstructionIdAlignOf:
+        case IrInstructionIdErrUnionTypeChild:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -2250,6 +2259,18 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
         case IrInstructionIdOverflowOp:
             return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
+        case IrInstructionIdTestErr:
+            return ir_render_test_err(g, executable, (IrInstructionTestErr *)instruction);
+        case IrInstructionIdUnwrapErrCode:
+            return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction);
+        case IrInstructionIdUnwrapErrPayload:
+            return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction);
+        case IrInstructionIdMaybeWrap:
+            return ir_render_maybe_wrap(g, executable, (IrInstructionMaybeWrap *)instruction);
+        case IrInstructionIdErrWrapCode:
+            return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction);
+        case IrInstructionIdErrWrapPayload:
+            return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction);
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdStructInit:
@@ -2835,6 +2856,15 @@ static void do_code_gen(CodeGen *g) {
             } else if (instruction->id == IrInstructionIdSlice) {
                 IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
                 slot = &slice_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdMaybeWrap) {
+                IrInstructionMaybeWrap *maybe_wrap_instruction = (IrInstructionMaybeWrap *)instruction;
+                slot = &maybe_wrap_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdErrWrapPayload) {
+                IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction;
+                slot = &err_wrap_payload_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdErrWrapCode) {
+                IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
+                slot = &err_wrap_code_instruction->tmp_ptr;
             } else {
                 zig_unreachable();
             }
src/eval.cpp
@@ -342,23 +342,6 @@ void eval_const_expr_implicit_cast(CastOp cast_op,
                 const_val->special = ConstValSpecialStatic;
                 break;
             }
-        case CastOpMaybeWrap:
-            const_val->data.x_maybe = other_val;
-            const_val->special = ConstValSpecialStatic;
-            break;
-        case CastOpNullToMaybe:
-            const_val->data.x_maybe = nullptr;
-            const_val->special = ConstValSpecialStatic;
-            break;
-        case CastOpErrorWrap:
-            const_val->data.x_err_union.err = nullptr;
-            const_val->data.x_err_union.payload = other_val;
-            const_val->special = ConstValSpecialStatic;
-            break;
-        case CastOpPureErrorWrap:
-            const_val->data.x_err_union.err = other_val->data.x_pure_err;
-            const_val->special = ConstValSpecialStatic;
-            break;
         case CastOpErrToInt:
             {
                 uint64_t value;
src/ir.cpp
@@ -93,6 +93,10 @@ static Buf *exec_c_import_buf(IrExecutable *exec) {
     return exec->c_import_buf;
 }
 
+static bool instr_is_comptime(IrInstruction *instruction) {
+    return instruction->static_value.special != ConstValSpecialRuntime;
+}
+
 static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) {
     new_instruction->other = old_instruction;
     old_instruction->other = new_instruction;
@@ -411,6 +415,34 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
     return IrInstructionIdOverflowOp;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) {
+    return IrInstructionIdTestErr;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) {
+    return IrInstructionIdUnwrapErrCode;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload *) {
+    return IrInstructionIdUnwrapErrPayload;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrUnionTypeChild *) {
+    return IrInstructionIdErrUnionTypeChild;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) {
+    return IrInstructionIdMaybeWrap;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) {
+    return IrInstructionIdErrWrapPayload;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapCode *) {
+    return IrInstructionIdErrWrapCode;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1037,8 +1069,11 @@ static IrInstruction *ir_build_to_ptr_type(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
-static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
-    IrInstructionPtrTypeChild *instruction = ir_build_instruction<IrInstructionPtrTypeChild>(irb, scope, source_node);
+static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction *value)
+{
+    IrInstructionPtrTypeChild *instruction = ir_build_instruction<IrInstructionPtrTypeChild>(
+        irb, scope, source_node);
     instruction->value = value;
 
     ir_ref_instruction(value);
@@ -1198,6 +1233,33 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *
     return new_instruction;
 }
 
+static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
+    IrInstructionMaybeWrap *instruction = ir_build_instruction<IrInstructionMaybeWrap>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
+    IrInstructionErrWrapPayload *instruction = ir_build_instruction<IrInstructionErrWrapPayload>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
+    IrInstructionErrWrapCode *instruction = ir_build_instruction<IrInstructionErrWrapCode>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
     IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(irb, scope, source_node);
     instruction->value = value;
@@ -1709,6 +1771,76 @@ static IrInstruction *ir_build_alignof(IrBuilder *irb, Scope *scope, AstNode *so
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction *value)
+{
+    IrInstructionTestErr *instruction = ir_build_instruction<IrInstructionTestErr>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_test_err_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
+    IrInstruction *new_instruction = ir_build_test_err(irb, old_instruction->scope, old_instruction->source_node,
+            value);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
+static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction *value)
+{
+    IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_unwrap_err_code_from(IrBuilder *irb, IrInstruction *old_instruction,
+    IrInstruction *value)
+{
+    IrInstruction *new_instruction = ir_build_unwrap_err_code(irb, old_instruction->scope,
+        old_instruction->source_node, value);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
+static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction *value, bool safety_check_on)
+{
+    IrInstructionUnwrapErrPayload *instruction = ir_build_instruction<IrInstructionUnwrapErrPayload>(irb, scope, source_node);
+    instruction->value = value;
+    instruction->safety_check_on = safety_check_on;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruction *old_instruction,
+    IrInstruction *value, bool safety_check_on)
+{
+    IrInstruction *new_instruction = ir_build_unwrap_err_payload(irb, old_instruction->scope,
+        old_instruction->source_node, value, safety_check_on);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
+static IrInstruction *ir_build_err_union_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction *type_value)
+{
+    IrInstructionErrUnionTypeChild *instruction = ir_build_instruction<IrInstructionErrUnionTypeChild>(irb, scope, source_node);
+    instruction->type_value = type_value;
+
+    ir_ref_instruction(type_value);
+
+    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;
@@ -1799,7 +1931,29 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 return ir_build_return(irb, scope, node, return_value);
             }
         case ReturnKindError:
-            zig_panic("TODO gen IR for %%return");
+            {
+                assert(expr_node);
+                IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
+                if (err_union_ptr == irb->codegen->invalid_instruction)
+                    return irb->codegen->invalid_instruction;
+                IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_ptr);
+
+                IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "ErrRetReturn");
+                IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "ErrRetContinue");
+                ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_inline);
+
+                ir_set_cursor_at_end(irb, return_block);
+                ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
+                IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
+                ir_build_return(irb, scope, node, err_val);
+
+                ir_set_cursor_at_end(irb, continue_block);
+                IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
+                if (lval != LValPurposeNone)
+                    return unwrapped_ptr;
+                else
+                    return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
+            }
         case ReturnKindMaybe:
             {
                 assert(expr_node);
@@ -2798,13 +2952,33 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode
     return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValPurposeNone);
 }
 
-static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
-    AstNode *expr = node->data.prefix_op_expr.primary_expr;
-    IrInstruction *value = ir_gen_node_extra(irb, expr, scope, LValPurposeAddressOf);
-    if (value == irb->codegen->invalid_instruction)
-        return value;
+static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
+    assert(node->type == NodeTypePrefixOpExpr);
+    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+    IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
+    if (err_union_ptr == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
+    IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, true);
+    if (payload_ptr == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
 
-    IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, value, true);
+    if (lval == LValPurposeNone)
+        return ir_build_load_ptr(irb, scope, node, payload_ptr);
+    else
+        return payload_ptr;
+}
+
+static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
+    assert(node->type == NodeTypePrefixOpExpr);
+    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+    IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
+    if (maybe_ptr == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
+    IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
     if (lval == LValPurposeNone)
         return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
     else
@@ -2860,9 +3034,9 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
         case PrefixOpError:
             return ir_gen_prefix_op_id(irb, scope, node, IrUnOpError);
         case PrefixOpUnwrapError:
-            return ir_gen_prefix_op_id(irb, scope, node, IrUnOpUnwrapError);
+            return ir_gen_err_assert_ok(irb, scope, node, lval);
         case PrefixOpUnwrapMaybe:
-            return ir_gen_prefix_op_unwrap_maybe(irb, scope, node, lval);
+            return ir_gen_maybe_assert_ok(irb, scope, node, lval);
     }
     zig_unreachable();
 }
@@ -3626,6 +3800,66 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node)
     return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, slice_expr->is_const);
 }
 
+static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeUnwrapErrorExpr);
+
+    AstNode *op1_node = node->data.unwrap_err_expr.op1;
+    AstNode *op2_node = node->data.unwrap_err_expr.op2;
+    AstNode *var_node = node->data.unwrap_err_expr.symbol;
+
+    bool is_inline = ir_should_inline(irb);
+
+    IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPurposeAddressOf);
+    if (err_union_ptr == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
+    IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_ptr);
+
+    IrBasicBlock *ok_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrOk");
+    IrBasicBlock *err_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrError");
+    IrBasicBlock *end_block = ir_build_basic_block(irb, parent_scope, "UnwrapErrEnd");
+    ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_inline);
+
+    ir_set_cursor_at_end(irb, err_block);
+    Scope *err_scope;
+    if (var_node) {
+        assert(var_node->type == NodeTypeSymbol);
+        IrInstruction *err_union_ptr_type = ir_build_typeof(irb, parent_scope, var_node, err_union_ptr);
+        IrInstruction *err_union_type = ir_build_ptr_type_child(irb, parent_scope, var_node, err_union_ptr_type);
+        IrInstruction *var_type = ir_build_err_union_type_child(irb, parent_scope, var_node, err_union_type);
+        Buf *var_name = var_node->data.symbol_expr.symbol;
+        bool is_const = true;
+        bool is_shadowable = false;
+        VariableTableEntry *var = ir_create_var(irb, node, parent_scope, var_name,
+            is_const, is_const, is_shadowable, is_inline);
+        err_scope = var->child_scope;
+        IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
+        ir_build_var_decl(irb, err_scope, var_node, var, var_type, err_val);
+    } else {
+        err_scope = parent_scope;
+    }
+    IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope);
+    if (err_result == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+    IrBasicBlock *after_err_block = irb->current_basic_block;
+    ir_build_br(irb, err_scope, node, end_block, is_inline);
+
+    ir_set_cursor_at_end(irb, ok_block);
+    IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false);
+    IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
+    IrBasicBlock *after_ok_block = irb->current_basic_block;
+    ir_build_br(irb, parent_scope, node, end_block, is_inline);
+
+    ir_set_cursor_at_end(irb, end_block);
+    IrInstruction **incoming_values = allocate<IrInstruction *>(2);
+    incoming_values[0] = err_result;
+    incoming_values[1] = unwrapped_payload;
+    IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+    incoming_blocks[0] = after_err_block;
+    incoming_blocks[1] = after_ok_block;
+    return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
+}
+
 static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
         LValPurpose lval)
 {
@@ -3703,6 +3937,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
         case NodeTypeSliceExpr:
             return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval);
         case NodeTypeUnwrapErrorExpr:
+            return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval);
         case NodeTypeZeroesLiteral:
         case NodeTypeVarLiteral:
         case NodeTypeFnProto:
@@ -4279,11 +4514,20 @@ static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *ins
 }
 
 static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
-    if (value->static_value.special != ConstValSpecialStatic) {
-        ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
-        return nullptr;
+    switch (value->static_value.special) {
+        case ConstValSpecialStatic:
+            return &value->static_value;
+        case ConstValSpecialRuntime:
+            ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
+            return nullptr;
+        case ConstValSpecialUndef:
+            ir_add_error(ira, value, buf_sprintf("use of undefined value"));
+            return nullptr;
+        case ConstValSpecialZeroes:
+            ir_add_error(ira, value, buf_sprintf("zeroes is deprecated"));
+            return nullptr;
     }
-    return &value->static_value;
+    zig_unreachable();
 }
 
 IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
@@ -4375,6 +4619,95 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
     return const_val->data.x_fn;
 }
 
+static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
+    assert(wanted_type->id == TypeTableEntryIdMaybe);
+
+    if (instr_is_comptime(value)) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->invalid_instruction;
+
+        IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(ira->new_irb.exec,
+                source_instr->scope, source_instr->source_node);
+        const_instruction->base.type_entry = wanted_type;
+        const_instruction->base.static_value.special = ConstValSpecialStatic;
+        const_instruction->base.static_value.depends_on_compile_var = val->depends_on_compile_var;
+        const_instruction->base.static_value.data.x_maybe = &value->static_value;
+        return &const_instruction->base;
+    }
+
+    IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
+    result->type_entry = wanted_type;
+    result->static_value.data.rh_maybe = RuntimeHintMaybeNonNull;
+    ir_add_alloca(ira, result, wanted_type);
+    return result;
+}
+
+static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
+    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+    if (instr_is_comptime(value)) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->invalid_instruction;
+
+        IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(ira->new_irb.exec,
+                source_instr->scope, source_instr->source_node);
+        const_instruction->base.type_entry = wanted_type;
+        const_instruction->base.static_value.special = ConstValSpecialStatic;
+        const_instruction->base.static_value.depends_on_compile_var = val->depends_on_compile_var;
+        const_instruction->base.static_value.data.x_err_union.err = nullptr;
+        const_instruction->base.static_value.data.x_err_union.payload = val;
+        return &const_instruction->base;
+    }
+
+    IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
+    result->type_entry = wanted_type;
+    result->static_value.data.rh_error_union = RuntimeHintErrorUnionNonError;
+    ir_add_alloca(ira, result, wanted_type);
+    return result;
+}
+
+static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
+    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+    if (instr_is_comptime(value)) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->invalid_instruction;
+
+        IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(ira->new_irb.exec,
+                source_instr->scope, source_instr->source_node);
+        const_instruction->base.type_entry = wanted_type;
+        const_instruction->base.static_value.special = ConstValSpecialStatic;
+        const_instruction->base.static_value.depends_on_compile_var = val->depends_on_compile_var;
+        const_instruction->base.static_value.data.x_err_union.err = val->data.x_pure_err;
+        const_instruction->base.static_value.data.x_err_union.payload = nullptr;
+        return &const_instruction->base;
+    }
+
+    IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
+    result->type_entry = wanted_type;
+    result->static_value.data.rh_error_union = RuntimeHintErrorUnionError;
+    ir_add_alloca(ira, result, wanted_type);
+    return result;
+}
+
+static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
+    assert(wanted_type->id == TypeTableEntryIdMaybe);
+    assert(instr_is_comptime(value));
+
+    ConstExprValue *val = ir_resolve_const(ira, value);
+    assert(val);
+
+    IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(ira->new_irb.exec, source_instr->scope, source_instr->source_node);
+    const_instruction->base.type_entry = wanted_type;
+    const_instruction->base.static_value.special = ConstValSpecialStatic;
+    const_instruction->base.static_value.depends_on_compile_var = val->depends_on_compile_var;
+    const_instruction->base.static_value.data.x_maybe = nullptr;
+    return &const_instruction->base;
+}
+
 static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
     TypeTableEntry *wanted_type, IrInstruction *value)
 {
@@ -4503,18 +4836,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     // explicit cast from child type of maybe type to maybe type
     if (wanted_type->id == TypeTableEntryIdMaybe) {
         if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
-            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                    CastOpMaybeWrap, true);
-            cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
-            return cast_instruction;
+            return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
         } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
                    actual_type->id == TypeTableEntryIdNumLitFloat)
         {
             if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type)) {
-                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                        CastOpMaybeWrap, true);
-                cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull;
-                return cast_instruction;
+                return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
             } else {
                 return ira->codegen->invalid_instruction;
             }
@@ -4525,27 +4852,18 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if (wanted_type->id == TypeTableEntryIdMaybe &&
         actual_type->id == TypeTableEntryIdNullLit)
     {
-        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                CastOpNullToMaybe, true);
-        cast_instruction->return_knowledge = ReturnKnowledgeKnownNull;
-        return cast_instruction;
+        return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
     }
 
     // explicit cast from child type of error type to error type
     if (wanted_type->id == TypeTableEntryIdErrorUnion) {
         if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
-            IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                    CastOpErrorWrap, true);
-            cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
-            return cast_instruction;
+            return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
         } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
                    actual_type->id == TypeTableEntryIdNumLitFloat)
         {
             if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type)) {
-                IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                        CastOpErrorWrap, true);
-                cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError;
-                return cast_instruction;
+                return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
             } else {
                 return ira->codegen->invalid_instruction;
             }
@@ -4556,10 +4874,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     if (wanted_type->id == TypeTableEntryIdErrorUnion &&
         actual_type->id == TypeTableEntryIdPureError)
     {
-        IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type,
-                CastOpPureErrorWrap, false);
-        cast_instruction->return_knowledge = ReturnKnowledgeKnownError;
-        return cast_instruction;
+        return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
     }
 
     // explicit cast from number literal to another type
@@ -5997,33 +6312,6 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
     zig_unreachable();
 }
 
-static TypeTableEntry *ir_analyze_unwrap_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
-    IrInstruction *value = un_op_instruction->value->other;
-    TypeTableEntry *type_entry = value->type_entry;
-    if (type_entry->id == TypeTableEntryIdInvalid) {
-        return type_entry;
-    } else if (type_entry->id == TypeTableEntryIdMaybe) {
-        if (value->static_value.special != ConstValSpecialRuntime) {
-            bool depends_on_compile_var = value->static_value.depends_on_compile_var;
-            ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, depends_on_compile_var);
-            ConstExprValue *child_val = value->static_value.data.x_maybe;
-            if (!child_val) {
-                ir_add_error(ira, &un_op_instruction->base,
-                        buf_sprintf("unable to unwrap null"));
-                return ira->codegen->builtin_types.entry_invalid;
-            }
-            *out_val = *child_val;
-            return type_entry->data.maybe.child_type;
-        }
-        ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpUnwrapMaybe, value);
-        return type_entry->data.maybe.child_type;
-    } else {
-        add_node_error(ira->codegen, un_op_instruction->base.source_node,
-            buf_sprintf("expected maybe type, found '%s'", buf_ptr(&type_entry->name)));
-        return ira->codegen->builtin_types.entry_invalid;
-    }
-}
-
 static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
     IrInstruction *value = un_op_instruction->value->other;
     TypeTableEntry *expr_type = value->type_entry;
@@ -6099,27 +6387,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
             return ir_analyze_maybe(ira, un_op_instruction);
         case IrUnOpError:
             return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
-        case IrUnOpUnwrapError:
-            zig_panic("TODO analyze PrefixOpUnwrapError");
-            //{
-            //    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
-            //    if (type_entry->id == TypeTableEntryIdInvalid) {
-            //        return type_entry;
-            //    } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
-            //        return type_entry->data.error.child_type;
-            //    } else {
-            //        add_node_error(g, *expr_node,
-            //            buf_sprintf("expected error type, found '%s'", buf_ptr(&type_entry->name)));
-            //        return g->builtin_types.entry_invalid;
-            //    }
-            //}
-        case IrUnOpUnwrapMaybe:
-            return ir_analyze_unwrap_maybe(ira, un_op_instruction);
-        case IrUnOpErrorReturn:
-            zig_panic("TODO analyze IrUnOpErrorReturn");
-        case IrUnOpMaybeReturn:
-            zig_panic("TODO analyze IrUnOpMaybeReturn");
     }
     zig_unreachable();
 }
@@ -6835,6 +7102,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
     if (type_entry->id == TypeTableEntryIdInvalid)
         return type_entry;
 
+    // TODO handle typedefs
     if (type_entry->id != TypeTableEntryIdPointer) {
         add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node,
                 buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name)));
@@ -7243,6 +7511,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
     assert(ptr_type->id == TypeTableEntryIdPointer);
 
     TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
+    // TODO handle typedef
     if (type_entry->id == TypeTableEntryIdInvalid) {
         return ira->codegen->builtin_types.entry_invalid;
     } else if (type_entry->id != TypeTableEntryIdMaybe) {
@@ -7253,9 +7522,12 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
     TypeTableEntry *child_type = type_entry->data.maybe.child_type;
     TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false);
 
-    if (value->static_value.special != ConstValSpecialRuntime) {
-        ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr;
-        assert(value->static_value.data.x_ptr.index == SIZE_MAX);
+    if (instr_is_comptime(value)) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->builtin_types.entry_invalid;
+        ConstExprValue *maybe_val = val->data.x_ptr.base_ptr;
+        assert(val->data.x_ptr.index == SIZE_MAX);
 
         if (maybe_val->special != ConstValSpecialRuntime) {
             if (!maybe_val->data.x_maybe) {
@@ -8837,6 +9109,161 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst
     return ira->codegen->builtin_types.entry_bool;
 }
 
+static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) {
+    IrInstruction *value = instruction->value->other;
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    TypeTableEntry *ptr_type = value->type_entry;
+
+    // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
+    assert(ptr_type->id == TypeTableEntryIdPointer);
+
+    TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
+    TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
+    if (canon_type->id == TypeTableEntryIdInvalid) {
+        return ira->codegen->builtin_types.entry_invalid;
+    } else if (canon_type->id == TypeTableEntryIdErrorUnion) {
+        if (instr_is_comptime(value)) {
+            ConstExprValue *ptr_val = ir_resolve_const(ira, value);
+            if (!ptr_val)
+                return ira->codegen->builtin_types.entry_invalid;
+            ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
+            assert(ptr_val->data.x_ptr.index == SIZE_MAX);
+
+            if (err_union_val->special != ConstValSpecialRuntime) {
+                bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
+                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+                out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
+                return ira->codegen->builtin_types.entry_bool;
+            }
+        }
+
+        ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
+        return ira->codegen->builtin_types.entry_bool;
+    } else {
+        ir_add_error(ira, value,
+            buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
+        // TODO if this is a typedecl, add error note showing the declaration of the type decl
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
+static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
+    IrInstructionUnwrapErrCode *instruction)
+{
+    IrInstruction *value = instruction->value->other;
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+    TypeTableEntry *ptr_type = value->type_entry;
+
+    // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
+    assert(ptr_type->id == TypeTableEntryIdPointer);
+
+    TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
+    TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
+    if (canon_type->id == TypeTableEntryIdInvalid) {
+        return ira->codegen->builtin_types.entry_invalid;
+    } else if (canon_type->id == TypeTableEntryIdErrorUnion) {
+        if (instr_is_comptime(value)) {
+            ConstExprValue *ptr_val = ir_resolve_const(ira, value);
+            if (!ptr_val)
+                return ira->codegen->builtin_types.entry_invalid;
+            ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
+            assert(ptr_val->data.x_ptr.index == SIZE_MAX);
+            if (err_union_val->special != ConstValSpecialRuntime) {
+                ErrorTableEntry *err = err_union_val->data.x_err_union.err;
+                assert(err);
+
+                bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
+                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+                out_val->data.x_pure_err = err;
+                return ira->codegen->builtin_types.entry_pure_error;
+            }
+        }
+
+        ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value);
+        return ira->codegen->builtin_types.entry_pure_error;
+    } else {
+        ir_add_error(ira, value,
+            buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
+        // TODO if this is a typedecl, add error note showing the declaration of the type decl
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
+
+static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
+    IrInstructionUnwrapErrPayload *instruction)
+{
+    IrInstruction *value = instruction->value->other;
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+    TypeTableEntry *ptr_type = value->type_entry;
+
+    // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
+    assert(ptr_type->id == TypeTableEntryIdPointer);
+
+    TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
+    TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
+    if (canon_type->id == TypeTableEntryIdInvalid) {
+        return ira->codegen->builtin_types.entry_invalid;
+    } else if (canon_type->id == TypeTableEntryIdErrorUnion) {
+        TypeTableEntry *child_type = canon_type->data.error.child_type;
+        TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false);
+        if (instr_is_comptime(value)) {
+            ConstExprValue *ptr_val = ir_resolve_const(ira, value);
+            if (!ptr_val)
+                return ira->codegen->builtin_types.entry_invalid;
+            ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
+            assert(ptr_val->data.x_ptr.index == SIZE_MAX);
+            if (err_union_val->special != ConstValSpecialRuntime) {
+                ErrorTableEntry *err = err_union_val->data.x_err_union.err;
+                if (err != nullptr) {
+                    ir_add_error(ira, &instruction->base,
+                        buf_sprintf("unable to unwrap error '%s'", buf_ptr(&err->name)));
+                    return ira->codegen->builtin_types.entry_invalid;
+                }
+
+                bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
+                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+                out_val->data.x_ptr.base_ptr = err_union_val->data.x_err_union.payload;
+                out_val->data.x_ptr.index = SIZE_MAX;
+                return result_type;
+            }
+        }
+
+        ir_build_unwrap_err_payload_from(&ira->new_irb, &instruction->base, value, instruction->safety_check_on);
+        return result_type;
+    } else {
+        ir_add_error(ira, value,
+            buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
+        // TODO if this is a typedecl, add error note showing the declaration of the type decl
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+}
+
+static TypeTableEntry *ir_analyze_instruction_err_union_type_child(IrAnalyze *ira,
+    IrInstructionErrUnionTypeChild *instruction)
+{
+    IrInstruction *type_value = instruction->type_value->other;
+    TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
+    if (type_entry->id == TypeTableEntryIdInvalid)
+        return type_entry;
+
+    // TODO handle typedefs
+    if (type_entry->id != TypeTableEntryIdErrorUnion) {
+        add_node_error(ira->codegen, instruction->base.source_node,
+                buf_sprintf("expected error type, found '%s'", buf_ptr(&type_entry->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
+            type_value->static_value.depends_on_compile_var);
+    out_val->data.x_type = type_entry->data.error.child_type;
+    return ira->codegen->builtin_types.entry_type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -8971,6 +9398,17 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_alignof(ira, (IrInstructionAlignOf *)instruction);
         case IrInstructionIdOverflowOp:
             return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
+        case IrInstructionIdTestErr:
+            return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)instruction);
+        case IrInstructionIdUnwrapErrCode:
+            return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction);
+        case IrInstructionIdUnwrapErrPayload:
+            return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction);
+        case IrInstructionIdErrUnionTypeChild:
+            return ir_analyze_instruction_err_union_type_child(ira, (IrInstructionErrUnionTypeChild *)instruction);
+        case IrInstructionIdMaybeWrap:
+        case IrInstructionIdErrWrapCode:
+        case IrInstructionIdErrWrapPayload:
         case IrInstructionIdCast:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdEnumFieldPtr:
@@ -9122,6 +9560,13 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdAlignOf:
         case IrInstructionIdReturnAddress:
         case IrInstructionIdFrameAddress:
+        case IrInstructionIdTestErr:
+        case IrInstructionIdUnwrapErrCode:
+        case IrInstructionIdUnwrapErrPayload:
+        case IrInstructionIdErrUnionTypeChild:
+        case IrInstructionIdMaybeWrap:
+        case IrInstructionIdErrWrapCode:
+        case IrInstructionIdErrWrapPayload:
             return false;
         case IrInstructionIdAsm:
             {
@@ -9131,268 +9576,3 @@ bool ir_has_side_effects(IrInstruction *instruction) {
     }
     zig_unreachable();
 }
-
-// TODO port over all this commented out code into new IR way of doing things
-
-//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    TypeTableEntry *expected_return_type = get_return_type(context);
-//
-//    switch (node->data.return_expr.kind) {
-//        case ReturnKindError:
-//            {
-//                TypeTableEntry *expected_err_type;
-//                if (expected_type) {
-//                    expected_err_type = get_error_type(g, expected_type);
-//                } else {
-//                    expected_err_type = nullptr;
-//                }
-//                TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_err_type,
-//                        node->data.return_expr.expr);
-//                if (resolved_type->id == TypeTableEntryIdInvalid) {
-//                    return resolved_type;
-//                } else if (resolved_type->id == TypeTableEntryIdErrorUnion) {
-//                    if (expected_return_type->id != TypeTableEntryIdErrorUnion &&
-//                        expected_return_type->id != TypeTableEntryIdPureError)
-//                    {
-//                        ErrorMsg *msg = add_node_error(g, node,
-//                            buf_sprintf("%%return statement in function with return type '%s'",
-//                                buf_ptr(&expected_return_type->name)));
-//                        AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
-//                        add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
-//                    }
-//
-//                    return resolved_type->data.error.child_type;
-//                } else {
-//                    add_node_error(g, node->data.return_expr.expr,
-//                        buf_sprintf("expected error type, found '%s'", buf_ptr(&resolved_type->name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//            }
-//    }
-//    zig_unreachable();
-//}
-//static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node)
-//{
-//    AstNode *op1 = node->data.unwrap_err_expr.op1;
-//    AstNode *op2 = node->data.unwrap_err_expr.op2;
-//    AstNode *var_node = node->data.unwrap_err_expr.symbol;
-//
-//    TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1);
-//    if (lhs_type->id == TypeTableEntryIdInvalid) {
-//        return lhs_type;
-//    } else if (lhs_type->id == TypeTableEntryIdErrorUnion) {
-//        TypeTableEntry *child_type = lhs_type->data.error.child_type;
-//        BlockContext *child_context;
-//        if (var_node) {
-//            child_context = new_block_context(node, parent_context);
-//            var_node->block_context = child_context;
-//            Buf *var_name = var_node->data.symbol_expr.symbol;
-//            node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name,
-//                    g->builtin_types.entry_pure_error, true, nullptr);
-//        } else {
-//            child_context = parent_context;
-//        }
-//
-//        analyze_expression(g, import, child_context, child_type, op2);
-//        return child_type;
-//    } else {
-//        add_node_error(g, op1,
-//            buf_sprintf("expected error type, found '%s'", buf_ptr(&lhs_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//}
-//
-//static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
-//    if (type_entry->id == TypeTableEntryIdMetaType) {
-//        add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type"));
-//    } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
-//        add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value"));
-//    }
-//}
-//
-//static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type,
-//        AstNode *arg_node)
-//{
-//    assert(node->type == NodeTypeFieldAccessExpr);
-//
-//    uint64_t value = node->data.field_access_expr.type_enum_field->value;
-//    LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref;
-//    LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false);
-//
-//    if (enum_type->data.enumeration.gen_field_count == 0) {
-//        return tag_value;
-//    } else {
-//        TypeTableEntry *arg_node_type = nullptr;
-//        LLVMValueRef new_union_val = gen_expr(g, arg_node);
-//        if (arg_node) {
-//            arg_node_type = get_expr_type(arg_node);
-//        } else {
-//            arg_node_type = g->builtin_types.entry_void;
-//        }
-//
-//        LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr;
-//
-//        // populate the new tag value
-//        LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
-//        LLVMBuildStore(g->builder, tag_value, tag_field_ptr);
-//
-//        if (arg_node_type->id != TypeTableEntryIdVoid) {
-//            // populate the union value
-//            TypeTableEntry *union_val_type = get_expr_type(arg_node);
-//            LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
-//            LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
-//                    LLVMPointerType(union_val_type->type_ref, 0), "");
-//
-//            gen_assign_raw(g, arg_node, BinOpTypeAssign, bitcasted_union_field_ptr, new_union_val,
-//                    union_val_type, union_val_type);
-//
-//        }
-//
-//        return tmp_struct_ptr;
-//    }
-//}
-//
-//static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeUnwrapErrorExpr);
-//
-//    AstNode *op1 = node->data.unwrap_err_expr.op1;
-//    AstNode *op2 = node->data.unwrap_err_expr.op2;
-//    VariableTableEntry *var = node->data.unwrap_err_expr.var;
-//
-//    LLVMValueRef expr_val = gen_expr(g, op1);
-//    TypeTableEntry *expr_type = get_expr_type(op1);
-//    TypeTableEntry *op2_type = get_expr_type(op2);
-//    assert(expr_type->id == TypeTableEntryIdErrorUnion);
-//    TypeTableEntry *child_type = expr_type->data.error.child_type;
-//    LLVMValueRef err_val;
-//    if (handle_is_ptr(expr_type)) {
-//        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
-//        err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
-//    } else {
-//        err_val = expr_val;
-//    }
-//    LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
-//    LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
-//
-//    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
-//    LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
-//    LLVMBasicBlockRef end_block;
-//    bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable;
-//    bool have_end_block = err_reachable && type_has_bits(child_type);
-//    if (have_end_block) {
-//        end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd");
-//    }
-//
-//    LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
-//
-//    LLVMPositionBuilderAtEnd(g->builder, err_block);
-//    if (var) {
-//        LLVMBuildStore(g->builder, err_val, var->value_ref);
-//    }
-//    LLVMValueRef err_result = gen_expr(g, op2);
-//    if (have_end_block) {
-//        LLVMBuildBr(g->builder, end_block);
-//    } else if (err_reachable) {
-//        LLVMBuildBr(g->builder, ok_block);
-//    }
-//
-//    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-//    if (!type_has_bits(child_type)) {
-//        return nullptr;
-//    }
-//    LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
-//    LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type);
-//
-//    if (!have_end_block) {
-//        return child_val;
-//    }
-//
-//    LLVMBuildBr(g->builder, end_block);
-//
-//    LLVMPositionBuilderAtEnd(g->builder, end_block);
-//    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), "");
-//    LLVMValueRef incoming_values[2] = {child_val, err_result};
-//    LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block};
-//    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-//    return phi;
-//}
-//
-//static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeReturnExpr);
-//    AstNode *param_node = node->data.return_expr.expr;
-//    assert(param_node);
-//    LLVMValueRef value = gen_expr(g, param_node);
-//    TypeTableEntry *value_type = get_expr_type(param_node);
-//
-//    switch (node->data.return_expr.kind) {
-//        case ReturnKindUnconditional:
-//            {
-//                Expr *expr = get_resolved_expr(param_node);
-//                if (expr->const_val.ok) {
-//                    if (value_type->id == TypeTableEntryIdErrorUnion) {
-//                        if (expr->const_val.data.x_err.err) {
-//                            expr->return_knowledge = ReturnKnowledgeKnownError;
-//                        } else {
-//                            expr->return_knowledge = ReturnKnowledgeKnownNonError;
-//                        }
-//                    } else if (value_type->id == TypeTableEntryIdMaybe) {
-//                        if (expr->const_val.data.x_maybe) {
-//                            expr->return_knowledge = ReturnKnowledgeKnownNonNull;
-//                        } else {
-//                            expr->return_knowledge = ReturnKnowledgeKnownNull;
-//                        }
-//                    }
-//                }
-//                return gen_return(g, node, value, expr->return_knowledge);
-//            }
-//        case ReturnKindError:
-//            {
-//                assert(value_type->id == TypeTableEntryIdErrorUnion);
-//                TypeTableEntry *child_type = value_type->data.error.child_type;
-//
-//                LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn");
-//                LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue");
-//
-//                LLVMValueRef err_val;
-//                if (type_has_bits(child_type)) {
-//                    LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
-//                    err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
-//                } else {
-//                    err_val = value;
-//                }
-//                LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
-//                LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
-//                LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
-//
-//                LLVMPositionBuilderAtEnd(g->builder, return_block);
-//                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
-//                if (return_type->id == TypeTableEntryIdPureError) {
-//                    gen_return(g, node, err_val, ReturnKnowledgeKnownError);
-//                } else if (return_type->id == TypeTableEntryIdErrorUnion) {
-//                    if (type_has_bits(return_type->data.error.child_type)) {
-//                        assert(g->cur_ret_ptr);
-//
-//                        LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
-//                        LLVMBuildStore(g->builder, err_val, tag_ptr);
-//                        LLVMBuildRetVoid(g->builder);
-//                    } else {
-//                        gen_return(g, node, err_val, ReturnKnowledgeKnownError);
-//                    }
-//                } else {
-//                    zig_unreachable();
-//                }
-//
-//                LLVMPositionBuilderAtEnd(g->builder, continue_block);
-//                if (type_has_bits(child_type)) {
-//                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
-//                    return get_handle_value(g, val_ptr, child_type);
-//                } else {
-//                    return nullptr;
-//                }
-//            }
-//    }
-//    zig_unreachable();
-//}
src/ir_print.cpp
@@ -295,14 +295,6 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
             return "?";
         case IrUnOpError:
             return "%";
-        case IrUnOpUnwrapError:
-            return "%%";
-        case IrUnOpUnwrapMaybe:
-            return "??";
-        case IrUnOpMaybeReturn:
-            return "?return";
-        case IrUnOpErrorReturn:
-            return "%return";
     }
     zig_unreachable();
 }
@@ -855,6 +847,51 @@ static void ir_print_overflow_op(IrPrint *irp, IrInstructionOverflowOp *instruct
     fprintf(irp->f, ")");
 }
 
+static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) {
+    fprintf(irp->f, "@testError(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_err_union_type_child(IrPrint *irp, IrInstructionErrUnionTypeChild *instruction) {
+    fprintf(irp->f, "@errorUnionTypeChild(");
+    ir_print_other_instruction(irp, instruction->type_value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) {
+    fprintf(irp->f, "@unwrapErrorCode(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) {
+    fprintf(irp->f, "@unwrapErrorPayload(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+    if (!instruction->safety_check_on) {
+        fprintf(irp->f, " // no safety");
+    }
+}
+
+static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) {
+    fprintf(irp->f, "@maybeWrap(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_err_wrap_code(IrPrint *irp, IrInstructionErrWrapCode *instruction) {
+    fprintf(irp->f, "@errWrapCode(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_err_wrap_payload(IrPrint *irp, IrInstructionErrWrapPayload *instruction) {
+    fprintf(irp->f, "@errWrapPayload(");
+    ir_print_other_instruction(irp, instruction->value);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -1067,6 +1104,27 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdOverflowOp:
             ir_print_overflow_op(irp, (IrInstructionOverflowOp *)instruction);
             break;
+        case IrInstructionIdTestErr:
+            ir_print_test_err(irp, (IrInstructionTestErr *)instruction);
+            break;
+        case IrInstructionIdUnwrapErrCode:
+            ir_print_unwrap_err_code(irp, (IrInstructionUnwrapErrCode *)instruction);
+            break;
+        case IrInstructionIdUnwrapErrPayload:
+            ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction);
+            break;
+        case IrInstructionIdErrUnionTypeChild:
+            ir_print_err_union_type_child(irp, (IrInstructionErrUnionTypeChild *)instruction);
+            break;
+        case IrInstructionIdMaybeWrap:
+            ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction);
+            break;
+        case IrInstructionIdErrWrapCode:
+            ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction);
+            break;
+        case IrInstructionIdErrWrapPayload:
+            ir_print_err_wrap_payload(irp, (IrInstructionErrWrapPayload *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/cases/err_wrapping.zig
@@ -0,0 +1,13 @@
+pub fn foo() -> %i32 {
+    const x = %return bar();
+    return x + 1
+}
+
+pub fn bar() -> %i32 {
+    return 13;
+}
+
+pub fn baz() -> %i32 {
+    const y = foo() %% 1234;
+    return y + 1;
+}
test/self_hosted2.zig
@@ -1,4 +1,5 @@
 const case_namespace_fn_call = @import("cases/namespace_fn_call.zig");
+const case_err_wrapping = @import("cases/err_wrapping.zig");
 
 pub const SYS_write = 1;
 pub const SYS_exit = 60;
@@ -354,6 +355,9 @@ fn assignToIfVarPtr() {
     assert(??maybe_bool == false);
 }
 
+fn errorWrapping() {
+    assert(%%case_err_wrapping.baz() == 15);
+}
 
 fn assert(ok: bool) {
     if (!ok)
@@ -390,6 +394,7 @@ fn runAllTests() {
     overflowIntrinsics();
     shlWithOverflow();
     assignToIfVarPtr();
+    errorWrapping();
 }
 
 export nakedcc fn _start() -> unreachable {