Skip to content

Make distinct error limit configurable #17532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ pub const InitOptions = struct {
pdb_source_path: ?[]const u8 = null,
/// (Windows) PDB output path
pdb_out_path: ?[]const u8 = null,
error_limit: ?Module.ErrorInt = null,
};

fn addModuleTableToCacheHash(
Expand Down Expand Up @@ -1414,6 +1415,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.local_zir_cache = local_zir_cache,
.emit_h = emit_h,
.tmp_hack_arena = std.heap.ArenaAllocator.init(gpa),
.error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1),
};
try module.init();

Expand Down Expand Up @@ -2458,6 +2460,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
man.hash.add(comp.bin_file.options.skip_linker_dependencies);
man.hash.add(comp.bin_file.options.parent_compilation_link_libc);
man.hash.add(mod.emit_h != null);
man.hash.add(mod.error_limit);
}

try man.addOptionalFile(comp.bin_file.options.linker_script);
Expand Down Expand Up @@ -2831,6 +2834,10 @@ pub fn totalErrorCount(self: *Compilation) u32 {
}
}
}

if (module.global_error_set.entries.len - 1 > module.error_limit) {
total += 1;
}
}

// The "no entry point found" error only counts if there are no semantic analysis errors.
Expand Down Expand Up @@ -2981,6 +2988,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
for (module.failed_exports.values()) |value| {
try addModuleErrorMsg(module, &bundle, value.*);
}

const actual_error_count = module.global_error_set.entries.len - 1;
if (actual_error_count > module.error_limit) {
try bundle.addRootErrorMessage(.{
.msg = try bundle.printString("module used more errors than possible: used {d}, max {d}", .{
actual_error_count, module.error_limit,
}),
.notes_len = 1,
});
const notes_start = try bundle.reserveNotes(1);
bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{
.msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
actual_error_count,
}),
}));
}
}

if (bundle.root_list.items.len == 0) {
Expand Down
8 changes: 8 additions & 0 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ deletion_set: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
/// Key is the error name, index is the error tag value. Index 0 has a length-0 string.
global_error_set: GlobalErrorSet = .{},

/// Maximum amount of distinct error values, set by --error-limit
error_limit: ErrorInt,

/// Incrementing integer used to compare against the corresponding Decl
/// field to determine whether a Decl's status applies to an ongoing update, or a
/// previous analysis.
Expand Down Expand Up @@ -5020,6 +5023,11 @@ pub fn getErrorValueFromSlice(
return getErrorValue(mod, interned_name);
}

pub fn errorSetBits(mod: *Module) u16 {
if (mod.error_limit == 0) return 0;
return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
}

pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !Decl.Index {
const src_decl = mod.declPtr(block.src_decl);
return mod.createAnonymousDeclFromDecl(src_decl, block.namespace, block.wip_capture_scope, typed_value);
Expand Down
9 changes: 9 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ const usage_build_generic =
\\ --deps [dep],[dep],... Set dependency names for the root package
\\ dep: [[import=]name]
\\ --main-mod-path Set the directory of the root module
\\ --error-limit [num] Set the maximum amount of distinct error values
\\ -fPIC Force-enable Position Independent Code
\\ -fno-PIC Force-disable Position Independent Code
\\ -fPIE Force-enable Position Independent Executable
Expand Down Expand Up @@ -921,6 +922,8 @@ fn buildOutputType(
var error_tracing: ?bool = null;
var pdb_out_path: ?[]const u8 = null;
var dwarf_format: ?std.dwarf.Format = null;
var error_limit: ?Module.ErrorInt = null;

// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
// This array is populated by zig cc frontend and then has to be converted to zig-style
// CPU features.
Expand Down Expand Up @@ -1049,6 +1052,11 @@ fn buildOutputType(
root_deps_str = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--main-mod-path")) {
main_mod_path = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--error-limit")) {
const next_arg = args_iter.nextOrFatal();
error_limit = std.fmt.parseUnsigned(Module.ErrorInt, next_arg, 0) catch |err| {
fatal("unable to parse error limit '{s}': {s}", .{ next_arg, @errorName(err) });
};
} else if (mem.eql(u8, arg, "-cflags")) {
extra_cflags.shrinkRetainingCapacity(0);
while (true) {
Expand Down Expand Up @@ -3538,6 +3546,7 @@ fn buildOutputType(
.reference_trace = reference_trace,
.error_tracing = error_tracing,
.pdb_out_path = pdb_out_path,
.error_limit = error_limit,
}) catch |err| switch (err) {
error.LibCUnavailable => {
const target = target_info.target;
Expand Down
46 changes: 26 additions & 20 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,11 @@ pub const Type = struct {
.opt_type => return abiAlignmentAdvancedOptional(ty, mod, strat),
.error_union_type => |info| return abiAlignmentAdvancedErrorUnion(ty, mod, strat, info.payload_type.toType()),

// TODO revisit this when we have the concept of the error tag type
.error_set_type, .inferred_error_set_type => return .{ .scalar = .@"2" },
.error_set_type, .inferred_error_set_type => {
const bits = mod.errorSetBits();
if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" };
return .{ .scalar = intAbiAlignment(bits, target) };
},

// represents machine code; not a pointer
.func_type => |func_type| return .{
Expand Down Expand Up @@ -967,10 +970,11 @@ pub const Type = struct {
else => return .{ .scalar = .@"16" },
},

// TODO revisit this when we have the concept of the error tag type
.anyerror,
.adhoc_inferred_error_set,
=> return .{ .scalar = .@"2" },
.anyerror, .adhoc_inferred_error_set => {
const bits = mod.errorSetBits();
if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" };
return .{ .scalar = intAbiAlignment(bits, target) };
},

.void,
.type,
Expand Down Expand Up @@ -1284,8 +1288,11 @@ pub const Type = struct {

.opt_type => return ty.abiSizeAdvancedOptional(mod, strat),

// TODO revisit this when we have the concept of the error tag type
.error_set_type, .inferred_error_set_type => return AbiSizeAdvanced{ .scalar = 2 },
.error_set_type, .inferred_error_set_type => {
const bits = mod.errorSetBits();
if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 };
return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) };
},

.error_union_type => |error_union_type| {
const payload_ty = error_union_type.payload_type.toType();
Expand Down Expand Up @@ -1379,10 +1386,11 @@ pub const Type = struct {
.enum_literal,
=> return AbiSizeAdvanced{ .scalar = 0 },

// TODO revisit this when we have the concept of the error tag type
.anyerror,
.adhoc_inferred_error_set,
=> return AbiSizeAdvanced{ .scalar = 2 },
.anyerror, .adhoc_inferred_error_set => {
const bits = mod.errorSetBits();
if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 };
return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) };
},

.prefetch_options => unreachable, // missing call to resolveTypeFields
.export_options => unreachable, // missing call to resolveTypeFields
Expand Down Expand Up @@ -1576,8 +1584,7 @@ pub const Type = struct {
return (try abiSizeAdvanced(ty, mod, strat)).scalar * 8;
},

// TODO revisit this when we have the concept of the error tag type
.error_set_type, .inferred_error_set_type => return 16,
.error_set_type, .inferred_error_set_type => return mod.errorSetBits(),

.error_union_type => {
// Optionals and error unions are not packed so their bitsize
Expand Down Expand Up @@ -1610,10 +1617,9 @@ pub const Type = struct {
.bool => return 1,
.void => return 0,

// TODO revisit this when we have the concept of the error tag type
.anyerror,
.adhoc_inferred_error_set,
=> return 16,
=> return mod.errorSetBits(),

.anyopaque => unreachable,
.type => unreachable,
Expand Down Expand Up @@ -2172,8 +2178,7 @@ pub const Type = struct {

while (true) switch (ty.toIntern()) {
.anyerror_type, .adhoc_inferred_error_set_type => {
// TODO revisit this when error sets support custom int types
return .{ .signedness = .unsigned, .bits = 16 };
return .{ .signedness = .unsigned, .bits = mod.errorSetBits() };
},
.usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() },
.isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() },
Expand All @@ -2192,8 +2197,9 @@ pub const Type = struct {
.enum_type => |enum_type| ty = enum_type.tag_ty.toType(),
.vector_type => |vector_type| ty = vector_type.child.toType(),

// TODO revisit this when error sets support custom int types
.error_set_type, .inferred_error_set_type => return .{ .signedness = .unsigned, .bits = 16 },
.error_set_type, .inferred_error_set_type => {
return .{ .signedness = .unsigned, .bits = mod.errorSetBits() };
},

.anon_struct_type => unreachable,

Expand Down
23 changes: 15 additions & 8 deletions src/value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -701,15 +701,20 @@ pub const Value = struct {
}
},
.ErrorSet => {
// TODO revisit this when we have the concept of the error tag type
const Int = u16;
const bits = mod.errorSetBits();
const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);

const name = switch (ip.indexToKey(val.toIntern())) {
.err => |err| err.name,
.error_union => |error_union| error_union.val.err_name,
else => unreachable,
};
const int = @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(name).?));
std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian);
var bigint_buffer: BigIntSpace = undefined;
const bigint = BigIntMutable.init(
&bigint_buffer.limbs,
mod.global_error_set.getIndex(name).?,
).toConst();
bigint.writeTwosComplement(buffer[0..byte_count], endian);
},
.Union => switch (ty.containerLayout(mod)) {
.Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
Expand Down Expand Up @@ -987,10 +992,12 @@ pub const Value = struct {
}
},
.ErrorSet => {
// TODO revisit this when we have the concept of the error tag type
const Int = u16;
const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], endian);
const name = mod.global_error_set.keys()[@as(usize, @intCast(int))];
const bits = mod.errorSetBits();
const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
const name = mod.global_error_set.keys()[@intCast(index)];

return (try mod.intern(.{ .err = .{
.ty = ty.toIntern(),
.name = name,
Expand Down