Skip to content

Commit a5d38ea

Browse files
authored
Merge pull request scala#5630 from adriaanm/rebase-5557
[backport] SI-10071 SI-8786 varargs methods
2 parents 3696732 + 359b0bc commit a5d38ea

File tree

21 files changed

+343
-132
lines changed

21 files changed

+343
-132
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ codebase and re-compiles too many files, resulting in long build times (check
134134
[sbt#1104](https://github.com/sbt/sbt/issues/1104) for progress on that front). In the
135135
meantime you can:
136136
- Enable "ant mode" in which sbt only re-compiles source files that were modified.
137-
Create a file `local.sbt` containing the line `(incOptions in ThisBuild) := (incOptions in ThisBuild).value.withNameHashing(false).withAntStyle(true)`.
137+
Create a file `local.sbt` containing the line `antStyle := true`.
138138
Add an entry `local.sbt` to your `~/.gitignore`.
139139
- Use IntelliJ IDEA for incremental compiles (see [IDE Setup](#ide-setup) below) - its
140140
incremental compiler is a bit less conservative, but usually correct.

project/PartestUtil.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object PartestUtil {
2929
val knownUnaryOptions = List(
3030
"--pos", "--neg", "--run", "--jvm", "--res", "--ant", "--scalap", "--specialized",
3131
"--scalacheck", "--instrumented", "--presentation", "--failed", "--update-check",
32-
"--show-diff", "--verbose", "--terse", "--debug", "--version", "--self-test", "--help")
32+
"--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--version", "--self-test", "--help")
3333
val srcPathOption = "--srcpath"
3434
val grepOption = "--grep"
3535

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,18 @@ abstract class Erasure extends AddInterfaces
336336

337337
case MethodType(params, restpe) =>
338338
val buf = new StringBuffer("(")
339-
params foreach (p => buf append jsig(p.tpe))
339+
params foreach (p => {
340+
val tp = p.attachments.get[TypeParamVarargsAttachment] match {
341+
case Some(att) =>
342+
// For @varargs forwarders, a T* parameter has type Array[Object] in the forwarder
343+
// instead of Array[T], as the latter would erase to Object (instead of Array[Object]).
344+
// To make the generic signature correct ("[T", not "[Object"), an attachment on the
345+
// parameter symbol stores the type T that was replaced by Object.
346+
buf.append("["); att.typeParamRef
347+
case _ => p.tpe
348+
}
349+
buf append jsig(tp)
350+
})
340351
buf append ")"
341352
buf append (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe))
342353
buf.toString

src/compiler/scala/tools/nsc/transform/UnCurry.scala

Lines changed: 50 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import scala.reflect.internal.util.ListOfNil
2626
* - for every repeated Scala parameter `x: T*' --> x: Seq[T].
2727
* - for every repeated Java parameter `x: T...' --> x: Array[T], except:
2828
* if T is an unbounded abstract type, replace --> x: Array[Object]
29+
* - for every method defining repeated parameters annotated with @varargs, generate
30+
* a synthetic Java-style vararg method
2931
* - for every argument list that corresponds to a repeated Scala parameter
3032
* (a_1, ..., a_n) => (Seq(a_1, ..., a_n))
3133
* - for every argument list that corresponds to a repeated Java parameter
@@ -43,6 +45,8 @@ import scala.reflect.internal.util.ListOfNil
4345
* def liftedTry$1 = try { x_i } catch { .. }
4446
* meth(x_1, .., liftedTry$1(), .. )
4547
* }
48+
* - remove calls to elidable methods and replace their bodies with NOPs when elide-below
49+
* requires it
4650
*/
4751
/*</export> */
4852
abstract class UnCurry extends InfoTransform
@@ -593,7 +597,13 @@ abstract class UnCurry extends InfoTransform
593597
case None => newRhs
594598
}
595599
)
596-
addJavaVarargsForwarders(dd, flatdd)
600+
// Only class members can reasonably be called from Java due to name mangling.
601+
// Additionally, the Uncurry info transformer only adds a forwarder symbol to class members,
602+
// since the other symbols are not part of the ClassInfoType (see reflect.internal.transform.UnCurry)
603+
if (dd.symbol.owner.isClass)
604+
addJavaVarargsForwarders(dd, flatdd)
605+
else
606+
flatdd
597607

