Skip to content

Improve BeginNode Type Handling #298

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
Apr 24, 2025

Conversation

smasato
Copy link
Contributor

@smasato smasato commented Apr 13, 2025

This PR enhances the BeginNode implementation to properly handle type information in rescue clauses, ensuring accurate type tracking across different execution paths in begin-rescue-else-ensure blocks.

@smasato smasato marked this pull request as draft April 13, 2025 12:19
@smasato smasato force-pushed the fix-rescue-clause-install branch from 8cf2963 to 3199a94 Compare April 15, 2025 14:16
@smasato smasato changed the title Improve variable tracking in begin/rescue/else blocks Improve BeginNode Type Handling Apr 15, 2025
@smasato smasato marked this pull request as ready for review April 15, 2025 14:26
@mame
Copy link
Member

mame commented Apr 22, 2025

Thank you! I'll take a closer look at it tomorrow, as I don't understand the changes, and it conflicts with #303.

Copy link
Member

@mame mame left a comment

Choose a reason for hiding this comment

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

Thank you! This PR looks good to me.

I just want to confirm one thing. I wasn't sure about the need for Lines 398..402. Do we need to trace the modified_vars in @rescue_clauses? If so, it would be nice to add a test scenario.

I feel like I would like to have scenarios to test the other added parts. For example, it would be nice to have a dedicated test scenario for the else clause, since it does a fine job to execute it in the state after body evaluation.

However, if you want, I am OK to add other tests after the merge.

Just FYI, I tested this PR with the following test scenario.

## update
def foo
  x = :a
  begin
    x = :b
    raise
    x = :c
  rescue
    check_rescue(x)
    x = :d
  #ensure # Looks like commenting out these two lines makes the test fail? Need to fix
  #  x = :f
  else
    check_else(x)
    x = :e
  end
  check_after(x)
end

def check_rescue(n) = nil
def check_else(n) = nil
def check_after(n) = nil

## assert
class Object
  def foo: -> nil
  def check_rescue: (:a | :c) -> nil
  def check_else: (:c) -> nil
  def check_after: (:a | :c | :d | :e) -> nil
end

Comment on lines 398 to 402
@rescue_clauses.each do |clause|
clause.modified_vars(@lenv.locals.keys, vars)
end
@else_clause.modified_vars(@lenv.locals.keys, vars) if @else_clause
@ensure_clause.modified_vars(@lenv.locals.keys, vars) if @ensure_clause
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@rescue_clauses.each do |clause|
clause.modified_vars(@lenv.locals.keys, vars)
end
@else_clause.modified_vars(@lenv.locals.keys, vars) if @else_clause
@ensure_clause.modified_vars(@lenv.locals.keys, vars) if @ensure_clause

Are these lines necessary? I tried deleting them, but the test results did not seem to change.
If you have a test case where these would be needed, it would be helpful if you could indicate it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was a lack of understanding. It was unnecessary and I deleted it.

old_vtx = old_vtxs[var]
nvtx = old_vtx.new_vertex(genv, self)

@changes.add_edge(genv, body_vtxs[var], nvtx) if body_vtxs[var]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@changes.add_edge(genv, body_vtxs[var], nvtx) if body_vtxs[var]
@changes.add_edge(genv, body_vtxs[var], nvtx) unless body_vtxs[var] == old_vtxs[var]

I couldn't figure out the case where body_vtxs[var] would be falsy.
Also, I think this guard is not needed if lines 398..402 were deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

398...402 were deleted and are no longer needed.

@lenv.set_var(var, body_vtxs[var])
end
@changes.add_edge(genv, @else_clause.install(genv), ret)
end
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
end
rescue_vtxs << {}
vars.each do |var|
rescue_vtxs.last[var] = @lenv.get_var(var)
end
end

It is great that the variable changes in the rescue clause are reflected after the begin node.
Why not reflect the changes in the else clause as well?

Also, it may be good to reflect the changes in the ensure clause.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The else clause is also reflected.
I will work on the reflection of the ensure clause in the future.

@smasato smasato force-pushed the fix-rescue-clause-install branch from 3199a94 to 9368586 Compare April 23, 2025 14:02
@smasato
Copy link
Contributor Author

smasato commented Apr 23, 2025

@mame
Thank you for your detailed review!
I have resolved the conflicts and updated and added the test scenario.
Please review when you have time.

@smasato smasato requested a review from mame April 23, 2025 14:12
@mame mame merged commit 75082bb into ruby:master Apr 24, 2025
7 checks passed
@mame
Copy link
Member

mame commented Apr 24, 2025

Thank you!

@smasato smasato deleted the fix-rescue-clause-install branch April 24, 2025 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants