Skip to content

Commit 94d61ce

Browse files
authored
Merge pull request #17651 from Vexu/error-limit
Make distinct error limit configurable (attempt #2)
2 parents b82459f + ed82e4f commit 94d61ce

14 files changed

+195
-110
lines changed

doc/langref.html.in

+5-4
Original file line numberDiff line numberDiff line change
@@ -5373,8 +5373,9 @@ test "fn reflection" {
53735373
gets assigned the same integer value.
53745374
</p>
53755375
<p>
5376-
The number of unique error values across the entire compilation should determine the size of the error set type.
5377-
However right now it is hard coded to be a {#syntax#}u16{#endsyntax#}. See <a href="https://github.com/ziglang/zig/issues/786">#786</a>.
5376+
The error set type defaults to a {#syntax#}u16{#endsyntax#}, though if the maximum number of distinct
5377+
error values is provided via the <kbd>--error-limit [num]</kbd> command line parameter an integer type
5378+
with the minimum number of bits required to represent all of the error values will be used.
53785379
</p>
53795380
<p>
53805381
You can {#link|coerce|Type Coercion#} an error from a subset to a superset:
@@ -8373,7 +8374,7 @@ test "main" {
83738374
{#header_close#}
83748375

83758376
{#header_open|@errorFromInt#}
8376-
<pre>{#syntax#}@errorFromInt(value: std.meta.Int(.unsigned, @sizeOf(anyerror) * 8)) anyerror{#endsyntax#}</pre>
8377+
<pre>{#syntax#}@errorFromInt(value: std.meta.Int(.unsigned, @bitSizeOf(anyerror))) anyerror{#endsyntax#}</pre>
83778378
<p>
83788379
Converts from the integer representation of an error into {#link|The Global Error Set#} type.
83798380
</p>
@@ -8694,7 +8695,7 @@ test "integer cast panic" {
86948695
{#header_close#}
86958696

86968697
{#header_open|@intFromError#}
8697-
<pre>{#syntax#}@intFromError(err: anytype) std.meta.Int(.unsigned, @sizeOf(anyerror) * 8){#endsyntax#}</pre>
8698+
<pre>{#syntax#}@intFromError(err: anytype) std.meta.Int(.unsigned, @bitSizeOf(anyerror)){#endsyntax#}</pre>
86988699
<p>
86998700
Supports the following types:
87008701
</p>

src/AstGen.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -8432,7 +8432,7 @@ fn builtinCall(
84328432
return rvalue(gz, ri, result, node);
84338433
},
84348434
.error_from_int => {
8435-
const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, params[0]);
8435+
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
84368436
const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
84378437
.node = gz.nodeIndexToRelative(node),
84388438
.operand = operand,

src/Compilation.zig

+23
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ pub const InitOptions = struct {
739739
pdb_source_path: ?[]const u8 = null,
740740
/// (Windows) PDB output path
741741
pdb_out_path: ?[]const u8 = null,
742+
error_limit: ?Module.ErrorInt = null,
742743
};
743744

744745
fn addModuleTableToCacheHash(
@@ -1432,6 +1433,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
14321433
.local_zir_cache = local_zir_cache,
14331434
.emit_h = emit_h,
14341435
.tmp_hack_arena = std.heap.ArenaAllocator.init(gpa),
1436+
.error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1),
14351437
};
14361438
try module.init();
14371439

@@ -2486,6 +2488,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24862488
man.hash.add(comp.bin_file.options.skip_linker_dependencies);
24872489
man.hash.add(comp.bin_file.options.parent_compilation_link_libc);
24882490
man.hash.add(mod.emit_h != null);
2491+
man.hash.add(mod.error_limit);
24892492
}
24902493

24912494
try man.addOptionalFile(comp.bin_file.options.linker_script);
@@ -2866,6 +2869,10 @@ pub fn totalErrorCount(self: *Compilation) u32 {
28662869
}
28672870
}
28682871
}
2872+
2873+
if (module.global_error_set.entries.len - 1 > module.error_limit) {
2874+
total += 1;
2875+
}
28692876
}
28702877

28712878
// The "no entry point found" error only counts if there are no semantic analysis errors.
@@ -3016,6 +3023,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
30163023
for (module.failed_exports.values()) |value| {
30173024
try addModuleErrorMsg(module, &bundle, value.*);
30183025
}
3026+
3027+
const actual_error_count = module.global_error_set.entries.len - 1;
3028+
if (actual_error_count > module.error_limit) {
3029+
try bundle.addRootErrorMessage(.{
3030+
.msg = try bundle.printString("module used more errors than possible: used {d}, max {d}", .{
3031+
actual_error_count, module.error_limit,
3032+
}),
3033+
.notes_len = 1,
3034+
});
3035+
const notes_start = try bundle.reserveNotes(1);
3036+
bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{
3037+
.msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
3038+
actual_error_count,
3039+
}),
3040+
}));
3041+
}
30193042
}
30203043

30213044
if (bundle.root_list.items.len == 0) {

src/Module.zig

+12
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ deletion_set: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
137137
/// Key is the error name, index is the error tag value. Index 0 has a length-0 string.
138138
global_error_set: GlobalErrorSet = .{},
139139

140+
/// Maximum amount of distinct error values, set by --error-limit
141+
error_limit: ErrorInt,
142+
140143
/// Incrementing integer used to compare against the corresponding Decl
141144
/// field to determine whether a Decl's status applies to an ongoing update, or a
142145
/// previous analysis.
@@ -5020,6 +5023,11 @@ pub fn getErrorValueFromSlice(
50205023
return getErrorValue(mod, interned_name);
50215024
}
50225025

5026+
pub fn errorSetBits(mod: *Module) u16 {
5027+
if (mod.error_limit == 0) return 0;
5028+
return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
5029+
}
5030+
50235031
pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !Decl.Index {
50245032
const src_decl = mod.declPtr(block.src_decl);
50255033
return mod.createAnonymousDeclFromDecl(src_decl, block.namespace, block.wip_capture_scope, typed_value);
@@ -5898,6 +5906,10 @@ pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allo
58985906
} })).toType();
58995907
}
59005908

5909+
pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type {
5910+
return mod.intType(.unsigned, mod.errorSetBits());
5911+
}
5912+
59015913
pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type {
59025914
const i = try intern(mod, .{ .array_type = info });
59035915
return i.toType();

src/Sema.zig

+12-9
Original file line numberDiff line numberDiff line change
@@ -8404,14 +8404,15 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
84048404
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
84058405
const uncasted_operand = try sema.resolveInst(extra.operand);
84068406
const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src);
8407+
const err_int_ty = try mod.errorIntType();
84078408

84088409
if (try sema.resolveMaybeUndefVal(operand)) |val| {
84098410
if (val.isUndef(mod)) {
8410-
return mod.undefRef(Type.err_int);
8411+
return mod.undefRef(err_int_ty);
84118412
}
84128413
const err_name = ip.indexToKey(val.toIntern()).err.name;
84138414
return Air.internedToRef((try mod.intValue(
8414-
Type.err_int,
8415+
err_int_ty,
84158416
try mod.getErrorValue(err_name),
84168417
)).toIntern());
84178418
}
@@ -8422,18 +8423,18 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
84228423
else => |err_set_ty_index| {
84238424
const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
84248425
switch (names.len) {
8425-
0 => return Air.internedToRef((try mod.intValue(Type.err_int, 0)).toIntern()),
8426+
0 => return Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()),
84268427
1 => {
84278428
const int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[0]).?);
8428-
return mod.intRef(Type.err_int, int);
8429+
return mod.intRef(err_int_ty, int);
84298430
},
84308431
else => {},
84318432
}
84328433
},
84338434
}
84348435

84358436
try sema.requireRuntimeBlock(block, src, operand_src);
8436-
return block.addBitCast(Type.err_int, operand);
8437+
return block.addBitCast(err_int_ty, operand);
84378438
}
84388439

84398440
fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@@ -8445,7 +8446,8 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
84458446
const src = LazySrcLoc.nodeOffset(extra.node);
84468447
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
84478448
const uncasted_operand = try sema.resolveInst(extra.operand);
8448-
const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src);
8449+
const err_int_ty = try mod.errorIntType();
8450+
const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
84498451

84508452
if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
84518453
const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod));
@@ -8459,7 +8461,7 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
84598461
try sema.requireRuntimeBlock(block, src, operand_src);
84608462
if (block.wantSafety()) {
84618463
const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
8462-
const zero_val = Air.internedToRef((try mod.intValue(Type.err_int, 0)).toIntern());
8464+
const zero_val = Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern());
84638465
const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
84648466
const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero);
84658467
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
@@ -21919,10 +21921,11 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
2191921921
}
2192021922

2192121923
try sema.requireRuntimeBlock(block, src, operand_src);
21924+
const err_int_ty = try mod.errorIntType();
2192221925
if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
2192321926
if (dest_tag == .ErrorUnion) {
2192421927
const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
21925-
const err_int = try block.addBitCast(Type.err_int, err_code);
21928+
const err_int = try block.addBitCast(err_int_ty, err_code);
2192621929
const zero_u16 = Air.internedToRef(try mod.intern(.{
2192721930
.int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } },
2192821931
}));
@@ -21938,7 +21941,7 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
2193821941
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
2193921942
}
2194021943
} else {
21941-
const err_int_inst = try block.addBitCast(Type.err_int, operand);
21944+
const err_int_inst = try block.addBitCast(err_int_ty, operand);
2194221945
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
2194321946
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
2194421947
}

src/arch/wasm/CodeGen.zig

+7-4
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
33023302
return WValue{ .imm32 = int };
33033303
},
33043304
.error_union => |error_union| {
3305+
const err_int_ty = try mod.errorIntType();
33053306
const err_tv: TypedValue = switch (error_union.val) {
33063307
.err_name => |err_name| .{
33073308
.ty = ty.errorUnionSet(mod),
@@ -3311,8 +3312,8 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
33113312
} })).toValue(),
33123313
},
33133314
.payload => .{
3314-
.ty = Type.err_int,
3315-
.val = try mod.intValue(Type.err_int, 0),
3315+
.ty = err_int_ty,
3316+
.val = try mod.intValue(err_int_ty, 0),
33163317
},
33173318
};
33183319
const payload_type = ty.errorUnionPayload(mod);
@@ -3711,8 +3712,10 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37113712
const errors_len = WValue{ .memory = sym_index };
37123713

37133714
try func.emitWValue(operand);
3714-
const errors_len_val = try func.load(errors_len, Type.err_int, 0);
3715-
const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt);
3715+
const mod = func.bin_file.base.options.module.?;
3716+
const err_int_ty = try mod.errorIntType();
3717+
const errors_len_val = try func.load(errors_len, err_int_ty, 0);
3718+
const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt);
37163719

37173720
return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op});
37183721
}

src/codegen.zig

+3-2
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,7 @@ pub fn genTypedValue(
10541054
const payload_type = typed_value.ty.errorUnionPayload(mod);
10551055
if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) {
10561056
// We use the error type directly as the type.
1057+
const err_int_ty = try mod.errorIntType();
10571058
switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).error_union.val) {
10581059
.err_name => |err_name| return genTypedValue(bin_file, src_loc, .{
10591060
.ty = err_type,
@@ -1063,8 +1064,8 @@ pub fn genTypedValue(
10631064
} })).toValue(),
10641065
}, owner_decl_index),
10651066
.payload => return genTypedValue(bin_file, src_loc, .{
1066-
.ty = Type.err_int,
1067-
.val = try mod.intValue(Type.err_int, 0),
1067+
.ty = err_int_ty,
1068+
.val = try mod.intValue(err_int_ty, 0),
10681069
}, owner_decl_index),
10691070
}
10701071
}

src/codegen/c.zig

+20-13
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,7 @@ pub const DeclGen = struct {
10381038
.error_union => |error_union| {
10391039
const payload_ty = ty.errorUnionPayload(mod);
10401040
const error_ty = ty.errorUnionSet(mod);
1041+
const err_int_ty = try mod.errorIntType();
10411042
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
10421043
switch (error_union.val) {
10431044
.err_name => |err_name| return dg.renderValue(
@@ -1051,8 +1052,8 @@ pub const DeclGen = struct {
10511052
),
10521053
.payload => return dg.renderValue(
10531054
writer,
1054-
Type.err_int,
1055-
try mod.intValue(Type.err_int, 0),
1055+
err_int_ty,
1056+
try mod.intValue(err_int_ty, 0),
10561057
location,
10571058
),
10581059
}
@@ -1087,8 +1088,8 @@ pub const DeclGen = struct {
10871088
),
10881089
.payload => try dg.renderValue(
10891090
writer,
1090-
Type.err_int,
1091-
try mod.intValue(Type.err_int, 0),
1091+
err_int_ty,
1092+
try mod.intValue(err_int_ty, 0),
10921093
location,
10931094
),
10941095
}
@@ -1244,7 +1245,7 @@ pub const DeclGen = struct {
12441245
payload_ty,
12451246
switch (opt.val) {
12461247
.none => switch (payload_ty.zigTypeTag(mod)) {
1247-
.ErrorSet => try mod.intValue(Type.err_int, 0),
1248+
.ErrorSet => try mod.intValue(try mod.errorIntType(), 0),
12481249
.Pointer => try mod.getCoerced(val, payload_ty),
12491250
else => unreachable,
12501251
},
@@ -5196,14 +5197,15 @@ fn airIsNull(
51965197
const operand_ty = f.typeOf(un_op);
51975198
const optional_ty = if (is_ptr) operand_ty.childType(mod) else operand_ty;
51985199
const payload_ty = optional_ty.optionalChild(mod);
5200+
const err_int_ty = try mod.errorIntType();
51995201

52005202
const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod))
52015203
TypedValue{ .ty = Type.bool, .val = Value.true }
52025204
else if (optional_ty.isPtrLikeOptional(mod))
52035205
// operand is a regular pointer, test `operand !=/== NULL`
52045206
TypedValue{ .ty = optional_ty, .val = try mod.getCoerced(Value.null, optional_ty) }
52055207
else if (payload_ty.zigTypeTag(mod) == .ErrorSet)
5206-
TypedValue{ .ty = Type.err_int, .val = try mod.intValue(Type.err_int, 0) }
5208+
TypedValue{ .ty = err_int_ty, .val = try mod.intValue(err_int_ty, 0) }
52075209
else if (payload_ty.isSlice(mod) and optional_ty.optionalReprIsPayload(mod)) rhs: {
52085210
try writer.writeAll(".ptr");
52095211
const slice_ptr_ty = payload_ty.slicePtrFieldType(mod);
@@ -5689,8 +5691,10 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
56895691
try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" })
56905692
else
56915693
try f.writeCValueMember(writer, operand, .{ .identifier = "error" })
5692-
else
5693-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Initializer);
5694+
else {
5695+
const err_int_ty = try mod.errorIntType();
5696+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Initializer);
5697+
}
56945698
}
56955699
try writer.writeAll(";\n");
56965700
return local;
@@ -5811,20 +5815,21 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
58115815
const error_union_ty = f.typeOf(ty_op.operand).childType(mod);
58125816

58135817
const payload_ty = error_union_ty.errorUnionPayload(mod);
5818+
const err_int_ty = try mod.errorIntType();
58145819

58155820
// First, set the non-error value.
58165821
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
58175822
try f.writeCValueDeref(writer, operand);
58185823
try writer.writeAll(" = ");
5819-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
5824+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other);
58205825
try writer.writeAll(";\n ");
58215826

58225827
return operand;
58235828
}
58245829
try reap(f, inst, &.{ty_op.operand});
58255830
try f.writeCValueDeref(writer, operand);
58265831
try writer.writeAll(".error = ");
5827-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
5832+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other);
58285833
try writer.writeAll(";\n");
58295834

58305835
// Then return the payload pointer (only if it is used)
@@ -5880,7 +5885,8 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
58805885
else
58815886
try f.writeCValueMember(writer, local, .{ .identifier = "error" });
58825887
try a.assign(f, writer);
5883-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
5888+
const err_int_ty = try mod.errorIntType();
5889+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other);
58845890
try a.end(f, writer);
58855891
}
58865892
return local;
@@ -5902,6 +5908,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
59025908
try f.writeCValue(writer, local, .Other);
59035909
try writer.writeAll(" = ");
59045910

5911+
const err_int_ty = try mod.errorIntType();
59055912
if (!error_ty.errorSetIsEmpty(mod))
59065913
if (payload_ty.hasRuntimeBits(mod))
59075914
if (is_ptr)
@@ -5911,11 +5918,11 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
59115918
else
59125919
try f.writeCValue(writer, operand, .Other)
59135920
else
5914-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
5921+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other);
59155922
try writer.writeByte(' ');
59165923
try writer.writeAll(operator);
59175924
try writer.writeByte(' ');
5918-
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
5925+
try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other);
59195926
try writer.writeAll(";\n");
59205927
return local;
59215928
}

0 commit comments

Comments
 (0)