dotty-cps-async icon indicating copy to clipboard operation
dotty-cps-async copied to clipboard

`.await` blows up with a macro exception

Open arturaz opened this issue 4 months ago • 2 comments

    val dbIO = async[ConnectionIO] {
      GeneralPracticeUserPermission.InviteUser.check(auth.userId, req.generalPracticeId).await
      val invitedAt = GeneralPracticeUserInvitedAt.nowIO.toConnectionIO.await
      /** run here causes the exception, it's nonsense, but the compiler should say that it's not awaitable.
       * 
       * https://typelevel.org/cats/api/cats/free/Free.html#run(implicitS:cats.Comonad[S]):A
       */
      val existingUser = GeneralPracticeUsers.queryIdByEmail(req.email).option.run.await
      val user = existingUser match {
        case Some(existingUserId) =>
          val assignedAt = GeneralPracticeUserAssignedToPracticeAt.nowIO.toConnectionIO.await
          val role = GeneralPracticeUserRole.Member
          val row = GeneralPracticesToUser.Row(req.generalPracticeId, existingUserId, role, assignedAt)
          GeneralPracticesToUser.Row.insert.toUpdate0(row).run.singleOrThrow_!.await
          val (fullName, phone) = queryGeneralPracticeUserDetails(existingUserId).unique.await
          GeneralPracticesListUsersResponse.ActiveUser(
            existingUserId,
            fullName,
            phone,
            role,
            assignedAt,
          )

        case None =>
          val row = GeneralPracticePendingUsers.Row(req.email, req.generalPracticeId, auth.userId, invitedAt)
          GeneralPracticePendingUsers.Row.insert.toUpdate0(row).run.singleOrThrow_!.await
          val (fullName, _) = queryGeneralPracticeUserDetails(auth.userId).unique.await
          GeneralPracticesListUsersResponse.PendingUser(req.email, auth.userId, fullName, invitedAt)
      }
      GeneralPracticesInviteUserResponse(user)
    }

Blows up with:

Exception occurred while executing macro expansion.
java.lang.AssertionError: NoDenotation.owner
at dotty.tools.dotc.core.SymDenotations$NoDenotation$.owner(SymDenotations.scala:2655)
at scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.owner(QuotesImpl.scala:2878)
at scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.owner(QuotesImpl.scala:2878)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.splicedName(SourceCode.scala:1466)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree(SourceCode.scala:334)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTrees$$anonfun$1(SourceCode.scala:658)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printSeparated$3(SourceCode.scala:647)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printList(SourceCode.scala:653)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTrees(SourceCode.scala:658)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree$$anonfun$7(SourceCode.scala:407)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree$$anonfun$adapted$5(SourceCode.scala:408)
at scala.Function0.apply$mcV$sp(Function0.scala:42)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.inParens(SourceCode.scala:81)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree(SourceCode.scala:408)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printQualTree(SourceCode.scala:548)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree(SourceCode.scala:340)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree(SourceCode.scala:282)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printSeparated$2(SourceCode.scala:635)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printStats(SourceCode.scala:641)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printFlatBlock$$anonfun$1(SourceCode.scala:603)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printFlatBlock$$anonfun$adapted$1(SourceCode.scala:604)
at scala.Function0.apply$mcV$sp(Function0.scala:42)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.indented(SourceCode.scala:75)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printFlatBlock(SourceCode.scala:604)
at scala.quoted.runtime.impl.printers.SourceCode$SourceCodePrinter.printTree(SourceCode.scala:473)
at scala.quoted.runtime.impl.printers.SourceCode$.showTree(SourceCode.scala:10)
at scala.quoted.runtime.impl.QuotesImpl$$anon$13.show(QuotesImpl.scala:3472)
at scala.quoted.runtime.impl.QuotesImpl$$anon$13.show(QuotesImpl.scala:3471)
at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.show(QuotesImpl.scala:111)
at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.show(QuotesImpl.scala:111)
at cps.macros.common.TransformUtil$.safeShow(TransformUtil.scala:155)
at cps.macros.forest.RootTreeTransform.runRoot(RootTreeTransform.scala:139)
at cps.macros.forest.RootTreeTransform.runRoot$(RootTreeTransform.scala:13)
at cps.macros.forest.InlinedTreeTransform$Bridge$1.runRoot(InlinedTreeTransform.scala:290)
at cps.macros.forest.InlinedTreeTransform.runInlined(InlinedTreeTransform.scala:254)
at cps.macros.forest.InlinedTreeTransform.runInlined$(InlinedTreeTransform.scala:14)
at cps.macros.forest.InlinedTreeTransform$Bridge$1.runInlined(InlinedTreeTransform.scala:290)
at cps.macros.forest.InlinedTreeTransform$Bridge$1.bridge(InlinedTreeTransform.scala:300)
at cps.macros.forest.InlinedTreeTransform$.run(InlinedTreeTransform.scala:303)
at cps.macros.Async$.rootTransform(Async.scala:268)
at cps.macros.Async$.$anonfun$1(Async.scala:144)
at cps.macros.misc.WithOptExprProxy$.apply(WithOptExprProxy.scala:25)
at cps.macros.Async$.transformMonad(Async.scala:168)
at cps.macros.Async$.transformImpl(Async.scala:101)
at cps.macros.Async$.transformNotInlined$1(Async.scala:326)
at cps.macros.Async$.$anonfun$7(Async.scala:337)
at cps.macros.Async$.inInlined$1(Async.scala:307)
at cps.macros.Async$.transformContextLambdaImpl(Async.scala:337)
at cps.macros.Async$.inferAsyncArgApplyImpl(Async.scala:76)

run is nonsense code, but I stumbled on this because I accidentally called something else which didn't make sense and instead of an error pointing you to where you messed up you get a macro exception :S

arturaz avatar Aug 27 '25 20:08 arturaz

Is it possible to get a self-contained reproduction?

rssh avatar Aug 27 '25 21:08 rssh

Hi @arturaz fellow CPS-Async user here 👋

While any bugs reports are useful a feedback signal, reproducible reports are far, far more likely to lead to a bugfix.

I think at this point in this tool's adoption curve, as users, we should expect to need to put some work in. It's advanced technology with little competition in Scala ecosystem, that is not sponsored by any large org, and its creator is living in a warzone!

Looking at this issue, Ruslan just might have a flash of insight and recognize the problem💡, but chances are it needs to be investigated to understand what's happening.

I've been in your shoes, but I promise, it's not as much work as it seems to cut down app code to a repro example.

Here's an example I did: https://gist.github.com/benhutchison/32ed2238fc8514928d76683559bbd6ea

Key techniques:

  • Scala CLI using directives to get the key dependencies
  • Stub any input data or context
  • Delete all the lines and code that isn't directly relevant. There's lots irrelevant in your example AFAICT. Check the bug is still happening as you delete
  • Simplify and cut away everything you can, alternating with checking the bug is still showing

Doesn't need perfect: when you reach a page of code or less, link it here 🙏

benhutchison avatar Nov 19 '25 00:11 benhutchison