aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Sema.zig825
-rw-r--r--test/behavior/eval.zig17
-rw-r--r--test/behavior/translate_c_macros.zig5
3 files changed, 540 insertions, 307 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 073f1e7e2e..bad343f941 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -19212,9 +19212,9 @@ fn elemValArray(
elem_index: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const array_ty = sema.typeOf(array);
- const array_sent = array_ty.sentinel() != null;
+ const array_sent = array_ty.sentinel();
const array_len = array_ty.arrayLen();
- const array_len_s = array_len + @boolToInt(array_sent);
+ const array_len_s = array_len + @boolToInt(array_sent != null);
const elem_ty = array_ty.childType();
if (array_len_s == 0) {
@@ -19228,8 +19228,13 @@ fn elemValArray(
if (maybe_index_val) |index_val| {
const index = @intCast(usize, index_val.toUnsignedInt(target));
+ if (array_sent) |s| {
+ if (index == array_len) {
+ return sema.addConstant(elem_ty, s);
+ }
+ }
if (index >= array_len_s) {
- const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
+ const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
}
}
@@ -19269,7 +19274,7 @@ fn elemValArray(
// Runtime check is only needed if unable to comptime check
if (maybe_index_val == null) {
const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
- const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
+ const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
}
}
@@ -20521,27 +20526,67 @@ fn storePtrVal(
operand_val: Value,
operand_ty: Type,
) !void {
- var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val);
+ var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val, operand_ty);
try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut);
- const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0);
+ switch (mut_kit.pointee) {
+ .direct => |val_ptr| {
+ if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) {
+ if (!operand_val.eql(val_ptr.*, operand_ty, sema.mod)) {
+ // TODO add note showing where default value is provided
+ return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
+ }
+ return;
+ }
+ const arena = mut_kit.beginArena(sema.mod);
+ defer mut_kit.finishArena(sema.mod);
- if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) {
- if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) {
- return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
- }
- return;
- }
+ val_ptr.* = try operand_val.copy(arena);
+ },
+ .reinterpret => |reinterpret| {
+ const target = sema.mod.getTarget();
+ const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target));
+ const buffer = try sema.gpa.alloc(u8, abi_size);
+ defer sema.gpa.free(buffer);
+ reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer);
+ operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]);
- const arena = mut_kit.beginArena(sema.mod);
- defer mut_kit.finishArena(sema.mod);
+ const arena = mut_kit.beginArena(sema.mod);
+ defer mut_kit.finishArena(sema.mod);
- mut_kit.val.* = try bitcasted_val.copy(arena);
+ reinterpret.val_ptr.* = try Value.readFromMemory(mut_kit.ty, sema.mod, buffer, arena);
+ },
+ .bad_decl_ty, .bad_ptr_ty => {
+ // TODO show the decl declaration site in a note and explain whether the decl
+ // or the pointer is the problematic type
+ 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)});
+ },
+ }
}
const ComptimePtrMutationKit = struct {
decl_ref_mut: Value.Payload.DeclRefMut.Data,
- val: *Value,
+ pointee: union(enum) {
+ /// The pointer type matches the actual comptime Value so a direct
+ /// modification is possible.
+ direct: *Value,
+ /// The largest parent Value containing pointee and having a well-defined memory layout.
+ /// This is used for bitcasting, if direct dereferencing failed.
+ reinterpret: struct {
+ val_ptr: *Value,
+ byte_offset: usize,
+ },
+ /// If the root decl could not be used as parent, this means `ty` is the type that
+ /// caused that by not having a well-defined layout.
+ /// This one means the Decl that owns the value trying to be modified does not
+ /// have a well defined memory layout.
+ bad_decl_ty,
+ /// If the root decl could not be used as parent, this means `ty` is the type that
+ /// caused that by not having a well-defined layout.
+ /// This one means the pointer type that is being stored through does not
+ /// have a well defined memory layout.
+ bad_ptr_ty,
+ },
ty: Type,
decl_arena: std.heap.ArenaAllocator = undefined,
@@ -20563,354 +20608,469 @@ fn beginComptimePtrMutation(
block: *Block,
src: LazySrcLoc,
ptr_val: Value,
+ ptr_elem_ty: Type,
) CompileError!ComptimePtrMutationKit {
-
- // 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.
-
+ const target = sema.mod.getTarget();
switch (ptr_val.tag()) {
.decl_ref_mut => {
const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
const decl = sema.mod.declPtr(decl_ref_mut.decl_index);
- return ComptimePtrMutationKit{
- .decl_ref_mut = decl_ref_mut,
- .val = &decl.val,
- .ty = decl.ty,
- };
+ return beginComptimePtrMutationInner(sema, block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut);
},
.comptime_field_ptr => {
const payload = ptr_val.castTag(.comptime_field_ptr).?.data;
const duped = try sema.arena.create(Value);
duped.* = payload.field_val;
- return ComptimePtrMutationKit{
- .decl_ref_mut = .{
- .decl_index = @intToEnum(Module.Decl.Index, 0),
- .runtime_index = .comptime_field_ptr,
- },
- .val = duped,
- .ty = payload.field_ty,
- };
+ return beginComptimePtrMutationInner(sema, block, src, payload.field_ty, duped, ptr_elem_ty, .{
+ .decl_index = @intToEnum(Module.Decl.Index, 0),
+ .runtime_index = .comptime_field_ptr,
+ });
},
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
- switch (parent.ty.zigTypeTag()) {
- .Array, .Vector => {
- const check_len = parent.ty.arrayLenIncludingSentinel();
- if (elem_ptr.index >= check_len) {
- // TODO have the parent include the decl so we can say "declared here"
- return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
- elem_ptr.index, check_len,
- });
- }
- const elem_ty = parent.ty.childType();
- switch (parent.val.tag()) {
- .undef => {
- // An array has been initialized to undefined at comptime and now we
- // are for the first time setting an element. We must change the representation
- // of the array from `undef` to `array`.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
-
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, Value.undef);
-
- parent.val.* = try Value.Tag.aggregate.create(arena, elems);
-
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .bytes => {
- // An array is memory-optimized to store a slice of bytes, but we are about
- // to modify an individual field and the representation has to change.
- // If we wanted to avoid this, there would need to be special detection
- // elsewhere to identify when writing a value to an array element that is stored
- // using the `bytes` tag, and handle it without making a call to this function.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
-
- const bytes = parent.val.castTag(.bytes).?.data;
- const dest_len = parent.ty.arrayLenIncludingSentinel();
- // bytes.len may be one greater than dest_len because of the case when
- // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
- assert(bytes.len >= dest_len);
- const elems = try arena.alloc(Value, @intCast(usize, dest_len));
- for (elems) |*elem, i| {
- elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
- }
-
- parent.val.* = try Value.Tag.aggregate.create(arena, elems);
-
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .str_lit => {
- // An array is memory-optimized to store a slice of bytes, but we are about
- // to modify an individual field and the representation has to change.
- // If we wanted to avoid this, there would need to be special detection
- // elsewhere to identify when writing a value to an array element that is stored
- // using the `str_lit` tag, and handle it without making a call to this function.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
-
- const str_lit = parent.val.castTag(.str_lit).?.data;
- const dest_len = parent.ty.arrayLenIncludingSentinel();
- const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
- const elems = try arena.alloc(Value, @intCast(usize, dest_len));
- for (bytes) |byte, i| {
- elems[i] = try Value.Tag.int_u64.create(arena, byte);
- }
- if (parent.ty.sentinel()) |sent_val| {
- assert(elems.len == bytes.len + 1);
- elems[bytes.len] = sent_val;
- }
-
- parent.val.* = try Value.Tag.aggregate.create(arena, elems);
-
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .repeated => {
- // An array is memory-optimized to store only a single element value, and
- // that value is understood to be the same for the entire length of the array.
- // However, now we want to modify an individual field and so the
- // representation has to change. If we wanted to avoid this, there would
- // need to be special detection elsewhere to identify when writing a value to an
- // array element that is stored using the `repeated` tag, and handle it
- // without making a call to this function.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
+ var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr, elem_ptr.elem_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| switch (parent.ty.zigTypeTag()) {
+ .Array, .Vector => {
+ const check_len = parent.ty.arrayLenIncludingSentinel();
+ if (elem_ptr.index >= check_len) {
+ // TODO have the parent include the decl so we can say "declared here"
+ return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
+ elem_ptr.index, check_len,
+ });
+ }
+ const elem_ty = parent.ty.childType();
+ switch (val_ptr.tag()) {
+ .undef => {
+ // An array has been initialized to undefined at comptime and now we
+ // are for the first time setting an element. We must change the representation
+ // of the array from `undef` to `array`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, Value.undef);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .bytes => {
+ // An array is memory-optimized to store a slice of bytes, but we are about
+ // to modify an individual field and the representation has to change.
+ // If we wanted to avoid this, there would need to be special detection
+ // elsewhere to identify when writing a value to an array element that is stored
+ // using the `bytes` tag, and handle it without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const bytes = val_ptr.castTag(.bytes).?.data;
+ const dest_len = parent.ty.arrayLenIncludingSentinel();
+ // bytes.len may be one greater than dest_len because of the case when
+ // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
+ assert(bytes.len >= dest_len);
+ const elems = try arena.alloc(Value, @intCast(usize, dest_len));
+ for (elems) |*elem, i| {
+ elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
+ }
- const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, repeated_val);
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
- parent.val.* = try Value.Tag.aggregate.create(arena, elems);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .str_lit => {
+ // An array is memory-optimized to store a slice of bytes, but we are about
+ // to modify an individual field and the representation has to change.
+ // If we wanted to avoid this, there would need to be special detection
+ // elsewhere to identify when writing a value to an array element that is stored
+ // using the `str_lit` tag, and handle it without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const str_lit = val_ptr.castTag(.str_lit).?.data;
+ const dest_len = parent.ty.arrayLenIncludingSentinel();
+ const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
+ const elems = try arena.alloc(Value, @intCast(usize, dest_len));
+ for (bytes) |byte, i| {
+ elems[i] = try Value.Tag.int_u64.create(arena, byte);
+ }
+ if (parent.ty.sentinel()) |sent_val| {
+ assert(elems.len == bytes.len + 1);
+ elems[bytes.len] = sent_val;
+ }
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
- .aggregate => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index],
- .ty = elem_ty,
- },
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .repeated => {
+ // An array is memory-optimized to store only a single element value, and
+ // that value is understood to be the same for the entire length of the array.
+ // However, now we want to modify an individual field and so the
+ // representation has to change. If we wanted to avoid this, there would
+ // need to be special detection elsewhere to identify when writing a value to an
+ // array element that is stored using the `repeated` tag, and handle it
+ // without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena);
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, repeated_val);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
- .the_only_possible_value => {
- const duped = try sema.arena.create(Value);
- duped.* = Value.initTag(.the_only_possible_value);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = duped,
- .ty = elem_ty,
- };
- },
+ .aggregate => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &val_ptr.castTag(.aggregate).?.data[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ .the_only_possible_value => {
+ const duped = try sema.arena.create(Value);
+ duped.* = Value.initTag(.the_only_possible_value);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ duped,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
- else => unreachable,
- }
+ else => unreachable,
+ }
+ },
+ else => {
+ if (elem_ptr.index != 0) {
+ // TODO include a "declared here" note for the decl
+ return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
+ elem_ptr.index,
+ });
+ }
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty,
+ val_ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
},
- else => {
- if (elem_ptr.index != 0) {
- // TODO include a "declared here" note for the decl
- return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
- elem_ptr.index,
- });
+ .reinterpret => |reinterpret| {
+ if (!elem_ptr.elem_ty.hasWellDefinedLayout()) {
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .bad_ptr_ty,
+ .ty = elem_ptr.elem_ty,
+ };
}
+
+ const elem_abi_size_u64 = try sema.typeAbiSize(block, src, elem_ptr.elem_ty);
+ const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = parent.val,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = reinterpret.val_ptr,
+ .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_ptr.index,
+ } },
.ty = parent.ty,
};
},
+ .bad_decl_ty, .bad_ptr_ty => return parent,
}
},
.field_ptr => {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
const field_index = @intCast(u32, field_ptr.field_index);
- switch (parent.val.tag()) {
- .undef => {
- // A struct or union has been initialized to undefined at comptime and now we
- // are for the first time setting a field. We must change the representation
- // of the struct/union from `undef` to `struct`/`union`.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
- switch (parent.ty.zigTypeTag()) {
- .Struct => {
- const fields = try arena.alloc(Value, parent.ty.structFieldCount());
- mem.set(Value, fields, Value.undef);
+ var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| switch (val_ptr.tag()) {
+ .undef => {
+ // A struct or union has been initialized to undefined at comptime and now we
+ // are for the first time setting a field. We must change the representation
+ // of the struct/union from `undef` to `struct`/`union`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ switch (parent.ty.zigTypeTag()) {
+ .Struct => {
+ const fields = try arena.alloc(Value, parent.ty.structFieldCount());
+ mem.set(Value, fields, Value.undef);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &fields[field_index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .Union => {
+ const payload = try arena.create(Value.Payload.Union);
+ payload.* = .{ .data = .{
+ .tag = try Value.Tag.enum_field_index.create(arena, field_index),
+ .val = Value.undef,
+ } };
- parent.val.* = try Value.Tag.aggregate.create(arena, fields);
+ val_ptr.* = Value.initPayload(&payload.base);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &fields[field_index],
- .ty = parent.ty.structFieldType(field_index),
- };
- },
- .Union => {
- const payload = try arena.create(Value.Payload.Union);
- payload.* = .{ .data = .{
- .tag = try Value.Tag.enum_field_index.create(arena, field_index),
- .val = Value.undef,
- } };
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &payload.data.val,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .Pointer => {
+ assert(parent.ty.isSlice());
+ val_ptr.* = try Value.Tag.slice.create(arena, .{
+ .ptr = Value.undef,
+ .len = Value.undef,
+ });
+
+ switch (field_index) {
+ Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
+ &val_ptr.castTag(.slice).?.data.ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+ Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ Type.usize,
+ &val_ptr.castTag(.slice).?.data.len,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ else => unreachable,
+ }
+ },
+ else => unreachable,
+ }
+ },
+ .aggregate => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &val_ptr.castTag(.aggregate).?.data[field_index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
- parent.val.* = Value.initPayload(&payload.base);
+ .@"union" => {
+ // We need to set the active field of the union.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.data.val,
- .ty = parent.ty.structFieldType(field_index),
- };
- },
- .Pointer => {
- assert(parent.ty.isSlice());
- parent.val.* = try Value.Tag.slice.create(arena, .{
- .ptr = Value.undef,
- .len = Value.undef,
- });
+ const payload = &val_ptr.castTag(.@"union").?.data;
+ payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
- switch (field_index) {
- Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.slice).?.data.ptr,
- .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
- },
- Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.slice).?.data.len,
- .ty = Type.usize,
- },
- else => unreachable,
- }
- },
- else => unreachable,
- }
- },
- .aggregate => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.aggregate).?.data[field_index],
- .ty = parent.ty.structFieldType(field_index),
- },
- .@"union" => {
- // We need to set the active field of the union.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &payload.val,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .slice => switch (field_index) {
+ Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
+ &val_ptr.castTag(.slice).?.data.ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ Type.usize,
+ &val_ptr.castTag(.slice).?.data.len,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
- const payload = &parent.val.castTag(.@"union").?.data;
- payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
+ else => unreachable,
+ },
+ else => unreachable,
+ },
+ .reinterpret => |reinterpret| {
+ const field_offset_u64 = field_ptr.container_ty.structFieldOffset(field_index, target);
+ const field_offset = try sema.usizeCast(block, src, field_offset_u64);
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.val,
- .ty = parent.ty.structFieldType(field_index),
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = reinterpret.val_ptr,
+ .byte_offset = reinterpret.byte_offset + field_offset,
+ } },
+ .ty = parent.ty,
};
},
- .slice => switch (field_index) {
- Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.slice).?.data.ptr,
- .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
- },
- Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.slice).?.data.len,
- .ty = Type.usize,
- },
- else => unreachable,
- },
-
- else => unreachable,
+ .bad_decl_ty, .bad_ptr_ty => return parent,
}
},
.eu_payload_ptr => {
const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr);
- const payload_ty = parent.ty.errorUnionPayload();
- switch (parent.val.tag()) {
- else => {
- // An error union has been initialized to undefined at comptime and now we
- // are for the first time setting the payload. We must change the
- // representation of the error union from `undef` to `opt_payload`.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
-
- const payload = try arena.create(Value.Payload.SubValue);
- payload.* = .{
- .base = .{ .tag = .eu_payload },
- .data = Value.undef,
- };
+ var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr, eu_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| {
+ const payload_ty = parent.ty.errorUnionPayload();
+ switch (val_ptr.tag()) {
+ else => {
+ // An error union has been initialized to undefined at comptime and now we
+ // are for the first time setting the payload. We must change the
+ // representation of the error union from `undef` to `opt_payload`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const payload = try arena.create(Value.Payload.SubValue);
+ payload.* = .{
+ .base = .{ .tag = .eu_payload },
+ .data = Value.undef,
+ };
- parent.val.* = Value.initPayload(&payload.base);
+ val_ptr.* = Value.initPayload(&payload.base);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.data,
- .ty = payload_ty,
- };
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &payload.data },
+ .ty = payload_ty,
+ };
+ },
+ .eu_payload => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
+ .ty = payload_ty,
+ },
+ }
},
- .eu_payload => return ComptimePtrMutationKit{
+ .bad_decl_ty, .bad_ptr_ty => return parent,
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ .reinterpret => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.eu_payload).?.data,
- .ty = payload_ty,
+ .pointee = .bad_ptr_ty,
+ .ty = eu_ptr.container_ty,
},
}
},
.opt_payload_ptr => {
const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr);
- const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
- switch (parent.val.tag()) {
- .undef, .null_value => {
- // An optional has been initialized to undefined at comptime and now we
- // are for the first time setting the payload. We must change the
- // representation of the optional from `undef` to `opt_payload`.
- const arena = parent.beginArena(sema.mod);
- defer parent.finishArena(sema.mod);
-
- const payload = try arena.create(Value.Payload.SubValue);
- payload.* = .{
- .base = .{ .tag = .opt_payload },
- .data = Value.undef,
- };
+ var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| {
+ const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
+ switch (val_ptr.tag()) {
+ .undef, .null_value => {
+ // An optional has been initialized to undefined at comptime and now we
+ // are for the first time setting the payload. We must change the
+ // representation of the optional from `undef` to `opt_payload`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
- parent.val.* = Value.initPayload(&payload.base);
+ const payload = try arena.create(Value.Payload.SubValue);
+ payload.* = .{
+ .base = .{ .tag = .opt_payload },
+ .data = Value.undef,
+ };
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.data,
- .ty = payload_ty,
- };
+ val_ptr.* = Value.initPayload(&payload.base);
+
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &payload.data },
+ .ty = payload_ty,
+ };
+ },
+ .opt_payload => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
+ .ty = payload_ty,
+ },
+
+ else => unreachable,
+ }
},
- .opt_payload => return ComptimePtrMutationKit{
+ .bad_decl_ty, .bad_ptr_ty => return parent,
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ .reinterpret => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.opt_payload).?.data,
- .ty = payload_ty,
+ .pointee = .bad_ptr_ty,
+ .ty = opt_ptr.container_ty,
},
-
- else => unreachable,
}
},
.decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
@@ -20918,10 +21078,63 @@ fn beginComptimePtrMutation(
}
}
+fn beginComptimePtrMutationInner(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ decl_ty: Type,
+ decl_val: *Value,
+ ptr_elem_ty: Type,
+ decl_ref_mut: Value.Payload.DeclRefMut.Data,
+) CompileError!ComptimePtrMutationKit {
+ const target = sema.mod.getTarget();
+ const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
+ if (coerce_ok) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .direct = decl_val },
+ .ty = decl_ty,
+ };
+ }
+
+ // Handle the case that the decl is an array and we're actually trying to point to an element.
+ if (decl_ty.isArrayOrVector()) {
+ const decl_elem_ty = decl_ty.childType();
+ if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .direct = decl_val },
+ .ty = decl_ty,
+ };
+ }
+ }
+
+ if (!decl_ty.hasWellDefinedLayout()) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .bad_decl_ty = {} },
+ .ty = decl_ty,
+ };
+ }
+ if (!ptr_elem_ty.hasWellDefinedLayout()) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .bad_ptr_ty = {} },
+ .ty = ptr_elem_ty,
+ };
+ }
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = decl_val,
+ .byte_offset = 0,
+ } },
+ .ty = decl_ty,
+ };
+}
+
const TypedValueAndOffset = struct {
tv: TypedValue,
- /// The starting byte offset of `val` from `root_val`.
- /// If the type does not have a well-defined memory layout, this is null.
byte_offset: usize,
};
@@ -21197,7 +21410,7 @@ fn bitCast(
return block.addBitCast(dest_ty, inst);
}
-pub fn bitCastVal(
+fn bitCastVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig
index e56ea0cad5..0ea3a33990 100644
--- a/test/behavior/eval.zig
+++ b/test/behavior/eval.zig
@@ -1252,3 +1252,20 @@ test "pass pointer to field of comptime-only type as a runtime parameter" {
};
try S.doTheTest();
}
+
+test "comptime write through extern struct reinterpreted as array" {
+ comptime {
+ const S = extern struct {
+ a: u8,
+ b: u8,
+ c: u8,
+ };
+ var s: S = undefined;
+ @ptrCast(*[3]u8, &s)[0] = 1;
+ @ptrCast(*[3]u8, &s)[1] = 2;
+ @ptrCast(*[3]u8, &s)[2] = 3;
+ assert(s.a == 1);
+ assert(s.b == 2);
+ assert(s.c == 3);
+ }
+}
diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig
index ec2695b4a0..e44996b990 100644
--- a/test/behavior/translate_c_macros.zig
+++ b/test/behavior/translate_c_macros.zig
@@ -19,7 +19,10 @@ test "casting to void with a macro" {
}
test "initializer list expression" {
- if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
try expectEqual(h.Color{
.r = 200,