commit 88a5dc48733c5d2ce51acb7c6ad0d9f1cd9cbd5b (tree)
parent af9f80f2f7c4828b65fe075975fcd95982b1a7e3
Author: Motiejus <motiejus@jakstys.lt>
Date: Sat, 7 Mar 2026 10:00:37 +0000
sema: port zirIsNonNull, zirIsNonErr handlers
Add handlers for ZIR instructions that check if optional/error union
values are non-null/non-error:
- ZIR_INST_IS_NON_NULL / IS_NON_NULL_PTR → AIR_INST_IS_NON_NULL
- ZIR_INST_IS_NON_ERR / IS_NON_ERR_PTR / RET_IS_NON_ERR → AIR_INST_IS_NON_ERR
zirIsNonNull also folds at comptime: opt_payload → true, null → false.
Add AIR_INST_IS_NON_NULL/PTR/ERR/ERR_PTR to semaTypeOf returning bool.
Ported from Sema.zig zirIsNonNull → analyzeIsNull(invert=true) and
zirIsNonErr.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | stage0/sema.c | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 56 insertions(+), 0 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -576,6 +576,12 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) {
case AIR_INST_MOD:
case AIR_INST_REM:
return semaTypeOf(sema, sema->air_inst_datas[inst_idx].bin_op.lhs);
+ // unary bool-result instructions.
+ case AIR_INST_IS_NON_NULL:
+ case AIR_INST_IS_NON_NULL_PTR:
+ case AIR_INST_IS_NON_ERR:
+ case AIR_INST_IS_NON_ERR_PTR:
+ return IP_INDEX_BOOL_TYPE;
// cmp bin_op: result type is bool.
case AIR_INST_CMP_LT:
case AIR_INST_CMP_LTE:
@@ -1218,6 +1224,45 @@ static AirInstRef zirBitNot(Sema* sema, SemaBlock* block, uint32_t inst) {
return analyzeBitNot(sema, block, operand);
}
+// zirIsNonNull: handle is_non_null ZIR instruction.
+// Ported from src/Sema.zig zirIsNonNull → analyzeIsNull(invert=true).
+// Comptime: fold based on opt_payload vs null.
+// Runtime: emit AIR_INST_IS_NON_NULL.
+static AirInstRef zirIsNonNull(Sema* sema, SemaBlock* block, uint32_t inst) {
+ ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
+ AirInstRef operand = resolveInst(sema, operand_ref);
+ // Comptime fold: check if operand is a known optional value.
+ if (AIR_REF_IS_IP(operand)) {
+ InternPoolIndex op_ip = AIR_REF_TO_IP(operand);
+ if (op_ip < sema->ip->items_len) {
+ InternPoolKey key = sema->ip->items[op_ip];
+ // opt_payload = non-null → true
+ if (key.tag == IP_KEY_OPT_PAYLOAD)
+ return AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE);
+ // null value → false (opt_null is not an IP_KEY type, but
+ // null literal is IP_INDEX_NULL_VALUE)
+ if (op_ip == IP_INDEX_NULL_VALUE)
+ return AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE);
+ }
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.un_op.operand = operand;
+ return semaAddInst(block, AIR_INST_IS_NON_NULL, data);
+}
+
+// zirIsNonErr: handle is_non_err ZIR instruction.
+// Ported from src/Sema.zig zirIsNonErr → analyzeIsErr(invert=true).
+// Runtime: emit AIR_INST_IS_NON_ERR.
+static AirInstRef zirIsNonErr(Sema* sema, SemaBlock* block, uint32_t inst) {
+ ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
+ AirInstRef operand = resolveInst(sema, operand_ref);
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.un_op.operand = operand;
+ return semaAddInst(block, AIR_INST_IS_NON_ERR, data);
+}
+
// zirBoolNot: handle bool_not ZIR instruction.
// Ported from src/Sema.zig zirBoolNot.
static AirInstRef zirBoolNot(Sema* sema, SemaBlock* block, uint32_t inst) {
@@ -12380,6 +12425,17 @@ bool analyzeBodyInner(
instMapPut(&sema->inst_map, inst, zirBoolNot(sema, block, inst));
i++;
continue;
+ case ZIR_INST_IS_NON_NULL:
+ case ZIR_INST_IS_NON_NULL_PTR:
+ instMapPut(&sema->inst_map, inst, zirIsNonNull(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_IS_NON_ERR:
+ case ZIR_INST_IS_NON_ERR_PTR:
+ case ZIR_INST_RET_IS_NON_ERR:
+ instMapPut(&sema->inst_map, inst, zirIsNonErr(sema, block, inst));
+ i++;
+ continue;
case ZIR_INST_INT_FROM_BOOL:
instMapPut(
&sema->inst_map, inst, zirIntFromBool(sema, block, inst));