From d18881de1be811c1dff52590223b92c916c4b773 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 May 2023 19:23:41 -0700 Subject: [PATCH] stage2: move anon tuples and anon structs to InternPool --- src/InternPool.zig | 203 ++++++++- src/Sema.zig | 719 +++++++++++++++++--------------- src/TypedValue.zig | 20 +- src/arch/x86_64/CodeGen.zig | 2 +- src/codegen/c.zig | 56 ++- src/codegen/c/type.zig | 6 +- src/codegen/llvm.zig | 504 +++++++++++----------- src/codegen/spirv.zig | 11 +- src/link/Dwarf.zig | 22 +- src/type.zig | 808 +++++++++++------------------------- src/value.zig | 52 +-- 11 files changed, 1143 insertions(+), 1260 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index eace006d4c..74cc452176 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -137,9 +137,14 @@ pub const Key = union(enum) { payload_type: Index, }, simple_type: SimpleType, - /// If `empty_struct_type` is handled separately, then this value may be - /// safely assumed to never be `none`. + /// This represents a struct that has been explicitly declared in source code, + /// or was created with `@Type`. It is unique and based on a declaration. + /// It may be a tuple, if declared like this: `struct {A, B, C}`. struct_type: StructType, + /// This is an anonymous struct or tuple type which has no corresponding + /// declaration. It is used for types that have no `struct` keyword in the + /// source code, and were not created via `@Type`. + anon_struct_type: AnonStructType, union_type: UnionType, opaque_type: OpaqueType, enum_type: EnumType, @@ -168,7 +173,7 @@ pub const Key = union(enum) { /// Each element/field stored as an `Index`. /// 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, + aggregate: Key.Aggregate, /// An instance of a union. un: Union, @@ -222,22 +227,25 @@ pub const Key = union(enum) { namespace: Module.Namespace.Index, }; - /// There are three possibilities here: - /// * `@TypeOf(.{})` (untyped empty struct literal) - /// - namespace == .none, index == .none - /// * A struct which has a namepace, but no fields. - /// - index == .none - /// * A struct which has fields as well as a namepace. pub const StructType = struct { - /// 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. + /// The `none` tag is used to represent a struct with no fields. index: Module.Struct.OptionalIndex, - /// This will be `none` only in the case of `@TypeOf(.{})` - /// (`Index.empty_struct_type`). + /// May be `none` if the struct has no declarations. namespace: Module.Namespace.OptionalIndex, }; + pub const AnonStructType = struct { + types: []const Index, + /// This may be empty, indicating this is a tuple. + names: []const NullTerminatedString, + /// These elements may be `none`, indicating runtime-known. + values: []const Index, + + pub fn isTuple(self: AnonStructType) bool { + return self.names.len == 0; + } + }; + pub const UnionType = struct { index: Module.Union.Index, runtime_tag: RuntimeTag, @@ -498,6 +506,12 @@ pub const Key = union(enum) { std.hash.autoHash(hasher, aggregate.ty); for (aggregate.fields) |field| std.hash.autoHash(hasher, field); }, + + .anon_struct_type => |anon_struct_type| { + for (anon_struct_type.types) |elem| std.hash.autoHash(hasher, elem); + for (anon_struct_type.values) |elem| std.hash.autoHash(hasher, elem); + for (anon_struct_type.names) |elem| std.hash.autoHash(hasher, elem); + }, } } @@ -650,6 +664,12 @@ pub const Key = union(enum) { if (a_info.ty != b_info.ty) return false; return std.mem.eql(Index, a_info.fields, b_info.fields); }, + .anon_struct_type => |a_info| { + const b_info = b.anon_struct_type; + return std.mem.eql(Index, a_info.types, b_info.types) and + std.mem.eql(Index, a_info.values, b_info.values) and + std.mem.eql(NullTerminatedString, a_info.names, b_info.names); + }, } } @@ -666,6 +686,7 @@ pub const Key = union(enum) { .union_type, .opaque_type, .enum_type, + .anon_struct_type, => .type_type, inline .ptr, @@ -1020,9 +1041,10 @@ pub const static_keys = [_]Key{ .{ .simple_type = .var_args_param }, // empty_struct_type - .{ .struct_type = .{ - .namespace = .none, - .index = .none, + .{ .anon_struct_type = .{ + .types = &.{}, + .names = &.{}, + .values = &.{}, } }, .{ .simple_value = .undefined }, @@ -1144,6 +1166,12 @@ pub const Tag = enum(u8) { /// Module.Struct object allocated for it. /// data is Module.Namespace.Index. type_struct_ns, + /// An AnonStructType which stores types, names, and values for each field. + /// data is extra index of `TypeStructAnon`. + type_struct_anon, + /// An AnonStructType which has only types and values for each field. + /// data is extra index of `TypeStructAnon`. + type_tuple_anon, /// A tagged union type. /// `data` is `Module.Union.Index`. type_union_tagged, @@ -1249,6 +1277,26 @@ pub const Tag = enum(u8) { only_possible_value, /// data is extra index to Key.Union. union_value, + /// An instance of a struct, array, or vector. + /// data is extra index to `Aggregate`. + aggregate, +}; + +/// Trailing: +/// 0. element: Index for each len +/// len is determined by the aggregate type. +pub const Aggregate = struct { + /// The type of the aggregate. + ty: Index, +}; + +/// Trailing: +/// 0. type: Index for each fields_len +/// 1. value: Index for each fields_len +/// 2. name: NullTerminatedString for each fields_len +/// The set of field names is omitted when the `Tag` is `type_tuple_anon`. +pub const TypeStructAnon = struct { + fields_len: u32, }; /// Having `SimpleType` and `SimpleValue` in separate enums makes it easier to @@ -1572,6 +1620,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { } pub fn indexToKey(ip: InternPool, index: Index) Key { + assert(index != .none); const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { @@ -1659,6 +1708,30 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .namespace = @intToEnum(Module.Namespace.Index, data).toOptional(), } }, + .type_struct_anon => { + const type_struct_anon = ip.extraDataTrail(TypeStructAnon, data); + const fields_len = type_struct_anon.data.fields_len; + const types = ip.extra.items[type_struct_anon.end..][0..fields_len]; + const values = ip.extra.items[type_struct_anon.end + fields_len ..][0..fields_len]; + const names = ip.extra.items[type_struct_anon.end + 2 * fields_len ..][0..fields_len]; + return .{ .anon_struct_type = .{ + .types = @ptrCast([]const Index, types), + .values = @ptrCast([]const Index, values), + .names = @ptrCast([]const NullTerminatedString, names), + } }; + }, + .type_tuple_anon => { + const type_struct_anon = ip.extraDataTrail(TypeStructAnon, data); + const fields_len = type_struct_anon.data.fields_len; + const types = ip.extra.items[type_struct_anon.end..][0..fields_len]; + const values = ip.extra.items[type_struct_anon.end + fields_len ..][0..fields_len]; + return .{ .anon_struct_type = .{ + .types = @ptrCast([]const Index, types), + .values = @ptrCast([]const Index, values), + .names = &.{}, + } }; + }, + .type_union_untagged => .{ .union_type = .{ .index = @intToEnum(Module.Union.Index, data), .runtime_tag = .none, @@ -1797,6 +1870,15 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { else => unreachable, }; }, + .aggregate => { + const extra = ip.extraDataTrail(Aggregate, data); + const len = @intCast(u32, ip.aggregateTypeLen(extra.data.ty)); + const fields = @ptrCast([]const Index, ip.extra.items[extra.end..][0..len]); + return .{ .aggregate = .{ + .ty = extra.data.ty, + .fields = fields, + } }; + }, .union_value => .{ .un = ip.extraData(Key.Union, data) }, .enum_tag => .{ .enum_tag = ip.extraData(Key.EnumTag, data) }, }; @@ -1982,6 +2064,45 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }); }, + .anon_struct_type => |anon_struct_type| { + assert(anon_struct_type.types.len == anon_struct_type.values.len); + for (anon_struct_type.types) |elem| assert(elem != .none); + + const fields_len = @intCast(u32, anon_struct_type.types.len); + if (anon_struct_type.names.len == 0) { + try ip.extra.ensureUnusedCapacity( + gpa, + @typeInfo(TypeStructAnon).Struct.fields.len + (fields_len * 2), + ); + ip.items.appendAssumeCapacity(.{ + .tag = .type_tuple_anon, + .data = ip.addExtraAssumeCapacity(TypeStructAnon{ + .fields_len = fields_len, + }), + }); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, anon_struct_type.types)); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, anon_struct_type.values)); + return @intToEnum(Index, ip.items.len - 1); + } + + assert(anon_struct_type.names.len == anon_struct_type.types.len); + + try ip.extra.ensureUnusedCapacity( + gpa, + @typeInfo(TypeStructAnon).Struct.fields.len + (fields_len * 3), + ); + ip.items.appendAssumeCapacity(.{ + .tag = .type_struct_anon, + .data = ip.addExtraAssumeCapacity(TypeStructAnon{ + .fields_len = fields_len, + }), + }); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, anon_struct_type.types)); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, anon_struct_type.values)); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, anon_struct_type.names)); + return @intToEnum(Index, ip.items.len - 1); + }, + .union_type => |union_type| { ip.items.appendAssumeCapacity(.{ .tag = switch (union_type.runtime_tag) { @@ -2269,6 +2390,16 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, .aggregate => |aggregate| { + assert(aggregate.ty != .none); + for (aggregate.fields) |elem| assert(elem != .none); + if (aggregate.fields.len != ip.aggregateTypeLen(aggregate.ty)) { + std.debug.print("aggregate fields len = {d}, type len = {d}\n", .{ + aggregate.fields.len, + ip.aggregateTypeLen(aggregate.ty), + }); + } + assert(aggregate.fields.len == ip.aggregateTypeLen(aggregate.ty)); + if (aggregate.fields.len == 0) { ip.items.appendAssumeCapacity(.{ .tag = .only_possible_value, @@ -2276,7 +2407,19 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }); return @intToEnum(Index, ip.items.len - 1); } - @panic("TODO"); + + try ip.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Aggregate).Struct.fields.len + aggregate.fields.len, + ); + + ip.items.appendAssumeCapacity(.{ + .tag = .aggregate, + .data = ip.addExtraAssumeCapacity(Aggregate{ + .ty = aggregate.ty, + }), + }); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, aggregate.fields)); }, .un => |un| { @@ -2913,6 +3056,14 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { .type_opaque => @sizeOf(Key.OpaqueType), .type_struct => @sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), .type_struct_ns => @sizeOf(Module.Namespace), + .type_struct_anon => b: { + const info = ip.extraData(TypeStructAnon, data); + break :b @sizeOf(TypeStructAnon) + (@sizeOf(u32) * 3 * info.fields_len); + }, + .type_tuple_anon => b: { + const info = ip.extraData(TypeStructAnon, data); + break :b @sizeOf(TypeStructAnon) + (@sizeOf(u32) * 2 * info.fields_len); + }, .type_union_tagged, .type_union_untagged, @@ -2942,6 +3093,12 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { }, .enum_tag => @sizeOf(Key.EnumTag), + .aggregate => b: { + const info = ip.extraData(Aggregate, data); + const fields_len = @intCast(u32, ip.aggregateTypeLen(info.ty)); + break :b @sizeOf(Aggregate) + (@sizeOf(u32) * fields_len); + }, + .float_f16 => 0, .float_f32 => 0, .float_f64 => @sizeOf(Float64), @@ -3079,3 +3236,13 @@ pub fn toEnum(ip: InternPool, comptime E: type, i: Index) E { const int = ip.indexToKey(i).enum_tag.int; return @intToEnum(E, ip.indexToKey(int).int.storage.u64); } + +pub fn aggregateTypeLen(ip: InternPool, ty: Index) u64 { + return switch (ip.indexToKey(ty)) { + .struct_type => |struct_type| ip.structPtrConst(struct_type.index.unwrap() orelse return 0).fields.count(), + .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, + .array_type => |array_type| array_type.len, + .vector_type => |vector_type| vector_type.len, + else => unreachable, + }; +} diff --git a/src/Sema.zig b/src/Sema.zig index 2fc364ebd7..31e07bdcdc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7896,12 +7896,15 @@ fn resolveGenericInstantiationType( } fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - if (!ty.isSimpleTupleOrAnonStruct()) return; - const tuple = ty.tupleFields(); - for (tuple.values, 0..) |field_val, i| { - try sema.resolveTupleLazyValues(block, src, tuple.types[i]); - if (field_val.ip_index == .unreachable_value) continue; - try sema.resolveLazyValue(field_val); + const mod = sema.mod; + const tuple = switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |tuple| tuple, + else => return, + }; + for (tuple.types, tuple.values) |field_ty, field_val| { + try sema.resolveTupleLazyValues(block, src, field_ty.toType()); + if (field_val == .none) continue; + try sema.resolveLazyValue(field_val.toValue()); } } @@ -12038,31 +12041,49 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstString(block, name_src, extra.rhs, "field name must be comptime-known"); const ty = try sema.resolveTypeFields(unresolved_ty); + const ip = &mod.intern_pool; const has_field = hf: { - if (ty.isSlice(mod)) { - if (mem.eql(u8, field_name, "ptr")) break :hf true; - if (mem.eql(u8, field_name, "len")) break :hf true; - break :hf false; + switch (ip.indexToKey(ty.ip_index)) { + .ptr_type => |ptr_type| switch (ptr_type.size) { + .Slice => { + if (mem.eql(u8, field_name, "ptr")) break :hf true; + if (mem.eql(u8, field_name, "len")) break :hf true; + break :hf false; + }, + else => {}, + }, + .anon_struct_type => |anon_struct| { + if (anon_struct.names.len != 0) { + // If the string is not interned, then the field certainly is not present. + const name_interned = ip.getString(field_name).unwrap() orelse break :hf false; + break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names, name_interned) != null; + } else { + const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch break :hf false; + break :hf field_index < ty.structFieldCount(mod); + } + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :hf false; + assert(struct_obj.haveFieldTypes()); + break :hf struct_obj.fields.contains(field_name); + }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + assert(union_obj.haveFieldTypes()); + break :hf union_obj.fields.contains(field_name); + }, + .enum_type => |enum_type| { + // If the string is not interned, then the field certainly is not present. + const name_interned = ip.getString(field_name).unwrap() orelse break :hf false; + break :hf enum_type.nameIndex(ip, name_interned) != null; + }, + .array_type => break :hf mem.eql(u8, field_name, "len"), + else => {}, } - if (ty.castTag(.anon_struct)) |pl| { - break :hf for (pl.data.names) |name| { - if (mem.eql(u8, name, field_name)) break true; - } else false; - } - if (ty.isTuple(mod)) { - const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch break :hf false; - break :hf field_index < ty.structFieldCount(mod); - } - break :hf switch (ty.zigTypeTag(mod)) { - .Struct => ty.structFields(mod).contains(field_name), - .Union => ty.unionFields(mod).contains(field_name), - .Enum => ty.enumFieldIndex(field_name, mod) != null, - .Array => mem.eql(u8, field_name, "len"), - else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ - ty.fmt(sema.mod), - }), - }; + return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ + ty.fmt(sema.mod), + }); }; if (has_field) { return Air.Inst.Ref.bool_true; @@ -12632,42 +12653,48 @@ fn analyzeTupleCat( } const final_len = try sema.usizeCast(block, rhs_src, dest_fields); - const types = try sema.arena.alloc(Type, final_len); - const values = try sema.arena.alloc(Value, final_len); + const types = try sema.arena.alloc(InternPool.Index, final_len); + const values = try sema.arena.alloc(InternPool.Index, final_len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; var i: u32 = 0; while (i < lhs_len) : (i += 1) { - types[i] = lhs_ty.structFieldType(i, mod); + types[i] = lhs_ty.structFieldType(i, mod).ip_index; const default_val = lhs_ty.structFieldDefaultValue(i, mod); - values[i] = default_val; + values[i] = default_val.ip_index; const operand_src = lhs_src; // TODO better source location if (default_val.ip_index == .unreachable_value) { runtime_src = operand_src; + values[i] = .none; } } i = 0; while (i < rhs_len) : (i += 1) { - types[i + lhs_len] = rhs_ty.structFieldType(i, mod); + types[i + lhs_len] = rhs_ty.structFieldType(i, mod).ip_index; const default_val = rhs_ty.structFieldDefaultValue(i, mod); - values[i + lhs_len] = default_val; + values[i + lhs_len] = default_val.ip_index; const operand_src = rhs_src; // TODO better source location if (default_val.ip_index == .unreachable_value) { runtime_src = operand_src; + values[i + lhs_len] = .none; } } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(tuple_ty, tuple_val); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .fields = values, + } }); + return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -12685,7 +12712,7 @@ fn analyzeTupleCat( try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12938,7 +12965,7 @@ fn analyzeTupleMul( block: *Block, src_node: i32, operand: Air.Inst.Ref, - factor: u64, + factor: usize, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const operand_ty = sema.typeOf(operand); @@ -12947,44 +12974,45 @@ fn analyzeTupleMul( const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; const tuple_len = operand_ty.structFieldCount(mod); - const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch + const final_len = std.math.mul(usize, tuple_len, factor) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); - if (final_len_u64 == 0) { + if (final_len == 0) { return sema.addConstant(Type.empty_struct_literal, Value.empty_struct); } - const final_len = try sema.usizeCast(block, rhs_src, final_len_u64); - - const types = try sema.arena.alloc(Type, final_len); - const values = try sema.arena.alloc(Value, final_len); + const types = try sema.arena.alloc(InternPool.Index, final_len); + const values = try sema.arena.alloc(InternPool.Index, final_len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; - var i: u32 = 0; - while (i < tuple_len) : (i += 1) { - types[i] = operand_ty.structFieldType(i, mod); - values[i] = operand_ty.structFieldDefaultValue(i, mod); + for (0..tuple_len) |i| { + types[i] = operand_ty.structFieldType(i, mod).ip_index; + values[i] = operand_ty.structFieldDefaultValue(i, mod).ip_index; const operand_src = lhs_src; // TODO better source location - if (values[i].ip_index == .unreachable_value) { + if (values[i] == .unreachable_value) { runtime_src = operand_src; + values[i] = .none; // TODO don't treat unreachable_value as special } } - i = 0; - while (i < factor) : (i += 1) { - mem.copyForwards(Type, types[tuple_len * i ..], types[0..tuple_len]); - mem.copyForwards(Value, values[tuple_len * i ..], values[0..tuple_len]); + for (0..factor) |i| { + mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); + mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstant(tuple_ty, tuple_val); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .fields = values, + } }); + return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -13000,7 +13028,7 @@ fn analyzeTupleMul( @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13020,7 +13048,8 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (lhs_ty.isTuple(mod)) { // In `**` rhs must be comptime-known, but lhs can be runtime-known const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known"); - return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); + const factor_casted = try sema.usizeCast(block, rhs_src, factor); + return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); } // Analyze the lhs first, to catch the case that someone tried to do exponentiation @@ -14533,19 +14562,14 @@ fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { .child = .u1_type, }) else Type.u1; - const types = try sema.arena.alloc(Type, 2); - const values = try sema.arena.alloc(Value, 2); - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ - .types = types, - .values = values, - }); - - types[0] = ty; - types[1] = ov_ty; - values[0] = Value.@"unreachable"; - values[1] = Value.@"unreachable"; - - return tuple_ty; + const types = [2]InternPool.Index{ ty.ip_index, ov_ty.ip_index }; + const values = [2]InternPool.Index{ .none, .none }; + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ + .types = &types, + .values = &values, + .names = &.{}, + } }); + return tuple_ty.toType(); } fn analyzeArithmetic( @@ -16506,57 +16530,66 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const layout = struct_ty.containerLayout(mod); const struct_field_vals = fv: { - if (struct_ty.isSimpleTupleOrAnonStruct()) { - const tuple = struct_ty.tupleFields(); - const field_types = tuple.types; - const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len); - for (struct_field_vals, 0..) |*struct_field_val, i| { - const field_ty = field_types[i]; - const name_val = v: { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - const bytes = if (struct_ty.castTag(.anon_struct)) |payload| - try anon_decl.arena().dupeZ(u8, payload.data.names[i]) - else - try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i}); - const new_decl = try anon_decl.finish( - try Type.array(anon_decl.arena(), bytes.len, Value.zero_u8, Type.u8, mod), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), - 0, // default alignment - ); - break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ - .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), - .len = try mod.intValue(Type.usize, bytes.len), - }); - }; + const struct_type = switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { + .anon_struct_type => |tuple| { + const struct_field_vals = try fields_anon_decl.arena().alloc(Value, tuple.types.len); + for ( + tuple.types, + tuple.values, + struct_field_vals, + 0.., + ) |field_ty, field_val, *struct_field_val, i| { + const name_val = v: { + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + const bytes = if (tuple.names.len != 0) + // https://github.com/ziglang/zig/issues/15709 + @as([]const u8, mod.intern_pool.stringToSlice(tuple.names[i])) + else + try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i}); + const new_decl = try anon_decl.finish( + try Type.array(anon_decl.arena(), bytes.len, Value.zero_u8, Type.u8, mod), + try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment + ); + break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ + .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), + .len = try mod.intValue(Type.usize, bytes.len), + }); + }; - const struct_field_fields = try fields_anon_decl.arena().create([5]Value); - const field_val = tuple.values[i]; - const is_comptime = field_val.ip_index != .unreachable_value; - const opt_default_val = if (is_comptime) field_val else null; - const default_val_ptr = try sema.optRefValue(block, field_ty, opt_default_val); - struct_field_fields.* = .{ - // name: []const u8, - name_val, - // type: type, - try Value.Tag.ty.create(fields_anon_decl.arena(), field_ty), - // default_value: ?*const anyopaque, - try default_val_ptr.copy(fields_anon_decl.arena()), - // is_comptime: bool, - Value.makeBool(is_comptime), - // alignment: comptime_int, - try field_ty.lazyAbiAlignment(mod, fields_anon_decl.arena()), - }; - struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields); - } - break :fv struct_field_vals; - } - const struct_fields = struct_ty.structFields(mod); - const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_fields.count()); + const struct_field_fields = try fields_anon_decl.arena().create([5]Value); + const is_comptime = field_val != .none; + const opt_default_val = if (is_comptime) field_val.toValue() else null; + const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val); + struct_field_fields.* = .{ + // name: []const u8, + name_val, + // type: type, + field_ty.toValue(), + // default_value: ?*const anyopaque, + try default_val_ptr.copy(fields_anon_decl.arena()), + // is_comptime: bool, + Value.makeBool(is_comptime), + // alignment: comptime_int, + try field_ty.toType().lazyAbiAlignment(mod, fields_anon_decl.arena()), + }; + struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields); + } + break :fv struct_field_vals; + }, + .struct_type => |s| s, + else => unreachable, + }; + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse + break :fv &[0]Value{}; + const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_obj.fields.count()); - for (struct_field_vals, 0..) |*field_val, i| { - const field = struct_fields.values()[i]; - const name = struct_fields.keys()[i]; + for ( + struct_field_vals, + struct_obj.fields.keys(), + struct_obj.fields.values(), + ) |*field_val, name, field| { const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); @@ -18013,7 +18046,7 @@ fn zirStructInit( try sema.requireRuntimeBlock(block, src, null); try sema.queueFullTypeResolution(resolved_ty); return block.addUnionInit(resolved_ty, field_index, init_inst); - } else if (resolved_ty.isAnonStruct()) { + } else if (resolved_ty.isAnonStruct(mod)) { return sema.fail(block, src, "TODO anon struct init validation", .{}); } unreachable; @@ -18034,60 +18067,54 @@ fn finishStructInit( var root_msg: ?*Module.ErrorMsg = null; errdefer if (root_msg) |msg| msg.destroy(sema.gpa); - if (struct_ty.isAnonStruct()) { - const struct_obj = struct_ty.castTag(.anon_struct).?.data; - for (struct_obj.values, 0..) |default_val, i| { - if (field_inits[i] != .none) continue; + switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { + .anon_struct_type => |anon_struct| { + for (anon_struct.types, anon_struct.values, 0..) |field_ty, default_val, i| { + if (field_inits[i] != .none) continue; - if (default_val.ip_index == .unreachable_value) { - const field_name = struct_obj.names[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, args); + if (default_val == .none) { + if (anon_struct.names.len == 0) { + const template = "missing tuple field with index {d}"; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, .{i}); + } else { + root_msg = try sema.errMsg(block, init_src, template, .{i}); + } + } else { + const field_name = mod.intern_pool.stringToSlice(anon_struct.names[i]); + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } + } } else { - root_msg = try sema.errMsg(block, init_src, template, args); + field_inits[i] = try sema.addConstant(field_ty.toType(), default_val.toValue()); } - } else { - field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val); } - } - } else if (struct_ty.isTuple(mod)) { - var i: u32 = 0; - const len = struct_ty.structFieldCount(mod); - while (i < len) : (i += 1) { - if (field_inits[i] != .none) continue; + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + for (struct_obj.fields.values(), 0..) |field, i| { + if (field_inits[i] != .none) continue; - const default_val = struct_ty.structFieldDefaultValue(i, mod); - if (default_val.ip_index == .unreachable_value) { - const template = "missing tuple field with index {d}"; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, .{i}); + if (field.default_val.ip_index == .unreachable_value) { + const field_name = struct_obj.fields.keys()[i]; + const template = "missing struct field: {s}"; + const args = .{field_name}; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, init_src, template, args); + } } else { - root_msg = try sema.errMsg(block, init_src, template, .{i}); + field_inits[i] = try sema.addConstant(field.ty, field.default_val); } - } else { - field_inits[i] = try sema.addConstant(struct_ty.structFieldType(i, mod), default_val); } - } - } else { - const struct_obj = mod.typeToStruct(struct_ty).?; - for (struct_obj.fields.values(), 0..) |field, i| { - if (field_inits[i] != .none) continue; - - if (field.default_val.ip_index == .unreachable_value) { - const field_name = struct_obj.fields.keys()[i]; - const template = "missing struct field: {s}"; - const args = .{field_name}; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(block, init_src, template, args); - } - } else { - field_inits[i] = try sema.addConstant(field.ty, field.default_val); - } - } + }, + else => unreachable, } if (root_msg) |msg| { @@ -18159,31 +18186,33 @@ fn zirStructInitAnon( is_ref: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; + const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); - const types = try sema.arena.alloc(Type, extra.data.fields_len); - const values = try sema.arena.alloc(Value, types.len); - var fields = std.StringArrayHashMapUnmanaged(u32){}; - defer fields.deinit(sema.gpa); - try fields.ensureUnusedCapacity(sema.gpa, types.len); + const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len); + const values = try sema.arena.alloc(InternPool.Index, types.len); + var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena); + try fields.ensureUnusedCapacity(types.len); // Find which field forces the expression to be runtime, if any. const opt_runtime_index = rs: { var runtime_index: ?usize = null; var extra_index = extra.end; - for (types, 0..) |*field_ty, i| { + for (types, 0..) |*field_ty, i_usize| { + const i = @intCast(u32, i_usize); const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; const name = sema.code.nullTerminatedString(item.data.field_name); - const gop = fields.getOrPutAssumeCapacity(name); + const name_ip = try mod.intern_pool.getOrPutString(gpa, name); + const gop = fields.getOrPutAssumeCapacity(name_ip); if (gop.found_existing) { const msg = msg: { const decl = sema.mod.declPtr(block.src_decl); const field_src = mod.initSrc(src.node_offset.x, decl, i); const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*); try sema.errNote(block, prev_source, msg, "other field here", .{}); @@ -18191,41 +18220,44 @@ fn zirStructInitAnon( }; return sema.failWithOwnedErrorMsg(msg); } - gop.value_ptr.* = @intCast(u32, i); + gop.value_ptr.* = i; const init = try sema.resolveInst(item.data.init); - field_ty.* = sema.typeOf(init); - if (types[i].zigTypeTag(mod) == .Opaque) { + field_ty.* = sema.typeOf(init).ip_index; + if (types[i].toType().zigTypeTag(mod) == .Opaque) { const msg = msg: { const decl = sema.mod.declPtr(block.src_decl); const field_src = mod.initSrc(src.node_offset.x, decl, i); const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, types[i]); + try sema.addDeclaredHereNote(msg, types[i].toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } if (try sema.resolveMaybeUndefVal(init)) |init_val| { - values[i] = init_val; + values[i] = init_val.ip_index; } else { - values[i] = Value.@"unreachable"; + values[i] = .none; runtime_index = i; } } break :rs runtime_index; }; - const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{ - .names = try sema.arena.dupe([]const u8, fields.keys()), + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ + .names = fields.keys(), .types = types, .values = values, - }); + } }); const runtime_index = opt_runtime_index orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, tuple_ty, tuple_val, is_ref); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .fields = values, + } }); + return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { @@ -18241,7 +18273,7 @@ fn zirStructInitAnon( if (is_ref) { const target = sema.mod.getTarget(); const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = tuple_ty, + .pointee_type = tuple_ty.toType(), .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -18254,9 +18286,9 @@ fn zirStructInitAnon( const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = field_ty, + .pointee_type = field_ty.toType(), }); - if (values[i].ip_index == .unreachable_value) { + if (values[i] == .none) { const init = try sema.resolveInst(item.data.init); const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, init); @@ -18274,7 +18306,7 @@ fn zirStructInitAnon( element_refs[i] = try sema.resolveInst(item.data.init); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn zirArrayInit( @@ -18400,43 +18432,47 @@ fn zirArrayInitAnon( const operands = sema.code.refSlice(extra.end, extra.data.operands_len); const mod = sema.mod; - const types = try sema.arena.alloc(Type, operands.len); - const values = try sema.arena.alloc(Value, operands.len); + const types = try sema.arena.alloc(InternPool.Index, operands.len); + const values = try sema.arena.alloc(InternPool.Index, operands.len); const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; for (operands, 0..) |operand, i| { const operand_src = src; // TODO better source location const elem = try sema.resolveInst(operand); - types[i] = sema.typeOf(elem); - if (types[i].zigTypeTag(mod) == .Opaque) { + types[i] = sema.typeOf(elem).ip_index; + if (types[i].toType().zigTypeTag(mod) == .Opaque) { const msg = msg: { const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, types[i]); + try sema.addDeclaredHereNote(msg, types[i].toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } if (try sema.resolveMaybeUndefVal(elem)) |val| { - values[i] = val; + values[i] = val.ip_index; } else { - values[i] = Value.@"unreachable"; + values[i] = .none; runtime_src = operand_src; } } break :rs runtime_src; }; - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ .types = types, .values = values, - }); + .names = &.{}, + } }); const runtime_src = opt_runtime_src orelse { - const tuple_val = try Value.Tag.aggregate.create(sema.arena, values); - return sema.addConstantMaybeRef(block, tuple_ty, tuple_val, is_ref); + const tuple_val = try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty, + .fields = values, + } }); + return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -18444,7 +18480,7 @@ fn zirArrayInitAnon( if (is_ref) { const target = sema.mod.getTarget(); const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = tuple_ty, + .pointee_type = tuple_ty.toType(), .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -18453,9 +18489,9 @@ fn zirArrayInitAnon( const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = types[i], + .pointee_type = types[i].toType(), }); - if (values[i].ip_index == .unreachable_value) { + if (values[i] == .none) { const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); } @@ -18469,7 +18505,7 @@ fn zirArrayInitAnon( element_refs[i] = try sema.resolveInst(operand); } - return block.addAggregateInit(tuple_ty, element_refs); + return block.addAggregateInit(tuple_ty.toType(), element_refs); } fn addConstantMaybeRef( @@ -18532,15 +18568,18 @@ fn fieldType( const resolved_ty = try sema.resolveTypeFields(cur_ty); cur_ty = resolved_ty; switch (cur_ty.zigTypeTag(mod)) { - .Struct => { - if (cur_ty.isAnonStruct()) { + .Struct => switch (mod.intern_pool.indexToKey(cur_ty.ip_index)) { + .anon_struct_type => |anon_struct| { const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); - return sema.addType(cur_ty.tupleFields().types[field_index]); - } - const struct_obj = mod.typeToStruct(cur_ty).?; - const field = struct_obj.fields.get(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); - return sema.addType(field.ty); + return sema.addType(anon_struct.types[field_index].toType()); + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + const field = struct_obj.fields.get(field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); + return sema.addType(field.ty); + }, + else => unreachable, }, .Union => { const union_obj = mod.typeToUnion(cur_ty).?; @@ -24697,7 +24736,7 @@ fn structFieldPtr( } const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); - } else if (struct_ty.isAnonStruct()) { + } else if (struct_ty.isAnonStruct(mod)) { const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); } @@ -24721,11 +24760,11 @@ fn structFieldPtrByIndex( struct_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { - if (struct_ty.isAnonStruct()) { + const mod = sema.mod; + if (struct_ty.isAnonStruct(mod)) { return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); } - const mod = sema.mod; const struct_obj = mod.typeToStruct(struct_ty).?; const field = struct_obj.fields.values()[field_index]; const struct_ptr_ty = sema.typeOf(struct_ptr); @@ -24830,45 +24869,42 @@ fn structFieldVal( assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); - switch (struct_ty.ip_index) { - .empty_struct_type => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty), - .none => switch (struct_ty.tag()) { - .tuple => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty), - .anon_struct => { + switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); + + const field_index_usize = struct_obj.fields.getIndex(field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); + const field_index = @intCast(u32, field_index_usize); + const field = struct_obj.fields.values()[field_index]; + + if (field.is_comptime) { + return sema.addConstant(field.ty, field.default_val); + } + + if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| { + if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty); + if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| { + return sema.addConstant(field.ty, opv); + } + + const field_values = struct_val.castTag(.aggregate).?.data; + return sema.addConstant(field.ty, field_values[field_index]); + } + + try sema.requireRuntimeBlock(block, src, null); + return block.addStructFieldVal(struct_byval, field_index, field.ty); + }, + .anon_struct_type => |anon_struct| { + if (anon_struct.names.len == 0) { + return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); + } else { const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); - }, - else => unreachable, - }, - else => switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); - - const field_index_usize = struct_obj.fields.getIndex(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); - const field_index = @intCast(u32, field_index_usize); - const field = struct_obj.fields.values()[field_index]; - - if (field.is_comptime) { - return sema.addConstant(field.ty, field.default_val); - } - - if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| { - if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty); - if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| { - return sema.addConstant(field.ty, opv); - } - - const field_values = struct_val.castTag(.aggregate).?.data; - return sema.addConstant(field.ty, field_values[field_index]); - } - - try sema.requireRuntimeBlock(block, src, null); - return block.addStructFieldVal(struct_byval, field_index, field.ty); - }, - else => unreachable, + } }, + else => unreachable, } } @@ -25931,7 +25967,7 @@ fn coerceExtra( .Union => { // pointer to anonymous struct to pointer to union if (inst_ty.isSinglePointer(mod) and - inst_ty.childType(mod).isAnonStruct() and + inst_ty.childType(mod).isAnonStruct(mod) and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); @@ -25940,7 +25976,7 @@ fn coerceExtra( .Struct => { // pointer to anonymous struct to pointer to struct if (inst_ty.isSinglePointer(mod) and - inst_ty.childType(mod).isAnonStruct() and + inst_ty.childType(mod).isAnonStruct(mod) and sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) { return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { @@ -26231,7 +26267,7 @@ fn coerceExtra( .Union => switch (inst_ty.zigTypeTag(mod)) { .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), .Struct => { - if (inst_ty.isAnonStruct()) { + if (inst_ty.isAnonStruct(mod)) { return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); } }, @@ -28771,8 +28807,8 @@ fn coerceAnonStructToUnion( return sema.failWithOwnedErrorMsg(msg); } - const anon_struct = inst_ty.castTag(.anon_struct).?.data; - const field_name = anon_struct.names[0]; + const anon_struct = mod.intern_pool.indexToKey(inst_ty.ip_index).anon_struct_type; + const field_name = mod.intern_pool.stringToSlice(anon_struct.names[0]); const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); } @@ -29010,13 +29046,14 @@ fn coerceTupleToStruct( @memset(field_refs, .none); const inst_ty = sema.typeOf(inst); + const anon_struct = mod.intern_pool.indexToKey(inst_ty.ip_index).anon_struct_type; var runtime_src: ?LazySrcLoc = null; - const field_count = inst_ty.structFieldCount(mod); - var field_i: u32 = 0; - while (field_i < field_count) : (field_i += 1) { + for (0..anon_struct.types.len) |field_index_usize| { + const field_i = @intCast(u32, field_index_usize); const field_src = inst_src; // TODO better source location - const field_name = if (inst_ty.castTag(.anon_struct)) |payload| - payload.data.names[field_i] + const field_name = if (anon_struct.names.len != 0) + // https://github.com/ziglang/zig/issues/15709 + @as([]const u8, mod.intern_pool.stringToSlice(anon_struct.names[field_i])) else try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); @@ -29094,21 +29131,22 @@ fn coerceTupleToTuple( inst_src: LazySrcLoc, ) !Air.Inst.Ref { const mod = sema.mod; - const dest_field_count = tuple_ty.structFieldCount(mod); - const field_vals = try sema.arena.alloc(Value, dest_field_count); + const dest_tuple = mod.intern_pool.indexToKey(tuple_ty.ip_index).anon_struct_type; + const field_vals = try sema.arena.alloc(InternPool.Index, dest_tuple.types.len); const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); @memset(field_refs, .none); const inst_ty = sema.typeOf(inst); - const inst_field_count = inst_ty.structFieldCount(mod); - if (inst_field_count > dest_field_count) return error.NotCoercible; + const src_tuple = mod.intern_pool.indexToKey(inst_ty.ip_index).anon_struct_type; + if (src_tuple.types.len > dest_tuple.types.len) return error.NotCoercible; var runtime_src: ?LazySrcLoc = null; - var field_i: u32 = 0; - while (field_i < inst_field_count) : (field_i += 1) { + for (dest_tuple.types, dest_tuple.values, 0..) |field_ty, default_val, field_index_usize| { + const field_i = @intCast(u32, field_index_usize); const field_src = inst_src; // TODO better source location - const field_name = if (inst_ty.castTag(.anon_struct)) |payload| - payload.data.names[field_i] + const field_name = if (src_tuple.names.len != 0) + // https://github.com/ziglang/zig/issues/15709 + @as([]const u8, mod.intern_pool.stringToSlice(src_tuple.names[field_i])) else try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); @@ -29118,23 +29156,21 @@ fn coerceTupleToTuple( const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); - const field_ty = tuple_ty.structFieldType(field_i, mod); - const default_val = tuple_ty.structFieldDefaultValue(field_i, mod); const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); - const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); + const coerced = try sema.coerce(block, field_ty.toType(), elem_ref, field_src); field_refs[field_index] = coerced; - if (default_val.ip_index != .unreachable_value) { + if (default_val != .none) { const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); }; - if (!init_val.eql(default_val, field_ty, sema.mod)) { + if (!init_val.eql(default_val.toValue(), field_ty.toType(), sema.mod)) { return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); } } if (runtime_src == null) { if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { - field_vals[field_index] = field_val; + field_vals[field_index] = field_val.ip_index; } else { runtime_src = field_src; } @@ -29145,14 +29181,16 @@ fn coerceTupleToTuple( var root_msg: ?*Module.ErrorMsg = null; errdefer if (root_msg) |msg| msg.destroy(sema.gpa); - for (field_refs, 0..) |*field_ref, i| { + for ( + dest_tuple.types, + dest_tuple.values, + field_refs, + 0.., + ) |field_ty, default_val, *field_ref, i| { if (field_ref.* != .none) continue; - const default_val = tuple_ty.structFieldDefaultValue(i, mod); - const field_ty = tuple_ty.structFieldType(i, mod); - const field_src = inst_src; // TODO better source location - if (default_val.ip_index == .unreachable_value) { + if (default_val == .none) { if (tuple_ty.isTuple(mod)) { const template = "missing tuple field: {d}"; if (root_msg) |msg| { @@ -29174,7 +29212,7 @@ fn coerceTupleToTuple( if (runtime_src == null) { field_vals[i] = default_val; } else { - field_ref.* = try sema.addConstant(field_ty, default_val); + field_ref.* = try sema.addConstant(field_ty.toType(), default_val.toValue()); } } @@ -29191,7 +29229,10 @@ fn coerceTupleToTuple( return sema.addConstant( tuple_ty, - try Value.Tag.aggregate.create(sema.arena, field_vals), + (try mod.intern(.{ .aggregate = .{ + .ty = tuple_ty.ip_index, + .fields = field_vals, + } })).toValue(), ); } @@ -31591,17 +31632,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { return sema.resolveTypeRequiresComptime(ty.optionalChild(mod)); }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; - if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) { - return true; - } - } - return false; - }, - .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -31690,6 +31720,16 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { } }, + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, field_val| { + const have_comptime_val = field_val != .none; + if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) { + return true; + } + } + return false; + }, + .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); switch (union_obj.requires_comptime) { @@ -31740,18 +31780,14 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { return sema.resolveTypeFully(child_ty); }, .Struct => switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - - for (tuple.types) |field_ty| { - try sema.resolveTypeFully(field_ty); - } - }, - else => {}, - }, + .none => {}, // TODO make this unreachable when all types are migrated to InternPool else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .struct_type => return sema.resolveStructFully(ty), + .anon_struct_type => |tuple| { + for (tuple.types) |field_ty| { + try sema.resolveTypeFully(field_ty.toType()); + } + }, else => {}, }, }, @@ -33038,17 +33074,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { } }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.values, 0..) |val, i| { - const is_comptime = val.ip_index != .unreachable_value; - if (is_comptime) continue; - if ((try sema.typeHasOnePossibleValue(tuple.types[i])) != null) continue; - return null; - } - return Value.empty_struct; - }, - .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, }, @@ -33150,7 +33175,36 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { } } } - // In this case the struct has no fields and therefore has one possible value. + // In this case the struct has no runtime-known fields and + // therefore has one possible value. + + // TODO: this is incorrect for structs with comptime fields, I think + // we should use a temporary allocator to construct an aggregate that + // is populated with the comptime values and then intern that value here. + // This TODO is repeated for anon_struct_type below, as well as + // in the redundant implementation of one-possible-value in type.zig. + const empty = try mod.intern(.{ .aggregate = .{ + .ty = ty.ip_index, + .fields = &.{}, + } }); + return empty.toValue(); + }, + + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + const is_comptime = val != .none; + if (is_comptime) continue; + if ((try sema.typeHasOnePossibleValue(field_ty.toType())) != null) continue; + return null; + } + // In this case the struct has no runtime-known fields and + // therefore has one possible value. + + // TODO: this is incorrect for structs with comptime fields, I think + // we should use a temporary allocator to construct an aggregate that + // is populated with the comptime values and then intern that value here. + // This TODO is repeated for struct_type above, as well as + // in the redundant implementation of one-possible-value in type.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, .fields = &.{}, @@ -33647,17 +33701,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { return sema.typeRequiresComptime(ty.optionalChild(mod)); }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; - if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) { - return true; - } - } - return false; - }, - .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -33752,6 +33795,15 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { }, } }, + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + const have_comptime_val = val != .none; + if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) { + return true; + } + } + return false; + }, .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); @@ -33865,7 +33917,7 @@ fn structFieldIndex( ) !u32 { const mod = sema.mod; const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); - if (struct_ty.isAnonStruct()) { + if (struct_ty.isAnonStruct(mod)) { return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); } else { const struct_obj = mod.typeToStruct(struct_ty).?; @@ -33882,9 +33934,10 @@ fn anonStructFieldIndex( field_name: []const u8, field_src: LazySrcLoc, ) !u32 { - const anon_struct = struct_ty.castTag(.anon_struct).?.data; + const mod = sema.mod; + const anon_struct = mod.intern_pool.indexToKey(struct_ty.ip_index).anon_struct_type; for (anon_struct.names, 0..) |name, i| { - if (mem.eql(u8, name, field_name)) { + if (mem.eql(u8, mod.intern_pool.stringToSlice(name), field_name)) { return @intCast(u32, i); } } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index a18f49b96f..ced20ac522 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -177,13 +177,16 @@ pub fn print( } if (field_ptr.container_ty.zigTypeTag(mod) == .Struct) { - switch (field_ptr.container_ty.tag()) { - .tuple => return writer.print(".@\"{d}\"", .{field_ptr.field_index}), - else => { - const field_name = field_ptr.container_ty.structFieldName(field_ptr.field_index, mod); - return writer.print(".{s}", .{field_name}); + switch (mod.intern_pool.indexToKey(field_ptr.container_ty.ip_index)) { + .anon_struct_type => |anon_struct| { + if (anon_struct.names.len == 0) { + return writer.print(".@\"{d}\"", .{field_ptr.field_index}); + } }, + else => {}, } + const field_name = field_ptr.container_ty.structFieldName(field_ptr.field_index, mod); + return writer.print(".{s}", .{field_name}); } else if (field_ptr.container_ty.zigTypeTag(mod) == .Union) { const field_name = field_ptr.container_ty.unionFields(mod).keys()[field_ptr.field_index]; return writer.print(".{s}", .{field_name}); @@ -396,12 +399,9 @@ fn printAggregate( while (i < max_len) : (i += 1) { if (i != 0) try writer.writeAll(", "); switch (ty.ip_index) { - .none => switch (ty.tag()) { - .anon_struct => try writer.print(".{s} = ", .{ty.structFieldName(i, mod)}), - else => {}, - }, + .none => {}, // TODO make this unreachable after finishing InternPool migration else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => try writer.print(".{s} = ", .{ty.structFieldName(i, mod)}), + .struct_type, .anon_struct_type => try writer.print(".{s} = ", .{ty.structFieldName(i, mod)}), else => {}, }, } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7e2e37667e..30c3248360 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -11411,7 +11411,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { const union_obj = mod.typeToUnion(union_ty).?; const field_name = union_obj.fields.keys()[extra.field_index]; const tag_ty = union_obj.tag_ty; - const field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); + const field_index = tag_ty.enumFieldIndex(field_name, mod).?; const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); const tag_int_val = try tag_val.enumToInt(tag_ty, mod); const tag_int = tag_int_val.toUnsignedInt(mod); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2ee7dab2fe..f45c178223 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3417,8 +3417,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const op_inst = Air.refToIndex(un_op); const op_ty = f.typeOf(un_op); const ret_ty = if (is_ptr) op_ty.childType(mod) else op_ty; - var lowered_ret_buf: LowerFnRetTyBuffer = undefined; - const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, mod); + const lowered_ret_ty = try lowerFnRetTy(ret_ty, mod); if (op_inst != null and f.air.instructions.items(.tag)[op_inst.?] == .call_always_tail) { try reap(f, inst, &.{un_op}); @@ -4115,8 +4114,7 @@ fn airCall( } resolved_arg.* = try f.resolveInst(arg); if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { - var lowered_arg_buf: LowerFnRetTyBuffer = undefined; - const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, mod); + const lowered_arg_ty = try lowerFnRetTy(arg_ty, mod); const array_local = try f.allocLocal(inst, lowered_arg_ty); try writer.writeAll("memcpy("); @@ -4146,8 +4144,7 @@ fn airCall( }; const ret_ty = fn_ty.fnReturnType(); - var lowered_ret_buf: LowerFnRetTyBuffer = undefined; - const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, mod); + const lowered_ret_ty = try lowerFnRetTy(ret_ty, mod); const result_local = result: { if (modifier == .always_tail) { @@ -5200,7 +5197,7 @@ fn fieldLocation( const field_ty = container_ty.structFieldType(next_field_index, mod); if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - break .{ .field = if (container_ty.isSimpleTuple()) + break .{ .field = if (container_ty.isSimpleTuple(mod)) .{ .field = next_field_index } else .{ .identifier = container_ty.structFieldName(next_field_index, mod) } }; @@ -5395,16 +5392,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const field_name: CValue = switch (struct_ty.ip_index) { .none => switch (struct_ty.tag()) { - .tuple, .anon_struct => if (struct_ty.isSimpleTuple()) - .{ .field = extra.field_index } - else - .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) }, - else => unreachable, }, else => switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) { .struct_type => switch (struct_ty.containerLayout(mod)) { - .Auto, .Extern => if (struct_ty.isSimpleTuple()) + .Auto, .Extern => if (struct_ty.isSimpleTuple(mod)) .{ .field = extra.field_index } else .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) }, @@ -5465,6 +5457,12 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { return local; }, }, + + .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0) + .{ .field = extra.field_index } + else + .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) }, + .union_type => |union_type| field_name: { const union_obj = mod.unionPtr(union_type.index); if (union_obj.layout == .Packed) { @@ -6791,7 +6789,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; const a = try Assignment.start(f, writer, field_ty); - try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple()) + try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod)) .{ .field = field_i } else .{ .identifier = inst_ty.structFieldName(field_i, mod) }); @@ -7704,25 +7702,21 @@ const Vectorize = struct { } }; -const LowerFnRetTyBuffer = struct { - names: [1][]const u8, - types: [1]Type, - values: [1]Value, - payload: Type.Payload.AnonStruct, -}; -fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, mod: *Module) Type { - if (ret_ty.zigTypeTag(mod) == .NoReturn) return Type.noreturn; +fn lowerFnRetTy(ret_ty: Type, mod: *Module) !Type { + if (ret_ty.ip_index == .noreturn_type) return Type.noreturn; if (lowersToArray(ret_ty, mod)) { - buffer.names = [1][]const u8{"array"}; - buffer.types = [1]Type{ret_ty}; - buffer.values = [1]Value{Value.@"unreachable"}; - buffer.payload = .{ .data = .{ - .names = &buffer.names, - .types = &buffer.types, - .values = &buffer.values, - } }; - return Type.initPayload(&buffer.payload.base); + const names = [1]InternPool.NullTerminatedString{ + try mod.intern_pool.getOrPutString(mod.gpa, "array"), + }; + const types = [1]InternPool.Index{ret_ty.ip_index}; + const values = [1]InternPool.Index{.none}; + const interned = try mod.intern(.{ .anon_struct_type = .{ + .names = &names, + .types = &types, + .values = &values, + } }); + return interned.toType(); } return if (ret_ty.hasRuntimeBitsIgnoreComptime(mod)) ret_ty else Type.void; diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index bcb4b92228..b51d81a30b 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1951,7 +1951,7 @@ pub const CType = extern union { defer c_field_i += 1; fields_pl[c_field_i] = .{ - .name = try if (ty.isSimpleTuple()) + .name = try if (ty.isSimpleTuple(mod)) std.fmt.allocPrintZ(arena, "f{}", .{field_i}) else arena.dupeZ(u8, switch (zig_ty_tag) { @@ -2102,7 +2102,7 @@ pub const CType = extern union { .payload => unreachable, }) or !mem.eql( u8, - if (ty.isSimpleTuple()) + if (ty.isSimpleTuple(mod)) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else switch (zig_ty_tag) { .Struct => ty.structFieldName(field_i, mod), @@ -2224,7 +2224,7 @@ pub const CType = extern union { .global => .global, .payload => unreachable, }); - hasher.update(if (ty.isSimpleTuple()) + hasher.update(if (ty.isSimpleTuple(mod)) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else switch (zig_ty_tag) { .Struct => ty.structFieldName(field_i, mod), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e485b58c35..3289d389b4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2009,83 +2009,84 @@ pub const Object = struct { break :blk fwd_decl; }; - if (ty.isSimpleTupleOrAnonStruct()) { - const tuple = ty.tupleFields(); + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |tuple| { + var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; + defer di_fields.deinit(gpa); - var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; - defer di_fields.deinit(gpa); + try di_fields.ensureUnusedCapacity(gpa, tuple.types.len); - try di_fields.ensureUnusedCapacity(gpa, tuple.types.len); + comptime assert(struct_layout_version == 2); + var offset: u64 = 0; - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; + for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| { + if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; - for (tuple.types, 0..) |field_ty, i| { - const field_val = tuple.values[i]; - if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + const field_size = field_ty.toType().abiSize(mod); + const field_align = field_ty.toType().abiAlignment(mod); + const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align); + offset = field_offset + field_size; - const field_size = field_ty.abiSize(mod); - const field_align = field_ty.abiAlignment(mod); - const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align); - offset = field_offset + field_size; + const field_name = if (tuple.names.len != 0) + mod.intern_pool.stringToSlice(tuple.names[i]) + else + try std.fmt.allocPrintZ(gpa, "{d}", .{i}); + defer gpa.free(field_name); - const field_name = if (ty.castTag(.anon_struct)) |payload| - try gpa.dupeZ(u8, payload.data.names[i]) - else - try std.fmt.allocPrintZ(gpa, "{d}", .{i}); - defer gpa.free(field_name); + try di_fields.append(gpa, dib.createMemberType( + fwd_decl.toScope(), + field_name, + null, // file + 0, // line + field_size * 8, // size in bits + field_align * 8, // align in bits + field_offset * 8, // offset in bits + 0, // flags + try o.lowerDebugType(field_ty.toType(), .full), + )); + } - try di_fields.append(gpa, dib.createMemberType( - fwd_decl.toScope(), - field_name, + const full_di_ty = dib.createStructType( + compile_unit_scope, + name.ptr, null, // file 0, // line - field_size * 8, // size in bits - field_align * 8, // align in bits - field_offset * 8, // offset in bits + ty.abiSize(mod) * 8, // size in bits + ty.abiAlignment(mod) * 8, // align in bits 0, // flags - try o.lowerDebugType(field_ty, .full), - )); - } + null, // derived from + di_fields.items.ptr, + @intCast(c_int, di_fields.items.len), + 0, // run time lang + null, // vtable holder + "", // unique id + ); + dib.replaceTemporary(fwd_decl, full_di_ty); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); + return full_di_ty; + }, + .struct_type => |struct_type| s: { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :s; - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod) * 8, // align in bits - 0, // flags - null, // derived from - di_fields.items.ptr, - @intCast(c_int, di_fields.items.len), - 0, // run time lang - null, // vtable holder - "", // unique id - ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); - return full_di_ty; - } - - if (mod.typeToStruct(ty)) |struct_obj| { - if (!struct_obj.haveFieldTypes()) { - // This can happen if a struct type makes it all the way to - // flush() without ever being instantiated or referenced (even - // via pointer). The only reason we are hearing about it now is - // that it is being used as a namespace to put other debug types - // into. Therefore we can satisfy this by making an empty namespace, - // rather than changing the frontend to unnecessarily resolve the - // struct field types. - const owner_decl_index = ty.getOwnerDecl(mod); - const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); - dib.replaceTemporary(fwd_decl, struct_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module }); - return struct_di_ty; - } + if (!struct_obj.haveFieldTypes()) { + // This can happen if a struct type makes it all the way to + // flush() without ever being instantiated or referenced (even + // via pointer). The only reason we are hearing about it now is + // that it is being used as a namespace to put other debug types + // into. Therefore we can satisfy this by making an empty namespace, + // rather than changing the frontend to unnecessarily resolve the + // struct field types. + const owner_decl_index = ty.getOwnerDecl(mod); + const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); + dib.replaceTemporary(fwd_decl, struct_di_ty); + // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` + // means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module }); + return struct_di_ty; + } + }, + else => {}, } if (!ty.hasRuntimeBitsIgnoreComptime(mod)) { @@ -2931,59 +2932,61 @@ pub const DeclGen = struct { // reference, we need to copy it here. gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); - if (t.isSimpleTupleOrAnonStruct()) { - const tuple = t.tupleFields(); - const llvm_struct_ty = dg.context.structCreateNamed(""); - gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls + const struct_type = switch (mod.intern_pool.indexToKey(t.ip_index)) { + .anon_struct_type => |tuple| { + const llvm_struct_ty = dg.context.structCreateNamed(""); + gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls - var llvm_field_types: std.ArrayListUnmanaged(*llvm.Type) = .{}; - defer llvm_field_types.deinit(gpa); + var llvm_field_types: std.ArrayListUnmanaged(*llvm.Type) = .{}; + defer llvm_field_types.deinit(gpa); - try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len); + try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len); - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; - var big_align: u32 = 0; + comptime assert(struct_layout_version == 2); + var offset: u64 = 0; + var big_align: u32 = 0; - for (tuple.types, 0..) |field_ty, i| { - const field_val = tuple.values[i]; - if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + for (tuple.types, tuple.values) |field_ty, field_val| { + if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; - const field_align = field_ty.abiAlignment(mod); - big_align = @max(big_align, field_align); - const prev_offset = offset; - offset = std.mem.alignForwardGeneric(u64, offset, field_align); + const field_align = field_ty.toType().abiAlignment(mod); + big_align = @max(big_align, field_align); + const prev_offset = offset; + offset = std.mem.alignForwardGeneric(u64, offset, field_align); - const padding_len = offset - prev_offset; - if (padding_len > 0) { - const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); - try llvm_field_types.append(gpa, llvm_array_ty); + const padding_len = offset - prev_offset; + if (padding_len > 0) { + const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); + try llvm_field_types.append(gpa, llvm_array_ty); + } + const field_llvm_ty = try dg.lowerType(field_ty.toType()); + try llvm_field_types.append(gpa, field_llvm_ty); + + offset += field_ty.toType().abiSize(mod); } - const field_llvm_ty = try dg.lowerType(field_ty); - try llvm_field_types.append(gpa, field_llvm_ty); - - offset += field_ty.abiSize(mod); - } - { - const prev_offset = offset; - offset = std.mem.alignForwardGeneric(u64, offset, big_align); - const padding_len = offset - prev_offset; - if (padding_len > 0) { - const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); - try llvm_field_types.append(gpa, llvm_array_ty); + { + const prev_offset = offset; + offset = std.mem.alignForwardGeneric(u64, offset, big_align); + const padding_len = offset - prev_offset; + if (padding_len > 0) { + const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); + try llvm_field_types.append(gpa, llvm_array_ty); + } } - } - llvm_struct_ty.structSetBody( - llvm_field_types.items.ptr, - @intCast(c_uint, llvm_field_types.items.len), - .False, - ); + llvm_struct_ty.structSetBody( + llvm_field_types.items.ptr, + @intCast(c_uint, llvm_field_types.items.len), + .False, + ); - return llvm_struct_ty; - } + return llvm_struct_ty; + }, + .struct_type => |struct_type| struct_type, + else => unreachable, + }; - const struct_obj = mod.typeToStruct(t).?; + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; if (struct_obj.layout == .Packed) { assert(struct_obj.haveLayout()); @@ -3625,71 +3628,74 @@ pub const DeclGen = struct { const field_vals = tv.val.castTag(.aggregate).?.data; const gpa = dg.gpa; - if (tv.ty.isSimpleTupleOrAnonStruct()) { - const tuple = tv.ty.tupleFields(); - var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{}; - defer llvm_fields.deinit(gpa); + const struct_type = switch (mod.intern_pool.indexToKey(tv.ty.ip_index)) { + .anon_struct_type => |tuple| { + var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{}; + defer llvm_fields.deinit(gpa); - try llvm_fields.ensureUnusedCapacity(gpa, tuple.types.len); + try llvm_fields.ensureUnusedCapacity(gpa, tuple.types.len); - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; - var big_align: u32 = 0; - var need_unnamed = false; + comptime assert(struct_layout_version == 2); + var offset: u64 = 0; + var big_align: u32 = 0; + var need_unnamed = false; - for (tuple.types, 0..) |field_ty, i| { - if (tuple.values[i].ip_index != .unreachable_value) continue; - if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; + for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| { + if (field_val != .none) continue; + if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue; - const field_align = field_ty.abiAlignment(mod); - big_align = @max(big_align, field_align); - const prev_offset = offset; - offset = std.mem.alignForwardGeneric(u64, offset, field_align); + const field_align = field_ty.toType().abiAlignment(mod); + big_align = @max(big_align, field_align); + const prev_offset = offset; + offset = std.mem.alignForwardGeneric(u64, offset, field_align); - const padding_len = offset - prev_offset; - if (padding_len > 0) { - const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); - // TODO make this and all other padding elsewhere in debug - // builds be 0xaa not undef. - llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); + const padding_len = offset - prev_offset; + if (padding_len > 0) { + const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); + // TODO make this and all other padding elsewhere in debug + // builds be 0xaa not undef. + llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); + } + + const field_llvm_val = try dg.lowerValue(.{ + .ty = field_ty.toType(), + .val = field_vals[i], + }); + + need_unnamed = need_unnamed or dg.isUnnamedType(field_ty.toType(), field_llvm_val); + + llvm_fields.appendAssumeCapacity(field_llvm_val); + + offset += field_ty.toType().abiSize(mod); + } + { + const prev_offset = offset; + offset = std.mem.alignForwardGeneric(u64, offset, big_align); + const padding_len = offset - prev_offset; + if (padding_len > 0) { + const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); + llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); + } } - const field_llvm_val = try dg.lowerValue(.{ - .ty = field_ty, - .val = field_vals[i], - }); - - need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val); - - llvm_fields.appendAssumeCapacity(field_llvm_val); - - offset += field_ty.abiSize(mod); - } - { - const prev_offset = offset; - offset = std.mem.alignForwardGeneric(u64, offset, big_align); - const padding_len = offset - prev_offset; - if (padding_len > 0) { - const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); - llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); + if (need_unnamed) { + return dg.context.constStruct( + llvm_fields.items.ptr, + @intCast(c_uint, llvm_fields.items.len), + .False, + ); + } else { + return llvm_struct_ty.constNamedStruct( + llvm_fields.items.ptr, + @intCast(c_uint, llvm_fields.items.len), + ); } - } + }, + .struct_type => |struct_type| struct_type, + else => unreachable, + }; - if (need_unnamed) { - return dg.context.constStruct( - llvm_fields.items.ptr, - @intCast(c_uint, llvm_fields.items.len), - .False, - ); - } else { - return llvm_struct_ty.constNamedStruct( - llvm_fields.items.ptr, - @intCast(c_uint, llvm_fields.items.len), - ); - } - } - - const struct_obj = mod.typeToStruct(tv.ty).?; + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; if (struct_obj.layout == .Packed) { assert(struct_obj.haveLayout()); @@ -4077,13 +4083,11 @@ pub const DeclGen = struct { return field_addr.constIntToPtr(final_llvm_ty); } - var ty_buf: Type.Payload.Pointer = undefined; - const parent_llvm_ty = try dg.lowerType(parent_ty); - if (llvmFieldIndex(parent_ty, field_index, mod, &ty_buf)) |llvm_field_index| { + if (llvmField(parent_ty, field_index, mod)) |llvm_field| { const indices: [2]*llvm.Value = .{ llvm_u32.constInt(0, .False), - llvm_u32.constInt(llvm_field_index, .False), + llvm_u32.constInt(llvm_field.index, .False), }; return parent_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len); } else { @@ -6006,8 +6010,7 @@ pub const FuncGen = struct { return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); }, else => { - var ptr_ty_buf: Type.Payload.Pointer = undefined; - const llvm_field_index = llvmFieldIndex(struct_ty, field_index, mod, &ptr_ty_buf).?; + const llvm_field_index = llvmField(struct_ty, field_index, mod).?.index; return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, ""); }, }, @@ -6035,16 +6038,22 @@ pub const FuncGen = struct { switch (struct_ty.zigTypeTag(mod)) { .Struct => { assert(struct_ty.containerLayout(mod) != .Packed); - var ptr_ty_buf: Type.Payload.Pointer = undefined; - const llvm_field_index = llvmFieldIndex(struct_ty, field_index, mod, &ptr_ty_buf).?; + const llvm_field = llvmField(struct_ty, field_index, mod).?; const struct_llvm_ty = try self.dg.lowerType(struct_ty); - const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, struct_llvm_val, llvm_field_index, ""); - const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base); + const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, struct_llvm_val, llvm_field.index, ""); + const field_ptr_ty = try mod.ptrType(.{ + .elem_type = llvm_field.ty.ip_index, + .alignment = llvm_field.alignment, + }); if (isByRef(field_ty, mod)) { if (canElideLoad(self, body_tail)) return field_ptr; - return self.loadByRef(field_ptr, field_ty, ptr_ty_buf.data.alignment(mod), false); + const field_alignment = if (llvm_field.alignment != 0) + llvm_field.alignment + else + llvm_field.ty.abiAlignment(mod); + return self.loadByRef(field_ptr, field_ty, field_alignment, false); } else { return self.load(field_ptr, field_ptr_ty); } @@ -6912,12 +6921,14 @@ pub const FuncGen = struct { const struct_ty = self.air.getRefType(ty_pl.ty); const field_index = ty_pl.payload; - var ptr_ty_buf: Type.Payload.Pointer = undefined; const mod = self.dg.module; - const llvm_field_index = llvmFieldIndex(struct_ty, field_index, mod, &ptr_ty_buf).?; + const llvm_field = llvmField(struct_ty, field_index, mod).?; const struct_llvm_ty = try self.dg.lowerType(struct_ty); - const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, self.err_ret_trace.?, llvm_field_index, ""); - const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base); + const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, self.err_ret_trace.?, llvm_field.index, ""); + const field_ptr_ty = try mod.ptrType(.{ + .elem_type = llvm_field.ty.ip_index, + .alignment = llvm_field.alignment, + }); return self.load(field_ptr, field_ptr_ty); } @@ -7430,9 +7441,8 @@ pub const FuncGen = struct { const result = self.builder.buildExtractValue(result_struct, 0, ""); const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); - var ty_buf: Type.Payload.Pointer = undefined; - const result_index = llvmFieldIndex(dest_ty, 0, mod, &ty_buf).?; - const overflow_index = llvmFieldIndex(dest_ty, 1, mod, &ty_buf).?; + const result_index = llvmField(dest_ty, 0, mod).?.index; + const overflow_index = llvmField(dest_ty, 1, mod).?.index; if (isByRef(dest_ty, mod)) { const result_alignment = dest_ty.abiAlignment(mod); @@ -7736,9 +7746,8 @@ pub const FuncGen = struct { const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); - var ty_buf: Type.Payload.Pointer = undefined; - const result_index = llvmFieldIndex(dest_ty, 0, mod, &ty_buf).?; - const overflow_index = llvmFieldIndex(dest_ty, 1, mod, &ty_buf).?; + const result_index = llvmField(dest_ty, 0, mod).?.index; + const overflow_index = llvmField(dest_ty, 1, mod).?.index; if (isByRef(dest_ty, mod)) { const result_alignment = dest_ty.abiAlignment(mod); @@ -9300,8 +9309,6 @@ pub const FuncGen = struct { return running_int; } - var ptr_ty_buf: Type.Payload.Pointer = undefined; - if (isByRef(result_ty, mod)) { const llvm_u32 = self.context.intType(32); // TODO in debug builds init to undef so that the padding will be 0xaa @@ -9313,7 +9320,7 @@ pub const FuncGen = struct { if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; const llvm_elem = try self.resolveInst(elem); - const llvm_i = llvmFieldIndex(result_ty, i, mod, &ptr_ty_buf).?; + const llvm_i = llvmField(result_ty, i, mod).?.index; indices[1] = llvm_u32.constInt(llvm_i, .False); const field_ptr = self.builder.buildInBoundsGEP(llvm_result_ty, alloca_inst, &indices, indices.len, ""); var field_ptr_payload: Type.Payload.Pointer = .{ @@ -9334,7 +9341,7 @@ pub const FuncGen = struct { if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; const llvm_elem = try self.resolveInst(elem); - const llvm_i = llvmFieldIndex(result_ty, i, mod, &ptr_ty_buf).?; + const llvm_i = llvmField(result_ty, i, mod).?.index; result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, ""); } return result; @@ -9796,9 +9803,8 @@ pub const FuncGen = struct { else => { const struct_llvm_ty = try self.dg.lowerPtrElemTy(struct_ty); - var ty_buf: Type.Payload.Pointer = undefined; - if (llvmFieldIndex(struct_ty, field_index, mod, &ty_buf)) |llvm_field_index| { - return self.builder.buildStructGEP(struct_llvm_ty, struct_ptr, llvm_field_index, ""); + if (llvmField(struct_ty, field_index, mod)) |llvm_field| { + return self.builder.buildStructGEP(struct_llvm_ty, struct_ptr, llvm_field.index, ""); } else { // If we found no index then this means this is a zero sized field at the // end of the struct. Treat our struct pointer as an array of two and get @@ -10457,59 +10463,61 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ }; } +const LlvmField = struct { + index: c_uint, + ty: Type, + alignment: u32, +}; + /// Take into account 0 bit fields and padding. Returns null if an llvm /// field could not be found. /// This only happens if you want the field index of a zero sized field at /// the end of the struct. -fn llvmFieldIndex( - ty: Type, - field_index: usize, - mod: *Module, - ptr_pl_buf: *Type.Payload.Pointer, -) ?c_uint { +fn llvmField(ty: Type, field_index: usize, mod: *Module) ?LlvmField { // Detects where we inserted extra padding fields so that we can skip // over them in this function. comptime assert(struct_layout_version == 2); var offset: u64 = 0; var big_align: u32 = 0; - if (ty.isSimpleTupleOrAnonStruct()) { - const tuple = ty.tupleFields(); - var llvm_field_index: c_uint = 0; - for (tuple.types, 0..) |field_ty, i| { - if (tuple.values[i].ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + const struct_type = switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |tuple| { + var llvm_field_index: c_uint = 0; + for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| { + if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; - const field_align = field_ty.abiAlignment(mod); - big_align = @max(big_align, field_align); - const prev_offset = offset; - offset = std.mem.alignForwardGeneric(u64, offset, field_align); + const field_align = field_ty.toType().abiAlignment(mod); + big_align = @max(big_align, field_align); + const prev_offset = offset; + offset = std.mem.alignForwardGeneric(u64, offset, field_align); + + const padding_len = offset - prev_offset; + if (padding_len > 0) { + llvm_field_index += 1; + } + + if (field_index <= i) { + return .{ + .index = llvm_field_index, + .ty = field_ty.toType(), + .alignment = field_align, + }; + } - const padding_len = offset - prev_offset; - if (padding_len > 0) { llvm_field_index += 1; + offset += field_ty.toType().abiSize(mod); } - - if (field_index <= i) { - ptr_pl_buf.* = .{ - .data = .{ - .pointee_type = field_ty, - .@"align" = field_align, - .@"addrspace" = .generic, - }, - }; - return llvm_field_index; - } - - llvm_field_index += 1; - offset += field_ty.abiSize(mod); - } - return null; - } - const layout = ty.containerLayout(mod); + return null; + }, + .struct_type => |s| s, + else => unreachable, + }; + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + const layout = struct_obj.layout; assert(layout != .Packed); var llvm_field_index: c_uint = 0; - var it = mod.typeToStruct(ty).?.runtimeFieldIterator(mod); + var it = struct_obj.runtimeFieldIterator(mod); while (it.next()) |field_and_index| { const field = field_and_index.field; const field_align = field.alignment(mod, layout); @@ -10523,14 +10531,11 @@ fn llvmFieldIndex( } if (field_index == field_and_index.index) { - ptr_pl_buf.* = .{ - .data = .{ - .pointee_type = field.ty, - .@"align" = field_align, - .@"addrspace" = .generic, - }, + return .{ + .index = llvm_field_index, + .ty = field.ty, + .alignment = field_align, }; - return llvm_field_index; } llvm_field_index += 1; @@ -11089,21 +11094,24 @@ fn isByRef(ty: Type, mod: *Module) bool { .Struct => { // Packed structs are represented to LLVM as integers. if (ty.containerLayout(mod) == .Packed) return false; - if (ty.isSimpleTupleOrAnonStruct()) { - const tuple = ty.tupleFields(); - var count: usize = 0; - for (tuple.values, 0..) |field_val, i| { - if (field_val.ip_index != .unreachable_value or !tuple.types[i].hasRuntimeBits(mod)) continue; + const struct_type = switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |tuple| { + var count: usize = 0; + for (tuple.types, tuple.values) |field_ty, field_val| { + if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; - count += 1; - if (count > max_fields_byval) return true; - if (isByRef(tuple.types[i], mod)) return true; - } - return false; - } + count += 1; + if (count > max_fields_byval) return true; + if (isByRef(field_ty.toType(), mod)) return true; + } + return false; + }, + .struct_type => |s| s, + else => unreachable, + }; + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; var count: usize = 0; - const fields = ty.structFields(mod); - for (fields.values()) |field| { + for (struct_obj.fields.values()) |field| { if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue; count += 1; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index a81e36fefa..32ea975b64 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -682,7 +682,7 @@ pub const DeclGen = struct { else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}), }, .Struct => { - if (ty.isSimpleTupleOrAnonStruct()) { + if (ty.isSimpleTupleOrAnonStruct(mod)) { unreachable; // TODO } else { const struct_ty = mod.typeToStruct(ty).?; @@ -1319,7 +1319,8 @@ pub const DeclGen = struct { defer self.gpa.free(member_names); var member_index: usize = 0; - for (struct_ty.fields.values(), 0..) |field, i| { + const struct_obj = void; // TODO + for (struct_obj.fields.values(), 0..) |field, i| { if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue; member_types[member_index] = try self.resolveType(field.ty, .indirect); @@ -1327,7 +1328,7 @@ pub const DeclGen = struct { member_index += 1; } - const name = try struct_ty.getFullyQualifiedName(self.module); + const name = try struct_obj.getFullyQualifiedName(self.module); defer self.module.gpa.free(name); return try self.spv.resolve(.{ .struct_type = .{ @@ -2090,7 +2091,7 @@ pub const DeclGen = struct { var i: usize = 0; while (i < mask_len) : (i += 1) { - const elem = try mask.elemValue(self.module, i); + const elem = try mask.elemValue(mod, i); if (elem.isUndef(mod)) { self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF); } else { @@ -2805,7 +2806,7 @@ pub const DeclGen = struct { const value = try self.resolve(bin_op.rhs); const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); - const val_is_undef = if (try self.air.value(bin_op.rhs, mod)) |val| val.isUndefDeep() else false; + const val_is_undef = if (try self.air.value(bin_op.rhs, mod)) |val| val.isUndefDeep(mod) else false; if (val_is_undef) { const undef = try self.spv.constUndef(ptr_ty_ref); try self.store(ptr_ty, ptr, undef); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index e20e127800..b9722f8c95 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -333,13 +333,12 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.udata try leb128.writeULEB128(dbg_info_buffer.writer(), ty.abiSize(mod)); - switch (ty.tag()) { - .tuple, .anon_struct => { + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |fields| { // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); - const fields = ty.tupleFields(); - for (fields.types, 0..) |field, field_index| { + for (fields.types, 0..) |field_ty, field_index| { // DW.AT.member try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string @@ -347,28 +346,30 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeRelocGlobal(atom_index, field, @intCast(u32, index)); + try self.addTypeRelocGlobal(atom_index, field_ty.toType(), @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.udata const field_off = ty.structFieldOffset(field_index, mod); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); } }, - else => { + .struct_type => |struct_type| s: { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :s; // DW.AT.name, DW.FORM.string const struct_name = try ty.nameAllocArena(arena, mod); try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); dbg_info_buffer.appendSliceAssumeCapacity(struct_name); dbg_info_buffer.appendAssumeCapacity(0); - const struct_obj = mod.typeToStruct(ty).?; if (struct_obj.layout == .Packed) { log.debug("TODO implement .debug_info for packed structs", .{}); break :blk; } - const fields = ty.structFields(mod); - for (fields.keys(), 0..) |field_name, field_index| { - const field = fields.get(field_name).?; + for ( + struct_obj.fields.keys(), + struct_obj.fields.values(), + 0.., + ) |field_name, field, field_index| { if (!field.ty.hasRuntimeBits(mod)) continue; // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); @@ -385,6 +386,7 @@ pub const DeclState = struct { try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); } }, + else => unreachable, } // DW.AT.structure_type delimit children diff --git a/src/type.zig b/src/type.zig index d051191bfe..ee9e7c8e17 100644 --- a/src/type.zig +++ b/src/type.zig @@ -54,10 +54,6 @@ pub const Type = struct { .error_union => return .ErrorUnion, .anyframe_T => return .AnyFrame, - - .tuple, - .anon_struct, - => return .Struct, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => return .Int, @@ -66,7 +62,7 @@ pub const Type = struct { .vector_type => return .Vector, .opt_type => return .Optional, .error_union_type => return .ErrorUnion, - .struct_type => return .Struct, + .struct_type, .anon_struct_type => return .Struct, .union_type => return .Union, .opaque_type => return .Opaque, .enum_type => return .Enum, @@ -465,76 +461,6 @@ pub const Type = struct { if (b.zigTypeTag(mod) != .AnyFrame) return false; return a.elemType2(mod).eql(b.elemType2(mod), mod); }, - - .tuple => { - if (!b.isSimpleTuple()) return false; - - const a_tuple = a.tupleFields(); - const b_tuple = b.tupleFields(); - - if (a_tuple.types.len != b_tuple.types.len) return false; - - for (a_tuple.types, 0..) |a_ty, i| { - const b_ty = b_tuple.types[i]; - if (!eql(a_ty, b_ty, mod)) return false; - } - - for (a_tuple.values, 0..) |a_val, i| { - const ty = a_tuple.types[i]; - const b_val = b_tuple.values[i]; - if (a_val.ip_index == .unreachable_value) { - if (b_val.ip_index == .unreachable_value) { - continue; - } else { - return false; - } - } else { - if (b_val.ip_index == .unreachable_value) { - return false; - } else { - if (!Value.eql(a_val, b_val, ty, mod)) return false; - } - } - } - - return true; - }, - .anon_struct => { - const a_struct_obj = a.castTag(.anon_struct).?.data; - const b_struct_obj = (b.castTag(.anon_struct) orelse return false).data; - - if (a_struct_obj.types.len != b_struct_obj.types.len) return false; - - for (a_struct_obj.names, 0..) |a_name, i| { - const b_name = b_struct_obj.names[i]; - if (!std.mem.eql(u8, a_name, b_name)) return false; - } - - for (a_struct_obj.types, 0..) |a_ty, i| { - const b_ty = b_struct_obj.types[i]; - if (!eql(a_ty, b_ty, mod)) return false; - } - - for (a_struct_obj.values, 0..) |a_val, i| { - const ty = a_struct_obj.types[i]; - const b_val = b_struct_obj.values[i]; - if (a_val.ip_index == .unreachable_value) { - if (b_val.ip_index == .unreachable_value) { - continue; - } else { - return false; - } - } else { - if (b_val.ip_index == .unreachable_value) { - return false; - } else { - if (!Value.eql(a_val, b_val, ty, mod)) return false; - } - } - } - - return true; - }, } } @@ -641,34 +567,6 @@ pub const Type = struct { std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame); hashWithHasher(ty.childType(mod), hasher, mod); }, - - .tuple => { - std.hash.autoHash(hasher, std.builtin.TypeId.Struct); - - const tuple = ty.tupleFields(); - std.hash.autoHash(hasher, tuple.types.len); - - for (tuple.types, 0..) |field_ty, i| { - hashWithHasher(field_ty, hasher, mod); - const field_val = tuple.values[i]; - if (field_val.ip_index == .unreachable_value) continue; - field_val.hash(field_ty, hasher, mod); - } - }, - .anon_struct => { - const struct_obj = ty.castTag(.anon_struct).?.data; - std.hash.autoHash(hasher, std.builtin.TypeId.Struct); - std.hash.autoHash(hasher, struct_obj.types.len); - - for (struct_obj.types, 0..) |field_ty, i| { - const field_name = struct_obj.names[i]; - const field_val = struct_obj.values[i]; - hasher.update(field_name); - hashWithHasher(field_ty, hasher, mod); - if (field_val.ip_index == .unreachable_value) continue; - field_val.hash(field_ty, hasher, mod); - } - }, } } @@ -733,41 +631,6 @@ pub const Type = struct { }; }, - .tuple => { - const payload = self.castTag(.tuple).?.data; - const types = try allocator.alloc(Type, payload.types.len); - const values = try allocator.alloc(Value, payload.values.len); - for (payload.types, 0..) |ty, i| { - types[i] = try ty.copy(allocator); - } - for (payload.values, 0..) |val, i| { - values[i] = try val.copy(allocator); - } - return Tag.tuple.create(allocator, .{ - .types = types, - .values = values, - }); - }, - .anon_struct => { - const payload = self.castTag(.anon_struct).?.data; - const names = try allocator.alloc([]const u8, payload.names.len); - const types = try allocator.alloc(Type, payload.types.len); - const values = try allocator.alloc(Value, payload.values.len); - for (payload.names, 0..) |name, i| { - names[i] = try allocator.dupe(u8, name); - } - for (payload.types, 0..) |ty, i| { - types[i] = try ty.copy(allocator); - } - for (payload.values, 0..) |val, i| { - values[i] = try val.copy(allocator); - } - return Tag.anon_struct.create(allocator, .{ - .names = names, - .types = types, - .values = values, - }); - }, .function => { const payload = self.castTag(.function).?.data; const param_types = try allocator.alloc(Type, payload.param_types.len); @@ -935,42 +798,6 @@ pub const Type = struct { ty = return_type; continue; }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; - try writer.writeAll("tuple{"); - for (tuple.types, 0..) |field_ty, i| { - if (i != 0) try writer.writeAll(", "); - const val = tuple.values[i]; - if (val.ip_index != .unreachable_value) { - try writer.writeAll("comptime "); - } - try field_ty.dump("", .{}, writer); - if (val.ip_index != .unreachable_value) { - try writer.print(" = {}", .{val.fmtDebug()}); - } - } - try writer.writeAll("}"); - return; - }, - .anon_struct => { - const anon_struct = ty.castTag(.anon_struct).?.data; - try writer.writeAll("struct{"); - for (anon_struct.types, 0..) |field_ty, i| { - if (i != 0) try writer.writeAll(", "); - const val = anon_struct.values[i]; - if (val.ip_index != .unreachable_value) { - try writer.writeAll("comptime "); - } - try writer.writeAll(anon_struct.names[i]); - try writer.writeAll(": "); - try field_ty.dump("", .{}, writer); - if (val.ip_index != .unreachable_value) { - try writer.print(" = {}", .{val.fmtDebug()}); - } - } - try writer.writeAll("}"); - return; - }, .optional => { const child_type = ty.castTag(.optional).?.data; try writer.writeByte('?'); @@ -1131,45 +958,6 @@ pub const Type = struct { try print(error_union.payload, writer, mod); }, - .tuple => { - const tuple = ty.castTag(.tuple).?.data; - - try writer.writeAll("tuple{"); - for (tuple.types, 0..) |field_ty, i| { - if (i != 0) try writer.writeAll(", "); - const val = tuple.values[i]; - if (val.ip_index != .unreachable_value) { - try writer.writeAll("comptime "); - } - try print(field_ty, writer, mod); - if (val.ip_index != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); - } - } - try writer.writeAll("}"); - }, - .anon_struct => { - const anon_struct = ty.castTag(.anon_struct).?.data; - - try writer.writeAll("struct{"); - for (anon_struct.types, 0..) |field_ty, i| { - if (i != 0) try writer.writeAll(", "); - const val = anon_struct.values[i]; - if (val.ip_index != .unreachable_value) { - try writer.writeAll("comptime "); - } - try writer.writeAll(anon_struct.names[i]); - try writer.writeAll(": "); - - try print(field_ty, writer, mod); - - if (val.ip_index != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); - } - } - try writer.writeAll("}"); - }, - .pointer => { const info = ty.ptrInfo(mod); @@ -1335,6 +1123,27 @@ pub const Type = struct { try writer.writeAll("@TypeOf(.{})"); } }, + .anon_struct_type => |anon_struct| { + try writer.writeAll("struct{"); + for (anon_struct.types, anon_struct.values, 0..) |field_ty, val, i| { + if (i != 0) try writer.writeAll(", "); + if (val != .none) { + try writer.writeAll("comptime "); + } + if (anon_struct.names.len != 0) { + const name = mod.intern_pool.stringToSlice(anon_struct.names[i]); + try writer.writeAll(name); + try writer.writeAll(": "); + } + + try print(field_ty.toType(), writer, mod); + + if (val != .none) { + try writer.print(" = {}", .{val.toValue().fmtValue(field_ty.toType(), mod)}); + } + } + try writer.writeAll("}"); + }, .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); @@ -1443,16 +1252,6 @@ pub const Type = struct { } }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const val = tuple.values[i]; - if (val.ip_index != .unreachable_value) continue; // comptime field - if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true; - } - return false; - }, - .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, }, @@ -1567,6 +1366,13 @@ pub const Type = struct { return false; } }, + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + if (val != .none) continue; // comptime field + if (try field_ty.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true; + } + return false; + }, .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); @@ -1634,8 +1440,6 @@ pub const Type = struct { .function, .error_union, .anyframe_T, - .tuple, - .anon_struct, => false, .inferred_alloc_mut => unreachable, @@ -1705,6 +1509,7 @@ pub const Type = struct { }; return struct_obj.layout != .Auto; }, + .anon_struct_type => false, .union_type => |union_type| switch (union_type.runtime_tag) { .none, .safety => mod.unionPtr(union_type.index).layout != .Auto, .tagged => false, @@ -1923,26 +1728,6 @@ pub const Type = struct { .optional => return abiAlignmentAdvancedOptional(ty, mod, strat), .error_union => return abiAlignmentAdvancedErrorUnion(ty, mod, strat), - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - var big_align: u32 = 0; - for (tuple.types, 0..) |field_ty, i| { - const val = tuple.values[i]; - if (val.ip_index != .unreachable_value) continue; // comptime field - if (!(field_ty.hasRuntimeBits(mod))) continue; - - switch (try field_ty.abiAlignmentAdvanced(mod, strat)) { - .scalar => |field_align| big_align = @max(big_align, field_align), - .val => switch (strat) { - .eager => unreachable, // field type alignment not resolved - .sema => unreachable, // passed to abiAlignmentAdvanced above - .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, - }, - } - } - return AbiAlignmentAdvanced{ .scalar = big_align }; - }, - .inferred_alloc_const, .inferred_alloc_mut, => unreachable, @@ -2100,6 +1885,24 @@ pub const Type = struct { } return AbiAlignmentAdvanced{ .scalar = big_align }; }, + .anon_struct_type => |tuple| { + var big_align: u32 = 0; + for (tuple.types, tuple.values) |field_ty, val| { + if (val != .none) continue; // comptime field + if (!(field_ty.toType().hasRuntimeBits(mod))) continue; + + switch (try field_ty.toType().abiAlignmentAdvanced(mod, strat)) { + .scalar => |field_align| big_align = @max(big_align, field_align), + .val => switch (strat) { + .eager => unreachable, // field type alignment not resolved + .sema => unreachable, // passed to abiAlignmentAdvanced above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + }, + } + } + return AbiAlignmentAdvanced{ .scalar = big_align }; + }, + .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); @@ -2287,18 +2090,6 @@ pub const Type = struct { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, - .tuple, .anon_struct => { - switch (strat) { - .sema => |sema| try sema.resolveTypeLayout(ty), - .lazy, .eager => {}, - } - const field_count = ty.structFieldCount(mod); - if (field_count == 0) { - return AbiSizeAdvanced{ .scalar = 0 }; - } - return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; - }, - .anyframe_T => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, .pointer => switch (ty.castTag(.pointer).?.data.size) { @@ -2496,6 +2287,18 @@ pub const Type = struct { return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; }, }, + .anon_struct_type => |tuple| { + switch (strat) { + .sema => |sema| try sema.resolveTypeLayout(ty), + .lazy, .eager => {}, + } + const field_count = tuple.types.len; + if (field_count == 0) { + return AbiSizeAdvanced{ .scalar = 0 }; + } + return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; + }, + .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); return abiSizeAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); @@ -2609,18 +2412,6 @@ pub const Type = struct { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, - .tuple, .anon_struct => { - if (opt_sema) |sema| _ = try sema.resolveTypeFields(ty); - if (ty.containerLayout(mod) != .Packed) { - return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; - } - var total: u64 = 0; - for (ty.tupleFields().types) |field_ty| { - total += try bitSizeAdvanced(field_ty, mod, opt_sema); - } - return total; - }, - .anyframe_T => return target.ptrBitWidth(), .pointer => switch (ty.castTag(.pointer).?.data.size) { @@ -2724,6 +2515,11 @@ pub const Type = struct { return try struct_obj.backing_int_ty.bitSizeAdvanced(mod, opt_sema); }, + .anon_struct_type => { + if (opt_sema) |sema| _ = try sema.resolveTypeFields(ty); + return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; + }, + .union_type => |union_type| { if (opt_sema) |sema| _ = try sema.resolveTypeFields(ty); if (ty.containerLayout(mod) != .Packed) { @@ -3220,23 +3016,17 @@ pub const Type = struct { } pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout { - return switch (ty.ip_index) { - .empty_struct_type => .Auto, - .none => switch (ty.tag()) { - .tuple, .anon_struct => .Auto, - else => unreachable, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return .Auto; + return struct_obj.layout; }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - 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, + .anon_struct_type => .Auto, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.layout; }, + else => unreachable, }; } @@ -3349,23 +3139,16 @@ pub const Type = struct { } pub fn arrayLenIp(ty: Type, ip: InternPool) u64 { - return switch (ty.ip_index) { - .empty_struct_type => 0, - .none => switch (ty.tag()) { - .tuple => ty.castTag(.tuple).?.data.types.len, - .anon_struct => ty.castTag(.anon_struct).?.data.types.len, + return switch (ip.indexToKey(ty.ip_index)) { + .vector_type => |vector_type| vector_type.len, + .array_type => |array_type| array_type.len, + .struct_type => |struct_type| { + const struct_obj = ip.structPtrUnwrapConst(struct_type.index) orelse return 0; + return struct_obj.fields.count(); + }, + .anon_struct_type => |tuple| tuple.types.len, - else => unreachable, - }, - else => switch (ip.indexToKey(ty.ip_index)) { - .vector_type => |vector_type| vector_type.len, - .array_type => |array_type| array_type.len, - .struct_type => |struct_type| { - const struct_obj = ip.structPtrUnwrapConst(struct_type.index) orelse return 0; - return struct_obj.fields.count(); - }, - else => unreachable, - }, + else => unreachable, }; } @@ -3374,16 +3157,10 @@ pub const Type = struct { } pub fn vectorLen(ty: Type, mod: *const Module) u32 { - return switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len), - .anon_struct => @intCast(u32, ty.castTag(.anon_struct).?.data.types.len), - else => unreachable, - }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .vector_type => |vector_type| vector_type.len, - else => unreachable, - }, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .vector_type => |vector_type| vector_type.len, + .anon_struct_type => |tuple| @intCast(u32, tuple.types.len), + else => unreachable, }; } @@ -3391,8 +3168,6 @@ pub const Type = struct { pub fn sentinel(ty: Type, mod: *const Module) ?Value { return switch (ty.ip_index) { .none => switch (ty.tag()) { - .tuple => null, - .pointer => ty.castTag(.pointer).?.data.sentinel, else => unreachable, @@ -3400,6 +3175,7 @@ pub const Type = struct { else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .vector_type, .struct_type, + .anon_struct_type, => null, .array_type => |t| if (t.sentinel != .none) t.sentinel.toValue() else null, @@ -3486,10 +3262,12 @@ pub const Type = struct { ty = struct_obj.backing_int_ty; }, .enum_type => |enum_type| ty = enum_type.tag_ty.toType(), + .vector_type => |vector_type| ty = vector_type.child.toType(), + + .anon_struct_type => unreachable, .ptr_type => unreachable, .array_type => unreachable, - .vector_type => |vector_type| ty = vector_type.child.toType(), .opt_type => unreachable, .error_union_type => unreachable, @@ -3711,17 +3489,6 @@ pub const Type = struct { } }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.values, 0..) |val, i| { - const is_comptime = val.ip_index != .unreachable_value; - if (is_comptime) continue; - if ((try tuple.types[i].onePossibleValue(mod)) != null) continue; - return null; - } - return Value.empty_struct; - }, - .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, }, @@ -3810,7 +3577,33 @@ pub const Type = struct { return null; } } - // In this case the struct has no fields and therefore has one possible value. + // In this case the struct has no runtime-known fields and + // therefore has one possible value. + + // TODO: this is incorrect for structs with comptime fields, I think + // we should use a temporary allocator to construct an aggregate that + // is populated with the comptime values and then intern that value here. + // This TODO is repeated for anon_struct_type below, as well as in + // the redundant implementation of one-possible-value logic in Sema.zig. + const empty = try mod.intern(.{ .aggregate = .{ + .ty = ty.ip_index, + .fields = &.{}, + } }); + return empty.toValue(); + }, + + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + if (val != .none) continue; // comptime field + if ((try field_ty.toType().onePossibleValue(mod)) != null) continue; + return null; + } + + // TODO: this is incorrect for structs with comptime fields, I think + // we should use a temporary allocator to construct an aggregate that + // is populated with the comptime values and then intern that value here. + // This TODO is repeated for struct_type above, as well as in + // the redundant implementation of one-possible-value logic in Sema.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, .fields = &.{}, @@ -3915,15 +3708,6 @@ pub const Type = struct { return ty.optionalChild(mod).comptimeOnly(mod); }, - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; - if (!have_comptime_val and field_ty.comptimeOnly(mod)) return true; - } - return false; - }, - .error_union => return ty.errorUnionPayload().comptimeOnly(mod), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; @@ -4007,6 +3791,14 @@ pub const Type = struct { } }, + .anon_struct_type => |tuple| { + for (tuple.types, tuple.values) |field_ty, val| { + const have_comptime_val = val != .none; + if (!have_comptime_val and field_ty.toType().comptimeOnly(mod)) return true; + } + return false; + }, + .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); switch (union_obj.requires_comptime) { @@ -4275,171 +4067,116 @@ pub const Type = struct { } pub fn structFieldName(ty: Type, field_index: usize, mod: *Module) []const u8 { - switch (ty.ip_index) { - .none => switch (ty.tag()) { - .anon_struct => return ty.castTag(.anon_struct).?.data.names[field_index], - else => unreachable, + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + assert(struct_obj.haveFieldTypes()); + return struct_obj.fields.keys()[field_index]; }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - assert(struct_obj.haveFieldTypes()); - return struct_obj.fields.keys()[field_index]; - }, - else => unreachable, + .anon_struct_type => |anon_struct| { + const name = anon_struct.names[field_index]; + return mod.intern_pool.stringToSlice(name); }, + else => unreachable, } } pub fn structFieldCount(ty: Type, mod: *Module) usize { - return switch (ty.ip_index) { - .empty_struct_type => 0, - .none => switch (ty.tag()) { - .tuple => ty.castTag(.tuple).?.data.types.len, - .anon_struct => ty.castTag(.anon_struct).?.data.types.len, - else => unreachable, - }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return 0; - assert(struct_obj.haveFieldTypes()); - return struct_obj.fields.count(); - }, - else => unreachable, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return 0; + assert(struct_obj.haveFieldTypes()); + return struct_obj.fields.count(); }, + .anon_struct_type => |anon_struct| anon_struct.types.len, + else => unreachable, }; } /// Supports structs and unions. pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type { - return switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => return ty.castTag(.tuple).?.data.types[index], - .anon_struct => return ty.castTag(.anon_struct).?.data.types[index], - else => unreachable, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + return struct_obj.fields.values()[index].ty; }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - 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, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.fields.values()[index].ty; }, + .anon_struct_type => |anon_struct| anon_struct.types[index].toType(), + else => unreachable, }; } pub fn structFieldAlign(ty: Type, index: usize, mod: *Module) u32 { - switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => return ty.castTag(.tuple).?.data.types[index].abiAlignment(mod), - .anon_struct => return ty.castTag(.anon_struct).?.data.types[index].abiAlignment(mod), - else => unreachable, + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + assert(struct_obj.layout != .Packed); + return struct_obj.fields.values()[index].alignment(mod, struct_obj.layout); }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - 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, + .anon_struct_type => |anon_struct| { + return anon_struct.types[index].toType().abiAlignment(mod); }, + .union_type => |union_type| { + const union_obj = mod.unionPtr(union_type.index); + return union_obj.fields.values()[index].normalAlignment(mod); + }, + else => unreachable, } } pub fn structFieldDefaultValue(ty: Type, index: usize, mod: *Module) Value { - switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => { - const tuple = ty.castTag(.tuple).?.data; - return tuple.values[index]; - }, - .anon_struct => { - const struct_obj = ty.castTag(.anon_struct).?.data; - return struct_obj.values[index]; - }, - else => unreachable, + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + return struct_obj.fields.values()[index].default_val; }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - return struct_obj.fields.values()[index].default_val; - }, - else => unreachable, + .anon_struct_type => |anon_struct| { + const val = anon_struct.values[index]; + // TODO: avoid using `unreachable` to indicate this. + if (val == .none) return Value.@"unreachable"; + return val.toValue(); }, + else => unreachable, } } pub fn structFieldValueComptime(ty: Type, mod: *Module, index: usize) !?Value { - switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => { - const tuple = ty.castTag(.tuple).?.data; - const val = tuple.values[index]; - if (val.ip_index == .unreachable_value) { - return tuple.types[index].onePossibleValue(mod); - } else { - return val; - } - }, - .anon_struct => { - const anon_struct = ty.castTag(.anon_struct).?.data; - const val = anon_struct.values[index]; - if (val.ip_index == .unreachable_value) { - return anon_struct.types[index].onePossibleValue(mod); - } else { - return val; - } - }, - else => unreachable, + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + const field = struct_obj.fields.values()[index]; + if (field.is_comptime) { + return field.default_val; + } else { + return field.ty.onePossibleValue(mod); + } }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - const field = struct_obj.fields.values()[index]; - if (field.is_comptime) { - return field.default_val; - } else { - return field.ty.onePossibleValue(mod); - } - }, - else => unreachable, + .anon_struct_type => |tuple| { + const val = tuple.values[index]; + if (val == .none) { + return tuple.types[index].toType().onePossibleValue(mod); + } else { + return val.toValue(); + } }, + else => unreachable, } } pub fn structFieldIsComptime(ty: Type, index: usize, mod: *Module) bool { - switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => { - const tuple = ty.castTag(.tuple).?.data; - const val = tuple.values[index]; - return val.ip_index != .unreachable_value; - }, - .anon_struct => { - const anon_struct = ty.castTag(.anon_struct).?.data; - const val = anon_struct.values[index]; - return val.ip_index != .unreachable_value; - }, - else => unreachable, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + if (struct_obj.layout == .Packed) return false; + const field = struct_obj.fields.values()[index]; + return field.is_comptime; }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - if (struct_obj.layout == .Packed) return false; - const field = struct_obj.fields.values()[index]; - return field.is_comptime; - }, - else => unreachable, - }, - } + .anon_struct_type => |anon_struct| anon_struct.values[index] != .none, + else => unreachable, + }; } pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, mod: *Module) u32 { @@ -4516,30 +4253,6 @@ pub const Type = struct { pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 { switch (ty.ip_index) { .none => switch (ty.tag()) { - .tuple, .anon_struct => { - const tuple = ty.tupleFields(); - - var offset: u64 = 0; - var big_align: u32 = 0; - - for (tuple.types, 0..) |field_ty, i| { - const field_val = tuple.values[i]; - if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) { - // comptime field - if (i == index) return offset; - continue; - } - - const field_align = field_ty.abiAlignment(mod); - big_align = @max(big_align, field_align); - offset = std.mem.alignForwardGeneric(u64, offset, field_align); - if (i == index) return offset; - offset += field_ty.abiSize(mod); - } - offset = std.mem.alignForwardGeneric(u64, offset, @max(big_align, 1)); - return offset; - }, - else => unreachable, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { @@ -4556,6 +4269,27 @@ pub const Type = struct { return std.mem.alignForwardGeneric(u64, it.offset, @max(it.big_align, 1)); }, + .anon_struct_type => |tuple| { + var offset: u64 = 0; + var big_align: u32 = 0; + + for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| { + if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) { + // comptime field + if (i == index) return offset; + continue; + } + + const field_align = field_ty.toType().abiAlignment(mod); + big_align = @max(big_align, field_align); + offset = std.mem.alignForwardGeneric(u64, offset, field_align); + if (i == index) return offset; + offset += field_ty.toType().abiSize(mod); + } + offset = std.mem.alignForwardGeneric(u64, offset, @max(big_align, 1)); + return offset; + }, + .union_type => |union_type| { if (!union_type.hasTag()) return 0; @@ -4655,10 +4389,6 @@ pub const Type = struct { inferred_alloc_const, // See last_no_payload_tag below. // After this, the tag requires a payload. - /// Possible Value tags for this: @"struct" - tuple, - /// Possible Value tags for this: @"struct" - anon_struct, pointer, function, optional, @@ -4691,8 +4421,6 @@ pub const Type = struct { .function => Payload.Function, .error_union => Payload.ErrorUnion, .error_set_single => Payload.Name, - .tuple => Payload.Tuple, - .anon_struct => Payload.AnonStruct, }; } @@ -4723,83 +4451,48 @@ pub const Type = struct { pub fn isTuple(ty: Type, mod: *Module) bool { return switch (ty.ip_index) { - .none => switch (ty.tag()) { - .tuple => true, - else => false, - }, - else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .none => false, + else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .struct_type => |struct_type| { const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; return struct_obj.is_tuple; }, + .anon_struct_type => |anon_struct| anon_struct.names.len == 0, else => false, }, }; } - pub fn isAnonStruct(ty: Type) bool { - return switch (ty.ip_index) { - .empty_struct_type => true, - .none => switch (ty.tag()) { - .anon_struct => true, - else => false, - }, + pub fn isAnonStruct(ty: Type, mod: *Module) bool { + if (ty.ip_index == .empty_struct_type) return true; + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |anon_struct_type| anon_struct_type.names.len > 0, else => false, }; } pub fn isTupleOrAnonStruct(ty: Type, mod: *Module) bool { - return switch (ty.ip_index) { - .empty_struct_type => true, - .none => switch (ty.tag()) { - .tuple, .anon_struct => true, - else => false, - }, - else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; - return struct_obj.is_tuple; - }, - else => false, - }, - }; - } - - pub fn isSimpleTuple(ty: Type) bool { - return switch (ty.ip_index) { - .empty_struct_type => true, - .none => switch (ty.tag()) { - .tuple => true, - else => false, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; + return struct_obj.is_tuple; }, + .anon_struct_type => |anon_struct_type| anon_struct_type.names.len == 0, else => false, }; } - pub fn isSimpleTupleOrAnonStruct(ty: Type) bool { - return switch (ty.ip_index) { - .empty_struct_type => true, - .none => switch (ty.tag()) { - .tuple, .anon_struct => true, - else => false, - }, + pub fn isSimpleTuple(ty: Type, mod: *Module) bool { + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |anon_struct_type| anon_struct_type.names.len == 0, else => false, }; } - // Only allowed for simple tuple types - pub fn tupleFields(ty: Type) Payload.Tuple.Data { - return switch (ty.ip_index) { - .empty_struct_type => .{ .types = &.{}, .values = &.{} }, - .none => switch (ty.tag()) { - .tuple => ty.castTag(.tuple).?.data, - .anon_struct => .{ - .types = ty.castTag(.anon_struct).?.data.types, - .values = ty.castTag(.anon_struct).?.data.values, - }, - else => unreachable, - }, - else => unreachable, + pub fn isSimpleTupleOrAnonStruct(ty: Type, mod: *Module) bool { + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => true, + else => false, }; } @@ -4947,29 +4640,6 @@ pub const Type = struct { /// memory is owned by `Module` data: []const u8, }; - - pub const Tuple = struct { - base: Payload = .{ .tag = .tuple }, - data: Data, - - pub const Data = struct { - types: []Type, - /// unreachable_value elements are used to indicate runtime-known. - values: []Value, - }; - }; - - pub const AnonStruct = struct { - base: Payload = .{ .tag = .anon_struct }, - data: Data, - - pub const Data = struct { - names: []const []const u8, - types: []Type, - /// unreachable_value elements are used to indicate runtime-known. - values: []Value, - }; - }; }; pub const @"u1": Type = .{ .ip_index = .u1_type, .legacy = undefined }; diff --git a/src/value.zig b/src/value.zig index 84408424f0..50e3fc8061 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1889,26 +1889,28 @@ pub const Value = struct { const b_field_vals = b.castTag(.aggregate).?.data; assert(a_field_vals.len == b_field_vals.len); - if (ty.isSimpleTupleOrAnonStruct()) { - const types = ty.tupleFields().types; - assert(types.len == a_field_vals.len); - for (types, 0..) |field_ty, i| { - if (!(try eqlAdvanced(a_field_vals[i], field_ty, b_field_vals[i], field_ty, mod, opt_sema))) { - return false; + switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .anon_struct_type => |anon_struct| { + assert(anon_struct.types.len == a_field_vals.len); + for (anon_struct.types, 0..) |field_ty, i| { + if (!(try eqlAdvanced(a_field_vals[i], field_ty.toType(), b_field_vals[i], field_ty.toType(), mod, opt_sema))) { + return false; + } } - } - return true; - } - - if (ty.zigTypeTag(mod) == .Struct) { - const fields = ty.structFields(mod).values(); - assert(fields.len == a_field_vals.len); - for (fields, 0..) |field, i| { - if (!(try eqlAdvanced(a_field_vals[i], field.ty, b_field_vals[i], field.ty, mod, opt_sema))) { - return false; + return true; + }, + .struct_type => |struct_type| { + const struct_obj = mod.structPtrUnwrap(struct_type.index).?; + const fields = struct_obj.fields.values(); + assert(fields.len == a_field_vals.len); + for (fields, 0..) |field, i| { + if (!(try eqlAdvanced(a_field_vals[i], field.ty, b_field_vals[i], field.ty, mod, opt_sema))) { + return false; + } } - } - return true; + return true; + }, + else => {}, } const elem_ty = ty.childType(mod); @@ -2017,20 +2019,6 @@ pub const Value = struct { if ((try ty.onePossibleValue(mod)) != null) { return true; } - if (a_ty.castTag(.anon_struct)) |payload| { - const tuple = payload.data; - if (tuple.values.len != 1) { - return false; - } - const field_name = tuple.names[0]; - const union_obj = mod.typeToUnion(ty).?; - const field_index = @intCast(u32, union_obj.fields.getIndex(field_name) orelse return false); - const tag_and_val = b.castTag(.@"union").?.data; - const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, field_index); - const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod); - if (!tag_matches) return false; - return eqlAdvanced(tag_and_val.val, union_obj.tag_ty, tuple.values[0], tuple.types[0], mod, opt_sema); - } return false; }, .Float => {