@@ -27258,7 +27258,7 @@ fn unionFieldVal(
27258
27258
return sema.failWithOwnedErrorMsg(block, msg);
27259
27259
}
27260
27260
},
27261
- .Packed, .Extern => {
27261
+ .Packed, .Extern => |layout| {
27262
27262
if (tag_matches) {
27263
27263
return Air.internedToRef(un.val);
27264
27264
} else {
@@ -27267,7 +27267,7 @@ fn unionFieldVal(
27267
27267
else
27268
27268
union_ty.unionFieldType(un.tag.toValue(), mod).?;
27269
27269
27270
- if (try sema.bitCastVal (block, src, un.val.toValue(), old_ty, field_ty, 0 )) |new_val| {
27270
+ if (try sema.bitCastUnionFieldVal (block, src, un.val.toValue(), old_ty, field_ty, layout )) |new_val| {
27271
27271
return Air.internedToRef(new_val.toIntern());
27272
27272
}
27273
27273
}
@@ -29788,13 +29788,19 @@ fn storePtrVal(
29788
29788
error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
29789
29789
error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
29790
29790
};
29791
- operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
29792
- error.OutOfMemory => return error.OutOfMemory,
29793
- error.ReinterpretDeclRef => unreachable,
29794
- error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
29795
- error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
29796
- };
29797
-
29791
+ if (reinterpret.write_packed) {
29792
+ operand_val.writeToPackedMemory(operand_ty, mod, buffer[reinterpret.byte_offset..], 0) catch |err| switch (err) {
29793
+ error.OutOfMemory => return error.OutOfMemory,
29794
+ error.ReinterpretDeclRef => unreachable,
29795
+ };
29796
+ } else {
29797
+ operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
29798
+ error.OutOfMemory => return error.OutOfMemory,
29799
+ error.ReinterpretDeclRef => unreachable,
29800
+ error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
29801
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
29802
+ };
29803
+ }
29798
29804
const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) {
29799
29805
error.OutOfMemory => return error.OutOfMemory,
29800
29806
error.IllDefinedMemoryLayout => unreachable,
@@ -29826,6 +29832,8 @@ const ComptimePtrMutationKit = struct {
29826
29832
reinterpret: struct {
29827
29833
val_ptr: *Value,
29828
29834
byte_offset: usize,
29835
+ /// If set, write the operand to packed memory
29836
+ write_packed: bool = false,
29829
29837
},
29830
29838
/// If the root decl could not be used as parent, this means `ty` is the type that
29831
29839
/// caused that by not having a well-defined layout.
@@ -30189,21 +30197,43 @@ fn beginComptimePtrMutation(
30189
30197
);
30190
30198
},
30191
30199
.@"union" => {
30192
- // We need to set the active field of the union.
30193
- const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod);
30194
-
30195
30200
const payload = &val_ptr.castTag(.@"union").?.data;
30196
- payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index );
30201
+ const layout = base_child_ty.containerLayout(mod );
30197
30202
30198
- return beginComptimePtrMutationInner(
30199
- sema,
30200
- block,
30201
- src,
30202
- parent.ty.structFieldType(field_index, mod),
30203
- &payload.val,
30204
- ptr_elem_ty,
30205
- parent.mut_decl,
30206
- );
30203
+ const tag_type = base_child_ty.unionTagTypeHypothetical(mod);
30204
+ const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index);
30205
+ if (layout == .Auto or (payload.tag != null and hypothetical_tag.eql(payload.tag.?, tag_type, mod))) {
30206
+ // We need to set the active field of the union.
30207
+ payload.tag = hypothetical_tag;
30208
+
30209
+ const field_ty = parent.ty.structFieldType(field_index, mod);
30210
+ return beginComptimePtrMutationInner(
30211
+ sema,
30212
+ block,
30213
+ src,
30214
+ field_ty,
30215
+ &payload.val,
30216
+ ptr_elem_ty,
30217
+ parent.mut_decl,
30218
+ );
30219
+ } else {
30220
+ // Writing to a different field (a different or unknown tag is active) requires reinterpreting
30221
+ // memory of the entire union, which requires knowing its abiSize.
30222
+ try sema.resolveTypeLayout(parent.ty);
30223
+
30224
+ // This union value no longer has a well-defined tag type.
30225
+ // The reinterpretation will read it back out as .none.
30226
+ payload.val = try payload.val.unintern(sema.arena, mod);
30227
+ return ComptimePtrMutationKit{
30228
+ .mut_decl = parent.mut_decl,
30229
+ .pointee = .{ .reinterpret = .{
30230
+ .val_ptr = val_ptr,
30231
+ .byte_offset = 0,
30232
+ .write_packed = layout == .Packed,
30233
+ } },
30234
+ .ty = parent.ty,
30235
+ };
30236
+ }
30207
30237
},
30208
30238
.slice => switch (field_index) {
30209
30239
Value.slice_ptr_index => return beginComptimePtrMutationInner(
@@ -30704,6 +30734,7 @@ fn bitCastVal(
30704
30734
// For types with well-defined memory layouts, we serialize them a byte buffer,
30705
30735
// then deserialize to the new type.
30706
30736
const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
30737
+
30707
30738
const buffer = try sema.gpa.alloc(u8, abi_size);
30708
30739
defer sema.gpa.free(buffer);
30709
30740
val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
@@ -30720,6 +30751,63 @@ fn bitCastVal(
30720
30751
};
30721
30752
}
30722
30753
30754
+ fn bitCastUnionFieldVal(
30755
+ sema: *Sema,
30756
+ block: *Block,
30757
+ src: LazySrcLoc,
30758
+ val: Value,
30759
+ old_ty: Type,
30760
+ field_ty: Type,
30761
+ layout: std.builtin.Type.ContainerLayout,
30762
+ ) !?Value {
30763
+ const mod = sema.mod;
30764
+ if (old_ty.eql(field_ty, mod)) return val;
30765
+
30766
+ const old_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
30767
+ const field_size = try sema.usizeCast(block, src, field_ty.abiSize(mod));
30768
+ const endian = mod.getTarget().cpu.arch.endian();
30769
+
30770
+ const buffer = try sema.gpa.alloc(u8, @max(old_size, field_size));
30771
+ defer sema.gpa.free(buffer);
30772
+
30773
+ // Reading a larger value means we need to reinterpret from undefined bytes.
30774
+ const offset = switch (layout) {
30775
+ .Extern => offset: {
30776
+ if (field_size > old_size) @memset(buffer[old_size..], 0xaa);
30777
+ val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
30778
+ error.OutOfMemory => return error.OutOfMemory,
30779
+ error.ReinterpretDeclRef => return null,
30780
+ error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
30781
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
30782
+ };
30783
+ break :offset 0;
30784
+ },
30785
+ .Packed => offset: {
30786
+ if (field_size > old_size) {
30787
+ const min_size = @max(old_size, 1);
30788
+ switch (endian) {
30789
+ .Little => @memset(buffer[min_size - 1 ..], 0xaa),
30790
+ .Big => @memset(buffer[0 .. buffer.len - min_size + 1], 0xaa),
30791
+ }
30792
+ }
30793
+
30794
+ val.writeToPackedMemory(old_ty, mod, buffer, 0) catch |err| switch (err) {
30795
+ error.OutOfMemory => return error.OutOfMemory,
30796
+ error.ReinterpretDeclRef => return null,
30797
+ };
30798
+
30799
+ break :offset if (endian == .Big) buffer.len - field_size else 0;
30800
+ },
30801
+ .Auto => unreachable,
30802
+ };
30803
+
30804
+ return Value.readFromMemory(field_ty, mod, buffer[offset..], sema.arena) catch |err| switch (err) {
30805
+ error.OutOfMemory => return error.OutOfMemory,
30806
+ error.IllDefinedMemoryLayout => unreachable,
30807
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{field_ty.fmt(mod)}),
30808
+ };
30809
+ }
30810
+
30723
30811
fn coerceArrayPtrToSlice(
30724
30812
sema: *Sema,
30725
30813
block: *Block,
0 commit comments