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 behaviour of closed-over locals in and/or expressions #1278

Merged
merged 2 commits into from
Apr 18, 2024

Conversation

zerbina
Copy link
Collaborator

@zerbina zerbina commented Apr 17, 2024

Summary

Fix closed-over locals defined as part of and/or expressions not
having their default value when their defined-in sub-expression was
short-circuited.

Details

Bindings part of and/or expressions were so far hoisted to the
start of their scope by the "unscoped def" handling in cgirgen, which
doesn't apply to lifted locals, since these don't have a def (they're
part of the environment object).

Performing the hoisting in transf makes sure the behaviour of closed-
over locals matches that of not closed-over ones, and it also removes
the reliance on cgirgen.

The hoisting works by scanning transformed and/or expressions for
bindings that are within the same scope as the and/or expression,
and moving them to the start of the and/or expression.

For example, (let a = 0; x) or (let b = 0; y) is transformed into
(let a; let b; (a = 0; x) or (b = 0; y)).

Lifted globals (.global and .thread) have to be accounted for by
the hoisting pass, since they weren't lifted at this point.

@zerbina zerbina added bug Something isn't working compiler General compiler tag labels Apr 17, 2024
@zerbina zerbina added this to the MIR phase milestone Apr 17, 2024
`var`/`let` bindings within `and`/`or` expression are embedded into the
surrounding scope, which would cease to be the case after lowering, for
example, `x and y` into `(var tmp = x; (;if tmp: tmp = y); tmp), as
`y`'s lifetime is then delimited by the `if`'s scope.

So far, this problems was partially addressed by the "unscoped def"
handling in `cgirgen`, but only for locals not lifted into the
environment. Performing the hoisting in `transf` makes sure lifted
locals work as expected, and it also makes sure the code works
correctly without `cgirgen`.
Copy link
Collaborator

@saem saem left a comment

Choose a reason for hiding this comment

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

Just a question/discussion point, it's good to merge regardless.

# lifetime earlier. That is, for ``var x = (var y = 0; y)`` the
# ``var y`` must be hoisted first
let src = hoist(c, move it[2], target)
target.add newTreeI(n.kind, it.info,
Copy link
Collaborator

Choose a reason for hiding this comment

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

When n.kind is an nkLetSection aren't we going to cause potential issues downstream when assigning via such a handle?

Not really expecting a change, seeing as it does work, given the test; at most a comment, but not sure it requires that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a good question, thank you for asking. Including and beyond transf, let and var sections are effectively interchangeable (only the symbol kind matters). A let binding not being assigned a value immediately is also fine.

What would be a problem is if a let location is either read from before it's assigned to, or assigned to multiple times (mirgen relies on both to never happen), but the hoisting cannot cause either.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for clarifying, that was my guess. I also think it's good in terms of long term direction, because making let, or some variant, to be an initialize once before use, rather than once at the time of definition, would be nice.

@saem
Copy link
Collaborator

saem commented Apr 18, 2024

/merge

Copy link

Merge requested by: @saem

Contents after the first section break of the PR description has been removed and preserved below:


Notes for Reviewers

  • a preparation towards the removal of cgirgen
  • fixing the bug is merely a side effect
  • in theory, mirgen could do the hoisting, but it's not as well-suited to do so as transf is

@chore-runner chore-runner bot added this pull request to the merge queue Apr 18, 2024
Merged via the queue into nim-works:devel with commit 8c27139 Apr 18, 2024
31 checks passed
@zerbina zerbina deleted the and-or-hoisting branch April 18, 2024 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working compiler General compiler tag
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants