Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kernel#lambda: return forwarded block as non-lambda proc #2289

Open
wants to merge 4 commits into
base: master
from

Conversation

@XrXr
Copy link
Member

XrXr commented Jul 14, 2019

Before this commit, Kernel#lambda can't tell the difference between a
directly passed literal block and one passed with an ampersand.

A block passed with an ampersand is semantically speaking already a
non-lambda proc. When Kernel#lambda receives a non-lambda proc, it
should simply return it.

Implementation wise, when the VM calls a method with a literal block, it
places the code for the block on the calling control frame and passes a
pointer (block handler) to the callee. Before this commit, the VM
forwards block arguments by simply forwarding the block handler, which
leaves the slot for block code unused when a control frame forwards its
block argument. I use the vacant space to indicate that a frame has
forwarded its block argument and look for this in Kernel#lambda to
detect forwarded blocks.

This is a very ad-hoc solution and relies heavily on the way block
passing works in the VM. However, it's the most self-contained solution
I have.

Bug #15620

@XrXr XrXr marked this pull request as ready for review Jul 14, 2019
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from 3be5a65 to 41f1479 Jul 14, 2019
@XrXr XrXr changed the title Kernel#lambda: return forwarded block as non-lambda proc [WIP] Kernel#lambda: return forwarded block as non-lambda proc Jul 14, 2019
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from 41f1479 to 74e066b Jul 14, 2019
@XrXr

This comment has been minimized.

Copy link
Member Author

XrXr commented Jul 14, 2019

It looks like this changes the semantics of calling into lambda using zsuper. Will look for solutions

EDIT: Okay, found a way to do it.

@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from 74e066b to 1c74774 Jul 15, 2019
@XrXr XrXr changed the title [WIP] Kernel#lambda: return forwarded block as non-lambda proc Kernel#lambda: return forwarded block as non-lambda proc Jul 15, 2019
@XrXr

This comment has been minimized.

Copy link
Member Author

XrXr commented Jul 15, 2019

The extra specs are just for making sure I didn't make unintentional semantic changes. I checked that they pass on supported versions and on master.

Some notes:

  • it's not enough to simply check whether lambda got a forwarded block since it needs to behave differently for block forwarded using & vs super
  • another possible solution is to make block handler an offset from the bottom of the stack plus a tag, packed into a VALUE. It would be a bigger change that might have wider perf consequences
  • I tried using another tag for the block handler (it's already tagged), but there aren't enough bits for another tag on 32 bit platforms #2288
k0kubun added a commit to XrXr/ruby that referenced this pull request Aug 15, 2019
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch 2 times, most recently from 895dadd to d5e8b25 Sep 26, 2019
@XrXr

This comment has been minimized.

Copy link
Member Author

XrXr commented Sep 28, 2019

Updated to issue a warning in cases where Kernel#lambda (unintuitively) returns the proc it receives.

@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from d5e8b25 to 8eefbe5 Oct 10, 2019
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from 8eefbe5 to c8c92d6 Nov 1, 2019
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from c8c92d6 to 61d0caf Nov 12, 2019
XrXr added 4 commits Jul 13, 2019
Before this commit, Kernel#lambda can't tell the difference between a
directly passed literal block and one passed with an ampersand.

A block passed with an ampersand is semantically speaking already a
non-lambda proc. When Kernel#lambda receives a non-lambda proc, it
should simply return it.

Implementation wise, when the VM calls a method with a literal block, it
places the code for the block on the calling control frame and passes a
pointer (block handler) to the callee. Before this commit, the VM
forwards block arguments by simply forwarding the block handler, which
leaves the slot for block code unused when a control frame forwards its
block argument. I use the vacant space to indicate that a frame has
forwarded its block argument and look this in Kernel#lambda to detect
forwarded blocks.

This is a very ad-hoc solution and relies *heavily* on the way block
passing works in the VM. However, it's the most self-contained solution
I have.

[Bug #15620]
When Kernel#lambda receives a proc, it returns the proc without
modifying it to avoid the pandora box of converting blocks into lambdas.
Issue a warning when this hapepns since it's counter intuitive.
Also, adjust test cases to avoid this warning.

[ruby-core:94054]
@XrXr XrXr force-pushed the XrXr:two-four-lambda branch from 61d0caf to acb3667 Nov 13, 2019
@eregon

This comment has been minimized.

Copy link
Member

eregon commented Nov 27, 2019

👍 for the warning.

I'm not sure about the super cases, it seems extremely uncommon to have a method named lambda and call super in it. But it's probably best to keep behavior as it-is in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants
You can’t perform that action at this time.