diff options
author | Aleksandar Pokopec <aleksandar.prokopec@epfl.ch> | 2010-11-24 20:18:08 +0000 |
---|---|---|
committer | Aleksandar Pokopec <aleksandar.prokopec@epfl.ch> | 2010-11-24 20:18:08 +0000 |
commit | 597acf7b0c26ca94a34f188fc2919b8482c3ab0a (patch) | |
tree | 19925504774f19edec80a3d700d0bb16f7c10507 | |
parent | 46d1d8e55aa40037a536767f0ae8d98665c73617 (diff) | |
download | scala-597acf7b0c26ca94a34f188fc2919b8482c3ab0a.tar.gz scala-597acf7b0c26ca94a34f188fc2919b8482c3ab0a.tar.bz2 scala-597acf7b0c26ca94a34f188fc2919b8482c3ab0a.zip |
Fix for #3621.
Added varargs annotation.
Review by Rytz.
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Definitions.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Flags.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/StdNames.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 120 | ||||
-rw-r--r-- | src/library/scala/annotation/varargs.scala | 18 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/Flags.scala | 2 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/Symbols.scala | 1 | ||||
-rw-r--r-- | test/files/jvm/varargs.check | 2 | ||||
-rw-r--r-- | test/files/jvm/varargs/JavaClass.java | 14 | ||||
-rw-r--r-- | test/files/jvm/varargs/VaClass.scala | 12 | ||||
-rw-r--r-- | test/files/jvm/varargs/varargs.scala | 21 | ||||
-rw-r--r-- | test/files/neg/varargs.check | 7 | ||||
-rw-r--r-- | test/files/neg/varargs.scala | 20 |
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]) { + + } + +} |