Skip to content

feat(ir): def+name* IR-compile seam — top-level (def x (fn …)) via the IR path - #647

Merged
nnunley merged 3 commits into
nooga:mainfrom
nnunley:salvage-ir-def-seam
Aug 7, 2026
Merged

feat(ir): def+name* IR-compile seam — top-level (def x (fn …)) via the IR path#647
nnunley merged 3 commits into
nooga:mainfrom
nnunley:salvage-ir-def-seam

Conversation

@nnunley

@nnunley nnunley commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

IR-compile seam for def+name* (grammar-style rule defs) — routes top-level (def NAME (name* ... (fn ...) ...)) through the IR pipeline via compile-def-fn-value.

AC-2 Verification (Seam Exercise)

The seam is proven by test/ir_def_namestar_seam_test.lg case 5 (seam-strict-throws-on-unlowerable-shape):

  • Under *ir-compile-strict*, a multi-arity name*-wrapped fn throws an error mentioning "ir-compile-strict" and "multi-arity"
  • Without the seam, bytecode compiler handles it silently (name* is just a call)
  • Silent outcome = seam never ran; throw = seam ran and detected the unlowerable shape

Shape is grammar-specific (not in main codebase by design); shape-specific test + non-vacuous proof satisfies workflow seam rule.

Return-Hinted Arity Vector Fix

Aligned compile-def-fn-value with #661's fix (b170a08): unwrap return-hinted arity vectors before testing for single-arity. Fixes forms like (def x (name* ... (fn ^long [args] ...))).

Base & Merge Order

Base: main@upstream
Merge order: 3rd (independent from #648/#649, stacked after their landing)

Verification

  • ✓ Build: go build ./... passes
  • ✓ Tests: go test ./pkg/ir passes (including seam test with return-hinted cases)
  • ✓ Generated: make check-generated passes

@mparrett mparrett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smallest of the four by diff, though not by behavior — it inserts a live call into defCompiler's path. Three things before it merges.

The seam is inert without #650. maybeIRCompileDefFnArg resolves ir.passes.pipeline/compile-def-fn-value and returns nil when that lookup fails. The var isn't on main and isn't in this PR — it's defined in #650's pipeline.lg diff. I built this branch alone and the var resolves to nil; built with #650 on top, the hook fires. So merged on its own this compiles, passes CI, and never runs, and with no test in the PR nothing would catch it. Either move the compile-def-fn-value defn here, or retarget this onto #650 and land them together.

Nothing in the repo produces the shape this hooks. name* has no definition on main — the only traces are a stub in pkg/ir/lisp_constfold_test.go:701 ("name*": '(defn name* [a b c] b)') and the unwrapping in passes/inline.lg. Even with #650 merged, no in-repo code constructs (def NAME (name* … (fn …) …)). That makes the coverage question sharper than "add a test": what exercises this seam, and can a test be written against main at all? I'd want that answered before it goes into defCompiler with zero coverage.

Strict mode. The seam consults clojure.core/*ir-compile* but never *ir-compile-strict*, and every early return is a bare return nil. The doc comment says the silence mirrors the defn macro's hybrid path, which held when #625 opened — #580 merged about a day later, adding *ir-compile-strict* (core.lg:991, throw at :1036) and the bytecode-path census specifically to make this class of fallback visible. As written the new seam is a hole the census can't see. Should it honor strict?

Separately: ir_bridge.lg adds ir/chunk-emit-pop-n, commented as "used by the branch pass-through fast path", but nothing calls it — not here, not in #648/#649/#650, and not in closed #625 either. Either its caller is in a piece that hasn't been posted, or it and the 24 generated lines behind it should come out.

@nnunley

nnunley commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Reworked to land self-contained. Addressing each point:

Inert without #650 — fixed. Moved the compile-def-fn-value defn into this PR (pkg/rt/core/ir/passes/pipeline.lg). maybeIRCompileDefFnArg now resolves it from this branch alone, so the hook fires when built by itself. #650 (salvage-ir-cache-window) drops back to just the *runtime-defn-ir-cache* retention tweak — no longer carries the defn, so the two no longer have to land together.

Coverage / what exercises the seam — added test/ir_def_namestar_seam_test.lg. Yes, it can be written against this branch, using exactly the stub you pointed at ((defn name* [tag f meta] f) — the runtime analogue of the lisp_constfold_test.go:701 stub). Because the seam is parity-preserving, a black-box "the fn still works" assertion can't prove the seam fired rather than the ordinary compiler — so the load-bearing assertion is the strict one: a name*-wrapped multi-arity fn is refused by compile-def-fn-value, and under *ir-compile-strict* the throw surfaces carrying both ir-compile-strict and multi-arity. That message can only come from the seam having run compile-def-fn-value, so it pins that the hook actually executed. Three deftests: happy-path single-arity (works), strict multi-arity (throws from the seam), and default multi-arity (silent hybrid fallback, still runs).

Strict mode — now honored. The seam consults *ir-compile-strict*. The "not a name*-fn-def" early returns stay silent (return nil, nil) — the seam simply doesn't apply there. But once the form IS a matched name*-wrapped single fn that should lower, a failure (compile-def-fn-value unavailable / not callable / Invoke error / nil / rebox error) returns an error under strict, so defCompiler fails loud and the bytecode-path census (#580) sees the miss instead of a silent fallback.

chunk-emit-pop-n — removed. Pulled the ir/chunk-emit-pop-n bridge decl and the generated lines behind it; nothing called it.

Verified: compiler + ir Go suites green, make check-generated OK under both build tags (bundle + -tags gogen_ir lowered tree).

@mparrett

mparrett commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

25261cdb on this branch is mine. It went up from a worktree I had open on salvage-ir-def-seam without checking with you first — my mistake, and worth flagging before you hit a conflict and wonder where it came from. Your 408b8746 is intact underneath it; nothing was rewritten.

What is in it:

  • pkg/compiler/compiler.go — excludes named fn forms from the seam. A (fn self [...] (self ...)) carries a lexical self-binding the defn-shaped helper cannot preserve: renaming self to the outer def name turns lexical recursion into a mutable Var lookup. Named forms therefore stay on the ordinary compiler even under strict mode.
  • test/ir_def_namestar_seam_test.lg — two further deftests, on the file you had already added there. Not a new file, which is the part I should have led with.

The rest is now dead weight. It also made return-hinted-arity-vector? and unwrap-arity-vector public in build.lg, which I needed locally to build this branch — and #661 merged an hour ago doing the same thing upstream. That is why build.lg, pipeline.lg and generated.sums conflict with main as of now.

Rebase onto main and drop 25261cdb. The helper half is upstream, so most of the conflict goes with it, and the named-fn exclusion belongs to this PR's design rather than to me — reapply it as your own commit if you agree with it, drop it if you do not. A git revert also works if you would rather keep the history visible, but the rebase is cleaner given you need one anyway.

I have not force-pushed it away. Would like to not get in the habit of writing to your branches.

On review state: my changes-requested is stale, not standing. You reworked this and force-pushed on 31 July, then replied point by point covering four items — inert without #650, coverage, strict mode, and dropping chunk-emit-pop-n — and I have not re-reviewed since. I will clear it or renew it once I have read the rework properly, and this commit does not count toward any of it either way.

@nnunley
nnunley force-pushed the salvage-ir-def-seam branch from 25261cd to 5a80eaa Compare August 4, 2026 01:12
@nnunley
nnunley requested a review from mparrett August 4, 2026 01:14
@nnunley

nnunley commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

Status Update: All Round-1 Items Addressed

Rework completed on 2026-07-31 covers all four original review points:

  1. Inert without perf(ir): retain *runtime-defn-ir-cache* for only the current ns #650 — ✅ FIXED

    • Moved compile-def-fn-value defn into this PR (pkg/rt/core/ir/passes/pipeline.lg)
    • Hook now fires when branch built independently
  2. Coverage / What exercises the seam — ✅ FIXED

    • Added test/ir_def_namestar_seam_test.lg with three deftests
    • Tests coverage using name*-wrapped stubs (like pkg/ir test stubs)
    • Load-bearing assertion: multi-arity strict-mode throw pins seam execution
  3. Strict mode — ✅ FIXED

    • Seam now honors *ir-compile-strict*
    • Mismatch failures return error to defCompiler (bytecode census sees miss)
    • Non-applicable paths still silent, maintaining hybrid behavior
  4. Unused chunk-emit-pop-n — ✅ FIXED

    • Bridge decl and generated lines removed

Current state:

@nooga nooga left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. All three blockers from the 07-30 review are fixed: the seam is now self-contained (compile-def-fn-value lives in this PR, no dependency on other open PRs), the "can this even be exercised" concern is answered with a non-vacuous strict-mode throw test, and *ir-compile-strict* is honored on every early-return path.

Verified:

  • go build ./... clean, merges cleanly with current main locally (see note below)
  • go test ./pkg/ir/... ./pkg/compiler/... ./pkg/rt/... and the new test/ir_def_namestar_seam_test.lg (5 deftests / 7 assertions) all pass
  • Confirmed no duplication vs. #598/#661 — reuses ir.build/return-hinted-arity-vector? / unwrap-arity-vector

One nit worth a one-line fix: in compile-def-fn-value (pipeline.lg:773-783), when the arg vector carries a return-type hint (^long [n]), the code builds defn-form from the unwrapped vector rather than the original hinted one — unlike the main defn-compile path, which keeps the hint and lets build-fn strip it internally. Nothing consumes that tag on the bytecode path today so this is inert, but it'd silently diverge from the main path's behavior the moment something does.

Also: GitHub shows this as CONFLICTING against main, but a local merge resolved with zero real conflicts — just needs a rebase push to refresh GitHub's mergeability check before landing.

@nnunley
nnunley force-pushed the salvage-ir-def-seam branch from 5a80eaa to d47ac24 Compare August 4, 2026 23:39

@mparrett mparrett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed d47ac240. All four points from my 07-30 review are addressed, and my stale CHANGES_REQUESTED was the only thing still blocking this — clearing it now.

  • Inert without #650 — fixed. compile-def-fn-value is defined here (pipeline.lg:764), so the hook fires when the branch is built alone.
  • Nothing exercises the shape — fixed. test/ir_def_namestar_seam_test.lg, five deftests.
  • Strict mode — fixed, and better than what I asked for. The seam separates a shape mismatch, which stays silent because it is not a miss, from a matched-but-unlowerable form, which errors so the #580 census can see it. The comment at compiler.go:47-51 states that distinction outright.
  • Unused chunk-emit-pop-n — removed, along with the generated lines behind it.

The coverage answer holds up under falsification

My hardest objection was whether this seam can be exercised against main at all. The test's argument is that a multi-arity name*-wrapped fn must throw under *ir-compile-strict*, and that a silent compile would prove the seam never ran. I checked that directly by stubbing maybeIRCompileDefFnArg to return immediately:

  • seam enabled: Tests: 5 Pass: 7 Fail: 0 Error: 0
  • seam stubbed off: Tests: 5 Pass: 4 Fail: 1 Error: 1

So the assertion is load-bearing rather than decorative. All five deftests register and run.

On the outstanding nit

The defn-form-from-unwrapped-vector point is already fixed: the current code passes args-vec, the hinted original, and uses unwrapped only for the variadic? and arity computation. That landed in d47ac240, after the approving review, so the review reads stale in the favorable direction.

No findings

Two things I checked and am not raising. The docstring in compile-def-fn-value sits after the arg vector, which makes it an inert expression rather than a docstring — but seed-inline-registry-from-cache (:58) and realize-specialized-fold! (:135) in the same file do the same, so this is the file's existing convention rather than a defect in this change. And make generate dirtied the digest in my worktree only because I had rebased; on the pushed head generated-artifacts is green.

Before landing

It is CONFLICTING only because five PRs landed on main today. A local rebase applied with no real conflicts, but make generate was needed afterward — committing that regenerated digest is the step to not skip, since omitting it is what is currently reding CI on #675.

@nnunley
nnunley force-pushed the salvage-ir-def-seam branch from d47ac24 to 9baf6fc Compare August 7, 2026 17:22
nnunley and others added 3 commits August 7, 2026 13:23
….)) via the IR path

Extracted from nooga#625 [1/4]. Adds the compiler.go seam that routes a
top-level single-fn def through *ir-compile* (top-level-only, single-fn,
silent fallback, FormSource preserved) plus the ir_bridge.lg hook.
The seam must unwrap return-hinted arity vectors (with-meta forms) before
checking for single-arity. This aligns with nooga#661's b170a08 fix for the
same issue in the pipeline's defn compilation path.

Fixes: multi-arity detection failure on (def x (name* ... (fn ^T [args] ...)))

Tests: go test ./pkg/ir passes; check-generated passes
@nnunley
nnunley force-pushed the salvage-ir-def-seam branch from 9baf6fc to 609c8a9 Compare August 7, 2026 17:34
@nnunley
nnunley merged commit 6435fb7 into nooga:main Aug 7, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants