summaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2016-11-21 13:27:16 +0100
committerAdriaan Moors <adriaan@lightbend.com>2017-01-09 15:34:29 -0800
commit5b972dc10f0c263bcecd11c528715e8b94ca9efa (patch)
tree89fad430622a806646d62a200617b29bcfe2187a /src/reflect
parentde361dfe4fab67a01750bccf960788ef10321e4d (diff)
downloadscala-5b972dc10f0c263bcecd11c528715e8b94ca9efa.tar.gz
scala-5b972dc10f0c263bcecd11c528715e8b94ca9efa.tar.bz2
scala-5b972dc10f0c263bcecd11c528715e8b94ca9efa.zip
SI-10071 Separate compilation for varargs methods
Make sure that methods annotated with varargs are properly mixed-in. This commit splits the transformation into an info transformer (that works on all symbols, whether they come from source or binary) and a tree transformer. The gist of this is that the symbol-creation part of the code was moved to the UnCurry info transformer, while tree operations remained in the tree transformer. The newly created symbol is attached to the original method so that the tree transformer can still retrieve the symbol. A few fall outs: - I removed a local map that was identical to TypeParamsVarargsAttachment - moved the said attachment to StdAttachments so it’s visible between reflect.internal and nsc.transform - a couple more comments in UnCurry to honour the boy-scout rule
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/scala/reflect/internal/StdAttachments.scala3
-rw-r--r--src/reflect/scala/reflect/internal/transform/UnCurry.scala82
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala2
3 files changed, 85 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala
index 3df31b538c..cddb0c8f72 100644
--- a/src/reflect/scala/reflect/internal/StdAttachments.scala
+++ b/src/reflect/scala/reflect/internal/StdAttachments.scala
@@ -58,4 +58,7 @@ trait StdAttachments {
* error to indicate that the earlier observation was incomplete.
*/
case object KnownDirectSubclassesCalled extends PlainAttachment
+
+ /** An attachment carrying information between uncurry and erasure */
+ case class TypeParamVarargsAttachment(val typeParamRef: Type)
}
diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala
index 85e3ac60e8..e86003226a 100644
--- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala
+++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala
@@ -4,6 +4,7 @@ package internal
package transform
import Flags._
+import scala.collection.mutable
trait UnCurry {
@@ -11,6 +12,12 @@ trait UnCurry {
import global._
import definitions._
+ /**
+ * The synthetic Java vararg method symbol corresponding to a Scala vararg method
+ * annotated with @varargs.
+ */
+ case class VarargsSymbolAttachment(varargMethod: Symbol)
+
/** Note: changing tp.normalize to tp.dealias in this method leads to a single
* test failure: run/t5688.scala, where instead of the expected output
* Vector(ta, tb, tab)
@@ -67,8 +74,25 @@ trait UnCurry {
tp match {
case ClassInfoType(parents, decls, clazz) =>
val parents1 = parents mapConserve uncurry
- if (parents1 eq parents) tp
- else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls??
+ val varargOverloads = mutable.ListBuffer.empty[Symbol]
+
+ // Not using `hasAnnotation` here because of dreaded cyclic reference errors:
+ // it may happen that VarargsClass has not been initialized yet and we get here
+ // while processing one of its superclasses (such as java.lang.Object). Since we
+ // don't need the more precise `matches` semantics, we only check the symbol, which
+ // is anyway faster and safer
+ for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) {
+ if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) {
+ val forwarderSym = varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info))
+ varargOverloads += forwarderSym
+ }
+ }
+ if ((parents1 eq parents) && varargOverloads.isEmpty) tp
+ else {
+ val newDecls = decls.cloneScope
+ varargOverloads.foreach(newDecls.enter)
+ ClassInfoType(parents1, newDecls, clazz)
+ } // @MAT normalize in decls??
case PolyType(_, _) =>
mapOver(tp)
case _ =>
@@ -77,6 +101,60 @@ trait UnCurry {
}
}
+ private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = {
+ val forwSym = currentClass.newMethod(origSym.name.toTermName, origSym.pos, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED)
+
+ // we are using `origSym.info`, which contains the type *before* the transformation
+ // so we still see repeated parameter types (uncurry replaces them with Seq)
+ val isRepeated = origSym.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe))
+ val oldPs = newInfo.paramss.head
+
+ val forwTpe = {
+ val (oldTps, tps) = newInfo match {
+ case PolyType(oldTps, _) =>
+ val newTps = oldTps.map(_.cloneSymbol(forwSym))
+ (oldTps, newTps)
+
+ case _ => (Nil, Nil)
+ }
+
+ 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 TypeParamVarargsAttachment
+ val originalArg = arg.substSym(oldTps, tps)
+ // Store the type parameter that was replaced by Object to emit the correct generic signature
+ newParam.updateAttachment(new TypeParamVarargsAttachment(originalArg))
+ ObjectTpe
+ } else
+ arg
+ arrayType(elem)
+ }
+
+ 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 = newInfo.finalResultType.substSym(oldPs, ps)
+ val mt = MethodType(ps, resTp)
+ val r = if (tps.isEmpty) mt else PolyType(tps, mt)
+ r.substSym(oldTps, tps)
+ }
+
+ origSym.updateAttachment(VarargsSymbolAttachment(forwSym))
+ forwSym.setInfo(forwTpe)
+ }
+
/** - return symbol's transformed type,
* - if symbol is a def parameter with transformed type T, return () => T
*
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 8481cd8996..45dd550e3e 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -42,6 +42,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.SyntheticUnitAttachment
this.SubpatternsAttachment
this.KnownDirectSubclassesCalled
+ this.TypeParamVarargsAttachment
this.noPrint
this.typeDebug
this.Range
@@ -444,6 +445,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.ScalaValueClassesNoUnit
definitions.ScalaValueClasses
+ uncurry.VarargsSymbolAttachment
uncurry.DesugaredParameterType
erasure.GenericArray
erasure.scalaErasure