authorgravatar for prokop@rdck.devProkop Randáček <prokop@rdck.dev> 2025-11-16 14:20:45+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-11-16 16:20:45+02:00
log94e98bfe80033439382bff22bcab02aaaa580dc5
treecb8b5e94a0a32e9b7eeb2c401ef0732db0d7dace
parentaa4332fb0e5f46657c8c41a3971150b642be1c19
signaturebadge-check Signed by PGP key B5690EEEBB952194

Dedupe types when printing error messages


9 files changed, 254 insertions(+), 46 deletions(-)

src/Air/print.zig+1-1
...@@ -363,7 +363,7 @@ const Writer = struct {...@@ -363,7 +363,7 @@ const Writer = struct {
363 }363 }
364364
365 fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void {365 fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void {
366 return ty.print(s, w.pt);366 return ty.print(s, w.pt, null);
367 }367 }
368368
369 fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {369 fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
src/Sema.zig+36-27
...@@ -2447,19 +2447,6 @@ fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, t...@@ -2447,19 +2447,6 @@ fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, t
2447 });2447 });
2448}2448}
24492449
2450fn failWithErrorSetCodeMissing(
2451 sema: *Sema,
2452 block: *Block,
2453 src: LazySrcLoc,
2454 dest_err_set_ty: Type,
2455 src_err_set_ty: Type,
2456) CompileError {
2457 const pt = sema.pt;
2458 return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{
2459 dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt),
2460 });
2461}
2462
2463pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {2450pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {
2464 const pt = sema.pt;2451 const pt = sema.pt;
2465 return sema.failWithOwnedErrorMsg(block, msg: {2452 return sema.failWithOwnedErrorMsg(block, msg: {
...@@ -2619,6 +2606,26 @@ pub fn errMsg(...@@ -2619,6 +2606,26 @@ pub fn errMsg(
2619 return Zcu.ErrorMsg.create(sema.gpa, src, format, args);2606 return Zcu.ErrorMsg.create(sema.gpa, src, format, args);
2620}2607}
26212608
2609fn typeMismatchErrMsg(sema: *Sema, src: LazySrcLoc, expected: Type, found: Type) Allocator.Error!*Zcu.ErrorMsg {
2610 const pt = sema.pt;
2611 var cmp: Type.Comparison = try .init(&.{ expected, found }, pt);
2612 defer cmp.deinit(pt);
2613
2614 const msg = try sema.errMsg(src, "expected type '{f}', found '{f}'", .{
2615 cmp.fmtType(expected, pt),
2616 cmp.fmtType(found, pt),
2617 });
2618 errdefer msg.destroy(sema.gpa);
2619
2620 for (cmp.type_dedupe_cache.keys(), cmp.type_dedupe_cache.values()) |ty, value| {
2621 if (value == .dont_dedupe) continue;
2622 const placeholder = value.dedupe;
2623 try sema.errNote(src, msg, "{f} = {f}", .{ placeholder, ty.fmt(pt) });
2624 }
2625
2626 return msg;
2627}
2628
2622pub fn fail(2629pub fn fail(
2623 sema: *Sema,2630 sema: *Sema,
2624 block: *Block,2631 block: *Block,
...@@ -2635,6 +2642,14 @@ pub fn fail(...@@ -2635,6 +2642,14 @@ pub fn fail(
2635 return sema.failWithOwnedErrorMsg(block, err_msg);2642 return sema.failWithOwnedErrorMsg(block, err_msg);
2636}2643}
26372644
2645fn failWithTypeMismatch(sema: *Sema, block: *Block, src: LazySrcLoc, expected: Type, found: Type) CompileError {
2646 const err_msg = try sema.typeMismatchErrMsg(src, expected, found);
2647 errdefer err_msg.destroy(sema.gpa);
2648 try addDeclaredHereNote(sema, err_msg, expected);
2649 try addDeclaredHereNote(sema, err_msg, found);
2650 return sema.failWithOwnedErrorMsg(block, err_msg);
2651}
2652
2638pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } {2653pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } {
2639 @branchHint(.cold);2654 @branchHint(.cold);
2640 const gpa = sema.gpa;2655 const gpa = sema.gpa;
...@@ -22933,7 +22948,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -22933,7 +22948,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
22933 const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;22948 const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;
22934 const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;22949 const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;
22935 if (operand_is_vector != dest_is_vector) {22950 if (operand_is_vector != dest_is_vector) {
22936 return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) });22951 return sema.failWithTypeMismatch(block, operand_src, dest_ty, operand_ty);
22937 }22952 }
2293822953
22939 if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {22954 if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
...@@ -29167,7 +29182,7 @@ fn coerceExtra(...@@ -29167,7 +29182,7 @@ fn coerceExtra(
29167 }29182 }
2916829183
29169 const msg = msg: {29184 const msg = msg: {
29170 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) });29185 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
29171 errdefer msg.destroy(sema.gpa);29186 errdefer msg.destroy(sema.gpa);
2917229187
29173 if (!can_coerce_to) {29188 if (!can_coerce_to) {
...@@ -30780,9 +30795,7 @@ fn coerceEnumToUnion(...@@ -30780,9 +30795,7 @@ fn coerceEnumToUnion(
3078030795
30781 const tag_ty = union_ty.unionTagType(zcu) orelse {30796 const tag_ty = union_ty.unionTagType(zcu) orelse {
30782 const msg = msg: {30797 const msg = msg: {
30783 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{30798 const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty);
30784 union_ty.fmt(pt), inst_ty.fmt(pt),
30785 });
30786 errdefer msg.destroy(sema.gpa);30799 errdefer msg.destroy(sema.gpa);
30787 try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});30800 try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
30788 try sema.addDeclaredHereNote(msg, union_ty);30801 try sema.addDeclaredHereNote(msg, union_ty);
...@@ -30933,9 +30946,7 @@ fn coerceArrayLike(...@@ -30933,9 +30946,7 @@ fn coerceArrayLike(
30933 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));30946 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));
30934 if (dest_len != inst_len) {30947 if (dest_len != inst_len) {
30935 const msg = msg: {30948 const msg = msg: {
30936 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{30949 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
30937 dest_ty.fmt(pt), inst_ty.fmt(pt),
30938 });
30939 errdefer msg.destroy(sema.gpa);30950 errdefer msg.destroy(sema.gpa);
30940 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});30951 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
30941 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});30952 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
...@@ -31018,9 +31029,7 @@ fn coerceTupleToArray(...@@ -31018,9 +31029,7 @@ fn coerceTupleToArray(
3101831029
31019 if (dest_len != inst_len) {31030 if (dest_len != inst_len) {
31020 const msg = msg: {31031 const msg = msg: {
31021 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{31032 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
31022 dest_ty.fmt(pt), inst_ty.fmt(pt),
31023 });
31024 errdefer msg.destroy(sema.gpa);31033 errdefer msg.destroy(sema.gpa);
31025 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});31034 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
31026 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});31035 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
...@@ -32719,12 +32728,12 @@ fn wrapErrorUnionSet(...@@ -32719,12 +32728,12 @@ fn wrapErrorUnionSet(
32719 break :ok;32728 break :ok;
32720 },32729 },
32721 }32730 }
32722 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);32731 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
32723 },32732 },
32724 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {32733 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
32725 .error_set_type => |error_set_type| ok: {32734 .error_set_type => |error_set_type| ok: {
32726 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;32735 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
32727 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);32736 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
32728 },32737 },
32729 .inferred_error_set_type => |func_index| ok: {32738 .inferred_error_set_type => |func_index| ok: {
32730 // We carefully do this in an order that avoids unnecessarily32739 // We carefully do this in an order that avoids unnecessarily
...@@ -32740,7 +32749,7 @@ fn wrapErrorUnionSet(...@@ -32740,7 +32749,7 @@ fn wrapErrorUnionSet(
32740 },32749 },
32741 }32750 }
3274232751
32743 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);32752 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
32744 },32753 },
32745 else => unreachable,32754 else => unreachable,
32746 },32755 },
src/Type.zig+192-13
...@@ -141,7 +141,7 @@ const Format = struct {...@@ -141,7 +141,7 @@ const Format = struct {
141 pt: Zcu.PerThread,141 pt: Zcu.PerThread,
142142
143 fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {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};
147147
...@@ -157,7 +157,17 @@ pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void {...@@ -157,7 +157,17 @@ pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void {
157157
158/// Prints a name suitable for `@typeName`.158/// Prints a name suitable for `@typeName`.
159/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.159/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.
160pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.Error!void {160pub 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 const zcu = pt.zcu;171 const zcu = pt.zcu;
162 const ip = &zcu.intern_pool;172 const ip = &zcu.intern_pool;
163 switch (ip.indexToKey(ty.toIntern())) {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,39 +219,39 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
209 if (info.flags.is_const) try writer.writeAll("const ");219 if (info.flags.is_const) try writer.writeAll("const ");
210 if (info.flags.is_volatile) try writer.writeAll("volatile ");220 if (info.flags.is_volatile) try writer.writeAll("volatile ");
211221
212 try print(Type.fromInterned(info.child), writer, pt);222 try print(Type.fromInterned(info.child), writer, pt, ctx);
213 return;223 return;
214 },224 },
215 .array_type => |array_type| {225 .array_type => |array_type| {
216 if (array_type.sentinel == .none) {226 if (array_type.sentinel == .none) {
217 try writer.print("[{d}]", .{array_type.len});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 } else {229 } else {
220 try writer.print("[{d}:{f}]", .{230 try writer.print("[{d}:{f}]", .{
221 array_type.len,231 array_type.len,
222 Value.fromInterned(array_type.sentinel).fmtValue(pt),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 return;236 return;
227 },237 },
228 .vector_type => |vector_type| {238 .vector_type => |vector_type| {
229 try writer.print("@Vector({d}, ", .{vector_type.len});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 try writer.writeAll(")");241 try writer.writeAll(")");
232 return;242 return;
233 },243 },
234 .opt_type => |child| {244 .opt_type => |child| {
235 try writer.writeByte('?');245 try writer.writeByte('?');
236 return print(Type.fromInterned(child), writer, pt);246 return print(Type.fromInterned(child), writer, pt, ctx);
237 },247 },
238 .error_union_type => |error_union_type| {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 try writer.writeByte('!');250 try writer.writeByte('!');
241 if (error_union_type.payload_type == .generic_poison_type) {251 if (error_union_type.payload_type == .generic_poison_type) {
242 try writer.writeAll("anytype");252 try writer.writeAll("anytype");
243 } else {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 return;256 return;
247 },257 },
...@@ -323,7 +333,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer....@@ -323,7 +333,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
323 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {333 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
324 try writer.writeAll(if (i == 0) " " else ", ");334 try writer.writeAll(if (i == 0) " " else ", ");
325 if (val != .none) try writer.writeAll("comptime ");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 if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});337 if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
328 }338 }
329 try writer.writeAll(" }");339 try writer.writeAll(" }");
...@@ -360,7 +370,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer....@@ -360,7 +370,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
360 if (param_ty == .generic_poison_type) {370 if (param_ty == .generic_poison_type) {
361 try writer.writeAll("anytype");371 try writer.writeAll("anytype");
362 } else {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 if (fn_info.is_var_args) {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,13 +397,13 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
387 if (fn_info.return_type == .generic_poison_type) {397 if (fn_info.return_type == .generic_poison_type) {
388 try writer.writeAll("anytype");398 try writer.writeAll("anytype");
389 } else {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 .anyframe_type => |child| {403 .anyframe_type => |child| {
394 if (child == .none) return writer.writeAll("anyframe");404 if (child == .none) return writer.writeAll("anyframe");
395 try writer.writeAll("anyframe->");405 try writer.writeAll("anyframe->");
396 return print(Type.fromInterned(child), writer, pt);406 return print(Type.fromInterned(child), writer, pt, ctx);
397 },407 },
398408
399 // values, not types409 // values, not types
...@@ -4046,6 +4056,175 @@ pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {...@@ -4046,6 +4056,175 @@ pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {
4046 return null;4056 return null;
4047}4057}
40484058
4059/// Recursively walks the type and marks for each subtype how many times it has been seen
4060fn 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
4136fn 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.
4174pub 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
4049pub const @"u1": Type = .{ .ip_index = .u1_type };4228pub const @"u1": Type = .{ .ip_index = .u1_type };
4050pub const @"u8": Type = .{ .ip_index = .u8_type };4229pub const @"u8": Type = .{ .ip_index = .u8_type };
4051pub const @"u16": Type = .{ .ip_index = .u16_type };4230pub const @"u16": Type = .{ .ip_index = .u16_type };
src/codegen/llvm.zig+1-1
...@@ -2697,7 +2697,7 @@ pub const Object = struct {...@@ -2697,7 +2697,7 @@ pub const Object = struct {
2697 fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 {2697 fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 {
2698 var aw: std.Io.Writer.Allocating = .init(o.gpa);2698 var aw: std.Io.Writer.Allocating = .init(o.gpa);
2699 defer aw.deinit();2699 defer aw.deinit();
2700 ty.print(&aw.writer, pt) catch |err| switch (err) {2700 ty.print(&aw.writer, pt, null) catch |err| switch (err) {
2701 error.WriteFailed => return error.OutOfMemory,2701 error.WriteFailed => return error.OutOfMemory,
2702 };2702 };
2703 return aw.toOwnedSliceSentinel(0);2703 return aw.toOwnedSliceSentinel(0);
src/codegen/spirv/CodeGen.zig+1-1
...@@ -1213,7 +1213,7 @@ fn resolveTypeName(cg: *CodeGen, ty: Type) ![]const u8 {...@@ -1213,7 +1213,7 @@ fn resolveTypeName(cg: *CodeGen, ty: Type) ![]const u8 {
1213 const gpa = cg.module.gpa;1213 const gpa = cg.module.gpa;
1214 var aw: std.Io.Writer.Allocating = .init(gpa);1214 var aw: std.Io.Writer.Allocating = .init(gpa);
1215 defer aw.deinit();1215 defer aw.deinit();
1216 ty.print(&aw.writer, cg.pt) catch |err| switch (err) {1216 ty.print(&aw.writer, cg.pt, null) catch |err| switch (err) {
1217 error.WriteFailed => return error.OutOfMemory,1217 error.WriteFailed => return error.OutOfMemory,
1218 };1218 };
1219 return try aw.toOwnedSlice();1219 return try aw.toOwnedSlice();
src/print_value.zig+1-1
...@@ -66,7 +66,7 @@ pub fn print(...@@ -66,7 +66,7 @@ pub fn print(
66 .func_type,66 .func_type,
67 .error_set_type,67 .error_set_type,
68 .inferred_error_set_type,68 .inferred_error_set_type,
69 => try Type.print(val.toType(), writer, pt),69 => try Type.print(val.toType(), writer, pt, null),
70 .undef => try writer.writeAll("undefined"),70 .undef => try writer.writeAll("undefined"),
71 .simple_value => |simple_value| switch (simple_value) {71 .simple_value => |simple_value| switch (simple_value) {
72 .void => try writer.writeAll("{}"),72 .void => try writer.writeAll("{}"),
test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig+2-1
...@@ -16,7 +16,8 @@ comptime {...@@ -16,7 +16,8 @@ comptime {
16//16//
17// :2:29: error: expected type '[][]const u8', found '*const [2][]const u8'17// :2:29: error: expected type '[][]const u8', found '*const [2][]const u8'
18// :2:29: note: cast discards const qualifier18// :2:29: note: cast discards const qualifier
19// :6:31: error: expected type '*[2][]const u8', found '*const [2][]const u8'19// :6:31: error: expected type '*<T>', found '*const <T>'
20// :6:31: note: <T> = [2][]const u8
20// :6:31: note: cast discards const qualifier21// :6:31: note: cast discards const qualifier
21// :11:19: error: expected type '*tmp.S', found '*const tmp.S'22// :11:19: error: expected type '*tmp.S', found '*const tmp.S'
22// :11:19: note: cast discards const qualifier23// :11:19: note: cast discards const qualifier
test/cases/compile_errors/type_dedupe.zig created+18
...@@ -0,0 +1,18 @@
1const SomeVeryLongName = struct {};
2
3fn foo(a: *SomeVeryLongName) void {
4 _ = a;
5}
6
7export fn entry() void {
8 const a: SomeVeryLongName = .{};
9
10 foo(a);
11}
12
13// error
14//
15// :10:9: error: expected type '*<T>', found '<T>'
16// :10:9: note: <T> = tmp.SomeVeryLongName
17// :1:26: note: struct declared here
18// :3:11: note: parameter type declared here
test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig+2-1
...@@ -5,4 +5,5 @@ export fn entry() void {...@@ -5,4 +5,5 @@ export fn entry() void {
55
6// error6// error
7//7//
8// :3:11: error: expected type '@TypeOf(.{})', found 'struct { comptime comptime_int = 1, comptime comptime_int = 2, comptime comptime_int = 3 }'8// :3:11: error: expected type '@TypeOf(.{})', found 'struct { comptime <T> = 1, comptime <T> = 2, comptime <T> = 3 }'
9// :3:11: note: <T> = comptime_int