Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scalac reports error on valid Java class: illegal cyclic reference involving type T #4744

Open
scabug opened this issue Jun 28, 2011 · 17 comments

Comments

@scabug
Copy link

scabug commented Jun 28, 2011

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.

@scabug
Copy link
Author

scabug commented Jun 28, 2011

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

@scabug
Copy link
Author

scabug commented Jun 30, 2011

@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.

@scabug
Copy link
Author

scabug commented Sep 29, 2012

@paulp said:
scala/scala#1422

@scabug
Copy link
Author

scabug commented May 31, 2014

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

@scabug
Copy link
Author

scabug commented Jan 27, 2015

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

@scabug
Copy link
Author

scabug commented Mar 6, 2015

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.

@scabug
Copy link
Author

scabug commented Jul 1, 2015

Per Mildner (per.mildner-at-sics.se) said:
Ping. Any news on this bug?

@scabug
Copy link
Author

scabug commented Nov 30, 2015

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.

@scabug
Copy link
Author

scabug commented Aug 9, 2016

Per Mildner (per.mildner-at-sics.se) said:
Any news? I this fixed in some other version/branch of Scala?

@scabug
Copy link
Author

scabug commented Aug 18, 2016

@SethTisue said:
2.12.0-M5 gives the same error

@scabug scabug added this to the 2.13.0-M1 milestone Apr 7, 2017
@adriaanm adriaanm modified the milestones: 2.13.0-M1, 2.13.0-M2 Apr 14, 2017
@szeiger szeiger modified the milestones: 2.13.0-M3, 2.13.0-M2 Jul 20, 2017
@adriaanm adriaanm modified the milestones: 2.13.0-M3, 2.13.0-M4 Jan 30, 2018
@SethTisue SethTisue removed the backport label Feb 6, 2018
@SethTisue

This comment was marked as outdated.

@som-snytt
Copy link

"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

@eleansa
Copy link

eleansa commented May 25, 2021

Hi @SethTisue, any updates on this?

@SethTisue
Copy link
Member

@eleansa no. nobody is working on this at present. a pull request with a fix would be very welcome

@dwijnand
Copy link
Member

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...

@ek-gh
Copy link

ek-gh commented Apr 28, 2022

Same issue has surfaced in Scala 3.1.2. I did not have this problem with 2.13.

@retronym
Copy link
Member

retronym commented Jul 8, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants