| 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,7 +1103,10 @@ pub const Key = union(enum) { |
| 1103 | pub const Union = extern struct { | 1103 | pub const Union = extern struct { |
| 1104 | /// This is the union type; not the field type. | 1104 | /// This is the union type; not the field type. |
| 1105 | ty: Index, | 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 | tag: Index, | 1110 | tag: Index, |
| 1108 | /// The value of the active field. | 1111 | /// The value of the active field. |
| 1109 | val: Index, | 1112 | val: Index, |
| ... | @@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { | ... | @@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { |
| 5128 | 5131 | ||
| 5129 | .un => |un| { | 5132 | .un => |un| { |
| 5130 | assert(un.ty != .none); | 5133 | assert(un.ty != .none); |
| 5131 | assert(un.tag != .none); | ||
| 5132 | assert(un.val != .none); | 5134 | assert(un.val != .none); |
| 5133 | ip.items.appendAssumeCapacity(.{ | 5135 | ip.items.appendAssumeCapacity(.{ |
| 5134 | .tag = .union_value, | 5136 | .tag = .union_value, |
src/Module.zig+2-1| ... | @@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { | ... | @@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { |
| 5825 | .aggregate => |aggregate| for (aggregate.storage.values()) |elem| | 5825 | .aggregate => |aggregate| for (aggregate.storage.values()) |elem| |
| 5826 | try mod.markReferencedDeclsAlive(elem.toValue()), | 5826 | try mod.markReferencedDeclsAlive(elem.toValue()), |
| 5827 | .un => |un| { | 5827 | .un => |un| { |
| 5828 | try mod.markReferencedDeclsAlive(un.tag.toValue()); | 5828 | if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue()); |
| 5829 | try mod.markReferencedDeclsAlive(un.val.toValue()); | 5829 | try mod.markReferencedDeclsAlive(un.val.toValue()); |
| 5830 | }, | 5830 | }, |
| 5831 | else => {}, | 5831 | else => {}, |
| ... | @@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in | ... | @@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in |
| 6609 | 6609 | ||
| 6610 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { | 6610 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { |
| 6611 | const ip = &mod.intern_pool; | 6611 | const ip = &mod.intern_pool; |
| 6612 | if (enum_tag.toIntern() == .none) return null; | ||
| 6612 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); | 6613 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); |
| 6613 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; | 6614 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; |
| 6614 | return enum_type.tagValueIndex(ip, enum_tag.toIntern()); | 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,7 +3861,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re |
| 3861 | const air_ptr_inst = Air.refToIndex(bin_op.lhs).?; | 3861 | const air_ptr_inst = Air.refToIndex(bin_op.lhs).?; |
| 3862 | const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?; | 3862 | const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?; |
| 3863 | const union_ty = sema.typeOf(bin_op.lhs).childType(mod); | 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 | if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| { | 3865 | if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| { |
| 3866 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; | 3866 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; |
| 3867 | const store_val = try mod.unionValue(union_ty, tag_val, payload_val); | 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,7 +11998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11998 | 11998 | ||
| 11999 | const analyze_body = if (union_originally) blk: { | 11999 | const analyze_body = if (union_originally) blk: { |
| 12000 | const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable; | 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 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12002 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12003 | } else true; | 12003 | } else true; |
| 12004 | 12004 | ||
| ... | @@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12124 | 12124 | ||
| 12125 | const analyze_body = if (union_originally) blk: { | 12125 | const analyze_body = if (union_originally) blk: { |
| 12126 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; | 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 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12128 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12129 | } else true; | 12129 | } else true; |
| 12130 | 12130 | ||
| ... | @@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12178 | const analyze_body = if (union_originally) | 12178 | const analyze_body = if (union_originally) |
| 12179 | for (items) |item| { | 12179 | for (items) |item| { |
| 12180 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; | 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 | if (field_ty.zigTypeTag(mod) != .NoReturn) break true; | 12182 | if (field_ty.zigTypeTag(mod) != .NoReturn) break true; |
| 12183 | } else false | 12183 | } else false |
| 12184 | else | 12184 | else |
| ... | @@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12330 | case_block.wip_capture_scope = child_block.wip_capture_scope; | 12330 | case_block.wip_capture_scope = child_block.wip_capture_scope; |
| 12331 | 12331 | ||
| 12332 | const analyze_body = if (union_originally) blk: { | 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 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12334 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12335 | } else true; | 12335 | } else true; |
| 12336 | 12336 | ||
| ... | @@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag( | ... | @@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag( |
| 16370 | 16370 | ||
| 16371 | if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { | 16371 | if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { |
| 16372 | if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool); | 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 | if (field_ty.zigTypeTag(mod) == .NoReturn) { | 16374 | if (field_ty.zigTypeTag(mod) == .NoReturn) { |
| 16375 | return .bool_false; | 16375 | return .bool_false; |
| 16376 | } | 16376 | } |
| ... | @@ -27207,7 +27207,11 @@ fn unionFieldVal( | ... | @@ -27207,7 +27207,11 @@ fn unionFieldVal( |
| 27207 | if (tag_matches) { | 27207 | if (tag_matches) { |
| 27208 | return Air.internedToRef(un.val); | 27208 | return Air.internedToRef(un.val); |
| 27209 | } else { | 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 | if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { | 27215 | if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { |
| 27212 | return Air.internedToRef(new_val.toIntern()); | 27216 | return Air.internedToRef(new_val.toIntern()); |
| 27213 | } | 27217 | } |
| ... | @@ -29733,10 +29737,15 @@ fn storePtrVal( | ... | @@ -29733,10 +29737,15 @@ fn storePtrVal( |
| 29733 | error.OutOfMemory => return error.OutOfMemory, | 29737 | error.OutOfMemory => return error.OutOfMemory, |
| 29734 | error.ReinterpretDeclRef => unreachable, | 29738 | error.ReinterpretDeclRef => unreachable, |
| 29735 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already | 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 | .bad_decl_ty, .bad_ptr_ty => { | 29750 | .bad_decl_ty, .bad_ptr_ty => { |
| 29742 | // TODO show the decl declaration site in a note and explain whether the decl | 29751 | // TODO show the decl declaration site in a note and explain whether the decl |
| ... | @@ -30648,7 +30657,12 @@ fn bitCastVal( | ... | @@ -30648,7 +30657,12 @@ fn bitCastVal( |
| 30648 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already | 30657 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already |
| 30649 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), | 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 | fn coerceArrayPtrToSlice( | 30668 | fn coerceArrayPtrToSlice( |
| ... | @@ -32858,7 +32872,7 @@ fn unionToTag( | ... | @@ -32858,7 +32872,7 @@ fn unionToTag( |
| 32858 | return Air.internedToRef(opv.toIntern()); | 32872 | return Air.internedToRef(opv.toIntern()); |
| 32859 | } | 32873 | } |
| 32860 | if (try sema.resolveMaybeUndefVal(un)) |un_val| { | 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 | try sema.requireRuntimeBlock(block, un_src, null); | 32877 | try sema.requireRuntimeBlock(block, un_src, null); |
| 32864 | return block.addTyOp(.get_union_tag, enum_ty, un); | 32878 | return block.addTyOp(.get_union_tag, enum_ty, un); |
src/TypedValue.zig+28-18| ... | @@ -87,15 +87,20 @@ pub fn print( | ... | @@ -87,15 +87,20 @@ pub fn print( |
| 87 | const union_val = val.castTag(.@"union").?.data; | 87 | const union_val = val.castTag(.@"union").?.data; |
| 88 | try writer.writeAll(".{ "); | 88 | try writer.writeAll(".{ "); |
| 89 | 89 | ||
| 90 | try print(.{ | 90 | if (union_val.tag.toIntern() != .none) { |
| 91 | .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(), | 91 | try print(.{ |
| 92 | .val = union_val.tag, | 92 | .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(), |
| 93 | }, writer, level - 1, mod); | 93 | .val = union_val.tag, |
| 94 | try writer.writeAll(" = "); | 94 | }, writer, level - 1, mod); |
| 95 | try print(.{ | 95 | try writer.writeAll(" = "); |
| 96 | .ty = ty.unionFieldType(union_val.tag, mod), | 96 | const field_ty = ty.unionFieldType(union_val.tag, mod).?; |
| 97 | .val = union_val.val, | 97 | try print(.{ |
| 98 | }, writer, level - 1, mod); | 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 | return writer.writeAll(" }"); | 105 | return writer.writeAll(" }"); |
| 101 | }, | 106 | }, |
| ... | @@ -404,15 +409,20 @@ pub fn print( | ... | @@ -404,15 +409,20 @@ pub fn print( |
| 404 | .un => |un| { | 409 | .un => |un| { |
| 405 | try writer.writeAll(".{ "); | 410 | try writer.writeAll(".{ "); |
| 406 | if (level > 0) { | 411 | if (level > 0) { |
| 407 | try print(.{ | 412 | if (un.tag != .none) { |
| 408 | .ty = ty.unionTagTypeHypothetical(mod), | 413 | try print(.{ |
| 409 | .val = un.tag.toValue(), | 414 | .ty = ty.unionTagTypeHypothetical(mod), |
| 410 | }, writer, level - 1, mod); | 415 | .val = un.tag.toValue(), |
| 411 | try writer.writeAll(" = "); | 416 | }, writer, level - 1, mod); |
| 412 | try print(.{ | 417 | try writer.writeAll(" = "); |
| 413 | .ty = ty.unionFieldType(un.tag.toValue(), mod), | 418 | const field_ty = ty.unionFieldType(un.tag.toValue(), mod).?; |
| 414 | .val = un.val.toValue(), | 419 | try print(.{ |
| 415 | }, writer, level - 1, mod); | 420 | .ty = field_ty, |
| 421 | .val = un.val.toValue(), | ||
| 422 | }, writer, level - 1, mod); | ||
| 423 | } else { | ||
| 424 | try writer.writeAll("(unknown tag)"); | ||
| 425 | } | ||
| 416 | } else try writer.writeAll("..."); | 426 | } else try writer.writeAll("..."); |
| 417 | return writer.writeAll(" }"); | 427 | return writer.writeAll(" }"); |
| 418 | }, | 428 | }, |
src/codegen.zig+20-10| ... | @@ -583,23 +583,33 @@ pub fn generateSymbol( | ... | @@ -583,23 +583,33 @@ pub fn generateSymbol( |
| 583 | } | 583 | } |
| 584 | 584 | ||
| 585 | const union_obj = mod.typeToUnion(typed_value.ty).?; | 585 | const union_obj = mod.typeToUnion(typed_value.ty).?; |
| 586 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | 586 | if (un.tag != .none) { |
| 587 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | 587 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| 588 | if (!field_ty.hasRuntimeBits(mod)) { | 588 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 589 | try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); | 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 | } else { | 605 | } else { |
| 591 | switch (try generateSymbol(bin_file, src_loc, .{ | 606 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 592 | .ty = field_ty, | 607 | .ty = ip.typeOf(un.val).toType(), |
| 593 | .val = un.val.toValue(), | 608 | .val = un.val.toValue(), |
| 594 | }, code, debug_output, reloc_info)) { | 609 | }, code, debug_output, reloc_info)) { |
| 595 | .ok => {}, | 610 | .ok => {}, |
| 596 | .fail => |em| return Result{ .fail = em }, | 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 | if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { | 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,25 +4108,28 @@ pub const Object = struct { |
| 4108 | if (layout.payload_size == 0) return o.lowerValue(un.tag); | 4108 | if (layout.payload_size == 0) return o.lowerValue(un.tag); |
| 4109 | 4109 | ||
| 4110 | const union_obj = mod.typeToUnion(ty).?; | 4110 | const union_obj = mod.typeToUnion(ty).?; |
| 4111 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | 4111 | const container_layout = union_obj.getLayout(ip); |
| 4112 | 4112 | ||
| 4113 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | 4113 | var need_unnamed = false; |
| 4114 | if (union_obj.getLayout(ip) == .Packed) { | 4114 | const payload = if (un.tag != .none) p: { |
| 4115 | if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); | 4115 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; |
| 4116 | const small_int_val = try o.builder.castConst( | 4116 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 4117 | if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, | 4117 | if (container_layout == .Packed) { |
| 4118 | try o.lowerValue(un.val), | 4118 | if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); |
| 4119 | try o.builder.intType(@intCast(field_ty.bitSize(mod))), | 4119 | const small_int_val = try o.builder.castConst( |
| 4120 | ); | 4120 | if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, |
| 4121 | return o.builder.convConst(.unsigned, small_int_val, union_ty); | 4121 | try o.lowerValue(un.val), |
| 4122 | } | 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 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { | 4133 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 4131 | const padding_len = layout.payload_size; | 4134 | const padding_len = layout.payload_size; |
| 4132 | break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8)); | 4135 | break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8)); |
| ... | @@ -4144,9 +4147,23 @@ pub const Object = struct { | ... | @@ -4144,9 +4147,23 @@ pub const Object = struct { |
| 4144 | try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }), | 4147 | try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }), |
| 4145 | &.{ payload, try o.builder.undefConst(padding_ty) }, | 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 | if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed) | 4167 | if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed) |
| 4151 | try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty}) | 4168 | try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty}) |
| 4152 | else | 4169 | else |
src/type.zig+11-5| ... | @@ -1647,8 +1647,12 @@ pub const Type = struct { | ... | @@ -1647,8 +1647,12 @@ pub const Type = struct { |
| 1647 | }, | 1647 | }, |
| 1648 | 1648 | ||
| 1649 | .union_type => |union_type| { | 1649 | .union_type => |union_type| { |
| 1650 | if (opt_sema) |sema| try sema.resolveTypeFields(ty); | 1650 | const is_packed = ty.containerLayout(mod) == .Packed; |
| 1651 | if (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 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | 1656 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; |
| 1653 | } | 1657 | } |
| 1654 | const union_obj = ip.loadUnionType(union_type); | 1658 | const union_obj = ip.loadUnionType(union_type); |
| ... | @@ -1659,6 +1663,7 @@ pub const Type = struct { | ... | @@ -1659,6 +1663,7 @@ pub const Type = struct { |
| 1659 | const field_ty = union_obj.field_types.get(ip)[field_index]; | 1663 | const field_ty = union_obj.field_types.get(ip)[field_index]; |
| 1660 | size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); | 1664 | size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); |
| 1661 | } | 1665 | } |
| 1666 | |||
| 1662 | return size; | 1667 | return size; |
| 1663 | }, | 1668 | }, |
| 1664 | .opaque_type => unreachable, | 1669 | .opaque_type => unreachable, |
| ... | @@ -1927,11 +1932,12 @@ pub const Type = struct { | ... | @@ -1927,11 +1932,12 @@ pub const Type = struct { |
| 1927 | return union_obj.enum_tag_ty.toType(); | 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 | const ip = &mod.intern_pool; | 1936 | const ip = &mod.intern_pool; |
| 1932 | const union_obj = mod.typeToUnion(ty).?; | 1937 | const union_obj = mod.typeToUnion(ty).?; |
| 1933 | const index = mod.unionTagFieldIndex(union_obj, enum_tag).?; | 1938 | const union_fields = union_obj.field_types.get(ip); |
| 1934 | return union_obj.field_types.get(ip)[index].toType(); | 1939 | const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null; |
| 1940 | return union_fields[index].toType(); | ||
| 1935 | } | 1941 | } |
| 1936 | 1942 | ||
| 1937 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | 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,7 +330,7 @@ pub const Value = struct { |
| 330 | return mod.intern(.{ .un = .{ | 330 | return mod.intern(.{ .un = .{ |
| 331 | .ty = ty.toIntern(), | 331 | .ty = ty.toIntern(), |
| 332 | .tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod), | 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,8 +703,21 @@ pub const Value = struct { |
| 703 | std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian); | 703 | std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian); |
| 704 | }, | 704 | }, |
| 705 | .Union => switch (ty.containerLayout(mod)) { | 705 | .Union => switch (ty.containerLayout(mod)) { |
| 706 | .Auto => return error.IllDefinedMemoryLayout, | 706 | .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already |
| 707 | .Extern => return error.Unimplemented, | 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 | .Packed => { | 721 | .Packed => { |
| 709 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; | 722 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; |
| 710 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); | 723 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| ... | @@ -817,14 +830,18 @@ pub const Value = struct { | ... | @@ -817,14 +830,18 @@ pub const Value = struct { |
| 817 | .Union => { | 830 | .Union => { |
| 818 | const union_obj = mod.typeToUnion(ty).?; | 831 | const union_obj = mod.typeToUnion(ty).?; |
| 819 | switch (union_obj.getLayout(ip)) { | 832 | switch (union_obj.getLayout(ip)) { |
| 820 | .Auto => unreachable, // Sema is supposed to have emitted a compile error already | 833 | .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory |
| 821 | .Extern => unreachable, // Handled in non-packed writeToMemory | ||
| 822 | .Packed => { | 834 | .Packed => { |
| 823 | const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?; | 835 | if (val.unionTag(mod)) |union_tag| { |
| 824 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); | 836 | const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?; |
| 825 | const field_val = try val.fieldValue(mod, field_index); | 837 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); |
| 826 | 838 | const field_val = try val.fieldValue(mod, field_index); | |
| 827 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); | 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,7 +873,11 @@ pub const Value = struct { |
| 856 | mod: *Module, | 873 | mod: *Module, |
| 857 | buffer: []const u8, | 874 | buffer: []const u8, |
| 858 | arena: Allocator, | 875 | arena: Allocator, |
| 859 | ) Allocator.Error!Value { | 876 | ) error{ |
| 877 | IllDefinedMemoryLayout, | ||
| 878 | Unimplemented, | ||
| 879 | OutOfMemory, | ||
| 880 | }!Value { | ||
| 860 | const ip = &mod.intern_pool; | 881 | const ip = &mod.intern_pool; |
| 861 | const target = mod.getTarget(); | 882 | const target = mod.getTarget(); |
| 862 | const endian = target.cpu.arch.endian(); | 883 | const endian = target.cpu.arch.endian(); |
| ... | @@ -966,6 +987,23 @@ pub const Value = struct { | ... | @@ -966,6 +987,23 @@ pub const Value = struct { |
| 966 | .name = name, | 987 | .name = name, |
| 967 | } })).toValue(); | 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 | .Pointer => { | 1007 | .Pointer => { |
| 970 | assert(!ty.isSlice(mod)); // No well defined layout. | 1008 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 971 | const int_val = try readFromMemory(Type.usize, mod, buffer, arena); | 1009 | const int_val = try readFromMemory(Type.usize, mod, buffer, arena); |
| ... | @@ -987,7 +1025,7 @@ pub const Value = struct { | ... | @@ -987,7 +1025,7 @@ pub const Value = struct { |
| 987 | }, | 1025 | }, |
| 988 | } })).toValue(); | 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,7 +1039,10 @@ pub const Value = struct { |
| 1001 | buffer: []const u8, | 1039 | buffer: []const u8, |
| 1002 | bit_offset: usize, | 1040 | bit_offset: usize, |
| 1003 | arena: Allocator, | 1041 | arena: Allocator, |
| 1004 | ) Allocator.Error!Value { | 1042 | ) error{ |
| 1043 | IllDefinedMemoryLayout, | ||
| 1044 | OutOfMemory, | ||
| 1045 | }!Value { | ||
| 1005 | const ip = &mod.intern_pool; | 1046 | const ip = &mod.intern_pool; |
| 1006 | const target = mod.getTarget(); | 1047 | const target = mod.getTarget(); |
| 1007 | const endian = target.cpu.arch.endian(); | 1048 | const endian = target.cpu.arch.endian(); |
| ... | @@ -1098,6 +1139,20 @@ pub const Value = struct { | ... | @@ -1098,6 +1139,20 @@ pub const Value = struct { |
| 1098 | .storage = .{ .elems = field_vals }, | 1139 | .storage = .{ .elems = field_vals }, |
| 1099 | } })).toValue(); | 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 | .Pointer => { | 1156 | .Pointer => { |
| 1102 | assert(!ty.isSlice(mod)); // No well defined layout. | 1157 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 1103 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); | 1158 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); |
| ... | @@ -1704,11 +1759,19 @@ pub const Value = struct { | ... | @@ -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 | if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag; | 1763 | if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag; |
| 1709 | return switch (mod.intern_pool.indexToKey(val.toIntern())) { | 1764 | return switch (mod.intern_pool.indexToKey(val.toIntern())) { |
| 1710 | .undef, .enum_tag => val, | 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 | else => unreachable, | 1775 | else => unreachable, |
| 1713 | }; | 1776 | }; |
| 1714 | } | 1777 | } |
test/behavior/comptime_memory.zig+52| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const std = @import("std"); | ||
| 1 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 2 | const endian = builtin.cpu.arch.endian(); | 3 | const endian = builtin.cpu.arch.endian(); |
| 3 | const testing = @import("std").testing; | 4 | const testing = @import("std").testing; |
| ... | @@ -454,3 +455,54 @@ test "type pun null pointer-like optional" { | ... | @@ -454,3 +455,54 @@ test "type pun null pointer-like optional" { |
| 454 | // note that expectEqual hides the bug | 455 | // note that expectEqual hides the bug |
| 455 | try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null); | 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 | } |