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

Stack safe recursion #259

Draft
wants to merge 94 commits into
base: hkmc2
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
9e2c4cd
Changes IR
AnsonYeung Dec 8, 2024
e7813a9
initial changes
CAG2Mark Dec 10, 2024
4444d4e
Update elaborator
CAG2Mark Dec 10, 2024
4f26189
update lowering
CAG2Mark Dec 10, 2024
942922d
fix whitespace
CAG2Mark Dec 10, 2024
827c5e7
refactor some code
CAG2Mark Dec 10, 2024
eef7131
Simplify test cases
AnsonYeung Dec 12, 2024
5b26719
Hide handlers behind a flag
AnsonYeung Dec 12, 2024
421b640
Implement transformation for non-resumptive effects
AnsonYeung Dec 16, 2024
76695ba
Continuation class transformation
AnsonYeung Dec 16, 2024
2263ddf
Port tests to new branch
AnsonYeung Dec 16, 2024
bdbee0a
Resume non tail resumptive handlers
AnsonYeung Dec 16, 2024
e9bab2b
Implement final pieces for nested handlers
AnsonYeung Dec 17, 2024
4bb2d73
Implement Match codegen for multiple arms
AnsonYeung Dec 17, 2024
7395b4c
Moved most wiring code to Predef
AnsonYeung Dec 17, 2024
5ef77f2
Changes to IR
AnsonYeung Dec 19, 2024
018dedd
Changes IR
AnsonYeung Dec 19, 2024
dac193e
Patch for JSBuilder
AnsonYeung Dec 19, 2024
68be072
Fix link error
AnsonYeung Dec 19, 2024
ab69c37
Merge branch 'hkmc2' into ir-handler
AnsonYeung Dec 19, 2024
6a71972
Changes to handler syntax
AnsonYeung Dec 19, 2024
ea621a9
Add class inheritance in IR
AnsonYeung Dec 20, 2024
d07371e
Add class inheritance in IR and fixed spacing
AnsonYeung Dec 20, 2024
6b74a07
Merge branch 'hkmc2' into ir-handler
AnsonYeung Dec 20, 2024
be15486
Remove unnecessary newline in JS Codegen
AnsonYeung Dec 20, 2024
db5aa6d
Random typo fix
AnsonYeung Dec 20, 2024
2b67aba
Merge branch 'ir-handler' into ir-handler-transform
AnsonYeung Dec 20, 2024
ef1eabc
Small fix
AnsonYeung Dec 20, 2024
61d2a27
Add mildly interesting test
LPTK Dec 21, 2024
564f020
Add more test cases and reveal some issues
LPTK Dec 21, 2024
9bc81df
Merge branch 'hkmc2' into ir-handler-transform
AnsonYeung Dec 21, 2024
4905f8a
Use subclass for effect and separate internal class
AnsonYeung Dec 24, 2024
c66c73d
Change how elaboration of handlers work
AnsonYeung Dec 24, 2024
f876483
add test
CAG2Mark Dec 24, 2024
879eece
Fixed one example
AnsonYeung Dec 25, 2024
f4e728e
add minimal example of effects in handler bug
CAG2Mark Dec 25, 2024
643ec6f
Fixed another test
AnsonYeung Dec 25, 2024
dc516cd
Fix missing thisProxy
AnsonYeung Dec 25, 2024
e6cd1ec
Fix generator
AnsonYeung Dec 25, 2024
8a6d15c
Fix typo
AnsonYeung Dec 25, 2024
2e07058
Fix function calls and clases sometimes not being in scope after tran…
CAG2Mark Dec 27, 2024
6339e8b
Update hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls
CAG2Mark Dec 27, 2024
a2900ec
Update hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala
CAG2Mark Dec 27, 2024
7596cf3
set up infrastructure
CAG2Mark Dec 31, 2024
4b837eb
Changed and added broken test
AnsonYeung Jan 6, 2025
0864f27
Update tests
AnsonYeung Jan 6, 2025
e6a819b
Minor style changes (? debatable improvement)
LPTK Jan 6, 2025
af17307
Fix elaboration scoping
AnsonYeung Jan 7, 2025
08a4d4c
Merge branch 'hkmc2' into ir-handler-transform
AnsonYeung Jan 7, 2025
2da74c0
Fixed some bugs
AnsonYeung Jan 7, 2025
80e9fba
Fix consistency
AnsonYeung Jan 7, 2025
964c146
Fixed comments
AnsonYeung Jan 7, 2025
6722e1a
initial
CAG2Mark Jan 7, 2025
edee182
Merge branch 'ir-handler-transform' into stack-safety
CAG2Mark Jan 7, 2025
b8c6120
move helper function outside
CAG2Mark Jan 7, 2025
9dfc35d
Merge branch 'ir-handler-transform' into stack-safety
CAG2Mark Jan 7, 2025
cb68929
initial stack safety working
CAG2Mark Jan 7, 2025
eb71e32
fix return leaked
CAG2Mark Jan 7, 2025
fe076e3
remove fixme
CAG2Mark Jan 7, 2025
67617c1
update test
CAG2Mark Jan 7, 2025
2393ea5
Remove usage of dummy class symbol
AnsonYeung Jan 8, 2025
810a639
Remove some unused code
AnsonYeung Jan 10, 2025
74fb276
Merge branch 'hkmc2' into ir-handler-transform
AnsonYeung Jan 10, 2025
b04031a
revert parser workaround
AnsonYeung Jan 10, 2025
e8113c7
predef clean up
AnsonYeung Jan 10, 2025
78f6f54
Simplify Predef
AnsonYeung Jan 10, 2025
d3fedd2
Rewriting most of Predef for clean up
AnsonYeung Jan 10, 2025
b30d8ca
Change test case to todo
AnsonYeung Jan 10, 2025
c9057ea
remove unused code
AnsonYeung Jan 11, 2025
4b73be5
Merge branch 'hkmc2' into ir-handler-transform
AnsonYeung Jan 13, 2025
235a6ec
Added codegen examples
AnsonYeung Jan 13, 2025
ed895eb
use SymbolSubst
CAG2Mark Jan 13, 2025
be37739
Update tests
CAG2Mark Jan 13, 2025
b8d0da8
fix whitespace
CAG2Mark Jan 13, 2025
7da8101
Fix symbol map issues
CAG2Mark Jan 13, 2025
41105c1
Merge upstream
CAG2Mark Jan 13, 2025
21843b4
Fix overriding hack
LPTK Jan 14, 2025
862bd6a
Address PR comments
CAG2Mark Jan 14, 2025
ac70aea
Address PR comments
CAG2Mark Jan 14, 2025
051bc09
Fix inaccurate inheritance clause.
LPTK Jan 14, 2025
e4f4922
style change
AnsonYeung Jan 14, 2025
b89eb90
revert whitespace change
CAG2Mark Jan 14, 2025
b22ac0b
Merge branch 'ir-handler-transform' of github.com:CAG2Mark/mlscript i…
CAG2Mark Jan 14, 2025
05a8623
Merge branch 'hkmc2' into ir-handler-transform
AnsonYeung Jan 15, 2025
2b3e83e
Eliminate many tail calls to Lowering#term to reduce stack usage
LPTK Jan 16, 2025
e901993
Make Handle a statement, as it should be
LPTK Jan 16, 2025
3131154
merge from ir-handler-transform
CAG2Mark Jan 16, 2025
e8c690f
preserve tail-calls
CAG2Mark Jan 16, 2025
0c5d205
deal with lambdas, logic fixes
CAG2Mark Jan 16, 2025
5d54505
fix some cases, don't optimise trivial fns
CAG2Mark Jan 16, 2025
a6cdfc9
initial merge
CAG2Mark Jan 16, 2025
8a1af99
fix merge
CAG2Mark Jan 16, 2025
edbbe95
Clean up
CAG2Mark Jan 16, 2025
944f499
merge from ir-handler-transform
CAG2Mark Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix generator
  • Loading branch information
AnsonYeung committed Dec 25, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit e6cd1ec2001913735084f82923f096c839bd13a4
113 changes: 78 additions & 35 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mjs
Original file line number Diff line number Diff line change
@@ -216,27 +216,23 @@ const Predef$class = class Predef {
handlerCont = tmp;
handlerCont.tail = handlerCont;
tmp4: while (true) {
if (cur instanceof this.__Return.class) {
return cur;
} else {
if (cur instanceof this.__EffectSig.class) {
tmp1 = this.__handleEffect(cur, handler1, handlerCont);
nxt = tmp1;
scrut = cur === nxt;
if (scrut) {
cur.tailHandler.nextHandler = handlerCont;
cur.tailHandler = handlerCont;
cur.tail = handlerCont.tail;
return cur;
} else {
cur = nxt;
tmp2 = null;
}
tmp3 = tmp2;
continue tmp4;
} else {
if (cur instanceof this.__EffectSig.class) {
tmp1 = this.__handleEffect(cur, handler1, handlerCont);
nxt = tmp1;
scrut = cur === nxt;
if (scrut) {
cur.tailHandler.nextHandler = handlerCont;
cur.tailHandler = handlerCont;
cur.tail = handlerCont.tail;
return cur;
} else {
cur = nxt;
tmp2 = null;
}
tmp3 = tmp2;
continue tmp4;
} else {
return cur;
}
break;
}
@@ -287,7 +283,7 @@ const Predef$class = class Predef {
cur1.tailHandler = origTail;
return cur1;
} else {
tmp4 = this.__resume(handlerCont, null, null);
tmp4 = this.__resume(handlerCont, null);
tmp5 = tmp4(cur1) ?? null;
cur1 = tmp5;
tmp6 = null;
@@ -348,27 +344,25 @@ const Predef$class = class Predef {
}
__resume(cur2, tail) {
return (value) => {
let scrut, nextHandler, tailHandler, cont, scrut1, tmp, tmp1, tmp2, tmp3, tmp4;
let scrut, cont, scrut1, scrut2, scrut3, scrut4, nxt, scrut5, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22;
scrut = cur2.resumed;
if (scrut) {
throw Error("multiple resumption");
} else {
tmp = null;
}
cur2.resumed = true;
nextHandler = cur2.nextHandler;
tailHandler = cur2.tailHandler;
cont = cur2.next;
tmp5: while (true) {
tmp23: while (true) {
if (cont instanceof this.__Cont.class) {
tmp1 = cont.resume(value) ?? null;
value = tmp1;
if (value instanceof this.__EffectSig.class) {
value.tail = tail;
scrut1 = tailHandler !== cur2;
scrut1 = cur2.tailHandler !== cur2;
if (scrut1) {
value.tailHandler.nextHandler = nextHandler;
value.tailHandler = tailHandler;
value.tailHandler.nextHandler = cur2.nextHandler;
value.tailHandler = cur2.tailHandler;
tmp2 = null;
} else {
tmp2 = null;
@@ -379,20 +373,69 @@ const Predef$class = class Predef {
tmp3 = null;
}
tmp4 = tmp3;
continue tmp5;
continue tmp23;
} else {
if (nextHandler instanceof this.__HandleBlock.class) {
cont = nextHandler.next;
nextHandler = nextHandler.nextHandler;
tmp4 = null;
continue tmp5;
tmp4 = null;
}
break;
}
tmp24: while (true) {
scrut2 = cur2.nextHandler;
if (scrut2 instanceof this.__HandleBlock.class) {
scrut4 = cur2.nextHandler.next;
if (scrut4 instanceof this.__Cont.class) {
nxt = cur2.nextHandler.next;
tmp5 = nxt.resume(value) ?? null;
value = tmp5;
if (value instanceof this.__EffectSig.class) {
tmp6 = cur2.tailHandler !== cur2;
tmp7 = this.assert(tmp6) ?? null;
tmp8 = cur2.nextHandler !== null;
tmp9 = this.assert(tmp8) ?? null;
scrut5 = cur2.nextHandler.next === value.tail.next;
if (scrut5) {
value.tail.next = null;
tmp10 = null;
} else {
cur2.nextHandler.next = cur2.nextHandler.next.next;
tmp10 = null;
}
value.tail = tail;
value.tailHandler.nextHandler = cur2.nextHandler;
value.tailHandler = cur2.tailHandler;
return value;
} else {
tmp11 = cur2.nextHandler.next !== cur2.nextHandler.next.next;
tmp12 = this.assert(tmp11) ?? null;
cur2.nextHandler.next = cur2.nextHandler.next.next;
tmp13 = null;
}
tmp14 = tmp13;
continue tmp24;
} else {
tmp4 = value;
scrut3 = true;
if (scrut3) {
tmp15 = cur2.nextHandler.next === null;
tmp16 = this.assert(tmp15) ?? null;
tmp17 = cur2.nextHandler !== cur2.nextHandler.nextHandler;
tmp18 = this.assert(tmp17) ?? null;
cur2.nextHandler = cur2.nextHandler.nextHandler;
tmp14 = null;
continue tmp24;
} else {
tmp19 = cur2.nextHandler === null;
tmp20 = this.assert(tmp19) ?? null;
return value;
}
}
} else {
tmp21 = cur2.nextHandler === null;
tmp22 = this.assert(tmp21) ?? null;
return value;
}
break;
}
return tmp4;
return tmp14;
};
}
toString() { return "Predef"; }
89 changes: 75 additions & 14 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mls
Original file line number Diff line number Diff line change
@@ -83,11 +83,18 @@ fun __mkEffect(handler, handlerFun) =
set res.tailHandler = res
res

// fun checkEffectHead(cur) =
// if cur is __EffectSig then
// let hand = cur
// while hand.nextHandler !== null do
// set hand = hand.nextHandler
// assert(hand === cur.tailHandler)
// assert(cur.resumed === false)

fun __handleBlockImpl(cur, handler) =
let handlerCont = new __HandleBlock(null, null, null, handler)
set handlerCont.tail = handlerCont
while cur is
__Return then return cur
__EffectSig then
let nxt = __handleEffect(cur, handler, handlerCont)
if cur === nxt then
@@ -122,7 +129,7 @@ fun __handleEffect(cur, handler, handlerTailList) =
set cur.tailHandler = origTail
return cur
else
set cur = __resume(handlerCont, null, null)(cur)
set cur = __resume(handlerCont, null)(cur)
else if handler === cur.handler then
let savedNext = handlerTailList.next
set cur = cur.handlerFun(__resume(cur, handlerTailList))
@@ -142,26 +149,80 @@ fun __handleEffect(cur, handler, handlerTailList) =
set cur = nxt.resume(cur)
else cur

// fun __resume(cur, tail)(value) =
// if cur.resumed do
// throw Error("multiple resumption")
// set cur.resumed = true
// let cont = cur.next
// while
// cont is __Cont then
// value = cont.resume(value)
// if value is __EffectSig then
// set value.tail = tail
// if cur.tailHandler !== cur do
// set value.tailHandler.nextHandler = cur.nextHandler
// set value.tailHandler = cur.tailHandler
// return value
// else
// set cont = cont.next
// cur.nextHandler is __HandleBlock then
// set cont = cur.nextHandler.next
// set cur.nextHandler = cur.nextHandler.nextHandler
// else value

fun __resume(cur, tail)(value) =
if cur.resumed do
throw Error("multiple resumption")
set cur.resumed = true
let nextHandler = cur.nextHandler
let tailHandler = cur.tailHandler

let cont = cur.next
while
cont is __Cont then
value = cont.resume(value)
cont is __Cont do
// NOTE: we do not need to link to cont.next again if it is effsig
// because cont.resume will not change its own next
set value = cont.resume(value)
if value is __EffectSig then
set value.tail = tail
if tailHandler !== cur do
set value.tailHandler.nextHandler = nextHandler
set value.tailHandler = tailHandler
if cur.tailHandler !== cur do
set value.tailHandler.nextHandler = cur.nextHandler
set value.tailHandler = cur.tailHandler
return value
else
set cont = cont.next
nextHandler is __HandleBlock then
set cont = nextHandler.next
set nextHandler = nextHandler.nextHandler
else value

// We're done with the head, now resume the handle blocks
while
cur.nextHandler is __HandleBlock and
cur.nextHandler.next is __Cont then
// resuming tailHandlerList or post handler continuations
let nxt = cur.nextHandler.next
set value = nxt.resume(value)
if value is __EffectSig then
// console.dir(cur.nextHandler.next)
// console.dir(value.tail.next)
// console.dir(value)
// console.dir(cur)
// console.dir(nxt)
// console.log(nxt.resume.toString())
assert(cur.tailHandler !== cur)
assert(cur.nextHandler !== null)
// This checks when continuation resume results in tail call to effectful func
if cur.nextHandler.next === value.tail.next then
set value.tail.next = null
else
set cur.nextHandler.next = cur.nextHandler.next.next
set value.tail = tail
set value.tailHandler.nextHandler = cur.nextHandler
set value.tailHandler = cur.tailHandler
return value
else
assert(cur.nextHandler.next !== cur.nextHandler.next.next)
set cur.nextHandler.next = cur.nextHandler.next.next
// here using else will emit the wrong code and cause null to be returned from the loop
true then
// the list is empty, go to next handle block
assert(cur.nextHandler.next === null)
assert(cur.nextHandler !== cur.nextHandler.nextHandler)
set cur.nextHandler = cur.nextHandler.nextHandler
else
assert(cur.nextHandler === null)
return value
29 changes: 20 additions & 9 deletions hkmc2/shared/src/test/mlscript/handlers/Generators.mls
Original file line number Diff line number Diff line change
@@ -21,26 +21,37 @@ fun permutations_impl(gen, l1, l2) =
fun permutations(gen, l) =
permutations_impl(gen, [], l)

:fixme
let res = []
handle gen = Generator with
fun produce(result)(resume) =
res.push(result)
resume(())
in permutations(gen, [1, 2, 3, 4])
res
//│ ═══[RUNTIME ERROR] Error: Unhandled effects
//│ res = [ [ 1, 2, 3, 4 ] ]
//│ > [
//│ > [ 1, 2, 3, 4 ], [ 1, 2, 4, 3 ],
//│ > [ 1, 3, 2, 4 ], [ 1, 3, 4, 2 ],
//│ > [ 1, 4, 2, 3 ], [ 1, 4, 3, 2 ],
//│ > [ 2, 1, 3, 4 ], [ 2, 1, 4, 3 ],
//│ > [ 2, 3, 1, 4 ], [ 2, 3, 4, 1 ],
//│ > [ 2, 4, 1, 3 ], [ 2, 4, 3, 1 ],
//│ > [ 3, 1, 2, 4 ], [ 3, 1, 4, 2 ],
//│ > [ 3, 2, 1, 4 ], [ 3, 2, 4, 1 ],
//│ > [ 3, 4, 1, 2 ], [ 3, 4, 2, 1 ],
//│ > [ 4, 1, 2, 3 ], [ 4, 1, 3, 2 ],
//│ > [ 4, 2, 1, 3 ], [ 4, 2, 3, 1 ],
//│ > [ 4, 3, 1, 2 ], [ 4, 3, 2, 1 ]
//│ res = ]

// FIXME: result is wrong
fun permutations_foreach(l, f) =
handle gen = Generator with
fun produce(result)(resume) =
f(result)
resume(())
permutations(gen, l)

:fixme
res = permutations_foreach([1, 2, 3], print)
permutations_foreach([1, 2, 3], print)
//│ > 1,2,3
//│ ═══[RUNTIME ERROR] Error: Unhandled effects
//│ > 1,3,2
//│ > 2,1,3
//│ > 2,3,1
//│ > 3,1,2
//│ > 3,2,1
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ handle h = Eff with
k()
f(h, box, 10)
box.n
//│ ═══[RUNTIME ERROR] Error: Unhandled effects
//│ ═══[RUNTIME ERROR] Expected: 1023, got: 5120

handle h1 = Eff with
fun perform()(k) =
24 changes: 23 additions & 1 deletion hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls
Original file line number Diff line number Diff line change
@@ -78,4 +78,26 @@ in res.toString()
//│ > B hola amigos
//│ = '‹,‹,‹,‹,hola,friend,›,bye,›,amigos,›,adios,›'


let res =
print("A " + "hi")
handle g = Effect with
fun perform(arg1)(k1) =
print("B " + "hi" + " " + arg1)
["‹", k1("hi"), arg1, "›"]
g.perform("bye")
g.perform("adios")
print("A " + "hola")
handle g2 = Effect with
fun perform(arg1)(k1) =
print("B " + "hola" + " " + arg1)
["‹", k1("hola"), arg1, "›"]
g2.perform("adios")
g2.perform("amigos")
in res.toString()
//│ > A hi
//│ > B hi bye
//│ > B hi adios
//│ > A hola
//│ > B hola adios
//│ > B hola amigos
//│ = '‹,‹,‹,‹,hola,amigos,›,adios,›,adios,›,bye,›'