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

Compiler crash on leftover do-call in FFI after capability passing #546

Closed
leonfuss opened this issue Aug 19, 2024 · 4 comments · Fixed by #633
Closed

Compiler crash on leftover do-call in FFI after capability passing #546

leonfuss opened this issue Aug 19, 2024 · 4 comments · Fixed by #633
Assignees
Labels
bug Something isn't working

Comments

@leonfuss
Copy link

During language exploration I ran into an unexpected compiler crash. The compiler (version: 0.2.2) always crashed when trying to compile the following code:

import io/file

record File (
    path: String
)

def main() = {
    try {
       val file = open("test.meff")
       println(file)
    } with IOException[A] { (msg) =>
        println("Error opening file")
    }
}

def open(path: String) : File / IOException = {
    File(path)
}

Stacktrace

[error] Should have been translated away (to explicit selection `@CAP.op()`) by capability passing.
[info]   at effekt.util.messages$CompilerPanic$.apply(Messages.scala:37)
[info]   at effekt.util.messages$ErrorReporter.panic(Messages.scala:111)
[info]   at effekt.util.messages$ErrorReporter.panic$(Messages.scala:90)
[info]   at effekt.context.Context.panic(Context.scala:39)
[info]   at effekt.util.messages$ErrorReporter.panic(Messages.scala:112)
[info]   at effekt.core.Transformer$.transformAsExpr(Transformer.scala:479)
[info]   at effekt.core.Transformer$.transformAsPure(Transformer.scala:310)
[info]   at effekt.core.Transformer$.transform$$anonfun$6(Transformer.scala:148)
[info]   at effekt.core.Transformer$.$anonfun$59(Transformer.scala:659)
[info]   at effekt.core.TransformerOps.withBindings$$anonfun$1(Transformer.scala:786)
[info]   at effekt.context.ContextOps.in(Context.scala:29)
[info]   at effekt.context.ContextOps.in$(Context.scala:18)
[info]   at effekt.context.Context.effekt$namer$NamerOps$$super$in(Context.scala:39)
[info]   at effekt.namer.NamerOps.in(Namer.scala:739)
[info]   at effekt.namer.NamerOps.in$(Namer.scala:724)
[info]   at effekt.context.Context.in(Context.scala:82)
[info]   at effekt.core.TransformerOps.withBindings(Transformer.scala:789)
[info]   at effekt.core.TransformerOps.withBindings$(Transformer.scala:737)
[info]   at effekt.context.Context.withBindings(Context.scala:39)
[info]   at effekt.core.Transformer$.insertBindings(Transformer.scala:659)
[info]   at effekt.core.Transformer$.transform(Transformer.scala:148)
[info]   at effekt.core.Transformer$.transformAsControlBlock(Transformer.scala:302)
[info]   at effekt.core.Transformer$.transformAsBlock(Transformer.scala:212)
[info]   at effekt.core.Transformer$.transformBox(Transformer.scala:200)
[info]   at effekt.core.Transformer$.transformAsExpr(Transformer.scala:336)
[info]   at effekt.core.Transformer$.$anonfun$13(Transformer.scala:105)
[info]   at scala.collection.immutable.List.map(List.scala:250)
[info]   at effekt.core.Transformer$.transformToplevel(Transformer.scala:105)
[info]   at effekt.core.Transformer$.$anonfun$1(Transformer.scala:33)
[info]   at scala.collection.immutable.List.flatMap(List.scala:293)
[info]   at effekt.core.Transformer$.transform$$anonfun$1(Transformer.scala:33)
[info]   at effekt.context.Context.using$$anonfun$1(Context.scala:73)
[info]   at effekt.context.Context.using(Context.scala:74)
[info]   at effekt.core.Transformer$.transform(Transformer.scala:41)
[info]   at effekt.core.Transformer$.run(Transformer.scala:26)
[info]   at effekt.core.Transformer$.run(Transformer.scala:19)
[info]   at effekt.Phase$$anon$1.run$$anonfun$1(Phase.scala:51)
[info]   at scala.Option.flatMap(Option.scala:283)
[info]   at effekt.Phase$$anon$1.run(Phase.scala:51)
[info]   at effekt.Phase$task$2$.run(Phase.scala:87)
[info]   at effekt.util.Task$.$anonfun$1(Task.scala:144)
[info]   at effekt.context.Context.withMessages(Context.scala:97)
[info]   at effekt.util.Task$.compute(Task.scala:145)
[info]   at effekt.util.Task$.need(Task.scala:174)
[info]   at effekt.util.Task$.need(Task.scala:165)
[info]   at effekt.util.Task.apply(Task.scala:29)
[info]   at effekt.util.Task.apply$(Task.scala:9)
[info]   at effekt.Phase$task$2$.apply(Phase.scala:85)
[info]   at effekt.Phase$$anon$4.run(Phase.scala:94)
[info]   at effekt.Phase.apply(Phase.scala:41)
[info]   at effekt.Phase.apply$(Phase.scala:34)
[info]   at effekt.Phase$$anon$4.apply(Phase.scala:92)
[info]   at effekt.Compiler$$anon$3.run$$anonfun$3$$anonfun$1(Compiler.scala:271)
[info]   at scala.collection.immutable.List.foldRight(List.scala:352)
[info]   at effekt.Compiler$$anon$3.run$$anonfun$3(Compiler.scala:273)
[info]   at scala.Option$WithFilter.flatMap(Option.scala:283)
[info]   at effekt.Compiler$$anon$3.run(Compiler.scala:274)
[info]   at effekt.Compiler$$anon$3.run(Compiler.scala:268)
[info]   at effekt.Phase$$anon$2.run(Phase.scala:56)
[info]   at effekt.Phase$$anon$2.apply(Phase.scala:54)
[info]   at effekt.generator.js.JavaScript.compile(JavaScript.scala:32)
[info]   at effekt.Driver.compile$1(Driver.scala:71)
[info]   at effekt.Driver.compileSource(Driver.scala:82)
[info]   at effekt.Driver.compileSource$(Driver.scala:21)
[info]   at effekt.Server$.compileSource(Server.scala:263)
[info]   at kiama.util.Compiler.compileFile(Compiler.scala:102)
[info]   at kiama.util.Compiler.compileFile$(Compiler.scala:22)
[info]   at effekt.Server$.compileFile(Server.scala:263)
[info]   at effekt.Driver.run$$anonfun$1(Driver.scala:41)
[info]   at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[info]   at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[info]   at scala.collection.immutable.List.foreach(List.scala:333)
[info]   at effekt.Driver.run(Driver.scala:42)
[info]   at effekt.Driver.run$(Driver.scala:21)
[info]   at effekt.Server$.run(Server.scala:263)
[info]   at kiama.util.Compiler.driver(Compiler.scala:86)
[info]   at kiama.util.Compiler.driver$(Compiler.scala:22)
[info]   at effekt.Server$.driver(Server.scala:263)
[info]   at kiama.util.Compiler.main(Compiler.scala:50)
[info]   at kiama.util.Compiler.main$(Compiler.scala:22)
[info]   at effekt.Server$.main(Server.scala:263)
[info]   at effekt.Server.main(Server.scala)

