aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/Applications.scala
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-12-16 10:53:13 +0100
committerGitHub <noreply@github.com>2016-12-16 10:53:13 +0100
commit8643876e31d6668555cf903c0a21ee49122f4f17 (patch)
treeeb3523d93595008b2741117d92a9ff1d37d950fd /compiler/src/dotty/tools/dotc/typer/Applications.scala
parenta5620ab882b95ef60acf42814ac5568d5f93bdd4 (diff)
parentb1553cb9c5894e2e91925a67afd2c986675e5c46 (diff)
downloaddotty-8643876e31d6668555cf903c0a21ee49122f4f17.tar.gz
dotty-8643876e31d6668555cf903c0a21ee49122f4f17.tar.bz2
dotty-8643876e31d6668555cf903c0a21ee49122f4f17.zip
Merge pull request #1801 from dotty-staging/fix-#1790
Fix #1790: Change by-name pattern matching.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/Applications.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala70
1 files changed, 52 insertions, 18 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
index a3b4cb311..4203ab9b2 100644
--- a/compiler/src/dotty/tools/dotc/typer/Applications.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -32,16 +32,37 @@ import reporting.diagnostic.Message
object Applications {
import tpd._
+ def extractorMember(tp: Type, name: Name)(implicit ctx: Context) = {
+ def isPossibleExtractorType(tp: Type) = tp match {
+ case _: MethodType | _: PolyType => false
+ case _ => true
+ }
+ tp.member(name).suchThat(d => isPossibleExtractorType(d.info))
+ }
+
def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx: Context) = {
- val ref = tp.member(name).suchThat(_.info.isParameterless)
+ val ref = extractorMember(tp, name)
if (ref.isOverloaded)
errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos)
- else if (ref.info.isInstanceOf[PolyType])
- errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", errorPos)
- else
- ref.info.widenExpr.dealias
+ ref.info.widenExpr.dealias
}
+ /** Does `tp` fit the "product match" conditions as an unapply result type
+ * for a pattern with `numArgs` subpatterns>
+ * This is the case of `tp` is a subtype of the Product<numArgs> class.
+ */
+ def isProductMatch(tp: Type, numArgs: Int)(implicit ctx: Context) =
+ 0 <= numArgs && numArgs <= Definitions.MaxTupleArity &&
+ tp.derivesFrom(defn.ProductNType(numArgs).typeSymbol)
+
+ /** Does `tp` fit the "get match" conditions as an unapply result type?
+ * This is the case of `tp` has a `get` member as well as a
+ * parameterless `isDefined` member of result type `Boolean`.
+ */
+ def isGetMatch(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context) =
+ extractorMemberType(tp, nme.isEmpty, errorPos).isRef(defn.BooleanClass) &&
+ extractorMemberType(tp, nme.get, errorPos).exists
+
def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = {
val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos)
sels.takeWhile(_.exists).toList
@@ -61,24 +82,37 @@ object Applications {
def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] = {
+ val unapplyName = unapplyFn.symbol.name
def seqSelector = defn.RepeatedParamType.appliedTo(unapplyResult.elemType :: Nil)
def getTp = extractorMemberType(unapplyResult, nme.get, pos)
- // println(s"unapply $unapplyResult ${extractorMemberType(unapplyResult, nme.isDefined)}")
- if (extractorMemberType(unapplyResult, nme.isDefined, pos) isRef defn.BooleanClass) {
- if (getTp.exists)
- if (unapplyFn.symbol.name == nme.unapplySeq) {
- val seqArg = boundsToHi(getTp.elemType)
- if (seqArg.exists) return args map Function.const(seqArg)
- }
- else return getUnapplySelectors(getTp, args, pos)
- else if (defn.isProductSubType(unapplyResult)) return productSelectorTypes(unapplyResult, pos)
+ def fail = {
+ ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos)
+ Nil
+ }
+
+ if (unapplyName == nme.unapplySeq) {
+ if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil
+ else if (isGetMatch(unapplyResult, pos)) {
+ val seqArg = boundsToHi(getTp.elemType)
+ if (seqArg.exists) args.map(Function.const(seqArg))
+ else fail
+ }
+ else fail
}
- if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil
- else if (unapplyResult isRef defn.BooleanClass) Nil
else {
- ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", pos)
- Nil
+ assert(unapplyName == nme.unapply)
+ if (isProductMatch(unapplyResult, args.length))
+ productSelectorTypes(unapplyResult)
+ else if (isGetMatch(unapplyResult, pos))
+ getUnapplySelectors(getTp, args, pos)
+ else if (unapplyResult isRef defn.BooleanClass)
+ Nil
+ else if (defn.isProductSubType(unapplyResult))
+ productSelectorTypes(unapplyResult)
+ // this will cause a "wrong number of arguments in pattern" error later on,
+ // which is better than the message in `fail`.
+ else fail
}
}