-
Notifications
You must be signed in to change notification settings - Fork 21
Description
Overview
Consider a type T
with at least one sub-type and a reference to that type in another file which invokes a macro (defined in another project) that invokes knownDirectSubclasses
against T
. If the reference to the type T
occurs in a file whose filename has a lexicographical ordering which occurs before the filename of the file in which the type T
is defined, a globalError
is raised due to T
being observed before all sub-types have been registered with the compiler.
This is related to SI-7046 which was generally thought to have been fixed in PR 5284. While that PR does list a few exceptional cases where this bug can still occur, it does not appear (to me) to list the cases documented here.
- Scala Versions
- The error is raised on all Scala versions in the range
[2.11.9, 2.13.0-M2]
. Although it appears to still be present before2.11.9
, but is silently ignored as the code which emits theglobalError
was only added in2.11.9
.
- The error is raised on all Scala versions in the range
- JVM Versions
- I have tested this on OpenJDK 1.8.0_144-b01
Details
This bug is a bit tricky to describe well in prose, so I have created an example project here (https://github.com/isomarcte/scala-compiler-knownSubclasses-bug) which may be more directly helpful. The below is taken from the README.md
for that project.
Here is what is happening.
Consider some sealed
type T
that defines all sub-classes in the companion object for the type.
sealed trait T
object T {
final case class TImpl() extends T
}
Consider some type-class F[_]
which can derive instances for certain types, assuming certain requirements for T
are met. The validation of these requirements and the derivation code is done using a macro. This macro invokes knownDirectSubclasses
at some point.
trait F[T]
object F {
def instanceImpl[T : c.WeakTypeTag](
c: Context
): c.Expr[F[T]] = {
import c.universe._
val wtt: c.WeakTypeTag[T] = implicitly[c.WeakTypeTag[T]]
wtt.tpe.typeSymbol.asClass.knownDirectSubclasses.toString
reify(new F[T]{})
}
implicit def instance[T]: F[T] = macro instanceImpl[T]
}
Finally, consider a function foo
which uses T
and requires that T
have an instances of F
.
object Foo {
def foo[T : F]: Unit = ()
foo[T]
}
If the T
is defined in a file whose filename has a lexicographical ordering which places it after the file in which foo
is defined and T
highest ranking implicit instance for F
is the macro derived instance, then the following will occur.
- The call to
knownDirectSubclasses
in the macro will fail to see any sub-classes ofT
- When the compiler type-checks
T
it will raise NglobalError
invocations, where N is the exactly equal to the number of sub-classes forT
.
If the file in which T
is defined has a lexicographical ordering which places it before the file in which foo
is defined, everything works great.