Skip to content

Commit 3f70d9c

Browse files
Fix scala#9463: create varargs forwarder symbols in the denotation transformer
1 parent fbd69ad commit 3f70d9c

File tree

1 file changed

+88
-72
lines changed

1 file changed

+88
-72
lines changed

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

Lines changed: 88 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ package transform
44
import core._
55
import StdNames.nme
66
import Types._
7-
import dotty.tools.dotc.transform.MegaPhase._
7+
import transform.MegaPhase._
88
import ast.Trees._
99
import Flags._
1010
import Contexts._
1111
import Symbols._
1212
import Constants._
1313
import Decorators._
1414
import Denotations._, SymDenotations._
15-
import dotty.tools.dotc.ast.tpd
1615
import TypeErasure.erasure
1716
import 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, isJavaOverride: Boolean): Unit =
43+
val hasAnnotation = hasVarargsAnnotation(sym)
44+
val hasRepeatedParam = hasRepeatedParams(sym)
45+
if hasRepeatedParam then
46+
if isJavaOverride || 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, isJavaOverride)
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) =>
64+
val sym = ref1.symbol
65+
val isJavaOverride = overridesJava(sym)
66+
transformVarArgs(sym, isJavaOverride)
67+
if (ref1 ne ref) && isJavaOverride 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,45 @@ 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)(using Context): Unit =
264+
val classInfo = original.owner.info
265+
val decls = classInfo.decls.cloneScope
266+
267+
// For simplicity we always set the varargs flag,
268+
// although it's not strictly necessary for overrides.
269+
val flags = original.flags | JavaVarargs
236270

237271
// 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
272+
val forwarder =
243273
original.copy(
244274
flags = if isBridge then flags | Artifact else flags,
245-
info = toJavaVarArgs(ddef.symbol.info)
275+
info = toJavaVarArgs(original.info)
246276
).asTerm
247-
}
248277

249278
// Find a method that would conflict with the forwarder if the latter existed.
250279
// 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) &&
280+
val conflict =
281+
classInfo.member(original.name).alternatives.find { s =>
282+
s.matches(forwarder) &&
254283
!(isBridge && s.asSymDenotation.is(JavaDefined))
255284
}
256-
}
257-
258285
conflict match
259286
case Some(conflict) =>
260287
report.error(s"@varargs produces a forwarder method that conflicts with ${conflict.showDcl}", original.sourcePos)
261-
ddef
262288
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)
289+
decls.enter(forwarder.enteredAfter(thisPhase))
274290

275291
/** Convert type from Scala to Java varargs method */
276292
private def toJavaVarArgs(tp: Type)(using Context): Type = tp match

0 commit comments

Comments
 (0)