| author | |
| committer | |
| log | f2a24b48e1221a8954ddf16e9070e1470ee13e8d |
| tree | 65443a1d67bb03be28927643462f92568fedbbc0 |
| parent | 2fddd767ba20374e7677003c101e60f470c3804c |
When the tag is not known, it's set to `.none`. In this case, the value is either an
array of bytes (for extern unions) or an integer (for packed unions).12 files changed, 147 insertions(+), 138 deletions(-)
src/InternPool.zig+4-2| ... | @@ -1105,7 +1105,10 @@ pub const Key = union(enum) { | ... | @@ -1105,7 +1105,10 @@ pub const Key = union(enum) { |
| 1105 | pub const Union = extern struct { | 1105 | pub const Union = extern struct { |
| 1106 | /// This is the union type; not the field type. | 1106 | /// This is the union type; not the field type. |
| 1107 | ty: Index, | 1107 | ty: Index, |
| 1108 | /// Indicates the active field. | 1108 | /// 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. |
| 1109 | /// In those cases, the type of `val` is: | ||
| 1110 | /// extern: a u8 array of the same byte length as the union | ||
| 1111 | /// packed: an unsigned integer with the same bit size as the union | ||
| 1109 | tag: Index, | 1112 | tag: Index, |
| 1110 | /// The value of the active field. | 1113 | /// The value of the active field. |
| 1111 | val: Index, | 1114 | val: Index, |
| ... | @@ -5130,7 +5133,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { | ... | @@ -5130,7 +5133,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { |
| 5130 | 5133 | ||
| 5131 | .un => |un| { | 5134 | .un => |un| { |
| 5132 | assert(un.ty != .none); | 5135 | assert(un.ty != .none); |
| 5133 | assert(un.tag != .none); | ||
| 5134 | assert(un.val != .none); | 5136 | assert(un.val != .none); |
| 5135 | ip.items.appendAssumeCapacity(.{ | 5137 | ip.items.appendAssumeCapacity(.{ |
| 5136 | .tag = .union_value, | 5138 | .tag = .union_value, |
src/Module.zig+2-29| ... | @@ -5823,7 +5823,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { | ... | @@ -5823,7 +5823,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { |
| 5823 | .aggregate => |aggregate| for (aggregate.storage.values()) |elem| | 5823 | .aggregate => |aggregate| for (aggregate.storage.values()) |elem| |
| 5824 | try mod.markReferencedDeclsAlive(elem.toValue()), | 5824 | try mod.markReferencedDeclsAlive(elem.toValue()), |
| 5825 | .un => |un| { | 5825 | .un => |un| { |
| 5826 | try mod.markReferencedDeclsAlive(un.tag.toValue()); | 5826 | if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue()); |
| 5827 | try mod.markReferencedDeclsAlive(un.val.toValue()); | 5827 | try mod.markReferencedDeclsAlive(un.val.toValue()); |
| 5828 | }, | 5828 | }, |
| 5829 | else => {}, | 5829 | else => {}, |
| ... | @@ -6607,7 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in | ... | @@ -6607,7 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in |
| 6607 | 6607 | ||
| 6608 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { | 6608 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { |
| 6609 | const ip = &mod.intern_pool; | 6609 | const ip = &mod.intern_pool; |
| 6610 | if (enum_tag.toIntern() == .undef) return null; | 6610 | if (enum_tag.toIntern() == .none) return null; |
| 6611 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); | 6611 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); |
| 6612 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; | 6612 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; |
| 6613 | return enum_type.tagValueIndex(ip, enum_tag.toIntern()); | 6613 | return enum_type.tagValueIndex(ip, enum_tag.toIntern()); |
| ... | @@ -6673,30 +6673,3 @@ pub fn structPackedFieldBitOffset( | ... | @@ -6673,30 +6673,3 @@ pub fn structPackedFieldBitOffset( |
| 6673 | } | 6673 | } |
| 6674 | unreachable; // index out of bounds | 6674 | unreachable; // index out of bounds |
| 6675 | } | 6675 | } |
| 6676 | |||
| 6677 | pub fn unionLargestField(mod: *Module, u: InternPool.UnionType) struct { | ||
| 6678 | ty: Type, | ||
| 6679 | index: u32, | ||
| 6680 | size: u64, | ||
| 6681 | } { | ||
| 6682 | const fields = u.field_types.get(&mod.intern_pool); | ||
| 6683 | assert(fields.len != 0); | ||
| 6684 | var largest_field_ty: Type = undefined; | ||
| 6685 | var largest_field_size: u64 = 0; | ||
| 6686 | var largest_field_index: u32 = 0; | ||
| 6687 | for (fields, 0..) |union_field, i| { | ||
| 6688 | const field_ty = union_field.toType(); | ||
| 6689 | const size: u32 = @intCast(field_ty.abiSize(mod)); | ||
| 6690 | if (size > largest_field_size) { | ||
| 6691 | largest_field_ty = field_ty; | ||
| 6692 | largest_field_size = size; | ||
| 6693 | largest_field_index = @intCast(i); | ||
| 6694 | } | ||
| 6695 | } | ||
| 6696 | |||
| 6697 | return .{ | ||
| 6698 | .ty = largest_field_ty, | ||
| 6699 | .index = largest_field_index, | ||
| 6700 | .size = largest_field_size, | ||
| 6701 | }; | ||
| 6702 | } |
src/Sema.zig+10-6| ... | @@ -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.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable; | 12126 | const item_val = sema.resolveConstLazyValue(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 | ||
| ... | @@ -12250,7 +12250,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12250,7 +12250,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12250 | 12250 | ||
| 12251 | const analyze_body = if (union_originally) blk: { | 12251 | const analyze_body = if (union_originally) blk: { |
| 12252 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; | 12252 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; |
| 12253 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | 12253 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; |
| 12254 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12254 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12255 | } else true; | 12255 | } else true; |
| 12256 | 12256 | ||
| ... | @@ -12304,7 +12304,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12304,7 +12304,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12304 | const analyze_body = if (union_originally) | 12304 | const analyze_body = if (union_originally) |
| 12305 | for (items) |item| { | 12305 | for (items) |item| { |
| 12306 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; | 12306 | const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; |
| 12307 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | 12307 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; |
| 12308 | if (field_ty.zigTypeTag(mod) != .NoReturn) break true; | 12308 | if (field_ty.zigTypeTag(mod) != .NoReturn) break true; |
| 12309 | } else false | 12309 | } else false |
| 12310 | else | 12310 | else |
| ... | @@ -12456,7 +12456,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12456,7 +12456,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12456 | case_block.wip_capture_scope = child_block.wip_capture_scope; | 12456 | case_block.wip_capture_scope = child_block.wip_capture_scope; |
| 12457 | 12457 | ||
| 12458 | const analyze_body = if (union_originally) blk: { | 12458 | const analyze_body = if (union_originally) blk: { |
| 12459 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod); | 12459 | const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; |
| 12460 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12460 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12461 | } else true; | 12461 | } else true; |
| 12462 | 12462 | ||
| ... | @@ -16496,7 +16496,7 @@ fn analyzeCmpUnionTag( | ... | @@ -16496,7 +16496,7 @@ fn analyzeCmpUnionTag( |
| 16496 | 16496 | ||
| 16497 | if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { | 16497 | if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { |
| 16498 | if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool); | 16498 | if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool); |
| 16499 | const field_ty = union_ty.unionFieldType(enum_val, mod); | 16499 | const field_ty = union_ty.unionFieldType(enum_val, mod).?; |
| 16500 | if (field_ty.zigTypeTag(mod) == .NoReturn) { | 16500 | if (field_ty.zigTypeTag(mod) == .NoReturn) { |
| 16501 | return .bool_false; | 16501 | return .bool_false; |
| 16502 | } | 16502 | } |
| ... | @@ -27208,7 +27208,11 @@ fn unionFieldVal( | ... | @@ -27208,7 +27208,11 @@ fn unionFieldVal( |
| 27208 | if (tag_matches) { | 27208 | if (tag_matches) { |
| 27209 | return Air.internedToRef(un.val); | 27209 | return Air.internedToRef(un.val); |
| 27210 | } else { | 27210 | } else { |
| 27211 | const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); | 27211 | const old_ty = if (un.tag == .none) |
| 27212 | ip.typeOf(un.val).toType() | ||
| 27213 | else | ||
| 27214 | union_ty.unionFieldType(un.tag.toValue(), mod).?; | ||
| 27215 | |||
| 27212 | if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { | 27216 | if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { |
| 27213 | return Air.internedToRef(new_val.toIntern()); | 27217 | return Air.internedToRef(new_val.toIntern()); |
| 27214 | } | 27218 | } |
src/TypedValue.zig+16-8| ... | @@ -92,10 +92,14 @@ pub fn print( | ... | @@ -92,10 +92,14 @@ pub fn print( |
| 92 | .val = union_val.tag, | 92 | .val = union_val.tag, |
| 93 | }, writer, level - 1, mod); | 93 | }, writer, level - 1, mod); |
| 94 | try writer.writeAll(" = "); | 94 | try writer.writeAll(" = "); |
| 95 | try print(.{ | 95 | if (ty.unionFieldType(union_val.tag, mod)) |field_ty| { |
| 96 | .ty = ty.unionFieldType(union_val.tag, mod), | 96 | try print(.{ |
| 97 | .val = union_val.val, | 97 | .ty = field_ty, |
| 98 | }, writer, level - 1, mod); | 98 | .val = union_val.val, |
| 99 | }, writer, level - 1, mod); | ||
| 100 | } else { | ||
| 101 | return writer.writeAll("(no tag)"); | ||
| 102 | } | ||
| 99 | 103 | ||
| 100 | return writer.writeAll(" }"); | 104 | return writer.writeAll(" }"); |
| 101 | }, | 105 | }, |
| ... | @@ -409,10 +413,14 @@ pub fn print( | ... | @@ -409,10 +413,14 @@ pub fn print( |
| 409 | .val = un.tag.toValue(), | 413 | .val = un.tag.toValue(), |
| 410 | }, writer, level - 1, mod); | 414 | }, writer, level - 1, mod); |
| 411 | try writer.writeAll(" = "); | 415 | try writer.writeAll(" = "); |
| 412 | try print(.{ | 416 | if (ty.unionFieldType(un.tag.toValue(), mod)) |field_ty| { |
| 413 | .ty = ty.unionFieldType(un.tag.toValue(), mod), | 417 | try print(.{ |
| 414 | .val = un.val.toValue(), | 418 | .ty = field_ty, |
| 415 | }, writer, level - 1, mod); | 419 | .val = un.val.toValue(), |
| 420 | }, writer, level - 1, mod); | ||
| 421 | } else { | ||
| 422 | try writer.writeAll("(no tag)"); | ||
| 423 | } | ||
| 416 | } else try writer.writeAll("..."); | 424 | } else try writer.writeAll("..."); |
| 417 | return writer.writeAll(" }"); | 425 | return writer.writeAll(" }"); |
| 418 | }, | 426 | }, |
src/arch/wasm/CodeGen.zig+1-4| ... | @@ -3259,10 +3259,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { | ... | @@ -3259,10 +3259,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 3259 | .un => |un| { | 3259 | .un => |un| { |
| 3260 | // in this case we have a packed union which will not be passed by reference. | 3260 | // in this case we have a packed union which will not be passed by reference. |
| 3261 | const union_obj = mod.typeToUnion(ty).?; | 3261 | const union_obj = mod.typeToUnion(ty).?; |
| 3262 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: { | 3262 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; |
| 3263 | assert(union_obj.getLayout(ip) == .Extern); | ||
| 3264 | break :f mod.unionLargestField(union_obj).index; | ||
| 3265 | }; | ||
| 3266 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | 3263 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 3267 | return func.lowerConstant(un.val.toValue(), field_ty); | 3264 | return func.lowerConstant(un.val.toValue(), field_ty); |
| 3268 | }, | 3265 | }, |
src/codegen.zig+1-4| ... | @@ -583,10 +583,7 @@ pub fn generateSymbol( | ... | @@ -583,10 +583,7 @@ 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) orelse f: { | 586 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| 587 | assert(union_obj.getLayout(ip) == .Extern); | ||
| 588 | break :f mod.unionLargestField(union_obj).index; | ||
| 589 | }; | ||
| 590 | 587 | ||
| 591 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | 588 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 592 | if (!field_ty.hasRuntimeBits(mod)) { | 589 | if (!field_ty.hasRuntimeBits(mod)) { |
src/codegen/c.zig+1-4| ... | @@ -1439,10 +1439,7 @@ pub const DeclGen = struct { | ... | @@ -1439,10 +1439,7 @@ pub const DeclGen = struct { |
| 1439 | } | 1439 | } |
| 1440 | 1440 | ||
| 1441 | const union_obj = mod.typeToUnion(ty).?; | 1441 | const union_obj = mod.typeToUnion(ty).?; |
| 1442 | const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: { | 1442 | const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; |
| 1443 | assert(union_obj.getLayout(ip) == .Extern); | ||
| 1444 | break :f mod.unionLargestField(union_obj).index; | ||
| 1445 | }; | ||
| 1446 | const field_ty = union_obj.field_types.get(ip)[field_i].toType(); | 1443 | const field_ty = union_obj.field_types.get(ip)[field_i].toType(); |
| 1447 | const field_name = union_obj.field_names.get(ip)[field_i]; | 1444 | const field_name = union_obj.field_names.get(ip)[field_i]; |
| 1448 | if (union_obj.getLayout(ip) == .Packed) { | 1445 | if (union_obj.getLayout(ip) == .Packed) { |
src/codegen/llvm.zig+35-21| ... | @@ -4108,28 +4108,28 @@ pub const Object = struct { | ... | @@ -4108,28 +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()) orelse f: { | 4111 | const container_layout = union_obj.getLayout(ip); |
| 4112 | assert(union_obj.getLayout(ip) == .Extern); | 4112 | |
| 4113 | break :f mod.unionLargestField(union_obj).index; | 4113 | var need_unnamed = false; |
| 4114 | }; | 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 | } | ||
| 4115 | 4126 | ||
| 4116 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); | 4127 | // Sometimes we must make an unnamed struct because LLVM does |
| 4117 | if (union_obj.getLayout(ip) == .Packed) { | 4128 | // not support bitcasting our payload struct to the true union payload type. |
| 4118 | if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); | 4129 | // Instead we use an unnamed struct and every reference to the global |
| 4119 | const small_int_val = try o.builder.castConst( | 4130 | // must pointer cast to the expected type before accessing the union. |
| 4120 | if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, | 4131 | need_unnamed = layout.most_aligned_field != field_index; |
| 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 | 4132 | ||
| 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 | var need_unnamed = layout.most_aligned_field != field_index; | ||
| 4132 | const payload = p: { | ||
| 4133 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { | 4133 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 4134 | const padding_len = layout.payload_size; | 4134 | const padding_len = layout.payload_size; |
| 4135 | 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)); |
| ... | @@ -4147,9 +4147,23 @@ pub const Object = struct { | ... | @@ -4147,9 +4147,23 @@ pub const Object = struct { |
| 4147 | try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }), | 4147 | try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }), |
| 4148 | &.{ payload, try o.builder.undefConst(padding_ty) }, | 4148 | &.{ payload, try o.builder.undefConst(padding_ty) }, |
| 4149 | ); | 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; | ||
| 4150 | }; | 4164 | }; |
| 4151 | const payload_ty = payload.typeOf(&o.builder); | ||
| 4152 | 4165 | ||
| 4166 | const payload_ty = payload.typeOf(&o.builder); | ||
| 4153 | 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) |
| 4154 | try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty}) | 4168 | try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty}) |
| 4155 | else | 4169 | else |
src/codegen/spirv.zig+1-4| ... | @@ -838,10 +838,7 @@ pub const DeclGen = struct { | ... | @@ -838,10 +838,7 @@ pub const DeclGen = struct { |
| 838 | return dg.todo("packed union constants", .{}); | 838 | return dg.todo("packed union constants", .{}); |
| 839 | } | 839 | } |
| 840 | 840 | ||
| 841 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module) orelse f: { | 841 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?; |
| 842 | assert(union_obj.getLayout(ip) == .Extern); | ||
| 843 | break :f mod.unionLargestField(union_obj).index; | ||
| 844 | }; | ||
| 845 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); | 842 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); |
| 846 | 843 | ||
| 847 | const has_tag = layout.tag_size != 0; | 844 | const has_tag = layout.tag_size != 0; |
src/type.zig+4-6| ... | @@ -1658,6 +1658,7 @@ pub const Type = struct { | ... | @@ -1658,6 +1658,7 @@ pub const Type = struct { |
| 1658 | const field_ty = union_obj.field_types.get(ip)[field_index]; | 1658 | const field_ty = union_obj.field_types.get(ip)[field_index]; |
| 1659 | size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); | 1659 | size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); |
| 1660 | } | 1660 | } |
| 1661 | |||
| 1661 | return size; | 1662 | return size; |
| 1662 | }, | 1663 | }, |
| 1663 | .opaque_type => unreachable, | 1664 | .opaque_type => unreachable, |
| ... | @@ -1926,15 +1927,12 @@ pub const Type = struct { | ... | @@ -1926,15 +1927,12 @@ pub const Type = struct { |
| 1926 | return union_obj.enum_tag_ty.toType(); | 1927 | return union_obj.enum_tag_ty.toType(); |
| 1927 | } | 1928 | } |
| 1928 | 1929 | ||
| 1929 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { | 1930 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type { |
| 1930 | const ip = &mod.intern_pool; | 1931 | const ip = &mod.intern_pool; |
| 1931 | const union_obj = mod.typeToUnion(ty).?; | 1932 | const union_obj = mod.typeToUnion(ty).?; |
| 1932 | const union_fields = union_obj.field_types.get(ip); | 1933 | const union_fields = union_obj.field_types.get(ip); |
| 1933 | if (mod.unionTagFieldIndex(union_obj, enum_tag)) |index| { | 1934 | const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null; |
| 1934 | return union_fields[index].toType(); | 1935 | return union_fields[index].toType(); |
| 1935 | } else { | ||
| 1936 | return mod.unionLargestField(union_obj).ty; | ||
| 1937 | } | ||
| 1938 | } | 1936 | } |
| 1939 | 1937 | ||
| 1940 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | 1938 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { |
src/value.zig+29-36| ... | @@ -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,22 +703,20 @@ pub const Value = struct { | ... | @@ -703,22 +703,20 @@ 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 => { | 707 | .Extern => { |
| 708 | const union_obj = mod.typeToUnion(ty).?; | 708 | const union_obj = mod.typeToUnion(ty).?; |
| 709 | const union_tag = val.unionTag(mod); | 709 | const union_tag = val.unionTag(mod); |
| 710 | 710 | if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| { | |
| 711 | const field_type, const field_index = if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| .{ | 711 | const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType(); |
| 712 | union_obj.field_types.get(&mod.intern_pool)[field_index].toType(), | 712 | const field_val = try val.fieldValue(mod, field_index); |
| 713 | field_index, | 713 | const byte_count = @as(usize, @intCast(field_type.abiSize(mod))); |
| 714 | } else f: { | 714 | return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]); |
| 715 | const largest_field = mod.unionLargestField(union_obj); | 715 | } else { |
| 716 | break :f .{ largest_field.ty, largest_field.index }; | 716 | const union_size = ty.abiSize(mod); |
| 717 | }; | 717 | const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type }); |
| 718 | 718 | return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..union_size]); | |
| 719 | const field_val = try val.fieldValue(mod, field_index); | 719 | } |
| 720 | const byte_count = @as(usize, @intCast(field_type.abiSize(mod))); | ||
| 721 | return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]); | ||
| 722 | }, | 720 | }, |
| 723 | .Packed => { | 721 | .Packed => { |
| 724 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; | 722 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; |
| ... | @@ -832,13 +830,11 @@ pub const Value = struct { | ... | @@ -832,13 +830,11 @@ pub const Value = struct { |
| 832 | .Union => { | 830 | .Union => { |
| 833 | const union_obj = mod.typeToUnion(ty).?; | 831 | const union_obj = mod.typeToUnion(ty).?; |
| 834 | switch (union_obj.getLayout(ip)) { | 832 | switch (union_obj.getLayout(ip)) { |
| 835 | .Auto => unreachable, // Sema is supposed to have emitted a compile error already | 833 | .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory |
| 836 | .Extern => unreachable, // Handled in non-packed writeToMemory | ||
| 837 | .Packed => { | 834 | .Packed => { |
| 838 | const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?; | 835 | const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?; |
| 839 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); | 836 | const field_type = union_obj.field_types.get(ip)[field_index].toType(); |
| 840 | const field_val = try val.fieldValue(mod, field_index); | 837 | const field_val = try val.fieldValue(mod, field_index); |
| 841 | |||
| 842 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); | 838 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); |
| 843 | }, | 839 | }, |
| 844 | } | 840 | } |
| ... | @@ -988,17 +984,14 @@ pub const Value = struct { | ... | @@ -988,17 +984,14 @@ pub const Value = struct { |
| 988 | .Union => switch (ty.containerLayout(mod)) { | 984 | .Union => switch (ty.containerLayout(mod)) { |
| 989 | .Auto => return error.IllDefinedMemoryLayout, | 985 | .Auto => return error.IllDefinedMemoryLayout, |
| 990 | .Extern => { | 986 | .Extern => { |
| 991 | const union_obj = mod.typeToUnion(ty).?; | 987 | const union_size = ty.abiSize(mod); |
| 992 | const largest_field = mod.unionLargestField(union_obj); | 988 | const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type }); |
| 993 | const field_size: usize = @intCast(largest_field.size); | 989 | const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod); |
| 994 | const val = try (try readFromMemory(largest_field.ty, mod, buffer[0..field_size], arena)).intern(largest_field.ty, mod); | 990 | return (try mod.intern(.{ .un = .{ |
| 995 | return (try mod.intern(.{ | 991 | .ty = ty.toIntern(), |
| 996 | .un = .{ | 992 | .tag = .none, |
| 997 | .ty = ty.toIntern(), | 993 | .val = val, |
| 998 | .tag = .undef, | 994 | } })).toValue(); |
| 999 | .val = val, | ||
| 1000 | }, | ||
| 1001 | })).toValue(); | ||
| 1002 | }, | 995 | }, |
| 1003 | .Packed => { | 996 | .Packed => { |
| 1004 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; | 997 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; |
| ... | @@ -1141,17 +1134,17 @@ pub const Value = struct { | ... | @@ -1141,17 +1134,17 @@ pub const Value = struct { |
| 1141 | } })).toValue(); | 1134 | } })).toValue(); |
| 1142 | }, | 1135 | }, |
| 1143 | .Union => switch (ty.containerLayout(mod)) { | 1136 | .Union => switch (ty.containerLayout(mod)) { |
| 1144 | .Auto => return error.IllDefinedMemoryLayout, | 1137 | .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory |
| 1145 | .Extern => unreachable, // Handled by non-packed readFromMemory | ||
| 1146 | .Packed => { | 1138 | .Packed => { |
| 1147 | const union_obj = mod.typeToUnion(ty).?; | 1139 | const union_bits: u16 = @intCast(ty.bitSize(mod)); |
| 1148 | const largest_field = mod.unionLargestField(union_obj); | 1140 | // TODO: Remove after tests pass |
| 1149 | const un_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), largest_field.index); | 1141 | assert(union_bits != 0); |
| 1150 | const un_val = try (try readFromPackedMemory(largest_field.ty, mod, buffer, bit_offset, arena)).intern(largest_field.ty, mod); | 1142 | const int_ty = try mod.intType(.unsigned, union_bits); |
| 1143 | const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern(); | ||
| 1151 | return (try mod.intern(.{ .un = .{ | 1144 | return (try mod.intern(.{ .un = .{ |
| 1152 | .ty = ty.toIntern(), | 1145 | .ty = ty.toIntern(), |
| 1153 | .tag = un_tag_val.ip_index, | 1146 | .tag = .none, |
| 1154 | .val = un_val, | 1147 | .val = val, |
| 1155 | } })).toValue(); | 1148 | } })).toValue(); |
| 1156 | }, | 1149 | }, |
| 1157 | }, | 1150 | }, |
test/behavior/comptime_memory.zig+43-14| ... | @@ -455,23 +455,52 @@ test "type pun null pointer-like optional" { | ... | @@ -455,23 +455,52 @@ test "type pun null pointer-like optional" { |
| 455 | } | 455 | } |
| 456 | 456 | ||
| 457 | test "reinterpret extern union" { | 457 | test "reinterpret extern union" { |
| 458 | const U = extern union { | 458 | { |
| 459 | a: u32, | 459 | const U = extern union { |
| 460 | b: u64, | 460 | a: u32, |
| 461 | }; | 461 | b: u8 align(8), |
| 462 | }; | ||
| 462 | 463 | ||
| 463 | comptime var u: U = undefined; | 464 | comptime var u: U = undefined; |
| 464 | comptime @memset(std.mem.asBytes(&u), 42); | 465 | comptime @memset(std.mem.asBytes(&u), 42); |
| 465 | try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b); | 466 | try comptime testing.expect(0x2a2a2a2a == u.a); |
| 467 | try comptime testing.expect(42 == u.b); | ||
| 468 | try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a); | ||
| 469 | try testing.expectEqual(42, u.b); | ||
| 470 | } | ||
| 466 | } | 471 | } |
| 467 | 472 | ||
| 468 | test "reinterpret packed union" { | 473 | test "reinterpret packed union" { |
| 469 | const U = packed union { | 474 | { |
| 470 | a: u32, | 475 | const U = packed union { |
| 471 | b: u64, | 476 | a: u32, |
| 472 | }; | 477 | b: u8 align(8), |
| 478 | }; | ||
| 473 | 479 | ||
| 474 | comptime var u: U = undefined; | 480 | comptime var u: U = undefined; |
| 475 | comptime @memset(std.mem.asBytes(&u), 42); | 481 | comptime @memset(std.mem.asBytes(&u), 42); |
| 476 | try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b); | 482 | try comptime testing.expect(0x2a2a2a2a == u.a); |
| 483 | try comptime testing.expect(0x2a == u.b); | ||
| 484 | try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a); | ||
| 485 | try testing.expectEqual(0x2a, u.b); | ||
| 486 | } | ||
| 487 | |||
| 488 | { | ||
| 489 | const U = packed union { | ||
| 490 | a: u7, | ||
| 491 | b: u1, | ||
| 492 | }; | ||
| 493 | |||
| 494 | const S = packed struct { | ||
| 495 | lsb: U, | ||
| 496 | msb: U, | ||
| 497 | }; | ||
| 498 | |||
| 499 | comptime var s: S = undefined; | ||
| 500 | comptime @memset(std.mem.asBytes(&s), 0xaa); | ||
| 501 | try comptime testing.expectEqual(@as(u7, 0x2a), s.lsb.a); | ||
| 502 | try comptime testing.expectEqual(@as(u1, 0), s.lsb.b); | ||
| 503 | try comptime testing.expectEqual(@as(u7, 0x55), s.msb.a); | ||
| 504 | try comptime testing.expectEqual(@as(u1, 1), s.msb.b); | ||
| 505 | } | ||
| 477 | } | 506 | } |