Skip to content

Ruby: Improve desugaring of for loops #13937

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

Merged
merged 2 commits into from
Aug 14, 2023

Conversation

hvitved
Copy link
Contributor

@hvitved hvitved commented Aug 10, 2023

Previously, we would desugar for loops

for x in xs do
  # body
end

into

xs.each do |synth|
  x = synth
  # body
end

However, when the iteration variable x is used after the loop (as a captured variable inside the loop), the rewrite only becomes correct when x either has a prior assignment, or if we add a nil initialization.

This PR therefor improves the desugaring by adding a conditional initial assignment:

if not defined?(x) then
  x = nil
end

xs.each do |synth|
  x = synth
  # body
end

The rewrite also works when there are multiple iteration variables:

for x, y in xs do
  # body
end

gets desugared into

if not defined?(x) then
  x = nil
end

if not defined?(y) then
  y = nil
end

xs.each do |synth|
  x, y = synth
  # body
end

@github-actions github-actions bot added the Ruby label Aug 10, 2023
@hvitved hvitved force-pushed the ruby/for-loop-desugar branch from 5051d49 to f6f88f1 Compare August 10, 2023 10:03
@hvitved hvitved force-pushed the ruby/for-loop-desugar branch from f6f88f1 to 77fca27 Compare August 10, 2023 11:22
@hvitved hvitved added the no-change-note-required This PR does not need a change note label Aug 10, 2023
@hvitved hvitved marked this pull request as ready for review August 10, 2023 12:55
@hvitved hvitved requested a review from a team as a code owner August 10, 2023 12:55
@aibaars
Copy link
Contributor

aibaars commented Aug 14, 2023

This does not match the behaviour mentioned in parse.y, but it seems to have the right effect. See also https://github.com/ruby/ruby/blob/master/parse.y#L3558-L3567. I think we don't need the "if defined" checks; instead we could insert x = nil assignments for each variable that does not exist in the current scope.

@hvitved
Copy link
Contributor Author

hvitved commented Aug 14, 2023

I think we don't need the "if defined" checks; instead we could insert x = nil assignments for each variable that does not exist in the current scope.

I would rather not make the desugaring transformation context sensitive, which is why I did it this way.

Copy link
Contributor

@aibaars aibaars left a comment

Choose a reason for hiding this comment

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

Looks good to me. Let's improve the locations of the synthesized nodes though.

@hvitved hvitved merged commit 061575f into github:main Aug 14, 2023
@hvitved hvitved deleted the ruby/for-loop-desugar branch August 14, 2023 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-change-note-required This PR does not need a change note Ruby
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants