Skip to content

Commit bd856dd

Browse files
authored
Merge pull request #3747 from dotty-staging/fix-#3248-1
Fix #3248: Handle repeated arguments in results of unapply selectors
2 parents 82dd280 + cc63c9e commit bd856dd

File tree

10 files changed

+84
-23
lines changed

10 files changed

+84
-23
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ class Definitions {
7777
val cls = denot.asClass.classSymbol
7878
val paramDecls = newScope
7979
val typeParam = enterSyntheticTypeParam(cls, paramFlags, paramDecls)
80-
val parents = parentConstrs.toList
80+
def instantiate(tpe: Type) =
81+
if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef)
82+
else tpe
83+
val parents = parentConstrs.toList map instantiate
8184
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parents, paramDecls)
8285
}
8386
}

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,15 @@ object Types {
13661366
*/
13671367
def signature(implicit ctx: Context): Signature = Signature.NotAMethod
13681368

1369+
def annotatedToRepeated(implicit ctx: Context): Type = this match {
1370+
case tp @ ExprType(tp1) => tp.derivedExprType(tp1.annotatedToRepeated)
1371+
case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
1372+
val typeSym = tp.typeSymbol.asClass
1373+
assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass)
1374+
tp.translateParameterized(typeSym, defn.RepeatedParamClass)
1375+
case _ => this
1376+
}
1377+
13691378
/** Convert to text */
13701379
def toText(printer: Printer): Text = printer.toText(this)
13711380

@@ -2863,21 +2872,12 @@ object Types {
28632872
* - add @inlineParam to inline call-by-value parameters
28642873
*/
28652874
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
2866-
def translateRepeated(tp: Type): Type = tp match {
2867-
case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1))
2868-
case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
2869-
val typeSym = tp.typeSymbol.asClass
2870-
assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass)
2871-
tp.translateParameterized(typeSym, defn.RepeatedParamClass)
2872-
case tp =>
2873-
tp
2874-
}
28752875
def translateInline(tp: Type): Type = tp match {
28762876
case _: ExprType => tp
28772877
case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
28782878
}
28792879
def paramInfo(param: Symbol) = {
2880-
val paramType = translateRepeated(param.info)
2880+
val paramType = param.info.annotatedToRepeated
28812881
if (param.is(Inline)) translateInline(paramType) else paramType
28822882
}
28832883

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ object PatternMatcher {
351351
}
352352
case WildcardPattern() =>
353353
onSuccess
354+
case SeqLiteral(pats, _) =>
355+
matchElemsPlan(scrutinee, pats, exact = true, onSuccess)
354356
case _ =>
355357
TestPlan(EqualTest(tree), scrutinee, tree.pos, onSuccess, onFailure)
356358
}

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
395395
Typ(pat.tpe.stripAnnots, false)
396396
case Alternative(trees) => Or(trees.map(project(_)))
397397
case Bind(_, pat) => project(pat)
398+
case SeqLiteral(pats, _) => projectSeq(pats)
398399
case UnApply(fun, _, pats) =>
399400
if (fun.symbol.name == nme.unapplySeq)
400401
if (fun.symbol.owner == scalaSeqFactoryClass)
@@ -496,7 +497,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
496497

497498
debug.println(s"signature of ${unappSym.showFullName} ----> ${sig.map(_.show).mkString(", ")}")
498499

499-
sig
500+
sig.map(_.annotatedToRepeated)
500501
}
501502

502503
/** Decompose a type into subspaces -- assume the type can be decomposed */

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ object Applications {
4747
val ref = extractorMember(tp, name)
4848
if (ref.isOverloaded)
4949
errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos)
50-
ref.info.widenExpr.dealias
50+
ref.info.widenExpr.annotatedToRepeated.dealias
5151
}
5252

5353
/** Does `tp` fit the "product match" conditions as an unapply result type
@@ -93,7 +93,10 @@ object Applications {
9393
def getTp = extractorMemberType(unapplyResult, nme.get, pos)
9494

9595
def fail = {
96-
ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos)
96+
val addendum =
97+
if (ctx.scala2Mode && unapplyName == nme.unapplySeq)
98+
"\n You might want to try to rewrite the extractor to use `unapply` instead."
99+
ctx.error(em"$unapplyResult is not a valid result type of an $unapplyName method of an extractor$addendum", pos)
97100
Nil
98101
}
99102

@@ -959,13 +962,19 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
959962

960963
var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos)
961964
for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show)
962-
val bunchedArgs = argTypes match {
963-
case argType :: Nil =>
964-
if (argType.isRepeatedParam) untpd.SeqLiteral(args, untpd.TypeTree()) :: Nil
965-
else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil
966-
else args
967-
case _ => args
968-
}
965+
val bunchedArgs =
966+
if (argTypes.nonEmpty && argTypes.last.isRepeatedParam)
967+
args.lastOption match {
968+
case Some(arg @ Typed(argSeq, _)) if untpd.isWildcardStarArg(arg) =>
969+
args.init :+ argSeq
970+
case _ =>
971+
val (regularArgs, varArgs) = args.splitAt(argTypes.length - 1)
972+
regularArgs :+ untpd.SeqLiteral(varArgs, untpd.TypeTree())
973+
}
974+
else if (argTypes.lengthCompare(1) == 0 && args.lengthCompare(1) > 0 && ctx.canAutoTuple)
975+
untpd.Tuple(args) :: Nil
976+
else
977+
args
969978
if (argTypes.length != bunchedArgs.length) {
970979
ctx.error(UnapplyInvalidNumberOfArguments(qual, argTypes), tree.pos)
971980
argTypes = argTypes.take(args.length) ++

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12891289
case _ =>
12901290
if (tree.name == nme.WILDCARD) body1
12911291
else {
1292-
val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos)
1292+
val sym = newPatternBoundSym(tree.name, body1.tpe.underlyingIfRepeated(isJava = false), tree.pos)
12931293
if (ctx.mode.is(Mode.InPatternAlternative))
12941294
ctx.error(i"Illegal variable ${sym.name} in pattern alternative", tree.pos)
12951295
assignType(cpy.Bind(tree)(tree.name, body1), sym)

tests/neg/i3248.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Test {
2+
class Foo(val name: String, val children: Int *)
3+
object Foo {
4+
def unapplySeq(f: Foo) = Some((f.name, f.children))
5+
}
6+
7+
def foo(f: Foo) = f match {
8+
case Foo(name, ns: _*) => 1 // error: not a valid unapply result type
9+
}
10+
}

tests/patmat/t8178.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ case class FailsChild2(a: Seq[String]) extends Fails
44
object FailsTest {
55
def matchOnVarArgsFirstFails(f: Fails) = {
66
f match {
7-
case VarArgs1(_) => ???
7+
case VarArgs1(_: _*) => ???
88
// BUG: Without this line we should get a non-exhaustive match compiler error.
99
//case FailsChild2(_) => ???
1010
}

tests/pos/i3248.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
class Foo(val name: String, val children: Int *)
3+
object Foo {
4+
def unapply(f: Foo) = Some((f.name, f.children))
5+
}
6+
7+
def foo(f: Foo) = f match {
8+
case Foo(name, cs : _*) => name :: cs.reverse.toList.map(_.toString)
9+
}
10+
def main(args: Array[String]) = {
11+
println(foo(new Foo("hi", 1, 2, 3)).mkString(" "))
12+
}
13+
}

tests/run/i3248.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
object Test extends App {
2+
class Foo(val name: String, val children: Int *)
3+
object Foo {
4+
def unapply(f: Foo) = Some((f.name, f.children))
5+
}
6+
7+
def foo(f: Foo) = (f: Any) match {
8+
case Foo(name, ns: _*) => ns.length
9+
case List(ns: _*) => ns.length
10+
}
11+
12+
case class Bar(val children: Int*)
13+
14+
def bar(f: Any) = f match {
15+
case Bar(1, 2, 3) => 0
16+
case Bar(a, b) => a + b
17+
case Bar(ns: _*) => ns.length
18+
}
19+
20+
assert(bar(new Bar(1, 2, 3)) == 0)
21+
assert(bar(new Bar(3, 2, 1)) == 3)
22+
assert(foo(new Foo("name", 1, 2, 3)) == 3)
23+
}

0 commit comments

Comments
 (0)