diff --git a/src/InternPool.zig b/src/InternPool.zig index 315865c966..4c4e3ab78a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -21,6 +21,13 @@ allocated_structs: std.SegmentedList(Module.Struct, 0) = .{}, /// When a Struct object is freed from `allocated_structs`, it is pushed into this stack. structs_free_list: std.ArrayListUnmanaged(Module.Struct.Index) = .{}, +/// Union objects are stored in this data structure because: +/// * They contain pointers such as the field maps. +/// * They need to be mutated after creation. +allocated_unions: std.SegmentedList(Module.Union, 0) = .{}, +/// When a Union object is freed from `allocated_unions`, it is pushed into this stack. +unions_free_list: std.ArrayListUnmanaged(Module.Union.Index) = .{}, + const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -59,10 +66,7 @@ pub const Key = union(enum) { /// If `empty_struct_type` is handled separately, then this value may be /// safely assumed to never be `none`. struct_type: StructType, - union_type: struct { - fields_len: u32, - // TODO move Module.Union data to InternPool - }, + union_type: UnionType, opaque_type: OpaqueType, simple_value: SimpleValue, @@ -87,6 +91,8 @@ pub const Key = union(enum) { /// In the case of sentinel-terminated arrays, the sentinel value *is* stored, /// so the slice length will be one more than the type's array length. aggregate: Aggregate, + /// An instance of a union. + un: Union, pub const IntType = std.builtin.Type.Int; @@ -145,13 +151,27 @@ pub const Key = union(enum) { /// - index == .none /// * A struct which has fields as well as a namepace. pub const StructType = struct { - /// This will be `none` only in the case of `@TypeOf(.{})` - /// (`Index.empty_struct_type`). - namespace: Module.Namespace.OptionalIndex, /// The `none` tag is used to represent two cases: /// * `@TypeOf(.{})`, in which case `namespace` will also be `none`. /// * A struct with no fields, in which case `namespace` will be populated. index: Module.Struct.OptionalIndex, + /// This will be `none` only in the case of `@TypeOf(.{})` + /// (`Index.empty_struct_type`). + namespace: Module.Namespace.OptionalIndex, + }; + + pub const UnionType = struct { + index: Module.Union.Index, + runtime_tag: RuntimeTag, + + pub const RuntimeTag = enum { none, safety, tagged }; + + pub fn hasTag(self: UnionType) bool { + return switch (self.runtime_tag) { + .none => false, + .tagged, .safety => true, + }; + } }; pub const Int = struct { @@ -198,6 +218,15 @@ pub const Key = union(enum) { val: Index, }; + pub const Union = struct { + /// This is the union type; not the field type. + ty: Index, + /// Indicates the active field. + tag: Index, + /// The value of the active field. + val: Index, + }; + pub const Aggregate = struct { ty: Index, fields: []const Index, @@ -229,12 +258,10 @@ pub const Key = union(enum) { .extern_func, .opt, .struct_type, + .union_type, + .un, => |info| std.hash.autoHash(hasher, info), - .union_type => |union_type| { - _ = union_type; - @panic("TODO"); - }, .opaque_type => |opaque_type| std.hash.autoHash(hasher, opaque_type.decl), .int => |int| { @@ -320,6 +347,14 @@ pub const Key = union(enum) { const b_info = b.struct_type; return std.meta.eql(a_info, b_info); }, + .union_type => |a_info| { + const b_info = b.union_type; + return std.meta.eql(a_info, b_info); + }, + .un => |a_info| { + const b_info = b.un; + return std.meta.eql(a_info, b_info); + }, .ptr => |a_info| { const b_info = b.ptr; @@ -371,14 +406,6 @@ pub const Key = union(enum) { @panic("TODO"); }, - .union_type => |a_info| { - const b_info = b.union_type; - - _ = a_info; - _ = b_info; - @panic("TODO"); - }, - .opaque_type => |a_info| { const b_info = b.opaque_type; return a_info.decl == b_info.decl; @@ -411,6 +438,7 @@ pub const Key = union(enum) { .extern_func, .enum_tag, .aggregate, + .un, => |x| return x.ty, .simple_value => |s| switch (s) { @@ -838,6 +866,15 @@ pub const Tag = enum(u8) { /// Module.Struct object allocated for it. /// data is Module.Namespace.Index. type_struct_ns, + /// A tagged union type. + /// `data` is `Module.Union.Index`. + type_union_tagged, + /// An untagged union type. It also has no safety tag. + /// `data` is `Module.Union.Index`. + type_union_untagged, + /// An untagged union type which has a safety tag. + /// `data` is `Module.Union.Index`. + type_union_safety, /// A value that can be represented with only an enum tag. /// data is SimpleValue enum value. @@ -908,6 +945,8 @@ pub const Tag = enum(u8) { /// * A struct which has 0 fields. /// data is Index of the type, which is known to be zero bits at runtime. only_possible_value, + /// data is extra index to Key.Union. + union_value, }; /// Having `SimpleType` and `SimpleValue` in separate enums makes it easier to @@ -1141,6 +1180,9 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.structs_free_list.deinit(gpa); ip.allocated_structs.deinit(gpa); + ip.unions_free_list.deinit(gpa); + ip.allocated_unions.deinit(gpa); + ip.* = undefined; } @@ -1233,6 +1275,19 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .namespace = @intToEnum(Module.Namespace.Index, data).toOptional(), } }, + .type_union_untagged => .{ .union_type = .{ + .index = @intToEnum(Module.Union.Index, data), + .runtime_tag = .none, + } }, + .type_union_tagged => .{ .union_type = .{ + .index = @intToEnum(Module.Union.Index, data), + .runtime_tag = .tagged, + } }, + .type_union_safety => .{ .union_type = .{ + .index = @intToEnum(Module.Union.Index, data), + .runtime_tag = .safety, + } }, + .opt_null => .{ .opt = .{ .ty = @intToEnum(Index, data), .val = .none, @@ -1303,6 +1358,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { else => unreachable, }; }, + .union_value => .{ .un = ip.extraData(Key.Union, data) }, }; } @@ -1350,7 +1406,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } - // TODO introduce more pointer encodings ip.items.appendAssumeCapacity(.{ .tag = .type_pointer, .data = try ip.addExtra(gpa, Pointer{ @@ -1450,8 +1505,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, .union_type => |union_type| { - _ = union_type; - @panic("TODO"); + ip.items.appendAssumeCapacity(.{ + .tag = switch (union_type.runtime_tag) { + .none => .type_union_untagged, + .safety => .type_union_safety, + .tagged => .type_union_tagged, + }, + .data = @enumToInt(union_type.index), + }); }, .opaque_type => |opaque_type| { @@ -1642,6 +1703,16 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { } @panic("TODO"); }, + + .un => |un| { + assert(un.ty != .none); + assert(un.tag != .none); + assert(un.val != .none); + ip.items.appendAssumeCapacity(.{ + .tag = .union_value, + .data = try ip.addExtra(gpa, un), + }); + }, } return @intToEnum(Index, ip.items.len - 1); } @@ -1923,6 +1994,17 @@ pub fn indexToStruct(ip: *InternPool, val: Index) Module.Struct.OptionalIndex { return @intToEnum(Module.Struct.Index, datas[@enumToInt(val)]).toOptional(); } +pub fn indexToUnion(ip: *InternPool, val: Index) Module.Union.OptionalIndex { + const tags = ip.items.items(.tag); + if (val == .none) return .none; + switch (tags[@enumToInt(val)]) { + .type_union_tagged, .type_union_untagged, .type_union_safety => {}, + else => return .none, + } + const datas = ip.items.items(.data); + return @intToEnum(Module.Union.Index, datas[@enumToInt(val)]).toOptional(); +} + pub fn isOptionalType(ip: InternPool, ty: Index) bool { const tags = ip.items.items(.tag); if (ty == .none) return false; @@ -1937,15 +2019,22 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { const items_size = (1 + 4) * ip.items.len; const extra_size = 4 * ip.extra.items.len; const limbs_size = 8 * ip.limbs.items.len; + const structs_size = ip.allocated_structs.len * + (@sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl)); + const unions_size = ip.allocated_unions.len * + (@sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl)); // TODO: map overhead size is not taken into account - const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size; + const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + + structs_size + unions_size; std.debug.print( \\InternPool size: {d} bytes \\ {d} items: {d} bytes \\ {d} extra: {d} bytes \\ {d} limbs: {d} bytes + \\ {d} structs: {d} bytes + \\ {d} unions: {d} bytes \\ , .{ total_size, @@ -1955,6 +2044,10 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { extra_size, ip.limbs.items.len, limbs_size, + ip.allocated_structs.len, + structs_size, + ip.allocated_unions.len, + unions_size, }); const tags = ip.items.items(.tag); @@ -1980,8 +2073,14 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { .type_error_union => @sizeOf(ErrorUnion), .type_enum_simple => @sizeOf(EnumSimple), .type_opaque => @sizeOf(Key.OpaqueType), - .type_struct => 0, - .type_struct_ns => 0, + .type_struct => @sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), + .type_struct_ns => @sizeOf(Module.Namespace), + + .type_union_tagged, + .type_union_untagged, + .type_union_safety, + => @sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), + .simple_type => 0, .simple_value => 0, .ptr_int => @sizeOf(PtrInt), @@ -2010,6 +2109,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { .extern_func => @panic("TODO"), .func => @panic("TODO"), .only_possible_value => 0, + .union_value => @sizeOf(Key.Union), }); } const SortContext = struct { @@ -2041,6 +2141,10 @@ pub fn structPtrUnwrapConst(ip: InternPool, index: Module.Struct.OptionalIndex) return structPtrConst(ip, index.unwrap() orelse return null); } +pub fn unionPtr(ip: *InternPool, index: Module.Union.Index) *Module.Union { + return ip.allocated_unions.at(@enumToInt(index)); +} + pub fn createStruct( ip: *InternPool, gpa: Allocator, @@ -2059,3 +2163,22 @@ pub fn destroyStruct(ip: *InternPool, gpa: Allocator, index: Module.Struct.Index // allocation failures here, instead leaking the Struct until garbage collection. }; } + +pub fn createUnion( + ip: *InternPool, + gpa: Allocator, + initialization: Module.Union, +) Allocator.Error!Module.Union.Index { + if (ip.unions_free_list.popOrNull()) |index| return index; + const ptr = try ip.allocated_unions.addOne(gpa); + ptr.* = initialization; + return @intToEnum(Module.Union.Index, ip.allocated_unions.len - 1); +} + +pub fn destroyUnion(ip: *InternPool, gpa: Allocator, index: Module.Union.Index) void { + ip.unionPtr(index).* = undefined; + ip.unions_free_list.append(gpa, index) catch { + // In order to keep `destroyUnion` a non-fallible function, we ignore memory + // allocation failures here, instead leaking the Union until garbage collection. + }; +} diff --git a/src/Module.zig b/src/Module.zig index ada69537f6..6478f7ce4f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -851,11 +851,10 @@ pub const Decl = struct { /// If the Decl has a value and it is a union, return it, /// otherwise null. - pub fn getUnion(decl: *Decl) ?*Union { + pub fn getUnion(decl: *Decl, mod: *Module) ?*Union { if (!decl.owns_tv) return null; const ty = (decl.val.castTag(.ty) orelse return null).data; - const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data; - return union_obj; + return mod.typeToUnion(ty); } /// If the Decl has a value and it is a function, return it, @@ -896,10 +895,6 @@ pub const Decl = struct { const enum_obj = ty.cast(Type.Payload.EnumFull).?.data; return enum_obj.namespace.toOptional(); }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - return union_obj.namespace.toOptional(); - }, else => return .none, } @@ -907,6 +902,10 @@ pub const Decl = struct { else => return switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .struct_type => |struct_type| struct_type.namespace, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.namespace.toOptional(); + }, else => .none, }, } @@ -1373,6 +1372,28 @@ pub const Union = struct { requires_comptime: PropertyBoolean = .unknown, assumed_runtime_bits: bool = false, + pub const Index = enum(u32) { + _, + + pub fn toOptional(i: Index) OptionalIndex { + return @intToEnum(OptionalIndex, @enumToInt(i)); + } + }; + + pub const OptionalIndex = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn init(oi: ?Index) OptionalIndex { + return @intToEnum(OptionalIndex, @enumToInt(oi orelse return .none)); + } + + pub fn unwrap(oi: OptionalIndex) ?Index { + if (oi == .none) return null; + return @intToEnum(Index, @enumToInt(oi)); + } + }; + pub const Field = struct { /// undefined until `status` is `have_field_types` or `have_layout`. ty: Type, @@ -3639,6 +3660,10 @@ pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace { return mod.allocated_namespaces.at(@enumToInt(index)); } +pub fn unionPtr(mod: *Module, index: Union.Index) *Union { + return mod.intern_pool.unionPtr(index); +} + pub fn structPtr(mod: *Module, index: Struct.Index) *Struct { return mod.intern_pool.structPtr(index); } @@ -4112,7 +4137,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { }; } - if (decl.getUnion()) |union_obj| { + if (decl.getUnion(mod)) |union_obj| { union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse { try file.deleted_decls.append(gpa, decl_index); continue; @@ -5988,20 +6013,10 @@ fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void { decl.analysis = .outdated; } -pub const CreateNamespaceOptions = struct { - parent: Namespace.OptionalIndex, - file_scope: *File, - ty: Type, -}; - -pub fn createNamespace(mod: *Module, options: CreateNamespaceOptions) !Namespace.Index { +pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index { if (mod.namespaces_free_list.popOrNull()) |index| return index; const ptr = try mod.allocated_namespaces.addOne(mod.gpa); - ptr.* = .{ - .parent = options.parent, - .file_scope = options.file_scope, - .ty = options.ty, - }; + ptr.* = initialization; return @intToEnum(Namespace.Index, mod.allocated_namespaces.len - 1); } @@ -6021,6 +6036,14 @@ pub fn destroyStruct(mod: *Module, index: Struct.Index) void { return mod.intern_pool.destroyStruct(mod.gpa, index); } +pub fn createUnion(mod: *Module, initialization: Union) Allocator.Error!Union.Index { + return mod.intern_pool.createUnion(mod.gpa, initialization); +} + +pub fn destroyUnion(mod: *Module, index: Union.Index) void { + return mod.intern_pool.destroyUnion(mod.gpa, index); +} + pub fn allocateNewDecl( mod: *Module, namespace: Namespace.Index, @@ -7068,6 +7091,15 @@ pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value { return i.toValue(); } +pub fn unionValue(mod: *Module, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value { + const i = try intern(mod, .{ .un = .{ + .ty = union_ty.ip_index, + .tag = tag.ip_index, + .val = val.ip_index, + } }); + return i.toValue(); +} + pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type { return intType(mod, .unsigned, Type.smallestUnsignedBits(max)); } @@ -7276,3 +7308,8 @@ pub fn typeToStruct(mod: *Module, ty: Type) ?*Struct { const struct_index = mod.intern_pool.indexToStruct(ty.ip_index).unwrap() orelse return null; return mod.structPtr(struct_index); } + +pub fn typeToUnion(mod: *Module, ty: Type) ?*Union { + const union_index = mod.intern_pool.indexToUnion(ty.ip_index).unwrap() orelse return null; + return mod.unionPtr(union_index); +} diff --git a/src/Sema.zig b/src/Sema.zig index 1f72470f9e..76ac887c06 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3123,6 +3123,8 @@ fn zirUnionDecl( const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; + const gpa = sema.gpa; const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; @@ -3142,49 +3144,57 @@ fn zirUnionDecl( break :blk decls_len; } else 0; - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - const union_obj = try new_decl_arena_allocator.create(Module.Union); - const type_tag = if (small.has_tag_type or small.auto_enum_tag) - Type.Tag.union_tagged - else if (small.layout != .Auto) - Type.Tag.@"union" - else switch (block.sema.mod.optimizeMode()) { - .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged, - .ReleaseFast, .ReleaseSmall => Type.Tag.@"union", - }; - const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); - union_payload.* = .{ - .base = .{ .tag = type_tag }, - .data = union_obj, - }; - const union_ty = Type.initPayload(&union_payload.base); - const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const mod = sema.mod; + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the union type gains an + // InternPool index. + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, - .val = union_val, + .val = undefined, }, small.name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - union_obj.* = .{ + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const union_index = try mod.createUnion(.{ .owner_decl = new_decl_index, .tag_ty = Type.null, .fields = .{}, .zir_index = inst, .layout = small.layout, .status = .none, - .namespace = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .ty = union_ty, - .file_scope = block.getFileScope(mod), - }), - }; + .namespace = new_namespace_index, + }); + errdefer mod.destroyUnion(union_index); - _ = try mod.scanNamespace(union_obj.namespace, extra_index, decls_len, new_decl); + const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ + .index = union_index, + .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) + .tagged + else if (small.layout != .Auto) + .none + else switch (block.sema.mod.optimizeMode()) { + .Debug, .ReleaseSafe => .safety, + .ReleaseFast, .ReleaseSmall => .none, + }, + } }); + errdefer mod.intern_pool.remove(union_ty); + + new_decl.val = union_ty.toValue(); + new_namespace.ty = union_ty.toType(); + + _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl_index); @@ -4246,6 +4256,8 @@ fn validateUnionInit( instrs: []const Zir.Inst.Index, union_ptr: Air.Inst.Ref, ) CompileError!void { + const mod = sema.mod; + if (instrs.len != 1) { const msg = msg: { const msg = try sema.errMsg( @@ -4343,7 +4355,7 @@ fn validateUnionInit( break; } - const tag_ty = union_ty.unionTagTypeHypothetical(); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); @@ -8273,7 +8285,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .Enum => operand, .Union => blk: { const union_ty = try sema.resolveTypeFields(operand_ty); - const tag_ty = union_ty.unionTagType() orelse { + const tag_ty = union_ty.unionTagType(mod) orelse { return sema.fail( block, operand_src, @@ -10158,7 +10170,7 @@ fn zirSwitchCapture( const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable; if (operand_ty.zigTypeTag(mod) == .Union) { const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?); - const union_obj = operand_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(operand_ty).?; const field_ty = union_obj.fields.values()[field_index].ty; if (try sema.resolveDefinedValue(block, sema.src, operand_ptr)) |union_val| { if (is_ref) { @@ -10229,7 +10241,7 @@ fn zirSwitchCapture( switch (operand_ty.zigTypeTag(mod)) { .Union => { - const union_obj = operand_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(operand_ty).?; const first_item = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, "") catch unreachable; @@ -10403,7 +10415,7 @@ fn zirSwitchCond( .Union => { const union_ty = try sema.resolveTypeFields(operand_ty); - const enum_ty = union_ty.unionTagType() orelse { + const enum_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{}); errdefer msg.destroy(sema.gpa); @@ -11627,7 +11639,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const analyze_body = if (union_originally and !special.is_inline) for (seen_enum_fields, 0..) |seen_field, index| { if (seen_field != null) continue; - const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(maybe_union_ty).?; const field_ty = union_obj.fields.values()[index].ty; if (field_ty.zigTypeTag(mod) != .NoReturn) break true; } else false @@ -12068,7 +12080,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } break :hf switch (ty.zigTypeTag(mod)) { .Struct => ty.structFields(mod).contains(field_name), - .Union => ty.unionFields().contains(field_name), + .Union => ty.unionFields(mod).contains(field_name), .Enum => ty.enumFields().contains(field_name), .Array => mem.eql(u8, field_name, "len"), else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ @@ -15415,7 +15427,7 @@ fn analyzeCmpUnionTag( ) CompileError!Air.Inst.Ref { const mod = sema.mod; const union_ty = try sema.resolveTypeFields(sema.typeOf(un)); - const union_tag_ty = union_ty.unionTagType() orelse { + const union_tag_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); errdefer msg.destroy(sema.gpa); @@ -16403,7 +16415,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.resolveTypeLayout(ty); // Getting alignment requires type layout const layout = union_ty.containerLayout(mod); - const union_fields = union_ty.unionFields(); + const union_fields = union_ty.unionFields(mod); const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count()); for (union_field_vals, 0..) |*field_val, i| { @@ -16458,7 +16470,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace(mod)); - const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: { + const enum_tag_ty_val = if (union_ty.unionTagType(mod)) |tag_ty| v: { const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty); break :v try Value.Tag.opt_payload.create(sema.arena, ty_val); } else Value.null; @@ -17877,12 +17889,13 @@ fn unionInit( field_name: []const u8, field_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); - const field = union_ty.unionFields().values()[field_index]; + const field = union_ty.unionFields(mod).values()[field_index]; const init = try sema.coerce(block, field.ty, uncasted_init, init_src); if (try sema.resolveMaybeUndefVal(init)) |init_val| { - const tag_ty = union_ty.unionTagTypeHypothetical(); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{ @@ -17983,7 +17996,7 @@ fn zirStructInit( const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); - const tag_ty = resolved_ty.unionTagTypeHypothetical(); + const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); @@ -18006,7 +18019,7 @@ fn zirStructInit( const alloc = try block.addTy(.alloc, alloc_ty); const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true); try sema.storePtr(block, src, field_ptr, init_inst); - const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val); + const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(mod), tag_val); _ = try block.addBinOp(.set_union_tag, alloc, new_tag); return sema.makePtrConst(block, alloc); } @@ -18544,7 +18557,7 @@ fn fieldType( return sema.addType(field.ty); }, .Union => { - const union_obj = cur_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(cur_ty).?; const field = union_obj.fields.get(field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); return sema.addType(field.ty); @@ -18726,7 +18739,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.addStrLit(block, bytes); }, .Enum => operand_ty, - .Union => operand_ty.unionTagType() orelse { + .Union => operand_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{ operand_ty.fmt(sema.mod), @@ -19245,42 +19258,53 @@ fn zirReify( errdefer new_decl_arena.deinit(); const new_decl_arena_allocator = new_decl_arena.allocator(); - const union_obj = try new_decl_arena_allocator.create(Module.Union); - const type_tag = if (!tag_type_val.isNull(mod)) - Type.Tag.union_tagged - else if (layout != .Auto) - Type.Tag.@"union" - else switch (mod.optimizeMode()) { - .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged, - .ReleaseFast, .ReleaseSmall => Type.Tag.@"union", - }; - const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); - union_payload.* = .{ - .base = .{ .tag = type_tag }, - .data = union_obj, - }; - const union_ty = Type.initPayload(&union_payload.base); - const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the union type gains an + // InternPool index. + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, - .val = new_union_val, + .val = undefined, }, name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - union_obj.* = .{ + + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const union_index = try mod.createUnion(.{ .owner_decl = new_decl_index, .tag_ty = Type.null, .fields = .{}, .zir_index = inst, .layout = layout, .status = .have_field_types, - .namespace = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .ty = union_ty, - .file_scope = block.getFileScope(mod), - }), - }; + .namespace = new_namespace_index, + }); + const union_obj = mod.unionPtr(union_index); + errdefer mod.destroyUnion(union_index); + + const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ + .index = union_index, + .runtime_tag = if (!tag_type_val.isNull(mod)) + .tagged + else if (layout != .Auto) + .none + else switch (mod.optimizeMode()) { + .Debug, .ReleaseSafe => .safety, + .ReleaseFast, .ReleaseSmall => .none, + }, + } }); + errdefer mod.intern_pool.remove(union_ty); + + new_decl.val = union_ty.toValue(); + new_namespace.ty = union_ty.toType(); // Tag type var tag_ty_field_names: ?Module.EnumFull.NameMap = null; @@ -21981,8 +22005,8 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr ptr_ty_data.@"align" = blk: { if (mod.typeToStruct(parent_ty)) |struct_obj| { break :blk struct_obj.fields.values()[field_index].abi_align; - } else if (parent_ty.cast(Type.Payload.Union)) |union_obj| { - break :blk union_obj.data.fields.values()[field_index].abi_align; + } else if (mod.typeToUnion(parent_ty)) |union_obj| { + break :blk union_obj.fields.values()[field_index].abi_align; } else { break :blk 0; } @@ -23443,8 +23467,7 @@ fn explainWhyTypeIsComptimeInner( .Union => { if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return; - if (ty.cast(Type.Payload.Union)) |payload| { - const union_obj = payload.data; + if (mod.typeToUnion(ty)) |union_obj| { for (union_obj.fields.values(), 0..) |field, i| { const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{ .index = i, @@ -24144,7 +24167,7 @@ fn fieldVal( } } const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType()) |enum_ty| { + if (union_ty.unionTagType(mod)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| { const field_index = @intCast(u32, field_index_usize); return sema.addConstant( @@ -24358,7 +24381,7 @@ fn fieldPtr( } } const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType()) |enum_ty| { + if (union_ty.unionTagType(mod)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name)) |field_index| { const field_index_u32 = @intCast(u32, field_index); var anon_decl = try block.startAnonDecl(); @@ -24489,7 +24512,7 @@ fn fieldCallBind( }, .Union => { const union_ty = try sema.resolveTypeFields(concrete_ty); - const fields = union_ty.unionFields(); + const fields = union_ty.unionFields(mod); const field_index_usize = fields.getIndex(field_name) orelse break :find_field; const field_index = @intCast(u32, field_index_usize); const field = fields.values()[field_index]; @@ -24964,7 +24987,7 @@ fn unionFieldPtr( const union_ptr_ty = sema.typeOf(union_ptr); const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; const ptr_field_ty = try Type.ptr(arena, mod, .{ @@ -25028,7 +25051,7 @@ fn unionFieldPtr( try sema.requireRuntimeBlock(block, src, null); if (!initializing and union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) { const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); @@ -25057,7 +25080,7 @@ fn unionFieldVal( assert(unresolved_union_ty.zigTypeTag(mod) == .Union); const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); @@ -25103,7 +25126,7 @@ fn unionFieldVal( try sema.requireRuntimeBlock(block, src, null); if (union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) { const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); @@ -26189,7 +26212,7 @@ fn coerceExtra( }, .Union => blk: { // union to its own tag type - const union_tag_ty = inst_ty.unionTagType() orelse break :blk; + const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk; if (union_tag_ty.eql(dest_ty, sema.mod)) { return sema.unionToTag(block, dest_ty, inst, inst_src); } @@ -28622,7 +28645,7 @@ fn coerceEnumToUnion( const mod = sema.mod; const inst_ty = sema.typeOf(inst); - const tag_ty = union_ty.unionTagType() orelse { + const tag_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), @@ -28649,7 +28672,7 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); }; - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field = union_obj.fields.values()[field_index]; const field_ty = try sema.resolveTypeFields(field.ty); if (field_ty.zigTypeTag(mod) == .NoReturn) { @@ -28679,10 +28702,7 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); }; - return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{ - .tag = val, - .val = opv, - })); + return sema.addConstant(union_ty, try mod.unionValue(union_ty, val, opv)); } try sema.requireRuntimeBlock(block, inst_src, null); @@ -28699,7 +28719,7 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); } - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; { var msg: ?*Module.ErrorMsg = null; errdefer if (msg) |some| some.destroy(sema.gpa); @@ -29350,10 +29370,13 @@ fn analyzeRef( const operand_ty = sema.typeOf(operand); if (try sema.resolveMaybeUndefVal(operand)) |val| { - switch (val.tag()) { - .extern_fn, .function => { - const decl_index = val.pointerDecl().?; - return sema.analyzeDeclRef(decl_index); + switch (val.ip_index) { + .none => switch (val.tag()) { + .extern_fn, .function => { + const decl_index = val.pointerDecl().?; + return sema.analyzeDeclRef(decl_index); + }, + else => {}, }, else => {}, } @@ -31523,8 +31546,9 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void } fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(resolved_ty).?; switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { @@ -31617,27 +31641,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { return false; }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - switch (union_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - var requires_comptime = false; - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; - } - if (requires_comptime) { - union_obj.requires_comptime = .yes; - } else { - union_obj.requires_comptime = .no; - } - return requires_comptime; - }, - } - }, - .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -31734,10 +31737,31 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { } }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + var requires_comptime = false; + union_obj.requires_comptime = .wip; + for (union_obj.fields.values()) |field| { + if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; + } + if (requires_comptime) { + union_obj.requires_comptime = .yes; + } else { + union_obj.requires_comptime = .no; + } + return requires_comptime; + }, + } + }, + .opaque_type => false, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -31829,8 +31853,9 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveUnionLayout(ty); + const mod = sema.mod; const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(resolved_ty).?; switch (union_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, @@ -31858,15 +31883,8 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { const mod = sema.mod; switch (ty.ip_index) { - .none => switch (ty.tag()) { - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - try sema.resolveTypeFieldsUnion(ty, union_obj); - return ty; - }, - - else => return ty, - }, + // TODO: After the InternPool transition is complete, change this to `unreachable`. + .none => return ty, .u1_type, .u8_type, @@ -31957,7 +31975,12 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { try sema.resolveTypeFieldsStruct(ty, struct_obj); return ty; }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + try sema.resolveTypeFieldsUnion(ty, union_obj); + return ty; + }, + else => return ty, }, } @@ -33123,32 +33146,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return null; } }, - .@"union", .union_safety_tagged, .union_tagged => { - const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; - const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse - return null; - const fields = union_obj.fields.values(); - if (fields.len == 0) return Value.@"unreachable"; - const only_field = fields[0]; - if (only_field.ty.eql(resolved_ty, sema.mod)) { - const msg = try Module.ErrorMsg.create( - sema.gpa, - union_obj.srcLoc(sema.mod), - "union '{}' depends on itself", - .{ty.fmt(sema.mod)}, - ); - try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); - return sema.failWithOwnedErrorMsg(msg); - } - const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse - return null; - // TODO make this not allocate. - return try Value.Tag.@"union".create(sema.arena, .{ - .tag = tag_val, - .val = val_val, - }); - }, .array => { if (ty.arrayLen(mod) == 0) @@ -33268,10 +33265,37 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return empty.toValue(); }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const resolved_ty = try sema.resolveTypeFields(ty); + const union_obj = mod.unionPtr(union_type.index); + const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse + return null; + const fields = union_obj.fields.values(); + if (fields.len == 0) return Value.@"unreachable"; + const only_field = fields[0]; + if (only_field.ty.eql(resolved_ty, sema.mod)) { + const msg = try Module.ErrorMsg.create( + sema.gpa, + union_obj.srcLoc(sema.mod), + "union '{}' depends on itself", + .{ty.fmt(sema.mod)}, + ); + try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); + return sema.failWithOwnedErrorMsg(msg); + } + const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse + return null; + const only = try mod.intern(.{ .un = .{ + .ty = resolved_ty.ip_index, + .tag = tag_val.ip_index, + .val = val_val.ip_index, + } }); + return only.toValue(); + }, .opaque_type => null, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -33710,30 +33734,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { return false; }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - switch (union_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (union_obj.status == .field_types_wip) - return false; - - try sema.resolveTypeFieldsUnion(ty, union_obj); - - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.typeRequiresComptime(field.ty)) { - union_obj.requires_comptime = .yes; - return true; - } - } - union_obj.requires_comptime = .no; - return false; - }, - } - }, - .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -33837,10 +33837,34 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { } }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + if (union_obj.status == .field_types_wip) + return false; + + try sema.resolveTypeFieldsUnion(ty, union_obj); + + union_obj.requires_comptime = .wip; + for (union_obj.fields.values()) |field| { + if (try sema.typeRequiresComptime(field.ty)) { + union_obj.requires_comptime = .yes; + return true; + } + } + union_obj.requires_comptime = .no; + return false; + }, + } + }, + .opaque_type => false, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -33905,8 +33929,9 @@ fn unionFieldIndex( field_name: []const u8, field_src: LazySrcLoc, ) !u32 { + const mod = sema.mod; const union_ty = try sema.resolveTypeFields(unresolved_union_ty); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_index_usize = union_obj.fields.getIndex(field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); return @intCast(u32, field_index_usize); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 2105d3108f..cf9888f357 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -91,7 +91,7 @@ pub fn print( try writer.writeAll(".{ "); try print(.{ - .ty = ty.cast(Type.Payload.Union).?.data.tag_ty, + .ty = mod.unionPtr(mod.intern_pool.indexToKey(ty.ip_index).union_type.index).tag_ty, .val = union_val.tag, }, writer, level - 1, mod); try writer.writeAll(" = "); @@ -185,7 +185,7 @@ pub fn print( }, } } else if (field_ptr.container_ty.zigTypeTag(mod) == .Union) { - const field_name = field_ptr.container_ty.unionFields().keys()[field_ptr.field_index]; + const field_name = field_ptr.container_ty.unionFields(mod).keys()[field_ptr.field_index]; return writer.print(".{s}", .{field_name}); } else if (field_ptr.container_ty.isSlice(mod)) { switch (field_ptr.field_index) { diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index 6589425fc2..72a6172895 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -79,7 +79,7 @@ fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 { const invalid = std.math.maxInt(u8); switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(); + const fields = ty.unionFields(mod); var max_count: u8 = 0; for (fields.values()) |field| { const field_count = countFloats(field.ty, mod, maybe_float_bits); @@ -118,7 +118,7 @@ fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 { pub fn getFloatArrayType(ty: Type, mod: *Module) ?Type { switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(); + const fields = ty.unionFields(mod); for (fields.values()) |field| { if (getFloatArrayType(field.ty, mod)) |some| return some; } diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index 7a7d632837..e4a07f22bf 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -62,7 +62,7 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class { const float_count = countFloats(ty, mod, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; - for (ty.unionFields().values()) |field| { + for (ty.unionFields(mod).values()) |field| { if (field.ty.bitSize(mod) > 32 or field.normalAlignment(mod) > 32) { return Class.arrSize(bit_size, 64); } @@ -121,7 +121,7 @@ fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u32 { const invalid = std.math.maxInt(u32); switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(); + const fields = ty.unionFields(mod); var max_count: u32 = 0; for (fields.values()) |field| { const field_count = countFloats(field.ty, mod, maybe_float_bits); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 90c26d5d84..c1409e4977 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1739,8 +1739,8 @@ fn isByRef(ty: Type, mod: *Module) bool { .Frame, => return ty.hasRuntimeBitsIgnoreComptime(mod), .Union => { - if (ty.castTag(.@"union")) |union_ty| { - if (union_ty.data.layout == .Packed) { + if (mod.typeToUnion(ty)) |union_obj| { + if (union_obj.layout == .Packed) { return ty.abiSize(mod) > 8; } } @@ -3175,7 +3175,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { }, .Union => { // in this case we have a packed union which will not be passed by reference. - const union_ty = ty.cast(Type.Payload.Union).?.data; + const union_ty = mod.typeToUnion(ty).?; const union_obj = val.castTag(.@"union").?.data; const field_index = ty.unionTagFieldIndex(union_obj.tag, func.bin_file.base.options.module.?).?; const field_ty = union_ty.fields.values()[field_index].ty; @@ -5086,12 +5086,12 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const result = result: { const union_ty = func.typeOfIndex(inst); const layout = union_ty.unionGetLayout(mod); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field = union_obj.fields.values()[extra.field_index]; const field_name = union_obj.fields.keys()[extra.field_index]; const tag_int = blk: { - const tag_ty = union_ty.unionTagTypeHypothetical(); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); const enum_field_index = tag_ty.enumFieldIndex(field_name).?; var tag_val_payload: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig index ee836bebdb..92b0f4dc40 100644 --- a/src/arch/wasm/abi.zig +++ b/src/arch/wasm/abi.zig @@ -70,8 +70,8 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class { } const layout = ty.unionGetLayout(mod); std.debug.assert(layout.tag_size == 0); - if (ty.unionFields().count() > 1) return memory; - return classifyType(ty.unionFields().values()[0].ty, mod); + if (ty.unionFields(mod).count() > 1) return memory; + return classifyType(ty.unionFields(mod).values()[0].ty, mod); }, .ErrorUnion, .Frame, @@ -111,11 +111,11 @@ pub fn scalarType(ty: Type, mod: *Module) Type { if (ty.containerLayout(mod) != .Packed) { const layout = ty.unionGetLayout(mod); if (layout.payload_size == 0 and layout.tag_size != 0) { - return scalarType(ty.unionTagTypeSafety().?, mod); + return scalarType(ty.unionTagTypeSafety(mod).?, mod); } - std.debug.assert(ty.unionFields().count() == 1); + std.debug.assert(ty.unionFields(mod).count() == 1); } - return scalarType(ty.unionFields().values()[0].ty, mod); + return scalarType(ty.unionFields(mod).values()[0].ty, mod); }, else => return ty, } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 77661b2a14..7b93ff2059 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -11410,9 +11410,9 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { const dst_mcv = try self.allocRegOrMem(inst, false); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_name = union_obj.fields.keys()[extra.field_index]; - const tag_ty = union_ty.unionTagTypeSafety().?; + const tag_ty = union_obj.tag_ty; const field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); var tag_pl = Value.Payload.U32{ .base = .{ .tag = .enum_field_index }, .data = field_index }; const tag_val = Value.initPayload(&tag_pl.base); diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 45ce64a98e..69df5dbf4c 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -338,7 +338,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class { if (ty_size > 64) return memory_class; - const fields = ty.unionFields(); + const fields = ty.unionFields(mod); for (fields.values()) |field| { if (field.abi_align != 0) { if (field.abi_align < field.ty.abiAlignment(mod)) { diff --git a/src/codegen.zig b/src/codegen.zig index b29af1ff93..5c022392bf 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -568,7 +568,7 @@ pub fn generateSymbol( if (layout.payload_size == 0) { return generateSymbol(bin_file, src_loc, .{ - .ty = typed_value.ty.unionTagType().?, + .ty = typed_value.ty.unionTagType(mod).?, .val = union_obj.tag, }, code, debug_output, reloc_info); } @@ -576,7 +576,7 @@ pub fn generateSymbol( // Check if we should store the tag first. if (layout.tag_align >= layout.payload_align) { switch (try generateSymbol(bin_file, src_loc, .{ - .ty = typed_value.ty.unionTagType().?, + .ty = typed_value.ty.unionTagType(mod).?, .val = union_obj.tag, }, code, debug_output, reloc_info)) { .ok => {}, @@ -584,7 +584,7 @@ pub fn generateSymbol( } } - const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data; + const union_ty = mod.typeToUnion(typed_value.ty).?; const field_index = typed_value.ty.unionTagFieldIndex(union_obj.tag, mod).?; assert(union_ty.haveFieldTypes()); const field_ty = union_ty.fields.values()[field_index].ty; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1c16216504..872bdb94d3 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -853,7 +853,7 @@ pub const DeclGen = struct { } try writer.writeByte('{'); - if (ty.unionTagTypeSafety()) |tag_ty| { + if (ty.unionTagTypeSafety(mod)) |tag_ty| { const layout = ty.unionGetLayout(mod); if (layout.tag_size != 0) { try writer.writeAll(" .tag = "); @@ -863,12 +863,12 @@ pub const DeclGen = struct { if (layout.tag_size != 0) try writer.writeByte(','); try writer.writeAll(" .payload = {"); } - for (ty.unionFields().values()) |field| { + for (ty.unionFields(mod).values()) |field| { if (!field.ty.hasRuntimeBits(mod)) continue; try dg.renderValue(writer, field.ty, val, initializer_type); break; } - if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); + if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); return writer.writeByte('}'); }, .ErrorUnion => { @@ -1451,8 +1451,8 @@ pub const DeclGen = struct { } const field_i = ty.unionTagFieldIndex(union_obj.tag, mod).?; - const field_ty = ty.unionFields().values()[field_i].ty; - const field_name = ty.unionFields().keys()[field_i]; + const field_ty = ty.unionFields(mod).values()[field_i].ty; + const field_name = ty.unionFields(mod).keys()[field_i]; if (ty.containerLayout(mod) == .Packed) { if (field_ty.hasRuntimeBits(mod)) { if (field_ty.isPtrAtRuntime(mod)) { @@ -1472,7 +1472,7 @@ pub const DeclGen = struct { } try writer.writeByte('{'); - if (ty.unionTagTypeSafety()) |tag_ty| { + if (ty.unionTagTypeSafety(mod)) |tag_ty| { const layout = ty.unionGetLayout(mod); if (layout.tag_size != 0) { try writer.writeAll(" .tag = "); @@ -1486,12 +1486,12 @@ pub const DeclGen = struct { try writer.print(" .{ } = ", .{fmtIdent(field_name)}); try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); try writer.writeByte(' '); - } else for (ty.unionFields().values()) |field| { + } else for (ty.unionFields(mod).values()) |field| { if (!field.ty.hasRuntimeBits(mod)) continue; try dg.renderValue(writer, field.ty, Value.undef, initializer_type); break; } - if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); + if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); try writer.writeByte('}'); }, @@ -5238,13 +5238,13 @@ fn fieldLocation( .Auto, .Extern => { const field_ty = container_ty.structFieldType(field_index, mod); if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) - return if (container_ty.unionTagTypeSafety() != null and + return if (container_ty.unionTagTypeSafety(mod) != null and !container_ty.unionHasAllZeroBitFieldTypes(mod)) .{ .field = .{ .identifier = "payload" } } else .begin; - const field_name = container_ty.unionFields().keys()[field_index]; - return .{ .field = if (container_ty.unionTagTypeSafety()) |_| + const field_name = container_ty.unionFields(mod).keys()[field_index]; + return .{ .field = if (container_ty.unionTagTypeSafety(mod)) |_| .{ .payload_identifier = field_name } else .{ .identifier = field_name } }; @@ -5424,37 +5424,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { else .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) }, - .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout(mod) == .Packed) { - const operand_lval = if (struct_byval == .constant) blk: { - const operand_local = try f.allocLocal(inst, struct_ty); - try f.writeCValue(writer, operand_local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, struct_byval, .Initializer); - try writer.writeAll(";\n"); - break :blk operand_local; - } else struct_byval; - - const local = try f.allocLocal(inst, inst_ty); - try writer.writeAll("memcpy(&"); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(", &"); - try f.writeCValue(writer, operand_lval, .Other); - try writer.writeAll(", sizeof("); - try f.renderType(writer, inst_ty); - try writer.writeAll("));\n"); - - if (struct_byval == .constant) { - try freeLocal(f, inst, operand_lval.new_local, 0); - } - - return local; - } else field_name: { - const name = struct_ty.unionFields().keys()[extra.field_index]; - break :field_name if (struct_ty.unionTagTypeSafety()) |_| - .{ .payload_identifier = name } - else - .{ .identifier = name }; - }, else => unreachable, }, else => switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { @@ -5520,6 +5489,41 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { return local; }, }, + .union_type => |union_type| field_name: { + const union_obj = mod.unionPtr(union_type.index); + if (union_obj.layout == .Packed) { + const operand_lval = if (struct_byval == .constant) blk: { + const operand_local = try f.allocLocal(inst, struct_ty); + try f.writeCValue(writer, operand_local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, struct_byval, .Initializer); + try writer.writeAll(";\n"); + break :blk operand_local; + } else struct_byval; + + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("memcpy(&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", &"); + try f.writeCValue(writer, operand_lval, .Other); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("));\n"); + + if (struct_byval == .constant) { + try freeLocal(f, inst, operand_lval.new_local, 0); + } + + return local; + } else { + const name = union_obj.fields.keys()[extra.field_index]; + break :field_name if (union_type.hasTag()) .{ + .payload_identifier = name, + } else .{ + .identifier = name, + }; + } + }, else => unreachable, }, }; @@ -6461,7 +6465,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const union_ty = f.typeOf(bin_op.lhs).childType(mod); const layout = union_ty.unionGetLayout(mod); if (layout.tag_size == 0) return .none; - const tag_ty = union_ty.unionTagTypeSafety().?; + const tag_ty = union_ty.unionTagTypeSafety(mod).?; const writer = f.object.writer(); const a = try Assignment.start(f, writer, tag_ty); @@ -6907,7 +6911,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = f.typeOfIndex(inst); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const field_name = union_obj.fields.keys()[extra.field_index]; const payload_ty = f.typeOf(extra.init); const payload = try f.resolveInst(extra.init); @@ -6923,7 +6927,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { return local; } - const field: CValue = if (union_ty.unionTagTypeSafety()) |tag_ty| field: { + const field: CValue = if (union_ty.unionTagTypeSafety(mod)) |tag_ty| field: { const layout = union_ty.unionGetLayout(mod); if (layout.tag_size != 0) { const field_index = tag_ty.enumFieldIndex(field_name).?; diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 3321df6d49..bcb4b92228 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -303,7 +303,7 @@ pub const CType = extern union { ); } pub fn unionPayloadAlign(union_ty: Type, mod: *Module) AlignAs { - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; const union_payload_align = union_obj.abiAlignment(mod, false); return init(union_payload_align, union_payload_align); } @@ -1498,7 +1498,7 @@ pub const CType = extern union { if (lookup.isMutable()) { for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields().count(), + .Union => ty.unionFields(mod).count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -1531,7 +1531,7 @@ pub const CType = extern union { .payload => unreachable, }); } else { - const tag_ty = ty.unionTagTypeSafety(); + const tag_ty = ty.unionTagTypeSafety(mod); const is_tagged_union_wrapper = kind != .payload and tag_ty != null; const is_struct = zig_ty_tag == .Struct or is_tagged_union_wrapper; switch (kind) { @@ -1580,7 +1580,7 @@ pub const CType = extern union { var is_packed = false; for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields().count(), + .Union => ty.unionFields(mod).count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -1930,7 +1930,7 @@ pub const CType = extern union { const zig_ty_tag = ty.zigTypeTag(mod); const fields_len = switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields().count(), + .Union => ty.unionFields(mod).count(), else => unreachable, }; @@ -1956,7 +1956,7 @@ pub const CType = extern union { else arena.dupeZ(u8, switch (zig_ty_tag) { .Struct => ty.structFieldName(field_i, mod), - .Union => ty.unionFields().keys()[field_i], + .Union => ty.unionFields(mod).keys()[field_i], else => unreachable, }), .type = store.set.typeToIndex(field_ty, mod, switch (kind) { @@ -1986,7 +1986,7 @@ pub const CType = extern union { unnamed_pl.* = .{ .base = .{ .tag = t }, .data = .{ .fields = fields_pl, .owner_decl = ty.getOwnerDecl(mod), - .id = if (ty.unionTagTypeSafety()) |_| 0 else unreachable, + .id = if (ty.unionTagTypeSafety(mod)) |_| 0 else unreachable, } }; return initPayload(unnamed_pl); }, @@ -2085,7 +2085,7 @@ pub const CType = extern union { var c_field_i: usize = 0; for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields().count(), + .Union => ty.unionFields(mod).count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -2106,7 +2106,7 @@ pub const CType = extern union { std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else switch (zig_ty_tag) { .Struct => ty.structFieldName(field_i, mod), - .Union => ty.unionFields().keys()[field_i], + .Union => ty.unionFields(mod).keys()[field_i], else => unreachable, }, mem.span(c_field.name), @@ -2122,7 +2122,7 @@ pub const CType = extern union { .packed_unnamed_union, => switch (self.kind) { .forward, .forward_parameter, .complete, .parameter, .global => unreachable, - .payload => if (ty.unionTagTypeSafety()) |_| { + .payload => if (ty.unionTagTypeSafety(mod)) |_| { const data = cty.cast(Payload.Unnamed).?.data; return ty.getOwnerDecl(mod) == data.owner_decl and data.id == 0; } else unreachable, @@ -2211,7 +2211,7 @@ pub const CType = extern union { const zig_ty_tag = ty.zigTypeTag(mod); for (0..switch (ty.zigTypeTag(mod)) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields().count(), + .Union => ty.unionFields(mod).count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -2228,7 +2228,7 @@ pub const CType = extern union { std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else switch (zig_ty_tag) { .Struct => ty.structFieldName(field_i, mod), - .Union => ty.unionFields().keys()[field_i], + .Union => ty.unionFields(mod).keys()[field_i], else => unreachable, }); autoHash(hasher, AlignAs.fieldAlign(ty, field_i, mod).@"align"); @@ -2241,7 +2241,7 @@ pub const CType = extern union { .packed_unnamed_union, => switch (self.kind) { .forward, .forward_parameter, .complete, .parameter, .global => unreachable, - .payload => if (ty.unionTagTypeSafety()) |_| { + .payload => if (ty.unionTagTypeSafety(mod)) |_| { autoHash(hasher, ty.getOwnerDecl(mod)); autoHash(hasher, @as(u32, 0)); } else unreachable, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6b12c447dc..c299253442 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2178,7 +2178,7 @@ pub const Object = struct { break :blk fwd_decl; }; - const union_obj = ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(ty).?; if (!union_obj.haveFieldTypes() or !ty.hasRuntimeBitsIgnoreComptime(mod)) { const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); dib.replaceTemporary(fwd_decl, union_di_ty); @@ -3063,7 +3063,7 @@ pub const DeclGen = struct { gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); const layout = t.unionGetLayout(mod); - const union_obj = t.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(t).?; if (union_obj.layout == .Packed) { const bitsize = @intCast(c_uint, t.bitSize(mod)); @@ -3797,11 +3797,11 @@ pub const DeclGen = struct { if (layout.payload_size == 0) { return lowerValue(dg, .{ - .ty = tv.ty.unionTagTypeSafety().?, + .ty = tv.ty.unionTagTypeSafety(mod).?, .val = tag_and_val.tag, }); } - const union_obj = tv.ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(tv.ty).?; const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?; assert(union_obj.haveFieldTypes()); @@ -3851,7 +3851,7 @@ pub const DeclGen = struct { } } const llvm_tag_value = try lowerValue(dg, .{ - .ty = tv.ty.unionTagTypeSafety().?, + .ty = tv.ty.unionTagTypeSafety(mod).?, .val = tag_and_val.tag, }); var fields: [3]*llvm.Value = undefined; @@ -9410,7 +9410,7 @@ pub const FuncGen = struct { const union_ty = self.typeOfIndex(inst); const union_llvm_ty = try self.dg.lowerType(union_ty); const layout = union_ty.unionGetLayout(mod); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(union_ty).?; if (union_obj.layout == .Packed) { const big_bits = union_ty.bitSize(mod); @@ -9427,7 +9427,7 @@ pub const FuncGen = struct { } const tag_int = blk: { - const tag_ty = union_ty.unionTagTypeHypothetical(); + const tag_ty = union_ty.unionTagTypeHypothetical(mod); const union_field_name = union_obj.fields.keys()[extra.field_index]; const enum_field_index = tag_ty.enumFieldIndex(union_field_name).?; var tag_val_payload: Value.Payload.U32 = .{ diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 41b523b8f4..1176eb746d 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -755,10 +755,10 @@ pub const DeclGen = struct { const layout = ty.unionGetLayout(mod); if (layout.payload_size == 0) { - return try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); + return try self.lower(ty.unionTagTypeSafety(mod).?, tag_and_val.tag); } - const union_ty = ty.cast(Type.Payload.Union).?.data; + const union_ty = mod.typeToUnion(ty).?; if (union_ty.layout == .Packed) { return dg.todo("packed union constants", .{}); } @@ -770,7 +770,7 @@ pub const DeclGen = struct { const tag_first = layout.tag_align >= layout.payload_align; if (has_tag and tag_first) { - try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); + try self.lower(ty.unionTagTypeSafety(mod).?, tag_and_val.tag); } const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { @@ -782,7 +782,7 @@ pub const DeclGen = struct { try self.addUndef(payload_padding_len); if (has_tag and !tag_first) { - try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); + try self.lower(ty.unionTagTypeSafety(mod).?, tag_and_val.tag); } try self.addUndef(layout.padding); @@ -1121,7 +1121,7 @@ pub const DeclGen = struct { fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { const mod = self.module; const layout = ty.unionGetLayout(mod); - const union_ty = ty.cast(Type.Payload.Union).?.data; + const union_ty = mod.typeToUnion(ty).?; if (union_ty.layout == .Packed) { return self.todo("packed union types", .{}); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 7d033de584..d1e8d9601b 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -432,7 +432,7 @@ pub const DeclState = struct { }, .Union => { const layout = ty.unionGetLayout(mod); - const union_obj = ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(ty).?; const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; const is_tagged = layout.tag_size > 0; @@ -476,7 +476,7 @@ pub const DeclState = struct { try dbg_info_buffer.writer().print("{s}\x00", .{union_name}); } - const fields = ty.unionFields(); + const fields = ty.unionFields(mod); for (fields.keys()) |field_name| { const field = fields.get(field_name).?; if (!field.ty.hasRuntimeBits(mod)) continue; diff --git a/src/type.zig b/src/type.zig index 4e374a39d5..0096a96aa2 100644 --- a/src/type.zig +++ b/src/type.zig @@ -68,11 +68,6 @@ pub const Type = struct { .enum_simple, .enum_numbered, => return .Enum, - - .@"union", - .union_safety_tagged, - .union_tagged, - => return .Union, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => return .Int, @@ -140,6 +135,7 @@ pub const Type = struct { }, // values, not types + .un => unreachable, .extern_func => unreachable, .int => unreachable, .ptr => unreachable, @@ -585,12 +581,6 @@ pub const Type = struct { const b_enum_obj = (b.cast(Payload.EnumNumbered) orelse return false).data; return a_enum_obj == b_enum_obj; }, - - .@"union", .union_safety_tagged, .union_tagged => { - const a_union_obj = a.cast(Payload.Union).?.data; - const b_union_obj = (b.cast(Payload.Union) orelse return false).data; - return a_union_obj == b_union_obj; - }, } } @@ -752,12 +742,6 @@ pub const Type = struct { std.hash.autoHash(hasher, std.builtin.TypeId.Enum); std.hash.autoHash(hasher, enum_obj); }, - - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data; - std.hash.autoHash(hasher, std.builtin.TypeId.Union); - std.hash.autoHash(hasher, union_obj); - }, } } @@ -935,7 +919,6 @@ pub const Type = struct { .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred), .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), - .@"union", .union_safety_tagged, .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union), .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple), .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered), .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull), @@ -1011,12 +994,6 @@ pub const Type = struct { while (true) { const t = ty.tag(); switch (t) { - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return writer.print("({s} decl={d})", .{ - @tagName(t), union_obj.owner_decl, - }); - }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; return writer.print("({s} decl={d})", .{ @@ -1221,11 +1198,6 @@ pub const Type = struct { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - const decl = mod.declPtr(union_obj.owner_decl); - try decl.renderFullyQualifiedName(mod, writer); - }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; const decl = mod.declPtr(enum_full.owner_decl); @@ -1518,13 +1490,18 @@ pub const Type = struct { } }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + const decl = mod.declPtr(union_obj.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); + }, .opaque_type => |opaque_type| { const decl = mod.declPtr(opaque_type.decl); try decl.renderFullyQualifiedName(mod, writer); }, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -1627,45 +1604,6 @@ pub const Type = struct { return int_tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); }, - .@"union" => { - const union_obj = ty.castTag(.@"union").?.data; - if (union_obj.status == .field_types_wip) { - // In this case, we guess that hasRuntimeBits() for this type is true, - // and then later if our guess was incorrect, we emit a compile error. - union_obj.assumed_runtime_bits = true; - return true; - } - switch (strat) { - .sema => |sema| _ = try sema.resolveTypeFields(ty), - .eager => assert(union_obj.haveFieldTypes()), - .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, - } - for (union_obj.fields.values()) |value| { - if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) - return true; - } else { - return false; - } - }, - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) { - return true; - } - - switch (strat) { - .sema => |sema| _ = try sema.resolveTypeFields(ty), - .eager => assert(union_obj.haveFieldTypes()), - .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, - } - for (union_obj.fields.values()) |value| { - if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) - return true; - } else { - return false; - } - }, - .array => return ty.arrayLen(mod) != 0 and try ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), .array_sentinel => return ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), @@ -1795,10 +1733,40 @@ pub const Type = struct { } }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_type.runtime_tag) { + .none => { + if (union_obj.status == .field_types_wip) { + // In this case, we guess that hasRuntimeBits() for this type is true, + // and then later if our guess was incorrect, we emit a compile error. + union_obj.assumed_runtime_bits = true; + return true; + } + }, + .safety, .tagged => { + if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) { + return true; + } + }, + } + switch (strat) { + .sema => |sema| _ = try sema.resolveTypeFields(ty), + .eager => assert(union_obj.haveFieldTypes()), + .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, + } + for (union_obj.fields.values()) |value| { + if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) + return true; + } else { + return false; + } + }, + .opaque_type => true, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -1847,8 +1815,6 @@ pub const Type = struct { => ty.childType(mod).hasWellDefinedLayout(mod), .optional => ty.isPtrLikeOptional(mod), - .@"union", .union_safety_tagged => ty.cast(Payload.Union).?.data.layout != .Auto, - .union_tagged => false, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => true, @@ -1912,10 +1878,14 @@ pub const Type = struct { }; return struct_obj.layout != .Auto; }, - .union_type => @panic("TODO"), + .union_type => |union_type| switch (union_type.runtime_tag) { + .none, .safety => mod.unionPtr(union_type.index).layout != .Auto, + .tagged => false, + }, .opaque_type => false, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -2146,14 +2116,6 @@ pub const Type = struct { const int_tag_ty = try ty.intTagType(mod); return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(mod) }; }, - .@"union" => { - const union_obj = ty.castTag(.@"union").?.data; - return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, false); - }, - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, true); - }, .inferred_alloc_const, .inferred_alloc_mut, @@ -2312,10 +2274,14 @@ pub const Type = struct { } return AbiAlignmentAdvanced{ .scalar = big_align }; }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); + }, .opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 }, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -2508,14 +2474,6 @@ pub const Type = struct { const int_tag_ty = try ty.intTagType(mod); return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(mod) }; }, - .@"union" => { - const union_obj = ty.castTag(.@"union").?.data; - return abiSizeAdvancedUnion(ty, mod, strat, union_obj, false); - }, - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return abiSizeAdvancedUnion(ty, mod, strat, union_obj, true); - }, .array => { const payload = ty.castTag(.array).?.data; @@ -2737,10 +2695,14 @@ pub const Type = struct { return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; }, }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return abiSizeAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); + }, .opaque_type => unreachable, // no size available // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -2860,21 +2822,6 @@ pub const Type = struct { return try bitSizeAdvanced(int_tag_ty, mod, opt_sema); }, - .@"union", .union_safety_tagged, .union_tagged => { - if (opt_sema) |sema| _ = try sema.resolveTypeFields(ty); - if (ty.containerLayout(mod) != .Packed) { - return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; - } - const union_obj = ty.cast(Payload.Union).?.data; - assert(union_obj.haveFieldTypes()); - - var size: u64 = 0; - for (union_obj.fields.values()) |field| { - size = @max(size, try bitSizeAdvanced(field.ty, mod, opt_sema)); - } - return size; - }, - .array => { const payload = ty.castTag(.array).?.data; const elem_size = std.math.max(payload.elem_type.abiAlignment(mod), payload.elem_type.abiSize(mod)); @@ -2996,10 +2943,24 @@ pub const Type = struct { return try struct_obj.backing_int_ty.bitSizeAdvanced(mod, opt_sema); }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + if (opt_sema) |sema| _ = try sema.resolveTypeFields(ty); + if (ty.containerLayout(mod) != .Packed) { + return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; + } + const union_obj = mod.unionPtr(union_type.index); + assert(union_obj.haveFieldTypes()); + + var size: u64 = 0; + for (union_obj.fields.values()) |field| { + size = @max(size, try bitSizeAdvanced(field.ty, mod, opt_sema)); + } + return size; + }, .opaque_type => unreachable, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -3022,8 +2983,8 @@ pub const Type = struct { return true; }, .Union => { - if (ty.cast(Payload.Union)) |union_ty| { - return union_ty.data.haveLayout(); + if (mod.typeToUnion(ty)) |union_obj| { + return union_obj.haveLayout(); } return true; }, @@ -3413,76 +3374,71 @@ pub const Type = struct { /// Returns the tag type of a union, if the type is a union and it has a tag type. /// Otherwise, returns `null`. - pub fn unionTagType(ty: Type) ?Type { - return switch (ty.tag()) { - .union_tagged => { - const union_obj = ty.castTag(.union_tagged).?.data; - assert(union_obj.haveFieldTypes()); - return union_obj.tag_ty; + pub fn unionTagType(ty: Type, mod: *Module) ?Type { + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .union_type => |union_type| switch (union_type.runtime_tag) { + .tagged => { + const union_obj = mod.unionPtr(union_type.index); + assert(union_obj.haveFieldTypes()); + return union_obj.tag_ty; + }, + else => null, }, - else => null, }; } /// Same as `unionTagType` but includes safety tag. /// Codegen should use this version. - pub fn unionTagTypeSafety(ty: Type) ?Type { - return switch (ty.tag()) { - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; + pub fn unionTagTypeSafety(ty: Type, mod: *Module) ?Type { + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .union_type => |union_type| { + if (!union_type.hasTag()) return null; + const union_obj = mod.unionPtr(union_type.index); assert(union_obj.haveFieldTypes()); return union_obj.tag_ty; }, - else => null, }; } /// Asserts the type is a union; returns the tag type, even if the tag will /// not be stored at runtime. - pub fn unionTagTypeHypothetical(ty: Type) Type { - const union_obj = ty.cast(Payload.Union).?.data; + pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type { + const union_obj = mod.typeToUnion(ty).?; assert(union_obj.haveFieldTypes()); return union_obj.tag_ty; } - pub fn unionFields(ty: Type) Module.Union.Fields { - const union_obj = ty.cast(Payload.Union).?.data; + pub fn unionFields(ty: Type, mod: *Module) Module.Union.Fields { + const union_obj = mod.typeToUnion(ty).?; assert(union_obj.haveFieldTypes()); return union_obj.fields; } pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { - const union_obj = ty.cast(Payload.Union).?.data; + const union_obj = mod.typeToUnion(ty).?; const index = ty.unionTagFieldIndex(enum_tag, mod).?; assert(union_obj.haveFieldTypes()); return union_obj.fields.values()[index].ty; } pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize { - const union_obj = ty.cast(Payload.Union).?.data; + const union_obj = mod.typeToUnion(ty).?; const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod) orelse return null; const name = union_obj.tag_ty.enumFieldName(index); return union_obj.fields.getIndex(name); } pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool { - return ty.cast(Payload.Union).?.data.hasAllZeroBitFieldTypes(mod); + const union_obj = mod.typeToUnion(ty).?; + return union_obj.hasAllZeroBitFieldTypes(mod); } pub fn unionGetLayout(ty: Type, mod: *Module) Module.Union.Layout { - switch (ty.tag()) { - .@"union" => { - const union_obj = ty.castTag(.@"union").?.data; - return union_obj.getLayout(mod, false); - }, - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.getLayout(mod, true); - }, - else => unreachable, - } + const union_type = mod.intern_pool.indexToKey(ty.ip_index).union_type; + const union_obj = mod.unionPtr(union_type.index); + return union_obj.getLayout(mod, union_type.hasTag()); } pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout { @@ -3490,9 +3446,6 @@ pub const Type = struct { .empty_struct_type => .Auto, .none => switch (ty.tag()) { .tuple, .anon_struct => .Auto, - .@"union" => ty.castTag(.@"union").?.data.layout, - .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.layout, - .union_tagged => ty.castTag(.union_tagged).?.data.layout, else => unreachable, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { @@ -3500,6 +3453,10 @@ pub const Type = struct { const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return .Auto; return struct_obj.layout; }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.layout; + }, else => unreachable, }, }; @@ -3777,6 +3734,7 @@ pub const Type = struct { .opaque_type => unreachable, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -4038,16 +3996,6 @@ pub const Type = struct { return null; } }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null; - if (union_obj.fields.count() == 0) return Value.@"unreachable"; - const only_field = union_obj.fields.values()[0]; - const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null; - _ = tag_val; - _ = val_val; - return Value.empty_struct; - }, .array => { if (ty.arrayLen(mod) == 0) @@ -4153,10 +4101,23 @@ pub const Type = struct { return empty.toValue(); }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null; + if (union_obj.fields.count() == 0) return Value.@"unreachable"; + const only_field = union_obj.fields.values()[0]; + const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null; + const only = try mod.intern(.{ .un = .{ + .ty = ty.ip_index, + .tag = tag_val.ip_index, + .val = val_val.ip_index, + } }); + return only.toValue(); + }, .opaque_type => return null, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -4216,20 +4177,6 @@ pub const Type = struct { return false; }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Type.Payload.Union).?.data; - switch (union_obj.requires_comptime) { - .wip, .unknown => { - // Return false to avoid incorrect dependency loops. - // This will be handled correctly once merged with - // `Sema.typeRequiresComptime`. - return false; - }, - .no => return false, - .yes => return true, - } - }, - .error_union => return ty.errorUnionPayload().comptimeOnly(mod), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -4321,10 +4268,24 @@ pub const Type = struct { } }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + switch (union_obj.requires_comptime) { + .wip, .unknown => { + // Return false to avoid incorrect dependency loops. + // This will be handled correctly once merged with + // `Sema.typeRequiresComptime`. + return false; + }, + .no => return false, + .yes => return true, + } + }, + .opaque_type => false, // values, not types + .un => unreachable, .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, @@ -4378,15 +4339,13 @@ pub const Type = struct { .none => switch (ty.tag()) { .enum_full => ty.castTag(.enum_full).?.data.namespace.toOptional(), .enum_nonexhaustive => ty.castTag(.enum_nonexhaustive).?.data.namespace.toOptional(), - .@"union" => ty.castTag(.@"union").?.data.namespace.toOptional(), - .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.namespace.toOptional(), - .union_tagged => ty.castTag(.union_tagged).?.data.namespace.toOptional(), - else => .none, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .struct_type => |struct_type| struct_type.namespace, + .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + else => .none, }, }; @@ -4474,20 +4433,23 @@ pub const Type = struct { /// Asserts the type is an enum or a union. pub fn intTagType(ty: Type, mod: *Module) !Type { - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty, - .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const field_count = enum_simple.fields.count(); - const bits: u16 = if (field_count == 0) 0 else std.math.log2_int_ceil(usize, field_count); - return mod.intType(.unsigned, bits); + return switch (ty.ip_index) { + .none => switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.tag_ty, + .enum_numbered => ty.castTag(.enum_numbered).?.data.tag_ty, + .enum_simple => { + const enum_simple = ty.castTag(.enum_simple).?.data; + const field_count = enum_simple.fields.count(); + const bits: u16 = if (field_count == 0) 0 else std.math.log2_int_ceil(usize, field_count); + return mod.intType(.unsigned, bits); + }, + else => unreachable, }, - .union_tagged => { - return ty.castTag(.union_tagged).?.data.tag_ty.intTagType(mod); + else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .union_type => |union_type| mod.unionPtr(union_type.index).tag_ty.intTagType(mod), + else => unreachable, }, - else => unreachable, - } + }; } pub fn isNonexhaustiveEnum(ty: Type) bool { @@ -4663,10 +4625,6 @@ pub const Type = struct { pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type { return switch (ty.ip_index) { .none => switch (ty.tag()) { - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.fields.values()[index].ty; - }, .tuple => return ty.castTag(.tuple).?.data.types[index], .anon_struct => return ty.castTag(.anon_struct).?.data.types[index], else => unreachable, @@ -4676,6 +4634,10 @@ pub const Type = struct { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; return struct_obj.fields.values()[index].ty; }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.fields.values()[index].ty; + }, else => unreachable, }, }; @@ -4684,10 +4646,6 @@ pub const Type = struct { pub fn structFieldAlign(ty: Type, index: usize, mod: *Module) u32 { switch (ty.ip_index) { .none => switch (ty.tag()) { - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.fields.values()[index].normalAlignment(mod); - }, .tuple => return ty.castTag(.tuple).?.data.types[index].abiAlignment(mod), .anon_struct => return ty.castTag(.anon_struct).?.data.types[index].abiAlignment(mod), else => unreachable, @@ -4698,6 +4656,10 @@ pub const Type = struct { assert(struct_obj.layout != .Packed); return struct_obj.fields.values()[index].alignment(mod, struct_obj.layout); }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.fields.values()[index].normalAlignment(mod); + }, else => unreachable, }, } @@ -4889,18 +4851,6 @@ pub const Type = struct { return offset; }, - .@"union" => return 0, - .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - const layout = union_obj.getLayout(mod, true); - if (layout.tag_align >= layout.payload_align) { - // {Tag, Payload} - return std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align); - } else { - // {Payload, Tag} - return 0; - } - }, else => unreachable, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { @@ -4917,6 +4867,20 @@ pub const Type = struct { return std.mem.alignForwardGeneric(u64, it.offset, @max(it.big_align, 1)); }, + .union_type => |union_type| { + if (!union_type.hasTag()) + return 0; + const union_obj = mod.unionPtr(union_type.index); + const layout = union_obj.getLayout(mod, true); + if (layout.tag_align >= layout.payload_align) { + // {Tag, Payload} + return std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align); + } else { + // {Payload, Tag} + return 0; + } + }, + else => unreachable, }, } @@ -4946,10 +4910,6 @@ pub const Type = struct { const error_set = ty.castTag(.error_set).?.data; return error_set.srcLoc(mod); }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.srcLoc(mod); - }, else => return null, }, @@ -4958,7 +4918,10 @@ pub const Type = struct { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; return struct_obj.srcLoc(mod); }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.srcLoc(mod); + }, .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type), else => null, }, @@ -4985,10 +4948,6 @@ pub const Type = struct { const error_set = ty.castTag(.error_set).?.data; return error_set.owner_decl; }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.owner_decl; - }, else => return null, }, @@ -4997,7 +4956,10 @@ pub const Type = struct { const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return null; return struct_obj.owner_decl; }, - .union_type => @panic("TODO"), + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.owner_decl; + }, .opaque_type => |opaque_type| opaque_type.decl, else => null, }, @@ -5039,9 +5001,6 @@ pub const Type = struct { /// The type is the inferred error set of a specific function. error_set_inferred, error_set_merged, - @"union", - union_safety_tagged, - union_tagged, enum_simple, enum_numbered, enum_full, @@ -5070,7 +5029,6 @@ pub const Type = struct { .function => Payload.Function, .error_union => Payload.ErrorUnion, .error_set_single => Payload.Name, - .@"union", .union_safety_tagged, .union_tagged => Payload.Union, .enum_full, .enum_nonexhaustive => Payload.EnumFull, .enum_simple => Payload.EnumSimple, .enum_numbered => Payload.EnumNumbered, @@ -5373,11 +5331,6 @@ pub const Type = struct { }; }; - pub const Union = struct { - base: Payload, - data: *Module.Union, - }; - pub const EnumFull = struct { base: Payload, data: *Module.EnumFull, diff --git a/src/value.zig b/src/value.zig index 3992888b3d..dfeaa44428 100644 --- a/src/value.zig +++ b/src/value.zig @@ -715,7 +715,7 @@ pub const Value = struct { } pub fn tagName(val: Value, ty: Type, mod: *Module) []const u8 { - if (ty.zigTypeTag(mod) == .Union) return val.unionTag().tagName(ty.unionTagTypeHypothetical(), mod); + if (ty.zigTypeTag(mod) == .Union) return val.unionTag().tagName(ty.unionTagTypeHypothetical(mod), mod); const field_index = switch (val.tag()) { .enum_field_index => val.castTag(.enum_field_index).?.data, @@ -1138,7 +1138,7 @@ pub const Value = struct { .Extern => unreachable, // Handled in non-packed writeToMemory .Packed => { const field_index = ty.unionTagFieldIndex(val.unionTag(), mod); - const field_type = ty.unionFields().values()[field_index.?].ty; + const field_type = ty.unionFields(mod).values()[field_index.?].ty; const field_val = try val.fieldValue(field_type, mod, field_index.?); return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); @@ -2021,7 +2021,7 @@ pub const Value = struct { const b_union = b.castTag(.@"union").?.data; switch (ty.containerLayout(mod)) { .Packed, .Extern => { - const tag_ty = ty.unionTagTypeHypothetical(); + const tag_ty = ty.unionTagTypeHypothetical(mod); if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, opt_sema))) { // In this case, we must disregard mismatching tags and compare // based on the in-memory bytes of the payloads. @@ -2029,7 +2029,7 @@ pub const Value = struct { } }, .Auto => { - const tag_ty = ty.unionTagTypeHypothetical(); + const tag_ty = ty.unionTagTypeHypothetical(mod); if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, opt_sema))) { return false; } @@ -2118,7 +2118,7 @@ pub const Value = struct { return false; } const field_name = tuple.names[0]; - const union_obj = ty.cast(Type.Payload.Union).?.data; + const union_obj = mod.typeToUnion(ty).?; const field_index = union_obj.fields.getIndex(field_name) orelse return false; const tag_and_val = b.castTag(.@"union").?.data; var field_tag_buf: Value.Payload.U32 = .{ @@ -2297,7 +2297,7 @@ pub const Value = struct { }, .Union => { const union_obj = val.cast(Payload.Union).?.data; - if (ty.unionTagType()) |tag_ty| { + if (ty.unionTagType(mod)) |tag_ty| { union_obj.tag.hash(tag_ty, hasher, mod); } const active_field_ty = ty.unionFieldType(union_obj.tag, mod);