Repository navigation
fix(typecheck): follow the kernel's drivers, and fall back loudly when it diverges - #97
Merged
Merged
Conversation
…n it diverges On ShenOSKernel 43.1 with the typecheck deref hoist, the translated shen.by-hypothesis called the new shen.by-hypothesis-h, which was not in the hard-coded DRIVER_FNS. NP's lazy lookup returned nil, and the call raised a plain Lua error mid-search. typecheck_dispatch rethrew everything but NATIVE_MISS, trap-error turned the Lua error into an ordinary Shen exception, and callers that trap typecheck errors (capcov's judgements, the fail-closed shen.typecheck Lua API) read it as "does not typecheck". Every typecheck failed and the process exited 0. - The driver set is now the closure of DRIVER_FNS under calls by name. The translator records each predicate a translated function calls, and any t-star.kl defun a driver calls is translated too. A call to anything that is neither a t-star.kl defun nor a hand port is reported before the first query. - Kernel conformance: the defuns the port reimplements rather than translates are fingerprinted (gensym-insensitive) against the versions it implements. These are the t-star hand ports and the prolog.kl primitives and builtin goals. The known set is 43.1 and 43.1+deref-hoist; scripts/kernel-fingerprints.lua regenerates it. - On any such problem, typechecks use the kernel's own KL typechecker, and stderr names the cause once. SHEN_TYPECHECK_NATIVE=require raises instead. - Call sites resolve through (NP[name] or MISS(name)). A predicate missing at run time raises a distinct sentinel, and the typecheck reruns the query on the KL engine, as it already did for untranslatable datatypes. g_call uses the same sentinel. test/kernel_conformance_spec.lua runs a typecheck battery on the vendored kernel, the deref-hoisted kernel (fixture, sha256 a0cedbb0...), and three deliberately mismatched kernels. Each run is compared with the kernel's KL typechecker. On the parent commit, 14 of its 23 checks fail: the hoisted kernel answers false to every typecheck. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
28 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #96 (base
perf/eqterm-native-equality). After #96 merges, retarget this PR tomain.What went wrong
Loading ShenOSKernel 43.1 with the typecheck deref hoist (shen-sources
perf/typecheck-deref-hoist, 5596445) gave this:"ok":false.The kernel split
search-user-datatypesandby-hypothesisinto a deref-once wrapper plus a new Prolog predicate (-h). Two separate faults turned that into wrong answers:typecheck_native.luatranslated a fixedDRIVER_FNSlist. The translatedshen.by-hypothesisemittedNP["shen.by-hypothesis-h"](...).NP's lazy__indexfound no registry entry and returned nil, so the call raised a plain Lua error in the middle of the search.typecheck_dispatchfell back to the KL typechecker only onNATIVE_MISSand rethrew everything else.trap-errorturns any Lua error into an ordinary Shen exception (TOEXCN).(trap-error (if (= false (shen.typecheck …)) cc.refused cc.derived) (/. E … cc.errored)), so the result was recorded as not ok. This port's own Luashen.typecheckAPI also "fails closed" and returnsfalseon any error.The fix: follow the kernel where we translate, verify it where we reimplement
Derived driver set.
NP[...]emit sites).ensure_driverstranslates the closure ofDRIVER_FNSunder those calls. Anyt-star.kldefun that a driver calls is translated too. On the hoisted kernel this addsshen.by-hypothesis-hwith no list edit.t-star.kldefun nor a hand port is reported before the first query.Kernel conformance fingerprints. Some kernel defuns the port reimplements rather than translates:
shen.typecheck,lookupsig/sigf,search-user-datatypes(+-h),showandmaxinfexceeded?;prolog.klprimitives and builtin goals that the translator and engine implement natively.A kernel change to any of these would silently keep the old semantics, so each one is fingerprinted at first typecheck and compared with the versions the port implements. The fingerprint hashes the defun's KL form with gensym variables renamed in first-occurrence order, so regenerating a kernel does not change it. The known set is 43.1 and 43.1+deref-hoist.
scripts/kernel-fingerprints.luaregenerates it, and adding a kernel there is an explicit claim that the native code implements it.Fallback policy.
shen-lua: native typechecker unavailable, using the kernel's KL typechecker: kernel defun shen.search-user-datatypes (t-star.kl) does not match ….SHEN_TYPECHECK_NATIVE=requireraises an error instead, for CI and kernel bring-up.Runtime net. Call sites are now
(NP[name] or MISS(name))(...), andg_calluses the same path. A predicate missing at run time raises a distinct sentinel,E.native_miss, nevernil(...). The typecheck entry recognises it and reruns the query on the KL engine, as it already did for untranslatable datatypes. The cost is within noise; see the timing table below.Why fingerprints and a derived closure, rather than one or the other
With both:
Tests
test/kernel_conformance_spec.luaboots a child per kernel, withSHEN_KL_DIRset and the kernel and fasl caches off. It runs a 10-typecheck battery: lambdas, pairs, higher-order, hypotheses, expected failures, and a datatype with a verified premise. It compares the verdicts with the same battery on the kernel's KL typechecker (SHEN_TYPECHECK_NATIVE=off) and with fixed expectations.by-hypothesisusesEQTERMtest/fixtures/kernel-43.1-deref-hoist/t-star.kl, sha256a0cedbb0…)shen.by-hypothesis-h, which usesEQTERM; passes underrequiresearch-user-datatypeschanged to never consult datatypesrequireraisesby-hypothesis-hmoved out oft-star.klOn #96 alone, 14 of the 23 checks fail. The hoisted kernel answers
falseto all 10 typechecks, and the mutated kernel gets the old semantics without a word.Gates
make testrun-kernel-tests.luab-typed CPU
Measured with
/usr/bin/time -lon the binary, 2 interleaved rounds (r1 base→A→AB→AB-hoist, r2 reversed), payload identical in every run.This PR costs nothing measurable: instructions are within ±1% of #96. The hoist buys shen-lua nothing either, because the native engine never copies on deref.
Other kernels
Before this PR, any kernel ran natively as long as translation succeeded. Now a kernel whose reimplemented defuns aren't in
KNOWNruns on the KL typechecker, which is correct but 4-5x slower on typecheck-heavy work, and prints a notice. I checked the kernels this repo has vendored withscripts/kernel-fingerprints.lua:1a58b83:klambda): every reimplemented defun fingerprints identical to 43.1, so it still runs natively.ea867d7^:klambda): differs inshen.pvar?only, so it would now fall back with a notice. I have not added it toKNOWN, because that requires gating the native engine against S42 first.Residual risk (not addressed here)
prims.luaalso overridesprolog.kl'sshen.lazyderef,shen.bindv,shen.bind!and related ops natively, and those overrides are used by both paths. The legacy fallback therefore cannot protect against a kernel change there. The fingerprints do cover those defuns, but a mismatch would only switch the typechecker to a legacy path that still runs the overridden ops.🤖 Generated with Claude Code