Skip to content

"Exception occurred while executing macro expansion." #971

@bwbecker

Description

@bwbecker

Play JSON Version (2.5.x / etc)

2.10.4

API (Scala / Java / Neither / Both)

Scala

Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10)

MacOS 14.2.1

bwbecker@beta playjson % uname -a
Darwin beta 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:18 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6000 arm64

JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing)

bwbecker@beta playjson % java -version
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment Homebrew (build 17.0.9+0)
OpenJDK 64-Bit Server VM Homebrew (build 17.0.9+0, mixed mode, sharing)

Library Dependencies

None.

Expected Behavior

I expect to be able to combine a type parameter and a default value in a case class and for Play-Json to be able to automatically derive the appropriate Reads and Writes.

// Version 3 -- Fails with "Exception occurred while executing macro expansion."
case class Data[T](
    data: T,
    descr: String = "Default value"
)

object Data {
  implicit def errorWrites[T](implicit writes:Writes[T]): Writes[Data[T]]     = Json.writes[Data[T]]
  implicit def errorReads[T](implicit reads:Reads[T]): Reads[Data[T]]         = Json.reads[Data[T]]
}

Actual Behavior

The compiler throws the following error:

bwbecker@beta playjson % scala-cli error.scala
Compiling project (Scala 3.3.1, JVM (17))
[error] ./error.scala:58:81
[error] Exception occurred while executing macro expansion.
[error] java.lang.Exception: Expected an expression. This is a partially applied Term. Try eta-expanding the term first.
[error] 	at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.asExpr(QuotesImpl.scala:110)
[error] 	at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.asExprOf(QuotesImpl.scala:116)
[error] 	at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.asExprOf(QuotesImpl.scala:115)
[error] 	at play.api.libs.json.JsMacroImpl$ReadsHelper$$anon$2.applyOrElse(JsMacroImpl.scala:332)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
[error] 	at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
[error] 	at scala.Option.collect(Option.scala:462)
...

Reproducible Test Case

Place the following in error.scala and then run it with scala-cli error.scala.

It contains three examples; remove/replace comments as appropriate for each one.

Example 1: Works as expected, demonstrating use of a type variable, T, for data.

Example 2: Works as expected, demonstrating the use of a default value for descr.

Example 3: Combines the two above examples, generating a compile-time exception.

//> using dep     com.typesafe.play::play-json:2.10.4


import play.api.libs.json._
import Data._

object Main {

    def main(args:Array[String]):Unit = {
        println("Hello, world!")

        // Versions 1 and 3
        val err = Data[Int](400, "Some error")
        val jsonString = Json.toJson(err).toString
        println(jsonString)
        val obj = Json.parse(jsonString).validate[Data[Int]].get
        println(obj)

        // Version 2
        // val err = Data(400)
        // val jsonString = Json.toJson(err).toString
        // println(jsonString)
        // val obj = Json.parse(jsonString).validate[Data].get
        // println(obj)

    }
}

// Version 1 -- Works as expected with type parameter
// case class Data[T](
//     data: T,
//     descr: String
// )

// object Data {
//   implicit def errorWrites[T](implicit writes:Writes[T]): Writes[Data[T]]     = Json.writes[Data[T]]
//   implicit def errorReads[T](implicit reads:Reads[T]): Reads[Data[T]]         = Json.reads[Data[T]]
// }

// Version 2 -- Works as expected with default parameter
// case class Data(
//     data: Int,
//     descr: String = "Default value"
// )

// object Data {
//   implicit def errorWrites: Writes[Data]     = Json.writes[Data]
//   implicit def errorReads: Reads[Data]         = Json.reads[Data]
// }

// Version 3 -- Fails with "Exception occurred while executing macro expansion."
case class Data[T](
    data: T,
    descr: String = "Default value"
)

object Data {
  implicit def errorWrites[T](implicit writes:Writes[T]): Writes[Data[T]]     = Json.writes[Data[T]]
  implicit def errorReads[T](implicit reads:Reads[T]): Reads[Data[T]]         = Json.reads[Data[T]]
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions