Dedupe types when printing error messages

This commit is contained in:
Prokop Randáček 2025-11-16 14:20:45 +00:00 committed by GitHub
parent aa4332fb0e
commit 94e98bfe80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 254 additions and 46 deletions

View file

@ -363,7 +363,7 @@ const Writer = struct {
} }
fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void { fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void {
return ty.print(s, w.pt); return ty.print(s, w.pt, null);
} }
fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {

View file

@ -2447,19 +2447,6 @@ fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, t
}); });
} }
fn failWithErrorSetCodeMissing(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
dest_err_set_ty: Type,
src_err_set_ty: Type,
) CompileError {
const pt = sema.pt;
return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{
dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt),
});
}
pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError { pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {
const pt = sema.pt; const pt = sema.pt;
return sema.failWithOwnedErrorMsg(block, msg: { return sema.failWithOwnedErrorMsg(block, msg: {
@ -2619,6 +2606,26 @@ pub fn errMsg(
return Zcu.ErrorMsg.create(sema.gpa, src, format, args); return Zcu.ErrorMsg.create(sema.gpa, src, format, args);
} }
fn typeMismatchErrMsg(sema: *Sema, src: LazySrcLoc, expected: Type, found: Type) Allocator.Error!*Zcu.ErrorMsg {
const pt = sema.pt;
var cmp: Type.Comparison = try .init(&.{ expected, found }, pt);
defer cmp.deinit(pt);
const msg = try sema.errMsg(src, "expected type '{f}', found '{f}'", .{
cmp.fmtType(expected, pt),
cmp.fmtType(found, pt),
});
errdefer msg.destroy(sema.gpa);
for (cmp.type_dedupe_cache.keys(), cmp.type_dedupe_cache.values()) |ty, value| {
if (value == .dont_dedupe) continue;
const placeholder = value.dedupe;
try sema.errNote(src, msg, "{f} = {f}", .{ placeholder, ty.fmt(pt) });
}
return msg;
}
pub fn fail( pub fn fail(
sema: *Sema, sema: *Sema,
block: *Block, block: *Block,
@ -2635,6 +2642,14 @@ pub fn fail(
return sema.failWithOwnedErrorMsg(block, err_msg); return sema.failWithOwnedErrorMsg(block, err_msg);
} }
fn failWithTypeMismatch(sema: *Sema, block: *Block, src: LazySrcLoc, expected: Type, found: Type) CompileError {
const err_msg = try sema.typeMismatchErrMsg(src, expected, found);
errdefer err_msg.destroy(sema.gpa);
try addDeclaredHereNote(sema, err_msg, expected);
try addDeclaredHereNote(sema, err_msg, found);
return sema.failWithOwnedErrorMsg(block, err_msg);
}
pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } { pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } {
@branchHint(.cold); @branchHint(.cold);
const gpa = sema.gpa; const gpa = sema.gpa;
@ -22933,7 +22948,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector; const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;
const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector; const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;
if (operand_is_vector != dest_is_vector) { if (operand_is_vector != dest_is_vector) {
return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }); return sema.failWithTypeMismatch(block, operand_src, dest_ty, operand_ty);
} }
if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
@ -29167,7 +29182,7 @@ fn coerceExtra(
} }
const msg = msg: { const msg = msg: {
const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) }); const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
errdefer msg.destroy(sema.gpa); errdefer msg.destroy(sema.gpa);
if (!can_coerce_to) { if (!can_coerce_to) {
@ -30780,9 +30795,7 @@ fn coerceEnumToUnion(
const tag_ty = union_ty.unionTagType(zcu) orelse { const tag_ty = union_ty.unionTagType(zcu) orelse {
const msg = msg: { const msg = msg: {
const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty);
union_ty.fmt(pt), inst_ty.fmt(pt),
});
errdefer msg.destroy(sema.gpa); errdefer msg.destroy(sema.gpa);
try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
try sema.addDeclaredHereNote(msg, union_ty); try sema.addDeclaredHereNote(msg, union_ty);
@ -30933,9 +30946,7 @@ fn coerceArrayLike(
const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu)); const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));
if (dest_len != inst_len) { if (dest_len != inst_len) {
const msg = msg: { const msg = msg: {
const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
dest_ty.fmt(pt), inst_ty.fmt(pt),
});
errdefer msg.destroy(sema.gpa); errdefer msg.destroy(sema.gpa);
try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
@ -31018,9 +31029,7 @@ fn coerceTupleToArray(
if (dest_len != inst_len) { if (dest_len != inst_len) {
const msg = msg: { const msg = msg: {
const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
dest_ty.fmt(pt), inst_ty.fmt(pt),
});
errdefer msg.destroy(sema.gpa); errdefer msg.destroy(sema.gpa);
try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
@ -32719,12 +32728,12 @@ fn wrapErrorUnionSet(
break :ok; break :ok;
}, },
} }
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
}, },
else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
.error_set_type => |error_set_type| ok: { .error_set_type => |error_set_type| ok: {
if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
}, },
.inferred_error_set_type => |func_index| ok: { .inferred_error_set_type => |func_index| ok: {
// We carefully do this in an order that avoids unnecessarily // We carefully do this in an order that avoids unnecessarily
@ -32740,7 +32749,7 @@ fn wrapErrorUnionSet(
}, },
} }
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
}, },
else => unreachable, else => unreachable,
}, },

View file

@ -141,7 +141,7 @@ const Format = struct {
pt: Zcu.PerThread, pt: Zcu.PerThread,
fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
return print(f.ty, writer, f.pt); return print(f.ty, writer, f.pt, null);
} }
}; };
@ -157,7 +157,17 @@ pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void {
/// Prints a name suitable for `@typeName`. /// Prints a name suitable for `@typeName`.
/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.
pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.Error!void { pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Comparison) std.Io.Writer.Error!void {
if (ctx) |c| {
const should_dedupe = shouldDedupeType(ty, c, pt) catch |err| switch (err) {
error.OutOfMemory => return error.WriteFailed,
};
switch (should_dedupe) {
.dont_dedupe => {},
.dedupe => |placeholder| return placeholder.format(writer),
}
}
const zcu = pt.zcu; const zcu = pt.zcu;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
switch (ip.indexToKey(ty.toIntern())) { switch (ip.indexToKey(ty.toIntern())) {
@ -209,39 +219,39 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
if (info.flags.is_const) try writer.writeAll("const "); if (info.flags.is_const) try writer.writeAll("const ");
if (info.flags.is_volatile) try writer.writeAll("volatile "); if (info.flags.is_volatile) try writer.writeAll("volatile ");
try print(Type.fromInterned(info.child), writer, pt); try print(Type.fromInterned(info.child), writer, pt, ctx);
return; return;
}, },
.array_type => |array_type| { .array_type => |array_type| {
if (array_type.sentinel == .none) { if (array_type.sentinel == .none) {
try writer.print("[{d}]", .{array_type.len}); try writer.print("[{d}]", .{array_type.len});
try print(Type.fromInterned(array_type.child), writer, pt); try print(Type.fromInterned(array_type.child), writer, pt, ctx);
} else { } else {
try writer.print("[{d}:{f}]", .{ try writer.print("[{d}:{f}]", .{
array_type.len, array_type.len,
Value.fromInterned(array_type.sentinel).fmtValue(pt), Value.fromInterned(array_type.sentinel).fmtValue(pt),
}); });
try print(Type.fromInterned(array_type.child), writer, pt); try print(Type.fromInterned(array_type.child), writer, pt, ctx);
} }
return; return;
}, },
.vector_type => |vector_type| { .vector_type => |vector_type| {
try writer.print("@Vector({d}, ", .{vector_type.len}); try writer.print("@Vector({d}, ", .{vector_type.len});
try print(Type.fromInterned(vector_type.child), writer, pt); try print(Type.fromInterned(vector_type.child), writer, pt, ctx);
try writer.writeAll(")"); try writer.writeAll(")");
return; return;
}, },
.opt_type => |child| { .opt_type => |child| {
try writer.writeByte('?'); try writer.writeByte('?');
return print(Type.fromInterned(child), writer, pt); return print(Type.fromInterned(child), writer, pt, ctx);
}, },
.error_union_type => |error_union_type| { .error_union_type => |error_union_type| {
try print(Type.fromInterned(error_union_type.error_set_type), writer, pt); try print(Type.fromInterned(error_union_type.error_set_type), writer, pt, ctx);
try writer.writeByte('!'); try writer.writeByte('!');
if (error_union_type.payload_type == .generic_poison_type) { if (error_union_type.payload_type == .generic_poison_type) {
try writer.writeAll("anytype"); try writer.writeAll("anytype");
} else { } else {
try print(Type.fromInterned(error_union_type.payload_type), writer, pt); try print(Type.fromInterned(error_union_type.payload_type), writer, pt, ctx);
} }
return; return;
}, },
@ -323,7 +333,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| { for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
try writer.writeAll(if (i == 0) " " else ", "); try writer.writeAll(if (i == 0) " " else ", ");
if (val != .none) try writer.writeAll("comptime "); if (val != .none) try writer.writeAll("comptime ");
try print(Type.fromInterned(field_ty), writer, pt); try print(Type.fromInterned(field_ty), writer, pt, ctx);
if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
} }
try writer.writeAll(" }"); try writer.writeAll(" }");
@ -360,7 +370,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread) std.Io.Writer.
if (param_ty == .generic_poison_type) { if (param_ty == .generic_poison_type) {
try writer.writeAll("anytype"); try writer.writeAll("anytype");
} else { } else {
try print(Type.fromInterned(param_ty), writer, pt); try print(Type.fromInterned(param_ty), writer, pt, ctx);
} }
} }
if (fn_info.is_var_args) { 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.
if (fn_info.return_type == .generic_poison_type) { if (fn_info.return_type == .generic_poison_type) {
try writer.writeAll("anytype"); try writer.writeAll("anytype");
} else { } else {
try print(Type.fromInterned(fn_info.return_type), writer, pt); try print(Type.fromInterned(fn_info.return_type), writer, pt, ctx);
} }
}, },
.anyframe_type => |child| { .anyframe_type => |child| {
if (child == .none) return writer.writeAll("anyframe"); if (child == .none) return writer.writeAll("anyframe");
try writer.writeAll("anyframe->"); try writer.writeAll("anyframe->");
return print(Type.fromInterned(child), writer, pt); return print(Type.fromInterned(child), writer, pt, ctx);
}, },
// values, not types // values, not types
@ -4046,6 +4056,175 @@ pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {
return null; return null;
} }
/// Recursively walks the type and marks for each subtype how many times it has been seen
fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUnmanaged(Type, u16)) error{OutOfMemory}!void {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const gop = try visited.getOrPut(zcu.gpa, ty);
if (gop.found_existing) {
gop.value_ptr.* += 1;
} else {
gop.value_ptr.* = 1;
}
switch (ip.indexToKey(ty.toIntern())) {
.ptr_type => try collectSubtypes(Type.fromInterned(ty.ptrInfo(zcu).child), pt, visited),
.array_type => |array_type| try collectSubtypes(Type.fromInterned(array_type.child), pt, visited),
.vector_type => |vector_type| try collectSubtypes(Type.fromInterned(vector_type.child), pt, visited),
.opt_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
.error_union_type => |error_union_type| {
try collectSubtypes(Type.fromInterned(error_union_type.error_set_type), pt, visited);
if (error_union_type.payload_type != .generic_poison_type) {
try collectSubtypes(Type.fromInterned(error_union_type.payload_type), pt, visited);
}
},
.tuple_type => |tuple| {
for (tuple.types.get(ip)) |field_ty| {
try collectSubtypes(Type.fromInterned(field_ty), pt, visited);
}
},
.func_type => |fn_info| {
const param_types = fn_info.param_types.get(&zcu.intern_pool);
for (param_types) |param_ty| {
if (param_ty != .generic_poison_type) {
try collectSubtypes(Type.fromInterned(param_ty), pt, visited);
}
}
if (fn_info.return_type != .generic_poison_type) {
try collectSubtypes(Type.fromInterned(fn_info.return_type), pt, visited);
}
},
.anyframe_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
// leaf types
.undef,
.inferred_error_set_type,
.error_set_type,
.struct_type,
.union_type,
.opaque_type,
.enum_type,
.simple_type,
.int_type,
=> {},
// values, not types
.simple_value,
.variable,
.@"extern",
.func,
.int,
.err,
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.slice,
.opt,
.aggregate,
.un,
// memoization, not types
.memoized_call,
=> unreachable,
}
}
fn shouldDedupeType(ty: Type, ctx: *Comparison, pt: Zcu.PerThread) error{OutOfMemory}!Comparison.DedupeEntry {
if (ctx.type_occurrences.get(ty)) |occ| {
if (ctx.type_dedupe_cache.get(ty)) |cached| {
return cached;
}
var discarding: std.Io.Writer.Discarding = .init(&.{});
print(ty, &discarding.writer, pt, null) catch
unreachable; // we are writing into a discarding writer, it should never fail
const type_len: i32 = @intCast(discarding.count);
const placeholder_len: i32 = 3;
const min_saved_bytes: i32 = 10;
const saved_bytes = (type_len - placeholder_len) * (occ - 1);
const max_placeholders = 7; // T to Z
const should_dedupe = saved_bytes >= min_saved_bytes and ctx.placeholder_index < max_placeholders;
const entry: Comparison.DedupeEntry = if (should_dedupe) b: {
ctx.placeholder_index += 1;
break :b .{ .dedupe = .{ .index = ctx.placeholder_index - 1 } };
} else .dont_dedupe;
try ctx.type_dedupe_cache.put(pt.zcu.gpa, ty, entry);
return entry;
} else {
return .{ .dont_dedupe = {} };
}
}
/// The comparison recursively walks all types given and notes how many times
/// each subtype occurs. It then while recursively printing decides for each
/// subtype whether to print the type inline or create a placeholder based on
/// the subtype length and number of occurences. Placeholders are then found by
/// iterating `type_dedupe_cache` which caches the inline/placeholder decisions.
pub const Comparison = struct {
type_occurrences: std.AutoArrayHashMapUnmanaged(Type, u16),
type_dedupe_cache: std.AutoArrayHashMapUnmanaged(Type, DedupeEntry),
placeholder_index: u8,
pub const Placeholder = struct {
index: u8,
pub fn format(p: Placeholder, writer: *std.Io.Writer) error{WriteFailed}!void {
return writer.print("<{c}>", .{p.index + 'T'});
}
};
pub const DedupeEntry = union(enum) {
dont_dedupe: void,
dedupe: Placeholder,
};
pub fn init(types: []const Type, pt: Zcu.PerThread) error{OutOfMemory}!Comparison {
var cmp: Comparison = .{
.type_occurrences = .empty,
.type_dedupe_cache = .empty,
.placeholder_index = 0,
};
errdefer cmp.deinit(pt);
for (types) |ty| {
try collectSubtypes(ty, pt, &cmp.type_occurrences);
}
return cmp;
}
pub fn deinit(cmp: *Comparison, pt: Zcu.PerThread) void {
const gpa = pt.zcu.gpa;
cmp.type_occurrences.deinit(gpa);
cmp.type_dedupe_cache.deinit(gpa);
}
pub fn fmtType(ctx: *Comparison, ty: Type, pt: Zcu.PerThread) Comparison.Formatter {
return .{ .ty = ty, .ctx = ctx, .pt = pt };
}
pub const Formatter = struct {
ty: Type,
ctx: *Comparison,
pt: Zcu.PerThread,
pub fn format(self: Comparison.Formatter, writer: anytype) error{WriteFailed}!void {
print(self.ty, writer, self.pt, self.ctx) catch return error.WriteFailed;
}
};
};
pub const @"u1": Type = .{ .ip_index = .u1_type }; pub const @"u1": Type = .{ .ip_index = .u1_type };
pub const @"u8": Type = .{ .ip_index = .u8_type }; pub const @"u8": Type = .{ .ip_index = .u8_type };
pub const @"u16": Type = .{ .ip_index = .u16_type }; pub const @"u16": Type = .{ .ip_index = .u16_type };

