Skip to content

feat(vm): Module/Class residual surface — const_get/const_defined? paths, const_missing, included_modules, remove_class_variable - #341

Merged
tannevaled merged 2 commits into
mainfrom
feat/module-residuals
Aug 9, 2026
Merged

feat(vm): Module/Class residual surface — const_get/const_defined? paths, const_missing, included_modules, remove_class_variable#341
tannevaled merged 2 commits into
mainfrom
feat/module-residuals

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Horodate: 2026-08-09 21:12 CEST

Completes the residual Module/Class reflection surface toward MRI 3.4/4.0, verified against ruby 4.0.5.

What's implemented

  • Module#const_get / #const_defined? — reworked to MRI semantics:
    • accept a String, Symbol or #to_str-coercible name;
    • resolve scoped "A::B" paths and a leading "::" toplevel qualifier;
    • honour the inherit flag (default true, coerced to boolean) — inherit searches the ancestor chain and, for a first segment, falls back to the toplevel; inherit=false consults only the receiver's own table;
    • reject malformed names (NameError "wrong constant name", incl. empty/successive :: segments) and scoping into a non-module (TypeError "… does not refer to class/module");
    • const_get routes an unresolved name through #const_missing; const_defined? reports a pending autoload as defined without triggering the require.
  • Module#const_missing — default hook raising NameError (omitting the Object:: prefix at the toplevel, per MRI), carrying the name so NameError#name reports it. The :: scope operator now fires the hook, so user overrides (including a private_class_method :const_missing) apply.
  • Module#included_modules — the modules (includes + prepends, transitively) in the ancestor chain, excluding the receiver.
  • Module#remove_class_variable — removes a class variable defined directly on the receiver and returns its value; NameError on a malformed or absent name.

Deferred (blocked by separate, pre-existing bugs — out of this scope)

  • const_source_location — needs per-constant source-location tracking threaded through constant assignment (invasive; no location is stored today).
  • const_get/const_defined? "searches the superclass chain" via a module mixed into Object at the toplevel, and const_defined? "special cases Object" — blocked by top-level Kernel#include not including into Object (Object.include?(M) is false; M is absent from subclasses' ancestors).
  • remove_class_variable "returns the value …" spec — blocked by Module#dup not copying class variables.
  • const_defined? EUC-JP-encoded constant name — encoding edge.

Verification

  • ruby/spec core/module deltas: const_get 26→42, const_defined? 19→29, const_missing 1→5, included_modules 0→1, remove_class_variable 5→7.
  • ruby/spec ratchet: 12284 passing (was floor 12216); FLOOR bumped to 12254 (N-30).
  • GOWORK=off go test ./... — pass (exit 0), no regressions.
  • Whole-package coverage gate (-coverpkg all internal/...): every added/touched function (incl. modified scopedConst, NameError#name) is 100%; the non-100% list is exactly the pre-existing tolerated partials.
  • gofmt clean; go vet ./internal/vm/ clean.

New table-driven tests in internal/vm/module_residuals_test.go.

🤖 Generated with Claude Code

tannevaled and others added 2 commits August 9, 2026 20:45
…g, included_modules, remove_class_variable

Horodate: 2026-08-09 20:55 CEST

Rework Module#const_get / #const_defined? to MRI 3.4/4.0 semantics and add the
default Module#const_missing hook, Module#included_modules and
Module#remove_class_variable, plus NameError#name.

- const_get / const_defined?: accept a String, Symbol or #to_str object; resolve
  scoped "A::B" paths and a leading "::" toplevel qualifier; honour the inherit
  flag (default true, coerced to boolean); reject malformed names (NameError) and
  scoping into a non-module (TypeError). const_get routes an unresolved name
  through #const_missing; const_defined? reports a pending autoload without
  triggering the require.
- const_missing: default hook raising NameError (no "Object::" prefix at the
  toplevel), carrying the name so NameError#name reports it. The `::` scope
  operator (scopedConst) now fires the hook, so user overrides apply.
- included_modules: the modules (includes + prepends, transitively) in the
  ancestor chain, excluding the receiver.
- remove_class_variable: removes a class variable defined directly on the
  receiver and returns its value; NameError on a malformed or absent name.

Verified against ruby 4.0.5. ruby/spec core/module deltas: const_get 26->42,
const_defined? 19->29, const_missing 1->5, included_modules 0->1,
remove_class_variable 5->7. New table-driven tests in
module_residuals_test.go.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Horodate: 2026-08-09 21:10 CEST

The Module/Class residual work (const_get/const_defined? paths + inherit +
#to_str + #const_missing, included_modules, remove_class_variable, NameError#name)
lifts ruby/spec to 12284 passing. Lock in at N-30 (12254) per convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 59d9ec9 into main Aug 9, 2026
5 of 7 checks passed
@tannevaled
tannevaled deleted the feat/module-residuals branch August 9, 2026 19:22
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.

1 participant