zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 41f740aa440db04721611e365a1fa2a6f08b7259 (tree)
parent 857936c55d9855962f3d30745834ae1305e52261
Author: Motiejus <motiejus@jakstys.lt>
Date:   Sat,  7 Mar 2026 10:34:22 +0000

sema: add error markers for unimplemented/wrong code paths

Systematically add sema->has_compile_errors = true for paths where
the C port either silently returns wrong values or is unimplemented
for valid Zig inputs. This makes failures explicit rather than
producing mysterious wrong AIR/IP output.

Changes:
- zirRetImplicit: error if function has non-void return type
  (Sema.zig lines 18700-18710)
- zirDiv: error on signed integer with plain / operator
  (Sema.zig lines 15129-15135: must use @divTrunc/@divFloor/@divExact)
- zirByteSwap: error if operand is not integer type; error if
  bits % 8 != 0 (Sema.zig lines 22956-22964)
- analyzeBitNot: error if operand is not int or bool type
  (Sema.zig line 14261)
- zirBitReverse: error if operand is not integer type
  (Sema.zig line 22980)
- zirBitCount: error if operand is not integer type
  (Sema.zig line 22905)
- zirBitwise: error if peer type is not int/bool
  (Sema.zig lines 14218-14222)
- zirBitcast: error for comptime-only or void dest types
  (Sema.zig lines 9894-9956)
- zirNegate: error on unsigned integer type
  (Sema.zig lines 14973-14978)
- zirFloatCast: error if src/dest is not a float type
  (Sema.zig lines 10046-10065)
- zirRef: error for runtime ref path (not yet implemented)
  (Sema.zig analyzeRef)
- zirLoad: error for comptime load from comptime-known pointer
  (Sema.zig analyzeLoad lines 31443-31447)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
