authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-10-28 01:22:30+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-10-28 06:30:28+01:00
logc1c9bc0c41eb8956b2fe0b21e2443c6504e73234
treeaa7e221d54438cb290c902f272bb4fc6d660478f
parent5257643d3ddd35b0fb40b82988a9ccf9f859a779

Sema: do not assume switch item indices align with union field indices

Resolves: #17754

2 files changed, 47 insertions(+), 16 deletions(-)

src/Sema.zig+24-16
...@@ -10789,23 +10789,24 @@ const SwitchProngAnalysis = struct {...@@ -10789,23 +10789,24 @@ const SwitchProngAnalysis = struct {
10789 const first_field_index: u32 = mod.unionTagFieldIndex(union_obj, first_item_val).?;10789 const first_field_index: u32 = mod.unionTagFieldIndex(union_obj, first_item_val).?;
10790 const first_field_ty = union_obj.field_types.get(ip)[first_field_index].toType();10790 const first_field_ty = union_obj.field_types.get(ip)[first_field_index].toType();
1079110791
10792 const field_tys = try sema.arena.alloc(Type, case_vals.len);10792 const field_indices = try sema.arena.alloc(u32, case_vals.len);
10793 for (case_vals, field_tys) |item, *field_ty| {10793 for (case_vals, field_indices) |item, *field_idx| {
10794 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;10794 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable;
10795 const field_idx = mod.unionTagFieldIndex(union_obj, item_val).?;10795 field_idx.* = mod.unionTagFieldIndex(union_obj, item_val).?;
10796 field_ty.* = union_obj.field_types.get(ip)[field_idx].toType();
10797 }10796 }
1079810797
10799 // Fast path: if all the operands are the same type already, we don't need to hit10798 // Fast path: if all the operands are the same type already, we don't need to hit
10800 // PTR! This will also allow us to emit simpler code.10799 // PTR! This will also allow us to emit simpler code.
10801 const same_types = for (field_tys[1..]) |field_ty| {10800 const same_types = for (field_indices[1..]) |field_idx| {
10802 if (!field_ty.eql(field_tys[0], sema.mod)) break false;10801 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10802 if (!field_ty.eql(first_field_ty, sema.mod)) break false;
10803 } else true;10803 } else true;
1080410804
10805 const capture_ty = if (same_types) field_tys[0] else capture_ty: {10805 const capture_ty = if (same_types) first_field_ty else capture_ty: {
10806 // We need values to run PTR on, so make a bunch of undef constants.10806 // We need values to run PTR on, so make a bunch of undef constants.
10807 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);10807 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10808 for (dummy_captures, field_tys) |*dummy, field_ty| {10808 for (dummy_captures, field_indices) |*dummy, field_idx| {
10809 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10809 dummy.* = try mod.undefRef(field_ty);10810 dummy.* = try mod.undefRef(field_ty);
10810 }10811 }
1081110812
...@@ -10852,7 +10853,8 @@ const SwitchProngAnalysis = struct {...@@ -10852,7 +10853,8 @@ const SwitchProngAnalysis = struct {
10852 // By-ref captures of hetereogeneous types are only allowed if each field10853 // By-ref captures of hetereogeneous types are only allowed if each field
10853 // pointer type is in-memory coercible to the capture pointer type.10854 // pointer type is in-memory coercible to the capture pointer type.
10854 if (!same_types) {10855 if (!same_types) {
10855 for (field_tys, 0..) |field_ty, i| {10856 for (field_indices, 0..) |field_idx, i| {
10857 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10856 const field_ptr_ty = try sema.ptrType(.{10858 const field_ptr_ty = try sema.ptrType(.{
10857 .child = field_ty.toIntern(),10859 .child = field_ty.toIntern(),
10858 .flags = .{10860 .flags = .{
...@@ -10915,7 +10917,8 @@ const SwitchProngAnalysis = struct {...@@ -10915,7 +10917,8 @@ const SwitchProngAnalysis = struct {
10915 // We may have to emit a switch block which coerces the operand to the capture type.10917 // We may have to emit a switch block which coerces the operand to the capture type.
10916 // If we can, try to avoid that using in-memory coercions.10918 // If we can, try to avoid that using in-memory coercions.
10917 const first_non_imc = in_mem: {10919 const first_non_imc = in_mem: {
10918 for (field_tys, 0..) |field_ty, i| {10920 for (field_indices, 0..) |field_idx, i| {
10921 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10919 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {10922 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
10920 break :in_mem i;10923 break :in_mem i;
10921 }10924 }
...@@ -10933,11 +10936,12 @@ const SwitchProngAnalysis = struct {...@@ -10933,11 +10936,12 @@ const SwitchProngAnalysis = struct {
10933 // be several, and we can squash all of these cases into the same switch prong using10936 // be several, and we can squash all of these cases into the same switch prong using
10934 // a simple bitcast. We'll make this the 'else' prong.10937 // a simple bitcast. We'll make this the 'else' prong.
1093510938
10936 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len);10939 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
10937 in_mem_coercible.unset(first_non_imc);10940 in_mem_coercible.unset(first_non_imc);
10938 {10941 {
10939 const next = first_non_imc + 1;10942 const next = first_non_imc + 1;
10940 for (field_tys[next..], next..) |field_ty, i| {10943 for (field_indices[next..], next..) |field_idx, i| {
10944 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10941 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {10945 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
10942 in_mem_coercible.unset(i);10946 in_mem_coercible.unset(i);
10943 }10947 }
...@@ -10954,7 +10958,7 @@ const SwitchProngAnalysis = struct {...@@ -10954,7 +10958,7 @@ const SwitchProngAnalysis = struct {
10954 },10958 },
10955 });10959 });
1095610960
10957 const prong_count = field_tys.len - in_mem_coercible.count();10961 const prong_count = field_indices.len - in_mem_coercible.count();
1095810962
10959 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts10963 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts
10960 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);10964 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
...@@ -10967,7 +10971,9 @@ const SwitchProngAnalysis = struct {...@@ -10967,7 +10971,9 @@ const SwitchProngAnalysis = struct {
10967 var coerce_block = block.makeSubBlock();10971 var coerce_block = block.makeSubBlock();
10968 defer coerce_block.instructions.deinit(sema.gpa);10972 defer coerce_block.instructions.deinit(sema.gpa);
1096910973
10970 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(idx), field_tys[idx]);10974 const field_idx = field_indices[idx];
10975 const field_ty = union_obj.field_types.get(ip)[field_idx].toType();
10976 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, field_idx, field_ty);
10971 const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) {10977 const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) {
10972 error.NeededSourceLocation => {10978 error.NeededSourceLocation => {
10973 const multi_idx = raw_capture_src.multi_capture;10979 const multi_idx = raw_capture_src.multi_capture;
...@@ -10993,8 +10999,10 @@ const SwitchProngAnalysis = struct {...@@ -10993,8 +10999,10 @@ const SwitchProngAnalysis = struct {
10993 var coerce_block = block.makeSubBlock();10999 var coerce_block = block.makeSubBlock();
10994 defer coerce_block.instructions.deinit(sema.gpa);11000 defer coerce_block.instructions.deinit(sema.gpa);
1099511001
10996 const first_imc = in_mem_coercible.findFirstSet().?;11002 const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
10997 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(first_imc), field_tys[first_imc]);11003 const first_imc_field_idx = field_indices[first_imc_item_idx];
11004 const first_imc_field_ty = union_obj.field_types.get(ip)[first_imc_field_idx].toType();
11005 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, first_imc_field_idx, first_imc_field_ty);
10998 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);11006 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
10999 _ = try coerce_block.addBr(capture_block_inst, coerced);11007 _ = try coerce_block.addBr(capture_block_inst, coerced);
1100011008
test/behavior/switch.zig+23
...@@ -800,3 +800,26 @@ test "nested break ignores switch conditions and breaks instead" {...@@ -800,3 +800,26 @@ test "nested break ignores switch conditions and breaks instead" {
800 // Originally reported at https://github.com/ziglang/zig/issues/10196800 // Originally reported at https://github.com/ziglang/zig/issues/10196
801 try expect(0x01 == try S.register_to_address("a0"));801 try expect(0x01 == try S.register_to_address("a0"));
802}802}
803
804test "peer type resolution on switch captures ignores unused payload bits" {
805 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
806
807 const Foo = union(enum) {
808 a: u32,
809 b: u64,
810 };
811
812 var val: Foo = undefined;
813 @memset(std.mem.asBytes(&val), 0xFF);
814
815 // This is runtime-known so the following store isn't comptime-known.
816 var rt: u32 = 123;
817 val = .{ .a = rt }; // will not necessarily zero remaning payload memory
818
819 // Fields intentionally backwards here
820 const x = switch (val) {
821 .b, .a => |x| x,
822 };
823
824 try expect(x == 123);
825}