Bug
Nested dereference operators << << << d2 where d2: ***int only perform one level of pointer load, outputting a raw address instead of the final int value.
Reproduction
// examples/10/10.1_pointers_simple.jai line 34
a2: int = 3;
b2: *int = *a2;
c2: **int = *b2;
d2: ***int = *c2;
print("%\n", << << << d2);
Expected: 3
Actual: 6100510096 (a raw address)
Root Cause Analysis
In bytecode_gen.zig line ~5904-5950, the shift_left/dot_star dereference handling has several early-return conditions:
- Line 5919:
if (ctx.typed != null and !ctx.typed.?.typeOf(operand).isPointer() and !operand_source_is_pointer) return operand_reg; — may short-circuit for intermediate deref results
- Line 5920:
if (ctx.pointer_addrs.get(operand_reg)) — the pointer_addrs map tracks address-of results; if the inner deref result was entered into this map, it would emit load instead of load_ptr
- Line 5927-5933:
resolved.local_values.get(operand) with non-pointer type check — shouldn't trigger for unary_expr operands
The typeTextForExpr at line 11125-11128 correctly strips one pointer level per deref, so type inference is fine. The issue is in the bytecode emission logic.
Files
bootstrap/src/bytecode_gen.zig (~lines 5904-5950)
Bug
Nested dereference operators
<< << << d2whered2: ***intonly perform one level of pointer load, outputting a raw address instead of the finalintvalue.Reproduction
Expected:
3Actual:
6100510096(a raw address)Root Cause Analysis
In
bytecode_gen.zigline ~5904-5950, theshift_left/dot_stardereference handling has several early-return conditions:if (ctx.typed != null and !ctx.typed.?.typeOf(operand).isPointer() and !operand_source_is_pointer) return operand_reg;— may short-circuit for intermediate deref resultsif (ctx.pointer_addrs.get(operand_reg))— thepointer_addrsmap tracks address-of results; if the inner deref result was entered into this map, it would emitloadinstead ofload_ptrresolved.local_values.get(operand)with non-pointer type check — shouldn't trigger for unary_expr operandsThe
typeTextForExprat line 11125-11128 correctly strips one pointer level per deref, so type inference is fine. The issue is in the bytecode emission logic.Files
bootstrap/src/bytecode_gen.zig(~lines 5904-5950)