Skip to content

Expr.summon does not work with imported givens from a class instance #11557

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

Closed
deusaquilus opened this issue Feb 28, 2021 · 6 comments · Fixed by #11591
Closed

Expr.summon does not work with imported givens from a class instance #11557

deusaquilus opened this issue Feb 28, 2021 · 6 comments · Fixed by #11591
Assignees
Milestone

Comments

@deusaquilus
Copy link
Contributor

deusaquilus commented Feb 28, 2021

Compiler version

3.0.0-RC1
also on 3.0.0-RC2-bin-20210227-07d9dd2-NIGHTLY

Minimized code

Create some kind of typeclass with an implementation inside of a class-instance

trait MyEncoder[T] {
  def encode(t: T): String
}

class MyContext {
  given intEncoder: MyEncoder[Int] = new MyEncoder[Int] { def encode(t: Int) = s"${t}" }
}

Then make a macro that summons this using Expr.summon.

object MyMacro {
  inline def getAndEncode[T](t: T): String = ${ getAndEncodeImpl('t) }
  def getAndEncodeImpl[T: Type](t: Expr[T])(using qctx: Quotes): Expr[String] = {
    import qctx.reflect._
    val enc = 
      Expr.summon[MyEncoder[T]] match
        case Some(enc) => enc
        case None => report.throwError("Can't find encoder")
    '{ $enc.encode($t) }
  }
}

Then import the context and use the macro:

  def doEncoding: Unit = {
    val ctx = new MyContext()
    import ctx.{*, given}
    println(MyMacro.getAndEncode(123))
  }

Output

Compilation fails, report error case "Can't find encoder" is hit.

[error] 15 |    println(MyMacro.getAndEncode(123))
[error]    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |            Can't find encoder

Expectation

Compile should work and the given should be correctly summoned.

Note that if instead of using a macro I use a regular method, it will work:

  def encodeIt[T](t: T)(using enc: MyEncoder[T]) = enc.encode(t)

  def doEncoding: Unit = {
    val ctx = new MyContext()
    import ctx.{*, given}
    println(encodeIt(123))
  }

Why the discrepancy?

Also, note that if you declare the given directly in the doEncoding method it will work:

  def doEncoding: Unit = {
    val ctx = new MyContext()
    given intEncoder: MyEncoder[Int] = new MyEncoder[Int] { def encode(t: Int) = s"${t}" }
    println(MyMacro.getAndEncode(123))
  }

Why the discrepancy?

Code

Full code sample is available here:
https://github.com/deusaquilus/expr_summon_issue

@deusaquilus
Copy link
Contributor Author

deusaquilus commented Feb 28, 2021

If I'm not totally mistaken and this is a real issue, RC1 is totally unusable for me 😢

@deusaquilus
Copy link
Contributor Author

Okay, so this doesn't work with object either. Say that I do this:

object Ctx {
  given intEncoder: MyEncoder[Int] = new MyEncoder[Int] { def encode(t: Int) = s"${t}" }
}

Then I do this:

  def doEncoding: Unit = {
    import Ctx.{given,*}
    println(MyMacro.getAndEncode(123))
  }

It does not work either:

[error] 20 |    println(MyMacro.getAndEncode(123))
[error]    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |            Can't find encoder

@deusaquilus
Copy link
Contributor Author

@nicolasstucki @liufengyun @odersky
This really, really scares me!
I was building just fine on the Feb 02 build (20210202-8d43a9c-NIGHTLY), then comes the RC1 release and Boom! This bug happens and everything I'm doing is completely broken.
I keep thinking, what if something like this happens in the 3.0.0 release!?

For the 3.0.0 release, can we have some kind of Scala 3 stage-gate making sure Dotty-Quill builds before the release is cut? Dotty-Quill seems to be the only thing constantly blowing up on the metaprogramming bugs in new releases. So it really needs to be double-checked.
Or maybe we bring Dotty-Quill into the Community Build ASAP and do it that way?

@nafg
Copy link

nafg commented Mar 1, 2021 via email

@nicolasstucki
Copy link
Contributor

Minimized

type MyEncoder

class MyContext:
  given intEncoder: MyEncoder = ???

def doEncoding(ctx: MyContext): Unit =
  import ctx.{*, given}
  summon[MyEncoder]
  summonInlineMyEncoder()

inline def summonInlineMyEncoder(): Unit =
  compiletime.summonInline[MyEncoder]

@SethTisue
Copy link
Member

SethTisue commented Mar 5, 2021

maybe we bring Dotty-Quill into the Community Build ASAP and do it that way

that certainly sounds like the right thing to do

it's also possible to add latest-Scala-3-nightly to your own CI matrix

@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants