mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Merge pull request #17215 from kcbanner/read_from_memory_union
sema: add support for unions in readFromMemory and writeToMemory
This commit is contained in:
commit
f4c884617f
@ -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,
|
||||
|
||||
@ -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());
|
||||
|
||||
36
src/Sema.zig
36
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);
|
||||
|
||||
@ -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(" }");
|
||||
},
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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 container_layout = union_obj.getLayout(ip);
|
||||
|
||||
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);
|
||||
}
|
||||
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) },
|
||||
);
|
||||
};
|
||||
const payload_ty = payload.typeOf(&o.builder);
|
||||
} 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);
|
||||
if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed)
|
||||
try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})
|
||||
else
|
||||
|
||||
16
src/type.zig
16
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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const endian = builtin.cpu.arch.endian();
|
||||
const testing = @import("std").testing;
|
||||
@ -454,3 +455,54 @@ test "type pun null pointer-like optional" {
|
||||
// note that expectEqual hides the bug
|
||||
try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null);
|
||||
}
|
||||
|
||||
test "reinterpret extern union" {
|
||||
{
|
||||
const U = extern union {
|
||||
a: u32,
|
||||
b: u8 align(8),
|
||||
};
|
||||
|
||||
comptime var u: U = undefined;
|
||||
comptime @memset(std.mem.asBytes(&u), 42);
|
||||
try comptime testing.expect(0x2a2a2a2a == u.a);
|
||||
try comptime testing.expect(42 == u.b);
|
||||
try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
|
||||
try testing.expectEqual(42, u.b);
|
||||
}
|
||||
}
|
||||
|
||||
test "reinterpret packed union" {
|
||||
{
|
||||
const U = packed union {
|
||||
a: u32,
|
||||
b: u8 align(8),
|
||||
};
|
||||
|
||||
comptime var u: U = undefined;
|
||||
comptime @memset(std.mem.asBytes(&u), 42);
|
||||
try comptime testing.expect(0x2a2a2a2a == u.a);
|
||||
try comptime testing.expect(0x2a == u.b);
|
||||
try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
|
||||
try testing.expectEqual(0x2a, u.b);
|
||||
}
|
||||
|
||||
{
|
||||
const U = packed union {
|
||||
a: u7,
|
||||
b: u1,
|
||||
};
|
||||
|
||||
const S = packed struct {
|
||||
lsb: U,
|
||||
msb: U,
|
||||
};
|
||||
|
||||
comptime var s: S = undefined;
|
||||
comptime @memset(std.mem.asBytes(&s), 0xaa);
|
||||
try comptime testing.expectEqual(@as(u7, 0x2a), s.lsb.a);
|
||||
try comptime testing.expectEqual(@as(u1, 0), s.lsb.b);
|
||||
try comptime testing.expectEqual(@as(u7, 0x55), s.msb.a);
|
||||
try comptime testing.expectEqual(@as(u1, 1), s.msb.b);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user