Commit fcd9f521d2

Pavel Verigo <paul.verigo@gmail.com>
2025-07-23 20:52:26
stage2-wasm: implement try_ptr + is_(non_)err_ptr
1 parent bc8e1a7
Changed files (2)
src
arch
test
behavior
src/arch/wasm/CodeGen.zig
@@ -1886,8 +1886,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .call_never_tail => cg.airCall(inst, .never_tail),
         .call_never_inline => cg.airCall(inst, .never_inline),
 
-        .is_err => cg.airIsErr(inst, .i32_ne),
-        .is_non_err => cg.airIsErr(inst, .i32_eq),
+        .is_err => cg.airIsErr(inst, .i32_ne, .value),
+        .is_non_err => cg.airIsErr(inst, .i32_eq, .value),
+        .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr),
+        .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr),
 
         .is_null => cg.airIsNull(inst, .i32_eq, .value),
         .is_non_null => cg.airIsNull(inst, .i32_ne, .value),
@@ -1970,8 +1972,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
 
         .assembly,
-        .is_err_ptr,
-        .is_non_err_ptr,
 
         .err_return_trace,
         .set_err_return_trace,
@@ -4105,7 +4105,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
     return cg.finishAir(inst, .none, &.{br.operand});
 }
 
-fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
+fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
     const zcu = cg.pt.zcu;
     const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
     const operand = try cg.resolveInst(un_op);
@@ -4122,7 +4122,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr
         }
 
         try cg.emitWValue(operand);
-        if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
+        if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
             try cg.addMemArg(.i32_load16_u, .{
                 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
                 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
@@ -6462,9 +6462,6 @@ fn lowerTry(
     operand_is_ptr: bool,
 ) InnerError!WValue {
     const zcu = cg.pt.zcu;
-    if (operand_is_ptr) {
-        return cg.fail("TODO: lowerTry for pointers", .{});
-    }
 
     const pl_ty = err_union_ty.errorUnionPayload(zcu);
     const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu);
@@ -6475,7 +6472,7 @@ fn lowerTry(
 
         // check if the error tag is set for the error union.
         try cg.emitWValue(err_union);
-        if (pl_has_bits) {
+        if (pl_has_bits or operand_is_ptr) {
             const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
             try cg.addMemArg(.i32_load16_u, .{
                 .offset = err_union.offset() + err_offset,
@@ -6497,12 +6494,12 @@ fn lowerTry(
     }
 
     // if we reach here it means error was not set, and we want the payload
-    if (!pl_has_bits) {
+    if (!pl_has_bits and !operand_is_ptr) {
         return .none;
     }
 
     const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
-    if (isByRef(pl_ty, zcu, cg.target)) {
+    if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
         return buildPointerOffset(cg, err_union, pl_offset, .new);
     }
     const payload = try cg.load(err_union, pl_ty, pl_offset);
test/behavior/try.zig
@@ -121,3 +121,82 @@ test "'return try' through conditional" {
         comptime std.debug.assert(result == 123);
     }
 }
+
+test "try ptr propagation const" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+
+    const S = struct {
+        fn foo0() !u32 {
+            return 0;
+        }
+
+        fn foo1() error{Bad}!u32 {
+            return 1;
+        }
+
+        fn foo2() anyerror!u32 {
+            return 2;
+        }
+
+        fn doTheTest() !void {
+            const res0: *const u32 = &(try foo0());
+            const res1: *const u32 = &(try foo1());
+            const res2: *const u32 = &(try foo2());
+            try expect(res0.* == 0);
+            try expect(res1.* == 1);
+            try expect(res2.* == 2);
+        }
+    };
+    try S.doTheTest();
+    try comptime S.doTheTest();
+}
+
+test "try ptr propagation mutate" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+
+    const S = struct {
+        fn foo0() !u32 {
+            return 0;
+        }
+
+        fn foo1() error{Bad}!u32 {
+            return 1;
+        }
+
+        fn foo2() anyerror!u32 {
+            return 2;
+        }
+
+        fn doTheTest() !void {
+            var f0 = foo0();
+            var f1 = foo1();
+            var f2 = foo2();
+
+            const res0: *u32 = &(try f0);
+            const res1: *u32 = &(try f1);
+            const res2: *u32 = &(try f2);
+
+            res0.* += 1;
+            res1.* += 1;
+            res2.* += 1;
+
+            try expect(f0 catch unreachable == 1);
+            try expect(f1 catch unreachable == 2);
+            try expect(f2 catch unreachable == 3);
+
+            try expect(res0.* == 1);
+            try expect(res1.* == 2);
+            try expect(res2.* == 3);
+        }
+    };
+    try S.doTheTest();
+    try comptime S.doTheTest();
+}