Sema: add zig_lazy parameter qualifier for inline calls#22
Sema: add zig_lazy parameter qualifier for inline calls#22Jarred-Sumner wants to merge 5 commits into
Conversation
A zig_lazy parameter's argument expression is captured unevaluated and only analyzed on first reference inside the inlined body, so arguments behind comptime-dead branches (assert/log/trace gated by build flags) are guaranteed to generate no code. Degrades to ordinary eager evaluation when the call is not inlined.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughAdds a new Changes
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Sema.zig`:
- Around line 249-251: The inst_map field in Sema (currently declared as
inst_map: InstMap) should not store a by-value snapshot because
analyzeArg/analyzeCall can grow/reallocate the caller map; change it to hold a
mutable reference to the caller map instead (e.g., make the field type a pointer
or reference to InstMap) so pointer/length updates propagate back to
old_inst_map when restoring caller context; update any construction sites that
populate Sema.inst_map and uses in analyzeCall/analyzeArg to pass and
dereference the mutable reference accordingly so the caller's InstMap is mutated
in place rather than copied.
- Around line 7932-7938: The pass currently skips parameters when
sema.inst_map.get(inst) returns null, which silently drops debug info for
lazily-forced params; change the logic so that when inst_map.get(inst) is null
you attempt to force/resolve the lazy arg (call resolveLazyArg or the
appropriate resolver) and then re-check sema.inst_map.get(inst) before
continuing, and only after that skip; once air_inst is available call
sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name) as before.
If a resolver function is not available in this scope, instead record the inst
in a deferred list and emit dbg vars after body analysis (i.e., run this
emission after resolveLazyArg populates inst_map). Ensure you reference
sema.inst_map.get, resolveLazyArg, sema.addDbgVar, dbg_arg_inline and
child_block when making the change.
- Around line 2040-2047: The code currently clears the caller's lazy context by
setting sema.lazy_args = .empty before analyzing the lazy expression; instead
assign the callee's lazy-argument context (the field on the lazy record, e.g.
lazy.args or lazy.lazy_args) to sema.lazy_args so nested inline/lazy lookups
work, and keep the existing defer that restores cur_lazy_args; update the
assignment of sema.lazy_args (the line setting it to .empty) to use the lazy
record's lazy-args field and ensure the defer continues to restore cur_lazy_args
alongside cur_code and cur_inst_map.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6b924721-6572-40b6-99a6-0ab13062bed9
📒 Files selected for processing (11)
lib/std/zig/Ast.ziglib/std/zig/Ast/Render.ziglib/std/zig/AstGen.ziglib/std/zig/Parse.ziglib/std/zig/Zir.ziglib/std/zig/tokenizer.zigsrc/Sema.zigsrc/Zcu.zigsrc/print_zir.zigtest/behavior.zigtest/behavior/lazy_param.zig
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@test/behavior/lazy_param.zig`:
- Around line 338-341: Add a regression to verify disabled comptime memoization
by updating the "zig_lazy: comptime call with elided arg" test to perform two
identical comptime invocations of the same zig_lazy helper (e.g., middleLazy)
that use a side-effecting counter: declare a comptime-visible
counter/side-effect, call middleLazy(comptime-side-effecting-arg,
`@compileError`("dead"), 2) twice, and assert the counter increments twice and the
returned results are correct; ensure the new assertions reference the existing
middleLazy/zig_lazy function names so the test verifies that the second comptime
call is not served from cache.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e4d10e39-3fda-46e3-8999-9ba29d26c5ab
📒 Files selected for processing (2)
src/Sema.zigtest/behavior/lazy_param.zig
| test "zig_lazy: comptime call with elided arg" { | ||
| const r = comptime middleLazy(1, @compileError("dead"), 2); | ||
| try expect(r == 3); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add a regression for disabled comptime memoization.
This only proves elision for a single comptime call. The PR also changes zig_lazy functions to bypass comptime call memoization, so it would be worth adding two identical comptime invocations with a counter/side effect to ensure they are not served from cache.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/behavior/lazy_param.zig` around lines 338 - 341, Add a regression to
verify disabled comptime memoization by updating the "zig_lazy: comptime call
with elided arg" test to perform two identical comptime invocations of the same
zig_lazy helper (e.g., middleLazy) that use a side-effecting counter: declare a
comptime-visible counter/side-effect, call
middleLazy(comptime-side-effecting-arg, `@compileError`("dead"), 2) twice, and
assert the counter increments twice and the returned results are correct; ensure
the new assertions reference the existing middleLazy/zig_lazy function names so
the test verifies that the second comptime call is not served from cache.
Adds a
zig_lazyparameter qualifier. Azig_lazyparameter's argument expression is captured unevaluated and only analyzed on first reference inside the inlined body. If that reference is behind a comptime-dead branch, the expression is never analyzed at all — side effects, type instantiation, and@compileErrorall disappear.Semantics
ifstill hoists evaluation before the inline body — laziness is comptime-only).callconv(.auto)in Debug builds),zig_lazyis a no-op and the argument is evaluated eagerly.zig_lazyparams.zig_lazyis a real keyword;lazyremains a valid identifier.Implementation
Mirrors the
noaliasplumbing: alazy_bits: u32mask is packed intoFuncFancytrailing data and surfaced viaZir.FnInfo.analyzeCallskipsanalyzeArgfor marked params on inline calls and stashes aLazyArgthunk (callercode/inst_map/block+ arg index) onSema.resolveInstfalls through toresolveLazyArgon the first lookup, which swaps to caller context, runsanalyzeArg, and memoizes the result in the callee'sinst_map.Tests
test/behavior/lazy_param.zig— 19 cases covering elision, evaluation,anytypetuples, mixed-type masks, position independence, evaluate-once, first-use ordering, nested inline, runtime-branch hoisting, log-style runtime tuples, and eager fallback for non-inlinecallconv.