mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Dedupe types when printing error messages
This commit is contained in:
parent
aa4332fb0e
commit
94e98bfe80
@ -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 {
|
||||||
|
|||||||
63
src/Sema.zig
63
src/Sema.zig
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
205
src/Type.zig
205
src/Type.zig
@ -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 };
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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("{}"),
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
18
test/cases/compile_errors/type_dedupe.zig
Normal file
18
test/cases/compile_errors/type_dedupe.zig
Normal 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
|
||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user