summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala3
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala3
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Flags.scala6
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala120
-rw-r--r--src/library/scala/annotation/varargs.scala18
-rwxr-xr-xsrc/library/scala/reflect/generic/Flags.scala2
-rwxr-xr-xsrc/library/scala/reflect/generic/Symbols.scala1
-rw-r--r--test/files/jvm/varargs.check2
-rw-r--r--test/files/jvm/varargs/JavaClass.java14
-rw-r--r--test/files/jvm/varargs/VaClass.scala12
-rw-r--r--test/files/jvm/varargs/varargs.scala21
-rw-r--r--test/files/neg/varargs.check7
-rw-r--r--test/files/neg/varargs.scala20
15 files changed, 223 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 0deb6c6d47..ad73d45dd0 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -344,7 +344,7 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
case class Parens(args: List[Tree]) extends Tree // only used during parsing
- /** emitted by typer, eliminated by refchecks **/
+ /** emitted by typer, eliminated by refchecks */
case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends AbsTypeTree
// ----- subconstructors --------------------------------------------
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 384dfacbc8..4046195ce2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -1881,7 +1881,8 @@ abstract class GenJVM extends SubComponent {
if (sym.isFinal && !sym.enclClass.isInterface && !sym.isClassConstructor) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
if (sym.isBridge) ACC_BRIDGE else 0,
- if (sym.isClass && !sym.isInterface) ACC_SUPER else 0
+ if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
+ if (sym.isVarargsMethod) ACC_VARARGS else 0
)
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 167a067485..47cd9bce86 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -129,6 +129,7 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val SwitchClass = getClass("scala.annotation.switch")
lazy val ElidableMethodClass = getClass("scala.annotation.elidable")
lazy val ImplicitNotFoundClass = getClass("scala.annotation.implicitNotFound")
+ lazy val VarargsClass = getClass("scala.annotation.varargs")
lazy val FieldTargetClass = getClass("scala.annotation.target.field")
lazy val GetterTargetClass = getClass("scala.annotation.target.getter")
lazy val SetterTargetClass = getClass("scala.annotation.target.setter")
@@ -380,6 +381,8 @@ trait Definitions extends reflect.generic.StandardDefinitions {
false
}
+ def isSeqType(tp: Type) = cond(tp.normalize) { case TypeRef(_, SeqClass, List(tparam)) => true }
+
def seqType(arg: Type) = typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(arg))
def arrayType(arg: Type) = typeRef(ArrayClass.typeConstructor.prefix, ArrayClass, List(arg))
diff --git a/src/compiler/scala/tools/nsc/symtab/Flags.scala b/src/compiler/scala/tools/nsc/symtab/Flags.scala
index be26b0fb15..f3b31d4bcd 100644
--- a/src/compiler/scala/tools/nsc/symtab/Flags.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Flags.scala
@@ -55,7 +55,7 @@ package symtab
// 40: SPECIALIZED
// 41: DEFAULTINIT/M
// 42: VBRIDGE
-// 43:
+// 43: VARARGS
// 44:
// 45:
// 46:
@@ -192,7 +192,7 @@ class Flags extends reflect.generic.Flags {
case SPECIALIZED => "<specialized>" // (1L << 40)
case DEFAULTINIT => "<defaultinit>" // (1L << 41)
case VBRIDGE => "<vbridge>" // (1L << 42)
- case 0x80000000000L => "" // (1L << 43)
+ case VARARGS => "<varargs>" // (1L << 43)
case 0x100000000000L => "" // (1L << 44)
case 0x200000000000L => "" // (1L << 45)
case 0x400000000000L => "" // (1L << 46)
@@ -217,4 +217,4 @@ class Flags extends reflect.generic.Flags {
}
}
-object Flags extends Flags { } \ No newline at end of file
+object Flags extends Flags { }
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 1e348f5d68..11773dae4c 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -291,6 +291,7 @@ trait StdNames extends reflect.generic.StdNames with NameManglers {
val wait_ = newTermName("wait")
val withFilter = newTermName("withFilter")
val zip = newTermName("zip")
+ val genericArrayOps = newTermName("genericArrayOps")
val ZAND = encode("&&")
val ZOR = encode("||")
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index eaaff2d2de..d1a4672805 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -34,9 +34,10 @@ import scala.collection.{ mutable, immutable }
* - convert non-local returns to throws with enclosing try statements.
*/
/*</export> */
-abstract class UnCurry extends InfoTransform with TypingTransformers {
+abstract class UnCurry extends InfoTransform with TypingTransformers with ast.TreeDSL {
import global._ // the global environment
import definitions._ // standard classes and methods
+ import CODE._
val phaseName: String = "uncurry"
@@ -154,6 +155,8 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
private var inConstructorFlag = 0L
private val byNameArgs = new mutable.HashSet[Tree]
private val noApply = new mutable.HashSet[Tree]
+ private val newMembers = mutable.ArrayBuffer[Tree]()
+ private val repeatedParams = mutable.Map[Symbol, List[ValDef]]()
override def transformUnit(unit: CompilationUnit) {
freeMutableVars.clear
@@ -521,7 +524,8 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
if (isElidable(tree)) elideIntoUnit(tree)
else tree match {
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd)
withNeedLift(false) {
if (tree.symbol.isClassConstructor) {
atOwner(tree.symbol) {
@@ -645,12 +649,30 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
}
tree match {
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ /* Some uncurry post transformations add members to templates.
+ * When inside a template, the following sequence is available:
+ * - newMembers
+ * Any entry in this sequence will be added into the template
+ * once the template transformation has finished.
+ *
+ * In particular, this case will add:
+ * - synthetic Java varargs forwarders for repeated parameters
+ */
+ case Template(parents, self, body) =>
+ localTyper = typer.atOwner(tree, currentClass)
+ val tmpl = if (!forMSIL || forMSIL) {
+ treeCopy.Template(tree, parents, self, transformTrees(newMembers.toList) ::: body)
+ } else super.transform(tree).asInstanceOf[Template]
+ newMembers.clear
+ tmpl
+ case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
val rhs1 = nonLocalReturnKeys.get(tree.symbol) match {
case None => rhs
case Some(k) => atPos(rhs.pos)(nonLocalReturnTry(rhs, k, tree.symbol))
}
- treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.flatten), tpt, rhs1)
+ val flatdd = treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.flatten), tpt, rhs1)
+ if (dd.symbol hasAnnotation VarargsClass) addJavaVarargsForwarders(dd, flatdd, tree)
+ flatdd
case Try(body, catches, finalizer) =>
if (catches forall treeInfo.isCatchCase) tree
else {
@@ -693,6 +715,95 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
if (tree.isType) TypeTree(tree.tpe) setPos tree.pos else tree
}
}
+
+ /* Analyzes repeated params if method is annotated as `varargs`.
+ * If the repeated params exist, it saves them into the `repeatedParams` map,
+ * which is used later.
+ */
+ private def saveRepeatedParams(dd: DefDef) {
+ val allparams = dd.vparamss.flatten
+ val reps = allparams.filter(p => isRepeatedParamType(p.symbol.tpe))
+ if (reps.isEmpty)
+ unit.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
+ else repeatedParams.put(dd.symbol, reps)
+ }
+
+ /* Called during post transform, after the method argument lists have been flattened.
+ * It looks for the method in the `repeatedParams` map, and generates a Java-style
+ * varargs forwarder. It then adds the forwarder to the `newMembers` sequence.
+ */
+ private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef, tree: Tree) = if (repeatedParams.contains(dd.symbol)) {
+ def toArrayType(tp: Type): Type = tp match {
+ case TypeRef(_, SeqClass, List(tparg)) => arrayType(tparg)
+ }
+ def toSeqType(tp: Type): Type = tp match {
+ case TypeRef(_, ArrayClass, List(tparg)) => seqType(tparg)
+ }
+ def arrayElemType(tp: Type): Type = tp match {
+ case TypeRef(_, ArrayClass, List(tparg)) => tparg
+ }
+
+ val reps = repeatedParams(dd.symbol)
+ val rpsymbols = reps.map(_.symbol).toSet
+ val theTyper = typer.atOwner(tree, currentClass)
+ val flatparams = flatdd.vparamss(0)
+
+ // create the type
+ val forwformals = for (p <- flatparams) yield
+ if (rpsymbols contains p.symbol) toArrayType(p.symbol.tpe)
+ else p.symbol.tpe
+ val forwresult = dd.symbol.tpe match {
+ case MethodType(_, resultType) => resultType
+ case PolyType(_, MethodType(_, resultType)) => resultType
+ }
+ val forwformsyms = (forwformals zip flatparams) map {
+ case (tp, oldparam) => currentClass.newValueParameter(oldparam.symbol.pos, oldparam.name).setInfo(tp)
+ }
+ val forwtype = dd.symbol.tpe match {
+ case MethodType(_, _) => MethodType(forwformsyms, forwresult)
+ case PolyType(tparams, _) => PolyType(tparams, MethodType(forwformsyms, forwresult))
+ }
+
+ // create the symbol
+ val forwsym = currentClass.newMethod(dd.pos, dd.name).setFlag(VARARGS | SYNTHETIC | flatdd.symbol.flags).setInfo(forwtype)
+
+ // create the tree
+ val forwtree = theTyper.typed {
+ val locals: List[ValDef] = for ((argsym, fp) <- (forwsym ARGS) zip flatparams) yield
+ if (rpsymbols contains fp.symbol)
+ VAL(forwsym.newValue(unit.fresh.newName("param$")).setInfo(fp.symbol.tpe)) === {
+ gen.mkWrapArray(Ident(argsym), arrayElemType(argsym.tpe))
+ }
+ else null
+ val emitted = for (l <- locals if l != null) yield l
+ val seqargs = for ((l, argsym) <- locals zip (forwsym ARGS)) yield
+ if (l == null) Ident(argsym)
+ else Ident(l.symbol)
+
+ atPos(dd.pos) {
+ val t = DEF(forwsym) === BLOCK {
+ (emitted ::: List(
+ Apply(REF(flatdd.symbol), seqargs)
+ )): _*
+ }
+ t
+ }
+ }
+
+ // check if the method with that name and those arguments already exists in the template
+ currentClass.info.decls.lookupAll(forwsym.name).find(s => s != forwsym && s.tpe.matches(forwsym.tpe)) match {
+ case Some(s) => unit.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
+
+ // add the method to `newMembers`
+ newMembers += forwtree
+ }
+ }
+
}
/** Set of mutable local variables that are free in some inner method. */
@@ -748,4 +859,5 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
super.traverse(tree)
}
}
+
}
diff --git a/src/library/scala/annotation/varargs.scala b/src/library/scala/annotation/varargs.scala
new file mode 100644
index 0000000000..ad6a3f9bd4
--- /dev/null
+++ b/src/library/scala/annotation/varargs.scala
@@ -0,0 +1,18 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+package scala.annotation
+
+/** <p>
+ * A method annotation which instructs the compiler to generate a
+ * Java varargs-style forwarder method for interop. This annotation can
+ * only be applied to methods with repeated parameters.
+ * </p>
+ *
+ * @since 2.9
+ */
+final class varargs extends StaticAnnotation
diff --git a/src/library/scala/reflect/generic/Flags.scala b/src/library/scala/reflect/generic/Flags.scala
index 1988e9df90..405069f8b6 100755
--- a/src/library/scala/reflect/generic/Flags.scala
+++ b/src/library/scala/reflect/generic/Flags.scala
@@ -78,6 +78,8 @@ class Flags extends ModifierFlags {
final val SPECIALIZED = 0x10000000000L// symbol is a generated specialized member
final val VBRIDGE = 0x40000000000L// symbol is a varargs bridge
+ final val VARARGS = 0x80000000000L// symbol is a Java-style varargs method
+
// pickling and unpickling of flags
// The flags from 0x001 to 0x800 are different in the raw flags
diff --git a/src/library/scala/reflect/generic/Symbols.scala b/src/library/scala/reflect/generic/Symbols.scala
index 0362e497e5..4dcf80efe3 100755
--- a/src/library/scala/reflect/generic/Symbols.scala
+++ b/src/library/scala/reflect/generic/Symbols.scala
@@ -138,6 +138,7 @@ trait Symbols { self: Universe =>
final def isImplClass = isClass && hasFlag(IMPLCLASS) // Is this symbol an implementation class for a mixin?
final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol
final def isMethod = isTerm && hasFlag(METHOD)
+ final def isVarargsMethod = isMethod && hasFlag(VARARGS)
final def isModule = isTerm && hasFlag(MODULE)
final def isModuleClass = isClass && hasFlag(MODULE)
final def isOverloaded = hasFlag(OVERLOADED)
diff --git a/test/files/jvm/varargs.check b/test/files/jvm/varargs.check
new file mode 100644
index 0000000000..fef7448acb
--- /dev/null
+++ b/test/files/jvm/varargs.check
@@ -0,0 +1,2 @@
+7
+10 \ No newline at end of file
diff --git a/test/files/jvm/varargs/JavaClass.java b/test/files/jvm/varargs/JavaClass.java
new file mode 100644
index 0000000000..b5158f2878
--- /dev/null
+++ b/test/files/jvm/varargs/JavaClass.java
@@ -0,0 +1,14 @@
+
+
+
+public class JavaClass {
+ public static <T> void varargz(int i, T... v) {
+ }
+
+ public static void callSomeAnnotations() {
+ VaClass va = new VaClass();
+ va.vs(4, "", "", "");
+ va.vi(1, 2, 3, 4);
+ varargz(5, 1.0, 2.0, 3.0);
+ }
+} \ No newline at end of file
diff --git a/test/files/jvm/varargs/VaClass.scala b/test/files/jvm/varargs/VaClass.scala
new file mode 100644
index 0000000000..ffa25027d3
--- /dev/null
+++ b/test/files/jvm/varargs/VaClass.scala
@@ -0,0 +1,12 @@
+
+
+import annotation.varargs
+
+
+
+class VaClass {
+
+ @varargs def vs(a: Int, b: String*) = println(a + b.length)
+ @varargs def vi(a: Int, b: Int*) = println(a + b.sum)
+
+}
diff --git a/test/files/jvm/varargs/varargs.scala b/test/files/jvm/varargs/varargs.scala
new file mode 100644
index 0000000000..6d2e707bdf
--- /dev/null
+++ b/test/files/jvm/varargs/varargs.scala
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+object Test {
+ def main(args: Array[String]) {
+ JavaClass.callSomeAnnotations
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/test/files/neg/varargs.check b/test/files/neg/varargs.check
new file mode 100644
index 0000000000..f3254708b8
--- /dev/null
+++ b/test/files/neg/varargs.check
@@ -0,0 +1,7 @@
+varargs.scala:11: error: A method without repeated parameters cannot be annotated with the `varargs` annotation.
+ @varargs def nov(a: Int) = 0
+ ^
+varargs.scala:13: error: A method with a varargs annotation produces a forwarder method with the same signature (a: Int,b: Array[java.lang.String])Int as an existing method.
+ @varargs def v2(a: Int, b: String*) = 0
+ ^
+two errors found \ No newline at end of file
diff --git a/test/files/neg/varargs.scala b/test/files/neg/varargs.scala
new file mode 100644
index 0000000000..a2f895a87d
--- /dev/null
+++ b/test/files/neg/varargs.scala
@@ -0,0 +1,20 @@
+
+
+
+import annotation.varargs
+
+
+
+// Failing varargs annotation
+object Test {
+
+ @varargs def nov(a: Int) = 0
+ @varargs def v(a: Int, b: String*) = a + b.length
+ @varargs def v2(a: Int, b: String*) = 0
+ def v2(a: Int, b: Array[String]) = 0
+
+ def main(args: Array[String]) {
+
+ }
+
+}