circe / circe

Yet another JSON library for Scala

Home Page:https://circe.github.io/circe/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Maximal number of successive inlines (32) exceeded, Maybe this is caused by a recursive inline method?

0bon opened this issue · comments

commented

I am using deriveCodec to translate to and from json to ADTs. However, when I started adding Lists to my ADT the compiler did not like it and started throwing errors. See code and error below. I am using Scala 3.2.2.
Trying to set -Xmax-inlines in the sbt build file gives me [warn] bad option '-Xmax-inlines 50' was ignored

libraryDependencies ++= Seq(
  "io.circe" %% "circe-core",
  "io.circe" %% "circe-generic",
  "io.circe" %% "circe-parser"
).map(_ % "0.14.5")
import io.circe.generic.semiauto.{deriveCodec, deriveDecoder}
import io.circe.{Decoder, Encoder, parser}
import io.circe.syntax.*

case class Property(address1: String, address2: String, address3: String, postcode: String)
case class AccountForm(name: String, property: List[Property], postcode: String)

object CreateAccount {
  implicit val decoder: Decoder[AccountForm] = deriveCodec[AccountForm]
  implicit val decoder2: Decoder[Property] = deriveCodec[Property]

  def fromJson(json: String): AccountForm = {
    parser.decode[AccountForm](json).toOption.get
  }
}
[error] -- Error: /AccountForm.scala:30:50 
[error]  30 |  implicit val decoder: Decoder[AccountForm] = deriveCodec[AccountForm]
[error]     |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]     |                   Maximal number of successive inlines (32) exceeded,
[error]     |                   Maybe this is caused by a recursive inline method?
[error]     |                   You can use -Xmax-inlines to change the limit.
[error]     |---------------------------------------------------------------------------
[error]     |Inline stack trace
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |This location contains code that was inlined from AccountForm.scala:30
[error]      --------------

Trying to set -Xmax-inlines in the sbt build file gives me [warn] bad option '-Xmax-inlines 50' was ignored

You need to pass it as two separate arguments

scalacOptions ++= Seq("-Xmax-inlines", "50")

Same here, and setting a higher max-inlines parameter didn't fix the issue

Likely would be helped by a solution to #2126 since there would be fewer inline calls overall

The example actually has two problems:

a) deriveCodec does not use a proper given Codec instance for List and instead tries to derive a codec for List
b) derivation for recursive structures suffers from bugs (see #2101)

With the pending fix for b) (#2187 ), the code compiles, however:

import io.circe.generic.semiauto.deriveCodec
import io.circe.*
import io.circe.syntax.*

case class Property(address1: String, address2: String, address3: String, postcode: String)
case class AccountForm(name: String, property: List[Property], postcode: String)

implicit val accountFormDecoder: Decoder[AccountForm] = deriveCodec[AccountForm]

object Main:
  
  def main(args: Array[String]) = 
    println(parser.decode[AccountForm]("""{"name":"x","property":[],"postcode":"y"}"""))
    // Left(DecodingFailure at .property: Got value '[]' with wrong type, expecting object)

    println(parser.decode[AccountForm]("""{"name":"x","property":{"Nil$":{}},"postcode":"y"}"""))
    // Right(AccountForm(x,List(),y))

Looks like for all three typeclasses (Codec, Encoder, Decoder), the behavior is:

If a product-member is a List[T] and there is a an instance of the typeclass for T, then derivation will pick up the appropriated instance for List that converts to/from Json-Array:

// with the fix from #2187
case class Ints(ints: List[Int]) derives Encoder.AsObject
println(Ints(List.empty).asJson.noSpaces)
// {"ints":[]}

If a product-member is a List[T] and there is no instance of the typeclass for T, then derivation will not pick up the appropriated instance for List and instead derive one that converts to/from Json-Objects:

// with the fix from #2187
case class Foos(foos: List[Foo.type]) derives Encoder.AsObject
println(foos(List.empty).asJson.noSpaces)
// {"foos":{"Nil$":{}}}

Note that I'm not sure 100% if such "transitive derivation" is even expected to work (?). If not, then the original example is just not using the library correctly (because it tries to derive a Codec[AccountForm] without having a given/implicitCodec[Property] in scope) - nevertheless, in that case, the error message is still less than ideal.

@0bon @caenrique As a workaround you can use jsoniter-scala's JsonCodecMaker.makeCirceLike macro to auto-derive safe an efficient JSON codecs (with clear compilation errors for unsupported types like Java classes and collections).