Skip to content

Commit 6345477

Browse files
authored
Merge pull request #8718 from dotty-staging/java-varargs
Fix handling of Java varargs
2 parents 54360d0 + c3cb92d commit 6345477

File tree

16 files changed

+81
-42
lines changed

16 files changed

+81
-42
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -483,13 +483,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
483483
case NullTag => emit(asm.Opcodes.ACONST_NULL)
484484

485485
case ClazzTag =>
486-
val toPush: BType = {
487-
toTypeKind(const.typeValue) match {
488-
case kind: PrimitiveBType => boxedClassOfPrimitive(kind)
489-
case kind => kind
490-
}
491-
}
492-
mnode.visitLdcInsn(toPush.toASMType)
486+
val tp = toTypeKind(const.typeValue)
487+
// classOf[Int] is transformed to Integer.TYPE by ClassOf
488+
assert(!tp.isPrimitive, s"expected class type in classOf[T], found primitive type $tp")
489+
mnode.visitLdcInsn(tp.toASMType)
493490

494491
case EnumTag =>
495492
val sym = const.symbolValue

compiler/src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,28 @@ class TypeApplications(val self: Type) extends AnyVal {
413413
def translateToRepeated(from: ClassSymbol)(using Context): Type =
414414
translateParameterized(from, defn.RepeatedParamClass)
415415

416+
/** Translate `T` by `T & Object` in the situations where an `Array[T]`
417+
* coming from Java would need to be interpreted as an `Array[T & Object]`
418+
* to be erased correctly.
419+
*
420+
* This is necessary because a fully generic Java array erases to an array of Object,
421+
* whereas a fully generic Java array erases to Object to allow primitive arrays
422+
* as subtypeS.
423+
*
424+
* Note: According to
425+
* <http://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html>,
426+
* in the future the JVM will consider that:
427+
*
428+
* int[] <: Integer[] <: Object[]
429+
*
430+
* So hopefully our grand-children will not have to deal with this non-sense!
431+
*/
432+
def translateJavaArrayElementType(using Context): Type =
433+
if self.typeSymbol.isAbstractOrParamType && !self.derivesFrom(defn.ObjectClass) then
434+
AndType(self, defn.ObjectType)
435+
else
436+
self
437+
416438
/** If this is an encoding of a (partially) applied type, return its arguments,
417439
* otherwise return Nil.
418440
* Existential types in arguments are returned as TypeBounds instances.

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -395,16 +395,8 @@ class ClassfileParser(
395395
tpe
396396
case ARRAY_TAG =>
397397
while ('0' <= sig(index) && sig(index) <= '9') index += 1
398-
var elemtp = sig2type(tparams, skiptvs)
399-
// make unbounded Array[T] where T is a type variable into Array[T with Object]
400-
// (this is necessary because such arrays have a representation which is incompatible
401-
// with arrays of primitive types.
402-
// NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object
403-
// if the bound is exactly Object, it will have been converted to Any, and the comparison will fail
404-
// see also RestrictJavaArraysMap (when compiling java sources directly)
405-
if (elemtp.typeSymbol.isAbstractOrParamType && !(elemtp.derivesFrom(defn.ObjectClass)))
406-
elemtp = AndType(elemtp, defn.ObjectType)
407-
defn.ArrayOf(elemtp)
398+
val elemtp = sig2type(tparams, skiptvs)
399+
defn.ArrayOf(elemtp.translateJavaArrayElementType)
408400
case '(' =>
409401
// we need a method symbol. given in line 486 by calling getType(methodSym, ..)
410402
val paramtypes = new ListBuffer[Type]()

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,9 @@ object Scala2Unpickler {
6767
case tp: MethodType =>
6868
val lastArg = tp.paramInfos.last
6969
assert(lastArg isRef defn.ArrayClass)
70-
val elemtp0 :: Nil = lastArg.baseType(defn.ArrayClass).argInfos
71-
val elemtp = elemtp0 match {
72-
case AndType(t1, t2) => // drop intersection with Object for abstract types and parameters in varargs. Erasure can handle them.
73-
if t2.isAnyRef then
74-
t1 match {
75-
case t1: TypeParamRef => t1
76-
case t1: TypeRef if t1.symbol.isAbstractOrParamType => t1
77-
case _ => elemtp0
78-
}
79-
else elemtp0
80-
case _ =>
81-
elemtp0
82-
}
8370
tp.derivedLambdaType(
8471
tp.paramNames,
85-
tp.paramInfos.init :+ defn.RepeatedParamType.appliedTo(elemtp),
72+
tp.paramInfos.init :+ lastArg.translateParameterized(defn.ArrayClass, defn.RepeatedParamClass),
8673
tp.resultType)
8774
case tp: PolyType =>
8875
tp.derivedLambdaType(tp.paramNames, tp.paramInfos, arrayToRepeated(tp.resultType))

compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
9999
ref(defn.DottyArraysModule)
100100
.select(nme.seqToArray)
101101
.appliedToType(elemType)
102-
.appliedTo(tree, Literal(Constant(elemClass.typeRef)))
102+
.appliedTo(tree, clsOf(elemClass.typeRef))
103103
}
104104

105105
/** Convert Java array argument to Scala Seq */

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,6 @@ object Erasure {
309309
assert(!pt.isInstanceOf[SingletonType], pt)
310310
if (pt isRef defn.UnitClass) unbox(tree, pt)
311311
else (tree.tpe.widen, pt) match {
312-
case (JavaArrayType(treeElem), JavaArrayType(ptElem))
313-
if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType =>
314-
// See SI-2386 for one example of when this might be necessary.
315-
cast(ref(defn.runtimeMethodRef(nme.toObjectArray)).appliedTo(tree), pt)
316-
317312
// When casting between two EVTs, we need to check which one underlies the other to determine
318313
// whether u2evt or evt2u should be used.
319314
case (tp1 @ ErasedValueType(tycon1, underlying1), tp2 @ ErasedValueType(tycon2, underlying2)) =>

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ class TreeChecker extends Phase with SymTransformer {
328328
|Original tree : ${tree.show}
329329
|After checking: ${tree1.show}
330330
|Why different :
331-
""".stripMargin + core.TypeComparer.explained((tp1 <:< tp2)(_))
331+
""".stripMargin + core.TypeComparer.explained(tp1 <:< tp2)
332332
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
333333
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
334334
tree1

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,15 @@ class Typer extends Namer
16411641
else if (tpt1.symbol == defn.orType)
16421642
checkedArgs = checkedArgs.mapconserve(arg =>
16431643
checkSimpleKinded(checkNoWildcard(arg)))
1644+
else if (ctx.compilationUnit.isJava)
1645+
if (tpt1.symbol eq defn.ArrayClass) || (tpt1.symbol eq defn.RepeatedParamClass) then
1646+
checkedArgs match {
1647+
case List(arg) =>
1648+
val elemtp = arg.tpe.translateJavaArrayElementType
1649+
if (elemtp ne arg.tpe)
1650+
checkedArgs = List(TypeTree(elemtp).withSpan(arg.span))
1651+
case _ =>
1652+
}
16441653
assignType(cpy.AppliedTypeTree(tree)(tpt1, checkedArgs), tpt1, checkedArgs)
16451654
}
16461655
}

tests/explicit-nulls/run/generic-java-array-src/Test.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ object Test {
44
// then on the Scala side we'll need to pass a nullable array.
55
// i.e. with explicit nulls the previously-implicit cast becomes an explicit
66
// type annotation.
7-
val x = new Array[Int|Null](1)
7+
val x = new Array[Integer|Null](1)
88
x(0) = 10
99
println(JA.get(x))
1010

tests/neg/i533/JA.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class JA {
2+
public static <T> T get(T[] arr) {
3+
return arr[0];
4+
}
5+
6+
public static <T> T getVarargs(T ...arr) {
7+
return arr[0];
8+
}
9+
}

tests/neg/i533/Test.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
val x = new Array[Int](1)
4+
x(0) = 10
5+
println(JA.get(x)) // error
6+
println(JA.getVarargs(x: _*)) // error
7+
}
8+
}

tests/pos/arrays2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ one warning found
2323

2424
// #2461
2525
object arrays3 {
26-
def apply[X](xs : X*) : java.util.List[X] = java.util.Arrays.asList(xs: _*)
26+
def apply[X <: AnyRef](xs : X*) : java.util.List[X] = java.util.Arrays.asList(xs: _*)
2727
}

tests/run/i533/JA.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ class JA {
22
public static <T> T get(T[] arr) {
33
return arr[0];
44
}
5-
}
5+
6+
public static <T> T getVarargs(T ...arr) {
7+
return arr[0];
8+
}
9+
}

tests/run/i533/Test.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
object Test {
22
def main(args: Array[String]): Unit = {
3-
val x = new Array[Int](1)
3+
val x = new Array[Integer](1)
44
x(0) = 10
55
println(JA.get(x))
6+
println(JA.getVarargs(x: _*))
67
}
78
}

tests/run/java-varargs/A_1.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class A_1 {
2+
public static void foo(int... args) {
3+
}
4+
5+
public static <T> void gen(T... args) {
6+
}
7+
}

tests/run/java-varargs/Test_2.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
A_1.foo(Array(1): _*)
4+
A_1.foo(Seq(1): _*)
5+
A_1.gen(Array(1): _*)
6+
A_1.gen(Seq(1): _*)
7+
}
8+
}

0 commit comments

Comments
 (0)