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

Scala 3 Derive Macro Compiler Error #905

Open
PsyfireX opened this issue Aug 28, 2022 · 9 comments
Open

Scala 3 Derive Macro Compiler Error #905

PsyfireX opened this issue Aug 28, 2022 · 9 comments
Labels

Comments

@PsyfireX
Copy link

PsyfireX commented Aug 28, 2022

Dependencies:


scalaVersion := "3.1.3"

libraryDependencies ++= Seq(
      "org.sangria-graphql"  %% "sangria"             % "3.3.0-RC1",
      "org.sangria-graphql"  %% "sangria-circe"       % "1.3.2",
      "io.circe"             %% "circe-parser"        % "0.14.2",
      "io.circe"             %% "circe-generic"       % "0.14.2",
    )

Code:

import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType

case class Ctx(ctx: String)

object PostSchema {
  import sangria.marshalling.circe._
  import io.circe.generic.auto._
  import sangria.macros.derive._

  case class UpdatePostGql() {
    @GraphQLField
    def text(text: TextInputGql): Future[String] = ???
  }

  object UpdatePostGql{
    implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
      deriveObjectType[Ctx, UpdatePostGql](
        ObjectTypeName("UpdatePost")
      )
  }

  case class TextInputGql(text: String)

  object TextInputGql{
    implicit val _it: InputObjectType[TextInputGql] =
      deriveInputObjectType[TextInputGql](
        InputObjectTypeName("TextInput")
      )
  }
}

Error:

[error] -- Error: {path}\src\main\scala\PostSchema.scala:35:42
[error] 35 |      deriveObjectType[Ctx, UpdatePostGql](
[error]    |      ^
[error]    |Exception occurred while executing macro expansion.
[error]    |scala.MatchError: OrType(AndType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class schema)),module class PostSchema$)),class TextInputGql),AppliedType(TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class sangria)),object util),tag),type Tagged),List(TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class sangria)),object marshalling),FromInput),InputObjectResult)))),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Null)) (of class dotty.tools.dotc.core.Types$CachedOrType)
[error]    |    at dotty.tools.dotc.transform.TypeUtils$.companionRef(TypeUtils.scala:91)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.companionPath(Synthesizer.scala:252)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.productMirror(Synthesizer.scala:311)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$6$$anonfun$1$$anonfun$1$$anonfun$1(Synthesizer.scala:396)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.makeMirror(Synthesizer.scala:388)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$6$$anonfun$1(Synthesizer.scala:396)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$8$$anonfun$1(Synthesizer.scala:413)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.recur$1(Synthesizer.scala:556)
[error]    |    at dotty.tools.dotc.typer.Synthesizer.tryAll(Synthesizer.scala:561)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:857)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Typer.implicitArgs$1(Typer.scala:3451)
[error]    |    at dotty.tools.dotc.typer.Typer.addImplicitArgs$1(Typer.scala:3487)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgsImplicitMethod$1(Typer.scala:3563)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:3761)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3995)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Typer.readapt$1(Typer.scala:3340)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3982)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit(Implicits.scala:1053)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.typedImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.tryImplicit(Implicits.scala:1173)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.rank$1(Implicits.scala:1272)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1442)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1470)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1503)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit(Implicits.scala:997)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:851)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Typer.implicitArgs$1(Typer.scala:3451)
[error]    |    at dotty.tools.dotc.typer.Typer.addImplicitArgs$1(Typer.scala:3487)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgsImplicitMethod$1(Typer.scala:3563)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:3761)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3995)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Typer.readapt$1(Typer.scala:3340)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3982)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit(Implicits.scala:1053)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.typedImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.tryImplicit(Implicits.scala:1173)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.rank$1(Implicits.scala:1272)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1442)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1470)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1478)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1503)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit(Implicits.scala:997)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:851)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Typer.implicitArgs$1(Typer.scala:3451)
[error]    |    at dotty.tools.dotc.typer.Typer.addImplicitArgs$1(Typer.scala:3487)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgsImplicitMethod$1(Typer.scala:3563)
[error]    |    at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:3761)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3995)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Typer.readapt$1(Typer.scala:3340)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3982)
[error]    |    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit(Implicits.scala:1053)
[error]    |    at dotty.tools.dotc.typer.Implicits.typedImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.typedImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.tryImplicit(Implicits.scala:1173)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.rank$1(Implicits.scala:1272)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1442)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1470)
[error]    |    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1503)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit(Implicits.scala:997)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicit$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicit(Typer.scala:117)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:851)
[error]    |    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
[error]    |    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
[error]    |    at scala.quoted.runtime.impl.QuotesImpl$reflect$Implicits$.search(QuotesImpl.scala:2396)
[error]    |    at scala.quoted.runtime.impl.QuotesImpl$reflect$Implicits$.search(QuotesImpl.scala:2395)
[error]    |    at scala.quoted.Expr$.summon(Expr.scala:232)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.createArg(DeriveObjectTypeMacro.scala:429)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.$anonfun$237$$anonfun$1(DeriveObjectTypeMacro.scala:299)
[error]    |    at scala.collection.immutable.List.map(List.scala:246)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.$anonfun$237(DeriveObjectTypeMacro.scala:299)
[error]    |    at scala.collection.immutable.List.map(List.scala:246)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.fieldWithArguments(DeriveObjectTypeMacro.scala:299)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.$anonfun$228(DeriveObjectTypeMacro.scala:171)
[error]    |    at scala.collection.immutable.List.map(List.scala:246)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.collectFields(DeriveObjectTypeMacro.scala:254)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro.deriveObjectType(DeriveObjectTypeMacro.scala:72)
[error]    |    at sangria.macros.derive.DeriveObjectTypeMacro$.deriveNormalObjectType(DeriveObjectTypeMacro.scala:45)
[error]    |
[error] 36 |        ObjectTypeName("UpdatePost")
[error] 37 |      )
[error]    |----------------------------------------------------------------------------
[error]    |Inline stack trace
[error]    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]    |This location contains code that was inlined from package.scala:14
[error]     ----------------------------------------------------------------------------
[error] one error found
@PsyfireX
Copy link
Author