View file

@ -2697,7 +2697,7 @@ pub const Object = struct {
fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 { fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 {
var aw: std.Io.Writer.Allocating = .init(o.gpa); var aw: std.Io.Writer.Allocating = .init(o.gpa);
defer aw.deinit(); defer aw.deinit();
ty.print(&aw.writer, pt) catch |err| switch (err) { ty.print(&aw.writer, pt, null) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory, error.WriteFailed => return error.OutOfMemory,
}; };
return aw.toOwnedSliceSentinel(0); return aw.toOwnedSliceSentinel(0);

View file

@ -1213,7 +1213,7 @@ fn resolveTypeName(cg: *CodeGen, ty: Type) ![]const u8 {
const gpa = cg.module.gpa; const gpa = cg.module.gpa;
var aw: std.Io.Writer.Allocating = .init(gpa); var aw: std.Io.Writer.Allocating = .init(gpa);
defer aw.deinit(); defer aw.deinit();
ty.print(&aw.writer, cg.pt) catch |err| switch (err) { ty.print(&aw.writer, cg.pt, null) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory, error.WriteFailed => return error.OutOfMemory,
}; };
return try aw.toOwnedSlice(); return try aw.toOwnedSlice();

View file

@ -66,7 +66,7 @@ pub fn print(
.func_type, .func_type,
.error_set_type, .error_set_type,
.inferred_error_set_type, .inferred_error_set_type,
=> try Type.print(val.toType(), writer, pt), => try Type.print(val.toType(), writer, pt, null),
.undef => try writer.writeAll("undefined"), .undef => try writer.writeAll("undefined"),
.simple_value => |simple_value| switch (simple_value) { .simple_value => |simple_value| switch (simple_value) {
.void => try writer.writeAll("{}"), .void => try writer.writeAll("{}"),

View file

@ -16,7 +16,8 @@ comptime {
// //
// :2:29: error: expected type '[][]const u8', found '*const [2][]const u8' // :2:29: error: expected type '[][]const u8', found '*const [2][]const u8'
// :2:29: note: cast discards const qualifier // :2:29: note: cast discards const qualifier
// :6:31: error: expected type '*[2][]const u8', found '*const [2][]const u8' // :6:31: error: expected type '*<T>', found '*const <T>'
// :6:31: note: <T> = [2][]const u8
// :6:31: note: cast discards const qualifier // :6:31: note: cast discards const qualifier
// :11:19: error: expected type '*tmp.S', found '*const tmp.S' // :11:19: error: expected type '*tmp.S', found '*const tmp.S'
// :11:19: note: cast discards const qualifier // :11:19: note: cast discards const qualifier

View file

@ -0,0 +1,18 @@
const SomeVeryLongName = struct {};
fn foo(a: *SomeVeryLongName) void {
_ = a;
}
export fn entry() void {
const a: SomeVeryLongName = .{};
foo(a);
}
// error
//
// :10:9: error: expected type '*<T>', found '<T>'
// :10:9: note: <T> = tmp.SomeVeryLongName
// :1:26: note: struct declared here
// :3:11: note: parameter type declared here

View file

@ -5,4 +5,5 @@ export fn entry() void {
// error // error
// //
// :3:11: error: expected type '@TypeOf(.{})', found 'struct { comptime comptime_int = 1, comptime comptime_int = 2, comptime comptime_int = 3 }' // :3:11: error: expected type '@TypeOf(.{})', found 'struct { comptime <T> = 1, comptime <T> = 2, comptime <T> = 3 }'
// :3:11: note: <T> = comptime_int