Repository navigation
Gate real File/Dir/IO/Pathname methods and reject invented ones - #96
jiro-by-nori[bot] wants to merge 1 commit into
Conversation
The prelude installed blanket `method_missing` guards on `File` and `Pathname`. They turned every miss into a named `Unsupported`, including names CRuby does not define (`File.zzz`), where the honest answer is `NoMethodError`. The inverse held for `Dir` and `IO`: they were absent from the generated singleton table, so real, unmodeled class methods such as `Dir.glob` and `IO.binread` raised `NoMethodError` — inside the very exception family `validateD` promises to exclude — instead of gating. Generate the method tables for `IO`, `File`, `Dir`, `FileTest` and the booted `Pathname` surface, and drop the blanket guards. Ordinary dispatch then draws the line the model draws everywhere else: a real unmodeled method gates (the ancestor walk covers names `File` inherits from `IO`), and an invented name reaches the native `NoMethodError`. `Pathname` is recorded at the booted `pathname.so` surface, not the `pathname.rb` wrapper the default environment does not load. The checker's projection of the instance-name table is updated to match, since it is kernel-checked against the generated tables. Fixes #29. Also fixes the wrong-answer half of #31: real `Dir` methods now gate and invented ones are `NoMethodError`. `Dir.empty?`/`Dir.children` remain named gates until the VFS step-3 work lands. Authored by Jiro, an autonomous agent. Co-authored-by: Nori <contact@tilework.tech>
There was a problem hiding this comment.
Reviewed head e6cba42.
Verdict: cannot approve — 1,047 changed lines against max_changed_lines = 500. Size-limit
block, not a defect finding. I read the change; it is coherent, but the automated approver is not
allowed to approve at this size.
Where the lines go
| File | Lines |
|---|---|
ruby-lean/RubyCore/Generated/PreludeJson.lean |
+333 / −342 |
books/Books/TypeSoundness/Checker/Static/NativeInstanceNames.lean |
+90 / −70 |
ruby-lean/RubyCore/Generated/CRubyNames.lean |
+76 / −1 |
ruby-lean/RubyCore/Generated/Prelude.lean |
+2 / −58 |
ruby-lean/prelude/prelude.rb |
+22 / −26 |
ruby-lean/scripts/gen_cruby_names.rb |
+22 / −1 |
books/Books/TypeSoundness/Checker/Guards/NativeGuards.lean |
+2 / −2 |
~972 lines are regenerated (the three Generated/ files plus the checker's oracle-derived name
list); the hand-authored delta is ~75 lines, of which the substantive part is the removal of the
blanket Pathname#method_missing/respond_to_missing? and File.method_missing/
respond_to_missing? guards, plus the IO/File/Dir/FileTest/Pathname entries in
gen_cruby_names.rb.
Why the generated churn is this large. PreludeJson.lean is one fixed-width-wrapped string
literal of the prelude AST; removing ~8 lines from the middle of a class re-wraps the rest of the
file. The reflow is the generator's, not this PR's, but it makes the limit unreachable for any
prelude-touching change.
Checks observed at this head: all seven contexts green — CI, checks not weakened,
conformance, lint, docs, front-end, proofs. No unresolved review threads. No human-gated
path touched (ruby-lean/scripts/gen_cruby_names.rb is not covered by scripts/**, which matches
only the repo-root scripts/).
Validation: none run locally — no checkout or toolchain in this session. make gen-check,
make conformance, make books and make soundness were not run; generated freshness and the
checker's build rest on the green checks not weakened, conformance and proofs contexts plus
the PR's reported runs.
On the change itself: replacing the blanket method_missing with ordinary lookup is the right
move under AGENTS.md rule 3 (a gate for a name CRuby does not define is a wrong answer, not a
refusal). Recording the booted pathname.so surface rather than the required pathname.rb one in
gen_cruby_names.rb is the right line to draw, and the comment there says so. Nothing weakens an
existing check.
What is needed (@sam-xif): the same owner decision as on #103 — either waive the changed-line
limit when every oversized file is a regenerated source and make gen-check passes, or make
PreludeJson emit stable per-node lines so the tail stops reflowing.
Summary
Fixes #29, and the wrong-answer half of #31.
The prelude installed blanket
method_missingguards onFileandPathname. Every miss became a namedUnsupported, including names CRuby does not define:DirandIOwere the inverse: they had no entry in the generated singleton table, so real, unmodeled class methods raisedNoMethodError, which is inside the exception familyvalidateDis meant to exclude:This change deletes the blanket guards and records the real method surfaces in the generated tables, so ordinary dispatch draws the line the model draws everywhere else:
unmodeled singleton method File.mtime,unmodeled method Pathname#readlink);NoMethodError, byte-for-byte with CRuby.What changed
ruby-lean/scripts/gen_cruby_names.rb: addIO,File,Dir,FileTest, andPathnameto the generated name tables.Pathnameis recorded at the bootedpathname.sosurface, not thepathname.rbwrapper (the default environment does not loadfind/rmtree/mktmpdir; the model'spathnamefeature adds nothing at require time either).ruby-lean/prelude/prelude.rb: remove the blanketmethod_missing/respond_to_missing?fromFileandPathname; update the comments to state how a miss is now classified. No modeled method changed.CRubyNames.lean,Prelude.lean,PreludeJson.leanwithmake(no hand edits).books/Books/TypeSoundness/Checker/Static/NativeInstanceNames.lean: extend the checker's projection of the instance-name table, which is kernel-checked against the generated tables (instance_covered).books/Books/TypeSoundness/Checker/Guards/NativeGuards.lean:Pathname#to_s/===are native queries andIOdefines singletonnew.This also turns the constructor wrong answers into honest gates:
File.new,Dir.newandIO.newused to reachObject#initializeand raiseArgumentError; they now gate on the unmodeledinitialize(unmodeled builtin would shadow: File#initialize). CRuby raisesErrno::ENOENT/TypeErrorthere, which the model does not yet model.Dir.empty?/Dir.childrenremain named gates until the VFS step-3 work lands onmain; this PR does not model them.Test plan
Targeted probes, each compared against CRuby 4.0.5 with
bin/ruby-lean --compare:File.zzz_does_not_existNoMethodError(AGREE)NoMethodErrorDir.zzzNoMethodError(already)NoMethodError(AGREE)NoMethodErrorFile.mtime(...)Dir.glob("*")NoMethodErrorPathname.new("/x").readlinkPathname.new("/x").zzzNoMethodError(AGREE)NoMethodErrorPathname.new("/x").findNoMethodError(AGREE)NoMethodError(no require)Pathname.mktmpdirNoMethodError(AGREE)NoMethodError(no require)File.basename/File.join/Pathname#to_s/#subFull suite (all on the final tree):
make check→make check: ALL CHECKS PASSEDmake conformance→CONFORMANCE OK;ran 1287,agree 1087,sut_unsupported 194,disagree 0, regressions and tier3 agree with CRubybooks/scripts/check-soundness.sh→SOUNDNESS CHECK PASSED(proofs, controls, and corpus;accepted.txtunchanged)make metatheory book-checks feature-loading difftest-test→ passmake gen-check→ all generated sources freshmake check-not-weakened→CHECKS NOT WEAKENEDNo recorded result or baseline was changed.
Notes
Authored by Jiro, an autonomous agent. The code, commit, and this description were produced by Jiro; they have not been reviewed by a human before opening.