Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
bf9e386
Add scoped definition
NeilKleistGao Nov 11, 2025
887ca0b
WIP: Try to get symbols for scoped
NeilKleistGao Nov 14, 2025
148006b
wip
ychenfo Nov 16, 2025
b28875a
wip and some tests
ychenfo Nov 16, 2025
5f9e63c
WIP: Try
NeilKleistGao Nov 17, 2025
cf9d78f
Try to use a pass to insert Scoped
NeilKleistGao Nov 18, 2025
44f132f
Revert "Try to use a pass to insert Scoped"
ychenfo Nov 18, 2025
07eff8f
Revert "WIP: Try"
ychenfo Nov 18, 2025
3108bac
wip:
ychenfo Nov 20, 2025
ab0d660
wip: add known bugs in the tests
ychenfo Nov 20, 2025
79bd913
wip wip
ychenfo Nov 22, 2025
c5d16e0
Revert "wip wip"
ychenfo Nov 23, 2025
69ad890
Revert "wip: add known bugs in the tests"
ychenfo Nov 23, 2025
aeb0c5a
Revert "wip:"
ychenfo Nov 23, 2025
a9cbbf9
Merge remote-tracking branch 'origin/hkmc2' into scope🥨
ychenfo Nov 23, 2025
021c1a0
use collection.Set
ychenfo Nov 23, 2025
0d4ae05
wip: a single if in a nested block..?
ychenfo Nov 23, 2025
f4ccdf5
wip
ychenfo Nov 23, 2025
e796c84
many collection of symbols created during lowering
ychenfo Nov 23, 2025
95c3c12
forgot to collect some symbols and create some scoped blocks
ychenfo Nov 23, 2025
245b9d4
minor
ychenfo Nov 23, 2025
214fc28
minor fix
ychenfo Nov 23, 2025
15f35be
fix two tests
ychenfo Nov 23, 2025
30c1ae9
hopefully complete `Term.definedSyms`
ychenfo Nov 23, 2025
46dd487
wip: make all tests pass
ychenfo Nov 24, 2025
046827a
wip: meaningful use scoped var a bit... now tests related to handler …
ychenfo Nov 24, 2025
12ec16e
wip: nested Scoped blocks for pattern matching bodies with bugs
ychenfo Nov 26, 2025
a11aa2d
Revert "wip: nested Scoped blocks for pattern matching bodies with bugs"
ychenfo Nov 26, 2025
50253ae
wip: try to fix errors in handler tests
ychenfo Nov 27, 2025
1871ea2
wip: smart constructors..?
ychenfo Nov 27, 2025
dde2adf
Revert "wip: try to fix errors in handler tests"
ychenfo Nov 27, 2025
112f419
wip: all tests can pass by not putting scoped blocks for UCS under th…
ychenfo Nov 27, 2025
4aa8c35
update tests
ychenfo Nov 27, 2025
a9cdfd1
jsbuilder: scope.nest.givenIn
ychenfo Nov 27, 2025
7f71ebe
Merge remote-tracking branch 'origin/hkmc2' into scope🥨
ychenfo Nov 27, 2025
2b0590e
WIP: Fix top-level scope & add nest
NeilKleistGao Nov 28, 2025
64385a2
WIP: Try to remove redundant braces (not clean up yet)
NeilKleistGao Nov 29, 2025
36f95ce
WIP: Remove redundant braces (not clean up yet)
NeilKleistGao Nov 29, 2025
0f2f069
WIP: Revert & try to handle non-nested Scoped separately
NeilKleistGao Nov 29, 2025
495956c
more tests; add a fixme; minor
ychenfo Nov 29, 2025
9b5d327
fix incorrect scoped symbols
ychenfo Nov 30, 2025
a64ac1f
get rid of `Term.definedSyms`
ychenfo Dec 1, 2025
ab74823
get rid of vararg
ychenfo Dec 1, 2025
d20dcca
rename
ychenfo Dec 1, 2025
09ea124
fix problems related to handlers in nested `if`/`while`s:
ychenfo Dec 1, 2025
1948288
cleanup
ychenfo Dec 1, 2025
4c7904e
Revert "cleanup"
ychenfo Dec 2, 2025
5ce54b6
Revert "fix problems related to handlers in nested `if`/`while`s:"
ychenfo Dec 2, 2025
10c2ae5
disable while loop rewriting; a temp fix
ychenfo Dec 2, 2025
80fa081
WIP: Add scope.locally
NeilKleistGao Dec 3, 2025
90efc09
Rerun testss
NeilKleistGao Dec 3, 2025
8b3b675
more fixes related to handlers
ychenfo Dec 3, 2025
3dd416f
Merge remote-tracking branch 'cunyuan/scope🥨' into scope🥨
ychenfo Dec 3, 2025
cbe96e0
Minor fix and clean up
NeilKleistGao Dec 4, 2025
e060b40
Remove dontFlatten field of Scoped and remove some other unexpected c…
NeilKleistGao Dec 4, 2025
1ac0ae9
fix scopes for nested if and `floatOutUntilScope`
ychenfo Dec 4, 2025
30f3850
Merge remote-tracking branch 'cunyuan/scope🥨' into scope🥨
ychenfo Dec 4, 2025
d0e2164
Merge remote-tracking branch 'origin/hkmc2' into scope🥨
ychenfo Dec 4, 2025
63fe1c4
try to add some comments to the entangled kludge
ychenfo Dec 4, 2025
43fdc78
update loop rewriting flag
ychenfo Dec 4, 2025
08d4467
some cleanup
ychenfo Dec 4, 2025
9d6b59c
Change the form of scope.locally
NeilKleistGao Dec 5, 2025
9f23fed
Fix the missing case
NeilKleistGao Dec 5, 2025
70ed84a
pr comments
ychenfo Dec 5, 2025
8c11671
cleanup and no "/** scoped **/"
ychenfo Dec 5, 2025
7dc143d
fixes
ychenfo Dec 5, 2025
0227b70
Make some improvements
LPTK Dec 5, 2025
5d99227
Minor fix
NeilKleistGao Dec 6, 2025
a6b1adb
Minor
LPTK Dec 6, 2025
fbc8c13
Merge remote-tracking branch 'origin/hkmc2' into scope🥨
ychenfo Dec 6, 2025
9231b1b
update comments for `definedVarsNoScoped`
ychenfo Dec 6, 2025
899ee24
Add some comments
NeilKleistGao Dec 7, 2025
2482b1e
Update hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala
ychenfo Dec 7, 2025
06aa495
Minor
LPTK Dec 8, 2025
7962e06
Fix flatten for nested scopes
NeilKleistGao Dec 10, 2025
562f927
Changes from meeting
LPTK Dec 10, 2025
3b8ccab
Merge from hkmc2 and fix ctor scope problem
NeilKleistGao Dec 12, 2025
a869edc
Remove braces in switch
NeilKleistGao Dec 12, 2025
2724146
Revert unnecessary changes
NeilKleistGao Dec 12, 2025
a8150c9
check before merge scoped
ychenfo Dec 14, 2025
4631ec2
add tests
ychenfo Dec 14, 2025
7c1007a
pre handler pass
ychenfo Dec 15, 2025
34faf7b
maintain scoped blocks in lambda rewriter
ychenfo Dec 15, 2025
7734f68
remove too much `:sjs` in tests; update comments in tests
ychenfo Dec 15, 2025
347b3e8
`registerTempSymbol` in `Lowering.scala`
ychenfo Dec 15, 2025
f2687cc
Update hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers…
ychenfo Dec 15, 2025
91f679b
output `preStr` for ssjs
ychenfo Dec 15, 2025
50ba727
validate IR without `Config.validateIR` for now
ychenfo Dec 15, 2025
4c47bbc
Merge remote-tracking branch 'origin/hkmc2' into scope🥨
ychenfo Dec 15, 2025
31348be
simplify `LambdaRewriter`
ychenfo Dec 15, 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
13 changes: 9 additions & 4 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ sealed abstract class Block extends Product:
case _: End => true
case _ => false

/** This variation of `definedVars` excludes the `syms` in `Scoped` blocks
* so that `Scoped` blocks' vars can be seen in the output js code;
* otherwise `blockPreamble` allocates all the `definedVars` such that
* we cannot see and test vars generated because of the existence of the `Scoped` blocks */
/** This variation of `definedVars` excludes the `syms` in `Scoped` blocks.
* It is used in JSBuilder now: names are allocated in JSBuilder for these `definedVarsNoScoped` symbols.
* This is needed now because
* - there are symbols that are not collected in the `Scoped` blocks (due to later passes),
* and they still need to be allocated a name in JSBuilder
* - if we don't exlude the `Scoped` symbols, they may be allocated a name
* prematurely and decalred in wrong places, e.g. symbols inside while bodies
* may be declared in an outer level
*/
lazy val definedVarsNoScoped: Set[Local] = this match
case _: Return | _: Throw => Set.empty
case Begin(sub, rst) => sub.definedVarsNoScoped ++ rst.definedVarsNoScoped
Expand Down
10 changes: 5 additions & 5 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case (sym, params, split) =>
val paramLists = params :: Nil
val bodyBlock = inScopedBlock(ucs.Normalization(this)(split)(Ret))
FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(isTailRec = false)
FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(forceTailRec = false)
// The return type is intended to be consistent with `gatherMembers`
(mtds, Nil, Nil, End())
case _ => gatherMembers(defn.body)
Expand Down Expand Up @@ -526,7 +526,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
N,
lamSym,
PlainParamList(Nil) :: Nil,
inScopedBlock(returnedTerm(arg2)))(isTailRec = false)
inScopedBlock(returnedTerm(arg2)))(forceTailRec = false)
Define(
lamDef,
k(Call(
Expand Down Expand Up @@ -678,7 +678,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
else
val lamSym = new BlockMemberSymbol("lambda", Nil, false)
loweringCtx.collectScopedSym(lamSym)
val lamDef = FunDefn.withFreshSymbol(N, lamSym, paramLists, bodyBlock)(isTailRec = false)
val lamDef = FunDefn.withFreshSymbol(N, lamSym, paramLists, bodyBlock)(forceTailRec = false)
Define(
lamDef,
k(lamDef.asPath))
Expand Down Expand Up @@ -1025,8 +1025,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case Lambda(params, body) =>
val lamSym = BlockMemberSymbol("lambda", Nil, false)
loweringCtx.collectScopedSym(lamSym)
val lamDef = FunDefn.withFreshSymbol(N, lamSym, params :: Nil, body)(isTailRec = false)
Define(lamDef, k(Value.Ref(lamSym, N)))
val lamDef = FunDefn.withFreshSymbol(N, lamSym, params :: Nil, body)(forceTailRec = false)
Define(lamDef, k(lamDef.asPath))
case r =>
val l = new TempSymbol(N)
loweringCtx.collectScopedSym(l)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e
val res = new BlockMemberSymbol("while", Nil, false)
outerCtx.collectScopedSym(res)
res
lazy val tSym = TermSymbol.fromFunBms(f, N)
val normalized = tl.scoped("ucs:normalize"):
normalize(inputSplit)(using VarSet())
tl.scoped("ucs:normalized"):
Expand All @@ -352,7 +353,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e
// NOTE: `shouldRewriteWhile` is not the same as `config.rewriteWhileLoops`
// as shouldRewriteWhile is always true when effect handler lowering is on
lazy val loopCont = if config.shouldRewriteWhile
then Return(Call(Value.Ref(f, N), Nil)(true, true, false), false)
then Return(Call(Value.Ref(f, S(tSym)), Nil)(true, true, false), false)
else Continue(loopLabel)
val cont =
if kw === `while` then
Expand Down Expand Up @@ -418,8 +419,8 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e
Select(Value.Ref(State.runtimeSymbol), Tree.Ident("LoopEnd"))(S(State.loopEndSymbol))
val blk = blockBuilder
.assign(l, Value.Lit(Tree.UnitLit(false)))
.define(FunDefn.withFreshSymbol(N, f, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd, false)))(isTailRec = false))
.assign(loopResult, Call(Value.Ref(f, N), Nil)(true, true, false))
.define(FunDefn(N, f, tSym, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd, false)))(forceTailRec = false))
.assign(loopResult, Call(Value.Ref(f, S(tSym)), Nil)(true, true, false))
if summon[LoweringCtx].mayRet then
blk
.assign(isReturned, Call(Value.Ref(State.builtinOpsMap("!==")),
Expand Down
6 changes: 3 additions & 3 deletions hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ case
//│ modulefulness = Modulefulness of N
//│ restParam = N
//│ body = Scoped:
//│ syms = HashSet($argument0$, a)
//│ syms = HashSet(a, $argument0$)
//│ body = Match:
//│ scrut = Ref:
//│ l = caseScrut
Expand Down Expand Up @@ -142,9 +142,9 @@ case
//│ value = Lit of StrLit of "match error"
//│ rest = End of ""
//│ rest = Return: \
//│ res = Ref:
//│ res = Ref{disamb=term:lambda}:
//│ l = member:lambda
//│ disamb = N
//│ disamb = S of term:lambda
//│ implct = true
//│ = fun

Expand Down
4 changes: 2 additions & 2 deletions hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ fun foo() = if false do foo()
//│ JS (unsanitized):
//│ let foo;
//│ foo = function foo() {
//│ let scrut;
//│ loopLabel: while (true) {
//│ let scrut;
//│ scrut = false;
//│ if (scrut === true) { continue loopLabel } else { return runtime.Unit }
//│ break;
Expand Down Expand Up @@ -68,7 +68,7 @@ do
//│ JS (unsanitized):
//│ let f, f1;
//│ f1 = 1;
//│ f = function f() { loopLabel: while (true) { continue loopLabel; break; } };
//│ f = function f() { loopLabel: while (true) { let tmp; continue loopLabel; break; } };
//│ runtime.Unit
//│ f = 1

Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ fun popByIndex(start, end, acc, lft) =
//│ JS (unsanitized):
//│ let popByIndex;
//│ popByIndex = function popByIndex(start, end, acc, lft) {
//│ let scrut, tmp34, tmp35, tmp36;
//│ loopLabel: while (true) {
//│ let scrut, tmp34, tmp35, tmp36;
//│ scrut = start >= end;
//│ if (scrut === true) {
//│ return acc
Expand Down
14 changes: 10 additions & 4 deletions hkmc2/shared/src/test/mlscript/tailrec/Simple.mls
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@tailrec
fun f = f
//│ JS (unsanitized):
//│ let f; f = function f() { loopLabel: while (true) { continue loopLabel; break; } };
//│ let f; f = function f() { loopLabel: while (true) { let tmp; continue loopLabel; break; } };

:fixme
module Foo with
Expand All @@ -29,8 +29,14 @@ fun f(x) = f(f(x) + 1)
//│ JS (unsanitized):
//│ let f3;
//│ f3 = function f(x) {
//│ let tmp, tmp1;
//│ loopLabel: while (true) { tmp = f3(x); tmp1 = tmp + 1; x = tmp1; continue loopLabel; break; }
//│ loopLabel: while (true) {
//│ let tmp, tmp1;
//│ tmp = f3(x);
//│ tmp1 = tmp + 1;
//│ x = tmp1;
//│ continue loopLabel;
//│ break;
//│ }
//│ };

module A with
Expand All @@ -44,7 +50,7 @@ module A with
module B with
fun g(x) = A.f(x * 2)
//│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec.
//│ ║ l.43: fun f(x) = B.g(x + 1)
//│ ║ l.49: fun f(x) = B.g(x + 1)
//│ ╙── ^


20 changes: 11 additions & 9 deletions hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@ fun f(a, b, c) =
else c
f(10000, 20000, 0)
//│ JS (unsanitized):
//│ let f, g, g_f;
//│ let f, g;
//│ let g_f;
//│ g_f = function g_f(id, param0, param1, param2, param3) {
//│ let scrut, scrut1, tmp, tmp1, tmp2;
//│ loopLabel: while (true) {
//│ if (id === 0) {
//│ let tmp;
//│ tmp = param2 + param3;
//│ param2 = tmp;
//│ id = 1;
//│ continue loopLabel
//│ } else if (id === 1) {
//│ let scrut, scrut1, tmp1, tmp2;
//│ scrut = param0 > 0;
//│ if (scrut === true) {
//│ tmp1 = param0 - 1;
Expand Down Expand Up @@ -72,8 +74,8 @@ A.sum(20000)
//│ runtime.Unit;
//│ }
//│ static sum_impl(n, acc) {
//│ let scrut, tmp, tmp1;
//│ loopLabel: while (true) {
//│ let scrut, tmp, tmp1;
//│ scrut = Predef.equals(n, 0);
//│ if (scrut === true) {
//│ return acc
Expand Down Expand Up @@ -143,7 +145,7 @@ fun f(x) =
@tailcall f(x)
@tailcall g()
//│ ╔══[ERROR] This call is not in tail position.
//│ ║ l.142: @tailcall f(x)
//│ ║ l.144: @tailcall f(x)
//│ ╙── ^

:lift
Expand All @@ -161,10 +163,10 @@ module A with
@tailcall g(x - 1)
A.f(10000)
//│ ╔══[ERROR] This tail call exits the current scope is not optimized.
//│ ║ l.160: fun g(x) = if x < 0 then 0 else @tailcall f(x)
//│ ║ l.162: fun g(x) = if x < 0 then 0 else @tailcall f(x)
//│ ╙── ^^^
//│ ╔══[ERROR] This tail call exits the current scope is not optimized.
//│ ║ l.161: @tailcall g(x - 1)
//│ ║ l.163: @tailcall g(x - 1)
//│ ╙── ^^^^^^^^
//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded

Expand All @@ -191,12 +193,12 @@ class A with
fun g(x) = if x == 0 then 1 else @tailcall f(x - 1)
(new A).f(10)
//│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall.
//│ ║ l.190: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1)
//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1)
//│ ╙── ^^^^^^^^
//│ ╔══[ERROR] Class methods may not yet be marked @tailrec.
//│ ║ l.190: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1)
//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1)
//│ ╙── ^
//│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall.
//│ ║ l.191: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1)
//│ ║ l.193: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1)
//│ ╙── ^^^^^^^^
//│ = 0
2 changes: 2 additions & 0 deletions hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ abstract class MLsDiffMaker extends DiffMaker:
val importQQ = NullaryCommand("qq")
val stageCode = NullaryCommand("staging")
val rewriteWhile = NullaryCommand("rewriteWhile")
val noTailRecOpt = NullaryCommand("noTailRec")

def mkConfig: Config =
import Config.*
Expand Down Expand Up @@ -100,6 +101,7 @@ abstract class MLsDiffMaker extends DiffMaker:
stageCode = stageCode.isSet,
target = if wasm.isSet then CompilationTarget.Wasm else CompilationTarget.JS,
rewriteWhileLoops = rewriteWhile.isSet,
tailRecOpt = !noTailRecOpt.isSet,
)


Expand Down
Loading