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

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

Open
0bon opened this issue Jun 17, 2023 · 7 comments

Comments

@0bon
Copy link

0bon commented Jun 17, 2023

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

@armanbilge
Copy link
Contributor

armanbilge commented Jun 17, 2023

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")

@caenrique
Copy link

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

@mrdziuban
Copy link
Contributor

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

@MartinHH
Copy link
Contributor

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

@MartinHH
Copy link
Contributor

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.

@plokhotnyuk
Copy link
Contributor

@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).

@MartinHH
Copy link
Contributor

MartinHH commented Apr 27, 2024

I think this could be closed now - #2258 (and the reference to #2183 (comment)) clarified that implicit val decoder: Decoder[AccountForm] = deriveCodec[AccountForm] is expected to fail:

How should semiauto derivation work by default for case classes?
i. It should fail if any case class field does not have a Decoder/Encoder
...

Furthermore, the error message in 0.14.7 is much more helpful:

[error] 30 |  implicit val decoder: Decoder[AccountForm] = deriveCodec[AccountForm]
[error]    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Failed to find an instance of Encoder[scala.collection.immutable.List[Property]]
[error] one error found

MancunianSam added a commit to nationalarchives/dr2-preservica-client that referenced this issue May 9, 2024
Based off this issue circe/circe#2162

The compiler was also complaining about `with` being replaced by `&` so
I've done that.
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

No branches or pull requests

6 participants