Please don’t hesitate to reach out if any additional informations are required!

@jiribenes
Copy link
Contributor

Hi @leonfuss, thanks for reporting!
I was able to reproduce this on 0.2.2 (with nix run github:jiribenes/effekt-nix -- <reprofile>) and I'll take a look if we fixed the underlying problem (an extra do <something> still existing after capability passing)

case source.Do(effect, id, targs, vargs, bargs) =>
Context.panic("Should have been translated away (to explicit selection `@CAP.op()`) by capability passing.")

I'll rename the title to make the exact nature of the issue more apparent.

The current state on master is some +500 new commits since the last release with a lot of new features and libraries (a much better IO library for example). We're aiming to release newer versions more often, see #538 (ideally starting this/next week).

Workarounds

If you just want to get IO working, I think that the file API on 0.2.2 required some hard to explain AsyncIO magic:

import io/async

def process(fileName: String): Unit / {AsyncIO} = {
    val promise = do read(fileName)
    val contents: String = promise.block()

    // ...
    ()
}

def main() = {
    asyncIO(box { process("input") })
}

It's somewhat more intuitive to do this on master:

import io
import io/files
import io/error

def process(fileName: String): Unit / {Files} = {
  val contents: String = do readFile(fileName)

  // ...
  ()
}

// Note: the eventloop currently must be encompassing the whole program
def main() = eventloop(box {
    with on[IOError].panic;
    with filesystem;

    process("input")
  })
}

@jiribenes jiribenes changed the title Unexpected Compiler Crash Compiler crash on leftover do-call after capability passing in v0.2.2 Aug 19, 2024
@jiribenes jiribenes added the bug Something isn't working label Aug 19, 2024
@jiribenes

This comment was marked as outdated.

@jiribenes
Copy link
Contributor

jiribenes commented Aug 19, 2024

Yeah, in v0.2.2 just the import already produces the same crash:

import io/file
def main() = ()

... and if we minimise io/file, we find the actual culprit:

def await[T](p: Promise[T]): T / { IOException } =
  try { awaitImpl(p){exc} } with exc : IOException[A] {
    msg => do IOException(msg)
  }

extern io def awaitImpl[T](p: Promise[T]){exc: IOException}: T =
  "$effekt.callcc(k => ${p}.then(res => k($effekt.pure(res)), err => k(${box { (msg: String) => do IOException[T](msg) }}(err.message)))).then(c => c)"
  // here instead of `do IOException`, we probably wanted to do `exc.IOException` instead!
  // ... if you do that, the issue goes away completely :)

@jiribenes
Copy link
Contributor

jiribenes commented Aug 19, 2024

OK, here's a very tiny version that still fails on master:

effect IOException(msg: String): Nothing
extern io def three {exc: IOException}: Nothing =
  js "${box { (msg: String) => do IOException(msg) }}('oops')}"

with the original error message:

[error] Should have been translated away (to explicit selection `@CAP.op()`) by capability passing.
[info]   at effekt.util.messages$CompilerPanic$.apply(Messages.scala:37)
...

... which means that this is still a relevant bug, even though the io/file library no longer exists.

I haven't been able to remove the weird indirection through FFI, but I'm running out of time and ideas :)
(my guess is something like "capability passing doesn't see through FFI splices" which makes sense, we don't even allow unhandled control effects on extern defs anyway)

@jiribenes jiribenes changed the title Compiler crash on leftover do-call after capability passing in v0.2.2 Compiler crash on leftover do-call in FFI after capability passing Aug 20, 2024
@dvdvgt dvdvgt self-assigned this Oct 2, 2024
@github-project-automation github-project-automation bot moved this from Waiting for Review to Done in Effekt Hackathon 02.10.2024 Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Development

Successfully merging a pull request may close this issue.

3 participants