aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorJohn Schmidt <john.schmidt.h@gmail.com>2024-02-04 23:04:43 +0100
committerJohn Schmidt <john.schmidt.h@gmail.com>2024-02-08 23:49:03 +0100
commitdbcd53def0c82e7b1197194f48be534930f9c34b (patch)
tree29f82b45f0e0713c2ee0fea8101c8a38ee465c59 /src/Sema.zig
parent919a3bae1c5f2024b09e127a15c752d9dc0aa9a6 (diff)
downloadzig-dbcd53def0c82e7b1197194f48be534930f9c34b.tar.gz
zig-dbcd53def0c82e7b1197194f48be534930f9c34b.zip
Preserve field alignment in union pointer captures
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig78
1 files changed, 47 insertions, 31 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 5f332d6e59..9bb2c87a99 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -10992,51 +10992,67 @@ const SwitchProngAnalysis = struct {
// By-reference captures have some further restrictions which make them easier to emit
if (capture_byref) {
+ const first_field_alignment = union_obj.fieldAlign(ip, first_field_index);
+ const same_alignment = for (field_indices[1..]) |field_idx| {
+ const field_alignment = union_obj.fieldAlign(ip, field_idx);
+ if (field_alignment != first_field_alignment) break false;
+ } else true;
const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
- const capture_ptr_ty = try sema.ptrType(.{
- .child = capture_ty.toIntern(),
- .flags = .{
- // TODO: alignment!
- .is_const = operand_ptr_info.flags.is_const,
- .is_volatile = operand_ptr_info.flags.is_volatile,
- .address_space = operand_ptr_info.flags.address_space,
- },
- });
-
- // By-ref captures of hetereogeneous types are only allowed if each field
- // pointer type is in-memory coercible to the capture pointer type.
- if (!same_types) {
- for (field_indices, 0..) |field_idx, i| {
+ const capture_ptr_ty = if (same_types and same_alignment) same: {
+ break :same try sema.ptrType(.{
+ .child = capture_ty.toIntern(),
+ .flags = .{
+ .is_const = operand_ptr_info.flags.is_const,
+ .is_volatile = operand_ptr_info.flags.is_volatile,
+ .address_space = operand_ptr_info.flags.address_space,
+ .alignment = first_field_alignment,
+ },
+ });
+ } else resolve: {
+ // By-ref captures of hetereogeneous types are only allowed if all field
+ // pointer types are peer resolvable to each other.
+ // We need values to run PTR on, so make a bunch of undef constants.
+ const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
+ for (field_indices, dummy_captures) |field_idx, *dummy| {
const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]);
const field_ptr_ty = try sema.ptrType(.{
.child = field_ty.toIntern(),
.flags = .{
- // TODO: alignment!
.is_const = operand_ptr_info.flags.is_const,
.is_volatile = operand_ptr_info.flags.is_volatile,
.address_space = operand_ptr_info.flags.address_space,
+ .alignment = union_obj.fieldAlign(ip, field_idx),
},
});
- if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
+ dummy.* = try mod.undefRef(field_ptr_ty);
+ }
+ const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
+ @memset(case_srcs, .unneeded);
+
+ break :resolve sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ // This must be a multi-prong so this must be a `multi_capture` src
const multi_idx = raw_capture_src.multi_capture;
const src_decl_ptr = sema.mod.declPtr(block.src_decl);
+ for (case_srcs, 0..) |*case_src, i| {
+ const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
+ case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ }
const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
- const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } };
- const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
- const msg = msg: {
- const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{
- field_ty.fmt(sema.mod),
- capture_ty.fmt(sema.mod),
- });
- try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
- break :msg msg;
+ _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
+ error.AnalysisFail => {
+ const msg = sema.err orelse return error.AnalysisFail;
+ try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
+ try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
+ return error.AnalysisFail;
+ },
+ else => |e| return e,
};
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
- }
+ unreachable;
+ },
+ else => |e| return e,
+ };
+ };
if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty);