effekt-lang / effekt

A language with lexical effect handlers and lightweight effect polymorphism
https://effekt-lang.org
MIT License
334 stars 24 forks source link

Mistranslation of match guard + while loop #577

Closed marzipankaiser closed 2 months ago

marzipankaiser commented 2 months ago

Problem

The following program:

def foo(): Int = {
  var queue: Bool = true
  while (queue is true) {
    queue = false
  }
  0
}

def main() = {
  println(foo())
}

Crashes on all backends with an error message from the underlying implementation (node/chez / machine.Transformer) that b_whileLoop_nnnn is not defined.

Error messages when running

JS ``` /Users/gaisseml/dev/effekt/experiments/out/test_copy.js:819 return b_whileLoop_2855_3991(); ^ ReferenceError: b_whileLoop_2855_3991 is not defined at /Users/gaisseml/dev/effekt/experiments/out/test_copy.js:819:9 at /Users/gaisseml/dev/effekt/experiments/out/test_copy.js:820:9 at Object.apply (/Users/gaisseml/dev/effekt/experiments/out/test_copy.js:515:19) at trampoline (/Users/gaisseml/dev/effekt/experiments/out/test_copy.js:435:19) at Object.run (/Users/gaisseml/dev/effekt/experiments/out/test_copy.js:542:18) at main_2840 (/Users/gaisseml/dev/effekt/experiments/out/test_copy.js:822:8) at Object.main (/Users/gaisseml/dev/effekt/experiments/out/test_copy.js:828:15) at Object. (/Users/gaisseml/dev/effekt/experiments/out/test_copy:2:27) at Module._compile (node:internal/modules/cjs/loader:1480:14) at Module._extensions..js (node:internal/modules/cjs/loader:1564:10) Node.js v22.1.0 [error] Process exited with non-zero exit code 1. ```
Chez (lift, callcc, and monadic) ``` Exception: variable b_whileLoop_2855_3988 is not bound [error] Process exited with non-zero exit code 255. ```
LLVM ``` Exception in thread "main" java.util.NoSuchElementException: key not found: b_whileLoop_2397 at scala.collection.immutable.Map$Map4.apply(Map.scala:515) at effekt.machine.Transformer$.transform(Transformer.scala:155) at effekt.machine.Transformer$.transform(Transformer.scala:147) at effekt.machine.Transformer$.transform$$anonfun$19(Transformer.scala:242) at effekt.machine.Transformer$.transform$$anonfun$39$$anonfun$1(Transformer.scala:463) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.pure$$anonfun$1(Transformer.scala:636) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.pure$$anonfun$1(Transformer.scala:636) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.pure$$anonfun$1(Transformer.scala:636) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.transform$$anonfun$35(Transformer.scala:442) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.pure$$anonfun$1(Transformer.scala:636) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$Binding.flatMap$$anonfun$1(Transformer.scala:632) at effekt.machine.Transformer$.transform(Transformer.scala:242) at effekt.machine.Transformer$.transform(Transformer.scala:147) at effekt.machine.Transformer$.transform(Transformer.scala:148) at effekt.machine.Transformer$.transform$$anonfun$24(Transformer.scala:322) at effekt.machine.Transformer$.pure$$anonfun$1(Transformer.scala:636) at effekt.machine.Transformer$.transform(Transformer.scala:322) at effekt.machine.Transformer$.transform(Transformer.scala:108) at effekt.machine.Transformer$.transform$$anonfun$42(Transformer.scala:492) at effekt.machine.Transformer$.transform$$anonfun$4(Transformer.scala:112) at scala.collection.immutable.List.foldRight(List.scala:352) at effekt.machine.Transformer$.transform(Transformer.scala:134) at effekt.machine.Transformer$.$anonfun$2(Transformer.scala:42) at scala.collection.LinearSeqOps.foldLeft(LinearSeq.scala:183) at scala.collection.LinearSeqOps.foldLeft$(LinearSeq.scala:179) at scala.collection.immutable.List.foldLeft(List.scala:79) at effekt.machine.Transformer$.transform(Transformer.scala:45) at effekt.Compiler.Machine$$anonfun$1$$anonfun$1(Compiler.scala:320) at effekt.Phase$$anon$3.run(Phase.scala:73) at effekt.Phase$$anon$1.run$$anonfun$1(Phase.scala:51) at scala.Option.flatMap(Option.scala:283) at effekt.Phase$$anon$1.run(Phase.scala:51) at effekt.Phase$$anon$2.run(Phase.scala:56) at effekt.Phase.apply(Phase.scala:41) at effekt.Phase.apply$(Phase.scala:34) at effekt.Phase$$anon$2.apply(Phase.scala:54) at effekt.generator.llvm.LLVM.compile(LLVM.scala:35) at effekt.Driver.compile$1$$anonfun$1(Driver.scala:73) at effekt.util.Timers.timed(Timer.scala:39) at effekt.util.Timers.timed$(Timer.scala:12) at effekt.context.Context.timed(Context.scala:40) at effekt.Driver.compile$1(Driver.scala:81) at effekt.Driver.compileSource(Driver.scala:85) at effekt.Driver.compileSource$(Driver.scala:21) at effekt.Server$.compileSource(Server.scala:263) at effekt.Server$.compileSource(Server.scala:263) at kiama.util.Compiler.compileFile(Compiler.scala:102) at kiama.util.Compiler.compileFile$(Compiler.scala:22) at effekt.Server$.compileFile(Server.scala:263) at effekt.Driver.run$$anonfun$1(Driver.scala:41) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at effekt.Driver.run(Driver.scala:42) at effekt.Driver.run$(Driver.scala:21) at effekt.Server$.run(Server.scala:263) at effekt.Server$.run(Server.scala:263) at kiama.util.Compiler.driver(Compiler.scala:86) at kiama.util.Compiler.driver$(Compiler.scala:22) at effekt.Server$.driver(Server.scala:263) at kiama.util.Compiler.main(Compiler.scala:50) at kiama.util.Compiler.main$(Compiler.scala:22) at effekt.Server$.main(Server.scala:263) at effekt.Server.main(Server.scala) ```

Generated core

def foo3326() =
  val v_r_33363349: Bool379 = true;
  var queue3327 = v_r_33363349 ;
  def b_k_33413350() =
    let v_whileThen_33403351 = queue3327.put(false)

    b_whileLoop_33373352();
  def b_k_33433353() =
    ();
  def b_whileLoop_33373352() =
    let v_r_33423354 = queue3327.get()

    if (infixEq89(v_r_33423354, true)) {
      b_k_33413350()
    } else {
      b_k_33433353()
    }

  b_whileLoop_33373352();
  0;
def main3325() =
  let v_r_33463355 = run {foo3326()}

  println6(v_r_33463355)

Here, b_whileLoop_33373352 is defined after its first usage.

b-studios commented 2 months ago

~Might be related to the free variable computation, which I tried to fix in #563. It could be that lambda lifting doesn't see this free variable and then doesn't preserve semantics.~

I sure read the whole issue before replying.

Could you still try the same on #563 to see whether it fixed it?

b-studios commented 2 months ago

It still might be related to free variables. The join points should be generated within the loop. Maybe they are and then lifted out?

b-studios commented 2 months ago

Maybe relevant:

Here we delimit bind with the handler insertBindings:

https://github.com/effekt-lang/effekt/blob/ba3741069c46129ab1823e4b137df8b3bab0c73b/effekt/shared/src/main/scala/effekt/core/Transformer.scala#L426

while here we don't:

https://github.com/effekt-lang/effekt/blob/ba3741069c46129ab1823e4b137df8b3bab0c73b/effekt/shared/src/main/scala/effekt/core/Transformer.scala#L428

marzipankaiser commented 2 months ago

563 does not fix it.

But your observation about insertBindings seems related (adding it for 428ff fixes this example). I will open a PR.