Skip to content

Commit 8212750

Browse files
Check parent methods for varargs annotation
1 parent a6b7234 commit 8212750

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

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

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
4646

4747
private def overridesJava(sym: Symbol)(using Context) = sym.allOverriddenSymbols.exists(_.is(JavaDefined))
4848

49-
private def hasVargsAnnotation(sym: Symbol)(using ctx: Context) = sym.hasAnnotation(defn.VarargsAnnot)
49+
private def hasVarargsAnnotation(sym: Symbol)(using Context) = sym.hasAnnotation(defn.VarargsAnnot)
50+
51+
private def parentHasAnnotation(sym: Symbol)(using Context) = sym.allOverriddenSymbols.exists(hasVarargsAnnotation)
5052

5153
/** Eliminate repeated parameters from method types. */
5254
private def elimRepeated(tp: Type)(using Context): Type = tp.stripTypeVar match
@@ -114,8 +116,9 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
114116
*/
115117
override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
116118
ctx.atPhase(thisPhase) {
117-
val isOverride = overridesJava(tree.symbol)
118-
val hasAnnotation = hasVargsAnnotation(tree.symbol)
119+
val sym = tree.symbol
120+
val isOverride = overridesJava(sym)
121+
val hasAnnotation = hasVarargsAnnotation(sym) || parentHasAnnotation(sym)
119122
if tree.symbol.info.isVarArgsMethod && (isOverride || hasAnnotation) then
120123
// non-overrides need the varargs bytecode flag and cannot be synthetic
121124
// otherwise javac refuses to call them.
@@ -125,7 +128,7 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
125128
}
126129

127130
/** Add a Java varargs bridge
128-
* @param ddef the original method definition
131+
* @param ddef the original method definition
129132
* @param addFlag the flag to add to the method symbol
130133
131134
* @return a thicket consisting of `ddef` and a varargs bridge method
@@ -141,6 +144,7 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
141144
private def addVarArgsBridge(ddef: DefDef, synthetic: Boolean)(using Context): Tree =
142145
val original = ddef.symbol.asTerm
143146
// For simplicity we always set the varargs flag
147+
// although it's not strictly necessary for overrides
144148
val flags = ddef.symbol.flags | JavaVarargs &~ Private
145149
val bridge = original.copy(
146150
flags = if synthetic then flags | Artifact else flags,
@@ -162,11 +166,26 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
162166

163167
/** Convert type from Scala to Java varargs method */
164168
private def toJavaVarArgs(tp: Type)(using Context): Type = tp match
165-
case tp: PolyType =>
166-
tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(tp.resultType))
167-
case tp: MethodType =>
168-
val inits :+ last = tp.paramInfos
169-
val last1 = last.translateFromRepeated(toArray = true)
170-
tp.derivedLambdaType(tp.paramNames, inits :+ last1, tp.resultType)
169+
case tp: PolyType =>
170+
tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(tp.resultType))
171+
case tp: MethodType =>
172+
val inits :+ last = tp.paramInfos
173+
val vararg = varargArrayType(last)
174+
val params = inits :+ vararg
175+
tp.derivedLambdaType(tp.paramNames, params, tp.resultType)
176+
177+
/** Translate a repeated type T* to an `Array[? <: Upper]`
178+
* such that it is compatible with java varargs.
179+
*
180+
* If T is not a primitive type, we set `Upper = T & AnyRef`
181+
* to prevent the erasure of `Array[? <: Upper]` to Object,
182+
* which would break the varargs from Java.
183+
*/
184+
private def varargArrayType(tp: Type)(using Context): Type =
185+
val array = tp.translateFromRepeated(toArray = true)
186+
val element = array.elemType.typeSymbol
187+
188+
if element.isPrimitiveValueClass then array
189+
else defn.ArrayOf(TypeBounds.upper(AndType(element.typeRef, defn.AnyRefType)))
171190

172191
}

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ enum-java
2727
zero-arity-case-class.scala
2828
tuple-ops.scala
2929
i7212
30+
varargs-abstract

tests/run/varargs-abstract/Test.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import java.util.Comparator;
2+
3+
public class Test {
4+
public static void main(String[] args) {
5+
ClassImplementsClass c = new ClassImplementsClass();
6+
7+
c.x("a", "b", "c");
8+
c.y("a", "b", "c");
9+
c.z("a", "b", "c");
10+
11+
VarargAbstractClass i = new ClassImplementsClass();
12+
13+
i.x("a", "b", "c");
14+
i.y("a", "b", "c");
15+
// i.z("a", "b", "c");
16+
// ClassCastException at runtime because the generated
17+
// signature of z doesn't mention the type parameter (it should)
18+
}
19+
}

tests/run/varargs-abstract/test.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import scala.annotation.varargs
2+
3+
abstract class VarargAbstractClass[T] {
4+
@varargs
5+
def x(els: String*): Int
6+
7+
@varargs
8+
def y(els: String*): Int
9+
10+
@varargs
11+
def z(els: T*): Int
12+
}
13+
class ClassImplementsClass extends VarargAbstractClass[String] {
14+
15+
override def x(els: String*): Int = els.length
16+
override def y(els: String*): Int = els.length
17+
override def z(els: String*): Int = els.length
18+
}

0 commit comments

Comments
 (0)