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...@@ -2023,5 +2023,22 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
2023 }) catch return;2023 }) catch return;
2024 }2024 }
2025 }2025 }
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 }
2026 };2043 };
2027}2044}
lib/std/mem.zig+1-1
...@@ -268,7 +268,7 @@ pub fn zeroes(comptime T: type) T {...@@ -268,7 +268,7 @@ pub fn zeroes(comptime T: type) T {
268 },268 },
269 .Struct => |struct_info| {269 .Struct => |struct_info| {
270 if (@sizeOf(T) == 0) return T{};270 if (@sizeOf(T) == 0) return T{};
271 if (comptime meta.containerLayout(T) == .Extern) {271 if (struct_info.layout == .Extern) {
272 var item: T = undefined;272 var item: T = undefined;
273 set(u8, asBytes(&item), 0);273 set(u8, asBytes(&item), 0);
274 return item;274 return item;
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,