Unexpected `Illegal cyclic reference` error since Scala 3.1.2
See original GitHub issueProblem found when migrating sangria to Scala 3.
Compiler version
3.1.3-RC4 Runs correctly in 3.1.1, broken since 3.1.2, dottyCompileBisect gave me 40ae7eee109a3c8a3a125c1ec4a2167bba2acd9c as the first bad commit and 3.1.2-RC1-bin-20211217-b3ab102-NIGHTLY as the first bad release.
Minimized code
Problematic code:
//> using scala "3.1.3-RC4"
import scala.annotation.implicitNotFound
sealed trait Tagged[U]
type @@[+T, U] = T with Tagged[U] // intersection type seems to be the culprit, compiles if removed
@implicitNotFound(
"Type ${Val} cannot be used as an input. Please consider defining an implicit instance of `FromInput` for it.")
trait FromInput[Val]
private object ScalarFromInput extends FromInput[Any] {}
implicit def coercedScalaInput[T]: FromInput[T @@ CoercedScalaResult] =
ScalarFromInput.asInstanceOf[FromInput[T @@ CoercedScalaResult]]
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = { // inline here fixes the issue
ev.asInstanceOf[FromInput[Option[T]]]
}
trait CoercedScalaResult
sealed trait InputType[+T]
trait WithoutInputTypeTags[T] {
type Res
}
object WithoutInputTypeTags {
implicit def coercedArgTpe[T]: WithoutInputTypeTags[T @@ CoercedScalaResult] { type Res = T } =
new WithoutInputTypeTags[T @@ CoercedScalaResult] {
type Res = T
}
implicit def coercedOptArgTpe[T]
: WithoutInputTypeTags[Option[T @@ CoercedScalaResult]] { type Res = Option[T] } =
new WithoutInputTypeTags[Option[T @@ CoercedScalaResult]] {
type Res = Option[T]
}
}
case class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]
case class ScalarType[T](name: String) extends InputType[T @@ CoercedScalaResult]
val BooleanType: ScalarType[Boolean] = ScalarType[Boolean]("Boolean")
case class Argument[T](
name: String, // seemingly important argument for this issue, compiles without it
argumentType: InputType[_],
fromInput: FromInput[_]
)
object Argument {
inline def apply[T](name: String, argumentType: InputType[T])(implicit // inline here makes no difference, added for MacroHelp
fromInput: FromInput[T],
res: WithoutInputTypeTags[T]): Argument[res.Res] = { // returned type seems to be the issue here
println(MacroHelp.showType[T]) // added for debugging
Argument(name, argumentType, fromInput)
}
}
object Main {
def main(args: Array[String]): Unit = {
Argument("name", OptionInputType(BooleanType)) :: Nil // List.apply works correctly but still infers weird
}
}
Macro I added to check inferred types:
import scala.quoted._
object MacroHelp {
inline def showType[T] = ${showTypeImpl[T]}
def showTypeImpl[T](using Quotes)(using t: Type[T]): Expr[String] = {
import quotes.reflect.*
val tpe = TypeRepr.of[T].show
Expr(tpe)
}
}
Output
[error] ./Cyclic.scala:66:52: Recursion limit exceeded.
[error] Maybe there is an illegal cyclic reference?
[error] If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
[error] A recurring operation is (inner to outer):
[error]
[error] subtype Tagged[CoercedScalaResult] <:< T
[error] Argument("name", OptionInputType(BooleanType)) :: Nil // List.apply works correctly but still infers weird
[error]
Expectation
The code should compile and run, like it does in 3.1.1. I added a macro that shows that inside of Argument.apply type T is inferred as scala.Option[Cyclic$package.@@[scala.Nothing, CoercedScalaResult] & Tagged[CoercedScalaResult]]
, whereas in scala 3.1.1 it was scala.Option[Test2$package.@@[scala.Any, CoercedScalaResult] & Tagged[CoercedScalaResult]]
. However, my intuition tells me that the type should be scala.Option[Cyclic$package.@@[scala.Boolean, CoercedScalaResult]]]
. It is also worth noting that since the code originally comes from Scala 2, the previously-compound-now-intersection type does not work like it did there, where it was possible to cast T
to T @@ Tagged[SomeOtherType]
- an operation on which Sangria’s internal type system relies heavily. Since that does not work as well, I imagine I will have to slightly rethink the api there for Scala 3, which may (or may not) make this issue less important. There are a lot of factors that make the problem appear and I had trouble minimizing code further, so I tried to add comments showing how things affect each other.
Issue Analytics
- State:
- Created a year ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
Bisecting the original example shows 3ab18a90ac24dba440c498aab6a8f0c763589605 from #14026 as the first bad commit.
Unlike the original example, the minimized one fails to compile with 3.1.1.
They both fail to compile on latest nightly 3.2.0-RC1-bin-20220602-42b5941-NIGHTLY.
Need to re-open because of #16344