mirror of
https://github.com/ziglang/zig.git
synced 2026-01-01 19:13:16 +00:00
Merge pull request #11851 from ziglang/stage2-comptime-store
Sema: rework beginComptimePtrMutation
This commit is contained in:
commit
6e42d45dcc
@ -2023,5 +2023,22 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
|
||||
}) catch return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
t: Trace,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
if (enabled) {
|
||||
try writer.writeAll("\n");
|
||||
t.dump();
|
||||
try writer.writeAll("\n");
|
||||
} else {
|
||||
return writer.writeAll("(value tracing disabled)");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ pub fn zeroes(comptime T: type) T {
|
||||
},
|
||||
.Struct => |struct_info| {
|
||||
if (@sizeOf(T) == 0) return T{};
|
||||
if (comptime meta.containerLayout(T) == .Extern) {
|
||||
if (struct_info.layout == .Extern) {
|
||||
var item: T = undefined;
|
||||
set(u8, asBytes(&item), 0);
|
||||
return item;
|
||||
|
||||
809
src/Sema.zig
809
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);
|
||||
|
||||
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 arena = mut_kit.beginArena(sema.mod);
|
||||
defer mut_kit.finishArena(sema.mod);
|
||||
|
||||
mut_kit.val.* = try bitcasted_val.copy(arena);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
val_ptr.* = 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);
|
||||
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 = 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]);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
parent.val.* = try Value.Tag.aggregate.create(arena, elems);
|
||||
val_ptr.* = 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);
|
||||
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 = 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
parent.val.* = try Value.Tag.aggregate.create(arena, elems);
|
||||
val_ptr.* = 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);
|
||||
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 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);
|
||||
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);
|
||||
|
||||
parent.val.* = try Value.Tag.aggregate.create(arena, elems);
|
||||
val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
|
||||
|
||||
return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &elems[elem_ptr.index],
|
||||
.ty = elem_ty,
|
||||
};
|
||||
},
|
||||
return beginComptimePtrMutationInner(
|
||||
sema,
|
||||
block,
|
||||
src,
|
||||
elem_ty,
|
||||
&elems[elem_ptr.index],
|
||||
ptr_elem_ty,
|
||||
parent.decl_ref_mut,
|
||||
);
|
||||
},
|
||||
|
||||
.aggregate => return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index],
|
||||
.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 ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = duped,
|
||||
.ty = elem_ty,
|
||||
};
|
||||
},
|
||||
.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);
|
||||
|
||||
parent.val.* = try Value.Tag.aggregate.create(arena, fields);
|
||||
switch (parent.ty.zigTypeTag()) {
|
||||
.Struct => {
|
||||
const fields = try arena.alloc(Value, parent.ty.structFieldCount());
|
||||
mem.set(Value, fields, Value.undef);
|
||||
|
||||
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,
|
||||
} };
|
||||
val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
|
||||
|
||||
parent.val.* = Value.initPayload(&payload.base);
|
||||
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,
|
||||
} };
|
||||
|
||||
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,
|
||||
});
|
||||
val_ptr.* = Value.initPayload(&payload.base);
|
||||
|
||||
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,
|
||||
),
|
||||
|
||||
.@"union" => {
|
||||
// We need to set the active field of the union.
|
||||
const arena = parent.beginArena(sema.mod);
|
||||
defer parent.finishArena(sema.mod);
|
||||
|
||||
const payload = &val_ptr.castTag(.@"union").?.data;
|
||||
payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
|
||||
|
||||
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,
|
||||
),
|
||||
|
||||
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);
|
||||
|
||||
const payload = &parent.val.castTag(.@"union").?.data;
|
||||
payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
|
||||
|
||||
return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &payload.val,
|
||||
.ty = parent.ty.structFieldType(field_index),
|
||||
};
|
||||
},
|
||||
.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,
|
||||
.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,
|
||||
.pointee = .{ .reinterpret = .{
|
||||
.val_ptr = reinterpret.val_ptr,
|
||||
.byte_offset = reinterpret.byte_offset + field_offset,
|
||||
} },
|
||||
.ty = parent.ty,
|
||||
};
|
||||
},
|
||||
.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);
|
||||
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,
|
||||
};
|
||||
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);
|
||||
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);
|
||||
|
||||
const payload = try arena.create(Value.Payload.SubValue);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .opt_payload },
|
||||
.data = Value.undef,
|
||||
};
|
||||
const payload = try arena.create(Value.Payload.SubValue);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .opt_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,
|
||||
};
|
||||
},
|
||||
.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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user