Commit 6a9a918fbe

Andrew Kelley <andrew@ziglang.org>
2023-05-15 05:37:22
stage2: encode one-possible-value tuple specially
Anonymous structs and anonymous tuples can be stored via a only_possible_value tag because their type encodings, by definition, will have every value specified, which can be used to populate the fields slice in `Key.Aggregate`. Also fix `isTupleOrAnonStruct`.
1 parent d18881d
Changed files (3)
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,
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| {
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,
         };
     }