5

At the present time I have an existing Java project with Ant build that I am trying to set up to build with mixed source (Java and Scala).

I hava a Java interface defined as - public interface One<T extends Two<? super T>>

that when I run scalac from Ant build, I get the following error at :

Error: Illegal cyclic reference involving type T.

Since I want the Scala and Java class to be inter-dependent in my project, I first run the scalac task before running javac in the Ant build. My scalac block looks as follows:

<scalac srcdir="${src.path}" destdir="${build.classes}" encoding="UTF-8" >
        <classpath refid="compile.classpath" />
        <include name="**/*.scala"/>
        <include name="**/*.java"/>
</scalac> 

Is there any way getting around this? I can't modify the Java code since there is tons of other code that uses it.

I am using Java 7 and Scala 2.11.5


Here’s what I see at my end:

com.arif.One.java

package com.arif.java;
public interface One<T extends One<? super T>> {
    void test1();
}

com.arif.Two.java

package com.arif.java;
public class Two implements One<Two> {
    @Override
    public void test1() {
    } 
}

com.arif.scala.Main.scala

package com.arif.scala
import com.arif.java.Two

object Main {
  def main(args:Array[String]) {
    var b:Two = null
  } 
}

build.xml

<property name="scala.home" value="/Users/arif/software/scala/scala-2.11.5"/>
<property name="build.classes" value="./build/classes"/>

<property name="src.path" value="src"/>
<property name="javac.target" value="1.7"/>
<property name="compile.debug" value="true" />
<property name="compile.deprecation" value="false" />
<property name="compile.optimize" value="false" />

<path id="compile.classpath">
    <fileset dir="${scala.home}/lib" includes="*.jar" />
</path>

<path id="scala.classpath">
    <pathelement location="${scala.home}/lib/scala-compiler.jar" />
    <pathelement location="${scala.home}/lib/scala-library.jar" />
    <pathelement location="${scala.home}/lib/scala-reflect.jar" />
</path>

<target name="init">
    <taskdef resource="scala/tools/ant/antlib.xml">
        <classpath refid="scala.classpath" />
    </taskdef>
</target>

<target name="build" description="build" depends="init">
    <scalac srcdir="${src.path}" destdir="${build.classes}" encoding="UTF-8" >
        <classpath refid="compile.classpath" />
        <include name="**/*.scala"/>
        <include name="**/*.java"/>
    </scalac>
    <javac srcdir="${src.path}" destdir="${build.classes}" memoryinitialsize="512m" target="${javac.target}" source="${javac.target}" 
        memorymaximumsize="1024m" nowarn="true" debug="${compile.debug}" deprecation="${compile.deprecation}" 
        optimize="${compile.optimize}" fork="true" encoding="iso-8859-1" includeantruntime="false">
        <classpath refid="compile.classpath" />
    </javac>
</target>

From command line in the project root - doing an "ant build" gives the following -

[scalac] Compiling 1 scala and 2 java source files to /Users/arif/rbc/scala-java/build/classes
[scalac] /Users/arif/rbc/scala-java/src/com/arif/java/One.java:3: error: illegal cyclic reference involving type T
[scalac] public interface One<T extends One<? super T>> {
[scalac]                                    ^
[scalac] one error found
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61

2 Answers2

2

The PR for SI-4744 mentions an experimental flag -Ybreak-cycles that might help with this issue.

Christopher Currie
  • 3,025
  • 1
  • 29
  • 40
  • Thanks very much to all who answered. That resolved it. I wonder why the flag is still experimental; Can't base a product build on an experimental flag. – Arif Pathan Jan 28 '15 at 15:07
  • Working under separate compilation is already good; scalac is not a javac. This comment on the PR indicates why it's behind -Y: `looking at it again now with foggy jetlag brain`. Also, the best thx is an upvote. – som-snytt Jun 06 '15 at 16:10
1

Assuming that you do something like that in your scala code (because your java-code compiles fine by scalac):

scala> class OneImpl[T <: Two[_ >: T]] extends One[T]
<console>:15: error: illegal cyclic reference involving type T
   class OneImpl[T <: Two[_ >: T]] extends One[T]

Which actually causes error independly from your Java code (and Ant itself). I would recommend to change it to:

scala> class OneImpl[Z >: T, T <: Two[Z]] extends One[T] 
defined class OneImpl

Anyway, Scala doesn't support cyclic references. You can also workaround it with (if you really need existential type here):

scala> class OneImpl[Z >: T, T <: Two[Z], K <: Two[_ >: Z]](implicit  ev: K =:= T) extends One[T]
defined class OneImpl

See, http://www.scala-lang.org/old/node/6358

Updated. There is an issue with scalac SI-4744. It's affecting not all versions, so didn't find it from start. The solution (workaround) is to compile only this interface by javac itself:

javac One.java

Or something like that for ant:

<target name="build" description="build" depends="init">
    <javac srcdir="${src.path}" destdir="${build.classes}" encoding="UTF-8">
        <classpath refid="compile.classpath" />
        <include name="**/One.java"/>
    </javac> 
    <scalac srcdir="${src.path}" destdir="${build.classes}" encoding="UTF-8" >
        <classpath refid="compile.classpath" />
        <include name="**/*.scala"/>
        <include name="**/*.java"/>
        <exclude name="**/One.java"/>
    </scalac>
    <javac>...</javac>
</target>
dk14
  • 22,206
  • 4
  • 51
  • 88
  • Thanks for responding. The issue is that ** scalac has problems with the java code **. I can't compile the Java code before Scala code, since the Java code has dependency on the new Scala code. Also, my Scala code has dependencies on some of the Java classes. From what I know so far, my option at this point is to 1) run scalac with both scala and java classes (where scalac only uses the java code to resolve external symbols) 2) Compile the java code next. The error is reported when I run the first step. – Arif Pathan Jan 27 '15 at 14:11
  • Actually I've implemented your java-interface with `class OneImpl[Z >: T, T <: Two[Z]] extends One[T] ` - and compiled `scalac One.java Two.java OneImpl.scala` and it works, so the problem is in your scala-code – dk14 Jan 27 '15 at 14:23
  • please see the addition to my original submit; I added code for the sample project I tried. – Arif Pathan Jan 27 '15 at 22:22
  • Oh. now I see - it's known issue with the compiler https://issues.scala-lang.org/browse/SI-4744, but it worked for me to just compile `One.java` with javac, and everything else (including java files) with scalac. – dk14 Jan 27 '15 at 23:04