mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
577 lines
26 KiB
Zig
577 lines
26 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
const Zcu = @import("Zcu.zig");
|
|
const InternPool = @import("InternPool.zig");
|
|
const Type = @import("Type.zig");
|
|
const Value = @import("Value.zig");
|
|
|
|
/// We use a tagged union here because while it wastes a few bytes for some tags, having a fixed
|
|
/// size for the type makes the common `aggregate` representation more efficient.
|
|
/// For aggregates, the sentinel value, if any, *is* stored.
|
|
pub const MutableValue = union(enum) {
|
|
/// An interned value.
|
|
interned: InternPool.Index,
|
|
/// An error union value which is a payload (not an error).
|
|
eu_payload: SubValue,
|
|
/// An optional value which is a payload (not `null`).
|
|
opt_payload: SubValue,
|
|
/// An aggregate consisting of a single repeated value.
|
|
repeated: SubValue,
|
|
/// An aggregate of `u8` consisting of "plain" bytes (no lazy or undefined elements).
|
|
bytes: Bytes,
|
|
/// An aggregate with arbitrary sub-values.
|
|
aggregate: Aggregate,
|
|
/// A slice, containing a pointer and length.
|
|
slice: Slice,
|
|
/// An instance of a union.
|
|
un: Union,
|
|
|
|
pub const SubValue = struct {
|
|
ty: InternPool.Index,
|
|
child: *MutableValue,
|
|
};
|
|
pub const Bytes = struct {
|
|
ty: InternPool.Index,
|
|
data: []u8,
|
|
};
|
|
pub const Aggregate = struct {
|
|
ty: InternPool.Index,
|
|
elems: []MutableValue,
|
|
};
|
|
pub const Slice = struct {
|
|
ty: InternPool.Index,
|
|
/// Must have the appropriate many-ptr type.
|
|
/// TODO: we want this to be an `InternPool.Index`, but `Sema.beginComptimePtrMutation` doesn't support it.
|
|
ptr: *MutableValue,
|
|
/// Must be of type `usize`.
|
|
/// TODO: we want this to be an `InternPool.Index`, but `Sema.beginComptimePtrMutation` doesn't support it.
|
|
len: *MutableValue,
|
|
};
|
|
pub const Union = struct {
|
|
ty: InternPool.Index,
|
|
tag: InternPool.Index,
|
|
payload: *MutableValue,
|
|
};
|
|
|
|
pub fn intern(mv: MutableValue, pt: Zcu.PerThread, arena: Allocator) Allocator.Error!Value {
|
|
return Value.fromInterned(switch (mv) {
|
|
.interned => |ip_index| ip_index,
|
|
.eu_payload => |sv| try pt.intern(.{ .error_union = .{
|
|
.ty = sv.ty,
|
|
.val = .{ .payload = (try sv.child.intern(pt, arena)).toIntern() },
|
|
} }),
|
|
.opt_payload => |sv| try pt.intern(.{ .opt = .{
|
|
.ty = sv.ty,
|
|
.val = (try sv.child.intern(pt, arena)).toIntern(),
|
|
} }),
|
|
.repeated => |sv| try pt.intern(.{ .aggregate = .{
|
|
.ty = sv.ty,
|
|
.storage = .{ .repeated_elem = (try sv.child.intern(pt, arena)).toIntern() },
|
|
} }),
|
|
.bytes => |b| try pt.intern(.{ .aggregate = .{
|
|
.ty = b.ty,
|
|
.storage = .{ .bytes = try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, pt.tid, b.data, .maybe_embedded_nulls) },
|
|
} }),
|
|
.aggregate => |a| {
|
|
const elems = try arena.alloc(InternPool.Index, a.elems.len);
|
|
for (a.elems, elems) |mut_elem, *interned_elem| {
|
|
interned_elem.* = (try mut_elem.intern(pt, arena)).toIntern();
|
|
}
|
|
return Value.fromInterned(try pt.intern(.{ .aggregate = .{
|
|
.ty = a.ty,
|
|
.storage = .{ .elems = elems },
|
|
} }));
|
|
},
|
|
.slice => |s| try pt.intern(.{ .slice = .{
|
|
.ty = s.ty,
|
|
.ptr = (try s.ptr.intern(pt, arena)).toIntern(),
|
|
.len = (try s.len.intern(pt, arena)).toIntern(),
|
|
} }),
|
|
.un => |u| try pt.internUnion(.{
|
|
.ty = u.ty,
|
|
.tag = u.tag,
|
|
.val = (try u.payload.intern(pt, arena)).toIntern(),
|
|
}),
|
|
});
|
|
}
|
|
|
|
/// Un-interns the top level of this `MutableValue`, if applicable.
|
|
/// * Non-error error unions use `eu_payload`
|
|
/// * Non-null optionals use `eu_payload
|
|
/// * Slices use `slice`
|
|
/// * Unions use `un`
|
|
/// * Aggregates use `repeated` or `bytes` or `aggregate`
|
|
/// If `!allow_bytes`, the `bytes` representation will not be used.
|
|
/// If `!allow_repeated`, the `repeated` representation will not be used.
|
|
pub fn unintern(
|
|
mv: *MutableValue,
|
|
pt: Zcu.PerThread,
|
|
arena: Allocator,
|
|
allow_bytes: bool,
|
|
allow_repeated: bool,
|
|
) Allocator.Error!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
switch (mv.*) {
|
|
.interned => |ip_index| switch (ip.indexToKey(ip_index)) {
|
|
.opt => |opt| if (opt.val != .none) {
|
|
const mut_payload = try arena.create(MutableValue);
|
|
mut_payload.* = .{ .interned = opt.val };
|
|
mv.* = .{ .opt_payload = .{
|
|
.ty = opt.ty,
|
|
.child = mut_payload,
|
|
} };
|
|
},
|
|
.error_union => |eu| switch (eu.val) {
|
|
.err_name => {},
|
|
.payload => |payload| {
|
|
const mut_payload = try arena.create(MutableValue);
|
|
mut_payload.* = .{ .interned = payload };
|
|
mv.* = .{ .eu_payload = .{
|
|
.ty = eu.ty,
|
|
.child = mut_payload,
|
|
} };
|
|
},
|
|
},
|
|
.slice => |slice| {
|
|
const ptr = try arena.create(MutableValue);
|
|
const len = try arena.create(MutableValue);
|
|
ptr.* = .{ .interned = slice.ptr };
|
|
len.* = .{ .interned = slice.len };
|
|
mv.* = .{ .slice = .{
|
|
.ty = slice.ty,
|
|
.ptr = ptr,
|
|
.len = len,
|
|
} };
|
|
},
|
|
.un => |un| {
|
|
const payload = try arena.create(MutableValue);
|
|
payload.* = .{ .interned = un.val };
|
|
mv.* = .{ .un = .{
|
|
.ty = un.ty,
|
|
.tag = un.tag,
|
|
.payload = payload,
|
|
} };
|
|
},
|
|
.aggregate => |agg| switch (agg.storage) {
|
|
.bytes => |bytes| {
|
|
const len: usize = @intCast(ip.aggregateTypeLenIncludingSentinel(agg.ty));
|
|
assert(ip.childType(agg.ty) == .u8_type);
|
|
if (allow_bytes) {
|
|
const arena_bytes = try arena.alloc(u8, len);
|
|
@memcpy(arena_bytes, bytes.toSlice(len, ip));
|
|
mv.* = .{ .bytes = .{
|
|
.ty = agg.ty,
|
|
.data = arena_bytes,
|
|
} };
|
|
} else {
|
|
const mut_elems = try arena.alloc(MutableValue, len);
|
|
for (bytes.toSlice(len, ip), mut_elems) |b, *mut_elem| {
|
|
mut_elem.* = .{ .interned = try pt.intern(.{ .int = .{
|
|
.ty = .u8_type,
|
|
.storage = .{ .u64 = b },
|
|
} }) };
|
|
}
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = agg.ty,
|
|
.elems = mut_elems,
|
|
} };
|
|
}
|
|
},
|
|
.elems => |elems| {
|
|
assert(elems.len == ip.aggregateTypeLenIncludingSentinel(agg.ty));
|
|
const mut_elems = try arena.alloc(MutableValue, elems.len);
|
|
for (elems, mut_elems) |interned_elem, *mut_elem| {
|
|
mut_elem.* = .{ .interned = interned_elem };
|
|
}
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = agg.ty,
|
|
.elems = mut_elems,
|
|
} };
|
|
},
|
|
.repeated_elem => |val| {
|
|
if (allow_repeated) {
|
|
const repeated_val = try arena.create(MutableValue);
|
|
repeated_val.* = .{ .interned = val };
|
|
mv.* = .{ .repeated = .{
|
|
.ty = agg.ty,
|
|
.child = repeated_val,
|
|
} };
|
|
} else {
|
|
const len = ip.aggregateTypeLenIncludingSentinel(agg.ty);
|
|
const mut_elems = try arena.alloc(MutableValue, @intCast(len));
|
|
@memset(mut_elems, .{ .interned = val });
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = agg.ty,
|
|
.elems = mut_elems,
|
|
} };
|
|
}
|
|
},
|
|
},
|
|
.undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {
|
|
.@"struct", .array, .vector => |type_tag| {
|
|
const ty = Type.fromInterned(ty_ip);
|
|
const opt_sent = ty.sentinel(zcu);
|
|
if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) {
|
|
const len_no_sent = ip.aggregateTypeLen(ty_ip);
|
|
const elems = try arena.alloc(MutableValue, @intCast(len_no_sent + @intFromBool(opt_sent != null)));
|
|
switch (type_tag) {
|
|
.array, .vector => {
|
|
const elem_ty = ip.childType(ty_ip);
|
|
const undef_elem = try pt.intern(.{ .undef = elem_ty });
|
|
@memset(elems[0..@intCast(len_no_sent)], .{ .interned = undef_elem });
|
|
},
|
|
.@"struct" => for (elems[0..@intCast(len_no_sent)], 0..) |*mut_elem, i| {
|
|
const field_ty = ty.fieldType(i, zcu).toIntern();
|
|
mut_elem.* = .{ .interned = try pt.intern(.{ .undef = field_ty }) };
|
|
},
|
|
else => unreachable,
|
|
}
|
|
if (opt_sent) |s| elems[@intCast(len_no_sent)] = .{ .interned = s.toIntern() };
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = ty_ip,
|
|
.elems = elems,
|
|
} };
|
|
} else {
|
|
const repeated_val = try arena.create(MutableValue);
|
|
repeated_val.* = .{
|
|
.interned = try pt.intern(.{ .undef = ip.childType(ty_ip) }),
|
|
};
|
|
mv.* = .{ .repeated = .{
|
|
.ty = ty_ip,
|
|
.child = repeated_val,
|
|
} };
|
|
}
|
|
},
|
|
.@"union" => {
|
|
const payload = try arena.create(MutableValue);
|
|
const backing_ty = try Type.fromInterned(ty_ip).unionBackingType(pt);
|
|
payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
|
|
mv.* = .{ .un = .{
|
|
.ty = ty_ip,
|
|
.tag = .none,
|
|
.payload = payload,
|
|
} };
|
|
},
|
|
.pointer => {
|
|
const ptr_ty = ip.indexToKey(ty_ip).ptr_type;
|
|
if (ptr_ty.flags.size != .slice) return;
|
|
const ptr = try arena.create(MutableValue);
|
|
const len = try arena.create(MutableValue);
|
|
ptr.* = .{ .interned = try pt.intern(.{ .undef = ip.slicePtrType(ty_ip) }) };
|
|
len.* = .{ .interned = .undef_usize };
|
|
mv.* = .{ .slice = .{
|
|
.ty = ty_ip,
|
|
.ptr = ptr,
|
|
.len = len,
|
|
} };
|
|
},
|
|
else => {},
|
|
},
|
|
else => {},
|
|
},
|
|
.bytes => |bytes| if (!allow_bytes) {
|
|
const elems = try arena.alloc(MutableValue, bytes.data.len);
|
|
for (bytes.data, elems) |byte, *interned_byte| {
|
|
interned_byte.* = .{ .interned = try pt.intern(.{ .int = .{
|
|
.ty = .u8_type,
|
|
.storage = .{ .u64 = byte },
|
|
} }) };
|
|
}
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = bytes.ty,
|
|
.elems = elems,
|
|
} };
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
/// Get a pointer to the `MutableValue` associated with a field/element.
|
|
/// The returned pointer can be safety mutated through to modify the field value.
|
|
/// The returned pointer is valid until the representation of `mv` changes.
|
|
pub fn elem(
|
|
mv: *MutableValue,
|
|
pt: Zcu.PerThread,
|
|
arena: Allocator,
|
|
field_idx: usize,
|
|
) Allocator.Error!*MutableValue {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
// Convert to the `aggregate` representation.
|
|
switch (mv.*) {
|
|
.eu_payload, .opt_payload, .un => unreachable,
|
|
.interned => {
|
|
try mv.unintern(pt, arena, false, false);
|
|
},
|
|
.bytes => |bytes| {
|
|
const elems = try arena.alloc(MutableValue, bytes.data.len);
|
|
for (bytes.data, elems) |byte, *interned_byte| {
|
|
interned_byte.* = .{ .interned = try pt.intern(.{ .int = .{
|
|
.ty = .u8_type,
|
|
.storage = .{ .u64 = byte },
|
|
} }) };
|
|
}
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = bytes.ty,
|
|
.elems = elems,
|
|
} };
|
|
},
|
|
.repeated => |repeated| {
|
|
const len = ip.aggregateTypeLenIncludingSentinel(repeated.ty);
|
|
const elems = try arena.alloc(MutableValue, @intCast(len));
|
|
@memset(elems, repeated.child.*);
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = repeated.ty,
|
|
.elems = elems,
|
|
} };
|
|
},
|
|
.slice, .aggregate => {},
|
|
}
|
|
switch (mv.*) {
|
|
.aggregate => |*agg| return &agg.elems[field_idx],
|
|
.slice => |*slice| return switch (field_idx) {
|
|
Value.slice_ptr_index => slice.ptr,
|
|
Value.slice_len_index => slice.len,
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// Modify a single field of a `MutableValue` which represents an aggregate or slice, leaving others
|
|
/// untouched. When an entire field must be modified, this should be used in preference to `elemPtr`
|
|
/// to allow for an optimal representation.
|
|
/// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`.
|
|
pub fn setElem(
|
|
mv: *MutableValue,
|
|
pt: Zcu.PerThread,
|
|
arena: Allocator,
|
|
field_idx: usize,
|
|
field_val: MutableValue,
|
|
) Allocator.Error!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const is_trivial_int = field_val.isTrivialInt(zcu);
|
|
try mv.unintern(pt, arena, is_trivial_int, true);
|
|
switch (mv.*) {
|
|
.interned,
|
|
.eu_payload,
|
|
.opt_payload,
|
|
.un,
|
|
=> unreachable,
|
|
.slice => |*s| switch (field_idx) {
|
|
Value.slice_ptr_index => s.ptr.* = field_val,
|
|
Value.slice_len_index => s.len.* = field_val,
|
|
else => unreachable,
|
|
},
|
|
.bytes => |b| {
|
|
assert(is_trivial_int);
|
|
assert(field_val.typeOf(zcu).toIntern() == .u8_type);
|
|
b.data[field_idx] = @intCast(Value.fromInterned(field_val.interned).toUnsignedInt(zcu));
|
|
},
|
|
.repeated => |r| {
|
|
if (field_val.eqlTrivial(r.child.*)) return;
|
|
// We must switch to either the `aggregate` or the `bytes` representation.
|
|
const len_inc_sent = ip.aggregateTypeLenIncludingSentinel(r.ty);
|
|
if (Type.fromInterned(r.ty).zigTypeTag(zcu) != .@"struct" and
|
|
is_trivial_int and
|
|
Type.fromInterned(r.ty).childType(zcu).toIntern() == .u8_type and
|
|
r.child.isTrivialInt(zcu))
|
|
{
|
|
// We can use the `bytes` representation.
|
|
const bytes = try arena.alloc(u8, @intCast(len_inc_sent));
|
|
const repeated_byte = Value.fromInterned(r.child.interned).toUnsignedInt(zcu);
|
|
@memset(bytes, @intCast(repeated_byte));
|
|
bytes[field_idx] = @intCast(Value.fromInterned(field_val.interned).toUnsignedInt(zcu));
|
|
mv.* = .{ .bytes = .{
|
|
.ty = r.ty,
|
|
.data = bytes,
|
|
} };
|
|
} else {
|
|
// We must use the `aggregate` representation.
|
|
const mut_elems = try arena.alloc(MutableValue, @intCast(len_inc_sent));
|
|
@memset(mut_elems, r.child.*);
|
|
mut_elems[field_idx] = field_val;
|
|
mv.* = .{ .aggregate = .{
|
|
.ty = r.ty,
|
|
.elems = mut_elems,
|
|
} };
|
|
}
|
|
},
|
|
.aggregate => |a| {
|
|
a.elems[field_idx] = field_val;
|
|
const is_struct = Type.fromInterned(a.ty).zigTypeTag(zcu) == .@"struct";
|
|
// Attempt to switch to a more efficient representation.
|
|
const is_repeated = for (a.elems) |e| {
|
|
if (!e.eqlTrivial(field_val)) break false;
|
|
} else true;
|
|
if (!is_struct and is_repeated) {
|
|
// Switch to `repeated` repr
|
|
const mut_repeated = try arena.create(MutableValue);
|
|
mut_repeated.* = field_val;
|
|
mv.* = .{ .repeated = .{
|
|
.ty = a.ty,
|
|
.child = mut_repeated,
|
|
} };
|
|
} else if (!is_struct and is_trivial_int and Type.fromInterned(a.ty).childType(zcu).toIntern() == .u8_type) {
|
|
// See if we can switch to `bytes` repr
|
|
for (a.elems) |e| {
|
|
switch (e) {
|
|
else => break,
|
|
.interned => |ip_index| switch (ip.indexToKey(ip_index)) {
|
|
else => break,
|
|
.int => |int| switch (int.storage) {
|
|
.u64, .i64, .big_int => {},
|
|
.lazy_align, .lazy_size => break,
|
|
},
|
|
},
|
|
}
|
|
} else {
|
|
const bytes = try arena.alloc(u8, a.elems.len);
|
|
for (a.elems, bytes) |elem_val, *b| {
|
|
b.* = @intCast(Value.fromInterned(elem_val.interned).toUnsignedInt(zcu));
|
|
}
|
|
mv.* = .{ .bytes = .{
|
|
.ty = a.ty,
|
|
.data = bytes,
|
|
} };
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Get the value of a single field of a `MutableValue` which represents an aggregate or slice.
|
|
/// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`.
|
|
pub fn getElem(
|
|
mv: MutableValue,
|
|
pt: Zcu.PerThread,
|
|
field_idx: usize,
|
|
) Allocator.Error!MutableValue {
|
|
return switch (mv) {
|
|
.eu_payload,
|
|
.opt_payload,
|
|
=> unreachable,
|
|
.interned => |ip_index| {
|
|
const ty = Type.fromInterned(pt.zcu.intern_pool.typeOf(ip_index));
|
|
switch (ty.zigTypeTag(pt.zcu)) {
|
|
.array, .vector => return .{ .interned = (try Value.fromInterned(ip_index).elemValue(pt, field_idx)).toIntern() },
|
|
.@"struct", .@"union" => return .{ .interned = (try Value.fromInterned(ip_index).fieldValue(pt, field_idx)).toIntern() },
|
|
.pointer => {
|
|
assert(ty.isSlice(pt.zcu));
|
|
return switch (field_idx) {
|
|
Value.slice_ptr_index => .{ .interned = Value.fromInterned(ip_index).slicePtr(pt.zcu).toIntern() },
|
|
Value.slice_len_index => .{ .interned = switch (pt.zcu.intern_pool.indexToKey(ip_index)) {
|
|
.undef => .undef_usize,
|
|
.slice => |s| s.len,
|
|
else => unreachable,
|
|
} },
|
|
else => unreachable,
|
|
};
|
|
},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
.un => |un| {
|
|
// TODO assert the tag is correct
|
|
return un.payload.*;
|
|
},
|
|
.slice => |s| switch (field_idx) {
|
|
Value.slice_ptr_index => s.ptr.*,
|
|
Value.slice_len_index => s.len.*,
|
|
else => unreachable,
|
|
},
|
|
.bytes => |b| .{ .interned = try pt.intern(.{ .int = .{
|
|
.ty = .u8_type,
|
|
.storage = .{ .u64 = b.data[field_idx] },
|
|
} }) },
|
|
.repeated => |r| r.child.*,
|
|
.aggregate => |a| a.elems[field_idx],
|
|
};
|
|
}
|
|
|
|
fn isTrivialInt(mv: MutableValue, zcu: *Zcu) bool {
|
|
return switch (mv) {
|
|
else => false,
|
|
.interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) {
|
|
else => false,
|
|
.int => |int| switch (int.storage) {
|
|
.u64, .i64, .big_int => true,
|
|
.lazy_align, .lazy_size => false,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn typeOf(mv: MutableValue, zcu: *Zcu) Type {
|
|
return switch (mv) {
|
|
.interned => |ip_index| Type.fromInterned(zcu.intern_pool.typeOf(ip_index)),
|
|
inline else => |x| Type.fromInterned(x.ty),
|
|
};
|
|
}
|
|
|
|
pub fn unpackOptional(mv: MutableValue, zcu: *Zcu) union(enum) {
|
|
undef,
|
|
null,
|
|
payload: MutableValue,
|
|
} {
|
|
return switch (mv) {
|
|
.opt_payload => |pl| return .{ .payload = pl.child.* },
|
|
.interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) {
|
|
.undef => return .undef,
|
|
.opt => |opt| if (opt.val == .none) .null else .{ .payload = .{ .interned = opt.val } },
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn unpackErrorUnion(mv: MutableValue, zcu: *Zcu) union(enum) {
|
|
undef,
|
|
err: InternPool.NullTerminatedString,
|
|
payload: MutableValue,
|
|
} {
|
|
return switch (mv) {
|
|
.eu_payload => |pl| return .{ .payload = pl.child.* },
|
|
.interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) {
|
|
.undef => return .undef,
|
|
.error_union => |eu| switch (eu.val) {
|
|
.err_name => |name| .{ .err = name },
|
|
.payload => |pl| .{ .payload = .{ .interned = pl } },
|
|
},
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Fast equality checking which may return false negatives.
|
|
/// Used for deciding when to switch aggregate representations without fully
|
|
/// interning many values.
|
|
fn eqlTrivial(a: MutableValue, b: MutableValue) bool {
|
|
const Tag = @typeInfo(MutableValue).@"union".tag_type.?;
|
|
if (@as(Tag, a) != @as(Tag, b)) return false;
|
|
return switch (a) {
|
|
.interned => |a_ip| a_ip == b.interned,
|
|
.eu_payload => |a_pl| a_pl.ty == b.eu_payload.ty and a_pl.child.eqlTrivial(b.eu_payload.child.*),
|
|
.opt_payload => |a_pl| a_pl.ty == b.opt_payload.ty and a_pl.child.eqlTrivial(b.opt_payload.child.*),
|
|
.repeated => |a_rep| a_rep.ty == b.repeated.ty and a_rep.child.eqlTrivial(b.repeated.child.*),
|
|
.bytes => |a_bytes| a_bytes.ty == b.bytes.ty and std.mem.eql(u8, a_bytes.data, b.bytes.data),
|
|
.aggregate => |a_agg| {
|
|
const b_agg = b.aggregate;
|
|
if (a_agg.ty != b_agg.ty) return false;
|
|
if (a_agg.elems.len != b_agg.elems.len) return false;
|
|
for (a_agg.elems, b_agg.elems) |a_elem, b_elem| {
|
|
if (!a_elem.eqlTrivial(b_elem)) return false;
|
|
}
|
|
return true;
|
|
},
|
|
.slice => |a_slice| a_slice.ty == b.slice.ty and
|
|
a_slice.ptr.interned == b.slice.ptr.interned and
|
|
a_slice.len.interned == b.slice.len.interned,
|
|
.un => |a_un| a_un.ty == b.un.ty and a_un.tag == b.un.tag and a_un.payload.eqlTrivial(b.un.payload.*),
|
|
};
|
|
}
|
|
};
|