Skip to content

Clean up Middle IR and variable assignment/reassignment #293

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

Draft
wants to merge 1 commit into
base: hkmc2
Choose a base branch
from

Conversation

LPTK
Copy link
Contributor

@LPTK LPTK commented Mar 18, 2025

I'm trying to refactor the currently messy handling of variables in MIR.

  • Distinguish initial variable assignments from mutable reassignments
  • Add a way of specifying where variables are bound. Is this really necessary, though, with the distinction above? Now we can differentiate when a loop allocates a variable within its body versus when it changes an already defined one.
  • Later: clean up the messy ad-hoc logic around local vals (that have no owner), private fields defined with let and val-less class parameters, etc.

@LPTK
Copy link
Contributor Author

LPTK commented Mar 18, 2025

@CAG2Mark The lifter is not really updated. There are tests failing as it does the wrong thing in some cases (see the diff of FunInFun.mls).

Comment on lines 462 to +465
val vars = t.definedVars.toSeq.filter(scope.lookup(_).isEmpty).sortBy(_.uid).iterator.map(l =>

// TODO: now should be possible to just write: (currently causes problems to handlers)
// val vars = t.definedVars.toSeq.sortBy(_.uid).iterator.map(l =>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CAG2Mark @AnsonYeung There are probably many instances where the effect handler transformation reassigns non-mutable symbols instead of simply assigning them. In principle it should be possible to make this change, but it breaks code that uses handlers.

Choose a reason for hiding this comment

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

Indeed the continuation class implicitly captures the variables so all assignments within continuation should be reassignments really (including the this.pc assignments), even though they might not be assigned or initialized in the first place yet.

A brief remark of why capturing is needed: any function could define other nested function that capture the same variable, and it is important for the continuation class to capture the same one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed the continuation class implicitly captures the variables so all assignments within continuation should be reassignments really (including the this.pc assignments), even though they might not be assigned or initialized in the first place yet.

I'm not following. Doesn't the continuation class just duplicate the function's implementation? If the function was assigning the variable for the first time (maybe the variable is even immutable), then the continuation class should also be assigning that variable for the first time in the corresponding part of its duplicated code. No?

Choose a reason for hiding this comment

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

Oh right, so we need proper binding site probably. With the changes in the comment above it would make the initial assignment to create a local variable at the function declaration which is incorrect as the resume function can return a effectsig and resume later which uses the same variable again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah right, I see. That's probably why it doesn't work currently.

We should list the variables that are bound within a given MIR block body.

@@ -486,7 +489,7 @@ class HandlerLowering(paths: HandlerPaths)(using TL, Raise, Elaborator.State, El

val resumedVal = VarSymbol(Tree.Ident("value$"))

def createAssignment(sym: Local) = Assign(sym, resumedVal.asPath, End())
def createAssignment(sym: Local) = Reassign(sym, resumedVal.asPath, End()) // TODO: or is this `Assign`?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

This is always a reassignment, as the old symbol would hold the previous EffectSig

case _ => initial

val remaining = rewritten match
case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match

// TODO (LP:) not sure about the semantics of these "Reassign" cases
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CAG2Mark 👀

@LPTK
Copy link
Contributor Author

LPTK commented Apr 10, 2025

In today's meeting, we figured that we currently don't actually need this change. We just need to change the way Label treats assignments, so it is basically treated similarly to a local function with its own scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants