Repository navigation
Gate real File/Pathname/Dir methods instead of a blanket method_missing (fixes #29, #31) - #54
Open
sam-xif wants to merge 1 commit into
Conversation
#31) The prelude's `File`/`Pathname` carried a blanket `method_missing` (and `respond_to_missing? => true`) that turned *every* unmodeled call into a named `Unsupported` gate. That answered a name CRuby does not define with a gate instead of a genuine `NoMethodError` — a fidelity defect (#29) — and `Dir`, which has no guard at all, mis-raised `NoMethodError` for real class methods like `mkdir`/`glob`/`pwd` (#31). Draw the line the way the model draws it everywhere else: a *real* unmodeled method gates (`unmodeled singleton method File.mtime`), an invented name reaches the native `NoMethodError`. That needs the dispatch-fidelity tables to know the real method set of `File`, `IO`, `Dir`, `FileTest` and `Pathname`, so add those classes to the generator's `FOLD` and regenerate `CRubyNames.lean`. `File < IO`, so inherited class methods are found by the shadow's ancestor walk; no folding is needed. The prelude drops the two blanket guards and lets ordinary lookup decide. The checker's compact projections (`nativeQueryRows`, `nativeSingletonNew`) and its `nativeInstanceNames` set are the copies of those tables; extend them to the new names so `NativeGuards`/`NativePrefix` still prove coverage. The additions only make the checker refuse more names, so soundness is preserved. Also correct the `Boot.lean` comment that described the removed guard.
This was referenced Nov 7, 2026
Open
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.
Summary
Fixes #29 and #31: the prelude's blanket
method_missingguards forFile/Pathname(and the complete absence of one forDir) answered every unmodeled call with a namedUnsupported, including names CRuby does not define at all. That is a fidelity defect — a gate for a non-method — andDir's real class methods (mkdir,glob,pwd, …) mis-raisedNoMethodError, which is inside the type-error family the validator claims to exclude.The model already draws the correct line in dispatch: a real unmodeled method gates (
unmodeled singleton method File.mtime), an invented name reaches the nativeNoMethodError. It only lacked the data — the generatedcrubySingletonNames/crubyMethodNamestables had no entries forFile,IO,Dir,FileTestorPathname.Stacked on #21 (VFS step 1). Base branch is
nori/vfs-step1-file-read-write; the diff below is this fix only.Closes: #29, #31.
What changed
Generator (
ruby-lean/scripts/gen_cruby_names.rb)IO,File,Dir,FileTest,PathnametoFOLDso their real method names enter the dispatch-fidelity tables.File < IO, so inherited class methods (read/write/open) are found by the shadow's ancestor walk — no folding needed.Pathnameis stdlib and CRuby 4.0 boots it, sorequire "pathname"makes the constant resolvable.Generated (
RubyCore/Generated/CRubyNames.lean)crubyMethodNames,crubySingletonNamesandcrubyNamespaceConstants; every pre-existing entry is byte-for-byte unchanged.Prelude (
ruby-lean/prelude/prelude.rb)Pathname#method_missing/#respond_to_missing?andFile.method_missing/File.respond_to_missing?. Ordinary lookup now decides: real method → namedUnsupported(viacrubySingletonShadow/crubyShadow), invented name → nativeNoMethodError.Checker projections (
books/)NativeGuards.nativeQueryRowsgains("Pathname", ["===", "to_s"])andnativeSingletonNewgains"IO".NativeInstanceNames.nativeInstanceNamesgains the 142 instance-method names the new classes introduce, soNativePrefix's coverage proof still holds. The additions only make the checker refuse more names, never fewer — soundness is preserved.Boot.lean: the comment that described the now-removed guard is corrected.Before / after
File.zzz_does_not_existNoMethodError: undefined method 'zzz_does_not_exist' for class FileNoMethodErrorFile.mtime("/x")File.mtime (only the pure path operations are modeled)unmodeled singleton method File.mtimePathname("/x").readlinkunmodeled method Pathname#readlinkPathname.new("x").zzzNoMethodError: undefined method 'zzz' for an instance of PathnameNoMethodErrorDir.mkdir("/tmp/d")NoMethodError: undefined method 'mkdir' for class Dirunmodeled singleton method Dir.mkdir0(creates it)Dir.zzz_inventedNoMethodErrorNoMethodError: undefined method 'zzz_invented' for class DirNoMethodErrorTest plan
Validated locally with a full toolchain built in this environment (Lean
v4.32.2fromlean-toolchain; CRuby4.0.5core; the same generator pipeline CI uses):rubycorebuilds:lake build rubycore— success.Books.TypeSoundness.Conformance.Names.NativePrefix,.Names.NativeGuards,.Class.ClassQueriesActual— thequery_covered/singleton_coveredproofs still hold after widening the tables;nativeQueryFreeB_sounddepends on[propext, Quot.sound]as before.CRubyNames.leanis exactly whatmake gen-checkwill regenerate: I regenerated the core tables with a local CRuby 4.0.5 and confirmed (a) every pre-existing entry is byte-identical to the committed file and (b) the added entries are exactly the new classes'. The feature section is unchanged, byte-for-byte.PreludeJson.lean/Prelude.leanare fresh: regenerated withscripts/gen_prelude.rb+genpreludeandcmp-equal to the committed files.difftest/corpus/regressions/through the model before and after this change; the observations are identical (0 differing files). The change only affects the invented-name branch the corpus never reaches.CI runs the full
make check(conformance against CRuby, difftest, proofs, comparator) on this PR.Notes
File.respond_to?(:mtime)now gates (it is a native singleton method), matching the model's existing rule for e.g.Integer.respond_to?(:sqrt), where CRuby answerstruebut the model declines rather than guess visibility. That is the model's documented choice, not a new behavior.