Skip to content

Rebind self in Class.new blocks; nested blocks inherit rebound binders - #1310

Draft
apiology wants to merge 1 commit into
castwide:masterfrom
apiology:classnew-yieldreceiver
Draft

Rebind self in Class.new blocks; nested blocks inherit rebound binders#1310
apiology wants to merge 1 commit into
castwide:masterfrom
apiology:classnew-yieldreceiver

Conversation

@apiology

Copy link
Copy Markdown
Contributor

At strong typecheck, calls that Ruby resolves fine inside Class.new blocks are flagged:

Class.new do
  define_method(:foo) { nil }                     # Unresolved call to define_method
  %i[a b].each { |m| define_method(m) { nil } }   # same, one block deeper
end

Two changes:

  1. Core fill: Class#new gets @yieldreceiver [::Class]. RBS records this binding only on Class#initialize ([self: Class], rbs >= 4.1); Class#new — the method actually resolved for Class.new { ... } — is (*untyped, **untyped) -> untyped in every RBS version, so a [self: ...] translator fix would not reach this call site. The runtime type is parameterized by the superclass argument, which @yieldreceiver can't express, so the fill matches RBS's own unparameterized Class.
  2. Pin::Block#rebind/#binder cascade: a block with no rebind of its own inherits its enclosing block's rebound binder instead of its statically-parsed context. Blocks don't change self; without this, every existing fill (class_eval, instance_eval, ...) stops applying one nesting level down.

Three new strong-level specs (direct, .each-nested, class_eval-nested).

🤖 This PR was written by Claude (Claude Code), operated by @apiology.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT

Two changes so strong typecheck resolves define_method (and any other
Module method) inside Class.new blocks:

1. Core fill: Class#new gets @yieldreceiver [::Class]. RBS records the
   binding only on Class#initialize ([self: Class], rbs >= 4.1);
   Class#new, the method actually resolved for Class.new-with-a-block,
   is (*untyped, **untyped) -> untyped in every RBS version, so a
   translator fix for [self: ...] would not reach this call site.

2. Pin::Block#rebind/#binder cascade: a block with no rebind of its own
   inherits its enclosing block's rebound binder instead of falling back
   to its statically-parsed context. Matches Ruby semantics (a block
   does not change self) and makes the existing class_eval/instance_eval
   fills work below one level of block nesting.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
apiology added a commit to apiology/solargraph that referenced this pull request Aug 17, 2026
…er inheritance

# Conflicts:
#	spec/type_checker/levels/strong_spec.rb
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