The reason this works at all in MRI is accidental; because MRI only updates its global per-call state piece by piece, the Proc.new call inherits the environment from the 'f' method and is able to see and capture its block.
I would like to disable this feature entirely, but I know some code uses it for Proc.new. The missing behavior is that we don't completely support it for subclasses of Proc, and this is a much harder thing to fix.
When JRuby compiles, it looks for things like blocks captured (&b), closures in the method body, "eval" calls, "binding" calls, and so on. It uses this information to prepare appropriate state for the call. In the case of:
We additionally use the presence of "Proc.new" in that exact form to know that this magic behavior is in play and the implicit block should not be discarded.
In the case reported here, because the form is not "Proc.new" exactly, we can't determine that the implicit block is being used for anything, and as an optimization we don't save it anywhere. When the subclass of Proc tries to "new", no block is available and an ArgumentError results.
The really awful part of this feature is that in order to support it we'd have to defeat a very large optimization that improves performance of Ruby method calls by many times. Given the rarity of a class that subclasses proc and is constructed without passing in a block or block argument, I don't think it's worth it.
There are a few workarounds possible in JRuby, and I'd be happy to make some of them more "formal" triggers to keep the block around. The first, and simplest, is to just capture the block argument normally (def foo(&block)). There's very little performance penalty for this if you don't pass in a block, which is one of the primary arguments for the magic Proc.new syntax.
A second way is to include another call that depends on being able to see the implicit block, like block_given?:
class P < Proc; end
P.new if block_given?
This should always work, since block_given? is a known method that triggers keeping the implicit block around.
At the moment, I'm going to leave this one unresolved and not attempt to fix it for 1.6, since doing so would require reverting one of the largest optimizations we've made in recent years.