598608
case tree: Try =>
599609
if (tree.catches exists (cd => !treeInfo.isCatchCase(cd)))
@@ -684,7 +694,7 @@ abstract class UnCurry extends InfoTransform
684694
// declared type and assign this to a synthetic val. Later, we'll patch
685695
// the method body to refer to this, rather than the parameter.
686696
val tempVal: ValDef = {
687-
// SI-9442: using the "uncurry-erased" type (the one after the uncurry phase) can lead to incorrect
697+
// SI-9442: using the "uncurry-erased" type (the one after the uncurry phase) can lead to incorrect
688698
// tree transformations. For example, compiling:
689699
// ```
690700
// def foo(c: Ctx)(l: c.Tree): Unit = {
@@ -713,7 +723,7 @@ abstract class UnCurry extends InfoTransform
713723
// to redo this desugaring manually here
714724
// 2. the type needs to be normalized, since `gen.mkCast` checks this (no HK here, just aliases have
715725
// to be expanded before handing the type to `gen.mkAttributedCast`, which calls `gen.mkCast`)
716-
val info0 =
726+
val info0 =
717727
enteringUncurry(p.symbol.info) match {
718728
case DesugaredParameterType(desugaredTpe) =>
719729
desugaredTpe
@@ -755,80 +765,59 @@ abstract class UnCurry extends InfoTransform
755765
if (!hasRepeated) reporter.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
756766
}
757767

758-
/* Called during post transform, after the method argument lists have been flattened.
759-
* It looks for the method in the `repeatedParams` map, and generates a Java-style
768+
/**
769+
* Called during post transform, after the method argument lists have been flattened.
770+
* It looks for the forwarder symbol in the symbol attachments and generates a Java-style
760771
* varargs forwarder.
772+
*
773+
* @note The Java-style varargs method symbol is generated in the Uncurry info transformer. If the
774+
* symbol can't be found this method reports a warning and carries on.
775+
* @see [[scala.reflect.internal.transform.UnCurry]]
761776
*/
762777
private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = {
763778
if (!dd.symbol.hasAnnotation(VarargsClass) || !enteringUncurry(mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))))
764779
return flatdd
765780

766-
def toArrayType(tp: Type): Type = {
767-
val arg = elementType(SeqClass, tp)
768-
// to prevent generation of an `Object` parameter from `Array[T]` parameter later
769-
// as this would crash the Java compiler which expects an `Object[]` array for varargs
770-
// e.g. def foo[T](a: Int, b: T*)
771-
// becomes def foo[T](a: Int, b: Array[Object])
772-
// instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
773-
arrayType(
774-
if (arg.typeSymbol.isTypeParameterOrSkolem) ObjectTpe
775-
else arg
776-
)
781+
val forwSym: Symbol = {
782+
currentClass.info // make sure the info is up to date, so the varargs forwarder symbol has been generated
783+
flatdd.symbol.attachments.get[VarargsSymbolAttachment] match {
784+
case Some(VarargsSymbolAttachment(sym)) => sym
785+
case None =>
786+
reporter.warning(dd.pos, s"Could not generate Java varargs forwarder for ${flatdd.symbol}. Please file a bug.")
787+
return flatdd
788+
}
777789
}
778790

779-
val theTyper = typer.atOwner(dd, currentClass)
780-
val flatparams = flatdd.symbol.paramss.head
791+
val newPs = forwSym.tpe.params
781792
val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)))
793+
val oldPs = flatdd.symbol.paramss.head
782794

