authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-12 15:19:15-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-12 15:19:15-04:00
log6e42d45dccf4ba6fa07082db1cb820897d36924f
tree16103f259ec3b7382ca8842025511b6989b62a1e
parent9360cfebc722d99a54b576fff8bcfc0f0354ad41
parente64d5a0753dd31702032a458d95c327005c09f85
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11851 from ziglang/stage2-comptime-store

Sema: rework beginComptimePtrMutation

5 files changed, 558 insertions(+), 308 deletions(-)

lib/std/debug.zig+17
......@@ -2023,5 +2023,22 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
20232023 }) catch return;
20242024 }
20252025 }
2026
2027 pub fn format(
2028 t: Trace,
2029 comptime fmt: []const u8,
2030 options: std.fmt.FormatOptions,
2031 writer: anytype,
2032 ) !void {
2033 _ = fmt;
2034 _ = options;
2035 if (enabled) {
2036 try writer.writeAll("\n");
2037 t.dump();
2038 try writer.writeAll("\n");
2039 } else {
2040 return writer.writeAll("(value tracing disabled)");
2041 }
2042 }
20262043 };
20272044}
lib/std/mem.zig+1-1
......@@ -268,7 +268,7 @@ pub fn zeroes(comptime T: type) T {
268268 },
269269 .Struct => |struct_info| {
270270 if (@sizeOf(T) == 0) return T{};
271 if (comptime meta.containerLayout(T) == .Extern) {
271 if (struct_info.layout == .Extern) {
272272 var item: T = undefined;
273273 set(u8, asBytes(&item), 0);
274274 return item;
src/Sema.zig+519-306
......@@ -19212,9 +19212,9 @@ fn elemValArray(
1921219212 elem_index: Air.Inst.Ref,
1921319213) CompileError!Air.Inst.Ref {
1921419214 const array_ty = sema.typeOf(array);
19215 const array_sent = array_ty.sentinel() != null;
19215 const array_sent = array_ty.sentinel();
1921619216 const array_len = array_ty.arrayLen();
19217 const array_len_s = array_len + @boolToInt(array_sent);
19217 const array_len_s = array_len + @boolToInt(array_sent != null);
1921819218 const elem_ty = array_ty.childType();
1921919219
1922019220 if (array_len_s == 0) {
......@@ -19228,8 +19228,13 @@ fn elemValArray(
1922819228
1922919229 if (maybe_index_val) |index_val| {
1923019230 const index = @intCast(usize, index_val.toUnsignedInt(target));
19231 if (array_sent) |s| {
19232 if (index == array_len) {
19233 return sema.addConstant(elem_ty, s);
19234 }
19235 }
1923119236 if (index >= array_len_s) {
19232 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
19237 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
1923319238 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
1923419239 }
1923519240 }
......@@ -19269,7 +19274,7 @@ fn elemValArray(
1926919274 // Runtime check is only needed if unable to comptime check
1927019275 if (maybe_index_val == null) {
1927119276 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
19272 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
19277 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
1927319278 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1927419279 }
1927519280 }
......@@ -20521,27 +20526,67 @@ fn storePtrVal(
2052120526 operand_val: Value,
2052220527 operand_ty: Type,
2052320528) !void {
20524 var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val);
20529 var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val, operand_ty);
2052520530 try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut);
2052620531
20527 const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0);
20532 switch (mut_kit.pointee) {
20533 .direct => |val_ptr| {
20534 if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) {
20535 if (!operand_val.eql(val_ptr.*, operand_ty, sema.mod)) {
20536 // TODO add note showing where default value is provided
20537 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
20538 }
20539 return;
20540 }
20541 const arena = mut_kit.beginArena(sema.mod);
20542 defer mut_kit.finishArena(sema.mod);
2052820543
20529 if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) {
20530 if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) {
20531 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
20532 }
20533 return;
20534 }
20544 val_ptr.* = try operand_val.copy(arena);
20545 },
20546 .reinterpret => |reinterpret| {
20547 const target = sema.mod.getTarget();
20548 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target));
20549 const buffer = try sema.gpa.alloc(u8, abi_size);
20550 defer sema.gpa.free(buffer);
20551 reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer);
20552 operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]);
2053520553
20536 const arena = mut_kit.beginArena(sema.mod);
20537 defer mut_kit.finishArena(sema.mod);
20554 const arena = mut_kit.beginArena(sema.mod);
20555 defer mut_kit.finishArena(sema.mod);
2053820556
20539 mut_kit.val.* = try bitcasted_val.copy(arena);
20557 reinterpret.val_ptr.* = try Value.readFromMemory(mut_kit.ty, sema.mod, buffer, arena);
20558 },
20559 .bad_decl_ty, .bad_ptr_ty => {
20560 // TODO show the decl declaration site in a note and explain whether the decl
20561 // or the pointer is the problematic type
20562 return sema.fail(block, src, "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", .{mut_kit.ty.fmt(sema.mod)});
20563 },
20564 }
2054020565}
2054120566
2054220567const ComptimePtrMutationKit = struct {
2054320568 decl_ref_mut: Value.Payload.DeclRefMut.Data,
20544 val: *Value,
20569 pointee: union(enum) {
20570 /// The pointer type matches the actual comptime Value so a direct
20571 /// modification is possible.
20572 direct: *Value,
20573 /// The largest parent Value containing pointee and having a well-defined memory layout.
20574 /// This is used for bitcasting, if direct dereferencing failed.
20575 reinterpret: struct {
20576 val_ptr: *Value,
20577 byte_offset: usize,
20578 },
20579 /// If the root decl could not be used as parent, this means `ty` is the type that
20580 /// caused that by not having a well-defined layout.
20581 /// This one means the Decl that owns the value trying to be modified does not
20582 /// have a well defined memory layout.
20583 bad_decl_ty,
20584 /// If the root decl could not be used as parent, this means `ty` is the type that
20585 /// caused that by not having a well-defined layout.
20586 /// This one means the pointer type that is being stored through does not
20587 /// have a well defined memory layout.
20588 bad_ptr_ty,
20589 },
2054520590 ty: Type,
2054620591 decl_arena: std.heap.ArenaAllocator = undefined,
2054720592
......@@ -20563,354 +20608,469 @@ fn beginComptimePtrMutation(
2056320608 block: *Block,
2056420609 src: LazySrcLoc,
2056520610 ptr_val: Value,
20611 ptr_elem_ty: Type,
2056620612) CompileError!ComptimePtrMutationKit {
20567
20568 // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use
20569 // `container_ty` and `array_ty`, instead of trusting that the parent decl type
20570 // matches the type used to derive the elem_ptr/field_ptr/etc.
20571 //
20572 // This is needed because the types will not match if the pointer we're mutating
20573 // through is reinterpreting comptime memory.
20574
20613 const target = sema.mod.getTarget();
2057520614 switch (ptr_val.tag()) {
2057620615 .decl_ref_mut => {
2057720616 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
2057820617 const decl = sema.mod.declPtr(decl_ref_mut.decl_index);
20579 return ComptimePtrMutationKit{
20580 .decl_ref_mut = decl_ref_mut,
20581 .val = &decl.val,
20582 .ty = decl.ty,
20583 };
20618 return beginComptimePtrMutationInner(sema, block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut);
2058420619 },
2058520620 .comptime_field_ptr => {
2058620621 const payload = ptr_val.castTag(.comptime_field_ptr).?.data;
2058720622 const duped = try sema.arena.create(Value);
2058820623 duped.* = payload.field_val;
20589 return ComptimePtrMutationKit{
20590 .decl_ref_mut = .{
20591 .decl_index = @intToEnum(Module.Decl.Index, 0),
20592 .runtime_index = .comptime_field_ptr,
20593 },
20594 .val = duped,
20595 .ty = payload.field_ty,
20596 };
20624 return beginComptimePtrMutationInner(sema, block, src, payload.field_ty, duped, ptr_elem_ty, .{
20625 .decl_index = @intToEnum(Module.Decl.Index, 0),
20626 .runtime_index = .comptime_field_ptr,
20627 });
2059720628 },
2059820629 .elem_ptr => {
2059920630 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
20600 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
20601 switch (parent.ty.zigTypeTag()) {
20602 .Array, .Vector => {
20603 const check_len = parent.ty.arrayLenIncludingSentinel();
20604 if (elem_ptr.index >= check_len) {
20605 // TODO have the parent include the decl so we can say "declared here"
20606 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
20607 elem_ptr.index, check_len,
20608 });
20609 }
20610 const elem_ty = parent.ty.childType();
20611 switch (parent.val.tag()) {
20612 .undef => {
20613 // An array has been initialized to undefined at comptime and now we
20614 // are for the first time setting an element. We must change the representation
20615 // of the array from `undef` to `array`.
20616 const arena = parent.beginArena(sema.mod);
20617 defer parent.finishArena(sema.mod);
20618
20619 const array_len_including_sentinel =
20620 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
20621 const elems = try arena.alloc(Value, array_len_including_sentinel);
20622 mem.set(Value, elems, Value.undef);
20623
20624 parent.val.* = try Value.Tag.aggregate.create(arena, elems);
20625
20626 return ComptimePtrMutationKit{
20627 .decl_ref_mut = parent.decl_ref_mut,
20628 .val = &elems[elem_ptr.index],
20629 .ty = elem_ty,
20630 };
20631 },
20632 .bytes => {
20633 // An array is memory-optimized to store a slice of bytes, but we are about
20634 // to modify an individual field and the representation has to change.
20635 // If we wanted to avoid this, there would need to be special detection
20636 // elsewhere to identify when writing a value to an array element that is stored
20637 // using the `bytes` tag, and handle it without making a call to this function.
20638 const arena = parent.beginArena(sema.mod);
20639 defer parent.finishArena(sema.mod);
20640
20641 const bytes = parent.val.castTag(.bytes).?.data;
20642 const dest_len = parent.ty.arrayLenIncludingSentinel();
20643 // bytes.len may be one greater than dest_len because of the case when
20644 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
20645 assert(bytes.len >= dest_len);
20646 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
20647 for (elems) |*elem, i| {
20648 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
20649 }
20650
20651 parent.val.* = try Value.Tag.aggregate.create(arena, elems);
20652
20653 return ComptimePtrMutationKit{
20654 .decl_ref_mut = parent.decl_ref_mut,
20655 .val = &elems[elem_ptr.index],
20656 .ty = elem_ty,
20657 };
20658 },
20659 .str_lit => {
20660 // An array is memory-optimized to store a slice of bytes, but we are about
20661 // to modify an individual field and the representation has to change.
20662 // If we wanted to avoid this, there would need to be special detection
20663 // elsewhere to identify when writing a value to an array element that is stored
20664 // using the `str_lit` tag, and handle it without making a call to this function.
20665 const arena = parent.beginArena(sema.mod);
20666 defer parent.finishArena(sema.mod);
20667
20668 const str_lit = parent.val.castTag(.str_lit).?.data;
20669 const dest_len = parent.ty.arrayLenIncludingSentinel();
20670 const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
20671 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
20672 for (bytes) |byte, i| {
20673 elems[i] = try Value.Tag.int_u64.create(arena, byte);
20674 }
20675 if (parent.ty.sentinel()) |sent_val| {
20676 assert(elems.len == bytes.len + 1);
20677 elems[bytes.len] = sent_val;
20678 }
20679
20680 parent.val.* = try Value.Tag.aggregate.create(arena, elems);
20681
20682 return ComptimePtrMutationKit{
20683 .decl_ref_mut = parent.decl_ref_mut,
20684 .val = &elems[elem_ptr.index],
20685 .ty = elem_ty,
20686 };
20687 },
20688 .repeated => {
20689 // An array is memory-optimized to store only a single element value, and
20690 // that value is understood to be the same for the entire length of the array.
20691 // However, now we want to modify an individual field and so the
20692 // representation has to change. If we wanted to avoid this, there would
20693 // need to be special detection elsewhere to identify when writing a value to an
20694 // array element that is stored using the `repeated` tag, and handle it
20695 // without making a call to this function.
20696 const arena = parent.beginArena(sema.mod);
20697 defer parent.finishArena(sema.mod);
20631 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr, elem_ptr.elem_ty);
20632 switch (parent.pointee) {
20633 .direct => |val_ptr| switch (parent.ty.zigTypeTag()) {
20634 .Array, .Vector => {
20635 const check_len = parent.ty.arrayLenIncludingSentinel();
20636 if (elem_ptr.index >= check_len) {
20637 // TODO have the parent include the decl so we can say "declared here"
20638 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
20639 elem_ptr.index, check_len,
20640 });
20641 }
20642 const elem_ty = parent.ty.childType();
20643 switch (val_ptr.tag()) {
20644 .undef => {
20645 // An array has been initialized to undefined at comptime and now we
20646 // are for the first time setting an element. We must change the representation
20647 // of the array from `undef` to `array`.
20648 const arena = parent.beginArena(sema.mod);
20649 defer parent.finishArena(sema.mod);
20650
20651 const array_len_including_sentinel =
20652 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
20653 const elems = try arena.alloc(Value, array_len_including_sentinel);
20654 mem.set(Value, elems, Value.undef);
20655
20656 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
20657
20658 return beginComptimePtrMutationInner(
20659 sema,
20660 block,
20661 src,
20662 elem_ty,
20663 &elems[elem_ptr.index],
20664 ptr_elem_ty,
20665 parent.decl_ref_mut,
20666 );
20667 },
20668 .bytes => {
20669 // An array is memory-optimized to store a slice of bytes, but we are about
20670 // to modify an individual field and the representation has to change.
20671 // If we wanted to avoid this, there would need to be special detection
20672 // elsewhere to identify when writing a value to an array element that is stored
20673 // using the `bytes` tag, and handle it without making a call to this function.
20674 const arena = parent.beginArena(sema.mod);
20675 defer parent.finishArena(sema.mod);
20676
20677 const bytes = val_ptr.castTag(.bytes).?.data;
20678 const dest_len = parent.ty.arrayLenIncludingSentinel();
20679 // bytes.len may be one greater than dest_len because of the case when
20680 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
20681 assert(bytes.len >= dest_len);
20682 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
20683 for (elems) |*elem, i| {
20684 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
20685 }
2069820686
20699 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
20700 const array_len_including_sentinel =
20701 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
20702 const elems = try arena.alloc(Value, array_len_including_sentinel);
20703 mem.set(Value, elems, repeated_val);
20687 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
2070420688
20705 parent.val.* = try Value.Tag.aggregate.create(arena, elems);
20689 return beginComptimePtrMutationInner(
20690 sema,
20691 block,
20692 src,
20693 elem_ty,
20694 &elems[elem_ptr.index],
20695 ptr_elem_ty,
20696 parent.decl_ref_mut,
20697 );
20698 },
20699 .str_lit => {
20700 // An array is memory-optimized to store a slice of bytes, but we are about
20701 // to modify an individual field and the representation has to change.
20702 // If we wanted to avoid this, there would need to be special detection
20703 // elsewhere to identify when writing a value to an array element that is stored
20704 // using the `str_lit` tag, and handle it without making a call to this function.
20705 const arena = parent.beginArena(sema.mod);
20706 defer parent.finishArena(sema.mod);
20707
20708 const str_lit = val_ptr.castTag(.str_lit).?.data;
20709 const dest_len = parent.ty.arrayLenIncludingSentinel();
20710 const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
20711 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
20712 for (bytes) |byte, i| {
20713 elems[i] = try Value.Tag.int_u64.create(arena, byte);
20714 }
20715 if (parent.ty.sentinel()) |sent_val| {
20716 assert(elems.len == bytes.len + 1);
20717 elems[bytes.len] = sent_val;
20718 }
2070620719
20707 return ComptimePtrMutationKit{
20708 .decl_ref_mut = parent.decl_ref_mut,
20709 .val = &elems[elem_ptr.index],
20710 .ty = elem_ty,
20711 };
20712 },
20720 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
2071320721
20714 .aggregate => return ComptimePtrMutationKit{
20715 .decl_ref_mut = parent.decl_ref_mut,
20716 .val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index],
20717 .ty = elem_ty,
20718 },
20722 return beginComptimePtrMutationInner(
20723 sema,
20724 block,
20725 src,
20726 elem_ty,
20727 &elems[elem_ptr.index],
20728 ptr_elem_ty,
20729 parent.decl_ref_mut,
20730 );
20731 },
20732 .repeated => {
20733 // An array is memory-optimized to store only a single element value, and
20734 // that value is understood to be the same for the entire length of the array.
20735 // However, now we want to modify an individual field and so the
20736 // representation has to change. If we wanted to avoid this, there would
20737 // need to be special detection elsewhere to identify when writing a value to an
20738 // array element that is stored using the `repeated` tag, and handle it
20739 // without making a call to this function.
20740 const arena = parent.beginArena(sema.mod);
20741 defer parent.finishArena(sema.mod);
20742
20743 const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena);
20744 const array_len_including_sentinel =
20745 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
20746 const elems = try arena.alloc(Value, array_len_including_sentinel);
20747 mem.set(Value, elems, repeated_val);
20748
20749 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
20750
20751 return beginComptimePtrMutationInner(
20752 sema,
20753 block,
20754 src,
20755 elem_ty,
20756 &elems[elem_ptr.index],
20757 ptr_elem_ty,
20758 parent.decl_ref_mut,
20759 );
20760 },
2071920761
20720 .the_only_possible_value => {
20721 const duped = try sema.arena.create(Value);
20722 duped.* = Value.initTag(.the_only_possible_value);
20723 return ComptimePtrMutationKit{
20724 .decl_ref_mut = parent.decl_ref_mut,
20725 .val = duped,
20726 .ty = elem_ty,
20727 };
20728 },
20762 .aggregate => return beginComptimePtrMutationInner(
20763 sema,
20764 block,
20765 src,
20766 elem_ty,
20767 &val_ptr.castTag(.aggregate).?.data[elem_ptr.index],
20768 ptr_elem_ty,
20769 parent.decl_ref_mut,
20770 ),
20771
20772 .the_only_possible_value => {
20773 const duped = try sema.arena.create(Value);
20774 duped.* = Value.initTag(.the_only_possible_value);
20775 return beginComptimePtrMutationInner(
20776 sema,
20777 block,
20778 src,
20779 elem_ty,
20780 duped,
20781 ptr_elem_ty,
20782 parent.decl_ref_mut,
20783 );
20784 },
2072920785
20730 else => unreachable,
20731 }
20786 else => unreachable,
20787 }
20788 },
20789 else => {
20790 if (elem_ptr.index != 0) {
20791 // TODO include a "declared here" note for the decl
20792 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
20793 elem_ptr.index,
20794 });
20795 }
20796 return beginComptimePtrMutationInner(
20797 sema,
20798 block,
20799 src,
20800 parent.ty,
20801 val_ptr,
20802 ptr_elem_ty,
20803 parent.decl_ref_mut,
20804 );
20805 },
2073220806 },
20733 else => {
20734 if (elem_ptr.index != 0) {
20735 // TODO include a "declared here" note for the decl
20736 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
20737 elem_ptr.index,
20738 });
20807 .reinterpret => |reinterpret| {
20808 if (!elem_ptr.elem_ty.hasWellDefinedLayout()) {
20809 // Even though the parent value type has well-defined memory layout, our
20810 // pointer type does not.
20811 return ComptimePtrMutationKit{
20812 .decl_ref_mut = parent.decl_ref_mut,
20813 .pointee = .bad_ptr_ty,
20814 .ty = elem_ptr.elem_ty,
20815 };
2073920816 }
20817
20818 const elem_abi_size_u64 = try sema.typeAbiSize(block, src, elem_ptr.elem_ty);
20819 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
2074020820 return ComptimePtrMutationKit{
2074120821 .decl_ref_mut = parent.decl_ref_mut,
20742 .val = parent.val,
20822 .pointee = .{ .reinterpret = .{
20823 .val_ptr = reinterpret.val_ptr,
20824 .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_ptr.index,
20825 } },
2074320826 .ty = parent.ty,
2074420827 };
2074520828 },
20829 .bad_decl_ty, .bad_ptr_ty => return parent,
2074620830 }
2074720831 },
2074820832 .field_ptr => {
2074920833 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
20750 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
2075120834 const field_index = @intCast(u32, field_ptr.field_index);
20752 switch (parent.val.tag()) {
20753 .undef => {
20754 // A struct or union has been initialized to undefined at comptime and now we
20755 // are for the first time setting a field. We must change the representation
20756 // of the struct/union from `undef` to `struct`/`union`.
20757 const arena = parent.beginArena(sema.mod);
20758 defer parent.finishArena(sema.mod);
2075920835
20760 switch (parent.ty.zigTypeTag()) {
20761 .Struct => {
20762 const fields = try arena.alloc(Value, parent.ty.structFieldCount());
20763 mem.set(Value, fields, Value.undef);
20836 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
20837 switch (parent.pointee) {
20838 .direct => |val_ptr| switch (val_ptr.tag()) {
20839 .undef => {
20840 // A struct or union has been initialized to undefined at comptime and now we
20841 // are for the first time setting a field. We must change the representation
20842 // of the struct/union from `undef` to `struct`/`union`.
20843 const arena = parent.beginArena(sema.mod);
20844 defer parent.finishArena(sema.mod);
20845
20846 switch (parent.ty.zigTypeTag()) {
20847 .Struct => {
20848 const fields = try arena.alloc(Value, parent.ty.structFieldCount());
20849 mem.set(Value, fields, Value.undef);
20850
20851 val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
20852
20853 return beginComptimePtrMutationInner(
20854 sema,
20855 block,
20856 src,
20857 parent.ty.structFieldType(field_index),
20858 &fields[field_index],
20859 ptr_elem_ty,
20860 parent.decl_ref_mut,
20861 );
20862 },
20863 .Union => {
20864 const payload = try arena.create(Value.Payload.Union);
20865 payload.* = .{ .data = .{
20866 .tag = try Value.Tag.enum_field_index.create(arena, field_index),
20867 .val = Value.undef,
20868 } };
2076420869
20765 parent.val.* = try Value.Tag.aggregate.create(arena, fields);
20870 val_ptr.* = Value.initPayload(&payload.base);
2076620871
20767 return ComptimePtrMutationKit{
20768 .decl_ref_mut = parent.decl_ref_mut,
20769 .val = &fields[field_index],
20770 .ty = parent.ty.structFieldType(field_index),
20771 };
20772 },
20773 .Union => {
20774 const payload = try arena.create(Value.Payload.Union);
20775 payload.* = .{ .data = .{
20776 .tag = try Value.Tag.enum_field_index.create(arena, field_index),
20777 .val = Value.undef,
20778 } };
20872 return beginComptimePtrMutationInner(
20873 sema,
20874 block,
20875 src,
20876 parent.ty.structFieldType(field_index),
20877 &payload.data.val,
20878 ptr_elem_ty,
20879 parent.decl_ref_mut,
20880 );
20881 },
20882 .Pointer => {
20883 assert(parent.ty.isSlice());
20884 val_ptr.* = try Value.Tag.slice.create(arena, .{
20885 .ptr = Value.undef,
20886 .len = Value.undef,
20887 });
20888
20889 switch (field_index) {
20890 Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
20891 sema,
20892 block,
20893 src,
20894 parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
20895 &val_ptr.castTag(.slice).?.data.ptr,
20896 ptr_elem_ty,
20897 parent.decl_ref_mut,
20898 ),
20899 Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
20900 sema,
20901 block,
20902 src,
20903 Type.usize,
20904 &val_ptr.castTag(.slice).?.data.len,
20905 ptr_elem_ty,
20906 parent.decl_ref_mut,
20907 ),
20908
20909 else => unreachable,
20910 }
20911 },
20912 else => unreachable,
20913 }
20914 },
20915 .aggregate => return beginComptimePtrMutationInner(
20916 sema,
20917 block,
20918 src,
20919 parent.ty.structFieldType(field_index),
20920 &val_ptr.castTag(.aggregate).?.data[field_index],
20921 ptr_elem_ty,
20922 parent.decl_ref_mut,
20923 ),
2077920924
20780 parent.val.* = Value.initPayload(&payload.base);
20925 .@"union" => {
20926 // We need to set the active field of the union.
20927 const arena = parent.beginArena(sema.mod);
20928 defer parent.finishArena(sema.mod);
2078120929
20782 return ComptimePtrMutationKit{
20783 .decl_ref_mut = parent.decl_ref_mut,
20784 .val = &payload.data.val,
20785 .ty = parent.ty.structFieldType(field_index),
20786 };
20787 },
20788 .Pointer => {
20789 assert(parent.ty.isSlice());
20790 parent.val.* = try Value.Tag.slice.create(arena, .{
20791 .ptr = Value.undef,
20792 .len = Value.undef,
20793 });
20930 const payload = &val_ptr.castTag(.@"union").?.data;
20931 payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
2079420932
20795 switch (field_index) {
20796 Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
20797 .decl_ref_mut = parent.decl_ref_mut,
20798 .val = &parent.val.castTag(.slice).?.data.ptr,
20799 .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
20800 },
20801 Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
20802 .decl_ref_mut = parent.decl_ref_mut,
20803 .val = &parent.val.castTag(.slice).?.data.len,
20804 .ty = Type.usize,
20805 },
20806 else => unreachable,
20807 }
20808 },
20809 else => unreachable,
20810 }
20811 },
20812 .aggregate => return ComptimePtrMutationKit{
20813 .decl_ref_mut = parent.decl_ref_mut,
20814 .val = &parent.val.castTag(.aggregate).?.data[field_index],
20815 .ty = parent.ty.structFieldType(field_index),
20816 },
20817 .@"union" => {
20818 // We need to set the active field of the union.
20819 const arena = parent.beginArena(sema.mod);
20820 defer parent.finishArena(sema.mod);
20933 return beginComptimePtrMutationInner(
20934 sema,
20935 block,
20936 src,
20937 parent.ty.structFieldType(field_index),
20938 &payload.val,
20939 ptr_elem_ty,
20940 parent.decl_ref_mut,
20941 );
20942 },
20943 .slice => switch (field_index) {
20944 Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
20945 sema,
20946 block,
20947 src,
20948 parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
20949 &val_ptr.castTag(.slice).?.data.ptr,
20950 ptr_elem_ty,
20951 parent.decl_ref_mut,
20952 ),
20953
20954 Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
20955 sema,
20956 block,
20957 src,
20958 Type.usize,
20959 &val_ptr.castTag(.slice).?.data.len,
20960 ptr_elem_ty,
20961 parent.decl_ref_mut,
20962 ),
2082120963
20822 const payload = &parent.val.castTag(.@"union").?.data;
20823 payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
20964 else => unreachable,
20965 },
2082420966
20967 else => unreachable,
20968 },
20969 .reinterpret => |reinterpret| {
20970 const field_offset_u64 = field_ptr.container_ty.structFieldOffset(field_index, target);
20971 const field_offset = try sema.usizeCast(block, src, field_offset_u64);
2082520972 return ComptimePtrMutationKit{
2082620973 .decl_ref_mut = parent.decl_ref_mut,
20827 .val = &payload.val,
20828 .ty = parent.ty.structFieldType(field_index),
20974 .pointee = .{ .reinterpret = .{
20975 .val_ptr = reinterpret.val_ptr,
20976 .byte_offset = reinterpret.byte_offset + field_offset,
20977 } },
20978 .ty = parent.ty,
2082920979 };
2083020980 },
20831 .slice => switch (field_index) {
20832 Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
20833 .decl_ref_mut = parent.decl_ref_mut,
20834 .val = &parent.val.castTag(.slice).?.data.ptr,
20835 .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
20836 },
20837 Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
20838 .decl_ref_mut = parent.decl_ref_mut,
20839 .val = &parent.val.castTag(.slice).?.data.len,
20840 .ty = Type.usize,
20841 },
20842 else => unreachable,
20843 },
20844
20845 else => unreachable,
20981 .bad_decl_ty, .bad_ptr_ty => return parent,
2084620982 }
2084720983 },
2084820984 .eu_payload_ptr => {
2084920985 const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
20850 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr);
20851 const payload_ty = parent.ty.errorUnionPayload();
20852 switch (parent.val.tag()) {
20853 else => {
20854 // An error union has been initialized to undefined at comptime and now we
20855 // are for the first time setting the payload. We must change the
20856 // representation of the error union from `undef` to `opt_payload`.
20857 const arena = parent.beginArena(sema.mod);
20858 defer parent.finishArena(sema.mod);
20859
20860 const payload = try arena.create(Value.Payload.SubValue);
20861 payload.* = .{
20862 .base = .{ .tag = .eu_payload },
20863 .data = Value.undef,
20864 };
20986 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr, eu_ptr.container_ty);
20987 switch (parent.pointee) {
20988 .direct => |val_ptr| {
20989 const payload_ty = parent.ty.errorUnionPayload();
20990 switch (val_ptr.tag()) {
20991 else => {
20992 // An error union has been initialized to undefined at comptime and now we
20993 // are for the first time setting the payload. We must change the
20994 // representation of the error union from `undef` to `opt_payload`.
20995 const arena = parent.beginArena(sema.mod);
20996 defer parent.finishArena(sema.mod);
20997
20998 const payload = try arena.create(Value.Payload.SubValue);
20999 payload.* = .{
21000 .base = .{ .tag = .eu_payload },
21001 .data = Value.undef,
21002 };
2086521003
20866 parent.val.* = Value.initPayload(&payload.base);
21004 val_ptr.* = Value.initPayload(&payload.base);
2086721005
20868 return ComptimePtrMutationKit{
20869 .decl_ref_mut = parent.decl_ref_mut,
20870 .val = &payload.data,
20871 .ty = payload_ty,
20872 };
21006 return ComptimePtrMutationKit{
21007 .decl_ref_mut = parent.decl_ref_mut,
21008 .pointee = .{ .direct = &payload.data },
21009 .ty = payload_ty,
21010 };
21011 },
21012 .eu_payload => return ComptimePtrMutationKit{
21013 .decl_ref_mut = parent.decl_ref_mut,
21014 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
21015 .ty = payload_ty,
21016 },
21017 }
2087321018 },
20874 .eu_payload => return ComptimePtrMutationKit{
21019 .bad_decl_ty, .bad_ptr_ty => return parent,
21020 // Even though the parent value type has well-defined memory layout, our
21021 // pointer type does not.
21022 .reinterpret => return ComptimePtrMutationKit{
2087521023 .decl_ref_mut = parent.decl_ref_mut,
20876 .val = &parent.val.castTag(.eu_payload).?.data,
20877 .ty = payload_ty,
21024 .pointee = .bad_ptr_ty,
21025 .ty = eu_ptr.container_ty,
2087821026 },
2087921027 }
2088021028 },
2088121029 .opt_payload_ptr => {
2088221030 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
20883 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr);
20884 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
20885 switch (parent.val.tag()) {
20886 .undef, .null_value => {
20887 // An optional has been initialized to undefined at comptime and now we
20888 // are for the first time setting the payload. We must change the
20889 // representation of the optional from `undef` to `opt_payload`.
20890 const arena = parent.beginArena(sema.mod);
20891 defer parent.finishArena(sema.mod);
20892
20893 const payload = try arena.create(Value.Payload.SubValue);
20894 payload.* = .{
20895 .base = .{ .tag = .opt_payload },
20896 .data = Value.undef,
20897 };
21031 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);
21032 switch (parent.pointee) {
21033 .direct => |val_ptr| {
21034 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
21035 switch (val_ptr.tag()) {
21036 .undef, .null_value => {
21037 // An optional has been initialized to undefined at comptime and now we
21038 // are for the first time setting the payload. We must change the
21039 // representation of the optional from `undef` to `opt_payload`.
21040 const arena = parent.beginArena(sema.mod);
21041 defer parent.finishArena(sema.mod);
2089821042
20899 parent.val.* = Value.initPayload(&payload.base);
21043 const payload = try arena.create(Value.Payload.SubValue);
21044 payload.* = .{
21045 .base = .{ .tag = .opt_payload },
21046 .data = Value.undef,
21047 };
2090021048
20901 return ComptimePtrMutationKit{
20902 .decl_ref_mut = parent.decl_ref_mut,
20903 .val = &payload.data,
20904 .ty = payload_ty,
20905 };
21049 val_ptr.* = Value.initPayload(&payload.base);
21050
21051 return ComptimePtrMutationKit{
21052 .decl_ref_mut = parent.decl_ref_mut,
21053 .pointee = .{ .direct = &payload.data },
21054 .ty = payload_ty,
21055 };
21056 },
21057 .opt_payload => return ComptimePtrMutationKit{
21058 .decl_ref_mut = parent.decl_ref_mut,
21059 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
21060 .ty = payload_ty,
21061 },
21062
21063 else => unreachable,
21064 }
2090621065 },
20907 .opt_payload => return ComptimePtrMutationKit{
21066 .bad_decl_ty, .bad_ptr_ty => return parent,
21067 // Even though the parent value type has well-defined memory layout, our
21068 // pointer type does not.
21069 .reinterpret => return ComptimePtrMutationKit{
2090821070 .decl_ref_mut = parent.decl_ref_mut,
20909 .val = &parent.val.castTag(.opt_payload).?.data,
20910 .ty = payload_ty,
21071 .pointee = .bad_ptr_ty,
21072 .ty = opt_ptr.container_ty,
2091121073 },
20912
20913 else => unreachable,
2091421074 }
2091521075 },
2091621076 .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
......@@ -20918,10 +21078,63 @@ fn beginComptimePtrMutation(
2091821078 }
2091921079}
2092021080
21081fn beginComptimePtrMutationInner(
21082 sema: *Sema,
21083 block: *Block,
21084 src: LazySrcLoc,
21085 decl_ty: Type,
21086 decl_val: *Value,
21087 ptr_elem_ty: Type,
21088 decl_ref_mut: Value.Payload.DeclRefMut.Data,
21089) CompileError!ComptimePtrMutationKit {
21090 const target = sema.mod.getTarget();
21091 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
21092 if (coerce_ok) {
21093 return ComptimePtrMutationKit{
21094 .decl_ref_mut = decl_ref_mut,
21095 .pointee = .{ .direct = decl_val },
21096 .ty = decl_ty,
21097 };
21098 }
21099
21100 // Handle the case that the decl is an array and we're actually trying to point to an element.
21101 if (decl_ty.isArrayOrVector()) {
21102 const decl_elem_ty = decl_ty.childType();
21103 if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
21104 return ComptimePtrMutationKit{
21105 .decl_ref_mut = decl_ref_mut,
21106 .pointee = .{ .direct = decl_val },
21107 .ty = decl_ty,
21108 };
21109 }
21110 }
21111
21112 if (!decl_ty.hasWellDefinedLayout()) {
21113 return ComptimePtrMutationKit{
21114 .decl_ref_mut = decl_ref_mut,
21115 .pointee = .{ .bad_decl_ty = {} },
21116 .ty = decl_ty,
21117 };
21118 }
21119 if (!ptr_elem_ty.hasWellDefinedLayout()) {
21120 return ComptimePtrMutationKit{
21121 .decl_ref_mut = decl_ref_mut,
21122 .pointee = .{ .bad_ptr_ty = {} },
21123 .ty = ptr_elem_ty,
21124 };
21125 }
21126 return ComptimePtrMutationKit{
21127 .decl_ref_mut = decl_ref_mut,
21128 .pointee = .{ .reinterpret = .{
21129 .val_ptr = decl_val,
21130 .byte_offset = 0,
21131 } },
21132 .ty = decl_ty,
21133 };
21134}
21135
2092121136const TypedValueAndOffset = struct {
2092221137 tv: TypedValue,
20923 /// The starting byte offset of `val` from `root_val`.
20924 /// If the type does not have a well-defined memory layout, this is null.
2092521138 byte_offset: usize,
2092621139};
2092721140
......@@ -21197,7 +21410,7 @@ fn bitCast(
2119721410 return block.addBitCast(dest_ty, inst);
2119821411}
2119921412
21200pub fn bitCastVal(
21413fn bitCastVal(
2120121414 sema: *Sema,
2120221415 block: *Block,
2120321416 src: LazySrcLoc,
test/behavior/eval.zig+17
......@@ -1252,3 +1252,20 @@ test "pass pointer to field of comptime-only type as a runtime parameter" {
12521252 };
12531253 try S.doTheTest();
12541254}
1255
1256test "comptime write through extern struct reinterpreted as array" {
1257 comptime {
1258 const S = extern struct {
1259 a: u8,
1260 b: u8,
1261 c: u8,
1262 };
1263 var s: S = undefined;
1264 @ptrCast(*[3]u8, &s)[0] = 1;
1265 @ptrCast(*[3]u8, &s)[1] = 2;
1266 @ptrCast(*[3]u8, &s)[2] = 3;
1267 assert(s.a == 1);
1268 assert(s.b == 2);
1269 assert(s.c == 3);
1270 }
1271}
test/behavior/translate_c_macros.zig+4-1
......@@ -19,7 +19,10 @@ test "casting to void with a macro" {
1919}
2020
2121test "initializer list expression" {
22 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2326
2427 try expectEqual(h.Color{
2528 .r = 200,