In http://lampsvn.epfl.ch/trac/scala/ticket/1342, mustaghattack submitted the following test cases:
File scala/ticket1342/JavaVarArgsInterface.java:
package scala.ticket1342;
interface JavaVarArgsInterface {
void varArgsMethod( String ... args );
}
File scala/ticket1342/ScalaVarArgsImpl.scala:
package scala.ticket1342
class ScalaVarArgsImpl extends JavaVarArgsInterface {
def varArgsMethod( args : String*) {
for( arg <- args ) println( arg )
}
}
I would now like to use ScalaVarArgsImpl.varArgsMethod both ways around
and I would like to achieve this with 2.7.X as well as with 2.8.0....
Let me first create File scala/ticket1342/ScalaVarArgsMain.scala:
package scala.ticket1342
object ScalaVarArgsMain {
def main( args : Array[String]) {
val svai = new ScalaVarArgsImpl
svai.varArgsMethod(args(0), args(1), args(2))
}
}
the interface
$ javac -d bin27X src/scala/ticket1342/JavaVarArgsInterface.java
$ javap -c -classpath bin27X/ scala.ticket1342.JavaVarArgsInterface
Compiled from "JavaVarArgsInterface.java"
interface scala.ticket1342.JavaVarArgsInterface{
public abstract void varArgsMethod(java.lang.String[]);
}
the implementation
$ scalac27X -d bin27X src/scala/ticket1342/ScalaVarArgsImpl.scala
$ javap -classpath bin27X/ scala.ticket1342.ScalaVarArgsImpl
Compiled from "ScalaVarArgsImpl.scala"
public class scala.ticket1342.ScalaVarArgsImpl extends java.lang.Object implements scala.ticket1342.JavaVarArgsInterface,scala.ScalaObject{
public scala.ticket1342.ScalaVarArgsImpl();
public void varArgsMethod(scala.Seq);
public int $tag() throws java.rmi.RemoteException;
}
and the cli
$ scalac27X -d bin27X src/scala/ticket1342/ScalaVarArgsMain.scala
$ javap -classpath bin27X/ scala.ticket1342.ScalaVarArgsMain
Compiled from "ScalaVarArgsMain.scala"
public final class scala.ticket1342.ScalaVarArgsMain extends java.lang.Object{
public static final void main(java.lang.String[]);
public static final int $tag() throws java.rmi.RemoteException;
}
now executed like
$ scala27X -cp bin27X scala.ticket1342.ScalaVarArgsMain aaa bbb ccc ddd aaa bbb ccc
For invocation from Java, let me create File scala/ticket1342/JavaVarArgsMain27X.java:
package scala.ticket1342;
import java.util.Arrays;
import java.util.ArrayList;
import scala.collection.jcl.Buffer;
import scala.collection.jcl.Conversions;
class JavaVarArgsMain27X {
static public void main( String[] args ) {
ScalaVarArgsImpl svai = new ScalaVarArgsImpl();
ArrayList al = new ArrayList(Arrays.asList(args[0], args[1], args[2]));
svai.varArgsMethod((Buffer)Conversions.convertList(al));
}
}
with compilation going as
$ javac -d bin27X/ -cp /usr/share/java/scala27X/scala-library.jar:bin27X src/scala/ticket1342/JavaVarArgsMain27X.java Note: src/scala/ticket1342/JavaVarArgsMain27X.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
and execution going as
$ java -cp /usr/share/java/scala27X/scala-library.jar:bin27X scala.ticket1342.JavaVarArgsMain27X aaa bbb ccc ddd aaa bbb ccc
the interface
$ javac -d bin280 src/scala/ticket1342/JavaVarArgsInterface.java
$ javap -c -classpath bin280/ scala.ticket1342.JavaVarArgsInterface
Compiled from "JavaVarArgsInterface.java"
interface scala.ticket1342.JavaVarArgsInterface{
public abstract void varArgsMethod(java.lang.String[]);
}
the implementation
$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsImpl.scala
src/scala/ticket1342/ScalaVarArgsImpl.scala:3: error: class ScalaVarArgsImpl needs to be abstract, since method varArgsMethod in trait JavaVarArgsInterface of type (x$1: <repeated...>[java.lang.String])Unit is not defined
class ScalaVarArgsImpl extends JavaVarArgsInterface {
^
one error found
Some investigation showed a difference between 2.7.X and 2.8.0 manifesting in
src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
(not sure whether the root cause is here).
287 // 2. Check that only abstract classes have deferred members
288 if (clazz.isClass && !clazz.isTrait) {
289 def abstractClassError(mustBeMixin: Boolean, msg: String) {
290 unit.error(clazz.pos,
291 (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible"
292 else if (mustBeMixin) clazz.toString() + " needs to be a mixin"
293 else clazz.toString() + " needs to be abstract") + ", since " + msg);
294 clazz.setFlag(ABSTRACT)
295 }
296 // Find a concrete Java method that overrides `sym' under the erasure model.
297 // Bridge symbols qualify.
298 // Used as a fall back if no overriding symbol of a Java abstract method can be found
299 def javaErasedOverridingSym(sym: Symbol): Symbol =
300 clazz.tpe.findMember(sym.name, PRIVATE, 0, false)(NoSymbol).filter(other =>
301 !other.isDeferred &&
302 (other hasFlag JAVA) && {
303 val tp1 = erasure.erasure(clazz.thisType.memberType(sym))
304 val tp2 = erasure.erasure(clazz.thisType.memberType(other))
305 atPhase(currentRun.erasurePhase.next)(tp1 matches tp2)
306 })
** 307 for (member <- clazz.tpe.nonPrivateMembers)**
308 if (member.isDeferred && !(clazz hasFlag ABSTRACT) &&
309 !isAbstractTypeWithoutFBound(member) &&
310 !((member hasFlag JAVA) && javaErasedOverridingSym(member) != NoSymbol)) {
311 abstractClassError(
312 false, infoString(member) + " is not defined" + analyzer.varNotice(member))
313 } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
314 val other = member.superSymbol(clazz);
315 abstractClassError(true,
316 infoString(member) + " is marked `abstract' and `override'" +
317 (if (other != NoSymbol)
318 " and overrides incomplete superclass member " + infoString(other)
319 else ""))
320 }
As it turned out, in the cycle starting at line 307, a member named varArgsMethod appeared twice in 2.8.0, once with attributes from the implementation and once with attributes from the abstract one, the latter resulting in an abstractClassError. A naive patch to RefChecks.scala looks like:
307 for (member <- clazz.tpe.nonPrivateMembers)
308 if (member.isDeferred && !(clazz hasFlag ABSTRACT) &&
309 !isAbstractTypeWithoutFBound(member) &&
** 310 !(member hasFlag JAVA) &&**
311 !((member hasFlag JAVA) && javaErasedOverridingSym(member) != NoSymbol)) {
312 abstractClassError(
313 false, infoString(member) + " is not defined" + analyzer.varNotice(member))
314 } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
315 val other = member.superSymbol(clazz);
316 abstractClassError(true,
317 infoString(member) + " is marked `abstract' and `override'" +
318 (if (other != NoSymbol)
319 " and overrides incomplete superclass member " + infoString(other)
320 else ""))
321 }
Now the implementation compiles with 2.8.0:
$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsImpl.scala
$ javap -classpath bin280/ scala.ticket1342.ScalaVarArgsImpl
Compiled from "ScalaVarArgsImpl.scala"
public class scala.ticket1342.ScalaVarArgsImpl extends java.lang.Object implements scala.ticket1342.JavaVarArgsInterface,scala.ScalaObject{
public scala.ticket1342.ScalaVarArgsImpl();
public void varArgsMethod(scala.collection.Seq);
}
and the cli
$ scalac280 -d bin280 src/scala/ticket1342/ScalaVarArgsMain.scala
$ javap -classpath bin280/ scala.ticket1342.ScalaVarArgsMain
Compiled from "ScalaVarArgsMain.scala"
public final class scala.ticket1342.ScalaVarArgsMain extends java.lang.Object{
public static final void main(java.lang.String[]);
}
now executed like
$ scala280 -cp bin280 scala.ticket1342.ScalaVarArgsMain aaa bbb ccc ddd aaa bbb ccc
For invocation from Java, let me create File scala/ticket1342/JavaVarArgsMain280.java:
package scala.ticket1342;
import java.util.Arrays;
import java.util.ArrayList;
import scala.collection.mutable.Buffer;
import scala.collection.JavaConversions;
class JavaVarArgsMain280 {
static public void main( String[] args ) {
ScalaVarArgsImpl svai = new ScalaVarArgsImpl();
ArrayList al = new ArrayList(Arrays.asList(args[0], args[1], args[2]));
svai.varArgsMethod((Buffer)JavaConversions.asBuffer(al));
}
}
with compilation going as
$ javac -d bin280/ -cp /usr/share/java/scala280/scala-library.jar:bin280 src/scala/ticket1342/JavaVarArgsMain280.java Note: src/scala/ticket1342/JavaVarArgsMain280.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
and execution going as
$ java -cp /usr/share/java/scala280/scala-library.jar:bin280 scala.ticket1342.JavaVarArgsMain280 aaa bbb ccc ddd aaa bbb ccc