authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-01-07 17:16:36+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 11:37:17+00:00
log9e949f95c15a7552f84209e01a78b5e0d12225db
tree0f26b6777dc3fd3b6740bbfa24931893855f20be
parent5b00e24b6e28c24d7fa2417e9b7b88e13e75741e
signaturelock-open Commit is signed but in an unrecognized format.

Sema: enhance comptime `is_non_err` resolution

This makes `is_non_err` and `unwrap_errunion_err[_ptr]` friendlier towards being emitted into comptime blocks. Also, `analyzeIsNonErr` now actually attempts to do something at comptime.

1 files changed, 47 insertions(+), 51 deletions(-)

src/Sema.zig+47-51
......@@ -1927,9 +1927,8 @@ fn analyzeBodyInner(
19271927 break :msg msg;
19281928 });
19291929 }
1930 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1931 assert(is_non_err != .none);
1932 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1930 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1931 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
19331932 if (is_non_err_val.toBool()) {
19341933 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
19351934 }
......@@ -1945,9 +1944,8 @@ fn analyzeBodyInner(
19451944 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
19461945 const operand = try sema.resolveInst(extra.data.operand);
19471946 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1948 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1949 assert(is_non_err != .none);
1950 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1947 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1948 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
19511949 if (is_non_err_val.toBool()) {
19521950 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
19531951 }
......@@ -8960,6 +8958,7 @@ fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air
89608958 const result_ty = operand_ty.errorUnionSet(zcu);
89618959
89628960 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
8961 if (val.getErrorName(zcu) == .none) return .unreachable_value;
89638962 return Air.internedToRef((try pt.intern(.{ .err = .{
89648963 .ty = result_ty.toIntern(),
89658964 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
......@@ -8997,7 +8996,7 @@ fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand:
89978996
89988997 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
89998998 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
9000 assert(val.getErrorName(zcu) != .none);
8999 if (val.getErrorName(zcu) == .none) return .unreachable_value;
90019000 return Air.internedToRef((try pt.intern(.{ .err = .{
90029001 .ty = result_ty.toIntern(),
90039002 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
......@@ -18689,14 +18688,13 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1868918688 break :msg msg;
1869018689 });
1869118690 }
18692 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
18693 if (is_non_err != .none) {
18691 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
1869418692 // We can propagate `.cold` hints from this branch since it's comptime-known
1869518693 // to be taken from the parent branch.
1869618694 const parent_hint = sema.branch_hint;
1869718695 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1869818696
18699 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
18697 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
1870018698 if (is_non_err_val.toBool()) {
1870118699 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
1870218700 }
......@@ -18753,14 +18751,13 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
1875318751 break :msg msg;
1875418752 });
1875518753 }
18756 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
18757 if (is_non_err != .none) {
18754 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
1875818755 // We can propagate `.cold` hints from this branch since it's comptime-known
1875918756 // to be taken from the parent branch.
1876018757 const parent_hint = sema.branch_hint;
1876118758 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1876218759
18763 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
18760 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
1876418761 if (is_non_err_val.toBool()) {
1876518762 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
1876618763 }
......@@ -31800,12 +31797,12 @@ fn analyzeIsNull(
3180031797 return block.addUnOp(air_tag, operand);
3180131798}
3180231799
31803fn analyzePtrIsNonErrComptimeOnly(
31800fn resolvePtrIsNonErrVal(
3180431801 sema: *Sema,
3180531802 block: *Block,
3180631803 src: LazySrcLoc,
3180731804 operand: Air.Inst.Ref,
31808) CompileError!Air.Inst.Ref {
31805) CompileError!?Value {
3180931806 const pt = sema.pt;
3181031807 const zcu = pt.zcu;
3181131808 const ptr_ty = sema.typeOf(operand);
......@@ -31813,41 +31810,44 @@ fn analyzePtrIsNonErrComptimeOnly(
3181331810 const child_ty = ptr_ty.childType(zcu);
3181431811
3181531812 const child_tag = child_ty.zigTypeTag(zcu);
31816 if (child_tag != .error_set and child_tag != .error_union) return .bool_true;
31817 if (child_tag == .error_set) return .bool_false;
31813 if (child_tag != .error_set and child_tag != .error_union) return .true;
31814 if (child_tag == .error_set) return .false;
3181831815 assert(child_tag == .error_union);
3181931816
31820 _ = block;
31821 _ = src;
31822
31823 return .none;
31817 if (try sema.resolveValue(operand)) |ptr_val| {
31818 if (ptr_val.isUndef(zcu)) return .undef_bool;
31819 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |val| {
31820 return try sema.resolveIsNonErrVal(block, src, .fromValue(val));
31821 }
31822 }
31823 return null;
3182431824}
3182531825
31826fn analyzeIsNonErrComptimeOnly(
31826fn resolveIsNonErrVal(
3182731827 sema: *Sema,
3182831828 block: *Block,
3182931829 src: LazySrcLoc,
3183031830 operand: Air.Inst.Ref,
31831) CompileError!Air.Inst.Ref {
31831) CompileError!?Value {
3183231832 const pt = sema.pt;
3183331833 const zcu = pt.zcu;
3183431834 const ip = &zcu.intern_pool;
3183531835 const operand_ty = sema.typeOf(operand);
3183631836 const ot = operand_ty.zigTypeTag(zcu);
31837 if (ot != .error_set and ot != .error_union) return .bool_true;
31838 if (ot == .error_set) return .bool_false;
31837 if (ot != .error_set and ot != .error_union) return .true;
31838 if (ot == .error_set) return .false;
3183931839 assert(ot == .error_union);
3184031840
3184131841 const payload_ty = operand_ty.errorUnionPayload(zcu);
3184231842 if (payload_ty.zigTypeTag(zcu) == .noreturn) {
31843 return .bool_false;
31843 return .false;
3184431844 }
3184531845
3184631846 if (operand == .undef) {
3184731847 return .undef_bool;
3184831848 } else if (@intFromEnum(operand) < InternPool.static_len) {
3184931849 // None of the ref tags can be errors.
31850 return .bool_true;
31850 return .true;
3185131851 }
3185231852
3185331853 const maybe_operand_val = try sema.resolveValue(operand);
......@@ -31870,23 +31870,23 @@ fn analyzeIsNonErrComptimeOnly(
3187031870 if (maybe_operand_val != null) break :blk;
3187131871
3187231872 // Try to avoid resolving inferred error set if possible.
31873 if (ies.errors.count() != 0) return .none;
31873 if (ies.errors.count() != 0) return null;
3187431874 switch (ies.resolved) {
31875 .anyerror_type => return .none,
31875 .anyerror_type => return null,
3187631876 .none => {},
3187731877 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31878 0 => return .bool_true,
31879 else => return .none,
31878 0 => return .true,
31879 else => return null,
3188031880 },
3188131881 }
3188231882 // We do not have a comptime answer because this inferred error
3188331883 // set is not resolved, and an instruction later in this function
3188431884 // body may or may not cause an error to be added to this set.
31885 return .none;
31885 return null;
3188631886 },
3188731887 else => switch (ip.indexToKey(set_ty)) {
3188831888 .error_set_type => |error_set_type| {
31889 if (error_set_type.names.len == 0) return .bool_true;
31889 if (error_set_type.names.len == 0) return .true;
3189031890 },
3189131891 .inferred_error_set_type => |func_index| blk: {
3189231892 // If the error set is empty, we must return a comptime true or false.
......@@ -31902,35 +31902,35 @@ fn analyzeIsNonErrComptimeOnly(
3190231902 if (sema.fn_ret_ty_ies) |ies| {
3190331903 if (ies.func == func_index) {
3190431904 // Try to avoid resolving inferred error set if possible.
31905 if (ies.errors.count() != 0) return .none;
31905 if (ies.errors.count() != 0) return null;
3190631906 switch (ies.resolved) {
31907 .anyerror_type => return .none,
31907 .anyerror_type => return null,
3190831908 .none => {},
3190931909 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31910 0 => return .bool_true,
31911 else => return .none,
31910 0 => return .true,
31911 else => return null,
3191231912 },
3191331913 }
3191431914 // We do not have a comptime answer because this inferred error
3191531915 // set is not resolved, and an instruction later in this function
3191631916 // body may or may not cause an error to be added to this set.
31917 return .none;
31917 return null;
3191831918 }
3191931919 }
3192031920 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
3192131921 if (resolved_ty == .anyerror_type)
3192231922 break :blk;
3192331923 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
31924 return .bool_true;
31924 return .true;
3192531925 },
3192631926 else => unreachable,
3192731927 },
3192831928 }
3192931929
3193031930 if (maybe_operand_val) |err_union| {
31931 return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false;
31931 return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .true else .false;
3193231932 }
31933 return .none;
31933 return null;
3193431934}
3193531935
3193631936fn analyzeIsNonErr(
......@@ -31939,12 +31939,10 @@ fn analyzeIsNonErr(
3193931939 src: LazySrcLoc,
3194031940 operand: Air.Inst.Ref,
3194131941) CompileError!Air.Inst.Ref {
31942 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
31943 if (result == .none) {
31944 try sema.requireRuntimeBlock(block, src, null);
31945 return block.addUnOp(.is_non_err, operand);
31942 if (try sema.resolveIsNonErrVal(block, src, operand)) |val| {
31943 return .fromValue(val);
3194631944 } else {
31947 return result;
31945 return block.addUnOp(.is_non_err, operand);
3194831946 }
3194931947}
3195031948
......@@ -31954,12 +31952,10 @@ fn analyzePtrIsNonErr(
3195431952 src: LazySrcLoc,
3195531953 operand: Air.Inst.Ref,
3195631954) CompileError!Air.Inst.Ref {
31957 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
31958 if (result == .none) {
31959 try sema.requireRuntimeBlock(block, src, null);
31960 return block.addUnOp(.is_non_err_ptr, operand);
31955 if (try sema.resolvePtrIsNonErrVal(block, src, operand)) |val| {
31956 return .fromValue(val);
3196131957 } else {
31962 return result;
31958 return block.addUnOp(.is_non_err_ptr, operand);
3196331959 }
3196431960}
3196531961