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

Support auto-splatting in captured block literals #10251

Merged

Conversation

HertzDevil
Copy link
Contributor

@HertzDevil HertzDevil commented Jan 14, 2021

Resolves #7706.

In a case like

def foo(&block : {Int32, Char} -> _)
  tup = {1, 'a'}
  block.call tup
end

foo do |x, y|
  {x, y}
end

The compiler now rewrites the block into the following ProcLiteral:

->(__temp : {Int32, Char}) do
  x = __temp[0]
  y = __temp[1]
  {x, y}
end

The conditions for auto-splatting are the same as non-captured blocks: the block must accept a single Tuple instance, there must be 2 or more block parameters, and none of these parameters must be a splat.

When there are fewer block parameters than tuple elements, the whole tuple is still passed but the __temp assignments corresponding to the missing parameters will be gone.

Tuple unpacking of individual block parameters is also possible. For example, given

def foo(&block : Tuple(Tuple(Int32, Char), Bool, Float64) -> _)
  tup = { {1, 'a'}, true, 1.0 }
  block.call tup
end

foo do |(x, y), z|
  {x, y, z} # => {1, 'a', true}
end

The expanded Proc is more or less equivalent to

->(__temp_1 : Tuple(Tuple(Int32, Char), Bool, Float64)) do
  __arg0 = __temp_1[0]
  z = __temp_1[1]
  # __temp_1[2] not accessed

  x = __arg0[0]
  y = __arg0[1]

  {x, y, z}
end

Internally (x, y) is transformed into __arg0 by the parser; __temp_1 is injected in the semantic phase when the block is actually matched against the enclosing method.

Copy link
Member

@asterite asterite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

src/compiler/crystal/semantic/call.cr Outdated Show resolved Hide resolved
@straight-shoota straight-shoota added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler:semantic labels Jan 23, 2021
@bcardiff bcardiff modified the milestone: 1.0.0 Feb 18, 2021
@bcardiff bcardiff added the pr:needs-work A PR requires modifications by the author. label Feb 18, 2021
@bcardiff
Copy link
Member

FYI, This CI is complaining for real in this PR in spec/compiler/semantic/closure_spec.cr:385, although for some reason it didn't fail on all platforms 🤔

@HertzDevil
Copy link
Contributor Author

Ah this is because I forgot to change some expected assert_error messages. Push will be up soon...

@bcardiff
Copy link
Member

It's still failing just on some platforms. The merge from master was done in an old commit. I am not sure if that's the reason.

@straight-shoota straight-shoota removed the pr:needs-work A PR requires modifications by the author. label Apr 13, 2021
@beta-ziliani beta-ziliani added this to the 1.2.0 milestone Aug 20, 2021
@straight-shoota straight-shoota merged commit 39e4c73 into crystal-lang:master Aug 24, 2021
@HertzDevil HertzDevil deleted the bug/captured-block-autosplat branch August 25, 2021 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler:semantic
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Iterable of tuple, methods select/reject/map do not unpack tuple
5 participants