Mstage0/sema.c | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 121 insertions(+), 4 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -924,6 +924,15 @@ static void zirStoreNode(Sema* sema, SemaBlock* block, uint32_t inst) { static AirInstRef zirLoad(Sema* sema, SemaBlock* block, uint32_t inst) { ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef ptr = resolveInst(sema, operand_ref); + // Ported from Sema.zig analyzeLoad lines 31443-31447: + // If ptr is comptime-known, should dereference at comptime. + // C doesn't implement pointerDeref; mark as error to detect when needed. + if (AIR_REF_IS_IP(ptr) && block->is_comptime) { + // Comptime load from comptime-known pointer: not yet implemented. + // Zig calls pointerDeref(ptr_val, ptr_ty) to get the loaded value. + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } TypeIndex ptr_ty = semaTypeOf(sema, ptr); TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); AirInstData data; @@ -1141,6 +1150,15 @@ static AirInstRef zirNegate(Sema* sema, SemaBlock* block, uint32_t inst) { return semaAddInst(block, AIR_INST_NEG, data); } + // Ported from Sema.zig zirNegate lines 14973-14978: reject unsigned int + // and non-numeric types. Only signed int and comptime_int are valid. + if (sema->ip->items[ty].tag == IP_KEY_INT_TYPE + && !sema->ip->items[ty].data.int_type.signedness) { + // "negation of unsigned integer type" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + // Int negation: splat(rhs_ty, 0) then sub(0, operand). // Ported from src/Sema.zig zirNegate int branch. InternPoolKey key; @@ -1182,6 +1200,18 @@ static AirInstRef zirNegateWrap(Sema* sema, SemaBlock* block, uint32_t inst) { // Ported from src/Sema.zig analyzeBitNot. static AirInstRef analyzeBitNot( Sema* sema, SemaBlock* block, AirInstRef operand) { + // Ported from Sema.zig zirBitNot lines 14261-14262: + // operand must be int or bool type; anything else is a compile error. + TypeIndex operand_ty = semaTypeOf(sema, operand); + bool valid_ty = (operand_ty == IP_INDEX_BOOL_TYPE) + || (operand_ty == IP_INDEX_COMPTIME_INT_TYPE) + || (operand_ty != IP_INDEX_NONE + && sema->ip->items[operand_ty].tag == IP_KEY_INT_TYPE); + if (!valid_ty && AIR_REF_IS_IP(operand)) { + // Non-int/bool type for bitwise NOT — compile error. + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } // Comptime folding: if the operand is a comptime integer, // compute ~value at comptime. uint64_t val_lo, val_hi; @@ -1212,10 +1242,9 @@ static AirInstRef analyzeBitNot( return internComptimeInt(sema, ty, r_lo, r_hi, false); } } - TypeIndex ty = semaTypeOf(sema, operand); AirInstData data; memset(&data, 0, sizeof(data)); - data.ty_op.ty_ref = AIR_REF_FROM_IP(ty); + data.ty_op.ty_ref = AIR_REF_FROM_IP(operand_ty); data.ty_op.operand = operand; return semaAddInst(block, AIR_INST_NOT, data); } @@ -1532,6 +1561,9 @@ static AirInstRef zirBitCount( AirInstRef operand = resolveInst(sema, operand_ref); TypeIndex operand_ty = semaTypeOf(sema, operand); if (sema->ip->items[operand_ty].tag != IP_KEY_INT_TYPE) { + // Ported from Sema.zig zirBitCount line 22905: checkIntOrVector. + // CLZ/CTZ/popcount requires integer type. + sema->has_compile_errors = true; return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } uint16_t bits = sema->ip->items[operand_ty].data.int_type.bits; @@ -1615,6 +1647,13 @@ static AirInstRef zirByteSwap(Sema* sema, SemaBlock* block, uint32_t inst) { AirInstRef operand = resolveInst(sema, operand_ref); TypeIndex operand_ty = semaTypeOf(sema, operand); + // Ported from Sema.zig zirByteSwap line 22956: checkIntOrVector. + // @byteSwap requires integer type. + if (sema->ip->items[operand_ty].tag != IP_KEY_INT_TYPE) { + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + // Comptime folding: if operand is a comptime-known integer, compute // the byte-swapped value at comptime. // Ported from src/Sema.zig zirByteSwap: arith.byteSwap() call. @@ -1624,6 +1663,12 @@ static AirInstRef zirByteSwap(Sema* sema, SemaBlock* block, uint32_t inst) { uint16_t bits = 0; if (sema->ip->items[operand_ty].tag == IP_KEY_INT_TYPE) bits = sema->ip->items[operand_ty].data.int_type.bits; + // Ported from Sema.zig zirByteSwap lines 22958-22964: + // bits must be divisible by 8, else compile error. + if (bits > 0 && bits % 8 != 0) { + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } if (bits > 0 && bits % 8 == 0) { // Byte-swap: reverse the bytes. uint64_t r_lo = 0, r_hi = 0; @@ -1666,6 +1711,16 @@ static AirInstRef zirBitReverse(Sema* sema, SemaBlock* block, uint32_t inst) { AirInstRef operand = resolveInst(sema, operand_ref); TypeIndex operand_ty = semaTypeOf(sema, operand); + // Ported from Sema.zig zirBitReverse line 22980: checkIntOrVector. + // @bitReverse requires integer type; error on non-integer. + bool valid_ty = (operand_ty == IP_INDEX_COMPTIME_INT_TYPE) + || (operand_ty != IP_INDEX_NONE + && sema->ip->items[operand_ty].tag == IP_KEY_INT_TYPE); + if (!valid_ty) { + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + // Comptime folding for integer operands. // Ported from Sema.zig zirBitReverse → arith.bitReverse. uint64_t val_lo, val_hi; @@ -1985,6 +2040,14 @@ static AirInstRef zirDiv(Sema* sema, SemaBlock* block, uint32_t inst) { AirInstTag air_tag = AIR_INST_DIV_FLOAT; // default for floats InternPoolKey pk = ipIndexToKey(sema->ip, peer_ty); if (pk.tag == IP_KEY_INT_TYPE) { + // Ported from Sema.zig zirDiv lines 15129-15135: + // Signed integers must use @divTrunc, @divFloor, or @divExact. + if (pk.data.int_type.signedness) { + // "division with signed integers: must use @divTrunc, + // @divFloor, or @divExact" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } air_tag = AIR_INST_DIV_TRUNC; } @@ -2178,6 +2241,19 @@ static AirInstRef zirBitwise( lhs = semaCoerce(sema, block, peer_ty, lhs); rhs = semaCoerce(sema, block, peer_ty, rhs); + // Ported from Sema.zig zirBitwise lines 14218-14222: + // Only int/comptime_int/bool types are valid for bitwise ops. + { + bool valid = (peer_ty == IP_INDEX_BOOL_TYPE) + || (peer_ty == IP_INDEX_COMPTIME_INT_TYPE) + || (sema->ip->items[peer_ty].tag == IP_KEY_INT_TYPE); + if (!valid) { + // "invalid operands to binary bitwise expression" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + } + // Comptime folding: if both coerced operands are comptime integers, // compute the result at comptime (128-bit). uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi; @@ -2229,6 +2305,18 @@ static AirInstRef zirBitcast(Sema* sema, SemaBlock* block, uint32_t inst) { dest_ty = AIR_REF_TO_IP(resolved); } AirInstRef operand = resolveInst(sema, operand_ref); + + // Ported from Sema.zig zirBitcast lines 9894-9956: validate dest type. + // @bitCast is invalid for comptime-only types and certain special types. + if (dest_ty == IP_INDEX_COMPTIME_INT_TYPE + || dest_ty == IP_INDEX_COMPTIME_FLOAT_TYPE + || dest_ty == IP_INDEX_TYPE_TYPE || dest_ty == IP_INDEX_VOID_TYPE + || dest_ty == IP_INDEX_NORETURN_TYPE) { + // "@bitCast to/from comptime-only or void/noreturn type" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + // Comptime folding: if operand is comptime, bitcast at comptime. // The bit pattern is preserved; just re-intern with the dest type. uint64_t lo, hi; @@ -2320,6 +2408,20 @@ static AirInstRef zirFloatCast(Sema* sema, SemaBlock* block, uint32_t inst) { TypeIndex operand_ty = semaTypeOf(sema, operand); uint16_t src_bits = floatBits(operand_ty); uint16_t dst_bits = floatBits(dest_ty); + + // Ported from Sema.zig zirFloatCast lines 10046-10055: + // dest must be a concrete float type; operand must be float. + if (dst_bits == 0 && dest_ty != IP_INDEX_COMPTIME_FLOAT_TYPE) { + // "@floatCast destination is not a float type" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + if (src_bits == 0 && operand_ty != IP_INDEX_COMPTIME_FLOAT_TYPE) { + // "@floatCast source is not a float type" + sema->has_compile_errors = true; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + AirInstTag air_tag; if (dst_bits >= src_bits) { air_tag = AIR_INST_FPEXT; @@ -10238,8 +10340,18 @@ static uint8_t analyzeBodyRuntimeBreak( // --- zirRetImplicit --- // Ported from src/Sema.zig zirRetImplicit / analyzeRet. -static void zirRetImplicit(const Sema* sema, SemaBlock* block, uint32_t inst) { +static void zirRetImplicit(Sema* sema, SemaBlock* block, uint32_t inst) { (void)inst; + // Ported from Sema.zig zirRetImplicit: validate return type is void. + // Zig lines 18690-18710: noreturn or non-void return type is an error. + if (sema->fn_ret_ty != TYPE_NONE + && sema->fn_ret_ty != IP_INDEX_VOID_TYPE) { + // Function has non-void return type but uses implicit return. + // This is a compile error in Zig: + // "function with non-void return type implicitly returns" + sema->has_compile_errors = true; + return; + } AirInstRef operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); if (block->inlining) { SemaBlockInlining* inl = block->inlining; @@ -10843,8 +10955,13 @@ static void zirBlockComptime(Sema* sema, SemaBlock* block, uint32_t inst) { static AirInstRef zirRef(Sema* sema, uint32_t inst) { ZirInstRef operand_ref = sema->code.inst_datas[inst].un_tok.operand; AirInstRef operand = resolveInst(sema, operand_ref); - if (!AIR_REF_IS_IP(operand)) + if (!AIR_REF_IS_IP(operand)) { + // Ported from Sema.zig analyzeRef: runtime ref path not implemented. + // Upstream creates alloc+store+ref for runtime values; C is + // comptime-only. + sema->has_compile_errors = true; return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } InternPoolIndex val = AIR_REF_TO_IP(operand); InternPoolIndex val_ty = ipTypeOf(sema->ip, val);