diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 19dab5bac91a..e4933305953f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -77,7 +77,10 @@ class Definitions { val cls = denot.asClass.classSymbol val paramDecls = newScope val typeParam = enterSyntheticTypeParam(cls, paramFlags, paramDecls) - val parents = parentConstrs.toList + def instantiate(tpe: Type) = + if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) + else tpe + val parents = parentConstrs.toList map instantiate denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parents, paramDecls) } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f0298cf63804..037edd244893 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1363,6 +1363,15 @@ object Types { */ def signature(implicit ctx: Context): Signature = Signature.NotAMethod + def annotatedToRepeated(implicit ctx: Context): Type = this match { + case tp @ ExprType(tp1) => tp.derivedExprType(tp1.annotatedToRepeated) + case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => + val typeSym = tp.typeSymbol.asClass + assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass) + tp.translateParameterized(typeSym, defn.RepeatedParamClass) + case _ => this + } + /** Convert to text */ def toText(printer: Printer): Text = printer.toText(this) @@ -2860,21 +2869,12 @@ object Types { * - add @inlineParam to inline call-by-value parameters */ def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { - def translateRepeated(tp: Type): Type = tp match { - case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1)) - case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => - val typeSym = tp.typeSymbol.asClass - assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass) - tp.translateParameterized(typeSym, defn.RepeatedParamClass) - case tp => - tp - } def translateInline(tp: Type): Type = tp match { case _: ExprType => tp case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) } def paramInfo(param: Symbol) = { - val paramType = translateRepeated(param.info) + val paramType = param.info.annotatedToRepeated if (param.is(Inline)) translateInline(paramType) else paramType } diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index d810ec420bb7..a2ecdc8c587a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -351,6 +351,8 @@ object PatternMatcher { } case WildcardPattern() => onSuccess + case SeqLiteral(pats, _) => + matchElemsPlan(scrutinee, pats, exact = true, onSuccess) case _ => TestPlan(EqualTest(tree), scrutinee, tree.pos, onSuccess, onFailure) } diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 267deb1f75a7..c9005c4c5728 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -395,6 +395,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Typ(pat.tpe.stripAnnots, false) case Alternative(trees) => Or(trees.map(project(_))) case Bind(_, pat) => project(pat) + case SeqLiteral(pats, _) => projectSeq(pats) case UnApply(fun, _, pats) => if (fun.symbol.name == nme.unapplySeq) if (fun.symbol.owner == scalaSeqFactoryClass) @@ -496,7 +497,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { debug.println(s"signature of ${unappSym.showFullName} ----> ${sig.map(_.show).mkString(", ")}") - sig + sig.map(_.annotatedToRepeated) } /** Decompose a type into subspaces -- assume the type can be decomposed */ diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a49e0f73bea9..7784cb64c979 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -47,7 +47,7 @@ object Applications { val ref = extractorMember(tp, name) if (ref.isOverloaded) errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos) - ref.info.widenExpr.dealias + ref.info.widenExpr.annotatedToRepeated.dealias } /** Does `tp` fit the "product match" conditions as an unapply result type @@ -93,7 +93,10 @@ object Applications { def getTp = extractorMemberType(unapplyResult, nme.get, pos) def fail = { - ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos) + val addendum = + if (ctx.scala2Mode && unapplyName == nme.unapplySeq) + "\n You might want to try to rewrite the extractor to use `unapply` instead." + ctx.error(em"$unapplyResult is not a valid result type of an $unapplyName method of an extractor$addendum", pos) Nil } @@ -959,13 +962,19 @@ trait Applications extends Compatibility { self: Typer with Dynamic => var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos) for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show) - val bunchedArgs = argTypes match { - case argType :: Nil => - if (argType.isRepeatedParam) untpd.SeqLiteral(args, untpd.TypeTree()) :: Nil - else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil - else args - case _ => args - } + val bunchedArgs = + if (argTypes.nonEmpty && argTypes.last.isRepeatedParam) + args.lastOption match { + case Some(arg @ Typed(argSeq, _)) if untpd.isWildcardStarArg(arg) => + args.init :+ argSeq + case _ => + val (regularArgs, varArgs) = args.splitAt(argTypes.length - 1) + regularArgs :+ untpd.SeqLiteral(varArgs, untpd.TypeTree()) + } + else if (argTypes.lengthCompare(1) == 0 && args.lengthCompare(1) > 0 && ctx.canAutoTuple) + untpd.Tuple(args) :: Nil + else + args if (argTypes.length != bunchedArgs.length) { ctx.error(UnapplyInvalidNumberOfArguments(qual, argTypes), tree.pos) argTypes = argTypes.take(args.length) ++ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fa2cd8f25c5b..5cdd4cfc9a7a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1275,7 +1275,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => if (tree.name == nme.WILDCARD) body1 else { - val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos) + val sym = newPatternBoundSym(tree.name, body1.tpe.underlyingIfRepeated(isJava = false), tree.pos) if (ctx.mode.is(Mode.InPatternAlternative)) ctx.error(i"Illegal variable ${sym.name} in pattern alternative", tree.pos) assignType(cpy.Bind(tree)(tree.name, body1), sym) diff --git a/tests/neg/i3248.scala b/tests/neg/i3248.scala new file mode 100644 index 000000000000..3d70f50cd066 --- /dev/null +++ b/tests/neg/i3248.scala @@ -0,0 +1,10 @@ +class Test { + class Foo(val name: String, val children: Int *) + object Foo { + def unapplySeq(f: Foo) = Some((f.name, f.children)) + } + + def foo(f: Foo) = f match { + case Foo(name, ns: _*) => 1 // error: not a valid unapply result type + } +} diff --git a/tests/patmat/t8178.scala b/tests/patmat/t8178.scala index 4fb39955bc0e..d1b5f9608ae4 100644 --- a/tests/patmat/t8178.scala +++ b/tests/patmat/t8178.scala @@ -4,7 +4,7 @@ case class FailsChild2(a: Seq[String]) extends Fails object FailsTest { def matchOnVarArgsFirstFails(f: Fails) = { f match { - case VarArgs1(_) => ??? + case VarArgs1(_: _*) => ??? // BUG: Without this line we should get a non-exhaustive match compiler error. //case FailsChild2(_) => ??? } diff --git a/tests/pos/i3248.scala b/tests/pos/i3248.scala new file mode 100644 index 000000000000..2126545c471f --- /dev/null +++ b/tests/pos/i3248.scala @@ -0,0 +1,13 @@ +object Test { + class Foo(val name: String, val children: Int *) + object Foo { + def unapply(f: Foo) = Some((f.name, f.children)) + } + + def foo(f: Foo) = f match { + case Foo(name, cs : _*) => name :: cs.reverse.toList.map(_.toString) + } + def main(args: Array[String]) = { + println(foo(new Foo("hi", 1, 2, 3)).mkString(" ")) + } +} diff --git a/tests/run/i3248.scala b/tests/run/i3248.scala new file mode 100644 index 000000000000..bf05b88b5883 --- /dev/null +++ b/tests/run/i3248.scala @@ -0,0 +1,23 @@ +object Test extends App { + class Foo(val name: String, val children: Int *) + object Foo { + def unapply(f: Foo) = Some((f.name, f.children)) + } + + def foo(f: Foo) = (f: Any) match { + case Foo(name, ns: _*) => ns.length + case List(ns: _*) => ns.length + } + + case class Bar(val children: Int*) + + def bar(f: Any) = f match { + case Bar(1, 2, 3) => 0 + case Bar(a, b) => a + b + case Bar(ns: _*) => ns.length + } + + assert(bar(new Bar(1, 2, 3)) == 0) + assert(bar(new Bar(3, 2, 1)) == 3) + assert(foo(new Foo("name", 1, 2, 3)) == 3) +}