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

Fix: named arguments expanded from double splat clash with local variable names #6378

Merged

Conversation

asterite
Copy link
Member

@asterite asterite commented Jul 13, 2018

Fixes #5693

When there's code like this:

def foo(**opts)
  opts
end

foo(x: 1, y: 2)

the compiler transforms it to something like this (call, per call):

def foo:x:y(x, y)
  opts = {x: x, y: y}
  # next comes the original def's body, in this case just "opts"
  opts
end

foo(x: 1, y: 2)

The problem with that approach is that this will not work correctly if the method already defined x or y as local variables. This:

def foo(**opts)
  x = 1
  opts
end

foo(x: 1, y: 2)

gets expanded to:

def foo:x:y(x, y)
  opts = {x: x, y: y}
  x = 1
  opts
end

foo(x: 1, y: 2)

This isn't usually a problem unless x is declared as a local variable with x : Type, or if x is captured in a closure.

The solution in this PR changes the expansion to be:

def foo:x:y(x __temp_1, y __temp_2)
  opts = {x: __temp_1, y: __temp_2}
  x = 1
  opts
end

foo(x: 1, y: 2)

That way there's no clash.

(well, there could be a clash with those temp names, but variables that start with underscore are reserved for the compiler, more so ones with double underscore (this is used all over the compiler, not just here))

@RX14 RX14 merged commit e6d0be2 into crystal-lang:master Jul 13, 2018
@RX14 RX14 added this to the Next milestone Jul 13, 2018
@RX14 RX14 modified the milestones: Next, 0.26.0 Jul 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants