summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala164
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala78
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala155
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala154
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala146
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala54
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala4
-rw-r--r--test/files/neg/t4425.check6
-rw-r--r--test/files/neg/t4425b.check42
-rw-r--r--test/files/neg/t5903a.check2
-rw-r--r--test/files/neg/t6675b.check37
-rw-r--r--test/files/neg/t6675b.flags1
-rw-r--r--test/files/neg/t6675b.scala40
-rw-r--r--test/files/neg/t7214neg.check7
-rw-r--r--test/files/neg/t7850.check7
-rw-r--r--test/files/neg/t7850.scala16
-rw-r--r--test/files/neg/t7897.check4
-rw-r--r--test/files/neg/t7897.scala23
-rw-r--r--test/files/neg/t997.check7
-rw-r--r--test/files/pos/t8045.scala17
-rw-r--r--test/files/pos/t8128.scala15
-rw-r--r--test/files/run/name-based-patmat.check2
-rw-r--r--test/files/run/name-based-patmat.scala42
-rw-r--r--test/files/run/patmat-mix-case-extractor.check8
-rw-r--r--test/files/run/patmat-mix-case-extractor.scala110
-rw-r--r--test/pending/pos/t8128b.scala18
30 files changed, 825 insertions, 355 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 844774e75f..ef50ae276f 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -457,12 +457,11 @@ abstract class UnCurry extends InfoTransform
else
super.transform(tree)
case UnApply(fn, args) =>
- val fn1 = transform(fn)
- val args1 = transformTrees(fn.symbol.name match {
- case nme.unapply => args
- case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, localTyper.expectedPatternTypes(fn, args))
- case _ => sys.error("internal error: UnApply node has wrong symbol")
- })
+ val fn1 = transform(fn)
+ val args1 = fn.symbol.name match {
+ case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, patmat.alignPatterns(tree).expectedTypes)
+ case _ => args
+ }
treeCopy.UnApply(tree, fn1, args1)
case Apply(fn, args) =>
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index 63f4a4bf25..699e98f963 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -31,6 +31,30 @@ trait MatchTranslation {
trait MatchTranslator extends TreeMakers with TreeMakerWarnings {
import typer.context
+ /** A conservative approximation of which patterns do not discern anything.
+ * They are discarded during the translation.
+ */
+ object WildcardPattern {
+ def unapply(pat: Tree): Boolean = pat match {
+ case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
+ case Star(WildcardPattern()) => true
+ case x: Ident => treeInfo.isVarPattern(x)
+ case Alternative(ps) => ps forall unapply
+ case EmptyTree => true
+ case _ => false
+ }
+ }
+
+ object PatternBoundToUnderscore {
+ def unapply(pat: Tree): Boolean = pat match {
+ case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol!
+ case Ident(nme.WILDCARD) => true
+ case Alternative(ps) => ps forall unapply
+ case Typed(PatternBoundToUnderscore(), _) => true
+ case _ => false
+ }
+ }
+
object SymbolBound {
def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match {
case Bind(_, expr) if hasSym(tree) => Some(tree.symbol -> expr)
@@ -86,10 +110,8 @@ trait MatchTranslation {
// example check: List[Int] <:< ::[Int]
private def extractorStep(): TranslationStep = {
- import extractor.{ paramType, treeMaker }
- if (!extractor.isTyped)
- ErrorUtils.issueNormalTypeError(tree, "Could not typecheck extractor call: "+ extractor)(context)
-
+ def paramType = extractor.aligner.wholeType
+ import extractor.treeMaker
// chain a type-testing extractor before the actual extractor call
// it tests the type, checks the outer pointer and casts to the expected type
// TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC]
@@ -355,36 +377,20 @@ trait MatchTranslation {
object ExtractorCall {
// TODO: check unargs == args
def apply(tree: Tree): ExtractorCall = tree match {
- case UnApply(unfun, args) => new ExtractorCallRegular(unfun, args) // extractor
- case Apply(fun, args) => new ExtractorCallProd(fun, args) // case class
+ case UnApply(unfun, args) => new ExtractorCallRegular(alignPatterns(tree), unfun, args) // extractor
+ case Apply(fun, args) => new ExtractorCallProd(alignPatterns(tree), fun, args) // case class
}
}
- abstract class ExtractorCall {
+ abstract class ExtractorCall(val aligner: PatternAligned) {
+ import aligner._
def fun: Tree
def args: List[Tree]
- val nbSubPats = args.length
- val starLength = if (hasStar) 1 else 0
- val nonStarLength = args.length - starLength
-
- // everything okay, captain?
- def isTyped: Boolean
- def isSeq: Boolean
-
- private def hasStar = nbSubPats > 0 && isStar(args.last)
- private def isNonEmptySeq = nbSubPats > 0 && isSeq
-
- /** This is special cased so that a single pattern will accept any extractor
- * result, even if it's a tuple (SI-6675)
- */
- def isSingle = nbSubPats == 1 && !isSeq
-
- // to which type should the previous binder be casted?
- def paramType : Type
-
- protected def rawSubPatTypes: List[Type]
- protected def resultType: Type
+ // don't go looking for selectors if we only expect one pattern
+ def rawSubPatTypes = aligner.extractedTypes
+ def resultInMonad = if (isBool) UnitTpe else typeOfMemberNamedGet(resultType)
+ def resultType = fun.tpe.finalResultType
/** Create the TreeMaker that embodies this extractor call
*
@@ -407,24 +413,14 @@ trait MatchTranslation {
lazy val ignoredSubPatBinders: Set[Symbol] = subPatBinders zip args collect { case (b, PatternBoundToUnderscore()) => b } toSet
// do repeated-parameter expansion to match up with the expected number of arguments (in casu, subpatterns)
- private def nonStarSubPatTypes = formalTypes(rawInit :+ repeatedType, nonStarLength)
+ private def nonStarSubPatTypes = aligner.typedNonStarPatterns map (_.tpe)
- def subPatTypes: List[Type] = (
- if (rawSubPatTypes.isEmpty || !isSeq) rawSubPatTypes
- else if (hasStar) nonStarSubPatTypes :+ sequenceType
- else nonStarSubPatTypes
- )
-
- private def rawGet = typeOfMemberNamedGetOrSelf(resultType)
- private def rawInit = rawSubPatTypes dropRight 1
- protected def sequenceType = typeOfLastSelectorOrSelf(rawGet)
- protected def elementType = elementTypeOfLastSelectorOrSelf(rawGet)
- protected def repeatedType = scalaRepeatedType(elementType)
+ def subPatTypes: List[Type] = typedPatterns map (_.tpe)
- // rawSubPatTypes.last is the Seq, thus there are `rawSubPatTypes.length - 1` non-seq elements in the tuple
- protected def firstIndexingBinder = rawSubPatTypes.length - 1
- protected def lastIndexingBinder = nbSubPats - 1 - starLength
- protected def expectedLength = lastIndexingBinder - firstIndexingBinder + 1
+ // there are `productArity` non-seq elements in the tuple.
+ protected def firstIndexingBinder = productArity
+ protected def expectedLength = elementArity
+ protected def lastIndexingBinder = totalArity - starArity - 1
private def productElemsToN(binder: Symbol, n: Int): List[Tree] = 1 to n map tupleSel(binder) toList
private def genTake(binder: Symbol, n: Int): List[Tree] = (0 until n).toList map (codegen index seqTree(binder))
@@ -438,12 +434,12 @@ trait MatchTranslation {
// referenced by `binder`
protected def subPatRefsSeq(binder: Symbol): List[Tree] = {
def lastTrees: List[Tree] = (
- if (!hasStar) Nil
+ if (!aligner.isStar) Nil
else if (expectedLength == 0) seqTree(binder) :: Nil
else genDrop(binder, expectedLength)
)
// this error-condition has already been checked by checkStarPatOK:
- // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
+ // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == totalArity, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
// [1] there are `firstIndexingBinder` non-seq tuple elements preceding the Seq
// [2] then we have to index the binder that represents the sequence for the remaining subpatterns, except for...
@@ -457,8 +453,10 @@ trait MatchTranslation {
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
// require (nbSubPats > 0 && (!lastIsStar || isSeq))
- protected def subPatRefs(binder: Symbol): List[Tree] =
- if (isNonEmptySeq) subPatRefsSeq(binder) else productElemsToN(binder, nbSubPats)
+ protected def subPatRefs(binder: Symbol): List[Tree] = (
+ if (totalArity > 0 && isSeq) subPatRefsSeq(binder)
+ else productElemsToN(binder, totalArity)
+ )
private def compareInts(t1: Tree, t2: Tree) =
gen.mkMethodCall(termMember(ScalaPackage, "math"), TermName("signum"), Nil, (t1 INT_- t2) :: Nil)
@@ -478,7 +476,7 @@ trait MatchTranslation {
// when the last subpattern is a wildcard-star the expectedLength is but a lower bound
// (otherwise equality is required)
def compareOp: (Tree, Tree) => Tree =
- if (hasStar) _ INT_>= _
+ if (aligner.isStar) _ INT_>= _
else _ INT_== _
// `if (binder != null && $checkExpectedLength [== | >=] 0) then else zero`
@@ -487,26 +485,14 @@ trait MatchTranslation {
def checkedLength: Option[Int] =
// no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied
- if (!isSeq || expectedLength < starLength) None
+ if (!isSeq || expectedLength < starArity) None
else Some(expectedLength)
}
// TODO: to be called when there's a def unapplyProd(x: T): U
// U must have N members _1,..., _N -- the _i are type checked, call their type Ti,
// for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it)
- class ExtractorCallProd(val fun: Tree, val args: List[Tree]) extends ExtractorCall {
- private def constructorTp = fun.tpe
-
- def isTyped = fun.isTyped
-
- // to which type should the previous binder be casted?
- def paramType = constructorTp.finalResultType
- def resultType = fun.tpe.finalResultType
-
- def isSeq = isVarArgTypes(rawSubPatTypes)
-
- protected def rawSubPatTypes = constructorTp.paramTypes
-
+ class ExtractorCallProd(aligner: PatternAligned, val fun: Tree, val args: List[Tree]) extends ExtractorCall(aligner) {
/** Create the TreeMaker that embodies this extractor call
*
* `binder` has been casted to `paramType` if necessary
@@ -535,20 +521,11 @@ trait MatchTranslation {
if (accessors isDefinedAt (i-1)) REF(binder) DOT accessors(i-1)
else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN
}
-
- override def toString() = s"ExtractorCallProd($fun:${fun.tpe} / ${fun.symbol} / args=$args)"
}
- class ExtractorCallRegular(extractorCallIncludingDummy: Tree, val args: List[Tree]) extends ExtractorCall {
+ class ExtractorCallRegular(aligner: PatternAligned, extractorCallIncludingDummy: Tree, val args: List[Tree]) extends ExtractorCall(aligner) {
val Unapplied(fun) = extractorCallIncludingDummy
- def tpe = fun.tpe
- def paramType = firstParamType(tpe)
- def resultType = tpe.finalResultType
- def isTyped = (tpe ne NoType) && fun.isTyped && (resultInMonad ne ErrorType)
- def isSeq = fun.symbol.name == nme.unapplySeq
- def isBool = resultType =:= BooleanTpe
-
/** Create the TreeMaker that embodies this extractor call
*
* `binder` has been casted to `paramType` if necessary
@@ -571,7 +548,7 @@ trait MatchTranslation {
ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(
subPatBinders,
subPatRefs(binder),
- isBool,
+ aligner.isBool,
checkedLength,
patBinderOrCasted,
ignoredSubPatBinders
@@ -583,9 +560,9 @@ trait MatchTranslation {
else super.seqTree(binder)
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
- // require (nbSubPats > 0 && (!lastIsStar || isSeq))
+ // require (totalArity > 0 && (!lastIsStar || isSeq))
override protected def subPatRefs(binder: Symbol): List[Tree] =
- if (isSingle) REF(binder) :: Nil // special case for extractors
+ if (aligner.isSingle) REF(binder) :: Nil // special case for extractors
else super.subPatRefs(binder)
protected def spliceApply(binder: Symbol): Tree = {
@@ -606,40 +583,7 @@ trait MatchTranslation {
splice transform extractorCallIncludingDummy
}
- // what's the extractor's result type in the monad? It is the type of its nullary member `get`.
- protected lazy val resultInMonad: Type = if (isBool) UnitTpe else typeOfMemberNamedGet(resultType)
-
- protected lazy val rawSubPatTypes = (
- if (isBool) Nil
- else if (isSingle) resultInMonad :: Nil // don't go looking for selectors if we only expect one pattern
- else typesOfSelectorsOrSelf(resultInMonad)
- )
-
- override def toString() = s"ExtractorCallRegular($fun: $tpe / ${fun.symbol})"
- }
-
- /** A conservative approximation of which patterns do not discern anything.
- * They are discarded during the translation.
- */
- object WildcardPattern {
- def unapply(pat: Tree): Boolean = pat match {
- case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
- case Star(WildcardPattern()) => true
- case x: Ident => treeInfo.isVarPattern(x)
- case Alternative(ps) => ps forall unapply
- case EmptyTree => true
- case _ => false
- }
- }
-
- object PatternBoundToUnderscore {
- def unapply(pat: Tree): Boolean = pat match {
- case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol!
- case Ident(nme.WILDCARD) => true
- case Alternative(ps) => ps forall unapply
- case Typed(PatternBoundToUnderscore(), _) => true
- case _ => false
- }
+ override def rawSubPatTypes = aligner.extractor.varargsTypes
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 7df03044aa..a80f158949 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -395,8 +395,10 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
debug.patmat("TTTM"+((prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp)))
lazy val outerTestNeeded = (
- !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass)
- && needsOuterTest(expectedTp, testedBinder.info, matchOwner))
+ (expectedTp.prefix ne NoPrefix)
+ && !expectedTp.prefix.typeSymbol.isPackageClass
+ && needsOuterTest(expectedTp, testedBinder.info, matchOwner)
+ )
// the logic to generate the run-time test that follows from the fact that
// a `prevBinder` is expected to have type `expectedTp`
@@ -406,44 +408,52 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
def renderCondition(cs: TypeTestCondStrategy): cs.Result = {
import cs._
- def default =
- // do type test first to ensure we won't select outer on null
- if (outerTestNeeded) and(typeTest(testedBinder, expectedTp), outerTest(testedBinder, expectedTp))
- else typeTest(testedBinder, expectedTp)
-
// propagate expected type
def expTp(t: Tree): t.type = t setType expectedTp
+ def testedWide = testedBinder.info.widen
+ def expectedWide = expectedTp.widen
+ def isAnyRef = testedWide <:< AnyRefTpe
+ def isAsExpected = testedWide <:< expectedTp
+ def isExpectedPrimitiveType = isAsExpected && isPrimitiveValueType(expectedTp)
+ def isExpectedReferenceType = isAsExpected && (expectedTp <:< AnyRefTpe)
+ def mkNullTest = nonNullTest(testedBinder)
+ def mkOuterTest = outerTest(testedBinder, expectedTp)
+ def mkTypeTest = typeTest(testedBinder, expectedWide)
+
+ def mkEqualsTest(lhs: Tree): cs.Result = equalsTest(lhs, testedBinder)
+ def mkEqTest(lhs: Tree): cs.Result = eqTest(lhs, testedBinder)
+ def addOuterTest(res: cs.Result): cs.Result = if (outerTestNeeded) and(res, mkOuterTest) else res
+
+ // If we conform to expected primitive type:
+ // it cannot be null and cannot have an outer pointer. No further checking.
+ // If we conform to expected reference type:
+ // have to test outer and non-null
+ // If we do not conform to expected type:
+ // have to test type and outer (non-null is implied by successful type test)
+ def mkDefault = (
+ if (isExpectedPrimitiveType) tru
+ else addOuterTest(
+ if (isExpectedReferenceType) mkNullTest
+ else mkTypeTest
+ )
+ )
+
// true when called to type-test the argument to an extractor
// don't do any fancy equality checking, just test the type
- if (extractorArgTypeTest) default
+ // TODO: verify that we don't need to special-case Array
+ // I think it's okay:
+ // - the isInstanceOf test includes a test for the element type
+ // - Scala's arrays are invariant (so we don't drop type tests unsoundly)
+ if (extractorArgTypeTest) mkDefault
else expectedTp match {
- // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types
- // this implies sym.isStable
- case SingleType(_, sym) => and(equalsTest(gen.mkAttributedQualifier(expectedTp), testedBinder), typeTest(testedBinder, expectedTp.widen))
- // must use == to support e.g. List() == Nil
- case ThisType(sym) if sym.isModule => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen))
- case ConstantType(Constant(null)) if testedBinder.info.widen <:< AnyRefTpe
- => eqTest(expTp(CODE.NULL), testedBinder)
- case ConstantType(const) => equalsTest(expTp(Literal(const)), testedBinder)
- case ThisType(sym) => eqTest(expTp(This(sym)), testedBinder)
-
- // TODO: verify that we don't need to special-case Array
- // I think it's okay:
- // - the isInstanceOf test includes a test for the element type
- // - Scala's arrays are invariant (so we don't drop type tests unsoundly)
- case _ if testedBinder.info.widen <:< expectedTp =>
- // if the expected type is a primitive value type, it cannot be null and it cannot have an outer pointer
- // since the types conform, no further checking is required
- if (isPrimitiveValueType(expectedTp)) tru
- // have to test outer and non-null only when it's a reference type
- else if (expectedTp <:< AnyRefTpe) {
- // do non-null check first to ensure we won't select outer on null
- if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp))
- else nonNullTest(testedBinder)
- } else default
-
- case _ => default
+ // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types - this implies sym.isStable
+ case SingleType(_, sym) => and(mkEqualsTest(gen.mkAttributedQualifier(expectedTp)), mkTypeTest)
+ case ThisType(sym) if sym.isModule => and(mkEqualsTest(CODE.REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil
+ case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(CODE.NULL))
+ case ConstantType(const) => mkEqualsTest(expTp(Literal(const)))
+ case ThisType(sym) => mkEqTest(expTp(This(sym)))
+ case _ => mkDefault
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala
new file mode 100644
index 0000000000..e84ccbf754
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala
@@ -0,0 +1,155 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala
+package tools
+package nsc
+package transform
+package patmat
+
+/** An extractor returns: F1, F2, ..., Fi, opt[Seq[E] or E*]
+ * A case matches: P1, P2, ..., Pj, opt[Seq[E]]
+ * Put together: P1/F1, P2/F2, ... Pi/Fi, Pi+1/E, Pi+2/E, ... Pj/E, opt[Seq[E]]
+ *
+ * Here Pm/Fi is the last pattern to match the fixed arity section.
+ *
+ * productArity: the value of i, i.e. the number of non-sequence types in the extractor
+ * nonStarArity: the value of j, i.e. the number of non-star patterns in the case definition
+ * elementArity: j - i, i.e. the number of non-star patterns which must match sequence elements
+ * starArity: 1 or 0 based on whether there is a star (sequence-absorbing) pattern
+ * totalArity: nonStarArity + starArity, i.e. the number of patterns in the case definition
+ *
+ * Note that productArity is a function only of the extractor, and
+ * nonStar/star/totalArity are all functions of the patterns. The key
+ * value for aligning and typing the patterns is elementArity, as it
+ * is derived from both sets of information.
+ */
+trait PatternExpander[Pattern, Type] {
+ /** You'll note we're not inside the cake. "Pattern" and "Type" are
+ * arbitrary types here, and NoPattern and NoType arbitrary values.
+ */
+ def NoPattern: Pattern
+ def NoType: Type
+
+ /** It's not optimal that we're carrying both sequence and repeated
+ * type here, but the implementation requires more unraveling before
+ * it can be avoided.
+ *
+ * sequenceType is Seq[T], elementType is T, repeatedType is T*.
+ */
+ sealed case class Repeated(sequenceType: Type, elementType: Type, repeatedType: Type) {
+ def exists = elementType != NoType
+
+ def elementList = if (exists) elementType :: Nil else Nil
+ def sequenceList = if (exists) sequenceType :: Nil else Nil
+ def repeatedList = if (exists) repeatedType :: Nil else Nil
+
+ override def toString = s"${elementType}*"
+ }
+ object NoRepeated extends Repeated(NoType, NoType, NoType) {
+ override def toString = "<none>"
+ }
+
+ final case class Patterns(fixed: List[Pattern], star: Pattern) {
+ def hasStar = star != NoPattern
+ def starArity = if (hasStar) 1 else 0
+ def nonStarArity = fixed.length
+ def totalArity = nonStarArity + starArity
+ def starPatterns = if (hasStar) star :: Nil else Nil
+ def all = fixed ::: starPatterns
+
+ override def toString = all mkString ", "
+ }
+
+ /** An 'extractor' can be a case class or an unapply or unapplySeq method.
+ * Decoding what it is that they extract takes place before we arrive here,
+ * so that this class can concentrate only on the relationship between
+ * patterns and types.
+ *
+ * In a case class, the class is the unextracted type and the fixed and
+ * repeated types are derived from its constructor parameters.
+ *
+ * In an unapply, this is reversed: the parameter to the unapply is the
+ * unextracted type, and the other types are derived based on the return
+ * type of the unapply method.
+ *
+ * In other words, this case class and unapply are encoded the same:
+ *
+ * case class Foo(x: Int, y: Int, zs: Char*)
+ * def unapplySeq(x: Foo): Option[(Int, Int, Seq[Char])]
+ *
+ * Both are Extractor(Foo, Int :: Int :: Nil, Repeated(Seq[Char], Char, Char*))
+ *
+ * @param whole The type in its unextracted form
+ * @param fixed The non-sequence types which are extracted
+ * @param repeated The sequence type which is extracted
+ */
+ final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated) {
+ require(whole != NoType, s"expandTypes($whole, $fixed, $repeated)")
+
+ def productArity = fixed.length
+ def hasSeq = repeated.exists
+ def elementType = repeated.elementType
+ def sequenceType = repeated.sequenceType
+ def allTypes = fixed ::: repeated.sequenceList
+ def varargsTypes = fixed ::: repeated.repeatedList
+ def isErroneous = allTypes contains NoType
+
+ private def typeStrings = fixed.map("" + _) ::: ( if (hasSeq) List("" + repeated) else Nil )
+
+ def offeringString = if (isErroneous) "<error>" else typeStrings match {
+ case Nil => "Boolean"
+ case tp :: Nil => tp
+ case tps => tps.mkString("(", ", ", ")")
+ }
+ override def toString = "%s => %s".format(whole, offeringString)
+ }
+
+ final case class TypedPat(pat: Pattern, tpe: Type) {
+ override def toString = s"$pat: $tpe"
+ }
+
+ /** If elementArity is...
+ * 0: A perfect match between extractor and the fixed patterns.
+ * If there is a star pattern it will match any sequence.
+ * > 0: There are more patterns than products. There will have to be a
+ * sequence which can populate at least <elementArity> patterns.
+ * < 0: There are more products than patterns: compile time error.
+ */
+ final case class Aligned(patterns: Patterns, extractor: Extractor) {
+ def elementArity = patterns.nonStarArity - productArity
+ def productArity = extractor.productArity
+ def starArity = patterns.starArity
+ def totalArity = patterns.totalArity
+
+ def wholeType = extractor.whole
+ def sequenceType = extractor.sequenceType
+ def productTypes = extractor.fixed
+ def extractedTypes = extractor.allTypes
+ def typedNonStarPatterns = products ::: elements
+ def typedPatterns = typedNonStarPatterns ::: stars
+
+ def isBool = !isSeq && productArity == 0
+ def isSingle = !isSeq && totalArity == 1
+ def isStar = patterns.hasStar
+ def isSeq = extractor.hasSeq
+
+ private def typedAsElement(pat: Pattern) = TypedPat(pat, extractor.elementType)
+ private def typedAsSequence(pat: Pattern) = TypedPat(pat, extractor.sequenceType)
+ private def productPats = patterns.fixed take productArity
+ private def elementPats = patterns.fixed drop productArity
+ private def products = (productPats, productTypes).zipped map TypedPat
+ private def elements = elementPats map typedAsElement
+ private def stars = patterns.starPatterns map typedAsSequence
+
+ override def toString = s"""
+ |Aligned {
+ | patterns $patterns
+ | extractor $extractor
+ | arities $productArity/$elementArity/$starArity // product/element/star
+ | typed ${typedPatterns mkString ", "}
+ |}""".stripMargin.trim
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
index 394ba98f17..f6c960d089 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
@@ -34,7 +34,8 @@ import scala.reflect.internal.util.Position
* - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?)
* - recover exhaustivity/unreachability of user-defined extractors by partitioning the types they match on using an HList or similar type-level structure
*/
-trait PatternMatching extends Transform with TypingTransformers
+trait PatternMatching extends Transform
+ with TypingTransformers
with Debugging
with Interface
with MatchTranslation
@@ -45,7 +46,8 @@ trait PatternMatching extends Transform with TypingTransformers
with Solving
with MatchAnalysis
with MatchOptimization
- with MatchWarnings {
+ with MatchWarnings
+ with ScalacPatternExpanders {
import global._
val phaseName: String = "patmat"
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
new file mode 100644
index 0000000000..7858cb5586
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
@@ -0,0 +1,154 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala
+package tools
+package nsc
+package transform
+package patmat
+
+/** This is scalac-specific logic layered on top of the scalac-agnostic
+ * "matching products to patterns" logic defined in PatternExpander.
+ */
+trait ScalacPatternExpanders {
+ val global: Global
+
+ import global._
+ import definitions._
+ import treeInfo._
+
+ 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 = global.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 (settings.lint && requiresTupling && effectivePatternArity(args) == 1)
+ currentUnit.warning(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)
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index cd6b77404d..4d0eda2377 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -625,8 +625,7 @@ trait ContextErrors {
setError(tree)
}
- def CaseClassConstructorError(tree: Tree) = {
- val baseMessage = tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method"
+ def CaseClassConstructorError(tree: Tree, baseMessage: String) = {
val addendum = directUnapplyMember(tree.symbol.info) match {
case sym if hasMultipleNonImplicitParamLists(sym) => s"\nNote: ${sym.defString} exists in ${tree.symbol}, but it cannot be used as an extractor due to its second non-implicit parameter list"
case _ => ""
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index 069d6d5fb2..41c656f8ce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -78,26 +78,34 @@ trait PatternTypers {
// Do some ad-hoc overloading resolution and update the tree's symbol and type
// do not update the symbol if the tree's symbol's type does not define an unapply member
// (e.g. since it's some method that returns an object with an unapply member)
- val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
- def caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
+ val caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val member = unapplyMember(fun.tpe)
+ def resultType = (fun.tpe memberType member).finalResultType
+ def isEmptyType = resultOfMatchingMethod(resultType, nme.isEmpty)()
+ def isOkay = (
+ resultType.isErroneous
+ || (resultType <:< BooleanTpe)
+ || (isEmptyType <:< BooleanTpe)
+ || member.isMacro
+ || member.isOverloaded // the whole overloading situation is over the rails
+ )
// Dueling test cases: pos/overloaded-unapply.scala, run/case-class-23.scala, pos/t5022.scala
// A case class with 23+ params has no unapply method.
- // A case class constructor be overloaded with unapply methods in the companion.
- if (caseClass.isCase && !unapplyMember(fun.tpe).isOverloaded)
+ // A case class constructor may be overloaded with unapply methods in the companion.
+ if (caseClass.isCase && !member.isOverloaded)
logResult(s"convertToCaseConstructor($fun, $caseClass, pt=$pt)")(convertToCaseConstructor(fun, caseClass, pt))
- else if (hasUnapplyMember(fun))
+ else if (!reallyExists(member))
+ CaseClassConstructorError(fun, s"${fun.symbol} is not a case class, nor does it have an unapply/unapplySeq member")
+ else if (isOkay)
fun
+ else if (isEmptyType == NoType)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean")
else
- CaseClassConstructorError(fun)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean (found: def isEmpty: $isEmptyType)")
}
- def expectedPatternTypes(fun: Tree, args: List[Tree]): List[Type] =
- newExtractorShape(fun, args).expectedPatternTypes
-
- def typedPatternArgs(fun: Tree, args: List[Tree], mode: Mode): List[Tree] =
- typedArgsForFormals(args, newExtractorShape(fun, args).formals, mode)
-
def typedArgsForFormals(args: List[Tree], formals: List[Type], mode: Mode): List[Tree] = {
def typedArgWithFormal(arg: Tree, pt: Type) = {
val newMode = if (isByNameParamType(pt)) mode.onlySticky else mode.onlySticky | BYVALmode
@@ -158,109 +166,6 @@ trait PatternTypers {
case _ => wrapClassTagUnapply(treeTyped, extractor, tpe)
}
}
-
- def newExtractorShape(fun: Tree, args: List[Tree]): ExtractorShape = ExtractorShape(fun, args)
-
- case class CaseClassInfo(clazz: Symbol, classType: Type) {
- def constructor = clazz.primaryConstructor
- def constructorType = classType.prefix memberType clazz memberType constructor
- def accessors = clazz.caseFieldAccessors
- }
- object NoCaseClassInfo extends CaseClassInfo(NoSymbol, NoType) {
- override def toString = "NoCaseClassInfo"
- }
-
- case class UnapplyMethodInfo(unapply: Symbol, tpe: Type) {
- def name = unapply.name
- def isUnapplySeq = name == nme.unapplySeq
- def unapplyType = tpe memberType method
- def resultType = tpe.finalResultType
- def method = unapplyMember(tpe)
- def paramType = firstParamType(unapplyType)
- def rawGet = if (isBool) UnitTpe else typeOfMemberNamedGetOrSelf(resultType)
- def rawTypes = if (isBool) Nil else typesOfSelectorsOrSelf(rawGet)
- def isBool = resultType =:= BooleanTpe // aka "Tuple0" or "Option[Unit]"
- }
-
- object NoUnapplyMethodInfo extends UnapplyMethodInfo(NoSymbol, NoType) {
- override def toString = "NoUnapplyMethodInfo"
- }
-
- case class ExtractorShape(fun: Tree, args: List[Tree]) {
- def pos = fun.pos
- private def symbol = fun.symbol
- private def tpe = fun.tpe
-
- val ccInfo = tpe.typeSymbol.linkedClassOfClass match {
- case clazz if clazz.isCase => CaseClassInfo(clazz, tpe)
- case _ => NoCaseClassInfo
- }
- val exInfo = UnapplyMethodInfo(symbol, tpe)
- import exInfo.{ rawGet, rawTypes, isUnapplySeq }
-
- override def toString = s"ExtractorShape($fun, $args)"
-
- def unapplyMethod = exInfo.method
- def unapplyType = exInfo.unapplyType
- def unapplyParamType = exInfo.paramType
- def enclClass = symbol.enclClass
-
- // TODO - merge these. The difference between these two methods is that expectedPatternTypes
- // expands the list of types so it is the same length as the number of patterns, whereas formals
- // leaves the varargs type unexpanded.
- def formals = (
- if (isUnapplySeq) productTypes :+ varargsType
- else if (elementArity == 0) productTypes
- else if (isSingle) squishIntoOne()
- else wrongArity(patternFixedArity)
- )
- def expectedPatternTypes = elementArity match {
- case 0 => productTypes
- case _ if elementArity > 0 && isUnapplySeq => productTypes ::: elementTypes
- case _ if productArity > 1 && patternFixedArity == 1 => squishIntoOne()
- case _ => wrongArity(patternFixedArity)
- }
-
- def elementType = elementTypeOfLastSelectorOrSelf(rawGet)
-
- private def hasBogusExtractor = directUnapplyMember(tpe).exists && !unapplyMethod.exists
- private def expectedArity = "" + productArity + ( if (isUnapplySeq) "+" else "")
- private def wrongArityMsg(n: Int) = (
- if (hasBogusExtractor) s"$enclClass does not define a valid extractor method"
- else s"wrong number of patterns for $enclClass offering $rawTypes_s: expected $expectedArity, found $n"
- )
- private def rawTypes_s = rawTypes match {
- case Nil => "()"
- case tp :: Nil => "" + tp
- case tps => tps.mkString("(", ", ", ")")
- }
-
- private def err(msg: String) = { unit.error(pos, msg) ; throw new TypeError(msg) }
- private def wrongArity(n: Int) = err(wrongArityMsg(n))
-
- def squishIntoOne() = {
- if (settings.lint)
- unit.warning(pos, s"$enclClass expects $expectedArity patterns to hold $rawGet but crushing into $productArity-tuple to fit single pattern (SI-6675)")
-
- rawGet :: Nil
- }
- // elementArity is the number of non-sequence patterns minus the
- // the number of non-sequence product elements returned by the extractor.
- // If it is zero, there is a perfect match between those parts, and
- // if there is a wildcard star it will match any sequence.
- // If it is positive, there are more patterns than products,
- // so a sequence will have to fill in the elements. If it is negative,
- // there are more products than patterns, which is a compile time error.
- def elementArity = patternFixedArity - productArity
- def patternFixedArity = treeInfo effectivePatternArity args
- def productArity = productTypes.size
- def isSingle = !isUnapplySeq && (patternFixedArity == 1)
-
- def productTypes = if (isUnapplySeq) rawTypes dropRight 1 else rawTypes
- def elementTypes = List.fill(elementArity)(elementType)
- def varargsType = scalaRepeatedType(elementType)
- }
-
private class VariantToSkolemMap extends TypeMap(trackVariance = true) {
private val skolemBuffer = mutable.ListBuffer[TypeSymbol]()
@@ -365,10 +270,12 @@ trait PatternTypers {
case OverloadedType(_, _) => OverloadedUnapplyError(fun) ; ErrorType
case _ => UnapplyWithSingleArgError(fun) ; ErrorType
}
- val shape = newExtractorShape(fun, args)
- import shape.{ unapplyParamType, unapplyType, unapplyMethod }
+ val unapplyMethod = unapplyMember(fun.tpe)
+ val unapplyType = fun.tpe memberType unapplyMethod
+ val unapplyParamType = firstParamType(unapplyType)
+ def isSeq = unapplyMethod.name == nme.unapplySeq
- def extractor = extractorForUncheckedType(shape.pos, unapplyParamType)
+ def extractor = extractorForUncheckedType(fun.pos, unapplyParamType)
def canRemedy = unapplyParamType match {
case RefinedType(_, decls) if !decls.isEmpty => false
case RefinedType(parents, _) if parents exists isUncheckable => false
@@ -400,7 +307,8 @@ trait PatternTypers {
// the union of the expected type and the inferred type of the argument to unapply
val glbType = glb(ensureFullyDefined(pt) :: unapplyArg.tpe_* :: Nil)
val wrapInTypeTest = canRemedy && !(fun1.symbol.owner isNonBottomSubClass ClassTagClass)
- val args1 = typedPatternArgs(fun1, args, mode)
+ val formals = patmat.alignPatterns(fun1, args).unexpandedFormals
+ val args1 = typedArgsForFormals(args, formals, mode)
val result = UnApply(fun1, args1) setPos tree.pos setType glbType
if (wrapInTypeTest)
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index dde3f1e9f7..42c9135495 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -813,46 +813,32 @@ trait Definitions extends api.StandardDefinitions {
def byNameType(arg: Type) = appliedType(ByNameParamClass, arg)
def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp)
def javaRepeatedType(arg: Type) = appliedType(JavaRepeatedParamClass, arg)
+ def optionType(tp: Type) = appliedType(OptionClass, tp)
def scalaRepeatedType(arg: Type) = appliedType(RepeatedParamClass, arg)
def seqType(arg: Type) = appliedType(SeqClass, arg)
// FYI the long clunky name is because it's really hard to put "get" into the
// name of a method without it sounding like the method "get"s something, whereas
// this method is about a type member which just happens to be named get.
- def typeOfMemberNamedGet(tp: Type) = resultOfMatchingMethod(tp, nme.get)()
- def typeOfMemberNamedHead(tp: Type) = resultOfMatchingMethod(tp, nme.head)()
- def typeOfMemberNamedApply(tp: Type) = resultOfMatchingMethod(tp, nme.apply)(IntTpe)
- def typeOfMemberNamedDrop(tp: Type) = resultOfMatchingMethod(tp, nme.drop)(IntTpe)
- def typeOfMemberNamedGetOrSelf(tp: Type) = typeOfMemberNamedGet(tp) orElse tp
- def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp))
- def typesOfCaseAccessors(tp: Type) = getterMemberTypes(tp, tp.typeSymbol.caseFieldAccessors)
-
- /** If this is a case class, the case field accessors (which may be an empty list.)
- * Otherwise, if there are any product selectors, that list.
- * Otherwise, a list containing only the type itself.
- */
- def typesOfSelectorsOrSelf(tp: Type): List[Type] = (
- if (tp.typeSymbol.isCase)
- typesOfCaseAccessors(tp)
- else typesOfSelectors(tp) match {
- case Nil => tp :: Nil
- case tps => tps
- }
- )
-
- /** If the given type has one or more product selectors, the type of the last one.
- * Otherwise, the type itself.
- */
- def typeOfLastSelectorOrSelf(tp: Type) = typesOfSelectorsOrSelf(tp).last
-
- def elementTypeOfLastSelectorOrSelf(tp: Type) = {
- val last = typeOfLastSelectorOrSelf(tp)
- ( typeOfMemberNamedHead(last)
- orElse typeOfMemberNamedApply(last)
- orElse elementType(ArrayClass, last)
- )
+ def typeOfMemberNamedGet(tp: Type) = typeArgOfBaseTypeOr(tp, OptionClass)(resultOfMatchingMethod(tp, nme.get)())
+ def typeOfMemberNamedHead(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.head)())
+ def typeOfMemberNamedApply(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.apply)(IntTpe))
+ def typeOfMemberNamedDrop(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.drop)(IntTpe))
+ def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp))
+ // SI-8128 Still using the type argument of the base type at Seq/Option if this is an old-style (2.10 compatible)
+ // extractor to limit exposure to regressions like the reported problem with existentials.
+ // TODO fix the existential problem in the general case, see test/pending/pos/t8128.scala
+ private def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp baseType baseClass).typeArgs match {
+ case x :: Nil => x
+ case _ => or
}
+ // Can't only check for _1 thanks to pos/t796.
+ def hasSelectors(tp: Type) = (
+ (tp.members containsName nme._1)
+ && (tp.members containsName nme._2)
+ )
+
/** Returns the method symbols for members _1, _2, ..., _N
* which exist in the given type.
*/
@@ -862,7 +848,9 @@ trait Definitions extends api.StandardDefinitions {
case m if m.paramss.nonEmpty => Nil
case m => m :: loop(n + 1)
}
- loop(1)
+ // Since ErrorType always returns a symbol from a call to member, we
+ // had better not start looking for _1, _2, etc. expecting it to run out.
+ if (tpe.isErroneous) Nil else loop(1)
}
/** If `tp` has a term member `name`, the first parameter list of which
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index ea6afa7349..a54aa1f6e8 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -686,6 +686,7 @@ trait StdNames {
val inlinedEquals: NameType = "inlinedEquals"
val isArray: NameType = "isArray"
val isDefinedAt: NameType = "isDefinedAt"
+ val isEmpty: NameType = "isEmpty"
val isInstanceOf_ : NameType = "isInstanceOf"
val isInstanceOf_Ob : NameType = "$isInstanceOf"
val java: NameType = "java"
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index 8fdf4dc27a..497a7c91b1 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -612,8 +612,8 @@ abstract class TreeInfo {
def effectivePatternArity(args: List[Tree]): Int = flattenedPatternArgs(args).length
def flattenedPatternArgs(args: List[Tree]): List[Tree] = args map unbind match {
- case Apply(fun, xs) :: Nil if isTupleSymbol(fun.symbol) => xs
- case xs => xs
+ case build.SyntacticTuple(xs) :: Nil => xs
+ case xs => xs
}
// used in the symbols for labeldefs and valdefs emitted by the pattern matcher
diff --git a/test/files/neg/t4425.check b/test/files/neg/t4425.check
index 95b88a6b3d..00006c08f0 100644
--- a/test/files/neg/t4425.check
+++ b/test/files/neg/t4425.check
@@ -1,12 +1,12 @@
-t4425.scala:3: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425.scala:3: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: Int)(y: Option[Int]): None.type exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
42 match { case _ X _ => () }
^
-t4425.scala:8: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425.scala:8: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: Int)(y: Int): Some[(Int, Int)] exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
42 match { case _ X _ => () }
^
-t4425.scala:13: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425.scala:13: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Some[(Int, Int)] exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
"" match { case _ X _ => () }
^
diff --git a/test/files/neg/t4425b.check b/test/files/neg/t4425b.check
index 1186e8b609..8418b4fd12 100644
--- a/test/files/neg/t4425b.check
+++ b/test/files/neg/t4425b.check
@@ -1,61 +1,49 @@
-t4425b.scala:5: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:5: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println( "" match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:6: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:6: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println((X: Any) match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:7: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:7: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println( "" match { case X(_) => "ok" ; case _ => "fail" })
^
-t4425b.scala:8: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:8: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println((X: Any) match { case X(_) => "ok" ; case _ => "fail" })
^
-t4425b.scala:9: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:9: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println( "" match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:10: error: object X is not a case class constructor, nor does it have an unapply/unapplySeq method
+t4425b.scala:10: error: object X is not a case class, nor does it have an unapply/unapplySeq member
Note: def unapply(x: String)(y: String): Nothing exists in object X, but it cannot be used as an extractor due to its second non-implicit parameter list
println((X: Any) match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:18: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:18: error: too many patterns for object X: expected 1, found 2
println( "" match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:19: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:19: error: too many patterns for object X: expected 1, found 2
println((X: Any) match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:22: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:22: error: too many patterns for object X: expected 1, found 2
println( "" match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:22: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
- println( "" match { case X(_, _) => "ok" ; case _ => "fail" })
- ^
-t4425b.scala:23: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:23: error: too many patterns for object X: expected 1, found 2
println((X: Any) match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:23: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
- println((X: Any) match { case X(_, _) => "ok" ; case _ => "fail" })
- ^
-t4425b.scala:31: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:31: error: too many patterns for object X offering Nothing: expected 1, found 2
println( "" match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:32: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:32: error: too many patterns for object X offering Nothing: expected 1, found 2
println((X: Any) match { case _ X _ => "ok" ; case _ => "fail" })
^
-t4425b.scala:35: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:35: error: too many patterns for object X offering Nothing: expected 1, found 2
println( "" match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:35: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
- println( "" match { case X(_, _) => "ok" ; case _ => "fail" })
- ^
-t4425b.scala:36: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
+t4425b.scala:36: error: too many patterns for object X offering Nothing: expected 1, found 2
println((X: Any) match { case X(_, _) => "ok" ; case _ => "fail" })
^
-t4425b.scala:36: error: wrong number of patterns for object X offering Nothing: expected 1, found 2
- println((X: Any) match { case X(_, _) => "ok" ; case _ => "fail" })
- ^
-18 errors found
+14 errors found
diff --git a/test/files/neg/t5903a.check b/test/files/neg/t5903a.check
index 2e5cc87167..34003b0a82 100644
--- a/test/files/neg/t5903a.check
+++ b/test/files/neg/t5903a.check
@@ -1,4 +1,4 @@
-Test_2.scala:4: error: wrong number of patterns for <$anon: AnyRef> offering (SomeTree.type, SomeTree.type): expected 2, found 3
+Test_2.scala:4: error: too many patterns for <$anon: AnyRef> offering (SomeTree.type, SomeTree.type): expected 2, found 3
case nq"$x + $y + $z" => println((x, y))
^
one error found
diff --git a/test/files/neg/t6675b.check b/test/files/neg/t6675b.check
new file mode 100644
index 0000000000..77f6b3ccbc
--- /dev/null
+++ b/test/files/neg/t6675b.check
@@ -0,0 +1,37 @@
+t6675b.scala:17: warning: object LeftOrRight expects 2 patterns to hold (Int, Int) but crushing into 2-tuple to fit single pattern (SI-6675)
+ def f1 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight(a) => a } // warn
+ ^
+t6675b.scala:19: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2, T3)
+ required: (Int, Int)
+ def f3 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight((a, b, c)) => a } // fail
+ ^
+t6675b.scala:24: warning: object LeftOrRight expects 2 patterns to hold (A, A) but crushing into 2-tuple to fit single pattern (SI-6675)
+ def f2[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case LeftOrRight(a) => a } // warn
+ ^
+t6675b.scala:26: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2, T3)
+ required: (?A11, ?A12) where type ?A12 <: A (this is a GADT skolem), type ?A11 <: A (this is a GADT skolem)
+ def f4[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case LeftOrRight((a, b, c)) => a } // fail
+ ^
+t6675b.scala:30: warning: object NativelyTwo expects 2 patterns to hold ((Int, Int), (Int, Int)) but crushing into 2-tuple to fit single pattern (SI-6675)
+ def f1 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case NativelyTwo(a) => a } // warn
+ ^
+t6675b.scala:32: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2, T3)
+ required: ((Int, Int), (Int, Int))
+ def f3 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case NativelyTwo((a, b, c)) => a } // fail
+ ^
+t6675b.scala:36: warning: object NativelyTwo expects 2 patterns to hold (A, A) but crushing into 2-tuple to fit single pattern (SI-6675)
+ def f1[A](x: A) = (Left(x): Either[A, A]) match { case NativelyTwo(a) => a } // warn
+ ^
+t6675b.scala:37: warning: object NativelyTwo expects 2 patterns to hold ((A, A), (A, A)) but crushing into 2-tuple to fit single pattern (SI-6675)
+ def f2[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case NativelyTwo(a) => a } // warn
+ ^
+t6675b.scala:39: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2, T3)
+ required: ((?A17, ?A18), (?A19, ?A20)) where type ?A20 <: A (this is a GADT skolem), type ?A19 <: A (this is a GADT skolem), type ?A18 <: A (this is a GADT skolem), type ?A17 <: A (this is a GADT skolem)
+ def f4[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case NativelyTwo((a, b, c)) => a } // fail
+ ^
+5 warnings found
+four errors found
diff --git a/test/files/neg/t6675b.flags b/test/files/neg/t6675b.flags
new file mode 100644
index 0000000000..1008b0a70c
--- /dev/null
+++ b/test/files/neg/t6675b.flags
@@ -0,0 +1 @@
+-Xlint
diff --git a/test/files/neg/t6675b.scala b/test/files/neg/t6675b.scala
new file mode 100644
index 0000000000..c86c9c3955
--- /dev/null
+++ b/test/files/neg/t6675b.scala
@@ -0,0 +1,40 @@
+object LeftOrRight {
+ def unapply[A](value: Either[A, A]): Option[A] = value match {
+ case scala.Left(x) => Some(x)
+ case scala.Right(x) => Some(x)
+ }
+}
+
+object NativelyTwo {
+ def unapply[A](value: Either[A, A]): Option[(A, A)] = value match {
+ case scala.Left(x) => Some(x -> x)
+ case scala.Right(x) => Some(x -> x)
+ }
+}
+
+
+class A {
+ def f1 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight(a) => a } // warn
+ def f2 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight((a, b)) => a } // no warn
+ def f3 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight((a, b, c)) => a } // fail
+}
+
+class B {
+ def f1[A](x: A) = (Left(x): Either[A, A]) match { case LeftOrRight(a) => a } // no warn
+ def f2[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case LeftOrRight(a) => a } // warn
+ def f3[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case LeftOrRight((a, b)) => a } // no warn
+ def f4[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case LeftOrRight((a, b, c)) => a } // fail
+}
+
+class C {
+ def f1 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case NativelyTwo(a) => a } // warn
+ def f2 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case NativelyTwo((a, b)) => a } // no warn
+ def f3 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case NativelyTwo((a, b, c)) => a } // fail
+}
+
+class D {
+ def f1[A](x: A) = (Left(x): Either[A, A]) match { case NativelyTwo(a) => a } // warn
+ def f2[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case NativelyTwo(a) => a } // warn
+ def f3[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case NativelyTwo((a, b)) => a } // no warn
+ def f4[A](x: A) = (Left(x -> x): Either[(A, A), (A, A)]) match { case NativelyTwo((a, b, c)) => a } // fail
+}
diff --git a/test/files/neg/t7214neg.check b/test/files/neg/t7214neg.check
index 0660cccd02..291af04578 100644
--- a/test/files/neg/t7214neg.check
+++ b/test/files/neg/t7214neg.check
@@ -1,7 +1,4 @@
-t7214neg.scala:28: error: wrong number of patterns for object Extractor offering Any: expected 1, found 0
+t7214neg.scala:28: error: not enough patterns for object Extractor offering Any: expected 1, found 0
case Extractor() =>
^
-t7214neg.scala:28: error: wrong number of patterns for object Extractor offering Any: expected 1, found 0
- case Extractor() =>
- ^
-two errors found
+one error found
diff --git a/test/files/neg/t7850.check b/test/files/neg/t7850.check
new file mode 100644
index 0000000000..317be2bbce
--- /dev/null
+++ b/test/files/neg/t7850.check
@@ -0,0 +1,7 @@
+t7850.scala:11: error: an unapply result must have a member `def isEmpty: Boolean (found: def isEmpty: Casey)
+ val Casey(x1) = new Casey(1)
+ ^
+t7850.scala:12: error: an unapply result must have a member `def isEmpty: Boolean
+ val Dingy(x2) = new Dingy(1)
+ ^
+two errors found
diff --git a/test/files/neg/t7850.scala b/test/files/neg/t7850.scala
new file mode 100644
index 0000000000..04edad82b5
--- /dev/null
+++ b/test/files/neg/t7850.scala
@@ -0,0 +1,16 @@
+// isEmpty returns non-boolean
+class Casey(a: Int) { def isEmpty = this; def get = this }
+object Casey { def unapply(a: Casey) = a }
+
+// no isEmpty method at all
+class Dingy(a: Int) { def get = this }
+object Dingy { def unapply(a: Dingy) = a }
+
+object Test {
+ def main(args: Array[String]) {
+ val Casey(x1) = new Casey(1)
+ val Dingy(x2) = new Dingy(1)
+ println(s"$x1 $x2")
+ }
+}
+
diff --git a/test/files/neg/t7897.check b/test/files/neg/t7897.check
new file mode 100644
index 0000000000..48eff511c7
--- /dev/null
+++ b/test/files/neg/t7897.check
@@ -0,0 +1,4 @@
+t7897.scala:19: error: value length is not a member of p0.Single
+ case p0.Single(x) => println(s"`$x` has ${x.length} chars")
+ ^
+one error found
diff --git a/test/files/neg/t7897.scala b/test/files/neg/t7897.scala
new file mode 100644
index 0000000000..87c966b1e0
--- /dev/null
+++ b/test/files/neg/t7897.scala
@@ -0,0 +1,23 @@
+package p0 {
+ class Single(val x: Any) extends AnyRef with Product1[String] {
+ private def s = "" + x
+ override def canEqual(x: Any) = this eq x.asInstanceOf[AnyRef]
+ def isEmpty = false
+ def get = this
+ def _1 = s + " only"
+
+ override def toString = s"Single(${_1})"
+ }
+
+ object Single {
+ def unapply(x: Any): Single = new Single(x)
+ }
+}
+object Test {
+ def main(args: Array[String]): Unit = {
+ "catdog" match {
+ case p0.Single(x) => println(s"`$x` has ${x.length} chars")
+ case x => println("fail: " + x)
+ }
+ }
+}
diff --git a/test/files/neg/t997.check b/test/files/neg/t997.check
index 8c41060ba2..b118792229 100644
--- a/test/files/neg/t997.check
+++ b/test/files/neg/t997.check
@@ -1,7 +1,4 @@
-t997.scala:13: error: wrong number of patterns for object Foo offering (String, String): expected 2, found 3
+t997.scala:13: error: too many patterns for object Foo offering (String, String): expected 2, found 3
"x" match { case Foo(a, b, c) => Console.println((a,b,c)) }
^
-t997.scala:13: error: wrong number of patterns for object Foo offering (String, String): expected 2, found 3
-"x" match { case Foo(a, b, c) => Console.println((a,b,c)) }
- ^
-two errors found
+one error found
diff --git a/test/files/pos/t8045.scala b/test/files/pos/t8045.scala
new file mode 100644
index 0000000000..21154e386a
--- /dev/null
+++ b/test/files/pos/t8045.scala
@@ -0,0 +1,17 @@
+object Test extends App {
+ case class Number(i: Int)
+
+ object UnliftNumber {
+ def unapply(t: Any): Option[Number] = t match {
+ case i: Int => Some(Number(i))
+ case _ => None
+ }
+ }
+
+ def eval(expr: Any): Option[Number] = expr match {
+ case UnliftNumber(n) => Some(n)
+ case _ => None
+ }
+
+ println(eval(1))
+}
diff --git a/test/files/pos/t8128.scala b/test/files/pos/t8128.scala
new file mode 100644
index 0000000000..b6f76691b0
--- /dev/null
+++ b/test/files/pos/t8128.scala
@@ -0,0 +1,15 @@
+object G {
+ def unapply(m: Any): Option[_] = Some("")
+}
+
+object H {
+ def unapplySeq(m: Any): Option[Seq[_]] = None
+}
+
+object Test {
+ (0: Any) match {
+ case G(v) => v
+ case H(v) => v
+ case _ =>
+ }
+}
diff --git a/test/files/run/name-based-patmat.check b/test/files/run/name-based-patmat.check
index 1cc605ea3d..3d5fc40ed7 100644
--- a/test/files/run/name-based-patmat.check
+++ b/test/files/run/name-based-patmat.check
@@ -1,3 +1,5 @@
+`catdog only` has 11 chars
+`catdog only, no product` has 23 chars
catdog
2 catdogs! A ha ha!
3 catdogs! A ha ha!
diff --git a/test/files/run/name-based-patmat.scala b/test/files/run/name-based-patmat.scala
index 2c429c141f..8e20940100 100644
--- a/test/files/run/name-based-patmat.scala
+++ b/test/files/run/name-based-patmat.scala
@@ -1,5 +1,33 @@
final class MiniSome[T](val get: T) extends AnyVal { def isEmpty = false }
+package p0 {
+ class Single(val x: Any) extends AnyRef with Product1[String] {
+ private def s = "" + x
+ override def canEqual(x: Any) = this eq x.asInstanceOf[AnyRef]
+ def isEmpty = false
+ def get = this
+ def _1 = s + " only"
+
+ override def toString = s"Single(${_1})"
+ }
+
+ object Single {
+ def unapply(x: Any): Single = new Single(x)
+ }
+
+ class SingleNoProduct(val x: Any) extends AnyRef {
+ private def s = "" + x
+ def isEmpty = false
+ def get = s + " only, no product"
+
+ override def toString = s"SingleNoProduct($get)"
+ }
+
+ object SingleNoProduct {
+ def unapply(x: Any): SingleNoProduct = new SingleNoProduct(x)
+ }
+}
+
package p1 {
class Triple(val x: Any) extends AnyRef with Product3[String, String, String] {
private def s = "" + x
@@ -49,14 +77,16 @@ package p3 {
}
object Test {
-
- // def f(x: Any) = x match {
- // case p1.Foo(x, y, z) => println((x, y, z))
- // case x => println(x)
- // }
-
def main(args: Array[String]): Unit = {
"catdog" match {
+ case p0.Single(x) => println(s"`${x._1}` has ${x._1.length} chars")
+ case x => println("fail: " + x)
+ }
+ "catdog" match {
+ case p0.SingleNoProduct(x) => println(s"`$x` has ${x.length} chars")
+ case x => println("fail: " + x)
+ }
+ "catdog" match {
case p1.Triple(x, y, z) => List(x, y, z) foreach println
case x => println("fail: " + x)
}
diff --git a/test/files/run/patmat-mix-case-extractor.check b/test/files/run/patmat-mix-case-extractor.check
new file mode 100644
index 0000000000..a6e1bd23df
--- /dev/null
+++ b/test/files/run/patmat-mix-case-extractor.check
@@ -0,0 +1,8 @@
+-1
+6
+4
+18
+-1
+1006
+1004
+1018
diff --git a/test/files/run/patmat-mix-case-extractor.scala b/test/files/run/patmat-mix-case-extractor.scala
new file mode 100644
index 0000000000..964e6f743c
--- /dev/null
+++ b/test/files/run/patmat-mix-case-extractor.scala
@@ -0,0 +1,110 @@
+trait CaseClass
+trait ProdCaseClass extends CaseClass { def x: Int }
+trait SeqCaseClass extends CaseClass { def xs: Seq[Int] }
+
+case class CaseClass1() extends CaseClass
+case class CaseClass2(xs: Int*) extends SeqCaseClass
+case class CaseClass3(x: Int) extends ProdCaseClass
+case class CaseClass4(x: Int, xs: Int*) extends ProdCaseClass with SeqCaseClass
+
+object Extractor1 { def unapply(x: CaseClass): Boolean = false }
+object Extractor2 { def unapplySeq(x: SeqCaseClass): Option[Seq[Int]] = Some(x.xs) }
+object Extractor3 { def unapply(x: ProdCaseClass): Option[Int] = Some(x.x) }
+object Extractor4 { def unapplySeq(x: ProdCaseClass with SeqCaseClass): Option[(Int, Seq[Int])] = Some(x.x, x.xs) }
+
+class A {
+ def f1(x: Any) = x match {
+ case CaseClass1() => -1
+ case CaseClass2(xs @ _*) => xs.sum
+ case CaseClass3(x) => x
+ case CaseClass4(x, xs @ _*) => x + xs.sum
+ case Extractor4(x, xs @ _*) => 1000 + x + xs.sum
+ case Extractor3(x) => 1000 + x
+ case Extractor2(xs @ _*) => 1000 + xs.sum
+ case Extractor1() => -3
+ case _ => -2
+ }
+ def f2(x: Any) = x match {
+ case Extractor4(x, xs @ _*) => 1000 + x + xs.sum
+ case Extractor3(x) => 1000 + x
+ case Extractor2(xs @ _*) => 1000 + xs.sum
+ case Extractor1() => -3
+ case CaseClass1() => -1
+ case CaseClass2(xs @ _*) => xs.sum
+ case CaseClass3(x) => x
+ case CaseClass4(x, xs @ _*) => x + xs.sum
+ case _ => -2
+ }
+ def run() {
+ List(
+ f1(CaseClass1()),
+ f1(CaseClass2(1, 2, 3)),
+ f1(CaseClass3(4)),
+ f1(CaseClass4(5, 6, 7)),
+ f2(CaseClass1()),
+ f2(CaseClass2(1, 2, 3)),
+ f2(CaseClass3(4)),
+ f2(CaseClass4(5, 6, 7))
+ ) foreach println
+ }
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ (new A).run
+ }
+}
+
+
+class B {
+ case class CaseClass0()
+ case class CaseClass0v(xs: Int*)
+
+ case class CaseClass(x: Int, y: Int)
+ object Extractor { def unapply(x: Any): Option[(Int, Int)] = Some((1, 1)) }
+
+ case class CaseSeq(x: Char, y: Double, zs: Int*)
+ object ExtractorSeq { def unapplySeq(x: Any): Option[(Int, Int, Seq[Int])] = Some((1, 1, List(1))) }
+
+ def f1(x: CaseClass) = x match { case CaseClass(y, z) => y }
+ def f2(x: Any) = x match { case Extractor(y, z) => y }
+
+ def f3(x: CaseSeq) = x match {
+ case CaseSeq(x, y) => y
+ case CaseSeq(x, y, z) => z
+ }
+ def f4(x: CaseSeq) = x match {
+ case CaseSeq(x, y, z) => z :: Nil
+ case CaseSeq(x, y, z @ _*) => z
+ }
+
+ def f5(x: Any) = x match { case ExtractorSeq(x, y, z) => z }
+ def f6(x: Any) = x match { case ExtractorSeq(x, y, z @ _*) => z }
+
+ def g1(x: CaseClass0) = x match {
+ case CaseClass0() => true
+ }
+ def g2(x: CaseClass0v) = x match {
+ case CaseClass0v() => true
+ case CaseClass0v(5) => true
+ case CaseClass0v(x) => true
+ case CaseClass0v(xs @ _*) => false
+ }
+}
+
+package p1 {
+ trait _X {
+ case class _Foo();
+ object _Bar {
+ def unapply(foo: _Foo): Boolean = true;
+ }
+ }
+
+ object Y extends _X {
+ val foo = _Foo()
+ foo match {
+ case _Bar() =>
+ case _ => assert(false)
+ }
+ }
+}
diff --git a/test/pending/pos/t8128b.scala b/test/pending/pos/t8128b.scala
new file mode 100644
index 0000000000..dd44a25a90
--- /dev/null
+++ b/test/pending/pos/t8128b.scala
@@ -0,0 +1,18 @@
+class Optiony[X] { def isEmpty = true; def get: X = ??? }
+class Seqy[X] { def head: X = ???; def length = 0; def apply(i: Int): X = ??? }
+
+object G {
+ def unapply(m: Any): Optiony[_] = ???
+}
+
+object H {
+ def unapplySeq(m: Any): Optiony[Seqy[_]] = ???
+}
+
+object Test {
+ (0: Any) match {
+ case G(v) => v
+ case H(v) => v
+ case _ =>
+ }
+}