summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-06-04 15:35:35 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-06-08 15:35:09 +0200
commit6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626 (patch)
tree44de8ceb19ff698d92c17bc37fcd6bbb84731ba1 /src
parent10292e4acbb8eb0143a5a087c750ed9699f31807 (diff)
downloadscala-6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626.tar.gz
scala-6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626.tar.bz2
scala-6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626.zip
turn unchecked type patterns into checked ones
the pattern `(_: T)` is made checkable using (ct: ClassTag[T]).unapply by rewriting it to `ct(_: T)` (if there's a ClassTag[T] available) similarly for extractors: if the formal type of the unapply method is an uncheckable type, wrap in the corresponding classtag extractor (if available) don't trigger rewrite on non-toplevel unchecked types (i.e., only look at type constructor part of T when looking for unchecked types) TODO: find outer match to figure out if we're supposed to be unchecked would like to give users a chance to opt-out from the wrapping, but finding the match to which this pattern belongs turned out to be tricky...
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala69
-rw-r--r--src/library/scala/reflect/ClassTag.scala10
2 files changed, 73 insertions, 6 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 95a2962199..9a27f3dd41 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -970,7 +970,7 @@ trait Typers extends Modes with Adaptations with Tags {
val overloadedExtractorOfObject = tree.symbol filter (sym => hasUnapplyMember(sym.tpe))
// if the tree's symbol's type does not define an extractor, maybe the tree's type does
// this is the case when we encounter an arbitrary tree as the target of an unapply call (rather than something that looks like a constructor call)
- // (for now, this only happens due to maybeTypeTagExtractor, but when we support parameterized extractors, it will become more common place)
+ // (for now, this only happens due to maybeWrapClassTagUnapply, but when we support parameterized extractors, it will become more common place)
val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe)
if (extractor != NoSymbol) {
// if we did some ad-hoc overloading resolution, update the tree's symbol
@@ -3120,12 +3120,64 @@ trait Typers extends Modes with Adaptations with Tags {
val itype = glb(List(pt1, arg.tpe))
arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking)
- UnApply(fun1, args1) setPos tree.pos setType itype
+ val unapply = UnApply(fun1, args1) setPos tree.pos setType itype
+
+ // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor
+ // skip if the unapply's type is not a method type with (at least, but really it should be exactly) one argument
+ if (unappType.paramTypes.isEmpty) unapply
+ else maybeWrapClassTagUnapply(unapply, unappType.paramTypes.head)
} else
duplErrorTree(WrongNumberArgsPatternError(tree, fun))
}
}
+ // TODO: disable when in unchecked match
+ def maybeWrapClassTagUnapply(uncheckedPattern: Tree, pt: Type): Tree = if (isPastTyper) uncheckedPattern else {
+ // only look at top-level type, can't (reliably) do anything about unchecked type args (in general)
+ pt.normalize.typeConstructor match {
+ // if at least one of the types in an intersection is checkable, use the checkable ones
+ // this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike`
+ // Coll is an abstract type, but SeqLike of course is not
+ case tp@RefinedType(parents, _) if (parents.length >= 2) && (parents.exists(tp => !infer.containsUnchecked(tp))) =>
+ uncheckedPattern
+
+ case ptCheckable if infer.containsUnchecked(ptCheckable) =>
+ uncheckedPattern match {
+ // are we being called on a classtag extractor call?
+ // don't recurse forever, it's not *that* much fun
+ case UnApply(fun, _) if fun.symbol.owner.isNonBottomSubClass(ClassTagClass) =>
+ uncheckedPattern
+
+ case _ =>
+ val typeTagExtractor = resolveClassTag(uncheckedPattern.pos, ptCheckable)
+ //orElse resolveTypeTag(uncheckedPattern.pos, ReflectMirrorPrefix.tpe, ptCheckable, concrete = true)
+
+ // we don't create a new Context for a Match, so find the CaseDef, then go out one level and navigate back to the match that has this case
+ // val thisCase = context.nextEnclosing(_.tree.isInstanceOf[CaseDef])
+ // val unchecked = thisCase.outer.tree.collect{case Match(selector, cases) if cases contains thisCase => selector} match {
+ // case List(Typed(_, tpt)) if treeInfo.isUncheckedAnnotation(tpt.tpe) => true
+ // case t => println("outer tree: "+ (t, thisCase, thisCase.outer.tree)); false
+ // }
+ // println("maybeWrapClassTagUnapply"+ (!isPastTyper && infer.containsUnchecked(pt), pt, uncheckedPattern))
+ // println("typeTagExtractor"+ typeTagExtractor)
+
+
+ if (typeTagExtractor != EmptyTree && unapplyMember(typeTagExtractor.tpe) != NoSymbol) {
+ // println("maybeWrapClassTagUnapply "+ typeTagExtractor)
+ // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true))
+ val args = List(uncheckedPattern)
+ // must call doTypedUnapply directly, as otherwise we get undesirable rewrites
+ // and re-typechecks of the target of the unapply call in PATTERNmode,
+ // this breaks down when the extractor (which defineds the unapply member) is not a simple reference to an object,
+ // but an arbitrary tree as is the case here
+ doTypedUnapply(Apply(typeTagExtractor, args), typeTagExtractor, typeTagExtractor, args, PATTERNmode, pt)
+ } else uncheckedPattern
+ }
+
+ case _ => uncheckedPattern
+ }
+ }
+
/**
* Convert an annotation constructor call into an AnnotationInfo.
*
@@ -4823,10 +4875,15 @@ trait Typers extends Modes with Adaptations with Tags {
}
case Typed(expr, tpt) =>
- val tpt1 = typedType(tpt, mode)
- val expr1 = typed(expr, onlyStickyModes(mode), tpt1.tpe.deconst)
- val ownType = if (isPatternMode) inferTypedPattern(tpt1, tpt1.tpe, pt) else tpt1.tpe
- treeCopy.Typed(tree, expr1, tpt1) setType ownType
+ val tptTyped = typedType(tpt, mode)
+ val exprTyped = typed(expr, onlyStickyModes(mode), tptTyped.tpe.deconst)
+ val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped)
+
+ if (isPatternMode) {
+ val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, pt)
+ maybeWrapClassTagUnapply(treeTyped setType ownType, tptTyped.tpe)
+ } else
+ treeTyped setType tptTyped.tpe
case TypeApply(fun, args) =>
// @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala
index 350003a8d8..f753dfbcbb 100644
--- a/src/library/scala/reflect/ClassTag.scala
+++ b/src/library/scala/reflect/ClassTag.scala
@@ -44,6 +44,16 @@ trait ClassTag[T] extends Equals with Serializable {
case _ => java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
}
+ /** A ClassTag[T] can serve as an extractor that matches only objects of type T.
+ *
+ * The compiler tries to turn unchecked type tests in pattern matches into checked ones
+ * by wrapping a `(_: T)` type pattern as `ct(_: T)`, where `ct` is the `ClassTag[T]` instance.
+ * Type tests necessary before calling other extractors are treated similarly.
+ * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)`
+ * is uncheckable, but we have an instance of `ClassTag[T]`.
+ */
+ def unapply(x: Any): Option[T] = if (runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[T]) else None
+
/** case class accessories */
override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]]
override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass