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(...@@ -19212,9 +19212,9 @@ fn elemValArray(
19212 elem_index: Air.Inst.Ref,19212 elem_index: Air.Inst.Ref,
19213) CompileError!Air.Inst.Ref {19213) CompileError!Air.Inst.Ref {
19214 const array_ty = sema.typeOf(array);19214 const array_ty = sema.typeOf(array);
19215 const array_sent = array_ty.sentinel() != null;19215 const array_sent = array_ty.sentinel();
19216 const array_len = array_ty.arrayLen();19216 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);
19218 const elem_ty = array_ty.childType();19218 const elem_ty = array_ty.childType();
1921919219
19220 if (array_len_s == 0) {19220 if (array_len_s == 0) {
...@@ -19228,8 +19228,13 @@ fn elemValArray(...@@ -19228,8 +19228,13 @@ fn elemValArray(
1922819228
19229 if (maybe_index_val) |index_val| {19229 if (maybe_index_val) |index_val| {
19230 const index = @intCast(usize, index_val.toUnsignedInt(target));19230 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 }
19231 if (index >= array_len_s) {19236 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 "";
19233 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });19238 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
19234 }19239 }
19235 }19240 }
...@@ -19269,7 +19274,7 @@ fn elemValArray(...@@ -19269,7 +19274,7 @@ fn elemValArray(
19269 // Runtime check is only needed if unable to comptime check19274 // Runtime check is only needed if unable to comptime check
19270 if (maybe_index_val == null) {19275 if (maybe_index_val == null) {
19271 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);19276 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;
19273 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);19278 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
19274 }19279 }
19275 }19280 }
...@@ -20521,27 +20526,67 @@ fn storePtrVal(...@@ -20521,27 +20526,67 @@ fn storePtrVal(
20521 operand_val: Value,20526 operand_val: Value,
20522 operand_ty: Type,20527 operand_ty: Type,
20523) !void {20528) !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);
20525 try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut);20530 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) {20544 val_ptr.* = try operand_val.copy(arena);
20530 if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) {20545 },
20531 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});20546 .reinterpret => |reinterpret| {
20532 }20547 const target = sema.mod.getTarget();
20533 return;20548 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target));
20534 }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);20554 const arena = mut_kit.beginArena(sema.mod);
20537 defer mut_kit.finishArena(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 }
20540}20565}
2054120566
20542const ComptimePtrMutationKit = struct {20567const ComptimePtrMutationKit = struct {
20543 decl_ref_mut: Value.Payload.DeclRefMut.Data,20568 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 },
20545 ty: Type,20590 ty: Type,
20546 decl_arena: std.heap.ArenaAllocator = undefined,20591 decl_arena: std.heap.ArenaAllocator = undefined,
2054720592
...@@ -20563,354 +20608,469 @@ fn beginComptimePtrMutation(...@@ -20563,354 +20608,469 @@ fn beginComptimePtrMutation(
20563 block: *Block,20608 block: *Block,
20564 src: LazySrcLoc,20609 src: LazySrcLoc,
20565 ptr_val: Value,20610 ptr_val: Value,
20611 ptr_elem_ty: Type,
20566) CompileError!ComptimePtrMutationKit {20612) CompileError!ComptimePtrMutationKit {
2056720613 const target = sema.mod.getTarget();
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
20575 switch (ptr_val.tag()) {20614 switch (ptr_val.tag()) {
20576 .decl_ref_mut => {20615 .decl_ref_mut => {
20577 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;20616 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
20578 const decl = sema.mod.declPtr(decl_ref_mut.decl_index);20617 const decl = sema.mod.declPtr(decl_ref_mut.decl_index);
20579 return ComptimePtrMutationKit{20618 return beginComptimePtrMutationInner(sema, block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut);
20580 .decl_ref_mut = decl_ref_mut,
20581 .val = &decl.val,
20582 .ty = decl.ty,
20583 };
20584 },20619 },
20585 .comptime_field_ptr => {20620 .comptime_field_ptr => {
20586 const payload = ptr_val.castTag(.comptime_field_ptr).?.data;20621 const payload = ptr_val.castTag(.comptime_field_ptr).?.data;
20587 const duped = try sema.arena.create(Value);20622 const duped = try sema.arena.create(Value);
20588 duped.* = payload.field_val;20623 duped.* = payload.field_val;
20589 return ComptimePtrMutationKit{20624 return beginComptimePtrMutationInner(sema, block, src, payload.field_ty, duped, ptr_elem_ty, .{
20590 .decl_ref_mut = .{20625 .decl_index = @intToEnum(Module.Decl.Index, 0),
20591 .decl_index = @intToEnum(Module.Decl.Index, 0),20626 .runtime_index = .comptime_field_ptr,
20592 .runtime_index = .comptime_field_ptr,20627 });
20593 },
20594 .val = duped,
20595 .ty = payload.field_ty,
20596 };
20597 },20628 },
20598 .elem_ptr => {20629 .elem_ptr => {
20599 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;20630 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
20600 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);20631 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr, elem_ptr.elem_ty);
20601 switch (parent.ty.zigTypeTag()) {20632 switch (parent.pointee) {
20602 .Array, .Vector => {20633 .direct => |val_ptr| switch (parent.ty.zigTypeTag()) {
20603 const check_len = parent.ty.arrayLenIncludingSentinel();20634 .Array, .Vector => {
20604 if (elem_ptr.index >= check_len) {20635 const check_len = parent.ty.arrayLenIncludingSentinel();
20605 // TODO have the parent include the decl so we can say "declared here"20636 if (elem_ptr.index >= check_len) {
20606 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{20637 // TODO have the parent include the decl so we can say "declared here"
20607 elem_ptr.index, check_len,20638 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
20608 });20639 elem_ptr.index, check_len,
20609 }20640 });
20610 const elem_ty = parent.ty.childType();20641 }
20611 switch (parent.val.tag()) {20642 const elem_ty = parent.ty.childType();
20612 .undef => {20643 switch (val_ptr.tag()) {
20613 // An array has been initialized to undefined at comptime and now we20644 .undef => {
20614 // are for the first time setting an element. We must change the representation20645 // An array has been initialized to undefined at comptime and now we
20615 // of the array from `undef` to `array`.20646 // are for the first time setting an element. We must change the representation
20616 const arena = parent.beginArena(sema.mod);20647 // of the array from `undef` to `array`.
20617 defer parent.finishArena(sema.mod);20648 const arena = parent.beginArena(sema.mod);
2061820649 defer parent.finishArena(sema.mod);
20619 const array_len_including_sentinel =20650
20620 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());20651 const array_len_including_sentinel =
20621 const elems = try arena.alloc(Value, array_len_including_sentinel);20652 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
20622 mem.set(Value, elems, Value.undef);20653 const elems = try arena.alloc(Value, array_len_including_sentinel);
2062320654 mem.set(Value, elems, Value.undef);
20624 parent.val.* = try Value.Tag.aggregate.create(arena, elems);20655
2062520656 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
20626 return ComptimePtrMutationKit{20657
20627 .decl_ref_mut = parent.decl_ref_mut,20658 return beginComptimePtrMutationInner(
20628 .val = &elems[elem_ptr.index],20659 sema,
20629 .ty = elem_ty,20660 block,
20630 };20661 src,
20631 },20662 elem_ty,
20632 .bytes => {20663 &elems[elem_ptr.index],
20633 // An array is memory-optimized to store a slice of bytes, but we are about20664 ptr_elem_ty,
20634 // to modify an individual field and the representation has to change.20665 parent.decl_ref_mut,
20635 // If we wanted to avoid this, there would need to be special detection20666 );
20636 // elsewhere to identify when writing a value to an array element that is stored20667 },
20637 // using the `bytes` tag, and handle it without making a call to this function.20668 .bytes => {
20638 const arena = parent.beginArena(sema.mod);20669 // An array is memory-optimized to store a slice of bytes, but we are about
20639 defer parent.finishArena(sema.mod);20670 // to modify an individual field and the representation has to change.
2064020671 // If we wanted to avoid this, there would need to be special detection
20641 const bytes = parent.val.castTag(.bytes).?.data;20672 // elsewhere to identify when writing a value to an array element that is stored
20642 const dest_len = parent.ty.arrayLenIncludingSentinel();20673 // using the `bytes` tag, and handle it without making a call to this function.
20643 // bytes.len may be one greater than dest_len because of the case when20674 const arena = parent.beginArena(sema.mod);
20644 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.20675 defer parent.finishArena(sema.mod);
20645 assert(bytes.len >= dest_len);20676
20646 const elems = try arena.alloc(Value, @intCast(usize, dest_len));20677 const bytes = val_ptr.castTag(.bytes).?.data;
20647 for (elems) |*elem, i| {20678 const dest_len = parent.ty.arrayLenIncludingSentinel();
20648 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);20679 // bytes.len may be one greater than dest_len because of the case when
20649 }20680 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
2065020681 assert(bytes.len >= dest_len);
20651 parent.val.* = try Value.Tag.aggregate.create(arena, elems);20682 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
2065220683 for (elems) |*elem, i| {
20653 return ComptimePtrMutationKit{20684 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
20654 .decl_ref_mut = parent.decl_ref_mut,20685 }
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);
2069820686
20699 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);20687 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
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);
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{20720 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
20708 .decl_ref_mut = parent.decl_ref_mut,
20709 .val = &elems[elem_ptr.index],
20710 .ty = elem_ty,
20711 };
20712 },
2071320721
20714 .aggregate => return ComptimePtrMutationKit{20722 return beginComptimePtrMutationInner(
20715 .decl_ref_mut = parent.decl_ref_mut,20723 sema,
20716 .val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index],20724 block,
20717 .ty = elem_ty,20725 src,
20718 },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 => {20762 .aggregate => return beginComptimePtrMutationInner(
20721 const duped = try sema.arena.create(Value);20763 sema,
20722 duped.* = Value.initTag(.the_only_possible_value);20764 block,
20723 return ComptimePtrMutationKit{20765 src,
20724 .decl_ref_mut = parent.decl_ref_mut,20766 elem_ty,
20725 .val = duped,20767 &val_ptr.castTag(.aggregate).?.data[elem_ptr.index],
20726 .ty = elem_ty,20768 ptr_elem_ty,
20727 };20769 parent.decl_ref_mut,
20728 },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,20786 else => unreachable,
20731 }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 },
20732 },20806 },
20733 else => {20807 .reinterpret => |reinterpret| {
20734 if (elem_ptr.index != 0) {20808 if (!elem_ptr.elem_ty.hasWellDefinedLayout()) {
20735 // TODO include a "declared here" note for the decl20809 // Even though the parent value type has well-defined memory layout, our
20736 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{20810 // pointer type does not.
20737 elem_ptr.index,20811 return ComptimePtrMutationKit{
20738 });20812 .decl_ref_mut = parent.decl_ref_mut,
20813 .pointee = .bad_ptr_ty,
20814 .ty = elem_ptr.elem_ty,
20815 };
20739 }20816 }
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);
20740 return ComptimePtrMutationKit{20820 return ComptimePtrMutationKit{
20741 .decl_ref_mut = parent.decl_ref_mut,20821 .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 } },
20743 .ty = parent.ty,20826 .ty = parent.ty,
20744 };20827 };
20745 },20828 },
20829 .bad_decl_ty, .bad_ptr_ty => return parent,
20746 }20830 }
20747 },20831 },
20748 .field_ptr => {20832 .field_ptr => {
20749 const field_ptr = ptr_val.castTag(.field_ptr).?.data;20833 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
20750 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
20751 const field_index = @intCast(u32, field_ptr.field_index);20834 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()) {20836 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
20761 .Struct => {20837 switch (parent.pointee) {
20762 const fields = try arena.alloc(Value, parent.ty.structFieldCount());20838 .direct => |val_ptr| switch (val_ptr.tag()) {
20763 mem.set(Value, fields, Value.undef);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{20872 return beginComptimePtrMutationInner(
20768 .decl_ref_mut = parent.decl_ref_mut,20873 sema,
20769 .val = &fields[field_index],20874 block,
20770 .ty = parent.ty.structFieldType(field_index),20875 src,
20771 };20876 parent.ty.structFieldType(field_index),
20772 },20877 &payload.data.val,
20773 .Union => {20878 ptr_elem_ty,
20774 const payload = try arena.create(Value.Payload.Union);20879 parent.decl_ref_mut,
20775 payload.* = .{ .data = .{20880 );
20776 .tag = try Value.Tag.enum_field_index.create(arena, field_index),20881 },
20777 .val = Value.undef,20882 .Pointer => {
20778 } };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{20930 const payload = &val_ptr.castTag(.@"union").?.data;
20783 .decl_ref_mut = parent.decl_ref_mut,20931 payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
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 });
2079420932
20795 switch (field_index) {20933 return beginComptimePtrMutationInner(
20796 Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{20934 sema,
20797 .decl_ref_mut = parent.decl_ref_mut,20935 block,
20798 .val = &parent.val.castTag(.slice).?.data.ptr,20936 src,
20799 .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),20937 parent.ty.structFieldType(field_index),
20800 },20938 &payload.val,
20801 Value.Payload.Slice.len_index => return ComptimePtrMutationKit{20939 ptr_elem_ty,
20802 .decl_ref_mut = parent.decl_ref_mut,20940 parent.decl_ref_mut,
20803 .val = &parent.val.castTag(.slice).?.data.len,20941 );
20804 .ty = Type.usize,20942 },
20805 },20943 .slice => switch (field_index) {
20806 else => unreachable,20944 Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
20807 }20945 sema,
20808 },20946 block,
20809 else => unreachable,20947 src,
20810 }20948 parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
20811 },20949 &val_ptr.castTag(.slice).?.data.ptr,
20812 .aggregate => return ComptimePtrMutationKit{20950 ptr_elem_ty,
20813 .decl_ref_mut = parent.decl_ref_mut,20951 parent.decl_ref_mut,
20814 .val = &parent.val.castTag(.aggregate).?.data[field_index],20952 ),
20815 .ty = parent.ty.structFieldType(field_index),20953
20816 },20954 Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
20817 .@"union" => {20955 sema,
20818 // We need to set the active field of the union.20956 block,
20819 const arena = parent.beginArena(sema.mod);20957 src,
20820 defer parent.finishArena(sema.mod);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;20964 else => unreachable,
20823 payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);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);
20825 return ComptimePtrMutationKit{20972 return ComptimePtrMutationKit{
20826 .decl_ref_mut = parent.decl_ref_mut,20973 .decl_ref_mut = parent.decl_ref_mut,
20827 .val = &payload.val,20974 .pointee = .{ .reinterpret = .{
20828 .ty = parent.ty.structFieldType(field_index),20975 .val_ptr = reinterpret.val_ptr,
20976 .byte_offset = reinterpret.byte_offset + field_offset,
20977 } },
20978 .ty = parent.ty,
20829 };20979 };
20830 },20980 },
20831 .slice => switch (field_index) {20981 .bad_decl_ty, .bad_ptr_ty => return parent,
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,
20846 }20982 }
20847 },20983 },
20848 .eu_payload_ptr => {20984 .eu_payload_ptr => {
20849 const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;20985 const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
20850 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr);20986 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr, eu_ptr.container_ty);
20851 const payload_ty = parent.ty.errorUnionPayload();20987 switch (parent.pointee) {
20852 switch (parent.val.tag()) {20988 .direct => |val_ptr| {
20853 else => {20989 const payload_ty = parent.ty.errorUnionPayload();
20854 // An error union has been initialized to undefined at comptime and now we20990 switch (val_ptr.tag()) {
20855 // are for the first time setting the payload. We must change the20991 else => {
20856 // representation of the error union from `undef` to `opt_payload`.20992 // An error union has been initialized to undefined at comptime and now we
20857 const arena = parent.beginArena(sema.mod);20993 // are for the first time setting the payload. We must change the
20858 defer parent.finishArena(sema.mod);20994 // representation of the error union from `undef` to `opt_payload`.
2085920995 const arena = parent.beginArena(sema.mod);
20860 const payload = try arena.create(Value.Payload.SubValue);20996 defer parent.finishArena(sema.mod);
20861 payload.* = .{20997
20862 .base = .{ .tag = .eu_payload },20998 const payload = try arena.create(Value.Payload.SubValue);
20863 .data = Value.undef,20999 payload.* = .{
20864 };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{21006 return ComptimePtrMutationKit{
20869 .decl_ref_mut = parent.decl_ref_mut,21007 .decl_ref_mut = parent.decl_ref_mut,
20870 .val = &payload.data,21008 .pointee = .{ .direct = &payload.data },
20871 .ty = payload_ty,21009 .ty = payload_ty,
20872 };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 }
20873 },21018 },
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{
20875 .decl_ref_mut = parent.decl_ref_mut,21023 .decl_ref_mut = parent.decl_ref_mut,
20876 .val = &parent.val.castTag(.eu_payload).?.data,21024 .pointee = .bad_ptr_ty,
20877 .ty = payload_ty,21025 .ty = eu_ptr.container_ty,
20878 },21026 },
20879 }21027 }
20880 },21028 },
20881 .opt_payload_ptr => {21029 .opt_payload_ptr => {
20882 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;21030 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
20883 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr);21031 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);
20884 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);21032 switch (parent.pointee) {
20885 switch (parent.val.tag()) {21033 .direct => |val_ptr| {
20886 .undef, .null_value => {21034 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
20887 // An optional has been initialized to undefined at comptime and now we21035 switch (val_ptr.tag()) {
20888 // are for the first time setting the payload. We must change the21036 .undef, .null_value => {
20889 // representation of the optional from `undef` to `opt_payload`.21037 // An optional has been initialized to undefined at comptime and now we
20890 const arena = parent.beginArena(sema.mod);21038 // are for the first time setting the payload. We must change the
20891 defer parent.finishArena(sema.mod);21039 // representation of the optional from `undef` to `opt_payload`.
2089221040 const arena = parent.beginArena(sema.mod);
20893 const payload = try arena.create(Value.Payload.SubValue);21041 defer parent.finishArena(sema.mod);
20894 payload.* = .{
20895 .base = .{ .tag = .opt_payload },
20896 .data = Value.undef,
20897 };
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{21049 val_ptr.* = Value.initPayload(&payload.base);
20902 .decl_ref_mut = parent.decl_ref_mut,21050
20903 .val = &payload.data,21051 return ComptimePtrMutationKit{
20904 .ty = payload_ty,21052 .decl_ref_mut = parent.decl_ref_mut,
20905 };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 }
20906 },21065 },
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{
20908 .decl_ref_mut = parent.decl_ref_mut,21070 .decl_ref_mut = parent.decl_ref_mut,
20909 .val = &parent.val.castTag(.opt_payload).?.data,21071 .pointee = .bad_ptr_ty,
20910 .ty = payload_ty,21072 .ty = opt_ptr.container_ty,
20911 },21073 },
20912
20913 else => unreachable,
20914 }21074 }
20915 },21075 },
20916 .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already21076 .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
...@@ -20918,10 +21078,63 @@ fn beginComptimePtrMutation(...@@ -20918,10 +21078,63 @@ fn beginComptimePtrMutation(
20918 }21078 }
20919}21079}
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
20921const TypedValueAndOffset = struct {21136const TypedValueAndOffset = struct {
20922 tv: TypedValue,21137 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.
20925 byte_offset: usize,21138 byte_offset: usize,
20926};21139};
2092721140
...@@ -21197,7 +21410,7 @@ fn bitCast(...@@ -21197,7 +21410,7 @@ fn bitCast(
21197 return block.addBitCast(dest_ty, inst);21410 return block.addBitCast(dest_ty, inst);
21198}21411}
2119921412
21200pub fn bitCastVal(21413fn bitCastVal(
21201 sema: *Sema,21414 sema: *Sema,
21202 block: *Block,21415 block: *Block,
21203 src: LazySrcLoc,21416 src: LazySrcLoc,
test/behavior/eval.zig+17
...@@ -1252,3 +1252,20 @@ test "pass pointer to field of comptime-only type as a runtime parameter" {...@@ -1252,3 +1252,20 @@ test "pass pointer to field of comptime-only type as a runtime parameter" {
1252 };1252 };
1253 try S.doTheTest();1253 try S.doTheTest();
1254}1254}
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" {...@@ -19,7 +19,10 @@ test "casting to void with a macro" {
19}19}
2020
21test "initializer list expression" {21test "initializer list expression" {
22 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO22 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
24 try expectEqual(h.Color{27 try expectEqual(h.Color{
25 .r = 200,28 .r = 200,