Compiler stack overflow when using Manifest with recursive existential types
class C[T <: C[T]]
class UseManifest[T : Manifest]
new UseManifest[C[T] forSome{ type T <: C[T] }]
Note: I'm calling it "recursive existential type" but I'm really not sure about the terminology... maybe there's a better term.
Imported From: https://issues.scala-lang.org/browse/SI-6255?orig=1 Reporter: Bruno Bieth (mustaghattack) See #6528
@retronym said: It's not the same bug, but is in the same food group as #6528.
Damir Vandic (damirv) said: Any updates on this? (or a possible/recommended workaround)
For the interested reader: I encountered this issue with trying to use 'recursive existential types' to call the following Java method (Titan Graph database v0.5.0):
public TitanGraphQuery<? extends TitanGraphQuery> query();
with TitanGraphQuery being
public interface TitanGraphQuery<Q extends TitanGraphQuery<Q>> {
public Q has(String key, Object value);
}
Example Scala code (without explicit types):
val attrs: Map[String, Any]
val attrQ = attrs.foldLeft(g.query())({
case (q, (name, value)) => q.has(name, value)
})
Does this bug mean I cannot use this method in Scala?
Related but perhaps slightly different: we hit this in our codebase during an upgrade of apache thrift due to TBase being typed in this way and scalac failing to infer a manifest for its erased type. The minimal repro in our case being:
repl_test ➤ echo "public interface Test<T extends Test<T>> {}" > Test.java
repl_test ➤ javac Test.java
repl_test ➤ scala -cp .
Welcome to Scala 2.13.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.
scala> manifest[Test[_]]
java.lang.StackOverflowError
at scala.reflect.internal.Symbols$Symbol.isPrimaryConstructor(Symbols.scala:955)
at scala.tools.nsc.typechecker.Contexts$Context.withOuter$1(Contexts.scala:1123)
at scala.tools.nsc.typechecker.Contexts$Context.implicitssImpl(Contexts.scala:1146)
at scala.tools.nsc.typechecker.Contexts$Context.withOuter$1(Contexts.scala:1125)
...
That entry seems to have slain the compiler. Shall I replay
your session? I can re-run each line except the last one.
[y/n]
Interestingly, the same type can be inferred without issue if declared as a scala trait instead of the java interface.
Repro'ed with both java 8 & 11 on latest versions of 2.11, 2.12, and 2.13.
Consider using ClassTag if you don't need full type information, and TypeTag if you do. As the Manifest documentation states:
it is advisable to migrate any
Manifest-based APIs to useTags
We did indeed switch to using a mix of tags (ClassTag where possible, TypeTag where required) which gets around this problem. I expect we'll have to revisit this when we eventually upgrade to Scala 3 as TypeTag seems to have been removed, but it's probably a few years yet before we cross that bridge.