@@ -68,6 +68,9 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
68
68
def transformTypeOfTree (tree : Tree )(using Context ): Tree =
69
69
tree.withType(elimRepeated(tree.tpe))
70
70
71
+ override def transformTypeApply (tree : TypeApply )(using Context ): Tree =
72
+ transformTypeOfTree(tree)
73
+
71
74
override def transformIdent (tree : Ident )(using Context ): Tree =
72
75
transformTypeOfTree(tree)
73
76
@@ -108,25 +111,52 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
108
111
private def arrayToSeq (tree : Tree )(using Context ): Tree =
109
112
tpd.wrapArray(tree, tree.tpe.elemType)
110
113
111
- override def transformTypeApply (tree : TypeApply )(using Context ): Tree =
112
- transformTypeOfTree(tree)
113
-
114
114
/** If method overrides a Java varargs method or is annotated with @varargs, add a varargs bridge.
115
115
* Also transform trees inside method annotation.
116
116
*/
117
117
override def transformDefDef (tree : DefDef )(using Context ): Tree =
118
118
atPhase(thisPhase) {
119
119
val sym = tree.symbol
120
- val isOverride = overridesJava(sym)
121
- val hasAnnotation = hasVarargsAnnotation(sym) || parentHasAnnotation(sym)
122
- if tree.symbol.info.isVarArgsMethod && (isOverride || hasAnnotation) then
123
- // non-overrides need the varargs bytecode flag and cannot be synthetic
124
- // otherwise javac refuses to call them.
125
- addVarArgsBridge(tree, isOverride)
120
+ val hasAnnotation = hasVarargsAnnotation(sym)
121
+ if hasRepeatedParams(sym) then
122
+ val isOverride = overridesJava(sym)
123
+ if isOverride || hasAnnotation || parentHasAnnotation(sym) then
124
+ // java varargs are more restrictive than scala's
125
+ // see https://github.com/scala/bug/issues/11714
126
+ if ! isValidJavaVarArgs(sym.info) then
127
+ ctx.error(""" To generate java-compatible varargs:
128
+ | - there must be a single repeated parameter
129
+ | - it must be the last argument in the last parameter list
130
+ |""" .stripMargin,
131
+ tree.sourcePos)
132
+ tree
133
+ else
134
+ addVarArgsBridge(tree, isOverride)
135
+ else
136
+ tree
126
137
else
138
+ if hasAnnotation then
139
+ ctx.error(" A method without repeated parameters cannot be annotated with @varargs" , tree.sourcePos)
127
140
tree
128
141
}
129
142
143
+ /** Is there a repeated parameter in some parameter list? */
144
+ private def hasRepeatedParams (sym : Symbol )(using Context ): Boolean =
145
+ sym.info.paramInfoss.flatten.exists(_.isRepeatedParam)
146
+
147
+ /** Is this the type of a method that has a repeated parameter type as
148
+ * its last parameter in the last parameter list?
149
+ */
150
+ private def isValidJavaVarArgs (t : Type )(using Context ): Boolean = t match
151
+ case mt : MethodType =>
152
+ val initp :+ lastp = mt.paramInfoss
153
+ initp.forall(_.forall(! _.isRepeatedParam)) &&
154
+ lastp.nonEmpty &&
155
+ lastp.init.forall(! _.isRepeatedParam) &&
156
+ lastp.last.isRepeatedParam
157
+ case _ => false
158
+
159
+
130
160
/** Add a Java varargs bridge
131
161
* @param ddef the original method definition
132
162
* @param addFlag the flag to add to the method symbol
@@ -141,38 +171,52 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
141
171
* The solution is to add a "bridge" method that converts its argument from `Array[? <: T]` to `Seq[T]` and
142
172
* forwards it to `ddef`.
143
173
*/
144
- private def addVarArgsBridge (ddef : DefDef , synthetic : Boolean )(using Context ): Tree =
174
+ private def addVarArgsBridge (ddef : DefDef , javaOverride : Boolean )(using ctx : Context ): Tree =
145
175
val original = ddef.symbol.asTerm
146
176
// For simplicity we always set the varargs flag
147
177
// although it's not strictly necessary for overrides
148
- val flags = ddef.symbol.flags | JavaVarargs &~ Private
178
+ // (but it is for non-overrides)
179
+ val flags = ddef.symbol.flags | JavaVarargs
149
180
val bridge = original.copy(
150
- flags = if synthetic then flags | Artifact else flags,
181
+ // non-overrides cannot be synthetic otherwise javac refuses to call them
182
+ flags = if javaOverride then flags | Artifact else flags,
151
183
info = toJavaVarArgs(ddef.symbol.info)
152
184
).enteredAfter(thisPhase).asTerm
153
185
154
186
val bridgeDef = polyDefDef(bridge, trefs => vrefss => {
155
- val (vrefs :+ varArgRef) :: vrefss1 = vrefss
187
+ val init :+ (last :+ vararg) = vrefss
156
188
// Can't call `.argTypes` here because the underlying array type is of the
157
189
// form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`.
158
- val elemtp = varArgRef .tpe.widen.argInfos.head
190
+ val elemtp = vararg .tpe.widen.argInfos.head
159
191
ref(original.termRef)
160
192
.appliedToTypes(trefs)
161
- .appliedToArgs(vrefs :+ tpd.wrapArray(varArgRef, elemtp) )
162
- .appliedToArgss(vrefss1 )
193
+ .appliedToArgss(init )
194
+ .appliedToArgs(last :+ tpd.wrapArray(vararg, elemtp) )
163
195
})
164
196
165
- Thicket (ddef, bridgeDef)
197
+ val bridgeDenot = bridge.denot
198
+ currentClass.info.member(bridge.name).alternatives.find { s =>
199
+ s.matches(bridgeDenot) &&
200
+ ! (s.asSymDenotation.is(JavaDefined ) && javaOverride)
201
+ } match
202
+ case Some (conflict) =>
203
+ ctx.error(s " @varargs produces a forwarder method that conflicts with ${conflict.showDcl}" , ddef.sourcePos)
204
+ EmptyTree
205
+ case None =>
206
+ Thicket (ddef, bridgeDef)
166
207
167
208
/** Convert type from Scala to Java varargs method */
168
209
private def toJavaVarArgs (tp : Type )(using Context ): Type = tp match
169
210
case tp : PolyType =>
170
211
tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(tp.resultType))
171
212
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)
213
+ tp.resultType match
214
+ case m : MethodType => // multiple param lists
215
+ tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(m))
216
+ case _ =>
217
+ val init :+ last = tp.paramInfos
218
+ val vararg = varargArrayType(last)
219
+ tp.derivedLambdaType(tp.paramNames, init :+ vararg, tp.resultType)
176
220
177
221
/** Translate a repeated type T* to an `Array[? <: Upper]`
178
222
* such that it is compatible with java varargs.
0 commit comments