PsyfireX commented Aug 28, 2022

Additional notes:

The code did compile previously under Scala 2.13

Debugging macros is beyond my current expertise. However, narrowing in closer to the issue, it appears to happen when mixing ObjectType and InputObjectType, where the input is a method-parameter.

When I use an ObjectType inside and ObjectType, or an InputObjectType inside and InputObjectType it seems to compile just fine:

ObjectType inside ObjectType (no errors)

package ai.tagr.graphql.tagr.schema

import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType

case class Ctx(ctx: String)

object PostSchema {
  import sangria.marshalling.circe._
  import io.circe.generic.auto._
  import sangria.macros.derive._

  case class UpdatePostGql() {
    @GraphQLField
    def text(text: String): Future[TextGql] = ???   // relevant line
  }

  object UpdatePostGql{
    implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
      deriveObjectType[Ctx, UpdatePostGql](
        ObjectTypeName("UpdatePost")
      )
  }

  case class TextGql(text: String)

  object TextGql{
    implicit val _it: ObjectType[Ctx, TextGql] =
      deriveObjectType[Ctx, TextGql](
        ObjectTypeName("Text")
      )
  }
}

InputObjectType inside InputObjectType (no errors)

import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType

case class Ctx(ctx: String)

object PostSchema {
  import sangria.marshalling.circe._
  import io.circe.generic.auto._
  import sangria.macros.derive._

  case class UpdatePostGql(id: String) {
    @GraphQLField
    def text(text: TextInputGql): Future[String] = ???    // relevant line
  }

  object UpdatePostGql{    // swapped from ObjectType to InputObjectType
    implicit val _it: InputObjectType[UpdatePostGql] =
      deriveInputObjectType[UpdatePostGql](
        InputObjectTypeName("UpdatePost")
      )
  }

  case class TextInputGql(text: String)

  object TextInputGql{
    implicit val _it: InputObjectType[TextInputGql] =
      deriveInputObjectType[TextInputGql](
        InputObjectTypeName("TextInput")
      )
  }
}

@PsyfireX
Copy link
Author

PsyfireX commented Aug 29, 2022

update:

This code has the same issue, and uses an id with a ScalarAlias[T, Long] for input, and has the same issue.

import scala.concurrent.Future
import scala.util.Try

case class Ctx(ctx: String)

case class SomeId(id: Long)

object SomeSchema {
  import sangria.marshalling.circe._
  import io.circe.generic.auto._
  import sangria.macros.derive._
  import sangria.schema._

  type LongAlias[T] = ScalarAlias[T, Long]

  def longAlias[ID](from: ID=>Long, to: Long=>ID): LongAlias[ID] =
    ScalarAlias(LongType, from, v ⇒ Right(to(v)))
  
  implicit val someIdType: LongAlias[SomeId] = longAlias[SomeId](_.id, SomeId.apply)

  case class SomeOT(id: String) {
    @GraphQLField
    def getSome1(id: SomeId): Future[String] = ???
  }

  object SomeOT{
    implicit val _ot: ObjectType[Ctx, SomeOT] =
      deriveObjectType[Ctx, SomeOT](
        ObjectTypeName("SomeOT")
      )
  }
}

However, I noticed that commenting out:

// import io.circe.generic.auto._

Appears to make the compiler error go away in this very tiny and limited code sample.

Now, I can't do that in my production code. Commenting that line out in my prod code causes a bunch of other errors, and it's apparently being used to support FromInput.

@yanns
Copy link
Contributor

yanns commented Aug 29, 2022

I'll have a look at this.
Pinging @jchyb who may have an opinion on what is going on.

@yanns
Copy link
Contributor

yanns commented Aug 29, 2022

I can confirm that the first example compiles if I remove import io.circe.generic.auto._ and if I define the FromInput[TextInputGql]:

import sangria.marshalling.FromInput
import sangria.schema.ObjectType

import scala.concurrent.Future
import sangria.schema.InputObjectType

case class Ctx(ctx: String)

object PostSchema {
  import sangria.macros.derive._

  case class UpdatePostGql() {
    @GraphQLField
    def text(text: TextInputGql): Future[String] = ???
  }

  object UpdatePostGql {
    implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
      deriveObjectType[Ctx, UpdatePostGql](
        ObjectTypeName("UpdatePost")
      )
  }

  case class TextInputGql(text: String)

  object TextInputGql {
    implicit val fi: FromInput[TextInputGql] = ???
    implicit val _it: InputObjectType[TextInputGql] =
      deriveInputObjectType[TextInputGql](
        InputObjectTypeName("TextInput")
      )
  }
}

So it seems that issue is coming from a different resolution of implicits.

yanns added a commit that referenced this issue Aug 29, 2022
@yanns
Copy link
Contributor

yanns commented Aug 29, 2022

I'm trying to check with semi-automatic derivation to better control which implicits are created:

import io.circe.Decoder
import sangria.schema.{InputObjectType, ObjectType}

import scala.concurrent.Future

case class Ctx(ctx: String)

object PostSchema {
  import sangria.marshalling.circe._
  import io.circe.generic.semiauto._
  import sangria.macros.derive._

  case class UpdatePostGql() {
    @GraphQLField
    def text(text: TextInputGql): Future[String] = ???
  }

  object UpdatePostGql {
    implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
      deriveObjectType[Ctx, UpdatePostGql](
        ObjectTypeName("UpdatePost")
      )
  }

  case class TextInputGql(
      @GraphQLInputType(sangria.schema.StringType)
      text: String)

  object TextInputGql {
    implicit val decoder: Decoder[TextInputGql] = deriveDecoder[TextInputGql]
    implicit val _it: InputObjectType[TextInputGql] =
      deriveInputObjectType[TextInputGql](
        InputObjectTypeName("TextInput")
      )
  }
}

It leads to the compilation error:

[error] 20 |      deriveObjectType[Ctx, UpdatePostGql](
[error]    |      ^
[error]    |GraphQlOutputType not found: scala.Function1[java.lang.String, sangria.schema.Action[Ctx, java.lang.String]]
[error] 21 |        ObjectTypeName("UpdatePost")
[error] 22 |      )
[error] one error found

I've pushed the changes to https://github.com/sangria-graphql/sangria/tree/issue-905 if someone wants to re-use that.

@PsyfireX
Copy link
Author

PsyfireX commented Aug 29, 2022

Thank you for starting the investigation! @yanns

I might not be very useful in fixing the bug itself with code-changes (due to no macro experience), however I also encountered what appears to be the exact same issue in both my top-level Query and Mutation types, using deriveContextObject, producing an almost identical MatchError, and similarly pointing to another InputObjectType

      val objectType: ObjectType[Ctx, Unit] =
        deriveContextObjectType[Ctx, AllQueries, Unit](_ctx => new AllQueries {
          override val ctx: Ctx = _ctx
        })
        

Since you are already started looking at the issue, I'll see if I can also extract this code into a reproducible, isolated, code-sample.

@PsyfireX
Copy link
Author

Here is the second code sample, revolving around deriveContextObject

package ai.tagr.graphql.tagr.schema

import scala.concurrent.Future
import scala.util.Try

case class Ctx(ctx: String)

case class SomeId(id: Long)

object SomeSchema {
  import sangria.marshalling.circe._
  // commenting out the import below eliminates one error around `getSome1`.  However, it causes  `getSome2` breaks, because it needs a `FromInput[SomeInput,InputObjectResult]`
  // import io.circe.generic.auto._ 
  import sangria.macros.derive._
  import sangria.schema._

  type LongAlias[T] = ScalarAlias[T, Long]

  def longAlias[ID](from: ID=>Long, to: Long=>ID): LongAlias[ID] =
    ScalarAlias(LongType, from, v ⇒ Right(to(v)))
  
  implicit val someIdType: LongAlias[SomeId] = longAlias[SomeId](_.id, SomeId.apply)

  case class SomeInput(text: String)
  object SomeInput{
    implicit val _it: InputObjectType[SomeInput] = deriveInputObjectType[SomeInput](
      InputObjectTypeName("SomeInput")
    )
  }

  trait QueryRoot {
    def ctx: Ctx

    @GraphQLField
    def getSome1(id: SomeId): Future[String] = ???

    @GraphQLField
    def getSome2(in: SomeInput): Future[String] = ???
  }

  object QueryRoot {
    implicit val _ot: ObjectType[Ctx, Unit] = 
      deriveContextObjectType[Ctx,QueryRoot,Unit](
        _ctx => new QueryRoot{  override def ctx: Ctx = _ctx}
      )
  }
}

@jchyb
Copy link
Contributor

jchyb commented Aug 30, 2022

I am taking a look at this right now, and I feel I am close to figuring this out. Slightly delaying implicit resolution in macro seems to make errors consistent between code with import io.circe.generic.auto._ and without it in the first snippet (no idea why yet). It still errors, but at least without the ugly dotc stack trace, just errors with failed implicit resolution. Those suggest that I might need to check what is going on in the sangria.marshalling.circe module. I'll try to prepare a PR with the fixes after figuring out the rest.

@PsyfireX
Copy link
Author

@jchyb Thank you! I believe this is the very last piece in order for me to finish converting my entire project over to Scala 3.

@yanns yanns added the scala3 label Sep 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants