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

Issues with implicit lookup within macros, when object type is resolved from typeRepr.typeSymbol.children #21122

Open
MateuszKubuszok opened this issue Jul 8, 2024 · 0 comments

Comments

@MateuszKubuszok
Copy link

Compiler version

3.3.3

Minimized code

example.scala:

//> using scala 3.3.3

package example

sealed trait Evidence[A]
object Evidence {
  private case object Instance extends Evidence[Nothing]

  def provide[A]: Evidence[A] = Instance.asInstanceOf[Evidence[A]]
}


object Example {

  inline def check[A]: Unit = ${ checkImpl[A] }

  import scala.quoted.*

  def checkImpl[A: Type](using Quotes): Expr[Unit] = {
    import quotes.*
    import quotes.reflect.*

    TypeRepr.of[A].typeSymbol.children.map { sym =>
      if sym.flags.is(Flags.Enum) then sym.typeRef.typeSymbol
      else if sym.flags.is(Flags.Module) then  sym.typeRef.typeSymbol.moduleClass
      else sym
    }.foreach { sym =>
      trait Existential {
        type Underlying
        implicit val Underlying: Type[Underlying]
      }
      val subtype: Existential = new Existential {
       type Underlying = Any
       implicit val Underlying: Type[Underlying] = sym.typeRef.asType.asInstanceOf[Type[Underlying]]
      }
      import subtype.Underlying as Subtype

      Expr.summon[Evidence[Subtype]] match {
        case Some(_) =>
        case None    => report.errorAndAbort(s"Oh noez (${TypeRepr.of[Subtype].show})", Position.ofMacroExpansion)
      }
    }

    '{ () }
  }
}

example.test.scala:

//> using dep org.scalameta::munit::1.0.0

package example

trait Tag

sealed trait SomeSealed
case class SomeClass() extends SomeSealed
case object SomeObject extends SomeSealed with Tag

class MacroTest extends munit.FunSuite {

  test("will it compile?") {
    implicit val explicitClass: Evidence[SomeClass] = Evidence.provide
    implicit def singletons[A <: Singleton]: Evidence[A] = Evidence.provide
    //implicit val explicitObject: Evidence[SomeObject.type] = Evidence.provide
    //implicit def tagged[A <: Tag]: Evidence[A] = Evidence.provide

    Example.check[SomeSealed]
  }
}

Output

Compiling project (Scala 3.3.3, JVM (17))
Compiled project (Scala 3.3.3, JVM (17))
Compiling project (test, Scala 3.3.3, JVM (17))
[error] ./macro.test.scala:15:5
[error] Oh noez (example.SomeObject)
[error]     Example.check[SomeSealed]
[error]     ^^^^^^^^^^^^^^^^^^^^^^^^^
Error compiling project (test, Scala 3.3.3, JVM (17))
Compilation failed

Expectation

Compiling project (Scala 3.3.3, JVM (17))
Compiled project (Scala 3.3.3, JVM (17))
Compiling project (test, Scala 3.3.3, JVM (17))
Compiled project (test, Scala 3.3.3, JVM (17))
example.MacroTest:
  + will it compile? 0.015s

The issue seems to be either a mismatch between the type obtained from turning child symbol into Type, or something about handling of singleton types (modules). It's very subtle:

  • if we remove the whole children machinery and attempt to just:

    def checkImpl[A: Type](using Quotes): Expr[Unit] =
      import quotes.*, quotes.reflect.*
      Expr.summon[Evidence[A]] match {
        case Some(_) => '{ () }
        case None    => report.errorAndAbort(s"Oh noez (${TypeRepr.of[Subtype].show})", Position.ofMacroExpansion)
      }

    then

    Example.check[SomeObject] // using singletons

    works as expected

  • if we comment out singletons implicit and replace it with explicitObject OR tagged (so that implicit resolution would not have to rely on <: Singleton type bound) it also works as expected

  • manual experiments shows that type constructed for SomeObject with sym.termRef.asType successfully pass TypeRepr.of[Subtype] <:< TypeRepr.of[Singleton] check while sym.typeRef.asType does not (however in my macros it breaks other things)

  • TypeRepr.of[A].memberType(sym) suggested in other issue for similar cases also does not work:

    def checkImpl[A: Type](using Quotes): Expr[Unit] = {
      import quotes.*
      import quotes.reflect.*
    
      TypeRepr.of[A].typeSymbol.children.map { sym =>
        if sym.flags.is(Flags.Enum) then sym.typeRef.typeSymbol
        else if sym.flags.is(Flags.Module) then  sym.typeRef.typeSymbol.moduleClass
        else sym
      }.foreach { sym =>
        trait Existential {
          type Underlying
          implicit val Underlying: Type[Underlying]
        }
        val subtype: Existential = new Existential {
         type Underlying = Any
         implicit val Underlying: Type[Underlying] =
           if sym.flags.is(Flags.Module) then TypeRepr.of[A].memberType(sym).asType.asInstanceOf[Type[Underlying]]
           else sym.typeRef.asType.asInstanceOf[Type[Underlying]]
        }
        import subtype.Underlying as Subtype
    
        Expr.summon[Evidence[Subtype]] match {
          case Some(_) =>
          case None    => report.errorAndAbort(s"Oh noez (${sym})", Position.ofMacroExpansion)
        }
      }
    
      '{ () }
    }

    produces

    Compiling project (Scala 3.3.3, JVM (17))
    Compiled project (Scala 3.3.3, JVM (17))
    Compiling project (test, Scala 3.3.3, JVM (17))
    [error] ./macro.test.scala:19:5
    [error] Oh noez (module class SomeObject$)
    [error]     Example.check[SomeSealed]
    [error]     ^^^^^^^^^^^^^^^^^^^^^^^^^
    Error compiling project (test, Scala 3.3.3, JVM (17))
    Compilation failed
    

    and requires workaround for printing as TypeRepr.of[Subtype].show would crash the compiler

@MateuszKubuszok MateuszKubuszok added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 8, 2024
@Gedochao Gedochao added area:typer area:inline area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants