limitations.md: document Class#initialize re-invocation difference

mruby's Class#initialize accepts re-invocation through __send__
silently, while CRuby raises TypeError. The superclass argument is
ignored on re-invocation, so no destructive side effect is possible.
Reported on Twitter by cacao_soft.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-05-12 22:33:09 +09:00
parent 82de0b0ee5
commit 27e14c16c4
+34
View File
@@ -404,3 +404,37 @@ LocalJumpError # always raised — the dup is orphan from the moment
mruby's stricter rule keeps `RProc` from needing a back-pointer to
the original block (which would also enlarge the GC mark set).
## `Class#initialize` Can Be Re-Invoked
CRuby raises `TypeError: already initialized class` when `initialize`
is invoked on a class that has already been set up. mruby's
`Class#initialize` has no such guard — invoking it on an existing
class through `__send__`, `send`, or `UnboundMethod#bind_call`
silently succeeds. The superclass argument is ignored in this case,
so the call cannot rewrite the class hierarchy; only the block (if
any) is evaluated with the class as receiver.
```ruby
Klass = Class.new
Klass.__send__(:initialize) {}
```
#### CRuby
```
TypeError: already initialized class
```
#### mruby
```
The block is evaluated in the context of Klass; no error is raised.
The superclass is not changed even when one is passed as an argument.
```
`Module#initialize` is re-callable in both implementations, so this
divergence is `Class`-specific. Adding the CRuby check would require
an additional flag bit on every `RClass`; mruby leaves the bit
unspent because no destructive side effects are possible through
this path.