783-
// create the type
784-
val forwformals = map2(flatparams, isRepeated) {
785-
case (p, true) => toArrayType(p.tpe)
786-
case (p, false)=> p.tpe
787-
}
788-
val forwresult = dd.symbol.tpe_*.finalResultType
789-
val forwformsyms = map2(forwformals, flatparams)((tp, oldparam) =>
790-
currentClass.newValueParameter(oldparam.name.toTermName, oldparam.pos).setInfo(tp)
791-
)
792-
def mono = MethodType(forwformsyms, forwresult)
793-
val forwtype = dd.symbol.tpe match {
794-
case MethodType(_, _) => mono
795-
case PolyType(tps, _) => PolyType(tps, mono)
796-
}
797-
798-
// create the symbol
799-
val forwsym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype
800-
def forwParams = forwsym.info.paramss.flatten
801-
802-
// create the tree
803-
val forwtree = theTyper.typedPos(dd.pos) {
804-
val locals = map3(forwParams, flatparams, isRepeated) {
805-
case (_, fp, false) => null
806-
case (argsym, fp, true) =>
807-
Block(Nil,
808-
gen.mkCast(
809-
gen.mkWrapArray(Ident(argsym), elementType(ArrayClass, argsym.tpe)),
810-
seqType(elementType(SeqClass, fp.tpe))
811-
)
812-
)
813-
}
814-
val seqargs = map2(locals, forwParams) {
815-
case (null, argsym) => Ident(argsym)
816-
case (l, _) => l
817-
}
818-
val end = if (forwsym.isConstructor) List(UNIT) else Nil
795+
val theTyper = typer.atOwner(dd, currentClass)
796+
val forwTree = theTyper.typedPos(dd.pos) {
797+
val seqArgs = map3(newPs, oldPs, isRepeated)((param, oldParam, isRep) => {
798+
if (!isRep) Ident(param)
799+
else {
800+
val parTp = elementType(ArrayClass, param.tpe)
801+
val wrap = gen.mkWrapArray(Ident(param), parTp)
802+
param.attachments.get[TypeParamVarargsAttachment] match {
803+
case Some(TypeParamVarargsAttachment(tp)) => gen.mkCast(wrap, seqType(tp))
804+
case _ => wrap
805+
}
806+
}
807+
})
819808

820-
DefDef(forwsym, BLOCK(Apply(gen.mkAttributedRef(flatdd.symbol), seqargs) :: end : _*))
809+
val forwCall = Apply(gen.mkAttributedRef(flatdd.symbol), seqArgs)
810+
DefDef(forwSym, if (forwSym.isConstructor) Block(List(forwCall), UNIT) else forwCall)
821811
}
822812

823813
// check if the method with that name and those arguments already exists in the template
824-
currentClass.info.member(forwsym.name).alternatives.find(s => s != forwsym && s.tpe.matches(forwsym.tpe)) match {
825-
case Some(s) => reporter.error(dd.symbol.pos,
826-
"A method with a varargs annotation produces a forwarder method with the same signature "
827-
+ s.tpe + " as an existing method.")
814+
enteringUncurry(currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe))) match {
815+
case Some(s) =>
816+
reporter.error(dd.symbol.pos,
817+
s"A method with a varargs annotation produces a forwarder method with the same signature ${s.tpe} as an existing method.")
828818
case None =>
829819
// enter symbol into scope
830-
currentClass.info.decls enter forwsym
831-
addNewMember(forwtree)
820+
addNewMember(forwTree)
832821
}
833822

834823
flatdd

src/reflect/scala/reflect/internal/StdAttachments.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,7 @@ trait StdAttachments {
5858
* error to indicate that the earlier observation was incomplete.
5959
*/
6060
case object KnownDirectSubclassesCalled extends PlainAttachment
61+
62+
/** An attachment carrying information between uncurry and erasure */
63+
case class TypeParamVarargsAttachment(val typeParamRef: Type)
6164
}

src/reflect/scala/reflect/internal/transform/UnCurry.scala

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@ package internal
44
package transform
55

66
import Flags._
7+
import scala.collection.mutable
78

89
trait UnCurry {
910

1011
val global: SymbolTable
1112
import global._
1213
import definitions._
1314

15+
/**
16+
* The synthetic Java vararg method symbol corresponding to a Scala vararg method
17+
* annotated with @varargs.
18+
*/
19+
case class VarargsSymbolAttachment(varargMethod: Symbol)
20+
1421
/** Note: changing tp.normalize to tp.dealias in this method leads to a single
1522
* test failure: run/t5688.scala, where instead of the expected output
1623
* Vector(ta, tb, tab)
@@ -65,18 +72,74 @@ trait UnCurry {
6572
def apply(tp0: Type): Type = {
6673
val tp = expandAlias(tp0)
6774
tp match {
68-
case ClassInfoType(parents, decls, clazz) =>
75+
case ClassInfoType(parents, decls, clazz) if !clazz.isJavaDefined =>
6976
val parents1 = parents mapConserve uncurry
70-
if (parents1 eq parents) tp
71-
else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls??
77+
val varargOverloads = mutable.ListBuffer.empty[Symbol]
78+
79+
// Not using `hasAnnotation` here because of dreaded cyclic reference errors:
80+
// it may happen that VarargsClass has not been initialized yet and we get here
81+
// while processing one of its superclasses (such as java.lang.Object). Since we
82+
// don't need the more precise `matches` semantics, we only check the symbol, which
83+
// is anyway faster and safer
84+
for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) {
85+
if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) {
86+
varargOverloads += varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info))
87+
}
88+
}
89+
if ((parents1 eq parents) && varargOverloads.isEmpty) tp
90+
else {
91+
val newDecls = decls.cloneScope
92+
varargOverloads.foreach(newDecls.enter)
93+
ClassInfoType(parents1, newDecls, clazz)
94+
} // @MAT normalize in decls??
95+
7296
case PolyType(_, _) =>
7397
mapOver(tp)
98+
7499
case _ =>
75100
tp
76101
}
77102
}
78103
}
79104

105+
private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = {
106+
val forwSym = origSym.cloneSymbol(currentClass, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED, origSym.name.toTermName).withoutAnnotations
107+
108+
// we are using `origSym.info`, which contains the type *before* the transformation
109+
// so we still see repeated parameter types (uncurry replaces them with Seq)
110+
val isRepeated = origSym.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe))
111+
val oldPs = newInfo.paramss.head
112+
def toArrayType(tp: Type, newParam: Symbol): Type = {
113+
val arg = elementType(SeqClass, tp)
114+
val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
115+
// To prevent generation of an `Object` parameter from `Array[T]` parameter later
116+
// as this would crash the Java compiler which expects an `Object[]` array for varargs
117+
// e.g. def foo[T](a: Int, b: T*)
118+
// becomes def foo[T](a: Int, b: Array[Object])
119+
// instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
120+
//
121+
// In order for the forwarder method to type check we need to insert a cast:
122+
// def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']])
123+
// The target element type for that cast (T') is stored in the TypeParamVarargsAttachment
124+
// val originalArg = arg.substSym(oldTps, tps)
125+
// Store the type parameter that was replaced by Object to emit the correct generic signature
126+
newParam.updateAttachment(new TypeParamVarargsAttachment(arg))
127+
ObjectTpe
128+
} else
129+
arg
130+
arrayType(elem)
131+
}
132+
133+
foreach2(forwSym.paramss.flatten, isRepeated)((p, isRep) =>
134+
if (isRep) {
135+
p.setInfo(toArrayType(p.info, p))
136+
}
137+
)
138+
139+
origSym.updateAttachment(VarargsSymbolAttachment(forwSym))
140+
forwSym
141+
}
142+
80143
/** - return symbol's transformed type,
81144
* - if symbol is a def parameter with transformed type T, return () => T
82145
*

src/reflect/scala/reflect/runtime/JavaUniverseForce.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
4242
this.SyntheticUnitAttachment
4343
this.SubpatternsAttachment
4444
this.KnownDirectSubclassesCalled
45+
this.TypeParamVarargsAttachment
4546
this.noPrint
4647
this.typeDebug
4748
this.Range
@@ -444,6 +445,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
444445
definitions.ScalaValueClassesNoUnit
445446
definitions.ScalaValueClasses
446447

448+
uncurry.VarargsSymbolAttachment
447449
uncurry.DesugaredParameterType
448450
erasure.GenericArray
449451
erasure.scalaErasure

0 commit comments

Comments
 (0)