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

Reproduces future value cast issue for scala 3 #1168

Closed
wants to merge 1 commit into from

Conversation

mprevel
Copy link

@mprevel mprevel commented Jan 6, 2025

Hi,

This PR is related to #906
I've added a test to reproduce the encountered issue.
It seems it may be related to derivation since adding the fields explicitly (commented code in the test) works, but I also encounter some strange behaviours while running the tests.

If I use +testQuick the new test fails for scala 3 on the 1st run, but it seems to pass on the next one (but I'm not sure it's running with scala 3, logs make me think it runs with scala 2.12)
If I use +testOnly test.issues.ticket_906.Issue906Spec*, it fails for scala 3 on each run.

@yanns
Copy link
Contributor

yanns commented Jan 6, 2025

Thanks for the reproduction!

The code generated for scala 2:

  sangria.schema.Field[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass, String, String](
    "myFutureString", 
    implicitly[sangria.macros.derive.GraphQLOutputTypeLookup[String]].graphqlType,
    scala.None,
    scala.collection.immutable.List(),
    ((c: sangria.schema.Context[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass]) => 
      c.value.graphqlMyFutureString),

The code generated for scala 3:

    sangria.schema.Field.apply[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass, scala.Any, scala.Any](
      "myFutureString",
      sangria.macros.derive.GraphQLOutputTypeLookup.outLookup[java.lang.String](sangria.schema.StringType).graphqlType,
      scala.Option.empty[java.lang.String],
      scala.Nil,
      ((`c₃`: sangria.schema.Context[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass]) =>
        ((`value₃`: scala.concurrent.Future[scala.Predef.String]) =>
          sangria.schema.Action.defaultAction[test.issues.ticket_906.Issue906Context.MyRepository, scala.concurrent.Future[scala.Predef.String]](`value₃`)).apply(`c₃`.value.graphqlMyFutureString)),

Instead of using the "simple"

((c: sangria.schema.Context[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass]) => 
      c.value.graphqlMyFutureString)

scala 3 is using something more complex:

      ((`c₃`: sangria.schema.Context[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass]) =>
        ((`value₃`: scala.concurrent.Future[scala.Predef.String]) =>
          sangria.schema.Action.defaultAction[test.issues.ticket_906.Issue906Context.MyRepository, scala.concurrent.Future[scala.Predef.String]](`value₃`)).apply(`c₃`.value.graphqlMyFutureString)),

It does not call graphqlMyFutureString anymore, but encapsulates the body of the function directly in the resolve?

@yanns
Copy link
Contributor

yanns commented Jan 6, 2025

for context, the PR that implemented the scala macros: #900

@yanns
Copy link
Contributor

yanns commented Jan 6, 2025

@jchyb pinging you if you have some quick ideas where to look at

@robberthcz
Copy link

I was looking at the code, and there is method collectFields in file DeriveObjectTypeMacro.scala. On line 124, the expression field.onType.memberType(field.method).widen.asType evaluates probably to Future[String] in case of method myFutureString.

Then on the line 132, the expression Expr.summon[t => Action[Ctx, t]] likely evaluates to something like Expr.summon[Future[String] => Action[Ctx, Future[String]]], which finds the implicit method Action.defaultAction and not the correct Action.futureAction.

It would correspond with the initial ClassCastException, where Sangria resolves the field probably as Future[Future[String]] and not the expected Future[String] at runtime.

Maybe I'm wrong, I don't really understand macros, so take it with a grain of salt. I will try to debug it more.

@yanns
Copy link
Contributor

yanns commented Jan 13, 2025

thanks for the investigation @robberthcz . It seems promising.
I also have no expertise with scala 3 macros, so I guess the only way to make progress is try and error (and debug)

@jchyb
Copy link
Contributor

jchyb commented Jan 13, 2025

Apologies, it's been some time since I looked into the codebase here, so I cannot pinpoint anything yet. I'll try to see if this is something I can help with. I remember trying to match scala-2's behavior as much as possible, so any deviation here is probably not intended.

@jchyb
Copy link
Contributor

jchyb commented Jan 13, 2025

I think I see the issue. In scala 2 the generated code does not have to type check, so we can rely on implicit conversions to do some of the job. So while we generate ((c: sangria.schema.Context[test.issues.ticket_906.Issue906Context.MyRepository, test.issues.ticket_906.Issue906Context.SampleCaseClass]) => c.value.graphqlMyFutureString) there, the c.value.graphqlMyFutureString result later gets wrapped in ReduceAction.futureAction() by the compiler:

implicit def futureAction[Ctx, Val](value: Future[Val]): ReduceAction[Ctx, Val] = FutureValue(
    value)

In scala 3 the generated code has to type check, so we summon it manually, but for some reason it summons defaultAction:

  implicit def defaultAction[Ctx, Val](value: Val): ReduceAction[Ctx, Val] = Value(value)

So it looks like the summon around line 330 might be wrong. I'll prepare a PR tomorrow

@yanns
Copy link
Contributor

yanns commented Jan 13, 2025

@jchyb you don't need to apologize, it's open source, and we're already so thankful to you for having done all the hard work for Scala 3! ❤️
And thank you again for your time here.

@yanns
Copy link
Contributor

yanns commented Jan 15, 2025

Can you all check that the new sangria release fixes your issue?

@yanns yanns closed this Jan 15, 2025
@robberthcz
Copy link

Big thanks. It fixed the issue for us.

@mprevel
Copy link
Author

mprevel commented Jan 15, 2025

Can you all check that the new sangria release fixes your issue?

It's fixed, thanks.

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 this pull request may close these issues.

4 participants