From bf61c5c0656e3b2198fb009fe5cc59f55263ceae Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 24 Jan 2023 13:35:10 +0200 Subject: [PATCH 1/3] make distinct error limit configurable Closes #786 --- src/Compilation.zig | 23 +++++++++++++++++++++++ src/Module.zig | 8 ++++++++ src/main.zig | 9 +++++++++ src/type.zig | 46 +++++++++++++++++++++++++-------------------- src/value.zig | 23 +++++++++++++++-------- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 800c8288d0..e15ade5d5b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -739,6 +739,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( @@ -1432,6 +1433,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(); @@ -2486,6 +2488,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); @@ -2866,6 +2869,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. @@ -3016,6 +3023,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) { diff --git a/src/Module.zig b/src/Module.zig index 41f4ec2b41..36d9a4284c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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. @@ -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); diff --git a/src/main.zig b/src/main.zig index 07e7d088ea..b435988f28 100644 --- a/src/main.zig +++ b/src/main.zig @@ -421,6 +421,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 @@ -911,6 +912,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. @@ -1040,6 +1043,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) { @@ -3546,6 +3554,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; diff --git a/src/type.zig b/src/type.zig index 5bdca27667..49582bd3c8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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 .{ @@ -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, @@ -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(); @@ -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 @@ -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 @@ -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, @@ -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() }, @@ -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, diff --git a/src/value.zig b/src/value.zig index 2c4a88da17..15289de6b2 100644 --- a/src/value.zig +++ b/src/value.zig @@ -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 @@ -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, From 9d9e22e716a4b6de9a26f2553ea5c980efc79d4a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 21 Oct 2023 12:56:05 +0300 Subject: [PATCH 2/3] remove uses of non-configurable `err_int` --- doc/langref.html.in | 4 +- src/AstGen.zig | 2 +- src/Module.zig | 4 ++ src/Sema.zig | 21 ++++--- src/arch/wasm/CodeGen.zig | 11 ++-- src/codegen.zig | 5 +- src/codegen/c.zig | 33 ++++++----- src/codegen/llvm.zig | 103 ++++++++++++++++++++--------------- src/codegen/llvm/Builder.zig | 1 - src/codegen/spirv.zig | 5 +- src/type.zig | 2 - 11 files changed, 111 insertions(+), 80 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 5f29524bc0..824547b326 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8373,7 +8373,7 @@ test "main" { {#header_close#} {#header_open|@errorFromInt#} -
{#syntax#}@errorFromInt(value: std.meta.Int(.unsigned, @sizeOf(anyerror) * 8)) anyerror{#endsyntax#}
+
{#syntax#}@errorFromInt(value: std.meta.Int(.unsigned, @bitSizeOf(anyerror))) anyerror{#endsyntax#}

Converts from the integer representation of an error into {#link|The Global Error Set#} type.

@@ -8694,7 +8694,7 @@ test "integer cast panic" { {#header_close#} {#header_open|@intFromError#} -
{#syntax#}@intFromError(err: anytype) std.meta.Int(.unsigned, @sizeOf(anyerror) * 8){#endsyntax#}
+
{#syntax#}@intFromError(err: anytype) std.meta.Int(.unsigned, @bitSizeOf(anyerror)){#endsyntax#}

Supports the following types:

diff --git a/src/AstGen.zig b/src/AstGen.zig index de512e5bdb..f5c1964482 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8430,7 +8430,7 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .error_from_int => { - const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, params[0]); + const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = operand, diff --git a/src/Module.zig b/src/Module.zig index 36d9a4284c..c6a28caab4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5906,6 +5906,10 @@ pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allo } })).toType(); } +pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type { + return mod.intType(.unsigned, mod.errorSetBits()); +} + pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type { const i = try intern(mod, .{ .array_type = info }); return i.toType(); diff --git a/src/Sema.zig b/src/Sema.zig index 2f861a87e6..82b41a789e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8384,14 +8384,15 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const uncasted_operand = try sema.resolveInst(extra.operand); const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src); + const err_int_ty = try mod.errorIntType(); if (try sema.resolveMaybeUndefVal(operand)) |val| { if (val.isUndef(mod)) { - return mod.undefRef(Type.err_int); + return mod.undefRef(err_int_ty); } const err_name = ip.indexToKey(val.toIntern()).err.name; return Air.internedToRef((try mod.intValue( - Type.err_int, + err_int_ty, try mod.getErrorValue(err_name), )).toIntern()); } @@ -8402,10 +8403,10 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD else => |err_set_ty_index| { const names = ip.indexToKey(err_set_ty_index).error_set_type.names; switch (names.len) { - 0 => return Air.internedToRef((try mod.intValue(Type.err_int, 0)).toIntern()), + 0 => return Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()), 1 => { const int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[0]).?); - return mod.intRef(Type.err_int, int); + return mod.intRef(err_int_ty, int); }, else => {}, } @@ -8413,7 +8414,7 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD } try sema.requireRuntimeBlock(block, src, operand_src); - return block.addBitCast(Type.err_int, operand); + return block.addBitCast(err_int_ty, operand); } fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -8425,7 +8426,8 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const uncasted_operand = try sema.resolveInst(extra.operand); - const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src); + const err_int_ty = try mod.errorIntType(); + const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod)); @@ -8439,7 +8441,7 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); - const zero_val = Air.internedToRef((try mod.intValue(Type.err_int, 0)).toIntern()); + const zero_val = Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()); const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero); try sema.addSafetyCheck(block, src, ok, .invalid_error_code); @@ -21899,10 +21901,11 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData } try sema.requireRuntimeBlock(block, src, operand_src); + const err_int_ty = try mod.errorIntType(); if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { if (dest_tag == .ErrorUnion) { const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand); - const err_int = try block.addBitCast(Type.err_int, err_code); + const err_int = try block.addBitCast(err_int_ty, err_code); const zero_u16 = Air.internedToRef(try mod.intern(.{ .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } }, })); @@ -21918,7 +21921,7 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData try sema.addSafetyCheck(block, src, ok, .invalid_error_code); } } else { - const err_int_inst = try block.addBitCast(Type.err_int, operand); + const err_int_inst = try block.addBitCast(err_int_ty, operand); const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); try sema.addSafetyCheck(block, src, ok, .invalid_error_code); } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3eac273164..9eaf1ba6c7 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3296,6 +3296,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { return WValue{ .imm32 = int }; }, .error_union => |error_union| { + const err_int_ty = try mod.errorIntType(); const err_tv: TypedValue = switch (error_union.val) { .err_name => |err_name| .{ .ty = ty.errorUnionSet(mod), @@ -3305,8 +3306,8 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { } })).toValue(), }, .payload => .{ - .ty = Type.err_int, - .val = try mod.intValue(Type.err_int, 0), + .ty = err_int_ty, + .val = try mod.intValue(err_int_ty, 0), }, }; const payload_type = ty.errorUnionPayload(mod); @@ -3705,8 +3706,10 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const errors_len = WValue{ .memory = sym_index }; try func.emitWValue(operand); - const errors_len_val = try func.load(errors_len, Type.err_int, 0); - const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt); + const mod = func.bin_file.base.options.module.?; + const err_int_ty = try mod.errorIntType(); + const errors_len_val = try func.load(errors_len, err_int_ty, 0); + const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt); return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op}); } diff --git a/src/codegen.zig b/src/codegen.zig index 24269f38ba..27de8cd5bb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1052,6 +1052,7 @@ pub fn genTypedValue( const payload_type = typed_value.ty.errorUnionPayload(mod); if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) { // We use the error type directly as the type. + const err_int_ty = try mod.errorIntType(); switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).error_union.val) { .err_name => |err_name| return genTypedValue(bin_file, src_loc, .{ .ty = err_type, @@ -1061,8 +1062,8 @@ pub fn genTypedValue( } })).toValue(), }, owner_decl_index), .payload => return genTypedValue(bin_file, src_loc, .{ - .ty = Type.err_int, - .val = try mod.intValue(Type.err_int, 0), + .ty = err_int_ty, + .val = try mod.intValue(err_int_ty, 0), }, owner_decl_index), } } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 94598985c0..49ccf3cbc1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1021,6 +1021,7 @@ pub const DeclGen = struct { .error_union => |error_union| { const payload_ty = ty.errorUnionPayload(mod); const error_ty = ty.errorUnionSet(mod); + const err_int_ty = try mod.errorIntType(); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { switch (error_union.val) { .err_name => |err_name| return dg.renderValue( @@ -1034,8 +1035,8 @@ pub const DeclGen = struct { ), .payload => return dg.renderValue( writer, - Type.err_int, - try mod.intValue(Type.err_int, 0), + err_int_ty, + try mod.intValue(err_int_ty, 0), location, ), } @@ -1070,8 +1071,8 @@ pub const DeclGen = struct { ), .payload => try dg.renderValue( writer, - Type.err_int, - try mod.intValue(Type.err_int, 0), + err_int_ty, + try mod.intValue(err_int_ty, 0), location, ), } @@ -1227,7 +1228,7 @@ pub const DeclGen = struct { payload_ty, switch (opt.val) { .none => switch (payload_ty.zigTypeTag(mod)) { - .ErrorSet => try mod.intValue(Type.err_int, 0), + .ErrorSet => try mod.intValue(try mod.errorIntType(), 0), .Pointer => try mod.getCoerced(val, payload_ty), else => unreachable, }, @@ -5179,6 +5180,7 @@ fn airIsNull( const operand_ty = f.typeOf(un_op); const optional_ty = if (is_ptr) operand_ty.childType(mod) else operand_ty; const payload_ty = optional_ty.optionalChild(mod); + const err_int_ty = try mod.errorIntType(); const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) TypedValue{ .ty = Type.bool, .val = Value.true } @@ -5186,7 +5188,7 @@ fn airIsNull( // operand is a regular pointer, test `operand !=/== NULL` TypedValue{ .ty = optional_ty, .val = try mod.getCoerced(Value.null, optional_ty) } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) - TypedValue{ .ty = Type.err_int, .val = try mod.intValue(Type.err_int, 0) } + TypedValue{ .ty = err_int_ty, .val = try mod.intValue(err_int_ty, 0) } else if (payload_ty.isSlice(mod) and optional_ty.optionalReprIsPayload(mod)) rhs: { try writer.writeAll(".ptr"); const slice_ptr_ty = payload_ty.slicePtrFieldType(mod); @@ -5672,8 +5674,10 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) else try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) - else - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Initializer); + else { + const err_int_ty = try mod.errorIntType(); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Initializer); + } } try writer.writeAll(";\n"); return local; @@ -5794,12 +5798,13 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const error_union_ty = f.typeOf(ty_op.operand).childType(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); + const err_int_ty = try mod.errorIntType(); // First, set the non-error value. if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { try f.writeCValueDeref(writer, operand); try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other); try writer.writeAll(";\n "); return operand; @@ -5807,7 +5812,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); try f.writeCValueDeref(writer, operand); try writer.writeAll(".error = "); - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other); try writer.writeAll(";\n"); // Then return the payload pointer (only if it is used) @@ -5863,7 +5868,8 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { else try f.writeCValueMember(writer, local, .{ .identifier = "error" }); try a.assign(f, writer); - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other); + const err_int_ty = try mod.errorIntType(); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other); try a.end(f, writer); } return local; @@ -5885,6 +5891,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); + const err_int_ty = try mod.errorIntType(); if (!error_ty.errorSetIsEmpty(mod)) if (payload_ty.hasRuntimeBits(mod)) if (is_ptr) @@ -5894,11 +5901,11 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const else try f.writeCValue(writer, operand, .Other) else - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other); try writer.writeByte(' '); try writer.writeAll(operator); try writer.writeByte(' '); - try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other); + try f.object.dg.renderValue(writer, err_int_ty, try mod.intValue(err_int_ty, 0), .Other); try writer.writeAll(";\n"); return local; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cddd4340d7..ec7fa249cf 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1111,7 +1111,7 @@ pub const Object = struct { // } const lhs = wip.arg(0); - const rhs = try o.builder.intValue(Builder.Type.err_int, errors_len); + const rhs = try o.builder.intValue(try o.errorIntType(), errors_len); const is_lt = try wip.icmp(.ult, lhs, rhs, ""); _ = try wip.ret(is_lt); try wip.finish(); @@ -3111,6 +3111,10 @@ pub const Object = struct { return variable_index; } + fn errorIntType(o: *Object) Allocator.Error!Builder.Type { + return o.builder.intType(o.module.errorSetBits()); + } + fn lowerType(o: *Object, t: Type) Allocator.Error!Builder.Type { const ty = try o.lowerTypeInner(t); const mod = o.module; @@ -3182,7 +3186,7 @@ pub const Object = struct { .bool_type => .i1, .void_type => .void, .type_type => unreachable, - .anyerror_type => Builder.Type.err_int, + .anyerror_type => try o.errorIntType(), .comptime_int_type, .comptime_float_type, .noreturn_type, @@ -3203,7 +3207,7 @@ pub const Object = struct { .optional_noreturn_type => unreachable, .anyerror_void_error_union_type, .adhoc_inferred_error_set_type, - => Builder.Type.err_int, + => try o.errorIntType(), .generic_poison_type, .empty_struct_type, => unreachable, @@ -3272,16 +3276,17 @@ pub const Object = struct { }, .anyframe_type => @panic("TODO implement lowerType for AnyFrame types"), .error_union_type => |error_union_type| { - const error_type = Builder.Type.err_int; + const error_type = try o.errorIntType(); if (!error_union_type.payload_type.toType().hasRuntimeBitsIgnoreComptime(mod)) return error_type; const payload_type = try o.lowerType(error_union_type.payload_type.toType()); + const err_int_ty = try mod.errorIntType(); const payload_align = error_union_type.payload_type.toType().abiAlignment(mod); - const error_align = Type.err_int.abiAlignment(mod); + const error_align = err_int_ty.abiAlignment(mod); const payload_size = error_union_type.payload_type.toType().abiSize(mod); - const error_size = Type.err_int.abiSize(mod); + const error_size = err_int_ty.abiSize(mod); var fields: [3]Builder.Type = undefined; var fields_len: usize = 2; @@ -3542,7 +3547,7 @@ pub const Object = struct { }, .enum_type => |enum_type| try o.lowerType(enum_type.tag_ty.toType()), .func_type => |func_type| try o.lowerTypeFn(func_type), - .error_set_type, .inferred_error_set_type => Builder.Type.err_int, + .error_set_type, .inferred_error_set_type => try o.errorIntType(), // values, not types .undef, .runtime_value, @@ -3725,7 +3730,7 @@ pub const Object = struct { }, .err => |err| { const int = try mod.getErrorValue(err.name); - const llvm_int = try o.builder.intConst(Builder.Type.err_int, int); + const llvm_int = try o.builder.intConst(try o.errorIntType(), int); return llvm_int; }, .error_union => |error_union| { @@ -3734,8 +3739,9 @@ pub const Object = struct { .ty = ty.errorUnionSet(mod).toIntern(), .name = err_name, } }), - .payload => (try mod.intValue(Type.err_int, 0)).toIntern(), + .payload => (try mod.intValue(try mod.errorIntType(), 0)).toIntern(), }; + const err_int_ty = try mod.errorIntType(); const payload_type = ty.errorUnionPayload(mod); if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) { // We use the error type directly as the type. @@ -3743,7 +3749,7 @@ pub const Object = struct { } const payload_align = payload_type.abiAlignment(mod); - const error_align = Type.err_int.abiAlignment(mod); + const error_align = err_int_ty.abiAlignment(mod); const llvm_error_value = try o.lowerValue(err_val); const llvm_payload_value = try o.lowerValue(switch (error_union.val) { .err_name => try mod.intern(.{ .undef = payload_type.toIntern() }), @@ -4285,8 +4291,9 @@ pub const Object = struct { return parent_ptr; } + const err_int_ty = try mod.errorIntType(); const payload_align = payload_ty.abiAlignment(mod); - const err_align = Type.err_int.abiAlignment(mod); + const err_align = err_int_ty.abiAlignment(mod); const index: u32 = if (payload_align.compare(.gt, err_align)) 2 else 1; return o.builder.gepConst(.inbounds, try o.lowerType(eu_ty), parent_ptr, null, &.{ try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, index), @@ -5399,7 +5406,7 @@ pub const FuncGen = struct { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced // to functions that return anyerror. - _ = try self.wip.ret(try o.builder.intValue(Builder.Type.err_int, 0)); + _ = try self.wip.ret(try o.builder.intValue(try o.errorIntType(), 0)); } else { _ = try self.wip.retVoid(); } @@ -5441,7 +5448,7 @@ pub const FuncGen = struct { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced // to functions that return anyerror. - _ = try self.wip.ret(try o.builder.intValue(Builder.Type.err_int, 0)); + _ = try self.wip.ret(try o.builder.intValue(try o.errorIntType(), 0)); } else { _ = try self.wip.retVoid(); } @@ -5788,24 +5795,25 @@ pub const FuncGen = struct { const payload_ty = err_union_ty.errorUnionPayload(mod); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(mod); const err_union_llvm_ty = try o.lowerType(err_union_ty); + const error_type = try o.errorIntType(); if (!err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { const loaded = loaded: { if (!payload_has_bits) { // TODO add alignment to this load break :loaded if (operand_is_ptr) - try fg.wip.load(.normal, Builder.Type.err_int, err_union, .default, "") + try fg.wip.load(.normal, error_type, err_union, .default, "") else err_union; } - const err_field_index = errUnionErrorOffset(payload_ty, mod); + const err_field_index = try errUnionErrorOffset(payload_ty, mod); if (operand_is_ptr or isByRef(err_union_ty, mod)) { const err_field_ptr = try fg.wip.gepStruct(err_union_llvm_ty, err_union, err_field_index, ""); // TODO add alignment to this load break :loaded try fg.wip.load( .normal, - Builder.Type.err_int, + error_type, err_field_ptr, .default, "", @@ -5813,7 +5821,7 @@ pub const FuncGen = struct { } break :loaded try fg.wip.extractValue(err_union, &.{err_field_index}, ""); }; - const zero = try o.builder.intValue(Builder.Type.err_int, 0); + const zero = try o.builder.intValue(error_type, 0); const is_err = try fg.wip.icmp(.ne, loaded, zero, ""); const return_block = try fg.wip.block(1, "TryRet"); @@ -5827,7 +5835,7 @@ pub const FuncGen = struct { } if (is_unused) return .none; if (!payload_has_bits) return if (operand_is_ptr) err_union else .none; - const offset = errUnionPayloadOffset(payload_ty, mod); + const offset = try errUnionPayloadOffset(payload_ty, mod); if (operand_is_ptr) { return fg.wip.gepStruct(err_union_llvm_ty, err_union, offset, ""); } else if (isByRef(err_union_ty, mod)) { @@ -7053,7 +7061,8 @@ pub const FuncGen = struct { const operand_ty = self.typeOf(un_op); const err_union_ty = if (operand_is_ptr) operand_ty.childType(mod) else operand_ty; const payload_ty = err_union_ty.errorUnionPayload(mod); - const zero = try o.builder.intValue(Builder.Type.err_int, 0); + const error_type = try o.errorIntType(); + const zero = try o.builder.intValue(error_type, 0); if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { const val: Builder.Constant = switch (cond) { @@ -7072,13 +7081,13 @@ pub const FuncGen = struct { return self.wip.icmp(cond, loaded, zero, ""); } - const err_field_index = errUnionErrorOffset(payload_ty, mod); + const err_field_index = try errUnionErrorOffset(payload_ty, mod); const loaded = if (operand_is_ptr or isByRef(err_union_ty, mod)) loaded: { const err_union_llvm_ty = try o.lowerType(err_union_ty); const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, err_field_index, ""); - break :loaded try self.wip.load(.normal, Builder.Type.err_int, err_field_ptr, .default, ""); + break :loaded try self.wip.load(.normal, error_type, err_field_ptr, .default, ""); } else try self.wip.extractValue(operand, &.{err_field_index}, ""); return self.wip.icmp(cond, loaded, zero, ""); } @@ -7173,7 +7182,7 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { return if (operand_is_ptr) operand else .none; } - const offset = errUnionPayloadOffset(payload_ty, mod); + const offset = try errUnionPayloadOffset(payload_ty, mod); const err_union_llvm_ty = try o.lowerType(err_union_ty); if (operand_is_ptr) { return self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); @@ -7200,27 +7209,28 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); + const error_type = try o.errorIntType(); const err_union_ty = if (operand_is_ptr) operand_ty.childType(mod) else operand_ty; if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { if (operand_is_ptr) { return operand; } else { - return o.builder.intValue(Builder.Type.err_int, 0); + return o.builder.intValue(error_type, 0); } } const payload_ty = err_union_ty.errorUnionPayload(mod); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { if (!operand_is_ptr) return operand; - return self.wip.load(.normal, Builder.Type.err_int, operand, .default, ""); + return self.wip.load(.normal, error_type, operand, .default, ""); } - const offset = errUnionErrorOffset(payload_ty, mod); + const offset = try errUnionErrorOffset(payload_ty, mod); if (operand_is_ptr or isByRef(err_union_ty, mod)) { const err_union_llvm_ty = try o.lowerType(err_union_ty); const err_field_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, offset, ""); - return self.wip.load(.normal, Builder.Type.err_int, err_field_ptr, .default, ""); + return self.wip.load(.normal, error_type, err_field_ptr, .default, ""); } return self.wip.extractValue(operand, &.{offset}, ""); @@ -7234,15 +7244,16 @@ pub const FuncGen = struct { const err_union_ty = self.typeOf(ty_op.operand).childType(mod); const payload_ty = err_union_ty.errorUnionPayload(mod); - const non_error_val = try o.builder.intValue(Builder.Type.err_int, 0); + const non_error_val = try o.builder.intValue(try o.errorIntType(), 0); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { _ = try self.wip.store(.normal, non_error_val, operand, .default); return operand; } const err_union_llvm_ty = try o.lowerType(err_union_ty); { - const error_alignment = Type.err_int.abiAlignment(mod).toLlvm(); - const error_offset = errUnionErrorOffset(payload_ty, mod); + const err_int_ty = try mod.errorIntType(); + const error_alignment = err_int_ty.abiAlignment(mod).toLlvm(); + const error_offset = try errUnionErrorOffset(payload_ty, mod); // First set the non-error value. const non_null_ptr = try self.wip.gepStruct(err_union_llvm_ty, operand, error_offset, ""); _ = try self.wip.store(.normal, non_error_val, non_null_ptr, error_alignment); @@ -7250,7 +7261,7 @@ pub const FuncGen = struct { // Then return the payload pointer (only if it is used). if (self.liveness.isUnused(inst)) return .none; - const payload_offset = errUnionPayloadOffset(payload_ty, mod); + const payload_offset = try errUnionPayloadOffset(payload_ty, mod); return self.wip.gepStruct(err_union_llvm_ty, operand, payload_offset, ""); } @@ -7353,11 +7364,11 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { return operand; } - const ok_err_code = try o.builder.intValue(Builder.Type.err_int, 0); + const ok_err_code = try o.builder.intValue(try o.errorIntType(), 0); const err_un_llvm_ty = try o.lowerType(err_un_ty); - const payload_offset = errUnionPayloadOffset(payload_ty, mod); - const error_offset = errUnionErrorOffset(payload_ty, mod); + const payload_offset = try errUnionPayloadOffset(payload_ty, mod); + const error_offset = try errUnionErrorOffset(payload_ty, mod); if (isByRef(err_un_ty, mod)) { const directReturn = self.isNextRet(body_tail); const result_ptr = if (directReturn) @@ -7369,7 +7380,8 @@ pub const FuncGen = struct { }; const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); - const error_alignment = Type.err_int.abiAlignment(mod).toLlvm(); + const err_int_ty = try mod.errorIntType(); + const error_alignment = err_int_ty.abiAlignment(mod).toLlvm(); _ = try self.wip.store(.normal, ok_err_code, err_ptr, error_alignment); const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); const payload_ptr_ty = try mod.singleMutPtrType(payload_ty); @@ -7393,8 +7405,8 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return operand; const err_un_llvm_ty = try o.lowerType(err_un_ty); - const payload_offset = errUnionPayloadOffset(payload_ty, mod); - const error_offset = errUnionErrorOffset(payload_ty, mod); + const payload_offset = try errUnionPayloadOffset(payload_ty, mod); + const error_offset = try errUnionErrorOffset(payload_ty, mod); if (isByRef(err_un_ty, mod)) { const directReturn = self.isNextRet(body_tail); const result_ptr = if (directReturn) @@ -7406,7 +7418,8 @@ pub const FuncGen = struct { }; const err_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, error_offset, ""); - const error_alignment = Type.err_int.abiAlignment(mod).toLlvm(); + const err_int_ty = try mod.errorIntType(); + const error_alignment = err_int_ty.abiAlignment(mod).toLlvm(); _ = try self.wip.store(.normal, operand, err_ptr, error_alignment); const payload_ptr = try self.wip.gepStruct(err_un_llvm_ty, result_ptr, payload_offset, ""); const payload_ptr_ty = try mod.singleMutPtrType(payload_ty); @@ -9363,7 +9376,7 @@ pub const FuncGen = struct { for (names) |name| { const err_int = mod.global_error_set.getIndex(name).?; - const this_tag_int_value = try o.builder.intConst(Builder.Type.err_int, err_int); + const this_tag_int_value = try o.builder.intConst(try o.errorIntType(), err_int); try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); } self.wip.cursor = .{ .block = valid_block }; @@ -9545,7 +9558,7 @@ pub const FuncGen = struct { if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; const function_index = try o.builder.addFunction( - try o.builder.fnType(.i1, &.{Builder.Type.err_int}, .normal), + try o.builder.fnType(.i1, &.{try o.errorIntType()}, .normal), name, toLlvmAddressSpace(.generic, o.module.getTarget()), ); @@ -10880,7 +10893,7 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu // If the return type is an error set or an error union, then we make this // anyerror return type instead, so that it can be coerced into a function // pointer type which has anyerror as the return type. - return if (return_type.isError(mod)) Builder.Type.err_int else .void; + return if (return_type.isError(mod)) try o.errorIntType() else .void; } const target = mod.getTarget(); switch (fn_info.cc) { @@ -11633,12 +11646,14 @@ fn buildAllocaInner( return wip.conv(.unneeded, alloca, .ptr, ""); } -fn errUnionPayloadOffset(payload_ty: Type, mod: *Module) u1 { - return @intFromBool(Type.err_int.abiAlignment(mod).compare(.gt, payload_ty.abiAlignment(mod))); +fn errUnionPayloadOffset(payload_ty: Type, mod: *Module) !u1 { + const err_int_ty = try mod.errorIntType(); + return @intFromBool(err_int_ty.abiAlignment(mod).compare(.gt, payload_ty.abiAlignment(mod))); } -fn errUnionErrorOffset(payload_ty: Type, mod: *Module) u1 { - return @intFromBool(Type.err_int.abiAlignment(mod).compare(.lte, payload_ty.abiAlignment(mod))); +fn errUnionErrorOffset(payload_ty: Type, mod: *Module) !u1 { + const err_int_ty = try mod.errorIntType(); + return @intFromBool(err_int_ty.abiAlignment(mod).compare(.lte, payload_ty.abiAlignment(mod))); } /// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a memory location diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 5a9703af3d..2b8669b4b0 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -159,7 +159,6 @@ pub const Type = enum(u32) { none = std.math.maxInt(u32), _, - pub const err_int = Type.i16; pub const ptr_amdgpu_constant = @field(Type, std.fmt.comptimePrint("ptr{ }", .{AddrSpace.amdgpu.constant})); diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index ded73d6afd..87c3a024f3 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -742,16 +742,17 @@ const DeclGen = struct { .error_union => |error_union| { // TODO: Error unions may be constructed with constant instructions if the payload type // allows it. For now, just generate it here regardless. + const err_int_ty = try mod.errorIntType(); const err_ty = switch (error_union.val) { .err_name => ty.errorUnionSet(mod), - .payload => Type.err_int, + .payload => err_int_ty, }; const err_val = switch (error_union.val) { .err_name => |err_name| (try mod.intern(.{ .err = .{ .ty = ty.errorUnionSet(mod).toIntern(), .name = err_name, } })).toValue(), - .payload => try mod.intValue(Type.err_int, 0), + .payload => try mod.intValue(err_int_ty, 0), }; const payload_ty = ty.errorUnionPayload(mod); const eu_layout = self.errorUnionLayout(payload_ty); diff --git a/src/type.zig b/src/type.zig index 49582bd3c8..868c555143 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3309,8 +3309,6 @@ pub const Type = struct { pub const generic_poison: Type = .{ .ip_index = .generic_poison_type }; - pub const err_int = Type.u16; - pub fn smallestUnsignedBits(max: u64) u16 { if (max == 0) return 0; const base = std.math.log2(max); From ed82e4f7ac057286444135dda79fb7c6a579573a Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Thu, 19 Oct 2023 14:32:44 -0700 Subject: [PATCH 3/3] langref: update to document --error-limit flag --- doc/langref.html.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 824547b326..6ee5d77ecb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5373,8 +5373,9 @@ test "fn reflection" { gets assigned the same integer value.

- The number of unique error values across the entire compilation should determine the size of the error set type. - However right now it is hard coded to be a {#syntax#}u16{#endsyntax#}. See #786. + The error set type defaults to a {#syntax#}u16{#endsyntax#}, though if the maximum number of distinct + error values is provided via the --error-limit [num] command line parameter an integer type + with the minimum number of bits required to represent all of the error values will be used.

You can {#link|coerce|Type Coercion#} an error from a subset to a superset: