@@ -4,15 +4,14 @@ package transform
4
4
import core ._
5
5
import StdNames .nme
6
6
import Types ._
7
- import dotty . tools . dotc . transform .MegaPhase ._
7
+ import transform .MegaPhase ._
8
8
import ast .Trees ._
9
9
import Flags ._
10
10
import Contexts ._
11
11
import Symbols ._
12
12
import Constants ._
13
13
import Decorators ._
14
14
import Denotations ._ , SymDenotations ._
15
- import dotty .tools .dotc .ast .tpd
16
15
import TypeErasure .erasure
17
16
import DenotTransformers ._
18
17
@@ -34,12 +33,43 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
34
33
def transformInfo (tp : Type , sym : Symbol )(using Context ): Type =
35
34
elimRepeated(tp)
36
35
36
+ /** Create forwarder symbols for the methods that are annotated
37
+ * with `@varargs` or that override java varargs.
38
+ *
39
+ * The definitions (DefDef) for these symbols are created by transformDefDef.
40
+ */
37
41
override def transform (ref : SingleDenotation )(using Context ): SingleDenotation =
42
+ def transformVarArgs (sym : Symbol , isJavaVarargsOverride : Boolean ): Unit =
43
+ val hasAnnotation = hasVarargsAnnotation(sym)
44
+ val hasRepeatedParam = hasRepeatedParams(sym)
45
+ if hasRepeatedParam then
46
+ if isJavaVarargsOverride || hasAnnotation || parentHasAnnotation(sym) then
47
+ // java varargs are more restrictive than scala's
48
+ // see https://github.com/scala/bug/issues/11714
49
+ val validJava = isValidJavaVarArgs(sym.info)
50
+ if ! validJava then
51
+ report.error(""" To generate java-compatible varargs:
52
+ | - there must be a single repeated parameter
53
+ | - it must be the last argument in the last parameter list
54
+ |""" .stripMargin,
55
+ sym.sourcePos)
56
+ else
57
+ addVarArgsForwarder(sym, isJavaVarargsOverride, hasAnnotation)
58
+ else if hasAnnotation
59
+ report.error(" A method without repeated parameters cannot be annotated with @varargs" , sym.sourcePos)
60
+ end
61
+
38
62
super .transform(ref) match
39
- case ref1 : SymDenotation if (ref1 ne ref) && overridesJava(ref1.symbol) =>
40
- // This method won't override the corresponding Java method at the end of this phase,
41
- // only the forwarder added by `addVarArgsForwarder` will.
42
- ref1.copySymDenotation(initFlags = ref1.flags &~ Override )
63
+ case ref1 : SymDenotation if ref1.is(Method , butNot = JavaDefined ) =>
64
+ val sym = ref1.symbol
65
+ val isJavaVarargsOverride = (ref1 ne ref) && overridesJava(sym)
66
+ transformVarArgs(sym, isJavaVarargsOverride)
67
+ if isJavaVarargsOverride then
68
+ // This method won't override the corresponding Java method at the end of this phase,
69
+ // only the forwarder added by `addVarArgsForwarder` will.
70
+ ref1.copySymDenotation(initFlags = ref1.flags &~ Override )
71
+ else
72
+ ref1
43
73
case ref1 =>
44
74
ref1
45
75
@@ -51,6 +81,11 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
51
81
52
82
private def parentHasAnnotation (sym : Symbol )(using Context ) = sym.allOverriddenSymbols.exists(hasVarargsAnnotation)
53
83
84
+ private def isVarargsMethod (sym : Symbol )(using Context ) =
85
+ hasVarargsAnnotation(sym) ||
86
+ hasRepeatedParams(sym) &&
87
+ (sym.allOverriddenSymbols.exists(s => s.is(JavaDefined ) || hasVarargsAnnotation(s)))
88
+
54
89
/** Eliminate repeated parameters from method types. */
55
90
private def elimRepeated (tp : Type )(using Context ): Type = tp.stripTypeVar match
56
91
case tp @ MethodTpe (paramNames, paramTypes, resultType) =>
@@ -162,39 +197,36 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
162
197
163
198
/** Convert an Array into a scala.Seq */
164
199
private def arrayToSeq (tree : Tree )(using Context ): Tree =
165
- tpd. wrapArray(tree, tree.tpe.elemType)
200
+ wrapArray(tree, tree.tpe.elemType)
166
201
167
- /** If method overrides a Java varargs method or is annotated with @varargs, add a varargs bridge.
168
- * Also transform trees inside method annotation.
169
- */
202
+ /** Generate the method definitions for the varargs forwarders created in transform */
170
203
override def transformDefDef (tree : DefDef )(using Context ): Tree =
204
+ // If transform reported an error, don't go further
205
+ if ctx.reporter.hasErrors then
206
+ return tree
207
+
171
208
val sym = tree.symbol
172
- val hasAnnotation = hasVarargsAnnotation(sym)
173
-
174
- // atPhase(thisPhase) is used where necessary to see the repeated
175
- // parameters before their elimination
176
- val hasRepeatedParam = atPhase(thisPhase)(hasRepeatedParams(sym))
177
- if hasRepeatedParam then
178
- val isOverride = atPhase(thisPhase)(overridesJava(sym))
179
- if isOverride || hasAnnotation || parentHasAnnotation(sym) then
180
- // java varargs are more restrictive than scala's
181
- // see https://github.com/scala/bug/issues/11714
182
- val validJava = atPhase(thisPhase)(isValidJavaVarArgs(sym.info))
183
- if ! validJava then
184
- report.error(""" To generate java-compatible varargs:
185
- | - there must be a single repeated parameter
186
- | - it must be the last argument in the last parameter list
187
- |""" .stripMargin,
188
- sym.sourcePos)
189
- tree
190
- else
191
- // non-overrides cannot be synthetic otherwise javac refuses to call them
192
- addVarArgsForwarder(tree, isBridge = isOverride)
193
- else
194
- tree
209
+ val isVarArgs = atPhase(thisPhase)(isVarargsMethod(sym))
210
+ if isVarArgs then
211
+ // Get the symbol generated in transform
212
+ val forwarderType = atPhase(thisPhase)(toJavaVarArgs(sym.info))
213
+ val forwarderSym = currentClass.info.decl(sym.name).alternatives
214
+ .find(_.info.matches(forwarderType))
215
+ .get
216
+ .symbol.asTerm
217
+ // Generate the method
218
+ val forwarderDef = polyDefDef(forwarderSym, trefs => vrefss => {
219
+ val init :+ (last :+ vararg) = vrefss
220
+ // Can't call `.argTypes` here because the underlying array type is of the
221
+ // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`.
222
+ val elemtp = vararg.tpe.widen.argInfos.head
223
+ ref(sym.termRef)
224
+ .appliedToTypes(trefs)
225
+ .appliedToArgss(init)
226
+ .appliedToArgs(last :+ wrapArray(vararg, elemtp))
227
+ })
228
+ Thicket (tree, forwarderDef)
195
229
else
196
- if hasAnnotation then
197
- report.error(" A method without repeated parameters cannot be annotated with @varargs" , sym.sourcePos)
198
230
tree
199
231
200
232
/** Is there a repeated parameter in some parameter list? */
@@ -216,61 +248,52 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
216
248
case _ =>
217
249
throw new Exception (" Match error in @varargs checks. This should not happen, please open an issue " + tp)
218
250
219
-
220
- /** Add a Java varargs forwarder
221
- * @param ddef the original method definition
222
- * @param isBridge true if we are generating a "bridge" (synthetic override forwarder)
251
+ /** Add the symbol of a Java varargs forwarder to the scope.
252
+ * It retains all the flags of the original method.
223
253
*
224
- * @return a thicket consisting of `ddef` and an additional method
225
- * that forwards java varargs to `ddef`. It retains all the
226
- * flags of `ddef` except `Private`.
254
+ * @param original the original method symbol
255
+ * @param isBridge true if we are generating a "bridge" (synthetic override forwarder)
227
256
*
228
- * A forwarder is necessary because the following hold :
229
- * - the varargs in `ddef ` will change from `RepeatedParam[T]` to `Seq[T]` after this phase
230
- * - _but_ the callers of `ddef` expect its varargs to be changed to `Array[? <: T]`
257
+ * A forwarder is necessary because the following holds :
258
+ * - the varargs in `original ` will change from `RepeatedParam[T]` to `Seq[T]` after this phase
259
+ * - _but_ the callers of the method expect its varargs to be changed to `Array[? <: T]`
231
260
* The solution is to add a method that converts its argument from `Array[? <: T]` to `Seq[T]` and
232
- * forwards it to `ddef` .
261
+ * forwards it to the original method .
233
262
*/
234
- private def addVarArgsForwarder (ddef : DefDef , isBridge : Boolean )(using Context ): Tree =
235
- val original = ddef.symbol
263
+ private def addVarArgsForwarder (original : Symbol , isBridge : Boolean , hasAnnotation : Boolean )(using Context ): Unit =
264
+ val owner = original.owner
265
+ if ! owner.isClass then
266
+ report.error(" inner methods cannot be annotated with @varargs" , original.sourcePos)
267
+ return
268
+
269
+ val classInfo = owner.info
270
+
271
+ // For simplicity we always set the varargs flag,
272
+ // although it's not strictly necessary for overrides.
273
+ val flags = original.flags | JavaVarargs
236
274
237
275
// The java-compatible forwarder symbol
238
- val sym = atPhase(thisPhase) {
239
- // Capture the flags before they get modified by #transform.
240
- // For simplicity we always set the varargs flag,
241
- // although it's not strictly necessary for overrides.
242
- val flags = original.flags | JavaVarargs
276
+ val forwarder =
243
277
original.copy(
244
278
flags = if isBridge then flags | Artifact else flags,
245
- info = toJavaVarArgs(ddef.symbol .info)
279
+ info = toJavaVarArgs(original .info)
246
280
).asTerm
247
- }
248
281
249
- // Find a method that would conflict with the forwarder if the latter existed.
282
+ // Find methods that would conflict with the forwarder if the latter existed.
250
283
// This needs to be done at thisPhase so that parent @varargs don't conflict.
251
- val conflict = atPhase(thisPhase) {
252
- currentClass.info.member(sym.name).alternatives.find { s =>
253
- s.matches(sym) &&
254
- ! (isBridge && s.asSymDenotation.is(JavaDefined ))
284
+ val conflicts =
285
+ classInfo.member(original.name).altsWith { s =>
286
+ s.matches(forwarder) && ! (isBridge && s.is(JavaDefined ))
255
287
}
256
- }
257
-
258
- conflict match
259
- case Some (conflict) =>
260
- report.error(s " @varargs produces a forwarder method that conflicts with ${conflict.showDcl}" , original.sourcePos)
261
- ddef
262
- case None =>
263
- val bridgeDef = polyDefDef(sym.enteredAfter(thisPhase), trefs => vrefss => {
264
- val init :+ (last :+ vararg) = vrefss
265
- // Can't call `.argTypes` here because the underlying array type is of the
266
- // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`.
267
- val elemtp = vararg.tpe.widen.argInfos.head
268
- ref(original.termRef)
269
- .appliedToTypes(trefs)
270
- .appliedToArgss(init)
271
- .appliedToArgs(last :+ tpd.wrapArray(vararg, elemtp))
272
- })
273
- Thicket (ddef, bridgeDef)
288
+ conflicts match
289
+ case conflict :: _ =>
290
+ val src =
291
+ if hasAnnotation then " @varargs"
292
+ else if isBridge then " overriding a java varargs method"
293
+ else " @varargs (on overriden method)"
294
+ report.error(s " $src produces a forwarder method that conflicts with ${conflict.showDcl}" , original.sourcePos)
295
+ case Nil =>
296
+ forwarder.enteredAfter(thisPhase)
274
297
275
298
/** Convert type from Scala to Java varargs method */
276
299
private def toJavaVarArgs (tp : Type )(using Context ): Type = tp match
0 commit comments