diff --git a/.gitmodules b/.gitmodules index 6cf9427b78b5..99cfc8fe442d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -67,7 +67,7 @@ url = https://github.com/dotty-staging/upickle.git [submodule "community-build/community-projects/sconfig"] path = community-build/community-projects/sconfig - url = https://github.com/ekrich/sconfig.git + url = https://github.com/dotty-staging/sconfig.git [submodule "community-build/community-projects/zio"] path = community-build/community-projects/zio url = https://github.com/dotty-staging/zio.git diff --git a/community-build/community-projects/sconfig b/community-build/community-projects/sconfig index a1579a935f79..38d620bdab66 160000 --- a/community-build/community-projects/sconfig +++ b/community-build/community-projects/sconfig @@ -1 +1 @@ -Subproject commit a1579a935f79d34c66a813f0cf71a674c092179b +Subproject commit 38d620bdab6656850ab0358583d2bb3638f83150 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 875916c62a39..49f281c6521f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -829,6 +829,7 @@ class Definitions { @tu lazy val FunctionalInterfaceAnnot: ClassSymbol = ctx.requiredClass("java.lang.FunctionalInterface") @tu lazy val InfixAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.infix") @tu lazy val AlphaAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.alpha") + @tu lazy val VarargsAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.varargs") // A list of annotations that are commonly used to indicate that a field/method argument or return // type is not null. These annotations are used by the nullification logic in JavaNullInterop to diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 069575176bea..c2effb6fc7b0 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -28,138 +28,221 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => override def phaseName: String = ElimRepeated.name - override def changesMembers: Boolean = true // the phase adds vararg bridges + override def changesMembers: Boolean = true // the phase adds vararg forwarders def transformInfo(tp: Type, sym: Symbol)(using Context): Type = elimRepeated(tp) override def transform(ref: SingleDenotation)(using Context): SingleDenotation = - super.transform(ref) match { + super.transform(ref) match case ref1: SymDenotation if (ref1 ne ref) && overridesJava(ref1.symbol) => // This method won't override the corresponding Java method at the end of this phase, - // only the bridge added by `addVarArgsBridge` will. + // only the forwarder added by `addVarArgsForwarder` will. ref1.copySymDenotation(initFlags = ref1.flags &~ Override) case ref1 => ref1 - } override def mayChange(sym: Symbol)(using Context): Boolean = sym.is(Method) private def overridesJava(sym: Symbol)(using Context) = sym.allOverriddenSymbols.exists(_.is(JavaDefined)) - private def elimRepeated(tp: Type)(using Context): Type = tp.stripTypeVar match { + private def hasVarargsAnnotation(sym: Symbol)(using Context) = sym.hasAnnotation(defn.VarargsAnnot) + + private def parentHasAnnotation(sym: Symbol)(using Context) = sym.allOverriddenSymbols.exists(hasVarargsAnnotation) + + /** Eliminate repeated parameters from method types. */ + private def elimRepeated(tp: Type)(using Context): Type = tp.stripTypeVar match case tp @ MethodTpe(paramNames, paramTypes, resultType) => val resultType1 = elimRepeated(resultType) val paramTypes1 = - if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { + if paramTypes.nonEmpty && paramTypes.last.isRepeatedParam then val last = paramTypes.last.translateFromRepeated(toArray = tp.isJavaMethod) paramTypes.init :+ last - } else paramTypes tp.derivedLambdaType(paramNames, paramTypes1, resultType1) case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, elimRepeated(tp.resultType)) case tp => tp - } def transformTypeOfTree(tree: Tree)(using Context): Tree = tree.withType(elimRepeated(tree.tpe)) + override def transformTypeApply(tree: TypeApply)(using Context): Tree = + transformTypeOfTree(tree) + override def transformIdent(tree: Ident)(using Context): Tree = transformTypeOfTree(tree) override def transformSelect(tree: Select)(using Context): Tree = transformTypeOfTree(tree) - override def transformApply(tree: Apply)(using Context): Tree = { + override def transformApply(tree: Apply)(using Context): Tree = val args = tree.args.mapConserve { case arg: Typed if isWildcardStarArg(arg) => val isJavaDefined = tree.fun.symbol.is(JavaDefined) val tpe = arg.expr.tpe - if (isJavaDefined && tpe.derivesFrom(defn.SeqClass)) + if isJavaDefined && tpe.derivesFrom(defn.SeqClass) then seqToArray(arg.expr) - else if (!isJavaDefined && tpe.derivesFrom(defn.ArrayClass)) + else if !isJavaDefined && tpe.derivesFrom(defn.ArrayClass) arrayToSeq(arg.expr) else arg.expr case arg => arg } transformTypeOfTree(cpy.Apply(tree)(tree.fun, args)) - } /** Convert sequence argument to Java array */ - private def seqToArray(tree: Tree)(using Context): Tree = tree match { + private def seqToArray(tree: Tree)(using Context): Tree = tree match case SeqLiteral(elems, elemtpt) => JavaSeqLiteral(elems, elemtpt) case _ => val elemType = tree.tpe.elemType var elemClass = erasure(elemType).classSymbol - if (defn.NotRuntimeClasses.contains(elemClass)) elemClass = defn.ObjectClass + if defn.NotRuntimeClasses.contains(elemClass) then + elemClass = defn.ObjectClass + end if ref(defn.DottyArraysModule) .select(nme.seqToArray) .appliedToType(elemType) .appliedTo(tree, clsOf(elemClass.typeRef)) - } /** Convert Java array argument to Scala Seq */ private def arrayToSeq(tree: Tree)(using Context): Tree = tpd.wrapArray(tree, tree.tpe.elemType) - override def transformTypeApply(tree: TypeApply)(using Context): Tree = - transformTypeOfTree(tree) - - /** If method overrides a Java varargs method, add a varargs bridge. - * Also transform trees inside method annotation + /** If method overrides a Java varargs method or is annotated with @varargs, add a varargs bridge. + * Also transform trees inside method annotation. */ override def transformDefDef(tree: DefDef)(using Context): Tree = - atPhase(thisPhase) { - if (tree.symbol.info.isVarArgsMethod && overridesJava(tree.symbol)) - addVarArgsBridge(tree) + val sym = tree.symbol + val hasAnnotation = hasVarargsAnnotation(sym) + + // atPhase(thisPhase) is used where necessary to see the repeated + // parameters before their elimination + val hasRepeatedParam = atPhase(thisPhase)(hasRepeatedParams(sym)) + if hasRepeatedParam then + val isOverride = atPhase(thisPhase)(overridesJava(sym)) + if isOverride || hasAnnotation || parentHasAnnotation(sym) then + // java varargs are more restrictive than scala's + // see https://github.com/scala/bug/issues/11714 + val validJava = atPhase(thisPhase)(isValidJavaVarArgs(sym.info)) + if !validJava then + ctx.error("""To generate java-compatible varargs: + | - there must be a single repeated parameter + | - it must be the last argument in the last parameter list + |""".stripMargin, + sym.sourcePos) + tree + else + // non-overrides cannot be synthetic otherwise javac refuses to call them + addVarArgsForwarder(tree, isBridge = isOverride) else tree - } + else + if hasAnnotation then + ctx.error("A method without repeated parameters cannot be annotated with @varargs", sym.sourcePos) + tree + + /** Is there a repeated parameter in some parameter list? */ + private def hasRepeatedParams(sym: Symbol)(using Context): Boolean = + sym.info.paramInfoss.flatten.exists(_.isRepeatedParam) - /** Add a Java varargs bridge - * @param ddef the original method definition which is assumed to override - * a Java varargs method JM up to this phase. - * @return a thicket consisting of `ddef` and a varargs bridge method - * which overrides the Java varargs method JM from this phase on - * and forwards to `ddef`. + /** Is this the type of a method that has a repeated parameter type as + * its last parameter in the last parameter list? + */ + private def isValidJavaVarArgs(tp: Type)(using Context): Boolean = tp match + case mt: MethodType => + val initp :+ lastp = mt.paramInfoss + initp.forall(_.forall(!_.isRepeatedParam)) && + lastp.nonEmpty && + lastp.init.forall(!_.isRepeatedParam) && + lastp.last.isRepeatedParam + case pt: PolyType => + isValidJavaVarArgs(pt.resultType) + case _ => + throw new Exception("Match error in @varargs checks. This should not happen, please open an issue " + tp) + + + /** Add a Java varargs forwarder + * @param ddef the original method definition + * @param isBridge true if we are generating a "bridge" (synthetic override forwarder) + * + * @return a thicket consisting of `ddef` and an additional method + * that forwards java varargs to `ddef`. It retains all the + * flags of `ddef` except `Private`. * - * A bridge is necessary because the following hold + * A forwarder is necessary because the following hold: * - the varargs in `ddef` will change from `RepeatedParam[T]` to `Seq[T]` after this phase - * - _but_ the callers of `ddef` expect its varargs to be changed to `Array[? <: T]`, since it overrides - * a Java varargs - * The solution is to add a "bridge" method that converts its argument from `Array[? <: T]` to `Seq[T]` and + * - _but_ the callers of `ddef` expect its varargs to be changed to `Array[? <: T]` + * The solution is to add a method that converts its argument from `Array[? <: T]` to `Seq[T]` and * forwards it to `ddef`. */ - private def addVarArgsBridge(ddef: DefDef)(using Context): Tree = { - val original = ddef.symbol.asTerm - val bridge = original.copy( - flags = ddef.symbol.flags &~ Private | Artifact, - info = toJavaVarArgs(ddef.symbol.info)).enteredAfter(thisPhase).asTerm - val bridgeDef = polyDefDef(bridge, trefs => vrefss => { - val (vrefs :+ varArgRef) :: vrefss1 = vrefss - // Can't call `.argTypes` here because the underlying array type is of the - // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`. - val elemtp = varArgRef.tpe.widen.argInfos.head - ref(original.termRef) - .appliedToTypes(trefs) - .appliedToArgs(vrefs :+ tpd.wrapArray(varArgRef, elemtp)) - .appliedToArgss(vrefss1) - }) - - Thicket(ddef, bridgeDef) - } + private def addVarArgsForwarder(ddef: DefDef, isBridge: Boolean)(using ctx: Context): Tree = + val original = ddef.symbol + + // The java-compatible forwarder symbol + val sym = atPhase(thisPhase) { + // Capture the flags before they get modified by #transform. + // For simplicity we always set the varargs flag, + // although it's not strictly necessary for overrides. + val flags = original.flags | JavaVarargs + original.copy( + flags = if isBridge then flags | Artifact else flags, + info = toJavaVarArgs(ddef.symbol.info) + ).asTerm + } + + // Find a method that would conflict with the forwarder if the latter existed. + // This needs to be done at thisPhase so that parent @varargs don't conflict. + val conflict = atPhase(thisPhase) { + currentClass.info.member(sym.name).alternatives.find { s => + s.matches(sym) && + !(isBridge && s.asSymDenotation.is(JavaDefined)) + } + } + + conflict match + case Some(conflict) => + ctx.error(s"@varargs produces a forwarder method that conflicts with ${conflict.showDcl}", original.sourcePos) + ddef + case None => + val bridgeDef = polyDefDef(sym.enteredAfter(thisPhase), trefs => vrefss => { + val init :+ (last :+ vararg) = vrefss + // Can't call `.argTypes` here because the underlying array type is of the + // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`. + val elemtp = vararg.tpe.widen.argInfos.head + ref(original.termRef) + .appliedToTypes(trefs) + .appliedToArgss(init) + .appliedToArgs(last :+ tpd.wrapArray(vararg, elemtp)) + }) + Thicket(ddef, bridgeDef) /** Convert type from Scala to Java varargs method */ - private def toJavaVarArgs(tp: Type)(using Context): Type = tp match { + private def toJavaVarArgs(tp: Type)(using Context): Type = tp match case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(tp.resultType)) case tp: MethodType => - val inits :+ last = tp.paramInfos - val last1 = last.translateFromRepeated(toArray = true) - tp.derivedLambdaType(tp.paramNames, inits :+ last1, tp.resultType) - } + tp.resultType match + case m: MethodType => // multiple param lists + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(m)) + case _ => + val init :+ last = tp.paramInfos + val vararg = varargArrayType(last) + tp.derivedLambdaType(tp.paramNames, init :+ vararg, tp.resultType) + + /** Translate a repeated type T* to an `Array[? <: Upper]` + * such that it is compatible with java varargs. + * + * When necessary we set `Upper = T & AnyRef` + * to prevent the erasure of `Array[? <: Upper]` to Object, + * which would break the varargs from Java. + */ + private def varargArrayType(tp: Type)(using Context): Type = + val array = tp.translateFromRepeated(toArray = true) // Array[? <: T] + val element = array.elemType.hiBound // T + + if element <:< defn.AnyRefType || element.typeSymbol.isPrimitiveValueClass then array + else defn.ArrayOf(TypeBounds.upper(AndType(element, defn.AnyRefType))) // Array[? <: T & AnyRef] } diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index e2d6362ce1ff..5b2592b5009b 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -186,10 +186,11 @@ object GenericSignatures { case defn.ArrayOf(elemtp) => if (isUnboundedGeneric(elemtp)) jsig(defn.ObjectType) - else { + else builder.append(ClassfileConstants.ARRAY_TAG) - jsig(elemtp) - } + elemtp match + case TypeBounds(lo, hi) => jsig(hi.widenDealias) + case _ => jsig(elemtp) case RefOrAppliedType(sym, pre, args) => if (sym == defn.PairClass && tp.tupleArity > Definitions.MaxTupleArity) @@ -313,8 +314,8 @@ object GenericSignatures { * - If the list contains one or more occurrences of scala.Array with * type parameters El1, El2, ... then the dominator is scala.Array with * type parameter of intersectionDominator(List(El1, El2, ...)). <--- @PP: not yet in spec. - * - Otherwise, the list is reduced to a subsequence containing only types - * which are not subtypes of other listed types (the span.) + * - Otherwise, the list is reduced to a subsequence containing only the + * types that are not supertypes of other listed types (the span.) * - If the span is empty, the dominator is Object. * - If the span contains a class Tc which is not a trait and which is * not Object, the dominator is Tc. <--- @PP: "which is not Object" not in spec. diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index ebcf2b861abe..aefe3b14b017 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -26,3 +26,5 @@ i7868.scala enum-java zero-arity-case-class.scala tuple-ops.scala +i7212 +varargs-abstract diff --git a/tests/neg/varargs-annot.scala b/tests/neg/varargs-annot.scala new file mode 100644 index 000000000000..085c211e4081 --- /dev/null +++ b/tests/neg/varargs-annot.scala @@ -0,0 +1,28 @@ +import annotation.varargs + +// Failing varargs annotation +object Test { + + trait A { + def v1(a: Int, b: Array[String]) = a + } + + trait B extends A { + @varargs def v1(a: Int, b: String*) = a + b.length // error + } + + @varargs def nov(a: Int) = 0 // error: A method without repeated parameters cannot be annotated with @varargs + @varargs def v(a: Int, b: String*) = a + b.length // ok + def v(a: Int, b: String) = a // ok + + @varargs def v2(a: Int, b: String*) = 0 // error + def v2(a: Int, b: Array[String]) = 0 + + @varargs def v3(a: String*)(b: Int) = b + a.length // error + @varargs def v4(a: String)(b: Int) = b + a.length // error + @varargs def v5(a: String)(b: Int*) = a + b.sum // ok + + @varargs def v6: Int = 1 // error + @varargs def v7(i: Int*)() = i.sum // error + +} diff --git a/tests/pos/varargs-annot-currying.scala b/tests/pos/varargs-annot-currying.scala new file mode 100644 index 000000000000..f34e4f2190a9 --- /dev/null +++ b/tests/pos/varargs-annot-currying.scala @@ -0,0 +1,18 @@ +import scala.annotation.varargs + +object VarArgs { + @varargs + def two(a: Int)(b: String*): Nothing = ??? + + @varargs + def twoPrimitive(a: Int)(b: Int*): Nothing = ??? + + @varargs + def three(a3: Int)(b3: String)(c3: String*): Nothing = ??? + + @varargs + def threePrimitive(a3: Int)(b3: String)(c3: Int*): Nothing = ??? + + @varargs + def emptyOk()()(xs: String*): Nothing = ??? +} diff --git a/tests/pos/varargs-annot-generics.scala b/tests/pos/varargs-annot-generics.scala new file mode 100644 index 000000000000..d3e0ee49ea2f --- /dev/null +++ b/tests/pos/varargs-annot-generics.scala @@ -0,0 +1,21 @@ +import scala.annotation.varargs + +object VarArgs { + @varargs + def foo1[A](x: A, xs: String*): A = ??? + + @varargs + def foo2[A](x: List[A], xs: String*): A = ??? + + @varargs + def foo3[A](n: Int*): Unit = ??? + + @varargs + def bar1[A](x: A*): Unit = ??? + + @varargs + def bar2[A](x: A, xs: A*): A = ??? + + @varargs + def bar3[A <: Comparable[A]](xs: A*): A = ??? +} diff --git a/tests/run/i7212/CompatVargs.scala b/tests/run/i7212/CompatVargs.scala new file mode 100644 index 000000000000..0269d27c5841 --- /dev/null +++ b/tests/run/i7212/CompatVargs.scala @@ -0,0 +1,11 @@ +import scala.annotation._ + +class CompatVargs { + @varargs + def vargs(args: String*): Unit = println(args) + + def vargsFromScala(): Unit = + vargs("single") + vargs("a", "b") + vargs(Seq("a", "b"): _*) +} diff --git a/tests/run/i7212/Test.java b/tests/run/i7212/Test.java new file mode 100644 index 000000000000..e2d21bd76dfd --- /dev/null +++ b/tests/run/i7212/Test.java @@ -0,0 +1,9 @@ +public class Test { + public static void main(String[] args) { + CompatVargs c = new CompatVargs(); + c.vargs("single"); + c.vargs("a", "b"); + c.vargs(new String[]{"a", "b"}); + c.vargsFromScala(); + } +} diff --git a/tests/run/vararg-extend-java/Vararg.java b/tests/run/vararg-extend-java/Vararg.java new file mode 100644 index 000000000000..c3f286640b7b --- /dev/null +++ b/tests/run/vararg-extend-java/Vararg.java @@ -0,0 +1,3 @@ +public class Vararg { + void v(int... i) {} +} diff --git a/tests/run/vararg-extend-java/VarargAbstract.java b/tests/run/vararg-extend-java/VarargAbstract.java new file mode 100644 index 000000000000..be9565f3eaef --- /dev/null +++ b/tests/run/vararg-extend-java/VarargAbstract.java @@ -0,0 +1,4 @@ +public abstract class VarargAbstract { + public abstract void v1(String... s); + public abstract void v2(String... s); +} diff --git a/tests/run/vararg-extend-java/test.scala b/tests/run/vararg-extend-java/test.scala new file mode 100644 index 000000000000..39e3c632124c --- /dev/null +++ b/tests/run/vararg-extend-java/test.scala @@ -0,0 +1,28 @@ +import scala.annotation.varargs + +class VarargImpl extends VarargAbstract { + def v1(s: String*) = () + + override def v2(s: String*) = () +} + +class VarargSub extends Vararg { + override def v(i: Int*) = () +} + +object Test { + def main(args: Array[String]): Unit = + val a: VarargAbstract = VarargImpl() + a.v1("a", "b", "c") + a.v2("a", "b", "c") + + val i: VarargImpl = VarargImpl() + i.v1("a", "b", "c") + i.v2("a", "b", "c") + + val b: Vararg = VarargSub() + b.v(1, 2, 3) + + val c: VarargSub = VarargSub() + c.v(1, 2, 3) +} diff --git a/tests/run/varargs-abstract/Test.java b/tests/run/varargs-abstract/Test.java new file mode 100644 index 000000000000..3233d2412894 --- /dev/null +++ b/tests/run/varargs-abstract/Test.java @@ -0,0 +1,25 @@ +import java.util.Comparator; + +public class Test { + public static void main(String[] args) { + VarargImplClass c = new VarargImplClass(); + + c.x(0, 1, 2); + c.y("a", "b", "c"); + c.z("a", "b", "c"); + c.generic("a", "b", "c"); + c.genericBounded("a", "b", "c"); + + VarargAbstractClass i = new VarargImplClass(); + + i.x(0, 1, 2); + i.y("a", "b", "c"); + i.z("a", "b", "c"); + i.generic("a", "b", "c"); + i.genericBounded("a", "b", "c"); + + VarargClassBounded b = new VarargClassBounded<>(); + b.v1("a", "b", "c"); + b.v2("a", "b", "c"); + } +} diff --git a/tests/run/varargs-abstract/test.scala b/tests/run/varargs-abstract/test.scala new file mode 100644 index 000000000000..2edb43c4cf50 --- /dev/null +++ b/tests/run/varargs-abstract/test.scala @@ -0,0 +1,33 @@ +import scala.annotation.varargs + +abstract class VarargAbstractClass[T] { + @varargs + def x(v: Int*): Int + + @varargs + def y(v: String*): Int + + @varargs + def z(v: T*): Int + + @varargs + def generic[E](e: E*): Unit = () + + @varargs + def genericBounded[E <: Comparable[E] & Serializable](e: E*): Unit = () +} + +class VarargImplClass extends VarargAbstractClass[String] { + + override def x(v: Int*): Int = v.length + override def y(v: String*): Int = v.length + override def z(v: String*): Int = v.length +} + +class VarargClassBounded[B <: Comparable[B]] { + @varargs + def v1(v: B*): Unit = () + + @varargs + def v2(first: B, more: B*): B = first +} diff --git a/tests/untried/neg/varargs.check b/tests/untried/neg/varargs.check deleted file mode 100644 index 5f002fdae4a9..000000000000 --- a/tests/untried/neg/varargs.check +++ /dev/null @@ -1,10 +0,0 @@ -varargs.scala:16: error: A method with a varargs annotation produces a forwarder method with the same signature (a: Int, b: Array[String])Int as an existing method. - @varargs def v1(a: Int, b: String*) = a + b.length - ^ -varargs.scala:19: error: A method without repeated parameters cannot be annotated with the `varargs` annotation. - @varargs def nov(a: Int) = 0 - ^ -varargs.scala:21: error: A method with a varargs annotation produces a forwarder method with the same signature (a: Int, b: Array[String])Int as an existing method. - @varargs def v2(a: Int, b: String*) = 0 - ^ -3 errors found diff --git a/tests/untried/neg/varargs.scala b/tests/untried/neg/varargs.scala deleted file mode 100644 index ce7f0d71fee8..000000000000 --- a/tests/untried/neg/varargs.scala +++ /dev/null @@ -1,27 +0,0 @@ - - - -import annotation.varargs - - - -// Failing varargs annotation -object Test { - - trait A { - def v1(a: Int, b: Array[String]) = a - } - - trait B extends A { - @varargs def v1(a: Int, b: String*) = a + b.length - } - - @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]): Unit = { - } - -}