summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-02-18 05:40:27 +0000
committerPaul Phillips <paulp@improving.org>2010-02-18 05:40:27 +0000
commit23e5428008fc88377e59a1a5c20d5476c586d62e (patch)
tree45a0eb248c9d7c11674c6d0484c009518197d0d4 /src/compiler
parentcf2f9d7fbe4311b2373957b4bc78520984f4eafc (diff)
downloadscala-23e5428008fc88377e59a1a5c20d5476c586d62e.tar.gz
scala-23e5428008fc88377e59a1a5c20d5476c586d62e.tar.bz2
scala-23e5428008fc88377e59a1a5c20d5476c586d62e.zip
Tighter pattern matching hits the street.
is final and does not conform to the pattern, it will no longer compile. See all the exciting things you can no longer do: "bob".reverse match { case Seq('b', 'o', 'b') => true } // denied! "bob".toArray match { case Seq('b', 'o', 'b') => true } // rejected! final class Dunk def f3(x: Dunk) = x match { case Seq('b', 'o', 'b') => true } // uh-uh! And so forth. Review by odersky.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/matching/MatchSupport.scala22
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala30
3 files changed, 42 insertions, 38 deletions
diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
index 117973865b..374e974170 100644
--- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
+++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
@@ -28,24 +28,10 @@ trait MatchSupport extends ast.TreeDSL { self: ParallelMatching =>
import definitions._
implicit def enrichType(x: Type): RichType = new RichType(x)
- // see bug1434.scala for an illustration of why "x <:< y" is insufficient.
- // this code is inadequate but slowly improving... Inherited comment:
- //
- // an approximation of _tp1 <:< tp2 that ignores _ types. this code is wrong,
- // ideally there is a better way to do it, and ideally defined in Types.scala
- private[matching] def matches(arg1: Type, arg2: Type) = {
- val List(t1, t2) = List(arg1, arg2) map decodedEqualsType
-
- def matchesIgnoringBounds(tp1: Type, tp2: Type) = tp2 match {
- case TypeRef(pre, sym, args) => tp1 <:< TypeRef(pre, sym, args map (_ => AnyClass.tpe))
- case _ => false
- }
- def okPrefix = t1.prefix =:= t2.prefix
- def okSubtype = t1.baseTypeSeq exists (_.typeSymbol eq t2.typeSymbol)
- def okBounds = matchesIgnoringBounds(t1, t2)
-
- (t1 <:< t2) || (okPrefix && okSubtype && okBounds)
- }
+ // A subtype test which creates fresh existentials for type
+ // parameters on the right hand side.
+ private[matching] def matches(arg1: Type, arg2: Type) =
+ decodedEqualsType(arg1) matchesPattern decodedEqualsType(arg2)
class RichType(undecodedTpe: Type) {
def tpe = decodedEqualsType(undecodedTpe)
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index e46d7bea84..aa88996876 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -587,6 +587,34 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
}
}
+ /** Can this type only be subtyped by bottom types?
+ * This is assessed to be the case if the class is final,
+ * and all type parameters (if any) are invariant.
+ */
+ def isFinalType = (
+ typeSymbol.isFinal &&
+ (typeSymbol.typeParams forall (_.variance == 0))
+ )
+
+ /** Is this type a subtype of that type in a pattern context?
+ * Any type arguments on the right hand side are replaced with
+ * fresh existentials, except for Arrays.
+ *
+ * See bug1434.scala for an example of code which would fail
+ * if only a <:< test were applied.
+ */
+ def matchesPattern(that: Type): Boolean = {
+ (this <:< that) || ((this, that) match {
+ case (TypeRef(_, ArrayClass, List(arg1)), TypeRef(_, ArrayClass, List(arg2))) if arg2.typeSymbol.typeParams.nonEmpty =>
+ arg1 matchesPattern arg2
+ case (_, TypeRef(_, _, args)) =>
+ val newtp = existentialAbstraction(args map (_.typeSymbol), that)
+ !(that =:= newtp) && (this <:< newtp)
+ case _ =>
+ false
+ })
+ }
+
def stat_<:<(that: Type): Boolean = {
incCounter(subtypeCount)
val start = startTimer(subtypeNanos)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 8761420778..3738e9a5ec 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -394,8 +394,6 @@ trait Infer {
}
}
- def isPlausiblyPopulated(tp1: Type, tp2: Type): Boolean = true
-
def isPlausiblyCompatible(tp: Type, pt: Type): Boolean = tp match {
case PolyType(_, restpe) =>
isPlausiblyCompatible(restpe, pt)
@@ -1496,6 +1494,11 @@ trait Infer {
def inferTypedPattern(pos: Position, pattp: Type, pt0: Type): Type = {
val pt = widen(pt0)
+
+ /** If we can absolutely rule out a match we can fail fast. */
+ if (pt.isFinalType && !(pt matchesPattern pattp))
+ error(pos, "scrutinee is incompatible with pattern type"+foundReqMsg(pattp, pt))
+
checkCheckable(pos, pattp, "pattern ")
if (!(pattp <:< pt)) {
val tpparams = freeTypeParamsOfTerms.collect(pattp)
@@ -1509,24 +1512,11 @@ trait Infer {
if (settings.debug.value) log("free type params (2) = " + ptparams)
val ptvars = ptparams map freshVar
val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
- if (!(isPopulated(tp, pt1) && isInstantiatable(tvars ::: ptvars))) {
- // In ticket #2486 we have this example of code which would fail
- // here without a change:
- //
- // class A[T]
- // class B extends A[Int]
- // class C[T] extends A[T] { def f(t: A[T]) = t match { case x: B => () } }
- //
- // This reports error: pattern type is incompatible with expected type;
- // found : B
- // required: A[T]
- //
- // I am not sure what is the ideal fix, but for the moment I am intercepting
- // it at the last minute and applying a looser check before failing.
- if (!isPlausiblyCompatible(pattp, pt)) {
- error(pos, "pattern type is incompatible with expected type"+foundReqMsg(pattp, pt))
- return pattp
- }
+ // See ticket #2486 we have this example of code which would incorrectly
+ // fail without verifying that !(pattp matchesPattern pt)
+ if (!(isPopulated(tp, pt1) && isInstantiatable(tvars ::: ptvars)) && !(pattp matchesPattern pt)) {
+ error(pos, "pattern type is incompatible with expected type"+foundReqMsg(pattp, pt))
+ return pattp
}
ptvars foreach instantiateTypeVar
}