@@ -47,11 +47,22 @@ object Applications {
47
47
48
48
/** Does `tp` fit the "product match" conditions as an unapply result type
49
49
* 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`.
51
51
*/
52
52
def isProductMatch (tp : Type , numArgs : Int , errorPos : SourcePosition = NoSourcePosition )(implicit ctx : Context ): Boolean =
53
53
numArgs > 0 && productArity(tp, errorPos) == numArgs
54
54
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
+
55
66
/** Does `tp` fit the "get match" conditions as an unapply result type?
56
67
* This is the case of `tp` has a `get` member as well as a
57
68
* parameterless `isEmpty` member of result type `Boolean`.
@@ -60,6 +71,39 @@ object Applications {
60
71
extractorMemberType(tp, nme.isEmpty, errorPos).isRef(defn.BooleanClass ) &&
61
72
extractorMemberType(tp, nme.get, errorPos).exists
62
73
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
+
63
107
def productSelectorTypes (tp : Type , errorPos : SourcePosition )(implicit ctx : Context ): List [Type ] = {
64
108
def tupleSelectors (n : Int , tp : Type ): List [Type ] = {
65
109
val sel = extractorMemberType(tp, nme.selectorName(n), errorPos)
@@ -92,57 +136,35 @@ object Applications {
92
136
else tp :: Nil
93
137
} else tp :: Nil
94
138
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
+
95
146
def unapplyArgs (unapplyResult : Type , unapplyFn : Tree , args : List [untpd.Tree ], pos : SourcePosition )(implicit ctx : Context ): List [Type ] = {
96
147
97
148
val unapplyName = unapplyFn.symbol.name
98
- def seqSelector = defn.RepeatedParamType .appliedTo(unapplyResult.elemType :: Nil )
99
149
def getTp = extractorMemberType(unapplyResult, nme.get, pos)
100
150
101
151
def fail = {
102
152
ctx.error(UnapplyInvalidReturnType (unapplyResult, unapplyName), pos)
103
153
Nil
104
154
}
105
155
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
137
161
}
138
162
139
163
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)
143
166
else fail
144
167
}
145
- else fail
146
168
}
147
169
else {
148
170
assert(unapplyName == nme.unapply)
@@ -1106,19 +1128,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1106
1128
1107
1129
var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.sourcePos)
1108
1130
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
+ }
1122
1137
if (argTypes.length != bunchedArgs.length) {
1123
1138
ctx.error(UnapplyInvalidNumberOfArguments (qual, argTypes), tree.sourcePos)
1124
1139
argTypes = argTypes.take(args.length) ++
0 commit comments