authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-26 16:52:39-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-26 16:52:39-08:00
log3e79c0f18ce05c302c6117e66275db4fcdf033e4
tree4ae226095dcc3b225301039da7e94be9922e518b
parent00ff123b1eb237f72f918ffd413c8d9c8936c46d
parent7cb227ab678b57dd10b65c1d9c89526a3e47cc2a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18859 from schmee/switch-union-capture-align

Sema: preserve field alignment in union pointer captures

3 files changed, 105 insertions(+), 39 deletions(-)

src/Sema.zig+32-31
...@@ -10924,50 +10924,51 @@ const SwitchProngAnalysis = struct {...@@ -10924,50 +10924,51 @@ const SwitchProngAnalysis = struct {
10924 // By-reference captures have some further restrictions which make them easier to emit10924 // By-reference captures have some further restrictions which make them easier to emit
10925 if (capture_byref) {10925 if (capture_byref) {
10926 const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);10926 const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
10927 const capture_ptr_ty = try sema.ptrType(.{10927 const capture_ptr_ty = resolve: {
10928 .child = capture_ty.toIntern(),10928 // By-ref captures of hetereogeneous types are only allowed if all field
10929 .flags = .{10929 // pointer types are peer resolvable to each other.
10930 // TODO: alignment!10930 // We need values to run PTR on, so make a bunch of undef constants.
10931 .is_const = operand_ptr_info.flags.is_const,10931 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10932 .is_volatile = operand_ptr_info.flags.is_volatile,10932 for (field_indices, dummy_captures) |field_idx, *dummy| {
10933 .address_space = operand_ptr_info.flags.address_space,
10934 },
10935 });
10936
10937 // By-ref captures of hetereogeneous types are only allowed if each field
10938 // pointer type is in-memory coercible to the capture pointer type.
10939 if (!same_types) {
10940 for (field_indices, 0..) |field_idx, i| {
10941 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);10933 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
10942 const field_ptr_ty = try sema.ptrType(.{10934 const field_ptr_ty = try sema.ptrType(.{
10943 .child = field_ty.toIntern(),10935 .child = field_ty.toIntern(),
10944 .flags = .{10936 .flags = .{
10945 // TODO: alignment!
10946 .is_const = operand_ptr_info.flags.is_const,10937 .is_const = operand_ptr_info.flags.is_const,
10947 .is_volatile = operand_ptr_info.flags.is_volatile,10938 .is_volatile = operand_ptr_info.flags.is_volatile,
10948 .address_space = operand_ptr_info.flags.address_space,10939 .address_space = operand_ptr_info.flags.address_space,
10940 .alignment = union_obj.fieldAlign(ip, field_idx),
10949 },10941 },
10950 });10942 });
10951 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {10943 dummy.* = try mod.undefRef(field_ptr_ty);
10944 }
10945 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
10946 @memset(case_srcs, .unneeded);
10947
10948 break :resolve sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
10949 error.NeededSourceLocation => {
10950 // This must be a multi-prong so this must be a `multi_capture` src
10952 const multi_idx = raw_capture_src.multi_capture;10951 const multi_idx = raw_capture_src.multi_capture;
10953 const src_decl_ptr = sema.mod.declPtr(block.src_decl);10952 const src_decl_ptr = sema.mod.declPtr(block.src_decl);
10953 for (case_srcs, 0..) |*case_src, i| {
10954 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
10955 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
10956 }
10954 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);10957 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
10955 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };10958 _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
10956 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);10959 error.AnalysisFail => {
10957 const msg = msg: {10960 const msg = sema.err orelse return error.AnalysisFail;
10958 const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});10961 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
10959 errdefer msg.destroy(sema.gpa);10962 try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
10960 try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{10963 return error.AnalysisFail;
10961 field_ty.fmt(sema.mod),10964 },
10962 capture_ty.fmt(sema.mod),10965 else => |e| return e,
10963 });
10964 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
10965 break :msg msg;
10966 };10966 };
10967 return sema.failWithOwnedErrorMsg(block, msg);10967 unreachable;
10968 }10968 },
10969 }10969 else => |e| return e,
10970 }10970 };
10971 };
1097110972
10972 if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {10973 if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
10973 if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty);10974 if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty);
test/behavior/switch.zig+70-7
...@@ -410,8 +410,8 @@ test "switch on integer with else capturing expr" {...@@ -410,8 +410,8 @@ test "switch on integer with else capturing expr" {
410 var x: i32 = 5;410 var x: i32 = 5;
411 _ = &x;411 _ = &x;
412 switch (x + 10) {412 switch (x + 10) {
413 14 => @panic("fail"),413 14 => return error.TestFailed,
414 16 => @panic("fail"),414 16 => return error.TestFailed,
415 else => |e| try expect(e == 15),415 else => |e| try expect(e == 15),
416 }416 }
417 }417 }
...@@ -522,7 +522,7 @@ test "switch with null and T peer types and inferred result location type" {...@@ -522,7 +522,7 @@ test "switch with null and T peer types and inferred result location type" {
522 else => null,522 else => null,
523 }) |v| {523 }) |v| {
524 _ = v;524 _ = v;
525 @panic("fail");525 return error.TestFailed;
526 }526 }
527 }527 }
528 };528 };
...@@ -548,12 +548,12 @@ test "switch prongs with cases with identical payload types" {...@@ -548,12 +548,12 @@ test "switch prongs with cases with identical payload types" {
548 fn doTheSwitch1(u: Union) !void {548 fn doTheSwitch1(u: Union) !void {
549 switch (u) {549 switch (u) {
550 .A, .C => |e| {550 .A, .C => |e| {
551 try expect(@TypeOf(e) == usize);551 comptime assert(@TypeOf(e) == usize);
552 try expect(e == 8);552 try expect(e == 8);
553 },553 },
554 .B => |e| {554 .B => |e| {
555 _ = e;555 _ = e;
556 @panic("fail");556 return error.TestFailed;
557 },557 },
558 }558 }
559 }559 }
...@@ -561,10 +561,10 @@ test "switch prongs with cases with identical payload types" {...@@ -561,10 +561,10 @@ test "switch prongs with cases with identical payload types" {
561 switch (u) {561 switch (u) {
562 .A, .C => |e| {562 .A, .C => |e| {
563 _ = e;563 _ = e;
564 @panic("fail");564 return error.TestFailed;
565 },565 },
566 .B => |e| {566 .B => |e| {
567 try expect(@TypeOf(e) == isize);567 comptime assert(@TypeOf(e) == isize);
568 try expect(e == -8);568 try expect(e == -8);
569 },569 },
570 }570 }
...@@ -574,6 +574,69 @@ test "switch prongs with cases with identical payload types" {...@@ -574,6 +574,69 @@ test "switch prongs with cases with identical payload types" {
574 try comptime S.doTheTest();574 try comptime S.doTheTest();
575}575}
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| comptime assert(@TypeOf(a) == *align(8) const u8),
589 .b, .c => |*p| {
590 _ = p;
591 return error.TestFailed;
592 },
593 }
594
595 switch (u) {
596 .a, .b => |*p| comptime assert(@TypeOf(p) == *align(4) const u8),
597 .c => |*p| {
598 _ = p;
599 return error.TestFailed;
600 },
601 }
602
603 switch (u) {
604 .a, .c => |*p| comptime assert(@TypeOf(p) == *const u8),
605 .b => |*p| {
606 _ = p;
607 return error.TestFailed;
608 },
609 }
610 }
611
612 fn doTheTest2() !void {
613 const un1 = U{ .b = 1 };
614 switch (un1) {
615 .b => |*b| comptime assert(@TypeOf(b) == *align(4) const u8),
616 .a, .c => |*p| {
617 _ = p;
618 return error.TestFailed;
619 },
620 }
621
622 const un2 = U{ .c = 1 };
623 switch (un2) {
624 .c => |*c| comptime assert(@TypeOf(c) == *const u8),
625 .a, .b => |*p| {
626 _ = p;
627 return error.TestFailed;
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
577test "switch on pointer type" {640test "switch on pointer type" {
578 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
579 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO642 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 {...@@ -23,5 +23,7 @@ export fn g() void {
23// :5:10: note: type 'u32' here23// :5:10: note: type 'u32' here
24// :5:14: note: type '*u8' here24// :5:14: note: type '*u8' here
25// :13:20: error: capture group with incompatible types25// :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
27// :13:20: note: this coercion is only possible when capturing by value29// :13:20: note: this coercion is only possible when capturing by value