authorgravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2024-02-04 23:04:43+01:00
committergravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2024-02-08 23:49:03+01:00
logdbcd53def0c82e7b1197194f48be534930f9c34b
tree29f82b45f0e0713c2ee0fea8101c8a38ee465c59
parent919a3bae1c5f2024b09e127a15c752d9dc0aa9a6

Preserve field alignment in union pointer captures


3 files changed, 113 insertions(+), 32 deletions(-)

src/Sema.zig+47-31
......@@ -10992,51 +10992,67 @@ const SwitchProngAnalysis = struct {
1099210992
1099310993 // By-reference captures have some further restrictions which make them easier to emit
1099410994 if (capture_byref) {
10995 const first_field_alignment = union_obj.fieldAlign(ip, first_field_index);
10996 const same_alignment = for (field_indices[1..]) |field_idx| {
10997 const field_alignment = union_obj.fieldAlign(ip, field_idx);
10998 if (field_alignment != first_field_alignment) break false;
10999 } else true;
1099511000 const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
10996 const capture_ptr_ty = try sema.ptrType(.{
10997 .child = capture_ty.toIntern(),
10998 .flags = .{
10999 // TODO: alignment!
11000 .is_const = operand_ptr_info.flags.is_const,
11001 .is_volatile = operand_ptr_info.flags.is_volatile,
11002 .address_space = operand_ptr_info.flags.address_space,
11003 },
11004 });
11005
11006 // By-ref captures of hetereogeneous types are only allowed if each field
11007 // pointer type is in-memory coercible to the capture pointer type.
11008 if (!same_types) {
11009 for (field_indices, 0..) |field_idx, i| {
11001 const capture_ptr_ty = if (same_types and same_alignment) same: {
11002 break :same try sema.ptrType(.{
11003 .child = capture_ty.toIntern(),
11004 .flags = .{
11005 .is_const = operand_ptr_info.flags.is_const,
11006 .is_volatile = operand_ptr_info.flags.is_volatile,
11007 .address_space = operand_ptr_info.flags.address_space,
11008 .alignment = first_field_alignment,
11009 },
11010 });
11011 } else resolve: {
11012 // By-ref captures of hetereogeneous types are only allowed if all field
11013 // pointer types are peer resolvable to each other.
11014 // We need values to run PTR on, so make a bunch of undef constants.
11015 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
11016 for (field_indices, dummy_captures) |field_idx, *dummy| {
1101011017 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
1101111018 const field_ptr_ty = try sema.ptrType(.{
1101211019 .child = field_ty.toIntern(),
1101311020 .flags = .{
11014 // TODO: alignment!
1101511021 .is_const = operand_ptr_info.flags.is_const,
1101611022 .is_volatile = operand_ptr_info.flags.is_volatile,
1101711023 .address_space = operand_ptr_info.flags.address_space,
11024 .alignment = union_obj.fieldAlign(ip, field_idx),
1101811025 },
1101911026 });
11020 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
11027 dummy.* = try mod.undefRef(field_ptr_ty);
11028 }
11029 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
11030 @memset(case_srcs, .unneeded);
11031
11032 break :resolve sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
11033 error.NeededSourceLocation => {
11034 // This must be a multi-prong so this must be a `multi_capture` src
1102111035 const multi_idx = raw_capture_src.multi_capture;
1102211036 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
11037 for (case_srcs, 0..) |*case_src, i| {
11038 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
11039 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
11040 }
1102311041 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
11024 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
11025 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
11026 const msg = msg: {
11027 const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
11028 errdefer msg.destroy(sema.gpa);
11029 try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{
11030 field_ty.fmt(sema.mod),
11031 capture_ty.fmt(sema.mod),
11032 });
11033 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
11034 break :msg msg;
11042 _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
11043 error.AnalysisFail => {
11044 const msg = sema.err orelse return error.AnalysisFail;
11045 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
11046 try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
11047 return error.AnalysisFail;
11048 },
11049 else => |e| return e,
1103511050 };
11036 return sema.failWithOwnedErrorMsg(block, msg);
11037 }
11038 }
11039 }
11051 unreachable;
11052 },
11053 else => |e| return e,
11054 };
11055 };
1104011056
1104111057 if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
1104211058 if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty);
test/behavior/switch.zig+63
......@@ -574,6 +574,69 @@ test "switch prongs with cases with identical payload types" {
574574 try comptime S.doTheTest();
575575}
576576
577test "switch prong pointer capture alignment" {
578 const U = union(enum) {
579 a: u8 align(8),
580 b: u8 align(4),
581 c: u8,
582 };
583
584 const S = struct {
585 fn doTheTest() !void {
586 const u = U{ .a = 1 };
587 switch (u) {
588 .a => |*a| try expectEqual(*align(8) const u8, @TypeOf(a)),
589 .b, .c => |*p| {
590 _ = p;
591 @panic("fail");
592 },
593 }
594
595 switch (u) {
596 .a, .b => |*p| try expectEqual(*align(4) const u8, @TypeOf(p)),
597 .c => |*p| {
598 _ = p;
599 @panic("fail");
600 },
601 }
602
603 switch (u) {
604 .a, .c => |*p| try expectEqual(*const u8, @TypeOf(p)),
605 .b => |*p| {
606 _ = p;
607 @panic("fail");
608 },
609 }
610 }
611
612 fn doTheTest2() !void {
613 const un1 = U{ .b = 1 };
614 switch (un1) {
615 .b => |*a| try expectEqual(*align(4) const u8, @TypeOf(a)),
616 .a, .c => |*p| {
617 _ = p;
618 @panic("fail");
619 },
620 }
621
622 const un2 = U{ .c = 1 };
623 switch (un2) {
624 .c => |*a| try expectEqual(*const u8, @TypeOf(a)),
625 .a, .b => |*p| {
626 _ = p;
627 @panic("fail");
628 },
629 }
630 }
631 };
632
633 try S.doTheTest();
634 try comptime S.doTheTest();
635
636 try S.doTheTest2();
637 try comptime S.doTheTest2();
638}
639
577640test "switch on pointer type" {
578641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
579642 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/cases/compile_errors/switch_capture_incompatible_types.zig+3-1
......@@ -23,5 +23,7 @@ export fn g() void {
2323// :5:10: note: type 'u32' here
2424// :5:14: note: type '*u8' here
2525// :13:20: error: capture group with incompatible types
26// :13:14: note: pointer type child 'u32' cannot cast into resolved pointer type child 'u64'
26// :13:20: note: incompatible types: '*u64' and '*u32'
27// :13:10: note: type '*u64' here
28// :13:14: note: type '*u32' here
2729// :13:20: note: this coercion is only possible when capturing by value