@@ -47,11 +47,22 @@ object Applications {
4747
4848 /** Does `tp` fit the "product match" conditions as an unapply result type
4949 * for a pattern with `numArgs` subpatterns?
50- * This is the case of `tp` has members `_1` to `_N` where `N == numArgs`.
50+ * This is the case if `tp` has members `_1` to `_N` where `N == numArgs`.
5151 */
5252 def isProductMatch (tp : Type , numArgs : Int , errorPos : SourcePosition = NoSourcePosition )(implicit ctx : Context ): Boolean =
5353 numArgs > 0 && productArity(tp, errorPos) == numArgs
5454
55+ /** Does `tp` fit the "product-seq match" conditions as an unapply result type
56+ * for a pattern with `numArgs` subpatterns?
57+ * This is the case if (1) `tp` has members `_1` to `_N` where `N <= numArgs + 1`.
58+ * (2) `tp._N` conforms to Seq match
59+ */
60+ def isProductSeqMatch (tp : Type , numArgs : Int , errorPos : SourcePosition = NoSourcePosition )(implicit ctx : Context ): Boolean = {
61+ val arity = productArity(tp, errorPos)
62+ arity > 0 && arity <= numArgs + 1 &&
63+ unapplySeqTypeElemTp(productSelectorTypes(tp, errorPos).last).exists
64+ }
65+
5566 /** Does `tp` fit the "get match" conditions as an unapply result type?
5667 * This is the case of `tp` has a `get` member as well as a
5768 * parameterless `isEmpty` member of result type `Boolean`.
@@ -60,6 +71,39 @@ object Applications {
6071 extractorMemberType(tp, nme.isEmpty, errorPos).isRef(defn.BooleanClass ) &&
6172 extractorMemberType(tp, nme.get, errorPos).exists
6273
74+ /** If `getType` is of the form:
75+ * ```
76+ * {
77+ * def lengthCompare(len: Int): Int // or, def length: Int
78+ * def apply(i: Int): T = a(i)
79+ * def drop(n: Int): scala.Seq[T]
80+ * def toSeq: scala.Seq[T]
81+ * }
82+ * ```
83+ * returns `T`, otherwise NoType.
84+ */
85+ def unapplySeqTypeElemTp (getTp : Type )(implicit ctx : Context ): Type = {
86+ def lengthTp = ExprType (defn.IntType )
87+ def lengthCompareTp = MethodType (List (defn.IntType ), defn.IntType )
88+ def applyTp (elemTp : Type ) = MethodType (List (defn.IntType ), elemTp)
89+ def dropTp (elemTp : Type ) = MethodType (List (defn.IntType ), defn.SeqType .appliedTo(elemTp))
90+ def toSeqTp (elemTp : Type ) = ExprType (defn.SeqType .appliedTo(elemTp))
91+
92+ // the result type of `def apply(i: Int): T`
93+ val elemTp = getTp.member(nme.apply).suchThat(_.info <:< applyTp(WildcardType )).info.resultType
94+
95+ def hasMethod (name : Name , tp : Type ) =
96+ getTp.member(name).suchThat(getTp.memberInfo(_) <:< tp).exists
97+
98+ val isValid =
99+ elemTp.exists &&
100+ (hasMethod(nme.lengthCompare, lengthCompareTp) || hasMethod(nme.length, lengthTp)) &&
101+ hasMethod(nme.drop, dropTp(elemTp)) &&
102+ hasMethod(nme.toSeq, toSeqTp(elemTp))
103+
104+ if (isValid) elemTp else NoType
105+ }
106+
63107 def productSelectorTypes (tp : Type , errorPos : SourcePosition )(implicit ctx : Context ): List [Type ] = {
64108 def tupleSelectors (n : Int , tp : Type ): List [Type ] = {
65109 val sel = extractorMemberType(tp, nme.selectorName(n), errorPos)
@@ -92,57 +136,35 @@ object Applications {
92136 else tp :: Nil
93137 } else tp :: Nil
94138
139+ def productSeqSelectors (tp : Type , argsNum : Int , pos : SourcePosition )(implicit ctx : Context ): List [Type ] = {
140+ val selTps = productSelectorTypes(tp, pos)
141+ val arity = selTps.length
142+ val elemTp = unapplySeqTypeElemTp(selTps.last)
143+ (0 until argsNum).map(i => if (i < arity - 1 ) selTps(i) else elemTp).toList
144+ }
145+
95146 def unapplyArgs (unapplyResult : Type , unapplyFn : Tree , args : List [untpd.Tree ], pos : SourcePosition )(implicit ctx : Context ): List [Type ] = {
96147
97148 val unapplyName = unapplyFn.symbol.name
98- def seqSelector = defn.RepeatedParamType .appliedTo(unapplyResult.elemType :: Nil )
99149 def getTp = extractorMemberType(unapplyResult, nme.get, pos)
100150
101151 def fail = {
102152 ctx.error(UnapplyInvalidReturnType (unapplyResult, unapplyName), pos)
103153 Nil
104154 }
105155
106- /** If `getType` is of the form:
107- * ```
108- * {
109- * def lengthCompare(len: Int): Int // or, def length: Int
110- * def apply(i: Int): T = a(i)
111- * def drop(n: Int): scala.Seq[T]
112- * def toSeq: scala.Seq[T]
113- * }
114- * ```
115- * returns `T`, otherwise NoType.
116- */
117- def unapplySeqTypeElemTp (getTp : Type ): Type = {
118- def lengthTp = ExprType (defn.IntType )
119- def lengthCompareTp = MethodType (List (defn.IntType ), defn.IntType )
120- def applyTp (elemTp : Type ) = MethodType (List (defn.IntType ), elemTp)
121- def dropTp (elemTp : Type ) = MethodType (List (defn.IntType ), defn.SeqType .appliedTo(elemTp))
122- def toSeqTp (elemTp : Type ) = defn.SeqType .appliedTo(elemTp)
123-
124- // the result type of `def apply(i: Int): T`
125- val elemTp = getTp.member(nme.apply).suchThat(_.info <:< applyTp(WildcardType )).info.resultType
126-
127- def hasMethod (name : Name , tp : Type ) =
128- getTp.member(name).suchThat(getTp.memberInfo(_) <:< tp).exists
129-
130- val isValid =
131- elemTp.exists &&
132- (hasMethod(nme.lengthCompare, lengthCompareTp) || hasMethod(nme.length, lengthTp)) &&
133- hasMethod(nme.drop, dropTp(elemTp)) &&
134- hasMethod(nme.toSeq, toSeqTp(elemTp))
135-
136- if (isValid) elemTp else NoType
156+ def unapplySeq (tp : Type )(fallback : => List [Type ]): List [Type ] = {
157+ val elemTp = unapplySeqTypeElemTp(tp)
158+ if (elemTp.exists) args.map(Function .const(elemTp))
159+ else if (isProductSeqMatch(tp, args.length, pos)) productSeqSelectors(tp, args.length, pos)
160+ else fallback
137161 }
138162
139163 if (unapplyName == nme.unapplySeq) {
140- if (isGetMatch(unapplyResult, pos)) {
141- val elemTp = unapplySeqTypeElemTp(getTp)
142- if (elemTp.exists) args.map(Function .const(elemTp))
164+ unapplySeq(unapplyResult) {
165+ if (isGetMatch(unapplyResult, pos)) unapplySeq(getTp)(fail)
143166 else fail
144167 }
145- else fail
146168 }
147169 else {
148170 assert(unapplyName == nme.unapply)
@@ -1106,19 +1128,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11061128
11071129 var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.sourcePos)
11081130 for (argType <- argTypes) assert(! isBounds(argType), unapplyApp.tpe.show)
1109- val bunchedArgs =
1110- if (argTypes.nonEmpty && argTypes.last.isRepeatedParam)
1111- args.lastOption match {
1112- case Some (arg @ Typed (argSeq, _)) if untpd.isWildcardStarArg(arg) =>
1113- args.init :+ argSeq
1114- case _ =>
1115- val (regularArgs, varArgs) = args.splitAt(argTypes.length - 1 )
1116- regularArgs :+ untpd.SeqLiteral (varArgs, untpd.TypeTree ()).withSpan(tree.span)
1117- }
1118- else if (argTypes.lengthCompare(1 ) == 0 && args.lengthCompare(1 ) > 0 && ctx.canAutoTuple)
1119- untpd.Tuple (args) :: Nil
1120- else
1121- args
1131+ val bunchedArgs = argTypes match {
1132+ case argType :: Nil =>
1133+ if (args.lengthCompare(1 ) > 0 && ctx.canAutoTuple) untpd.Tuple (args) :: Nil
1134+ else args
1135+ case _ => args
1136+ }
11221137 if (argTypes.length != bunchedArgs.length) {
11231138 ctx.error(UnapplyInvalidNumberOfArguments (qual, argTypes), tree.sourcePos)
11241139 argTypes = argTypes.take(args.length) ++
0 commit comments