summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2016-07-05 14:22:00 +0200
committerAdriaan Moors <adriaan@lightbend.com>2017-01-09 15:34:26 -0800
commitde361dfe4fab67a01750bccf960788ef10321e4d (patch)
tree7a53bd46e157055c4f99fca884643d1b41f9a48a /src
parentf841dab6c2eb8d9002c286d57ea80df637a0d4bc (diff)
downloadscala-de361dfe4fab67a01750bccf960788ef10321e4d.tar.gz
scala-de361dfe4fab67a01750bccf960788ef10321e4d.tar.bz2
scala-de361dfe4fab67a01750bccf960788ef10321e4d.zip
SI-8786 fix generic signature for @varargs forwarder methods
When generating a varargs forwarder for def foo[T](a: T*) the parameter type of the forwarder needs to be Array[Object]. If we generate Array[T] in UnCurry, that would be erased to plain Object, and the method would not be a valid varargs. Unfortunately, setting the parameter type to Array[Object] lead to an invalid generic signature - the generic signature should reflect the real signature. This change adds an attachment to the parameter symbol in the varargs forwarder method and special-cases signature generation. Also cleans up the code to produce the varargs forwarder. For example, type parameter and parameter symbols in the forwarder's method type were not clones, but the same symbols from the original method were re-used. Backported from 0d2760dce189cdcb363e54868381175af4b2646f, with a small tweak (checkVarargs) to make the test work on Java 6, as well as later versions.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala14
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala118
2 files changed, 80 insertions, 52 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index a04625c9c5..a7cf6d9e8d 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -336,7 +336,18 @@ abstract class Erasure extends AddInterfaces
case MethodType(params, restpe) =>
val buf = new StringBuffer("(")
- params foreach (p => buf append jsig(p.tpe))
+ params foreach (p => {
+ val tp = p.attachments.get[TypeParamVarargsAttachment] match {
+ case Some(att) =>
+ // For @varargs forwarders, a T* parameter has type Array[Object] in the forwarder
+ // instead of Array[T], as the latter would erase to Object (instead of Array[Object]).
+ // To make the generic signature correct ("[T", not "[Object"), an attachment on the
+ // parameter symbol stores the type T that was replaced by Object.
+ buf.append("["); att.typeParamRef
+ case _ => p.tpe
+ }
+ buf append jsig(tp)
+ })
buf append ")"
buf append (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe))
buf.toString
@@ -1178,4 +1189,5 @@ abstract class Erasure extends AddInterfaces
}
private class TypeRefAttachment(val tpe: TypeRef)
+ class TypeParamVarargsAttachment(val typeParamRef: Type)
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 870c35338c..17f4471533 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -763,72 +763,88 @@ abstract class UnCurry extends InfoTransform
if (!dd.symbol.hasAnnotation(VarargsClass) || !enteringUncurry(mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))))
return flatdd
- def toArrayType(tp: Type): Type = {
- val arg = elementType(SeqClass, tp)
- // to prevent generation of an `Object` parameter from `Array[T]` parameter later
- // as this would crash the Java compiler which expects an `Object[]` array for varargs
- // e.g. def foo[T](a: Int, b: T*)
- // becomes def foo[T](a: Int, b: Array[Object])
- // instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
- arrayType(
- if (arg.typeSymbol.isTypeParameterOrSkolem) ObjectTpe
- else arg
- )
- }
+ val forwSym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags)
- val theTyper = typer.atOwner(dd, currentClass)
- val flatparams = flatdd.symbol.paramss.head
val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)))
- // create the type
- val forwformals = map2(flatparams, isRepeated) {
- case (p, true) => toArrayType(p.tpe)
- case (p, false)=> p.tpe
- }
- val forwresult = dd.symbol.tpe_*.finalResultType
- val forwformsyms = map2(forwformals, flatparams)((tp, oldparam) =>
- currentClass.newValueParameter(oldparam.name.toTermName, oldparam.pos).setInfo(tp)
- )
- def mono = MethodType(forwformsyms, forwresult)
- val forwtype = dd.symbol.tpe match {
- case MethodType(_, _) => mono
- case PolyType(tps, _) => PolyType(tps, mono)
- }
+ val oldPs = flatdd.symbol.paramss.head
+
+ // see comment in method toArrayType below
+ val arrayTypesMappedToObject = mutable.Map.empty[Symbol, Type]
- // create the symbol
- val forwsym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype
- def forwParams = forwsym.info.paramss.flatten
-
- // create the tree
- val forwtree = theTyper.typedPos(dd.pos) {
- val locals = map3(forwParams, flatparams, isRepeated) {
- case (_, fp, false) => null
- case (argsym, fp, true) =>
- Block(Nil,
- gen.mkCast(
- gen.mkWrapArray(Ident(argsym), elementType(ArrayClass, argsym.tpe)),
- seqType(elementType(SeqClass, fp.tpe))
- )
- )
+ val forwTpe = {
+ val (oldTps, tps) = dd.symbol.tpe match {
+ case PolyType(oldTps, _) =>
+ val newTps = oldTps.map(_.cloneSymbol(forwSym))
+ (oldTps, newTps)
+
+ case _ => (Nil, Nil)
}
- val seqargs = map2(locals, forwParams) {
- case (null, argsym) => Ident(argsym)
- case (l, _) => l
+
+ def toArrayType(tp: Type, newParam: Symbol): Type = {
+ val arg = elementType(SeqClass, tp)
+ val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
+ // To prevent generation of an `Object` parameter from `Array[T]` parameter later
+ // as this would crash the Java compiler which expects an `Object[]` array for varargs
+ // e.g. def foo[T](a: Int, b: T*)
+ // becomes def foo[T](a: Int, b: Array[Object])
+ // instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
+ //
+ // In order for the forwarder method to type check we need to insert a cast:
+ // def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']])
+ // The target element type for that cast (T') is stored in the `arrayTypesMappedToObject` map.
+ val originalArg = arg.substSym(oldTps, tps)
+ arrayTypesMappedToObject(newParam) = originalArg
+ // Store the type parameter that was replaced by Object to emit the correct generic signature
+ newParam.updateAttachment(new erasure.TypeParamVarargsAttachment(originalArg))
+ ObjectTpe
+ } else
+ arg
+ arrayType(elem)
}
- val end = if (forwsym.isConstructor) List(UNIT) else Nil
- DefDef(forwsym, BLOCK(Apply(gen.mkAttributedRef(flatdd.symbol), seqargs) :: end : _*))
+ val ps = map2(oldPs, isRepeated)((oldParam, isRep) => {
+ val newParam = oldParam.cloneSymbol(forwSym)
+ val tp = if (isRep) toArrayType(oldParam.tpe, newParam) else oldParam.tpe
+ newParam.setInfo(tp)
+ })
+
+ val resTp = dd.symbol.tpe_*.finalResultType.substSym(oldPs, ps)
+ val mt = MethodType(ps, resTp)
+ val r = if (tps.isEmpty) mt else PolyType(tps, mt)
+ r.substSym(oldTps, tps)
+ }
+
+ forwSym.setInfo(forwTpe)
+ val newPs = forwTpe.params
+
+ val theTyper = typer.atOwner(dd, currentClass)
+ val forwTree = theTyper.typedPos(dd.pos) {
+ val seqArgs = map3(newPs, oldPs, isRepeated)((param, oldParam, isRep) => {
+ if (!isRep) Ident(param)
+ else {
+ val parTp = elementType(ArrayClass, param.tpe)
+ val wrap = gen.mkWrapArray(Ident(param), parTp)
+ arrayTypesMappedToObject.get(param) match {
+ case Some(tp) => gen.mkCast(wrap, seqType(tp))
+ case _ => wrap
+ }
+ }
+ })
+
+ val forwCall = Apply(gen.mkAttributedRef(flatdd.symbol), seqArgs)
+ DefDef(forwSym, if (forwSym.isConstructor) Block(List(forwCall), UNIT) else forwCall)
}
// check if the method with that name and those arguments already exists in the template
- currentClass.info.member(forwsym.name).alternatives.find(s => s != forwsym && s.tpe.matches(forwsym.tpe)) match {
+ currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe)) match {
case Some(s) => reporter.error(dd.symbol.pos,
"A method with a varargs annotation produces a forwarder method with the same signature "
+ s.tpe + " as an existing method.")
case None =>
// enter symbol into scope
- currentClass.info.decls enter forwsym
- addNewMember(forwtree)
+ currentClass.info.decls enter forwSym
+ addNewMember(forwTree)
}
flatdd