fix(vm): warn-on-core-shadow never fired for compiled defs - #734
Conversation
mparrett
left a comment
There was a problem hiding this comment.
The mechanism matters beyond parity: this warning is the guard against a generated //lg:native def shadowing a fast-path closure — the exact shape of the #656 reduce regression — and compiled defs are where those shadows originate, so the check being dead there was the guard's blind spot. The factoring keeps all six suppression guards and the warn-once-per-ns behavior, LookupOrAdd is compile-time-only so there's no dispatch-path cost, and the edn :refer-clojure :exclude matches the io/slurp precedent.
One finding, P2, non-blocking: the harness still doesn't run in CI. go.yml invokes go test directly in every job and never runs make test, so shadow-warning-test — now wired into make — remains un-executed exactly where "nothing ran it" bit before. Either add a step to go.yml (it already carries bespoke steps for the e2e lowering tests, so this matches the pattern) or wrap the harness in a Go test that shells out to run.sh so ./... picks it up. As written, a future regression in the warning would again pass CI silently.
Performance: branch vs mainThe ratchet's baseline (
Branch measured against
Allocation counts are identical between control and branch — Interleaved A/B
Every family median lands within ±0.8% (largest: Limits of the above
Separately, |
a252d97 to
82f95da
Compare
|
Updated at |
82f95da to
356b325
Compare
mparrett
left a comment
There was a problem hiding this comment.
Approved. The core-shadow warning fix behaves as intended, and the focused harness, short package suite, VM race tests, vet, and generated-artifact checks pass.
Non-blocking P2 follow-up: test/namespace_shadow_warning_harness_test.go unconditionally executes Bash. On Windows or Plan 9, go test ./... will fail before exercising the warning behavior. Please consider implementing the assertions in Go or guarding/skipping the wrapper when Bash is unavailable.
The branch still needs rebasing because the generated manifest files currently conflict with main.
|
@nnunley — approved from my side. It has picked up conflicts with |
|
Following up on my rebase note, because "conflicts" undersells what is happening and I would rather you did not chase it. I fetched this head and ran GitHub does not run custom merge drivers on its server-side merge, so this PR will read CONFLICTING again the next time So the rebase is still worth doing to get the merge box green, but treat the red as an artifact of the driver gap rather than a signal about the change. Same picture on #781. |
nooga
left a comment
There was a problem hiding this comment.
LGTM, mparrett's approval stands and his CI-coverage nit is fixed in the follow-up commit. Only the generated manifest conflicts. Merging after a rebase.
The check in Namespace.Def compared against clojure.core's refers and printed the Clojure-parity warning, and its own comment described user code using the default (ns ...) form as the case it existed for. That case never reached it. The def special form interns through Namespace.LookupOrAdd (pkg/compiler/compiler.go:1925), not through Def, so every compiled (def ...) and (defn ...) bypassed the warning entirely. Only Go-side ns.Def callers ever tripped it — the inverse of what the comment claimed. Factor the check into warnOnCoreShadow and call it from both intern paths. The guards are unchanged: excluded, unmapped, suppressShadowWarn, core itself, private core vars, and a name this namespace already owns. Turning it on surfaced one unsuppressed intentional shadow: edn/read-string shadows clojure.core/read-string, which is the point of the namespace. Declared with (:refer-clojure :exclude [read-string]), matching how io/slurp and io/spit are handled on the Go side. test/namespace_shadow_warning_test/run.sh asserts on stderr, so it cannot live in the Go table tests. Nothing ran it — not make, not CI — so its first case had been failing silently. Wired into `make test` as shadow-warning-test. Verified: 5/5 harness cases pass, ./pkg/... and ./test pass, and no stray WARNING appears anywhere in that run.
356b325 to
c68e348
Compare
Summary
The warn-on-core-shadow check has never fired for user code. Every compiled
(def …)and(defn …)bypassed it.Stacked on #733.
The defect
Namespace.Defcompares the name against clojure.core's refers and prints the Clojure-parity warning. Its own comment describes the case it exists for:That case never reaches it. The
defspecial form interns throughNamespace.LookupOrAdd(pkg/compiler/compiler.go:1925), not throughDef, so only Go-sidens.Defcallers ever tripped the warning — the inverse of what the comment claimed.Observable before this change:
Fix
Factor the check into
warnOnCoreShadowand call it from both intern paths. Guards are unchanged: core itself,suppressShadowWarn,:exclude, unmapped, private core vars, and a name this namespace already owns. InLookupOrAddthe call sits on the interning branch only, so a repeated def of our own var stays silent.What turning it on surfaced
One unsuppressed intentional shadow, during boot:
edn/read-stringshadowingclojure.core/read-stringis the point of that namespace. Declared with(:refer-clojure :exclude [read-string]), matching howio/slurpandio/spitare handled throughns.Excludeon the Go side. Consumers are unaffected:(:require [edn :as e]),(:require [edn :refer [read-string]]), and:refer-clojure :excludeplus:referall resolve as before.Why it went unnoticed
test/namespace_shadow_warning_test/run.shasserts on stderr, so it cannot live in the Go table tests. Nothing ran it — notmake, not CI — so its first case had been failing silently. It is nowmake shadow-warning-test, invoked frommake test.Validation
go test ./pkg/... ./testpasses, with zero strayWARNING:lines anywhere in the run.ednchange;make check-generatedclean.