| ... | ... | @@ -141,7 +141,7 @@ const Format = struct { |
| 141 | 141 | pt: Zcu.PerThread, |
| 142 | 142 | |
| 143 | 143 | fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 144 | | return print(f.ty, writer, f.pt); |
| 144 | return print(f.ty, writer, f.pt, null); |
| 145 | 145 | } |
| 146 | 146 | }; |
| 147 | 147 | |
| ... | ... | @@ -157,7 +157,17 @@ pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 157 | 157 | |
| 158 | 158 | /// Prints a name suitable for `@typeName`. |
| 159 | 159 | /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. |
| 160 | | pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.Error!void { |
| 160 | pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Comparison) std.Io.Writer.Error!void { |
| 161 | if (ctx) |c| { |
| 162 | const should_dedupe = shouldDedupeType(ty, c, pt) catch |err| switch (err) { |
| 163 | error.OutOfMemory => return error.WriteFailed, |
| 164 | }; |
| 165 | switch (should_dedupe) { |
| 166 | .dont_dedupe => {}, |
| 167 | .dedupe => |placeholder| return placeholder.format(writer), |
| 168 | } |
| 169 | } |
| 170 | |
| 161 | 171 | const zcu = pt.zcu; |
| 162 | 172 | const ip = &zcu.intern_pool; |
| 163 | 173 | switch (ip.indexToKey(ty.toIntern())) { |
| ... | ... | @@ -209,39 +219,39 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer. |
| 209 | 219 | if (info.flags.is_const) try writer.writeAll("const "); |
| 210 | 220 | if (info.flags.is_volatile) try writer.writeAll("volatile "); |
| 211 | 221 | |
| 212 | | try print(Type.fromInterned(info.child), writer, pt); |
| 222 | try print(Type.fromInterned(info.child), writer, pt, ctx); |
| 213 | 223 | return; |
| 214 | 224 | }, |
| 215 | 225 | .array_type => |array_type| { |
| 216 | 226 | if (array_type.sentinel == .none) { |
| 217 | 227 | try writer.print("[{d}]", .{array_type.len}); |
| 218 | | try print(Type.fromInterned(array_type.child), writer, pt); |
| 228 | try print(Type.fromInterned(array_type.child), writer, pt, ctx); |
| 219 | 229 | } else { |
| 220 | 230 | try writer.print("[{d}:{f}]", .{ |
| 221 | 231 | array_type.len, |
| 222 | 232 | Value.fromInterned(array_type.sentinel).fmtValue(pt), |
| 223 | 233 | }); |
| 224 | | try print(Type.fromInterned(array_type.child), writer, pt); |
| 234 | try print(Type.fromInterned(array_type.child), writer, pt, ctx); |
| 225 | 235 | } |
| 226 | 236 | return; |
| 227 | 237 | }, |
| 228 | 238 | .vector_type => |vector_type| { |
| 229 | 239 | try writer.print("@Vector({d}, ", .{vector_type.len}); |
| 230 | | try print(Type.fromInterned(vector_type.child), writer, pt); |
| 240 | try print(Type.fromInterned(vector_type.child), writer, pt, ctx); |
| 231 | 241 | try writer.writeAll(")"); |
| 232 | 242 | return; |
| 233 | 243 | }, |
| 234 | 244 | .opt_type => |child| { |
| 235 | 245 | try writer.writeByte('?'); |
| 236 | | return print(Type.fromInterned(child), writer, pt); |
| 246 | return print(Type.fromInterned(child), writer, pt, ctx); |
| 237 | 247 | }, |
| 238 | 248 | .error_union_type => |error_union_type| { |
| 239 | | try print(Type.fromInterned(error_union_type.error_set_type), writer, pt); |
| 249 | try print(Type.fromInterned(error_union_type.error_set_type), writer, pt, ctx); |
| 240 | 250 | try writer.writeByte('!'); |
| 241 | 251 | if (error_union_type.payload_type == .generic_poison_type) { |
| 242 | 252 | try writer.writeAll("anytype"); |
| 243 | 253 | } else { |
| 244 | | try print(Type.fromInterned(error_union_type.payload_type), writer, pt); |
| 254 | try print(Type.fromInterned(error_union_type.payload_type), writer, pt, ctx); |
| 245 | 255 | } |
| 246 | 256 | return; |
| 247 | 257 | }, |
| ... | ... | @@ -323,7 +333,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer. |
| 323 | 333 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| { |
| 324 | 334 | try writer.writeAll(if (i == 0) " " else ", "); |
| 325 | 335 | if (val != .none) try writer.writeAll("comptime "); |
| 326 | | try print(Type.fromInterned(field_ty), writer, pt); |
| 336 | try print(Type.fromInterned(field_ty), writer, pt, ctx); |
| 327 | 337 | if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); |
| 328 | 338 | } |
| 329 | 339 | try writer.writeAll(" }"); |
| ... | ... | @@ -360,7 +370,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer. |
| 360 | 370 | if (param_ty == .generic_poison_type) { |
| 361 | 371 | try writer.writeAll("anytype"); |
| 362 | 372 | } else { |
| 363 | | try print(Type.fromInterned(param_ty), writer, pt); |
| 373 | try print(Type.fromInterned(param_ty), writer, pt, ctx); |
| 364 | 374 | } |
| 365 | 375 | } |
| 366 | 376 | if (fn_info.is_var_args) { |
| ... | ... | @@ -387,13 +397,13 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer. |
| 387 | 397 | if (fn_info.return_type == .generic_poison_type) { |
| 388 | 398 | try writer.writeAll("anytype"); |
| 389 | 399 | } else { |
| 390 | | try print(Type.fromInterned(fn_info.return_type), writer, pt); |
| 400 | try print(Type.fromInterned(fn_info.return_type), writer, pt, ctx); |
| 391 | 401 | } |
| 392 | 402 | }, |
| 393 | 403 | .anyframe_type => |child| { |
| 394 | 404 | if (child == .none) return writer.writeAll("anyframe"); |
| 395 | 405 | try writer.writeAll("anyframe->"); |
| 396 | | return print(Type.fromInterned(child), writer, pt); |
| 406 | return print(Type.fromInterned(child), writer, pt, ctx); |
| 397 | 407 | }, |
| 398 | 408 | |
| 399 | 409 | // values, not types |
| ... | ... | @@ -4046,6 +4056,175 @@ pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool { |
| 4046 | 4056 | return null; |
| 4047 | 4057 | } |
| 4048 | 4058 | |
| 4059 | /// Recursively walks the type and marks for each subtype how many times it has been seen |
| 4060 | fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUnmanaged(Type, u16)) error{OutOfMemory}!void { |
| 4061 | const zcu = pt.zcu; |
| 4062 | const ip = &zcu.intern_pool; |
| 4063 | |
| 4064 | const gop = try visited.getOrPut(zcu.gpa, ty); |
| 4065 | if (gop.found_existing) { |
| 4066 | gop.value_ptr.* += 1; |
| 4067 | } else { |
| 4068 | gop.value_ptr.* = 1; |
| 4069 | } |
| 4070 | |
| 4071 | switch (ip.indexToKey(ty.toIntern())) { |
| 4072 | .ptr_type => try collectSubtypes(Type.fromInterned(ty.ptrInfo(zcu).child), pt, visited), |
| 4073 | .array_type => |array_type| try collectSubtypes(Type.fromInterned(array_type.child), pt, visited), |
| 4074 | .vector_type => |vector_type| try collectSubtypes(Type.fromInterned(vector_type.child), pt, visited), |
| 4075 | .opt_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited), |
| 4076 | .error_union_type => |error_union_type| { |
| 4077 | try collectSubtypes(Type.fromInterned(error_union_type.error_set_type), pt, visited); |
| 4078 | if (error_union_type.payload_type != .generic_poison_type) { |
| 4079 | try collectSubtypes(Type.fromInterned(error_union_type.payload_type), pt, visited); |
| 4080 | } |
| 4081 | }, |
| 4082 | .tuple_type => |tuple| { |
| 4083 | for (tuple.types.get(ip)) |field_ty| { |
| 4084 | try collectSubtypes(Type.fromInterned(field_ty), pt, visited); |
| 4085 | } |
| 4086 | }, |
| 4087 | .func_type => |fn_info| { |
| 4088 | const param_types = fn_info.param_types.get(&zcu.intern_pool); |
| 4089 | for (param_types) |param_ty| { |
| 4090 | if (param_ty != .generic_poison_type) { |
| 4091 | try collectSubtypes(Type.fromInterned(param_ty), pt, visited); |
| 4092 | } |
| 4093 | } |
| 4094 | |
| 4095 | if (fn_info.return_type != .generic_poison_type) { |
| 4096 | try collectSubtypes(Type.fromInterned(fn_info.return_type), pt, visited); |
| 4097 | } |
| 4098 | }, |
| 4099 | .anyframe_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited), |
| 4100 | |
| 4101 | // leaf types |
| 4102 | .undef, |
| 4103 | .inferred_error_set_type, |
| 4104 | .error_set_type, |
| 4105 | .struct_type, |
| 4106 | .union_type, |
| 4107 | .opaque_type, |
| 4108 | .enum_type, |
| 4109 | .simple_type, |
| 4110 | .int_type, |
| 4111 | => {}, |
| 4112 | |
| 4113 | // values, not types |
| 4114 | .simple_value, |
| 4115 | .variable, |
| 4116 | .@"extern", |
| 4117 | .func, |
| 4118 | .int, |
| 4119 | .err, |
| 4120 | .error_union, |
| 4121 | .enum_literal, |
| 4122 | .enum_tag, |
| 4123 | .empty_enum_value, |
| 4124 | .float, |
| 4125 | .ptr, |
| 4126 | .slice, |
| 4127 | .opt, |
| 4128 | .aggregate, |
| 4129 | .un, |
| 4130 | // memoization, not types |
| 4131 | .memoized_call, |
| 4132 | => unreachable, |
| 4133 | } |
| 4134 | } |
| 4135 | |
| 4136 | fn shouldDedupeType(ty: Type, ctx: *Comparison, pt: Zcu.PerThread) error{OutOfMemory}!Comparison.DedupeEntry { |
| 4137 | if (ctx.type_occurrences.get(ty)) |occ| { |
| 4138 | if (ctx.type_dedupe_cache.get(ty)) |cached| { |
| 4139 | return cached; |
| 4140 | } |
| 4141 | |
| 4142 | var discarding: std.Io.Writer.Discarding = .init(&.{}); |
| 4143 | |
| 4144 | print(ty, &discarding.writer, pt, null) catch |
| 4145 | unreachable; // we are writing into a discarding writer, it should never fail |
| 4146 | |
| 4147 | const type_len: i32 = @intCast(discarding.count); |
| 4148 | |
| 4149 | const placeholder_len: i32 = 3; |
| 4150 | const min_saved_bytes: i32 = 10; |
| 4151 | |
| 4152 | const saved_bytes = (type_len - placeholder_len) * (occ - 1); |
| 4153 | const max_placeholders = 7; // T to Z |
| 4154 | const should_dedupe = saved_bytes >= min_saved_bytes and ctx.placeholder_index < max_placeholders; |
| 4155 | |
| 4156 | const entry: Comparison.DedupeEntry = if (should_dedupe) b: { |
| 4157 | ctx.placeholder_index += 1; |
| 4158 | break :b .{ .dedupe = .{ .index = ctx.placeholder_index - 1 } }; |
| 4159 | } else .dont_dedupe; |
| 4160 | |
| 4161 | try ctx.type_dedupe_cache.put(pt.zcu.gpa, ty, entry); |
| 4162 | |
| 4163 | return entry; |
| 4164 | } else { |
| 4165 | return .{ .dont_dedupe = {} }; |
| 4166 | } |
| 4167 | } |
| 4168 | |
| 4169 | /// The comparison recursively walks all types given and notes how many times |
| 4170 | /// each subtype occurs. It then while recursively printing decides for each |
| 4171 | /// subtype whether to print the type inline or create a placeholder based on |
| 4172 | /// the subtype length and number of occurences. Placeholders are then found by |
| 4173 | /// iterating `type_dedupe_cache` which caches the inline/placeholder decisions. |
| 4174 | pub const Comparison = struct { |
| 4175 | type_occurrences: std.AutoArrayHashMapUnmanaged(Type, u16), |
| 4176 | type_dedupe_cache: std.AutoArrayHashMapUnmanaged(Type, DedupeEntry), |
| 4177 | placeholder_index: u8, |
| 4178 | |
| 4179 | pub const Placeholder = struct { |
| 4180 | index: u8, |
| 4181 | |
| 4182 | pub fn format(p: Placeholder, writer: *std.Io.Writer) error{WriteFailed}!void { |
| 4183 | return writer.print("<{c}>", .{p.index + 'T'}); |
| 4184 | } |
| 4185 | }; |
| 4186 | |
| 4187 | pub const DedupeEntry = union(enum) { |
| 4188 | dont_dedupe: void, |
| 4189 | dedupe: Placeholder, |
| 4190 | }; |
| 4191 | |
| 4192 | pub fn init(types: []const Type, pt: Zcu.PerThread) error{OutOfMemory}!Comparison { |
| 4193 | var cmp: Comparison = .{ |
| 4194 | .type_occurrences = .empty, |
| 4195 | .type_dedupe_cache = .empty, |
| 4196 | .placeholder_index = 0, |
| 4197 | }; |
| 4198 | |
| 4199 | errdefer cmp.deinit(pt); |
| 4200 | |
| 4201 | for (types) |ty| { |
| 4202 | try collectSubtypes(ty, pt, &cmp.type_occurrences); |
| 4203 | } |
| 4204 | |
| 4205 | return cmp; |
| 4206 | } |
| 4207 | |
| 4208 | pub fn deinit(cmp: *Comparison, pt: Zcu.PerThread) void { |
| 4209 | const gpa = pt.zcu.gpa; |
| 4210 | cmp.type_occurrences.deinit(gpa); |
| 4211 | cmp.type_dedupe_cache.deinit(gpa); |
| 4212 | } |
| 4213 | |
| 4214 | pub fn fmtType(ctx: *Comparison, ty: Type, pt: Zcu.PerThread) Comparison.Formatter { |
| 4215 | return .{ .ty = ty, .ctx = ctx, .pt = pt }; |
| 4216 | } |
| 4217 | pub const Formatter = struct { |
| 4218 | ty: Type, |
| 4219 | ctx: *Comparison, |
| 4220 | pt: Zcu.PerThread, |
| 4221 | |
| 4222 | pub fn format(self: Comparison.Formatter, writer: anytype) error{WriteFailed}!void { |
| 4223 | print(self.ty, writer, self.pt, self.ctx) catch return error.WriteFailed; |
| 4224 | } |
| 4225 | }; |
| 4226 | }; |
| 4227 | |
| 4049 | 4228 | pub const @"u1": Type = .{ .ip_index = .u1_type }; |
| 4050 | 4229 | pub const @"u8": Type = .{ .ip_index = .u8_type }; |
| 4051 | 4230 | pub const @"u16": Type = .{ .ip_index = .u16_type }; |