| author | |
| committer | |
| log | f4c884617f499b52eaecc0ef674609c774052f8f |
| tree | c24e4628c314d5e815f4b9796d0ff70335fa58a3 |
| parent | 2adb932ad6ee4ff3d3c640cb8fb7bf7db0ff5d74 |
| parent | 9f4649b197b720dbc168ced25eee0805d3b678b1 |
| signature |
sema: add support for unions in readFromMemory and writeToMemory9 files changed, 256 insertions(+), 81 deletions(-)
src/InternPool.zig+4-2| ... | ... | @@ -1103,7 +1103,10 @@ pub const Key = union(enum) { |
| 1103 | 1103 | pub const Union = extern struct { |
| 1104 | 1104 | /// This is the union type; not the field type. |
| 1105 | 1105 | ty: Index, |
| 1106 | /// Indicates the active field. | |
| 1106 | /// 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. | |
| 1107 | /// In those cases, the type of `val` is: | |
| 1108 | /// extern: a u8 array of the same byte length as the union | |
| 1109 | /// packed: an unsigned integer with the same bit size as the union | |
| 1107 | 1110 | tag: Index, |
| 1108 | 1111 | /// The value of the active field. |
| 1109 | 1112 | val: Index, |
| ... | ... | @@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { |
| 5128 | 5131 | |
| 5129 | 5132 | .un => |un| { |
| 5130 | 5133 | assert(un.ty != .none); |
| 5131 | assert(un.tag != .none); | |
| 5132 | 5134 | assert(un.val != .none); |
| 5133 | 5135 | ip.items.appendAssumeCapacity(.{ |
| 5134 | 5136 | .tag = .union_value, |
src/Module.zig+2-1| ... | ... | @@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { |
| 5825 | 5825 | .aggregate => |aggregate| for (aggregate.storage.values()) |elem| |
| 5826 | 5826 | try mod.markReferencedDeclsAlive(elem.toValue()), |
| 5827 | 5827 | .un => |un| { |
| 5828 | try mod.markReferencedDeclsAlive(un.tag.toValue()); | |
| 5828 | if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue()); | |
| 5829 | 5829 | try mod.markReferencedDeclsAlive(un.val.toValue()); |
| 5830 | 5830 | }, |
| 5831 | 5831 | else => {}, |
| ... | ... | @@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in |
| 6609 | 6609 | |
| 6610 | 6610 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { |
| 6611 | 6611 | const ip = &mod.intern_pool; |
| 6612 | if (enum_tag.toIntern() == .none) return null; | |
| 6612 | 6613 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); |
| 6613 | 6614 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; |
| 6614 | 6615 | return enum_type.tagValueIndex(ip, enum_tag.toIntern()); |
src/Sema.zig+25-11| ... | ... | @@ -3861,7 +3861,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re |
| 3861 | 3861 | const air_ptr_inst = Air.refToIndex(bin_op.lhs).?; |
| 3862 | 3862 | const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?; |
| 3863 | 3863 | const union_ty = sema.typeOf(bin_op.lhs).childType(mod); |
| 3864 | const payload_ty = union_ty.unionFieldType(tag_val, mod); | |
| 3864 | const payload_ty = union_ty.unionFieldType(tag_val, mod).?; | |
| 3865 | 3865 | if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| { |
| 3866 | 3866 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; |
| 3867 | 3867 | 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 |
| 11998 | 11998 | |
| 11999 | 11999 | const analyze_body = if (union_originally) blk: { |
| 12000 | 12000 | const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable; |
| 12001 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | |
| 12001 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; | |
| 12002 | 12002 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12003 | 12003 | } else true; |
| 12004 | 12004 | |
| ... | ... | @@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12124 | 12124 | |
| 12125 | 12125 | const analyze_body = if (union_originally) blk: { |
| 12126 | 12126 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; |
| 12127 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | |
| 12127 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; | |
| 12128 | 12128 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12129 | 12129 | } else true; |
| 12130 | 12130 | |
| ... | ... | @@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12178 | 12178 | const analyze_body = if (union_originally) |
| 12179 | 12179 | for (items) |item| { |
| 12180 | 12180 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; |
| 12181 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | |
| 12181 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; | |
| 12182 | 12182 | if (field_ty.zigTypeTag(mod) != .NoReturn) break true; |
| 12183 | 12183 | } else false |
| 12184 | 12184 | else |
| ... | ... | @@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12330 | 12330 | case_block.wip_capture_scope = child_block.wip_capture_scope; |
| 12331 | 12331 | |
| 12332 | 12332 | const analyze_body = if (union_originally) blk: { |
| 12333 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | |
| 12333 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; | |
| 12334 | 12334 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12335 | 12335 | } else true; |
| 12336 | 12336 | |
| ... | ... | @@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag( |
| 16370 | 16370 | |
| 16371 | 16371 | if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { |
| 16372 | 16372 | if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool); |
| 16373 | const field_ty = union_ty.unionFieldType(enum_val, mod); | |
| 16373 | const field_ty = union_ty.unionFieldType(enum_val, mod).?; | |
| 16374 | 16374 | if (field_ty.zigTypeTag(mod) == .NoReturn) { |
| 16375 | 16375 | return .bool_false; |
| 16376 | 16376 | } |
| ... | ... | @@ -27207,7 +27207,11 @@ fn unionFieldVal( |
| 27207 | 27207 | if (tag_matches) { |
| 27208 | 27208 | return Air.internedToRef(un.val); |
| 27209 | 27209 | } else { |
| 27210 | const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); | |
| 27210 | const old_ty = if (un.tag == .none) | |
| 27211 | ip.typeOf(un.val).toType() | |
| 27212 | else | |
| 27213 | union_ty.unionFieldType(un.tag.toValue(), mod).?; | |
| 27214 | ||
| 27211 | 27215 | if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { |
| 27212 | 27216 | return Air.internedToRef(new_val.toIntern()); |
| 27213 | 27217 | } |
| ... | ... | @@ -29733,10 +29737,15 @@ fn storePtrVal( |
| 29733 | 29737 | error.OutOfMemory => return error.OutOfMemory, |
| 29734 | 29738 | error.ReinterpretDeclRef => unreachable, |
| 29735 | 29739 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already |
| 29736 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), | |
| 29740 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}), | |
| 29737 | 29741 | }; |
| 29738 | 29742 | |
| 29739 | reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue(); | |
| 29743 | const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) { | |
| 29744 | error.OutOfMemory => return error.OutOfMemory, | |
| 29745 | error.IllDefinedMemoryLayout => unreachable, | |
| 29746 | error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), | |
| 29747 | }; | |
| 29748 | reinterpret.val_ptr.* = (try val.intern(mut_kit.ty, mod)).toValue(); | |
| 29740 | 29749 | }, |
| 29741 | 29750 | .bad_decl_ty, .bad_ptr_ty => { |
| 29742 | 29751 | // TODO show the decl declaration site in a note and explain whether the decl |
| ... | ... | @@ -30648,7 +30657,12 @@ fn bitCastVal( |
| 30648 | 30657 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already |
| 30649 | 30658 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), |
| 30650 | 30659 | }; |
| 30651 | return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena); | |
| 30660 | ||
| 30661 | return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) { | |
| 30662 | error.OutOfMemory => return error.OutOfMemory, | |
| 30663 | error.IllDefinedMemoryLayout => unreachable, | |
| 30664 | error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}), | |
| 30665 | }; | |
| 30652 | 30666 | } |
| 30653 | 30667 | |
| 30654 | 30668 | fn coerceArrayPtrToSlice( |
| ... | ... | @@ -32858,7 +32872,7 @@ fn unionToTag( |
| 32858 | 32872 | return Air.internedToRef(opv.toIntern()); |
| 32859 | 32873 | } |
| 32860 | 32874 | if (try sema.resolveMaybeUndefVal(un)) |un_val| { |
| 32861 | return Air.internedToRef(un_val.unionTag(mod).toIntern()); | |
| 32875 | return Air.internedToRef(un_val.unionTag(mod).?.toIntern()); | |
| 32862 | 32876 | } |
| 32863 | 32877 | try sema.requireRuntimeBlock(block, un_src, null); |
| 32864 | 32878 | return block.addTyOp(.get_union_tag, enum_ty, un); |
src/TypedValue.zig+28-18| ... | ... | @@ -87,15 +87,20 @@ pub fn print( |
| 87 | 87 | const union_val = val.castTag(.@"union").?.data; |
| 88 | 88 | try writer.writeAll(".{ "); |
| 89 | 89 | |
| 90 | try print(.{ | |
| 91 | .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(), | |
| 92 | .val = union_val.tag, | |
| 93 | }, writer, level - 1, mod); | |
| 94 | try writer.writeAll(" = "); | |
| 95 | try print(.{ | |
| 96 | .ty = ty.unionFieldType(union_val.tag, mod), | |
| 97 | .val = union_val.val, | |
| 98 | }, writer, level - 1, mod); | |
| 90 | if (union_val.tag.toIntern() != .none) { | |
| 91 | try print(.{ | |
| 92 | .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(), | |
| 93 | .val = union_val.tag, | |
| 94 | }, writer, level - 1, mod); | |
| 95 | try writer.writeAll(" = "); | |
| 96 | const field_ty = ty.unionFieldType(union_val.tag, mod).?; | |
| 97 | try print(.{ | |
| 98 | .ty = field_ty, | |
| 99 | .val = union_val.val, | |
| 100 | }, writer, level - 1, mod); | |
| 101 | } else { | |
| 102 | return writer.writeAll("(unknown tag)"); | |
| 103 | } | |
| 99 | 104 | |
| 100 | 105 | return writer.writeAll(" }"); |
| 101 | 106 | }, |
| ... | ... | @@ -404,15 +409,20 @@ pub fn print( |
| 404 | 409 | .un => |un| { |
| 405 | 410 | try writer.writeAll(".{ "); |
| 406 | 411 | if (level > 0) { |
| 407 | try print(.{ | |
| 408 | .ty = ty.unionTagTypeHypothetical(mod), | |
| 409 | .val = un.tag.toValue(), | |
| 410 | }, writer, level - 1, mod); | |
| 411 | try writer.writeAll(" = "); | |
| 412 | try print(.{ | |
| 413 | .ty = ty.unionFieldType(un.tag.toValue(), mod), | |
| 414 | .val = un.val.toValue(), | |
| 415 | }, writer, level - 1, mod); | |
| 412 | if (un.tag != .none) { | |
| 413 | try print(.{ | |
| 414 | .ty = ty.unionTagTypeHypothetical(mod), | |
| 415 | .val = un.tag.toValue(), | |
| 416 | }, writer, level - 1, mod); | |
| 417 | try writer.writeAll(" = "); | |
| 418 | const field_ty = ty.unionFieldType(un.tag.toValue(), mod).?; | |
| 419 | try print(.{ | |
| 420 | .ty = field_ty, | |
| 421 | .val = un.val.toValue(), | |
| 422 | }, writer, level - 1, mod); | |
| 423 | } else { | |
| 424 | try writer.writeAll("(unknown tag)"); | |
| 425 | } | |
| 416 | 426 | } else try writer.writeAll("..."); |
| 417 | 427 | return writer.writeAll(" }"); |
| 418 | 428 | }, |
src/codegen.zig+20-10| ... | ... | @@ -583,23 +583,33 @@ pub fn generateSymbol( |
| 583 | 583 | } |
| 584 | 584 | |
| 585 | 585 | const union_obj = mod.typeToUnion(typed_value.ty).?; |
| 586 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | |
| 587 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | |
| 588 | if (!field_ty.hasRuntimeBits(mod)) { | |
| 589 | try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); | |
| 586 | if (un.tag != .none) { | |
| 587 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | |
| 588 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | |
| 589 | if (!field_ty.hasRuntimeBits(mod)) { | |
| 590 | try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); | |
| 591 | } else { | |
| 592 | switch (try generateSymbol(bin_file, src_loc, .{ | |
| 593 | .ty = field_ty, | |
| 594 | .val = un.val.toValue(), | |
| 595 | }, code, debug_output, reloc_info)) { | |
| 596 | .ok => {}, | |
| 597 | .fail => |em| return Result{ .fail = em }, | |
| 598 | } | |
| 599 | ||
| 600 | const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow; | |
| 601 | if (padding > 0) { | |
| 602 | try code.appendNTimes(0, padding); | |
| 603 | } | |
| 604 | } | |
| 590 | 605 | } else { |
| 591 | 606 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 592 | .ty = field_ty, | |
| 607 | .ty = ip.typeOf(un.val).toType(), | |
| 593 | 608 | .val = un.val.toValue(), |
| 594 | 609 | }, code, debug_output, reloc_info)) { |
| 595 | 610 | .ok => {}, |
| 596 | 611 | .fail => |em| return Result{ .fail = em }, |
| 597 | 612 | } |
| 598 | ||
| 599 | const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow; | |
| 600 | if (padding > 0) { | |
| 601 | try code.appendNTimes(0, padding); | |
| 602 | } | |
| 603 | 613 | } |
| 604 | 614 | |
| 605 | 615 | if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { |
src/codegen/llvm.zig+36-19| ... | ... | @@ -4108,25 +4108,28 @@ pub const Object = struct { |
| 4108 | 4108 | if (layout.payload_size == 0) return o.lowerValue(un.tag); |
| 4109 | 4109 | |
| 4110 | 4110 | const union_obj = mod.typeToUnion(ty).?; |
| 4111 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | |
| 4112 | ||
| 4113 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | |
| 4114 | if (union_obj.getLayout(ip) == .Packed) { | |
| 4115 | if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); | |
| 4116 | const small_int_val = try o.builder.castConst( | |
| 4117 | if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, | |
| 4118 | try o.lowerValue(un.val), | |
| 4119 | try o.builder.intType(@intCast(field_ty.bitSize(mod))), | |
| 4120 | ); | |
| 4121 | return o.builder.convConst(.unsigned, small_int_val, union_ty); | |
| 4122 | } | |
| 4111 | const container_layout = union_obj.getLayout(ip); | |
| 4112 | ||
| 4113 | var need_unnamed = false; | |
| 4114 | const payload = if (un.tag != .none) p: { | |
| 4115 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | |
| 4116 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | |
| 4117 | if (container_layout == .Packed) { | |
| 4118 | if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); | |
| 4119 | const small_int_val = try o.builder.castConst( | |
| 4120 | if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, | |
| 4121 | try o.lowerValue(un.val), | |
| 4122 | try o.builder.intType(@intCast(field_ty.bitSize(mod))), | |
| 4123 | ); | |
| 4124 | return o.builder.convConst(.unsigned, small_int_val, union_ty); | |
| 4125 | } | |
| 4126 | ||
| 4127 | // Sometimes we must make an unnamed struct because LLVM does | |
| 4128 | // not support bitcasting our payload struct to the true union payload type. | |
| 4129 | // Instead we use an unnamed struct and every reference to the global | |
| 4130 | // must pointer cast to the expected type before accessing the union. | |
| 4131 | need_unnamed = layout.most_aligned_field != field_index; | |
| 4123 | 4132 | |
| 4124 | // Sometimes we must make an unnamed struct because LLVM does | |
| 4125 | // not support bitcasting our payload struct to the true union payload type. | |
| 4126 | // Instead we use an unnamed struct and every reference to the global | |
| 4127 | // must pointer cast to the expected type before accessing the union. | |
| 4128 | var need_unnamed = layout.most_aligned_field != field_index; | |
| 4129 | const payload = p: { | |
| 4130 | 4133 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 4131 | 4134 | const padding_len = layout.payload_size; |
| 4132 | 4135 | break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8)); |
| ... | ... | @@ -4144,9 +4147,23 @@ pub const Object = struct { |
| 4144 | 4147 | try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }), |
| 4145 | 4148 | &.{ payload, try o.builder.undefConst(padding_ty) }, |
| 4146 | 4149 | ); |
| 4150 | } else p: { | |
| 4151 | assert(layout.tag_size == 0); | |
| 4152 | const union_val = try o.lowerValue(un.val); | |
| 4153 | if (container_layout == .Packed) { | |
| 4154 | const bitcast_val = try o.builder.castConst( | |
| 4155 | .bitcast, | |
| 4156 | union_val, | |
| 4157 | try o.builder.intType(@intCast(ty.bitSize(mod))), | |
| 4158 | ); | |
| 4159 | return o.builder.convConst(.unsigned, bitcast_val, union_ty); | |
| 4160 | } | |
| 4161 | ||
| 4162 | need_unnamed = true; | |
| 4163 | break :p union_val; | |
| 4147 | 4164 | }; |
| 4148 | const payload_ty = payload.typeOf(&o.builder); | |
| 4149 | 4165 | |
| 4166 | const payload_ty = payload.typeOf(&o.builder); | |
| 4150 | 4167 | if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed) |
| 4151 | 4168 | try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty}) |
| 4152 | 4169 | else |
src/type.zig+11-5| ... | ... | @@ -1647,8 +1647,12 @@ pub const Type = struct { |
| 1647 | 1647 | }, |
| 1648 | 1648 | |
| 1649 | 1649 | .union_type => |union_type| { |
| 1650 | if (opt_sema) |sema| try sema.resolveTypeFields(ty); | |
| 1651 | if (ty.containerLayout(mod) != .Packed) { | |
| 1650 | const is_packed = ty.containerLayout(mod) == .Packed; | |
| 1651 | if (opt_sema) |sema| { | |
| 1652 | try sema.resolveTypeFields(ty); | |
| 1653 | if (is_packed) try sema.resolveTypeLayout(ty); | |
| 1654 | } | |
| 1655 | if (!is_packed) { | |
| 1652 | 1656 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; |
| 1653 | 1657 | } |
| 1654 | 1658 | const union_obj = ip.loadUnionType(union_type); |
| ... | ... | @@ -1659,6 +1663,7 @@ pub const Type = struct { |
| 1659 | 1663 | const field_ty = union_obj.field_types.get(ip)[field_index]; |
| 1660 | 1664 | size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); |
| 1661 | 1665 | } |
| 1666 | ||
| 1662 | 1667 | return size; |
| 1663 | 1668 | }, |
| 1664 | 1669 | .opaque_type => unreachable, |
| ... | ... | @@ -1927,11 +1932,12 @@ pub const Type = struct { |
| 1927 | 1932 | return union_obj.enum_tag_ty.toType(); |
| 1928 | 1933 | } |
| 1929 | 1934 | |
| 1930 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { | |
| 1935 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type { | |
| 1931 | 1936 | const ip = &mod.intern_pool; |
| 1932 | 1937 | const union_obj = mod.typeToUnion(ty).?; |
| 1933 | const index = mod.unionTagFieldIndex(union_obj, enum_tag).?; | |
| 1934 | return union_obj.field_types.get(ip)[index].toType(); | |
| 1938 | const union_fields = union_obj.field_types.get(ip); | |
| 1939 | const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null; | |
| 1940 | return union_fields[index].toType(); | |
| 1935 | 1941 | } |
| 1936 | 1942 | |
| 1937 | 1943 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { |
src/value.zig+78-15| ... | ... | @@ -330,7 +330,7 @@ pub const Value = struct { |
| 330 | 330 | return mod.intern(.{ .un = .{ |
| 331 | 331 | .ty = ty.toIntern(), |
| 332 | 332 | .tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod), |
| 333 | .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod), mod), | |
| 333 | .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod).?, mod), | |
| 334 | 334 | } }); |
| 335 | 335 | }, |
| 336 | 336 | } |
| ... | ... | @@ -703,8 +703,21 @@ pub const Value = struct { |
| 703 | 703 | std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian); |
| 704 | 704 | }, |
| 705 | 705 | .Union => switch (ty.containerLayout(mod)) { |
| 706 | .Auto => return error.IllDefinedMemoryLayout, | |
| 707 | .Extern => return error.Unimplemented, | |
| 706 | .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already | |
| 707 | .Extern => { | |
| 708 | const union_obj = mod.typeToUnion(ty).?; | |
| 709 | if (val.unionTag(mod)) |union_tag| { | |
| 710 | const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?; | |
| 711 | const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType(); | |
| 712 | const field_val = try val.fieldValue(mod, field_index); | |
| 713 | const byte_count = @as(usize, @intCast(field_type.abiSize(mod))); | |
| 714 | return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]); | |
| 715 | } else { | |
| 716 | const union_size = ty.abiSize(mod); | |
| 717 | const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type }); | |
| 718 | return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..@as(usize, @intCast(union_size))]); | |
| 719 | } | |
| 720 | }, | |
| 708 | 721 | .Packed => { |
| 709 | 722 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; |
| 710 | 723 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| ... | ... | @@ -817,14 +830,18 @@ pub const Value = struct { |
| 817 | 830 | .Union => { |
| 818 | 831 | const union_obj = mod.typeToUnion(ty).?; |
| 819 | 832 | switch (union_obj.getLayout(ip)) { |
| 820 | .Auto => unreachable, // Sema is supposed to have emitted a compile error already | |
| 821 | .Extern => unreachable, // Handled in non-packed writeToMemory | |
| 833 | .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory | |
| 822 | 834 | .Packed => { |
| 823 | const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?; | |
| 824 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); | |
| 825 | const field_val = try val.fieldValue(mod, field_index); | |
| 826 | ||
| 827 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); | |
| 835 | if (val.unionTag(mod)) |union_tag| { | |
| 836 | const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?; | |
| 837 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); | |
| 838 | const field_val = try val.fieldValue(mod, field_index); | |
| 839 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); | |
| 840 | } else { | |
| 841 | const union_bits: u16 = @intCast(ty.bitSize(mod)); | |
| 842 | const int_ty = try mod.intType(.unsigned, union_bits); | |
| 843 | return val.unionValue(mod).writeToPackedMemory(int_ty, mod, buffer, bit_offset); | |
| 844 | } | |
| 828 | 845 | }, |
| 829 | 846 | } |
| 830 | 847 | }, |
| ... | ... | @@ -856,7 +873,11 @@ pub const Value = struct { |
| 856 | 873 | mod: *Module, |
| 857 | 874 | buffer: []const u8, |
| 858 | 875 | arena: Allocator, |
| 859 | ) Allocator.Error!Value { | |
| 876 | ) error{ | |
| 877 | IllDefinedMemoryLayout, | |
| 878 | Unimplemented, | |
| 879 | OutOfMemory, | |
| 880 | }!Value { | |
| 860 | 881 | const ip = &mod.intern_pool; |
| 861 | 882 | const target = mod.getTarget(); |
| 862 | 883 | const endian = target.cpu.arch.endian(); |
| ... | ... | @@ -966,6 +987,23 @@ pub const Value = struct { |
| 966 | 987 | .name = name, |
| 967 | 988 | } })).toValue(); |
| 968 | 989 | }, |
| 990 | .Union => switch (ty.containerLayout(mod)) { | |
| 991 | .Auto => return error.IllDefinedMemoryLayout, | |
| 992 | .Extern => { | |
| 993 | const union_size = ty.abiSize(mod); | |
| 994 | const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type }); | |
| 995 | const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod); | |
| 996 | return (try mod.intern(.{ .un = .{ | |
| 997 | .ty = ty.toIntern(), | |
| 998 | .tag = .none, | |
| 999 | .val = val, | |
| 1000 | } })).toValue(); | |
| 1001 | }, | |
| 1002 | .Packed => { | |
| 1003 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; | |
| 1004 | return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena); | |
| 1005 | }, | |
| 1006 | }, | |
| 969 | 1007 | .Pointer => { |
| 970 | 1008 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 971 | 1009 | const int_val = try readFromMemory(Type.usize, mod, buffer, arena); |
| ... | ... | @@ -987,7 +1025,7 @@ pub const Value = struct { |
| 987 | 1025 | }, |
| 988 | 1026 | } })).toValue(); |
| 989 | 1027 | }, |
| 990 | else => @panic("TODO implement readFromMemory for more types"), | |
| 1028 | else => return error.Unimplemented, | |
| 991 | 1029 | } |
| 992 | 1030 | } |
| 993 | 1031 | |
| ... | ... | @@ -1001,7 +1039,10 @@ pub const Value = struct { |
| 1001 | 1039 | buffer: []const u8, |
| 1002 | 1040 | bit_offset: usize, |
| 1003 | 1041 | arena: Allocator, |
| 1004 | ) Allocator.Error!Value { | |
| 1042 | ) error{ | |
| 1043 | IllDefinedMemoryLayout, | |
| 1044 | OutOfMemory, | |
| 1045 | }!Value { | |
| 1005 | 1046 | const ip = &mod.intern_pool; |
| 1006 | 1047 | const target = mod.getTarget(); |
| 1007 | 1048 | const endian = target.cpu.arch.endian(); |
| ... | ... | @@ -1098,6 +1139,20 @@ pub const Value = struct { |
| 1098 | 1139 | .storage = .{ .elems = field_vals }, |
| 1099 | 1140 | } })).toValue(); |
| 1100 | 1141 | }, |
| 1142 | .Union => switch (ty.containerLayout(mod)) { | |
| 1143 | .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory | |
| 1144 | .Packed => { | |
| 1145 | const union_bits: u16 = @intCast(ty.bitSize(mod)); | |
| 1146 | assert(union_bits != 0); | |
| 1147 | const int_ty = try mod.intType(.unsigned, union_bits); | |
| 1148 | const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern(); | |
| 1149 | return (try mod.intern(.{ .un = .{ | |
| 1150 | .ty = ty.toIntern(), | |
| 1151 | .tag = .none, | |
| 1152 | .val = val, | |
| 1153 | } })).toValue(); | |
| 1154 | }, | |
| 1155 | }, | |
| 1101 | 1156 | .Pointer => { |
| 1102 | 1157 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 1103 | 1158 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); |
| ... | ... | @@ -1704,11 +1759,19 @@ pub const Value = struct { |
| 1704 | 1759 | }; |
| 1705 | 1760 | } |
| 1706 | 1761 | |
| 1707 | pub fn unionTag(val: Value, mod: *Module) Value { | |
| 1762 | pub fn unionTag(val: Value, mod: *Module) ?Value { | |
| 1708 | 1763 | if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag; |
| 1709 | 1764 | return switch (mod.intern_pool.indexToKey(val.toIntern())) { |
| 1710 | 1765 | .undef, .enum_tag => val, |
| 1711 | .un => |un| un.tag.toValue(), | |
| 1766 | .un => |un| if (un.tag != .none) un.tag.toValue() else return null, | |
| 1767 | else => unreachable, | |
| 1768 | }; | |
| 1769 | } | |
| 1770 | ||
| 1771 | pub fn unionValue(val: Value, mod: *Module) Value { | |
| 1772 | if (val.ip_index == .none) return val.castTag(.@"union").?.data.val; | |
| 1773 | return switch (mod.intern_pool.indexToKey(val.toIntern())) { | |
| 1774 | .un => |un| un.val.toValue(), | |
| 1712 | 1775 | else => unreachable, |
| 1713 | 1776 | }; |
| 1714 | 1777 | } |
test/behavior/comptime_memory.zig+52| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const std = @import("std"); | |
| 1 | 2 | const builtin = @import("builtin"); |
| 2 | 3 | const endian = builtin.cpu.arch.endian(); |
| 3 | 4 | const testing = @import("std").testing; |
| ... | ... | @@ -454,3 +455,54 @@ test "type pun null pointer-like optional" { |
| 454 | 455 | // note that expectEqual hides the bug |
| 455 | 456 | try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null); |
| 456 | 457 | } |
| 458 | ||
| 459 | test "reinterpret extern union" { | |
| 460 | { | |
| 461 | const U = extern union { | |
| 462 | a: u32, | |
| 463 | b: u8 align(8), | |
| 464 | }; | |
| 465 | ||
| 466 | comptime var u: U = undefined; | |
| 467 | comptime @memset(std.mem.asBytes(&u), 42); | |
| 468 | try comptime testing.expect(0x2a2a2a2a == u.a); | |
| 469 | try comptime testing.expect(42 == u.b); | |
| 470 | try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a); | |
| 471 | try testing.expectEqual(42, u.b); | |
| 472 | } | |
| 473 | } | |
| 474 | ||
| 475 | test "reinterpret packed union" { | |
| 476 | { | |
| 477 | const U = packed union { | |
| 478 | a: u32, | |
| 479 | b: u8 align(8), | |
| 480 | }; | |
| 481 | ||
| 482 | comptime var u: U = undefined; | |
| 483 | comptime @memset(std.mem.asBytes(&u), 42); | |
| 484 | try comptime testing.expect(0x2a2a2a2a == u.a); | |
| 485 | try comptime testing.expect(0x2a == u.b); | |
| 486 | try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a); | |
| 487 | try testing.expectEqual(0x2a, u.b); | |
| 488 | } | |
| 489 | ||
| 490 | { | |
| 491 | const U = packed union { | |
| 492 | a: u7, | |
| 493 | b: u1, | |
| 494 | }; | |
| 495 | ||
| 496 | const S = packed struct { | |
| 497 | lsb: U, | |
| 498 | msb: U, | |
| 499 | }; | |
| 500 | ||
| 501 | comptime var s: S = undefined; | |
| 502 | comptime @memset(std.mem.asBytes(&s), 0xaa); | |
| 503 | try comptime testing.expectEqual(@as(u7, 0x2a), s.lsb.a); | |
| 504 | try comptime testing.expectEqual(@as(u1, 0), s.lsb.b); | |
| 505 | try comptime testing.expectEqual(@as(u7, 0x55), s.msb.a); | |
| 506 | try comptime testing.expectEqual(@as(u1, 1), s.msb.b); | |
| 507 | } | |
| 508 | } |