A single return in the source code might return to 2 different lexical places.
That seems wrong to me, as AFAIK all other control flow language constructs always jump to a single place.
defm(call_proc)r=->{# This single return in the source might exit the lambda or the method!proc=Proc.new{return:return}ifcall_procproc.call:after_in_lambdaelseprocend}.call# returns here if call_procifcall_proc[:after_in_method,r]elser.call:never_reachedendendpm(true)# => [:after_in_method, :return]pm(false)# :return
what are actually the semantics of return inside a proc?
The semantics seem incredibly complicated to a point developers have no idea where return actually goes.
Also it must get even more complicated if one defines a lambda method as the block in lambda { return } is then non-deterministically a proc or lambda.
I should also note some of these semantics might significantly harm the performance of Ruby.
CRuby seems to walk the stack on every return.
On others VMs there need to be some extra logic to find if the frame to return to is still on the stack.
It's already quite complicated but then if return can go to two places, it becomes a huge mess.
Hans I don't think anyone is debating the basic idea of what return in a proc or lambda does - I think we're talking about the edge-case for a proc in a return in the example above, which isn't explained by the text you have.
I think the behavior makes sense to some extent, because the proc is within 2 nested contexts. Since the proc is within the lambda context, calling it in the lambda returns from the lambda. And since the proc is also within the method context, calling it in the method returns from the method.
The call_proc branching logic makes this look more complicated than it really is, but if you separate the logic I feel the behavior is rather reasonable. What do you think should be the behavior of m2 below?
defm1r=->{proc=Proc.new{return:return}proc.call#return from lambda:after_in_lambda}.call[:after_in_method,r]enddefm2r=->{proc=Proc.new{return:return}}.callr.call#return from method:never_reachedendpm1#=> [:after_in_method, :return]pm2#=> :return
IMHO it should be a LocalJumpError. The Proc should return to the lambda, that's syntactically the closest scope it should return to.
Since it's not possible to return to it (the lambda is no longer on stack), it should be a LocalJumpError.
In addition to the confusing (and possibly inefficient) behavior that results from having two possible return targets, there's also a bug potential here if someone "accidentally" allows a proc containing a return to escape from its lambda container. Rather than returning from the lambda as it should have done, it will now return from the next "returnable" scope, and likely interrupt execution in an unexpected way.
I would challenge anyone to explain why the current behavior should exist, since I can't think of a single valid use case. If there's no use case for a confusing "feature", we should remove it.
A "return" statement in a Proc in a lambda like: lambda{ proc{ return }.call }
should return outer lambda block. However, the inner Proc can become
orphan Proc from the lambda block. This "return" escape outer-scope
like method, but this behavior was decieded as a bug.
[Bug #17105]
This patch raises LocalJumpError by checking the proc is orphan or
not from lambda blocks before escaping by "return".