Implement several interconnected features for function declarations:
- noalias_bits: Track which parameters have the noalias keyword by setting
corresponding bits in a uint32_t (supports up to 32 parameters)
- is_var_args: Detect ellipsis3 (...) token in parameter list
- is_noinline/has_inline_keyword: Detect noinline/inline modifiers
- callconv handling: Extract callconv_expr from fn_proto variants, create
cc_gz sub-block, emit builtin_value for explicit callconv() or inline
- func_fancy instruction: When any of cc_ref, is_var_args, noalias_bits,
or is_noinline are present, emit func_fancy instead of func/func_inferred
with the appropriate FuncFancy payload layout
- fn_var_args: Track in AstGenCtx for body code that checks it
- BuiltinValue constants: Add all ZIR_BUILTIN_VALUE_* defines to zir.h
- addBuiltinValue helper: Emit extended builtin_value instructions
Generic tracking (any_param_used, ret_ty_is_generic, ret_body_param_refs)
is not yet implemented as it requires is_used_or_discarded support in
ScopeLocalVal scope lookups.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move rvalue calls inside builtinCall (all builtins now call rvalue
internally, matching upstream) and remove outer rvalue wrap from
call site
- Add rlResultTypeForCast that errors when no result type is available,
used by @bitCast, @intCast, @truncate, @ptrCast, @enumFromInt
- Fix @import to compute res_ty from result location instead of
hardcoding ZIR_REF_NONE
- Fix @embedFile to evaluate operand with coerced_ty=slice_const_u8_type
- Fix @cInclude/simpleCBuiltin to check c_import scope and use
comptimeExpr with coerced_ty=slice_const_u8_type
- Fix @cImport to pass actual block_result to ensure_result_used instead
of hardcoded ZIR_REF_VOID_VALUE
Not fixed: Issue 14 (ptrCast nested pointer cast collapsing) — upstream
routes @ptrCast through a dedicated ptrCast() function that walks nested
pointer cast builtins. Currently uses simple typeCast path only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port two missing features from upstream AstGen.zig varDecl:
1. Add reachableExprComptime (AstGen.zig:418-438) which wraps init
expressions in comptimeExpr when force_comptime is set, and checks
for noreturn results. Replace plain exprRl calls in all three varDecl
paths (const rvalue, const alloc, var) with reachableExprComptime.
2. Extract comptime_token by scanning backwards from mut_token (matching
Ast.zig fullVarDeclComponents). For const path, set force_comptime to
wrap init in comptime block. For var path, use comptime_token to set
is_comptime which selects alloc_comptime_mut/alloc_inferred_comptime_mut
tags and sets maybe_comptime on the scope.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port two missing features from upstream AstGen.zig:
1. Handle identifier-named tests (decltest): when the token after `test`
is an identifier, set decl_id to DECL_ID_DECLTEST and record the
identifier string as the test name. Upstream performs full scope
resolution for validation which is skipped here.
2. Add `within_fn` field to AstGenCtx (mirrors AstGen.within_fn). Save,
set to true, and restore in both testDecl and fnDecl. This flag
propagates to maybe_generic on namespace scopes for container
declarations inside function/test bodies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port two missing features from upstream AstGen.zig ret() function:
1. Add any_defer_node field to GenZir (AstGen.zig:11812) to track
whether we're inside a defer expression. Set it in defer body
generation and propagate via makeSubBlock. retExpr now checks
this field and errors with "cannot return from defer expression"
(AstGen.zig:8127-8135). Also reorder retExpr checks to match
upstream: fn_block null check first, then any_defer_node check,
then emitDbgNode.
2. Add reachableExpr wrapper (AstGen.zig:408-416) that calls exprRl
and checks refIsNoReturn to detect unreachable code. Use it in
retExpr instead of plain exprRl for the return operand
(AstGen.zig:8185-8186). nameStratExpr is left as TODO since
containerDecl does not yet accept a name_strategy parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add two fixes from audit of ptrTypeExpr against upstream AstGen.zig ptrType:
1. Reject `[*c]allowzero T` with a compile error matching upstream
(AstGen.zig:3840-3842). C pointers always allow address zero, so
the allowzero modifier is invalid on them.
2. Save source_offset/source_line/source_column before typeExpr and
restore them before evaluating each trailing expression (sentinel,
addrspace, align). This ensures correct debug info source locations
matching upstream (AstGen.zig:3844-3846, 3859-3861, 3876-3878,
3885-3887).
Issue 3 (addrspace RL using addBuiltinValue) is skipped as
addBuiltinValue is not yet implemented.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port assignShift (AstGen.zig:3786) and assignShiftSat (AstGen.zig:3812)
from upstream, handling <<=, >>=, and <<|= operators as both statements
in blockExprStmts and expressions in exprRl. Previously these fell
through to SET_ERROR.
Add grouped_expression unwrapping loop in blockExprStmts (matching
AstGen.zig:2569-2630) so that parenthesized statements like `(x += 1)`
are correctly dispatched to assignment handlers instead of going through
the default unusedResultExpr path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix three issues in the RL annotation pre-pass (rlExpr):
1. Label detection for `inline while`/`inline for` now accounts for
the `keyword_inline` token before checking for `identifier colon`,
matching upstream fullWhileComponents/fullForComponents logic.
2. `assign_destructure` now recurses into variable nodes and the value
expression with RL_RI_NONE, matching upstream behavior instead of
returning false without visiting sub-expressions.
3. `rlTokenIdentEqual` now handles @"..."-quoted identifiers by comparing
the quoted content rather than stopping at the `@` character, which
previously caused all @-quoted identifiers to compare as equal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The exprRl function was wrapping blockExprExpr's return value in an
extra rvalue() call, but blockExprExpr already applies rvalue internally
for labeled blocks when need_result_rvalue=true. The upstream expr()
function at AstGen.zig:991 returns blockExpr's result directly without
extra rvalue wrapping. This could produce duplicate coercion/store
instructions for non-trivial result locations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port two missing checks from upstream AstGen.zig rvalueInner to the C
rvalue function:
1. isAlwaysVoid (Zir.zig:1343-1608): When the result refers to an
instruction that always produces void (e.g., dbg_stmt, store_node,
export, memcpy, etc.), replace the result with void_value before
proceeding. This prevents emitting unnecessary type coercions or
stores on always-void instructions.
2. endsWithNoReturn (AstGen.zig:11068): When the current GenZir block
ends with a noreturn instruction, return the result immediately
without emitting any rvalue instructions. This avoids emitting dead
ZIR instructions after noreturn.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port the \u{NNNNNN} unicode escape parsing from upstream Zig's
string_literal.zig:parseEscapeSequence into both strLitAsString
(string literal decoding with UTF-8 encoding) and char_literal
(codepoint value extraction). Without this, \u escapes fell through
to the default branch which wrote a literal 'u' character, producing
incorrect ZIR string bytes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BuiltinCall.Flags has ensure_result_used at bit 1, not bit 3 like
Call/FieldCall. Separate the case to use the correct bit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three bugs found by auditing against upstream AstGen.zig/AstRlAnnotate.zig:
1. rlExpr: defer was recursing into nd.rhs (always 0) instead of nd.lhs
(the actual deferred expression), so the RL annotation pass never
visited defer bodies.
2. addEnsureResult: compile_error was missing from the noreturn
instruction list, causing spurious ensure_result_used instructions
to be emitted after @compileError calls.
3. blockExprExpr: force_comptime was derived from gz->is_comptime,
but upstream blockExpr always passes force_comptime=false to
labeledBlockExpr. This caused labeled blocks in comptime contexts
to incorrectly emit BLOCK_COMPTIME + BREAK_INLINE instead of
BLOCK + BREAK.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix two parser bugs found by auditing against upstream Parse.zig:
1. In parseTypeExpr's while case, the continue expression was parsed
inline as `eatToken(COLON) ? expectExpr : 0` which missed the
required parentheses. Use parseWhileContinueExpr(p) instead,
matching what parseWhileExpr already does.
2. In expectStatement, comptime blocks used parseBlock() which only
matches `{ ... }`. Use parseBlockExpr() to also recognize labeled
blocks like `comptime label: { ... }`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match Zig's Signedness enum values (unsigned=1, signed=0) and
reorder int_type struct fields to match Zig's layout:
[src_node, bit_count, signedness, pad].
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inline index_inst at usage site to narrow scope, initialize
var_init_rl.ctx to RI_CTX_NONE (matching upstream default).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- whileExpr: emit emitDbgNode before condition evaluation to match
upstream AstGen.zig:6579. Fixes astgen_test.zig corpus (1 missing
DBG_STMT).
- Block expressions in exprRl: wrap blockExprExpr result with rvalue()
to handle result location storage (RL_PTR → STORE_NODE, etc.).
Fixes parser_test.zig inst_len to exact match.
- parser_test.zig corpus now has matching inst_len and all tags, but
has 1 int_type data signedness mismatch (pre-existing issue).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Typed struct init empty (SomeType{}) was returning the result directly
without going through rvalue(), missing STORE_NODE/STORE_TO_INFERRED_PTR/
COERCE_PTR_ELEM_TY+REF emissions when result location requires storage.
Reduces parser_test.zig corpus diff from 5 to 1 instruction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- retExpr: check nodesNeedRl to use RL_PTR with ret_ptr/ret_load instead of
always RL_COERCED_TY with ret_node. Handle .always/.maybe error paths with
load from ptr when needed.
- Use typeExpr() instead of expr()/exprRl() for type sub-expressions in
optional_type, error_union, merge_error_sets, and array elem types in
structInitExpr/arrayInitExpr. This generates BLOCK_COMPTIME wrappers for
non-primitive type identifiers.
- arrayInitExpr: only use ARRAY_INIT_REF for RL_REF (not RL_REF_COERCED_TY),
and pass non-ref results through rvalue().
- slice_sentinel: emit SLICE_SENTINEL_TY and coerce sentinel to that type.
All slice variants: coerce start/end to usize.
- COERCE_PTR_ELEM_TY in rvalue for RL_REF_COERCED_TY.
- rvalueNoCoercePreRef for local variable references.
- structInitExprPtr/arrayInitExprPtr for RL_PTR with OPT_EU_BASE_PTR_INIT.
- Typed struct init: use RL_COERCED_TY with field type for init expressions.
Reduces parser_test.zig corpus diff from 225 to 5 instructions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- @as builtin: propagate RL_TY with dest_type through exprRl instead of
evaluating operand with RL_NONE and manually emitting as_node. Matches
upstream AstGen.zig lines 8909-8920.
- rlResultType: add missing RL_REF_COERCED_TY case (elem_type extraction).
- continue handler: use AST_NODE_OFFSET_NONE for addBreak operand_src_node
instead of computing node offset. Upstream uses addBreak (not
addBreakWithSrcNode), which writes .none.
- varDecl: set init_rl.src_node = 0 for RL_PTR (upstream leaves
PtrResultLoc.src_node at default .none).
Enables astgen_test.zig corpus test — all corpus tests now pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
addInstruction() already returns idx + ZIR_REF_START_INDEX (a ref),
so the extra + ZIR_REF_START_INDEX on the inplace_arith_result_ty path
resulted in a double-offset (+248 instead of +124) being stored in
extra data for += and -= compound assignments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BREAK/CONTINUE: lhs is opt_token (null=UINT32_MAX), not opt_node
(null=0). Check nd.lhs != UINT32_MAX instead of != 0.
- ERROR_VALUE: last token is main_token + 2 (error.name has 3 tokens),
not main_token.
- advanceSourceCursor: replace silent return on backward movement with
assert, matching upstream behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cursor backward issue at inst 1557 (src_off goes 10502 -> 8256).
Needs investigation of statement ordering in switch expression body.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Save source cursor before evaluating sub-expressions in array_access
and @tagName (cursor was being mutated by inner expr calls)
- Add is_comptime guard to advanceSourceCursorToMainToken matching
upstream maybeAdvanceSourceCursorToMainToken (skip in comptime)
- Re-skip astgen_test.zig corpus (dbg_stmt mismatch remains at inst 1557)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mechanically match upstream comptimeExpr signature which accepts ResultInfo.
This fixes coercion in comptime contexts (e.g. sentinel 0 becoming zero_u8
instead of generic zero when elem_type is u8).
- comptimeExpr: add ResultLoc rl parameter, thread to exprRl
- typeExpr: pass coerced_ty=type_type (matching upstream coerced_type_ri)
- ptrType: pass ty=elem_type for sentinel, coerced_ty=u29 for align,
coerced_ty=u16 for bit_range
- retExpr: set RI_CTX_RETURN
- tryExpr: set RI_CTX_ERROR_HANDLING_EXPR for operand
- orelseCatchExpr: set RI_CTX_ERROR_HANDLING_EXPR when do_err_trace
- ifExpr: set RI_CTX_ERROR_HANDLING_EXPR for error union condition
- shiftOp: set RI_CTX_SHIFT_OP, use as_shift_operand in rvalue
- breakResultInfo: don't forward ctx for discard case
- fnDecl ret_body break: use AST_NODE_OFFSET_NONE
Passes corpus tests for test_all.zig, build.zig, tokenizer_test.zig.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace linear scan of all string_bytes with a string_table that
only contains explicitly registered strings (via identAsString and
strLitAsString). This prevents false deduplication against multiline
string content that upstream's hash table would never match.
Also handle embedded null bytes in strLitAsString: when decoded string
contains \x00, skip dedup and don't add trailing null, matching upstream
AstGen.zig:11560. Fix c_include extended instruction small field to
0xAAAA (undefined) matching upstream addExtendedPayload.
Passes corpus tests for test_all.zig, build.zig, tokenizer_test.zig.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major fixes to match upstream AstGen.zig:
- Call/FieldCall: flags at offset 0, scratch_extra for arg bodies,
pop_error_return_trace from ResultCtx instead of hardcoded true
- CondBr: write {condition, then_body_len, else_body_len} then bodies
(was interleaving lengths with bodies)
- For loop: use instructionsSliceUpto, resurrect loop_scope for
increment/repeat after then/else unstacked
- validate_struct_init_result_ty: un_node encoding (no extra payload)
- addEnsureResult: flags always at pi+0 for all call types
- addFunc: param_insts extra refs for correct body attribution
- array_init_elem_type: addBin instead of addPlNodeBin
- Pre-register struct field names for correct string ordering
- comptime break_inline: AST_NODE_OFFSET_NONE
- varDecl: pass RI_CTX_CONST_INIT context
- Rewrite test infrastructure with field-by-field ZIR comparison
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Comprehensive firstToken: handle all AST node types matching upstream
Ast.zig (call, struct_init, slice, binary ops, fn_decl, blocks, etc.)
instead of falling through to main_token for unknown types.
- Slice LHS uses .ref rl: pass RL_REF_VAL for slice_open/slice/
slice_sentinel LHS evaluation, matching upstream AstGen.zig:882-939.
- fnDecl param name before type: resolve parameter name via
identAsString before evaluating the type expression, matching upstream
AstGen.zig:4283-4335 ordering.
- Break label comparison: use tokenIdentEql (source text comparison)
instead of identAsString to avoid adding label names to string_bytes,
matching upstream AstGen.zig:2176 tokenIdentEql.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix call instruction not being appended to gz's instruction list due
to a debug range check left in callExpr. This caused emitDbgStmt's
dedup logic to not see call instructions, resulting in 10 missing
dbg_stmt instructions in the build.zig corpus test.
Also port shiftOp from upstream (AstGen.zig:9978) for shl/shr operators,
which need typeof_log2_int_type for RHS coercion and their own emitDbgStmt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port the missing rvalue() call in orelseCatchExpr's then-branch
(AstGen.zig:6088-6091). The upstream applies rvalue with
block_scope.break_result_info to the unwrapped payload before
breaking, which emits as_node coercion when needed. The C code
was passing the unwrapped value directly to addBreak without
coercion.
Also update the corpus build.zig TODO with current diff state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use nodeIndexToRelative(decl_node) = node - proto_node for the
break_inline returning func to declaration, matching upstream
AstGen.zig:4495. Previously used AST_NODE_OFFSET_NONE which
produced incorrect extra data values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handle anonymous struct init (.{.a = b}) when the result location has
a type (RL_TY/RL_COERCED_TY). Emit validate_struct_init_result_ty and
struct_init_field_type instructions, matching upstream AstGen.zig:
1706-1731 and structInitExprTyped.
Also add validate_struct_init_result_ty to test comparison functions
and fix char literal escape sequences.
build.zig corpus: improved from 25 to 3 inst diff (remaining:
as_node coercion in rvalue).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add RL_REF_COERCED_TY to the result location enum, matching the upstream
ref_coerced_ty variant. This carries a pointer type through the result
location so that array init and struct init expressions can generate
validate_array_init_ref_ty and struct_init_empty_ref_result instructions.
- Use RL_REF_COERCED_TY in address_of when result type is available
- Handle in arrayInitDotExpr to emit validate_array_init_ref_ty
- Handle in structInitExpr for empty .{} to emit struct_init_empty_ref_result
- Add RL_IS_REF() macro for checking both RL_REF and RL_REF_COERCED_TY
- Update rvalue to treat RL_REF_COERCED_TY like RL_REF
tokenizer_test.zig corpus: instructions now match (7026). Extra and
string_bytes still have diffs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
astgen_test.zig corpus: extra_len and string_bytes diffs remain.
tokenizer_test.zig/build.zig: need ref_coerced_ty result location.
Both issues require significant architectural work in the AstRlAnnotate
pre-pass to properly support typed result locations (ref_coerced_ty,
coerced_ty) that generate different instruction sequences.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>