aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2023-09-26 11:16:03 +0300
committerGitHub <noreply@github.com>2023-09-26 11:16:03 +0300
commitf4c884617f499b52eaecc0ef674609c774052f8f (patch)
treec24e4628c314d5e815f4b9796d0ff70335fa58a3 /src
parent2adb932ad6ee4ff3d3c640cb8fb7bf7db0ff5d74 (diff)
parent9f4649b197b720dbc168ced25eee0805d3b678b1 (diff)
downloadzig-f4c884617f499b52eaecc0ef674609c774052f8f.tar.gz
zig-f4c884617f499b52eaecc0ef674609c774052f8f.zip
Merge pull request #17215 from kcbanner/read_from_memory_union
sema: add support for unions in readFromMemory and writeToMemory
Diffstat (limited to 'src')
-rw-r--r--src/InternPool.zig6
-rw-r--r--src/Module.zig3
-rw-r--r--src/Sema.zig36
-rw-r--r--src/TypedValue.zig46
-rw-r--r--src/codegen.zig30
-rw-r--r--src/codegen/llvm.zig55
-rw-r--r--src/type.zig16
-rw-r--r--src/value.zig93
8 files changed, 204 insertions, 81 deletions
diff --git a/src/InternPool.zig b/src/InternPool.zig
index 1768833d49..d2073d3c67 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -1103,7 +1103,10 @@ pub const Key = union(enum) {
pub const Union = extern struct {
/// This is the union type; not the field type.
ty: Index,
- /// Indicates the active field.
+ /// Indicates the active field. This could be `none`, which indicates the tag is not known. `none` is only a valid value for extern and packed unions.
+ /// In those cases, the type of `val` is:
+ /// extern: a u8 array of the same byte length as the union
+ /// packed: an unsigned integer with the same bit size as the union
tag: Index,
/// The value of the active field.
val: Index,
@@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
.un => |un| {
assert(un.ty != .none);
- assert(un.tag != .none);
assert(un.val != .none);
ip.items.appendAssumeCapacity(.{
.tag = .union_value,
diff --git a/src/Module.zig b/src/Module.zig
index 0601a77320..c847fadc17 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {
.aggregate => |aggregate| for (aggregate.storage.values()) |elem|
try mod.markReferencedDeclsAlive(elem.toValue()),
.un => |un| {
- try mod.markReferencedDeclsAlive(un.tag.toValue());
+ if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue());
try mod.markReferencedDeclsAlive(un.val.toValue());
},
else => {},
@@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in
pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {
const ip = &mod.intern_pool;
+ if (enum_tag.toIntern() == .none) return null;
assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);
const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;
return enum_type.tagValueIndex(ip, enum_tag.toIntern());
diff --git a/src/Sema.zig b/src/Sema.zig
index 030e216078..9b7fedfbf6 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3861,7 +3861,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re
const air_ptr_inst = Air.refToIndex(bin_op.lhs).?;
const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?;
const union_ty = sema.typeOf(bin_op.lhs).childType(mod);
- const payload_ty = union_ty.unionFieldType(tag_val, mod);
+ const payload_ty = union_ty.unionFieldType(tag_val, mod).?;
if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| {
const new_ptr = ptr_mapping.get(air_ptr_inst).?;
const store_val = try mod.unionValue(union_ty, tag_val, payload_val);
@@ -11998,7 +11998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
const analyze_body = if (union_originally) blk: {
const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable;
- const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
+ const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
break :blk field_ty.zigTypeTag(mod) != .NoReturn;
} else true;
@@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
const analyze_body = if (union_originally) blk: {
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
- const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
+ const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
break :blk field_ty.zigTypeTag(mod) != .NoReturn;
} else true;
@@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
const analyze_body = if (union_originally)
for (items) |item| {
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
- const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
+ const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
} else false
else
@@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
case_block.wip_capture_scope = child_block.wip_capture_scope;
const analyze_body = if (union_originally) blk: {
- const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
+ const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
break :blk field_ty.zigTypeTag(mod) != .NoReturn;
} else true;
@@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag(
if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {
if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);
- const field_ty = union_ty.unionFieldType(enum_val, mod);
+ const field_ty = union_ty.unionFieldType(enum_val, mod).?;
if (field_ty.zigTypeTag(mod) == .NoReturn) {
return .bool_false;
}
@@ -27207,7 +27207,11 @@ fn unionFieldVal(
if (tag_matches) {
return Air.internedToRef(un.val);
} else {
- const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod);
+ const old_ty = if (un.tag == .none)
+ ip.typeOf(un.val).toType()
+ else
+ union_ty.unionFieldType(un.tag.toValue(), mod).?;
+
if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| {
return Air.internedToRef(new_val.toIntern());
}
@@ -29733,10 +29737,15 @@ fn storePtrVal(
error.OutOfMemory => return error.OutOfMemory,
error.ReinterpretDeclRef => unreachable,
error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
- error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
};
- reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue();
+ const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.IllDefinedMemoryLayout => unreachable,
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
+ };
+ reinterpret.val_ptr.* = (try val.intern(mut_kit.ty, mod)).toValue();
},
.bad_decl_ty, .bad_ptr_ty => {
// TODO show the decl declaration site in a note and explain whether the decl
@@ -30648,7 +30657,12 @@ fn bitCastVal(
error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
};
- return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena);
+
+ return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.IllDefinedMemoryLayout => unreachable,
+ error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}),
+ };
}
fn coerceArrayPtrToSlice(
@@ -32858,7 +32872,7 @@ fn unionToTag(
return Air.internedToRef(opv.toIntern());
}
if (try sema.resolveMaybeUndefVal(un)) |un_val| {
- return Air.internedToRef(un_val.unionTag(mod).toIntern());
+ return Air.internedToRef(un_val.unionTag(mod).?.toIntern());
}
try sema.requireRuntimeBlock(block, un_src, null);
return block.addTyOp(.get_union_tag, enum_ty, un);
diff --git a/src/TypedValue.zig b/src/TypedValue.zig
index c2be851d15..cf705cdf89 100644
--- a/src/TypedValue.zig
+++ b/src/TypedValue.zig
@@ -87,15 +87,20 @@ pub fn print(
const union_val = val.castTag(.@"union").?.data;
try writer.writeAll(".{ ");
- try print(.{
- .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
- .val = union_val.tag,
- }, writer, level - 1, mod);
- try writer.writeAll(" = ");
- try print(.{
- .ty = ty.unionFieldType(union_val.tag, mod),
- .val = union_val.val,
- }, writer, level - 1, mod);
+ if (union_val.tag.toIntern() != .none) {
+ try print(.{
+ .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
+ .val = union_val.tag,
+ }, writer, level - 1, mod);
+ try writer.writeAll(" = ");
+ const field_ty = ty.unionFieldType(union_val.tag, mod).?;
+ try print(.{
+ .ty = field_ty,
+ .val = union_val.val,
+ }, writer, level - 1, mod);
+ } else {
+ return writer.writeAll("(unknown tag)");
+ }
return writer.writeAll(" }");
},
@@ -404,15 +409,20 @@ pub fn print(
.un => |un| {
try writer.writeAll(".{ ");
if (level > 0) {
- try print(.{
- .ty = ty.unionTagTypeHypothetical(mod),
- .val = un.tag.toValue(),
- }, writer, level - 1, mod);
- try writer.writeAll(" = ");
- try print(.{
- .ty = ty.unionFieldType(un.tag.toValue(), mod),
- .val = un.val.toValue(),
- }, writer, level - 1, mod);
+ if (un.tag != .none) {
+ try print(.{
+ .ty = ty.unionTagTypeHypothetical(mod),
+ .val = un.tag.toValue(),
+ }, writer, level - 1, mod);
+ try writer.writeAll(" = ");
+ const field_ty = ty.unionFieldType(un.tag.toValue(), mod).?;
+ try print(.{
+ .ty = field_ty,
+ .val = un.val.toValue(),
+ }, writer, level - 1, mod);
+ } else {
+ try writer.writeAll("(unknown tag)");
+ }
} else try writer.writeAll("...");
return writer.writeAll(" }");
},
diff --git a/src/codegen.zig b/src/codegen.zig
index 992b51c635..738281cf55 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -583,23 +583,33 @@ pub fn generateSymbol(
}
const union_obj = mod.typeToUnion(typed_value.ty).?;
- const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
- const field_ty = union_obj.field_types.get(ip)[field_index].toType();
- if (!field_ty.hasRuntimeBits(mod)) {
- try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
+ if (un.tag != .none) {
+ const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
+ const field_ty = union_obj.field_types.get(ip)[field_index].toType();
+ if (!field_ty.hasRuntimeBits(mod)) {
+ try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
+ } else {
+ switch (try generateSymbol(bin_file, src_loc, .{
+ .ty = field_ty,
+ .val = un.val.toValue(),
+ }, code, debug_output, reloc_info)) {
+ .ok => {},
+ .fail => |em| return Result{ .fail = em },
+ }
+
+ const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
+ if (padding > 0) {
+ try code.appendNTimes(0, padding);
+ }
+ }
} else {
switch (try generateSymbol(bin_file, src_loc, .{
- .ty = field_ty,
+ .ty = ip.typeOf(un.val).toType(),
.val = un.val.toValue(),
}, code, debug_output, reloc_info)) {
.ok => {},
.fail => |em| return Result{ .fail = em },
}
-
- const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
- if (padding > 0) {
- try code.appendNTimes(0, padding);
- }
}
if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index dc2e2f3859..bfbcac1e73 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4108,25 +4108,28 @@ pub const Object = struct {
if (layout.payload_size == 0) return o.lowerValue(un.tag);
const union_obj = mod.typeToUnion(ty).?;
- const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
-
- const field_ty = union_obj.field_types.get(ip)[field_index].toType();
- if (union_obj.getLayout(ip) == .Packed) {
- if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);
- const small_int_val = try o.builder.castConst(
- if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,
- try o.lowerValue(un.val),
- try o.builder.intType(@intCast(field_ty.bitSize(mod))),
- );
- return o.builder.convConst(.unsigned, small_int_val, union_ty);
- }
+ const container_layout = union_obj.getLayout(ip);
+
+ var need_unnamed = false;
+ const payload = if (un.tag != .none) p: {
+ const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
+ const field_ty = union_obj.field_types.get(ip)[field_index].toType();
+ if (container_layout == .Packed) {
+ if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);
+ const small_int_val = try o.builder.castConst(
+ if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,
+ try o.lowerValue(un.val),
+ try o.builder.intType(@intCast(field_ty.bitSize(mod))),
+ );
+ return o.builder.convConst(.unsigned, small_int_val, union_ty);
+ }
+
+ // Sometimes we must make an unnamed struct because LLVM does
+ // not support bitcasting our payload struct to the true union payload type.
+ // Instead we use an unnamed struct and every reference to the global
+ // must pointer cast to the expected type before accessing the union.
+ need_unnamed = layout.most_aligned_field != field_index;
- // Sometimes we must make an unnamed struct because LLVM does
- // not support bitcasting our payload struct to the true union payload type.
- // Instead we use an unnamed struct and every reference to the global
- // must pointer cast to the expected type before accessing the union.
- var need_unnamed = layout.most_aligned_field != field_index;
- const payload = p: {
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
const padding_len = layout.payload_size;
break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));
@@ -4144,9 +4147,23 @@ pub const Object = struct {
try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
&.{ payload, try o.builder.undefConst(padding_ty) },
);
+ } else p: {
+ assert(layout.tag_size == 0);
+ const union_val = try o.lowerValue(un.val);
+ if (container_layout == .Packed) {
+ const bitcast_val = try o.builder.castConst(
+ .bitcast,
+ union_val,
+ try o.builder.intType(@intCast(ty.bitSize(mod))),
+ );
+ return o.builder.convConst(.unsigned, bitcast_val, union_ty);
+ }
+
+ need_unnamed = true;
+ break :p union_val;
};
- const payload_ty = payload.typeOf(&o.builder);
+ const payload_ty = payload.typeOf(&o.builder);
if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed)
try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})
else
diff --git a/src/type.zig b/src/type.zig
index 88a3a7cc43..6345f1ef6a 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -1647,8 +1647,12 @@ pub const Type = struct {
},
.union_type => |union_type| {
- if (opt_sema) |sema| try sema.resolveTypeFields(ty);
- if (ty.containerLayout(mod) != .Packed) {
+ const is_packed = ty.containerLayout(mod) == .Packed;
+ if (opt_sema) |sema| {
+ try sema.resolveTypeFields(ty);
+ if (is_packed) try sema.resolveTypeLayout(ty);
+ }
+ if (!is_packed) {
return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8;
}
const union_obj = ip.loadUnionType(union_type);
@@ -1659,6 +1663,7 @@ pub const Type = struct {
const field_ty = union_obj.field_types.get(ip)[field_index];
size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));
}
+
return size;
},
.opaque_type => unreachable,
@@ -1927,11 +1932,12 @@ pub const Type = struct {
return union_obj.enum_tag_ty.toType();
}
- pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {
+ pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type {
const ip = &mod.intern_pool;
const union_obj = mod.typeToUnion(ty).?;
- const index = mod.unionTagFieldIndex(union_obj, enum_tag).?;
- return union_obj.field_types.get(ip)[index].toType();
+ const union_fields = union_obj.field_types.get(ip);
+ const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null;
+ return union_fields[index].toType();
}
pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {
diff --git a/src/value.zig b/src/value.zig
index 2d0523f986..279f52e3e0 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -330,7 +330,7 @@ pub const Value = struct {
return mod.intern(.{ .un = .{
.ty = ty.toIntern(),
.tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod),
- .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod), mod),
+ .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod).?, mod),
} });
},
}
@@ -703,8 +703,21 @@ pub const Value = struct {
std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian);
},
.Union => switch (ty.containerLayout(mod)) {
- .Auto => return error.IllDefinedMemoryLayout,
- .Extern => return error.Unimplemented,
+ .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
+ .Extern => {
+ const union_obj = mod.typeToUnion(ty).?;
+ if (val.unionTag(mod)) |union_tag| {
+ const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
+ const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType();
+ const field_val = try val.fieldValue(mod, field_index);
+ const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
+ return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
+ } else {
+ const union_size = ty.abiSize(mod);
+ const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
+ return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..@as(usize, @intCast(union_size))]);
+ }
+ },
.Packed => {
const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
@@ -817,14 +830,18 @@ pub const Value = struct {
.Union => {
const union_obj = mod.typeToUnion(ty).?;
switch (union_obj.getLayout(ip)) {
- .Auto => unreachable, // Sema is supposed to have emitted a compile error already
- .Extern => unreachable, // Handled in non-packed writeToMemory
+ .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
.Packed => {
- const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;
- const field_type = union_obj.field_types.get(ip)[field_index].toType();
- const field_val = try val.fieldValue(mod, field_index);
-
- return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
+ if (val.unionTag(mod)) |union_tag| {
+ const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
+ const field_type = union_obj.field_types.get(ip)[field_index].toType();
+ const field_val = try val.fieldValue(mod, field_index);
+ return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
+ } else {
+ const union_bits: u16 = @intCast(ty.bitSize(mod));
+ const int_ty = try mod.intType(.unsigned, union_bits);
+ return val.unionValue(mod).writeToPackedMemory(int_ty, mod, buffer, bit_offset);
+ }
},
}
},
@@ -856,7 +873,11 @@ pub const Value = struct {
mod: *Module,
buffer: []const u8,
arena: Allocator,
- ) Allocator.Error!Value {
+ ) error{
+ IllDefinedMemoryLayout,
+ Unimplemented,
+ OutOfMemory,
+ }!Value {
const ip = &mod.intern_pool;
const target = mod.getTarget();
const endian = target.cpu.arch.endian();
@@ -966,6 +987,23 @@ pub const Value = struct {
.name = name,
} })).toValue();
},
+ .Union => switch (ty.containerLayout(mod)) {
+ .Auto => return error.IllDefinedMemoryLayout,
+ .Extern => {
+ const union_size = ty.abiSize(mod);
+ const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
+ const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
+ return (try mod.intern(.{ .un = .{
+ .ty = ty.toIntern(),
+ .tag = .none,
+ .val = val,
+ } })).toValue();
+ },
+ .Packed => {
+ const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
+ return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
+ },
+ },
.Pointer => {
assert(!ty.isSlice(mod)); // No well defined layout.
const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
@@ -987,7 +1025,7 @@ pub const Value = struct {
},
} })).toValue();
},
- else => @panic("TODO implement readFromMemory for more types"),
+ else => return error.Unimplemented,
}
}
@@ -1001,7 +1039,10 @@ pub const Value = struct {
buffer: []const u8,
bit_offset: usize,
arena: Allocator,
- ) Allocator.Error!Value {
+ ) error{
+ IllDefinedMemoryLayout,
+ OutOfMemory,
+ }!Value {
const ip = &mod.intern_pool;
const target = mod.getTarget();
const endian = target.cpu.arch.endian();
@@ -1098,6 +1139,20 @@ pub const Value = struct {
.storage = .{ .elems = field_vals },
} })).toValue();
},
+ .Union => switch (ty.containerLayout(mod)) {
+ .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
+ .Packed => {
+ const union_bits: u16 = @intCast(ty.bitSize(mod));
+ assert(union_bits != 0);
+ const int_ty = try mod.intType(.unsigned, union_bits);
+ const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern();
+ return (try mod.intern(.{ .un = .{
+ .ty = ty.toIntern(),
+ .tag = .none,
+ .val = val,
+ } })).toValue();
+ },
+ },
.Pointer => {
assert(!ty.isSlice(mod)); // No well defined layout.
return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
@@ -1704,11 +1759,19 @@ pub const Value = struct {
};
}
- pub fn unionTag(val: Value, mod: *Module) Value {
+ pub fn unionTag(val: Value, mod: *Module) ?Value {
if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
return switch (mod.intern_pool.indexToKey(val.toIntern())) {
.undef, .enum_tag => val,
- .un => |un| un.tag.toValue(),
+ .un => |un| if (un.tag != .none) un.tag.toValue() else return null,
+ else => unreachable,
+ };
+ }
+
+ pub fn unionValue(val: Value, mod: *Module) Value {
+ if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
+ return switch (mod.intern_pool.indexToKey(val.toIntern())) {
+ .un => |un| un.val.toValue(),
else => unreachable,
};
}