-
Notifications
You must be signed in to change notification settings - Fork 29
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
Class Lifter #266
base: hkmc2
Are you sure you want to change the base?
Class Lifter #266
Conversation
What's the intended scheme regarding mutable local variables? FWIW, we should distinguish mutable from immutable variables. I intend to let When there are only immutable variables being captured, the scheme can be simpler. |
I don't distinguish between local variables that are mutated / not mutated for now. I intend to move every local variable that's referenced inside a nested class / function into a closure. Would it be better to add the immutable local variables to each class/function directly? P.S. seems I accidentally marked this PR as ready earlier, but it's definitely not. It's now marked as a draft. |
Added much more into to the the first comment. |
…ses that don't capture variables
@@ -15,7 +15,7 @@ foo() | |||
|
|||
let x = 1 | |||
let f = () => x | |||
//│ f = [function f] | |||
//│ f = [function lambda] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Did this actually rewrite it as a proper function before? Or is this just due to the way it's printed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? The name is taken from the name of the binding, because we generate things like f = function f ...
.
OK, I think it's ready for review. |
@@ -238,3 +244,28 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst | |||
then b else HandleBlock(l2, res2, par2, args2, cls2, hdr2, bod, rst2) | |||
case _ => super.applyBlock(b) | |||
|
|||
// does not traverse into any other block |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The semantics of this transformer seems broken wrt Begin
. If you have Begin{foo()}; bar()
you're not going to change foo
even though you would have for the equivalent foo(); bar()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The job of this transformer is just to modify the paths and results in a block which do not require traversing another block to reach -- it's the job of the person using this transformer to traverse the sub-blocks, probably used in combination with one of the other block transformers.
As for Begin
, both of its sub-blocks are expected to be traversed by whoever is using this transformer, not the transformer itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then please pick a better name for this class and document in the code what you've just written here. The current comment is inadequate as documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it feels like a better architecture might be to use a method called applySubBlock
in BlockTransformer
whenever a sub-block (not a top-level block) is traversed, which would mean you only have to override that here to return input unchanged, instead of all this boilerplate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I think that will be a better solution.
//│ Foo1 = function Foo(x1) { return new Foo.class(x1); }; | ||
//│ Foo1 = function Foo(x1) { | ||
//│ return new Foo.class(x1); | ||
//│ }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any idea why there are no longer on one line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there's parameter lists, we need to curry the ctor using lambdas, and it's quite ugly to have it all on one line. I decided it was not worth special-casing non-curried ctors to be on one line such it's purely a superficial change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, I didn't realize the line was previously missing the breaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be marked as resolved?
//│ undefined | ||
//│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 | ||
//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good if we could avoid making every lambda get the name "lambda", at least in such error messages. Ideally they'd be unnamed and we would still have the old message here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently it seems BlockMemberSymbol
s must have names. Maybe we could add an flag which disables the name from being printed in error messages -- I don't know if it makes sense for BlockMemberSymbols to be unnamed.
Thanks, I'll try to properly review it tomorrow. In the meantime please fix the conflicts and comments. |
@@ -91,12 +92,37 @@ sealed abstract class Block extends Product with AutoLocated: | |||
case TryBlock(sub, finallyDo, rest) => sub.freeVars ++ finallyDo.freeVars ++ rest.freeVars | |||
case Assign(lhs, rhs, rest) => Set(lhs) ++ rhs.freeVars ++ rest.freeVars | |||
case AssignField(lhs, nme, rhs, rest) => lhs.freeVars ++ rhs.freeVars ++ rest.freeVars | |||
case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really meant to use freeVarsLLIR
? 🤔
Lifts classes and nested functions to the top-level.
Method
General idea: when functions have to capture local variables, we need to rewrite their parameter lists. For example:
is transformed into
Variables which are not mutated will be passed as a parameter directly.
TODO
Instantiate
does not work at allg(unused)
is symbolically replaced.("lifting" curried functions is much easier than the lifting in general, so it can be done later)
Auxiliary Parameters
Classes can now have auxiliary parameters (IR only currently) with curried constructors.