From 56fad80e8ce14a23f6642e73f545ed6c8b394929 Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Sat, 17 Oct 2026 01:21:20 +0200 Subject: [PATCH] wasm: fix switch on wide integers and ?error repr --- src/codegen/wasm/CodeGen.zig | 23 +++--- test/behavior/optional.zig | 33 ++++++++ test/behavior/switch.zig | 146 +++++++++++++++++++++++++++++------ 3 files changed, 169 insertions(+), 33 deletions(-) diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index c464928168910f7509ebd9bbc3da1544dcb52834..90da3698142b255453c955a95b1b2af91ca39a79 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -2599,17 +2599,11 @@ const IntType = struct { .one, .many, .c => .{ .is_signed = false, .bits = cg.target.ptrBitWidth() }, .slice => unreachable, }, - .opt_type => |opt_child| return if (!Type.fromInterned(opt_child).hasRuntimeBits(zcu)) - .{ .is_signed = false, .bits = 1 } - else switch (ip.indexToKey(opt_child)) { - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .one, .many => switch (ptr_type.flags.is_allowzero) { - false => .{ .is_signed = false, .bits = cg.target.ptrBitWidth() }, - true => unreachable, - }, - .slice, .c => unreachable, - }, - else => unreachable, + .opt_type => |opt_child| { + if (!Type.fromInterned(opt_child).hasRuntimeBits(zcu)) + return .{ .is_signed = false, .bits = 1 }; + assert(Type.fromInterned(ty_index).optionalReprIsPayload(zcu)); + ty_index = opt_child; }, .error_union_type => |error_union_type| return if (!Type.fromInterned(error_union_type.payload_type) .hasRuntimeBits(zcu)) .{ .is_signed = false, .bits = zcu.errorSetBits() } else unreachable, @@ -6039,6 +6033,13 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner if (use_br_table) { const width = width_maybe.?; + if (int_ty.bits > 32) { + _ = try cg.intCmp(int_ty, .lt, target, try cg.resolveValue(min.?)); + try cg.addLabel(.br_if, branch_count - 1); + _ = try cg.intCmp(int_ty, .gt, target, try cg.resolveValue(max.?)); + try cg.addLabel(.br_if, branch_count - 1); + } + const br_value_original = try cg.intSub(int_ty, target, try cg.resolveValue(min.?)); _ = try cg.intCast(.u32, int_ty, br_value_original); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 4a6695fd5ec46df9bb5abb5b86de558a4a136ab6..f8e36204603a2eb991c13eaa4fd3e4c8e0406018 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -58,6 +58,39 @@ fn testNullPtrsEql() !void { try expect(&number == x); } +test "optional error equality" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + + const S = struct { + fn testEql(comptime T: type, a: ?T, b: ?T, equal: bool) !void { + try expect((a == b) == equal); + try expect((a != b) == !equal); + } + + fn doTheTest() !void { + try testEql(error{}, null, null, true); + + try testEql(error{ A, B }, null, null, true); + try testEql(error{ A, B }, error.A, error.A, true); + try testEql(error{ A, B }, error.A, error.B, false); + try testEql(error{ A, B }, null, error.A, false); + try testEql(error{ A, B }, error.A, null, false); + + try testEql(anyerror, null, null, true); + try testEql(anyerror, error.A, error.A, true); + try testEql(anyerror, error.A, error.B, false); + try testEql(anyerror, null, error.A, false); + try testEql(anyerror, error.A, null, false); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); +} + test "optional with zero-bit type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 1533d6d5693c7b6e283eab6653d8e018ea0c2d33..151d08c116cc7e4b2f3c120a43f0de93bc357ae2 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -47,34 +47,136 @@ test "switch arbitrary int size" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO - try expect(testSwitchArbInt(u64, 0) == 0); - try expect(testSwitchArbInt(u64, 12) == 1); - try expect(testSwitchArbInt(u64, maxInt(u64)) == 2); - try expect(testSwitchArbInt(u64, 5555) == 3); + const S = struct { + fn testSwitchArbInt(comptime T: type, x: T) u32 { + return switch (x) { + minInt(T) => 0, + 10...15 => 1, + maxInt(T) => 2, + else => 3, + }; + } - try expect(testSwitchArbInt(i64, minInt(i64)) == 0); - try expect(testSwitchArbInt(i64, 12) == 1); - try expect(testSwitchArbInt(i64, maxInt(i64)) == 2); - try expect(testSwitchArbInt(i64, -1000) == 3); + fn doTheTest() !void { + try expect(testSwitchArbInt(u64, 0) == 0); + try expect(testSwitchArbInt(u64, 12) == 1); + try expect(testSwitchArbInt(u64, maxInt(u64)) == 2); + try expect(testSwitchArbInt(u64, 5555) == 3); - try expect(testSwitchArbInt(u128, 0) == 0); - try expect(testSwitchArbInt(u128, 12) == 1); - try expect(testSwitchArbInt(u128, maxInt(u128)) == 2); - try expect(testSwitchArbInt(u128, 5555) == 3); + try expect(testSwitchArbInt(i64, minInt(i64)) == 0); + try expect(testSwitchArbInt(i64, 12) == 1); + try expect(testSwitchArbInt(i64, maxInt(i64)) == 2); + try expect(testSwitchArbInt(i64, -1000) == 3); - try expect(testSwitchArbInt(i128, minInt(i128)) == 0); - try expect(testSwitchArbInt(i128, 12) == 1); - try expect(testSwitchArbInt(i128, maxInt(i128)) == 2); - try expect(testSwitchArbInt(i128, -1000) == 3); + try expect(testSwitchArbInt(u128, 0) == 0); + try expect(testSwitchArbInt(u128, 12) == 1); + try expect(testSwitchArbInt(u128, maxInt(u128)) == 2); + try expect(testSwitchArbInt(u128, 5555) == 3); + + try expect(testSwitchArbInt(i128, minInt(i128)) == 0); + try expect(testSwitchArbInt(i128, 12) == 1); + try expect(testSwitchArbInt(i128, maxInt(i128)) == 2); + try expect(testSwitchArbInt(i128, -1000) == 3); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); } -fn testSwitchArbInt(comptime T: type, x: T) u32 { - return switch (x) { - minInt(T) => 0, - 10...15 => 1, - maxInt(T) => 2, - else => 3, +test "dense switch" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S = struct { + fn unsigned(comptime T: type, x: T) u8 { + return switch (x) { + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 3, + else => 4, + }; + } + + fn signed(comptime T: type, x: T) u8 { + return switch (x) { + -2 => 0, + -1 => 1, + 0 => 2, + 1 => 3, + else => 4, + }; + } + + fn doTheTest() !void { + try expect(unsigned(u32, 0) == 0); + try expect(unsigned(u32, 1) == 1); + try expect(unsigned(u32, 2) == 2); + try expect(unsigned(u32, 3) == 3); + try expect(unsigned(u32, maxInt(u32)) == 4); + try expect(unsigned(u32, (1 << 16) + 1) == 4); + + try expect(signed(i32, -2) == 0); + try expect(signed(i32, -1) == 1); + try expect(signed(i32, 0) == 2); + try expect(signed(i32, 1) == 3); + try expect(signed(i32, minInt(i32)) == 4); + try expect(signed(i32, -(1 << 16) - 2) == 4); + try expect(signed(i32, (1 << 16) + 1) == 4); + + try expect(unsigned(u64, 0) == 0); + try expect(unsigned(u64, 1) == 1); + try expect(unsigned(u64, 2) == 2); + try expect(unsigned(u64, 3) == 3); + try expect(unsigned(u64, maxInt(u64)) == 4); + try expect(unsigned(u64, (1 << 32) + 1) == 4); + + try expect(signed(i64, -2) == 0); + try expect(signed(i64, -1) == 1); + try expect(signed(i64, 0) == 2); + try expect(signed(i64, 1) == 3); + try expect(signed(i64, minInt(i64)) == 4); + try expect(signed(i64, -(1 << 32) - 2) == 4); + try expect(signed(i64, (1 << 32) + 1) == 4); + + try expect(unsigned(u128, 0) == 0); + try expect(unsigned(u128, 1) == 1); + try expect(unsigned(u128, 2) == 2); + try expect(unsigned(u128, 3) == 3); + try expect(unsigned(u128, maxInt(u128)) == 4); + try expect(unsigned(u128, (1 << 64) + 1) == 4); + + try expect(signed(i128, -2) == 0); + try expect(signed(i128, -1) == 1); + try expect(signed(i128, 0) == 2); + try expect(signed(i128, 1) == 3); + try expect(signed(i128, minInt(i128)) == 4); + try expect(signed(i128, -(1 << 64) - 2) == 4); + try expect(signed(i128, (1 << 64) + 1) == 4); + + try expect(unsigned(u999, 0) == 0); + try expect(unsigned(u999, 1) == 1); + try expect(unsigned(u999, 2) == 2); + try expect(unsigned(u999, 3) == 3); + try expect(unsigned(u999, maxInt(u999)) == 4); + try expect(unsigned(u999, (1 << 499) + 1) == 4); + + try expect(signed(i999, -2) == 0); + try expect(signed(i999, -1) == 1); + try expect(signed(i999, 0) == 2); + try expect(signed(i999, 1) == 3); + try expect(signed(i999, minInt(i999)) == 4); + try expect(signed(i999, -(1 << 499) - 2) == 4); + try expect(signed(i999, (1 << 499) + 1) == 4); + } }; + + try S.doTheTest(); + try comptime S.doTheTest(); } test "implicit comptime switch" { -- 2.54.0