| author | |
| committer | |
| log | 3e79c0f18ce05c302c6117e66275db4fcdf033e4 |
| tree | 4ae226095dcc3b225301039da7e94be9922e518b |
| parent | 00ff123b1eb237f72f918ffd413c8d9c8936c46d |
| parent | 7cb227ab678b57dd10b65c1d9c89526a3e47cc2a |
| signature |
Sema: preserve field alignment in union pointer captures3 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 emit | 10924 | // 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 | }; | ||
| 10971 | 10972 | ||
| 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 | } |
| 576 | 576 | ||
| 577 | test "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 | |||
| 577 | test "switch on pointer type" { | 640 | test "switch on pointer type" { |
| 578 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 641 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 579 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 642 | 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' here | 23 | // :5:10: note: type 'u32' here |
| 24 | // :5:14: note: type '*u8' here | 24 | // :5:14: note: type '*u8' here |
| 25 | // :13:20: error: capture group with incompatible types | 25 | // :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 value | 29 | // :13:20: note: this coercion is only possible when capturing by value |