Skip to content

Commit ef84d2f

Browse files
authored
Merge pull request #5403 from abeln/java-varargs
Fix #5140: Ycheck failure in covariant java varargs
2 parents 3cfbb6d + 72e6cd5 commit ef84d2f

File tree

4 files changed

+34
-5
lines changed

4 files changed

+34
-5
lines changed

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -435,15 +435,19 @@ class TypeApplications(val self: Type) extends AnyVal {
435435
case _ => if (self.isMatch) MatchAlias(self) else TypeAlias(self)
436436
}
437437

438-
/** Translate a type of the form From[T] to To[T], keep other types as they are.
438+
/** Translate a type of the form From[T] to either To[T] or To[_ <: T] (if `wildcardArg` is set). Keep other types as they are.
439439
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
440440
* Do the same for by name types => From[T] and => To[T]
441441
*/
442-
def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type = self match {
442+
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false)(implicit ctx: Context): Type = self match {
443443
case self @ ExprType(tp) =>
444444
self.derivedExprType(tp.translateParameterized(from, to))
445445
case _ =>
446-
if (self.derivesFrom(from)) to.typeRef.appliedTo(self.baseType(from).argInfos)
446+
if (self.derivesFrom(from)) {
447+
val arg = self.baseType(from).argInfos.head
448+
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
449+
to.typeRef.appliedTo(arg1)
450+
}
447451
else self
448452
}
449453

@@ -453,7 +457,9 @@ class TypeApplications(val self: Type) extends AnyVal {
453457
def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type =
454458
if (self.isRepeatedParam) {
455459
val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass
456-
translateParameterized(defn.RepeatedParamClass, seqClass)
460+
// If `isJava` is set, then we want to turn `RepeatedParam[T]` into `Array[_ <: T]`,
461+
// since arrays aren't covariant until after erasure. See `tests/pos/i5140`.
462+
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = isJava)
457463
}
458464
else self
459465

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
125125
* @return a thicket consisting of `ddef` and a varargs bridge method
126126
* which overrides the Java varargs method JM from this phase on
127127
* and forwards to `ddef`.
128+
*
129+
* A bridge is necessary because the following hold
130+
* - the varargs in `ddef` will change from `RepeatedParam[T]` to `Seq[T]` after this phase
131+
* - _but_ the callers of `ddef` expect its varargs to be changed to `Array[_ <: T]`, since it overrides
132+
* a Java varargs
133+
* The solution is to add a "bridge" method that converts its argument from `Array[_ <: T]` to `Seq[T]` and
134+
* forwards it to `ddef`.
128135
*/
129136
private def addVarArgsBridge(ddef: DefDef)(implicit ctx: Context): Tree = {
130137
val original = ddef.symbol.asTerm
@@ -133,7 +140,9 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
133140
info = toJavaVarArgs(ddef.symbol.info)).enteredAfter(thisPhase).asTerm
134141
val bridgeDef = polyDefDef(bridge, trefs => vrefss => {
135142
val (vrefs :+ varArgRef) :: vrefss1 = vrefss
136-
val elemtp = varArgRef.tpe.widen.argTypes.head
143+
// Can't call `.argTypes` here because the underlying array type is of the
144+
// form `Array[_ <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`.
145+
val elemtp = varArgRef.tpe.widen.argInfos.head
137146
ref(original.termRef)
138147
.appliedToTypes(trefs)
139148
.appliedToArgs(vrefs :+ tpd.wrapArray(varArgRef, elemtp))

tests/pos/i5140/J.java

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Animal {}
2+
class Dog extends Animal {}
3+
class J {
4+
void foo(Animal... animal) {}
5+
}

tests/pos/i5140/S.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class S {
2+
val j = new J()
3+
val x: Array[Dog] = ???
4+
// Check that the java varargs for `foo` gets typed as `Array[_ <: Animal]`.
5+
// Otherwise, the call below would fail in -Ycheck:elimRepeated because arrays are invariant before erasure.
6+
// This is unsound but allowed.
7+
j.foo(x: _*)
8+
j.foo(new Dog, new Dog)
9+
}

0 commit comments

Comments
 (0)