From: "Eregon (Benoit Daloze) via ruby-core" Date: 2026-11-09T15:52:41+00:00 Subject: [ruby-core:127382] [Ruby Bug#22522] `Ractor.shareable_proc` silently drops the block of the method for `block_given?`, `defined?(yield)` and `super` Issue #22522 has been reported by Eregon (Benoit Daloze). ---------------------------------------- Bug #22522: `Ractor.shareable_proc` silently drops the block of the method for `block_given?`, `defined?(yield)` and `super` https://bugs.ruby-lang.org/issues/22522 * Author: Eregon (Benoit Daloze) * Status: Open * Target version: 4.1 * ruby -v: ruby 4.1.0dev (2026-11-09T15:47:20Z master 7e5d882817) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- `yield` in a block calls the block of the enclosing method. A shareable Proc cannot keep that block: the block (or the Proc it becomes) has its own `self` and environment, which are generally not shareable, and the copied environment of the shareable Proc has no block. That is why `Ractor.shareable_proc` already rejects `yield`: ```ruby def m = Ractor.shareable_proc { yield } m {} # can not make a Proc shareable because it uses 'yield'. (Ractor::IsolationError) ``` But other constructs inside a block use the block of the enclosing method too, and are not rejected. The shareable Proc silently behaves as if the method had no block: ```ruby def m1 = Ractor.shareable_proc { block_given? } p m1 {}.call # Ruby block semantics: true def m2 = Ractor.shareable_proc { defined?(yield) } p m2 {}.call # "yield" class Parent def foo = block_given? end class Child < Parent # super without an explicit block passes the block of the method def foo = Ractor.shareable_proc(self: self) { super() } end obj = Ractor.make_shareable(Child.new) p obj.foo {}.call # true ``` ``` $ ruby -v block.rb ruby 4.1.0dev (2026-11-09T15:47:20Z master 7e5d882817) +PRISM [arm64-darwin25] false nil false ``` Same result on 4.0.7. `iterator?` (the deprecated alias of `block_given?`) and zsuper (`super`) behave the same way. Since #22444 this also applies to `Ractor.new` blocks. These should raise `Ractor::IsolationError` like `yield` does, for the same reason. Following [Feature #21557], it is better to raise than to silently change the semantics of the block. ## Fix PR: https://github.com/ruby/ruby/pull/19803 The compiler now records `block_given?`/`iterator?` calls without a receiver, `defined?(yield)`, and `super` without an explicit block argument or literal block, the same way it records `yield`. This is done only when the enclosing scope is a method, since otherwise there is no block of the method and nothing to lose, so `Ractor.new { block_given? }` keeps working. The error message names the construct: ``` can not make a Proc shareable because it uses 'block_given?'. (Ractor::IsolationError) ``` Like the existing check for `yield`, this check is static, so it raises even if the method was called without a block. A possible later relaxation, for `yield` too, would be to check at runtime instead and reject only when the method has a block which is not shareable. A method's block cannot change during the call, so without a block the shareable Proc behaves exactly like the original. Code which currently creates such a shareable Proc gets `Ractor::IsolationError` after this fix, so I don't think this should be backported. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/