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

"Exception occurred while executing macro expansion." #971

Closed
bwbecker opened this issue Feb 12, 2024 · 2 comments · Fixed by #994
Closed

"Exception occurred while executing macro expansion." #971

bwbecker opened this issue Feb 12, 2024 · 2 comments · Fixed by #994

Comments

@bwbecker
Copy link

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]]
}
@bwbecker
Copy link
Author

A work-around is to move the default value to an apply method:

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]]

  def apply[T](data: T, descr: String = "Default value"): Data[T] =
    new Data[T](data, descr)
}

@cchantep
Copy link
Member

Focused reproducer:

import play.api.libs.json._

case class Data[T](
   data: T,
   descr: String
)

def reproducer[T](implicit reads:Reads[T]): Reads[Data[T]] = Json.reads[Data[T]]

sgodbillon added a commit to sgodbillon/play-json that referenced this issue Mar 3, 2024
sgodbillon added a commit to sgodbillon/play-json that referenced this issue Mar 4, 2024
mergify bot pushed a commit that referenced this issue Apr 29, 2024
mkurz added a commit that referenced this issue Apr 29, 2024
[2.10.x] Fix #971 – "Exception occurred while executing macro expansion." (backport #994) by @sgodbillon
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants