diff --git a/src/InternPool.zig b/src/InternPool.zig index 74cc452176..2435e0ad31 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1166,10 +1166,10 @@ 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. + /// An AnonStructType which stores types, names, and values for fields. /// data is extra index of `TypeStructAnon`. type_struct_anon, - /// An AnonStructType which has only types and values for each field. + /// An AnonStructType which has only types and values for fields. /// data is extra index of `TypeStructAnon`. type_tuple_anon, /// A tagged union type. @@ -1272,7 +1272,8 @@ pub const Tag = enum(u8) { /// only one possible value. Not all only-possible-values are encoded this way; /// for example structs which have all comptime fields are not encoded this way. /// The set of values that are encoded this way is: - /// * A struct which has 0 fields. + /// * An array or vector which has length 0. + /// * A struct which has all fields comptime-known. /// data is Index of the type, which is known to be zero bits at runtime. only_possible_value, /// data is extra index to Key.Union. @@ -1863,10 +1864,21 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .only_possible_value => { const ty = @intToEnum(Index, data); return switch (ip.indexToKey(ty)) { + // TODO: migrate structs to properly use the InternPool rather + // than using the SegmentedList trick, then the struct type will + // have a slice of comptime values that can be used here for when + // the struct has one possible value due to all fields comptime (same + // as the tuple case below). .struct_type => .{ .aggregate = .{ .ty = ty, .fields = &.{}, } }, + // There is only one possible value precisely due to the + // fact that this values slice is fully populated! + .anon_struct_type => |anon_struct_type| .{ .aggregate = .{ + .ty = ty, + .fields = anon_struct_type.values, + } }, else => unreachable, }; }, @@ -2392,12 +2404,6 @@ 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) { @@ -2408,6 +2414,22 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } + switch (ip.indexToKey(aggregate.ty)) { + .anon_struct_type => |anon_struct_type| { + if (std.mem.eql(Index, anon_struct_type.values, aggregate.fields)) { + // This encoding works thanks to the fact that, as we just verified, + // the type itself contains a slice of values that can be provided + // in the aggregate fields. + ip.items.appendAssumeCapacity(.{ + .tag = .only_possible_value, + .data = @enumToInt(aggregate.ty), + }); + return @intToEnum(Index, ip.items.len - 1); + } + }, + else => {}, + } + try ip.extra.ensureUnusedCapacity( gpa, @typeInfo(Aggregate).Struct.fields.len + aggregate.fields.len, @@ -3121,8 +3143,8 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { } }; counts.sort(SortContext{ .map = &counts }); - const len = @min(50, counts.count()); - std.debug.print(" top 50 tags:\n", .{}); + const len = @min(25, counts.count()); + std.debug.print(" top 25 tags:\n", .{}); for (counts.keys()[0..len], counts.values()[0..len]) |tag, stats| { std.debug.print(" {s}: {d} occurrences, {d} total bytes\n", .{ @tagName(tag), stats.count, stats.bytes, diff --git a/src/Sema.zig b/src/Sema.zig index 31e07bdcdc..74b3cdd114 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18237,6 +18237,7 @@ fn zirStructInitAnon( return sema.failWithOwnedErrorMsg(msg); } if (try sema.resolveMaybeUndefVal(init)) |init_val| { + assert(init_val.ip_index != .none); values[i] = init_val.ip_index; } else { values[i] = .none; @@ -33181,8 +33182,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?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. + // This TODO is repeated in the redundant implementation of + // one-possible-value in type.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, .fields = &.{}, @@ -33191,25 +33192,15 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { }, .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; + for (tuple.values) |val| { + if (val == .none) return null; } - // In this case the struct has no runtime-known fields and + // In this case the struct has all comptime-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 = .{ + return (try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = &.{}, - } }); - return empty.toValue(); + .fields = tuple.values, + } })).toValue(); }, .union_type => |union_type| { diff --git a/src/type.zig b/src/type.zig index ee9e7c8e17..32fa64a1ac 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3583,8 +3583,8 @@ pub const Type = struct { // 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. + // This TODO is repeated in the redundant implementation of + // one-possible-value logic in Sema.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, .fields = &.{}, @@ -3593,22 +3593,15 @@ pub const Type = struct { }, .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; + for (tuple.values) |val| { + if (val == .none) 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 = .{ + // In this case the struct has all comptime-known fields and + // therefore has one possible value. + return (try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = &.{}, - } }); - return empty.toValue(); + .fields = tuple.values, + } })).toValue(); }, .union_type => |union_type| { @@ -4477,7 +4470,7 @@ pub const Type = struct { 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, + .anon_struct_type => true, else => false, }; }