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(...@@ -1927,9 +1927,8 @@ fn analyzeBodyInner(
1927 break :msg msg;1927 break :msg msg;
1928 });1928 });
1929 }1929 }
1930 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);1930 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1931 assert(is_non_err != .none);1931 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
1932 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1933 if (is_non_err_val.toBool()) {1932 if (is_non_err_val.toBool()) {
1934 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);1933 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
1935 }1934 }
...@@ -1945,9 +1944,8 @@ fn analyzeBodyInner(...@@ -1945,9 +1944,8 @@ fn analyzeBodyInner(
1945 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);1944 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
1946 const operand = try sema.resolveInst(extra.data.operand);1945 const operand = try sema.resolveInst(extra.data.operand);
1947 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);1946 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1948 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);1947 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1949 assert(is_non_err != .none);1948 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
1950 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1951 if (is_non_err_val.toBool()) {1949 if (is_non_err_val.toBool()) {
1952 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);1950 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
1953 }1951 }
...@@ -8960,6 +8958,7 @@ fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air...@@ -8960,6 +8958,7 @@ fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air
8960 const result_ty = operand_ty.errorUnionSet(zcu);8958 const result_ty = operand_ty.errorUnionSet(zcu);
89618959
8962 if (try sema.resolveDefinedValue(block, src, operand)) |val| {8960 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
8961 if (val.getErrorName(zcu) == .none) return .unreachable_value;
8963 return Air.internedToRef((try pt.intern(.{ .err = .{8962 return Air.internedToRef((try pt.intern(.{ .err = .{
8964 .ty = result_ty.toIntern(),8963 .ty = result_ty.toIntern(),
8965 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,8964 .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:...@@ -8997,7 +8996,7 @@ fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand:
89978996
8998 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {8997 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
8999 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {8998 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;
9001 return Air.internedToRef((try pt.intern(.{ .err = .{9000 return Air.internedToRef((try pt.intern(.{ .err = .{
9002 .ty = result_ty.toIntern(),9001 .ty = result_ty.toIntern(),
9003 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,9002 .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!...@@ -18689,14 +18688,13 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
18689 break :msg msg;18688 break :msg msg;
18690 });18689 });
18691 }18690 }
18692 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);18691 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
18693 if (is_non_err != .none) {
18694 // We can propagate `.cold` hints from this branch since it's comptime-known18692 // We can propagate `.cold` hints from this branch since it's comptime-known
18695 // to be taken from the parent branch.18693 // to be taken from the parent branch.
18696 const parent_hint = sema.branch_hint;18694 const parent_hint = sema.branch_hint;
18697 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;18695 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);
18700 if (is_non_err_val.toBool()) {18698 if (is_non_err_val.toBool()) {
18701 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);18699 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
18702 }18700 }
...@@ -18753,14 +18751,13 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -18753,14 +18751,13 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
18753 break :msg msg;18751 break :msg msg;
18754 });18752 });
18755 }18753 }
18756 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);18754 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
18757 if (is_non_err != .none) {
18758 // We can propagate `.cold` hints from this branch since it's comptime-known18755 // We can propagate `.cold` hints from this branch since it's comptime-known
18759 // to be taken from the parent branch.18756 // to be taken from the parent branch.
18760 const parent_hint = sema.branch_hint;18757 const parent_hint = sema.branch_hint;
18761 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;18758 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);
18764 if (is_non_err_val.toBool()) {18761 if (is_non_err_val.toBool()) {
18765 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);18762 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
18766 }18763 }
...@@ -31800,12 +31797,12 @@ fn analyzeIsNull(...@@ -31800,12 +31797,12 @@ fn analyzeIsNull(
31800 return block.addUnOp(air_tag, operand);31797 return block.addUnOp(air_tag, operand);
31801}31798}
3180231799
31803fn analyzePtrIsNonErrComptimeOnly(31800fn resolvePtrIsNonErrVal(
31804 sema: *Sema,31801 sema: *Sema,
31805 block: *Block,31802 block: *Block,
31806 src: LazySrcLoc,31803 src: LazySrcLoc,
31807 operand: Air.Inst.Ref,31804 operand: Air.Inst.Ref,
31808) CompileError!Air.Inst.Ref {31805) CompileError!?Value {
31809 const pt = sema.pt;31806 const pt = sema.pt;
31810 const zcu = pt.zcu;31807 const zcu = pt.zcu;
31811 const ptr_ty = sema.typeOf(operand);31808 const ptr_ty = sema.typeOf(operand);
...@@ -31813,41 +31810,44 @@ fn analyzePtrIsNonErrComptimeOnly(...@@ -31813,41 +31810,44 @@ fn analyzePtrIsNonErrComptimeOnly(
31813 const child_ty = ptr_ty.childType(zcu);31810 const child_ty = ptr_ty.childType(zcu);
3181431811
31815 const child_tag = child_ty.zigTypeTag(zcu);31812 const child_tag = child_ty.zigTypeTag(zcu);
31816 if (child_tag != .error_set and child_tag != .error_union) return .bool_true;31813 if (child_tag != .error_set and child_tag != .error_union) return .true;
31817 if (child_tag == .error_set) return .bool_false;31814 if (child_tag == .error_set) return .false;
31818 assert(child_tag == .error_union);31815 assert(child_tag == .error_union);
3181931816
31820 _ = block;31817 if (try sema.resolveValue(operand)) |ptr_val| {
31821 _ = src;31818 if (ptr_val.isUndef(zcu)) return .undef_bool;
3182231819 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |val| {
31823 return .none;31820 return try sema.resolveIsNonErrVal(block, src, .fromValue(val));
31821 }
31822 }
31823 return null;
31824}31824}
3182531825
31826fn analyzeIsNonErrComptimeOnly(31826fn resolveIsNonErrVal(
31827 sema: *Sema,31827 sema: *Sema,
31828 block: *Block,31828 block: *Block,
31829 src: LazySrcLoc,31829 src: LazySrcLoc,
31830 operand: Air.Inst.Ref,31830 operand: Air.Inst.Ref,
31831) CompileError!Air.Inst.Ref {31831) CompileError!?Value {
31832 const pt = sema.pt;31832 const pt = sema.pt;
31833 const zcu = pt.zcu;31833 const zcu = pt.zcu;
31834 const ip = &zcu.intern_pool;31834 const ip = &zcu.intern_pool;
31835 const operand_ty = sema.typeOf(operand);31835 const operand_ty = sema.typeOf(operand);
31836 const ot = operand_ty.zigTypeTag(zcu);31836 const ot = operand_ty.zigTypeTag(zcu);
31837 if (ot != .error_set and ot != .error_union) return .bool_true;31837 if (ot != .error_set and ot != .error_union) return .true;
31838 if (ot == .error_set) return .bool_false;31838 if (ot == .error_set) return .false;
31839 assert(ot == .error_union);31839 assert(ot == .error_union);
3184031840
31841 const payload_ty = operand_ty.errorUnionPayload(zcu);31841 const payload_ty = operand_ty.errorUnionPayload(zcu);
31842 if (payload_ty.zigTypeTag(zcu) == .noreturn) {31842 if (payload_ty.zigTypeTag(zcu) == .noreturn) {
31843 return .bool_false;31843 return .false;
31844 }31844 }
3184531845
31846 if (operand == .undef) {31846 if (operand == .undef) {
31847 return .undef_bool;31847 return .undef_bool;
31848 } else if (@intFromEnum(operand) < InternPool.static_len) {31848 } else if (@intFromEnum(operand) < InternPool.static_len) {
31849 // None of the ref tags can be errors.31849 // None of the ref tags can be errors.
31850 return .bool_true;31850 return .true;
31851 }31851 }
3185231852
31853 const maybe_operand_val = try sema.resolveValue(operand);31853 const maybe_operand_val = try sema.resolveValue(operand);
...@@ -31870,23 +31870,23 @@ fn analyzeIsNonErrComptimeOnly(...@@ -31870,23 +31870,23 @@ fn analyzeIsNonErrComptimeOnly(
31870 if (maybe_operand_val != null) break :blk;31870 if (maybe_operand_val != null) break :blk;
3187131871
31872 // Try to avoid resolving inferred error set if possible.31872 // 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;
31874 switch (ies.resolved) {31874 switch (ies.resolved) {
31875 .anyerror_type => return .none,31875 .anyerror_type => return null,
31876 .none => {},31876 .none => {},
31877 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {31877 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31878 0 => return .bool_true,31878 0 => return .true,
31879 else => return .none,31879 else => return null,
31880 },31880 },
31881 }31881 }
31882 // We do not have a comptime answer because this inferred error31882 // We do not have a comptime answer because this inferred error
31883 // set is not resolved, and an instruction later in this function31883 // set is not resolved, and an instruction later in this function
31884 // body may or may not cause an error to be added to this set.31884 // body may or may not cause an error to be added to this set.
31885 return .none;31885 return null;
31886 },31886 },
31887 else => switch (ip.indexToKey(set_ty)) {31887 else => switch (ip.indexToKey(set_ty)) {
31888 .error_set_type => |error_set_type| {31888 .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;
31890 },31890 },
31891 .inferred_error_set_type => |func_index| blk: {31891 .inferred_error_set_type => |func_index| blk: {
31892 // If the error set is empty, we must return a comptime true or false.31892 // If the error set is empty, we must return a comptime true or false.
...@@ -31902,35 +31902,35 @@ fn analyzeIsNonErrComptimeOnly(...@@ -31902,35 +31902,35 @@ fn analyzeIsNonErrComptimeOnly(
31902 if (sema.fn_ret_ty_ies) |ies| {31902 if (sema.fn_ret_ty_ies) |ies| {
31903 if (ies.func == func_index) {31903 if (ies.func == func_index) {
31904 // Try to avoid resolving inferred error set if possible.31904 // 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;
31906 switch (ies.resolved) {31906 switch (ies.resolved) {
31907 .anyerror_type => return .none,31907 .anyerror_type => return null,
31908 .none => {},31908 .none => {},
31909 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {31909 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31910 0 => return .bool_true,31910 0 => return .true,
31911 else => return .none,31911 else => return null,
31912 },31912 },
31913 }31913 }
31914 // We do not have a comptime answer because this inferred error31914 // We do not have a comptime answer because this inferred error
31915 // set is not resolved, and an instruction later in this function31915 // set is not resolved, and an instruction later in this function
31916 // body may or may not cause an error to be added to this set.31916 // body may or may not cause an error to be added to this set.
31917 return .none;31917 return null;
31918 }31918 }
31919 }31919 }
31920 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);31920 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
31921 if (resolved_ty == .anyerror_type)31921 if (resolved_ty == .anyerror_type)
31922 break :blk;31922 break :blk;
31923 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)31923 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
31924 return .bool_true;31924 return .true;
31925 },31925 },
31926 else => unreachable,31926 else => unreachable,
31927 },31927 },
31928 }31928 }
3192931929
31930 if (maybe_operand_val) |err_union| {31930 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;
31932 }31932 }
31933 return .none;31933 return null;
31934}31934}
3193531935
31936fn analyzeIsNonErr(31936fn analyzeIsNonErr(
...@@ -31939,12 +31939,10 @@ fn analyzeIsNonErr(...@@ -31939,12 +31939,10 @@ fn analyzeIsNonErr(
31939 src: LazySrcLoc,31939 src: LazySrcLoc,
31940 operand: Air.Inst.Ref,31940 operand: Air.Inst.Ref,
31941) CompileError!Air.Inst.Ref {31941) CompileError!Air.Inst.Ref {
31942 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);31942 if (try sema.resolveIsNonErrVal(block, src, operand)) |val| {
31943 if (result == .none) {31943 return .fromValue(val);
31944 try sema.requireRuntimeBlock(block, src, null);
31945 return block.addUnOp(.is_non_err, operand);
31946 } else {31944 } else {
31947 return result;31945 return block.addUnOp(.is_non_err, operand);
31948 }31946 }
31949}31947}
3195031948
...@@ -31954,12 +31952,10 @@ fn analyzePtrIsNonErr(...@@ -31954,12 +31952,10 @@ fn analyzePtrIsNonErr(
31954 src: LazySrcLoc,31952 src: LazySrcLoc,
31955 operand: Air.Inst.Ref,31953 operand: Air.Inst.Ref,
31956) CompileError!Air.Inst.Ref {31954) CompileError!Air.Inst.Ref {
31957 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);31955 if (try sema.resolvePtrIsNonErrVal(block, src, operand)) |val| {
31958 if (result == .none) {31956 return .fromValue(val);
31959 try sema.requireRuntimeBlock(block, src, null);
31960 return block.addUnOp(.is_non_err_ptr, operand);
31961 } else {31957 } else {
31962 return result;31958 return block.addUnOp(.is_non_err_ptr, operand);
31963 }31959 }
31964}31960}
3196531961