@@ -4,15 +4,14 @@ package transform
44import core ._
55import StdNames .nme
66import Types ._
7- import dotty . tools . dotc . transform .MegaPhase ._
7+ import transform .MegaPhase ._
88import ast .Trees ._
99import Flags ._
1010import Contexts ._
1111import Symbols ._
1212import Constants ._
1313import Decorators ._
1414import Denotations ._ , SymDenotations ._
15- import dotty .tools .dotc .ast .tpd
1615import TypeErasure .erasure
1716import DenotTransformers ._
1817
@@ -34,12 +33,43 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
3433 def transformInfo (tp : Type , sym : Symbol )(using Context ): Type =
3534 elimRepeated(tp)
3635
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+ */
3741 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+
3862 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
4373 case ref1 =>
4474 ref1
4575
@@ -51,6 +81,11 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
5181
5282 private def parentHasAnnotation (sym : Symbol )(using Context ) = sym.allOverriddenSymbols.exists(hasVarargsAnnotation)
5383
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+
5489 /** Eliminate repeated parameters from method types. */
5590 private def elimRepeated (tp : Type )(using Context ): Type = tp.stripTypeVar match
5691 case tp @ MethodTpe (paramNames, paramTypes, resultType) =>
@@ -162,39 +197,36 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
162197
163198 /** Convert an Array into a scala.Seq */
164199 private def arrayToSeq (tree : Tree )(using Context ): Tree =
165- tpd. wrapArray(tree, tree.tpe.elemType)
200+ wrapArray(tree, tree.tpe.elemType)
166201
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 */
170203 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+
171208 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)
195229 else
196- if hasAnnotation then
197- report.error(" A method without repeated parameters cannot be annotated with @varargs" , sym.sourcePos)
198230 tree
199231
200232 /** Is there a repeated parameter in some parameter list? */
@@ -216,61 +248,52 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
216248 case _ =>
217249 throw new Exception (" Match error in @varargs checks. This should not happen, please open an issue " + tp)
218250
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.
223253 *
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)
227256 *
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]`
231260 * 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 .
233262 */
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
236274
237275 // 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 =
243277 original.copy(
244278 flags = if isBridge then flags | Artifact else flags,
245- info = toJavaVarArgs(ddef.symbol .info)
279+ info = toJavaVarArgs(original .info)
246280 ).asTerm
247- }
248281
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.
250283 // 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 ))
255287 }
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)
274297
275298 /** Convert type from Scala to Java varargs method */
276299 private def toJavaVarArgs (tp : Type )(using Context ): Type = tp match
0 commit comments