bug
bug copied to clipboard
scalac reports error on valid Java class: illegal cyclic reference involving type T
When compiling a mix of Java and Scala files, scalac will report an error on otherwise valid Java files.
To reproduce, create 2 files:
Foo.java:
public class Foo<T extends Comparable<? super T>> {}
Bar.scala:
class Bar {
val quux = new Foo[Int]()
}
And compile them using scalac:
$ scalac Foo.java Bar.scala
Foo.java:1: error: illegal cyclic reference involving type T
public class Foo<T extends Comparable<? super T>> {
^
one error found
Impact: it's not possible to use scala for mixed Scala/Java projects containing such declarations.
Imported From: https://issues.scala-lang.org/browse/SI-4744?orig=1 Reporter: Christoph Breitkopf (bokesan) Affected Versions: 2.9.0, 2.11.2, 2.12.0-M5
@paulp said: That's nicely reduced. Given that the scala code compiles fine against the compiled java, there must be something we can do here.
@paulp said: https://github.com/scala/scala/pull/1422
Bhashit Parikh (bhashit) said (edited on May 31, 2014 3:19:46 PM UTC): I think this is still occurring when the java class in question is being referenced from any scala class.
This is the scenario:
A.java
public class A<T extends Comparable<? super T>> implements Comparable<T> {
public int compareTo(T other) {
return 0;
}
}
B.scala
class B {
println (new A)
}
- Command *
scalac ./*.java ./\*.scala
If I compile just the java files, it works. If the scala code doesn't refer to that java class, it works.
$ scalac -version
Scala compiler version 2.11.1 -- Copyright 2002-2013, LAMP/EPFL
Dmytro Kondratiuk (dk14) said: Still experiencing issue with 2.11.2, 2.11.5 - http://stackoverflow.com/questions/28158173/building-a-project-with-mixed-scala-and-java-source-files-using-ant-illegal-cy
Arif (apathan) said: Under what timeframe will this fix be released? I'd love to start using Scala in our application (that is currently all Java), but can't proceed due to this issue. Any update is greatly appreciated. Thx.
Per Mildner (per.mildner-at-sics.se) said: Ping. Any news on this bug?
Per Mildner (per.mildner-at-sics.se) said:
Any news on this bug? It is unfortunate that Scala can not be mixed with Java classes that implement, e.g. Comparable.
Per Mildner (per.mildner-at-sics.se) said: Any news? I this fixed in some other version/branch of Scala?
@SethTisue said: 2.12.0-M5 gives the same error
@adriaanm @szeiger is someone specific actually intending to tackle this...? if not, let's remove it from the 2.13.0-M4 milestone
"Dotty is so right, it reports it twice!"
➜ t4744 ~/dotty-0.26.0/bin/dotc -d /tmp/ *a
-- [E057] Type Mismatch Error: B.scala:5:23 ------------------------------------
5 | val quux = new A[Int]()
| ^
|Type argument Int does not conform to upper bound Comparable[? >: LazyRef(Int)]
-- [E057] Type Mismatch Error: B.scala:5:19 ------------------------------------
5 | val quux = new A[Int]()
| ^
|Type argument Int does not conform to upper bound Comparable[? >: LazyRef(Int)]
2 errors found
Hi @SethTisue, any updates on this?
@eleansa no. nobody is working on this at present. a pull request with a fix would be very welcome
Blows up in Typer. There's a "is this defined in Java" predicate in the model, so probably a good application of that where this is checked would suppress the error. The difficulty is figuring out how to not suppress it when it's right...
Same issue has surfaced in Scala 3.1.2. I did not have this problem with 2.13.
Scala 2 has an experimental option -Ybreak-cycles that avoids the cyclic error.
The program above proceeds to compile as far as:
[error] /Users/jz/code/scala-bug-4744/src/main/scala/Bar.scala:2:7: type arguments [Int] do not conform to class Foo's type parameter bounds [T <: Comparable[_ >: T]
[error] val quux = new Foo[Int]()
[error] ^
[error] /Users/jz/code/scala-bug-4744/src/main/scala/Bar.scala:2:18: type arguments [Int] do not conform to class Foo's type parameter bounds [T <: Comparable[_ >: T]]
[error] val quux = new Foo[Int]()
[error] ^
Using val quux = new Foo[String]() instead compiles successfully.
The code that checks for this is:
def findCyclicalLowerBound(tp: Type): Symbol = {
tp match {
case TypeBounds(lo, _) =>
// check that lower bound is not an F-bound
// but carefully: class Foo[T <: Bar[_ >: T]] should be allowed
for (tp1 @ TypeRef(_, sym, _) <- lo) {
if (settings.breakCycles) {
if (!sym.maybeInitialize) {
log(s"Cycle inspecting $lo for possible f-bounds: ${sym.fullLocationString}")
return sym
}
}
else sym.initialize
}
case _ =>
}
NoSymbol
}
The caller to this method knows if we are checking a Java-defined symbol; it could pass this information in we could change the condition to:
if (isJava || settings.breakCycles)`
I'm not 100% sure, but I feel like the better approach might be to use a custom type traverser (rather than tp.foreach) that stops descending at the _ >: T existential.