Description
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 withexplicitObject
ORtagged
(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
withsym.termRef.asType
successfully passTypeRepr.of[Subtype] <:< TypeRepr.of[Singleton]
check whilesym.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