aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/PatternMatcher.scala
diff options
context:
space:
mode:
authorDmitry Petrashko <dmitry.petrashko@gmail.com>2014-09-09 15:44:56 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-09-17 18:07:16 +0200
commite5aafda8ee452ec9d3b42b8b5fa022a3124134d9 (patch)
tree2996096c46027185e5905b5e26dadea87ea2a750 /src/dotty/tools/dotc/transform/PatternMatcher.scala
parentf03bae91b08896cb4fee61e9ec18c882fab86021 (diff)
downloaddotty-e5aafda8ee452ec9d3b42b8b5fa022a3124134d9.tar.gz
dotty-e5aafda8ee452ec9d3b42b8b5fa022a3124134d9.tar.bz2
dotty-e5aafda8ee452ec9d3b42b8b5fa022a3124134d9.zip
Pattern expanders for patmat
Diffstat (limited to 'src/dotty/tools/dotc/transform/PatternMatcher.scala')
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala143
1 files changed, 141 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 9e4ec2af2..5ca66b021 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -939,7 +939,7 @@ class PatternMatcher extends MiniPhaseTransform {
trait MatchTranslator extends TreeMakers {
- def isBackquoted(x: Ident) = false //x.hasAttachment[BackquotedIdentifierAttachment.type]
+ def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent]
def isVarPattern(pat: Tree): Boolean = pat match {
case x: Ident => !isBackquoted(x) && nme.isVariableName(x.name)
@@ -954,7 +954,7 @@ class PatternMatcher extends MiniPhaseTransform {
case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
//case Star(WildcardPattern()) => true // dd todo:?
case x: Ident => isVarPattern(x)
- case Alternative(ps) => ps.forall(x => unapply(x))
+ case Alternative(ps) => ps forall unapply
case EmptyTree => true
case _ => false
}
@@ -1654,4 +1654,143 @@ class PatternMatcher extends MiniPhaseTransform {
|}""".stripMargin.trim
}
}
+
+ /** This is scalac-specific logic layered on top of the scalac-agnostic
+ * "matching products to patterns" logic defined in PatternExpander.
+ */
+ trait ScalacPatternExpanders {
+
+ type PatternAligned = ScalacPatternExpander#Aligned
+
+ implicit class AlignedOps(val aligned: PatternAligned) {
+ import aligned._
+ def expectedTypes = typedPatterns map (_.tpe)
+ def unexpandedFormals = extractor.varargsTypes
+ }
+ trait ScalacPatternExpander extends PatternExpander[Tree, Type] {
+ def NoPattern = EmptyTree
+ def NoType = NoType
+
+ def newPatterns(patterns: List[Tree]): Patterns = patterns match {
+ case init :+ last if isStar(last) => Patterns(init, last)
+ case _ => Patterns(patterns, NoPattern)
+ }
+ def elementTypeOf(tpe: Type) = {
+ val seq = repeatedToSeq(tpe)
+
+ ( typeOfMemberNamedHead(seq)
+ orElse typeOfMemberNamedApply(seq)
+ orElse definitions.elementType(ArrayClass, seq)
+ )
+ }
+ def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated): Extractor =
+ logResult(s"newExtractor($whole, $fixed, $repeated")(Extractor(whole, fixed, repeated))
+
+ // Turn Seq[A] into Repeated(Seq[A], A, A*)
+ def repeatedFromSeq(seqType: Type): Repeated = {
+ val elem = elementTypeOf(seqType)
+ val repeated = scalaRepeatedType(elem)
+
+ Repeated(seqType, elem, repeated)
+ }
+ // Turn A* into Repeated(Seq[A], A, A*)
+ def repeatedFromVarargs(repeated: Type): Repeated =
+ Repeated(repeatedToSeq(repeated), repeatedToSingle(repeated), repeated)
+
+ /** In this case we are basing the pattern expansion on a case class constructor.
+ * The argument is the MethodType carried by the primary constructor.
+ */
+ def applyMethodTypes(method: Type): Extractor = {
+ val whole = method.finalResultType
+
+ method.paramTypes match {
+ case init :+ last if isScalaRepeatedParamType(last) => newExtractor(whole, init, repeatedFromVarargs(last))
+ case tps => newExtractor(whole, tps, NoRepeated)
+ }
+ }
+
+ /** In this case, expansion is based on an unapply or unapplySeq method.
+ * Unfortunately the MethodType does not carry the information of whether
+ * it was unapplySeq, so we have to funnel that information in separately.
+ */
+ def unapplyMethodTypes(method: Type, isSeq: Boolean): Extractor = {
+ val whole = firstParamType(method)
+ val result = method.finalResultType
+ val expanded = (
+ if (result =:= BooleanTpe) Nil
+ else typeOfMemberNamedGet(result) match {
+ case rawGet if !hasSelectors(rawGet) => rawGet :: Nil
+ case rawGet => typesOfSelectors(rawGet)
+ }
+ )
+ expanded match {
+ case init :+ last if isSeq => newExtractor(whole, init, repeatedFromSeq(last))
+ case tps => newExtractor(whole, tps, NoRepeated)
+ }
+ }
+ }
+ object alignPatterns extends ScalacPatternExpander {
+ /** Converts a T => (A, B, C) extractor to a T => ((A, B, CC)) extractor.
+ */
+ def tupleExtractor(extractor: Extractor): Extractor =
+ extractor.copy(fixed = tupleType(extractor.fixed) :: Nil)
+
+ private def validateAligned(tree: Tree, aligned: Aligned): Aligned = {
+ import aligned._
+
+ def owner = tree.symbol.owner
+ def offering = extractor.offeringString
+ def symString = tree.symbol.fullLocationString
+ def offerString = if (extractor.isErroneous) "" else s" offering $offering"
+ def arityExpected = ( if (extractor.hasSeq) "at least " else "" ) + productArity
+
+ def err(msg: String) = currentUnit.error(tree.pos, msg)
+ def warn(msg: String) = currentUnit.warning(tree.pos, msg)
+ def arityError(what: String) = err(s"$what patterns for $owner$offerString: expected $arityExpected, found $totalArity")
+
+ if (isStar && !isSeq)
+ err("Star pattern must correspond with varargs or unapplySeq")
+ else if (elementArity < 0)
+ arityError("not enough")
+ else if (elementArity > 0 && !extractor.hasSeq)
+ arityError("too many")
+
+ aligned
+ }
+
+ def apply(sel: Tree, args: List[Tree]): Aligned = {
+ val fn = sel match {
+ case Unapplied(fn) => fn
+ case _ => sel
+ }
+ val patterns = newPatterns(args)
+ val isSeq = sel.symbol.name == nme.unapplySeq
+ val isUnapply = sel.symbol.name == nme.unapply
+ val extractor = sel.symbol.name match {
+ case nme.unapply => unapplyMethodTypes(fn.tpe, isSeq = false)
+ case nme.unapplySeq => unapplyMethodTypes(fn.tpe, isSeq = true)
+ case _ => applyMethodTypes(fn.tpe)
+ }
+
+ /** Rather than let the error that is SI-6675 pollute the entire matching
+ * process, we will tuple the extractor before creation Aligned so that
+ * it contains known good values.
+ */
+ def productArity = extractor.productArity
+ def acceptMessage = if (extractor.isErroneous) "" else s" to hold ${extractor.offeringString}"
+ val requiresTupling = isUnapply && patterns.totalArity == 1 && productArity > 1
+
+ if (requiresTupling && effectivePatternArity(args) == 1)
+ currentUnit.deprecationWarning(sel.pos, s"${sel.symbol.owner} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)")
+
+ val normalizedExtractor = if (requiresTupling) tupleExtractor(extractor) else extractor
+ validateAligned(fn, Aligned(patterns, normalizedExtractor))
+ }
+
+ def apply(tree: Tree): Aligned = tree match {
+ case Apply(fn, args) => apply(fn, args)
+ case UnApply(fn, args) => apply(fn, args)
+ }
+ }
+ }
} \ No newline at end of file