authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-12 01:00:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-12 01:33:56-07:00
loge64d5a0753dd31702032a458d95c327005c09f85
tree16103f259ec3b7382ca8842025511b6989b62a1e
parentc29746aa553a72fe2ef2d414c1b616ee2a94eab4

Sema: rework beginComptimePtrMutation

This comment is now deleted because the task is completed in this commit: ``` // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use // `container_ty` and `array_ty`, instead of trusting that the parent decl type // matches the type used to derive the elem_ptr/field_ptr/etc. // // This is needed because the types will not match if the pointer we're mutating // through is reinterpreting comptime memory. ``` The main strategy is to change the ComptimePtrMutationKit struct so that instead of `val: *Value` it now returns a tagged union which can be one of three possibilities: * The pointer type matches the actual comptime Value so a direct modification is possible. Before this commit, the implementation incorrectly assumed this was always the case. * In the case of needing to write through a reinterpreted pointer, a mutable base Value pointer is provided along with a byte offset pointing to the element value in virtual memory. * Otherwise, it means a compile error must be emitted because one or both of the types (the owner of the value, or the pointer type being used to write through) do not have a well-defined memory layout. After calling beginComptimePtrMutation, the one callsite now switches on this tagged union and does the appropriate thing. The main new logic is for the second case, which involves pointer reinterpretation, which now takes this strategy: 1. write the base value to a memory buffer. 2. perform the pointer store at the proper byte offset, thereby modifying a subset of the buffer. 3. read the base value from the memory buffer, overwriting the old base value.

3 files changed, 540 insertions(+), 307 deletions(-)

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,