Skip to content

Expr.summon does not resolve opaque types #21199

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

Open
joan38 opened this issue Jul 15, 2024 · 7 comments
Open

Expr.summon does not resolve opaque types #21199

joan38 opened this issue Jul 15, 2024 · 7 comments
Labels

Comments

@joan38
Copy link
Contributor

joan38 commented Jul 15, 2024

Compiler version

Issue reproduced in 3.4.2 and 3.5.0-RC4.

Minimized code

Please check the min repro repo https://github.com/joan38/macro-summon-bug

inline def summonValuesInPackage(inline packageName: String): Seq[?] = ${ summonValuesInPackageImpl('packageName) }

private def summonValuesInPackageImpl(packageName: Expr[String])(using Quotes): Expr[Seq[?]] =
  import quotes.reflect.*

  def findAllTypesInPackage(pkg: Symbol): Seq[TypeRepr] =
    pkg.declarations.flatMap:
      case sym if sym.isPackageDef || (sym.isValDef && sym.flags.is(Flags.Module)) =>
        findAllTypesInPackage(sym) // Recurse
      case sym if sym.isTypeDef => Seq(sym.typeRef.dealiasKeepOpaques) // Include
      case _                    => Seq.empty                           // Skip

  // Get the package where we will look for types
  val typesPackage = Symbol.requiredPackage(packageName.valueOrAbort)

  // Find all types in the package and summon them
  val values = findAllTypesInPackage(typesPackage).flatMap: typeRepr =>
    typeRepr.asType match
      case '[t] => Expr.summon[t] // This does not seem to work when `t` is an opaque type

  Expr.ofSeq(values)

Output

Gives only MyCaseClass instances (not sure why 3 times but that's a different issue):

ArraySeq(MyCaseClass(), MyCaseClass(), MyCaseClass())

Expectation

Expr.summon[t] should resolve opaque type too:

ArraySeq(MyCaseClass(), MyCaseClass(), MyCaseClass(), true, true, true)

Thanks

@joan38 joan38 added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 15, 2024
@joan38
Copy link
Contributor Author

joan38 commented Jul 16, 2024

Sounds like it's the same with summonInline instead of Expr.summon

@joan38
Copy link
Contributor Author

joan38 commented Jul 17, 2024

Also another thing I experimented with to make it even simplier:

import scala.quoted.*
import types.Hello

inline def summonHello: Hello = ${ summonHelloImpl }

private def summonHelloImpl(using Quotes): Expr[Hello] =
  import quotes.reflect.*

  Symbol.requiredModule("types").typeMember("Hello").typeRef.asType match
    case '[t] => Expr.summon[t].getOrElse(report.errorAndAbort(s"Cannot summon a value of type ${TypeRepr.of[Hello]}")).asExprOf[Hello]

and:

object types:
  opaque type Hello = String
  def Hello(s: String): Hello = s
import types.*

@main def run =
  given Hello = Hello("dummy")
  println(summonHello)

Which fails with:

% ./scala -Xcheck-macros .
Compiling project (Scala 3.4.2, JVM (17))
[error] ./run.scala:8:11
[error] Cannot summon a value of type TypeRef(TermRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),types),Hello)
[error]   println(summonHello)
[error]           ^^^^^^^^^^^
Error compiling project (Scala 3.4.2, JVM (17))

@Gedochao Gedochao added area:opaque-types and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 17, 2024
@bishabosha bishabosha added the area:metaprogramming:quotes Issues related to quotes and splices label Jul 17, 2024
@bishabosha
Copy link
Member

seems pretty clearly broken, thanks for the minimisation

@joan38
Copy link
Contributor Author

joan38 commented Jul 17, 2024

Thanks.
If I change the line:

case '[t] => Expr.summon[t].getOrElse(report.errorAndAbort(s"Cannot summon a value of type ${TypeRepr.of[Hello]}")).asExprOf[Hello]

by

case '[t] => Expr.summon[t].getOrElse(report.errorAndAbort(s"Cannot summon a value of type ${TypeRepr.of[t]}")).asExprOf[Hello]

then it prints:

Cannot summon a value of type TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)

So that means it looks for a String instead of Hello?

@joan38
Copy link
Contributor Author

joan38 commented Jul 17, 2024

Here another conclusion:

import scala.quoted.*
import types.Hello

inline def summonHello: Hello = ${ summonHelloImpl }

private def summonHelloImpl(using Quotes): Expr[Hello] =
  import quotes.reflect.*

  val tpe = Symbol.requiredPackage("types").typeMember("Hello").typeRef
  val value = Implicits.search(tpe) match
    case iss: ImplicitSearchSuccess => Some(iss.tree.asExprOf[Hello])
    case isf: ImplicitSearchFailure => None

  value.getOrElse(report.errorAndAbort(s"Cannot summon a value of type ${tpe.typeSymbol.fullName} - ${tpe}"))

and:

package types:
  opaque type Hello = String
  object Hello:
    def apply(s: String): Hello = s
    given Hello = Hello("dummy") // Some instance in the companion object
import types.*

@main def run =
  // import Hello.given // Uncomment this line and it works
  println(summonHello)

Fails to resolve the implicit but with an import of the givens from the companion object, it works.
So that means it's not able to resolve givens from companion objects of opaque types.

@joan38
Copy link
Contributor Author

joan38 commented Jul 17, 2024

Also:

  val tpe = Symbol.requiredPackage("types").typeMember("MyCaseClass").typeRef
  println(tpe.typeSymbol.companionModule.exists.toString)

Would return true if there is a case class MyCaseClass declared in the package types.
But false for an opaque type.
I wonder why?

@joan38
Copy link
Contributor Author

joan38 commented Aug 22, 2024

BTW I'm happy to look into this issue but not sure where to start from.
I'm actually surprised it does not work because it sounds like Expr.summon plugs into the compiler's code

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