summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-05-07 23:29:02 +0200
committerJason Zaugg <jzaugg@gmail.com>2014-11-14 22:19:49 +1000
commit5b8327b807c40cab867065cf695191a36c9210ee (patch)
tree8e3964382814439fe2436fa37941610179a92781 /src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
parentb431a4bd83d3bfb2b95d0426d2905b34ce1265ad (diff)
downloadscala-5b8327b807c40cab867065cf695191a36c9210ee.tar.gz
scala-5b8327b807c40cab867065cf695191a36c9210ee.tar.bz2
scala-5b8327b807c40cab867065cf695191a36c9210ee.zip
SI-7459 Handle pattern binders used as prefixes in TypeTrees.
Match translation was incorrect for: case t => new t.C case D(t) => new d.C We would end up with Types in TypeTrees referring to the wrong symbols, e.g: // t7459a.scala ((x0$1: this.LM) => { case <synthetic> val x1: this.LM = x0$1; case4(){ matchEnd3(new tttt.Node[Any]()) }; matchEnd3(x: Any){ x } Or: // t7459b.scala ((x0$1: CC) => { case <synthetic> val x1: CC = x0$1; case4(){ if (x1.ne(null)) matchEnd3(new tttt.Node[Any]()) else case5() }; This commit: - Changes `bindSubPats` to traverse types, as well as terms, in search of references to bound symbols - Changes `Substitution` to reuse `Tree#substituteSymbols` rather than the home-brew substitution from `Tree`s to `Tree`s, if the `to` trees are all `Ident`s - extends `substIdentsForTrees` to substitute selections like `x1.caseField` into types. I had to dance around the awkward handling of "swatches" (exception handlers that can be implemented with JVM native type switches) by duplicating trees to avoid seeing the results of `substituteSymbols` in `caseDefs` after we abandon that approach if we detect the patterns are too complex late in the game. I also had to add an escape hatch for the "type selection from volatile type" check in the type checker. Without this, the translation of `pos/t7459c.scala`: case <synthetic> val x1: _$1 = (null: Test.Mirror[_]).universe; case5(){ if (x1.isInstanceOf[Test.JavaUniverse]) { <synthetic> val x2: _$1 with Test.JavaUniverse = (x1.asInstanceOf[_$1 with Test.JavaUniverse]: _$1 with Test.JavaUniverse); matchEnd4({ val ju1: Test.JavaUniverse = x2; val f: () => x2.Type = (() => (null: x2.TypeTag[Nothing]).tpe); .. triggers that error at `x2.TypeTag`.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala')
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala38
1 files changed, 31 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
index ef50e083a1..d35aad964d 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
@@ -12,7 +12,7 @@ import scala.language.postfixOps
import scala.tools.nsc.transform.TypingTransformers
import scala.tools.nsc.transform.Transform
import scala.reflect.internal.util.Statistics
-import scala.reflect.internal.Types
+import scala.reflect.internal.{Mode, Types}
import scala.reflect.internal.util.Position
/** Translate pattern matching.
@@ -198,33 +198,57 @@ trait Interface extends ast.TreeDSL {
}
class Substitution(val from: List[Symbol], val to: List[Tree]) {
- import global.{Transformer, Ident, NoType}
+ import global.{Transformer, Ident, NoType, TypeTree, SingleType}
// We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed,
// and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees.
def apply(tree: Tree): Tree = {
// according to -Ystatistics 10% of translateMatch's time is spent in this method...
// since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst
- if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree
- else (new Transformer {
+ val toIdents = to.forall(_.isInstanceOf[Ident])
+ val containsSym = tree.exists {
+ case i@Ident(_) => from contains i.symbol
+ case tt: TypeTree => tt.tpe.exists {
+ case SingleType(_, sym) =>
+ (from contains sym) && {
+ if (!toIdents) global.devWarning(s"Unexpected substitution of non-Ident into TypeTree `$tt`, subst= $this")
+ true
+ }
+ case _ => false
+ }
+ case _ => false
+ }
+ val toSyms = to.map(_.symbol)
+ object substIdentsForTrees extends Transformer {
private def typedIfOrigTyped(to: Tree, origTp: Type): Tree =
if (origTp == null || origTp == NoType) to
// important: only type when actually substing and when original tree was typed
// (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors)
else typer.typed(to)
+ def typedStable(t: Tree) = typer.typed(t.shallowDuplicate, Mode.MonoQualifierModes | Mode.TYPEPATmode)
+ lazy val toTypes: List[Type] = to map (tree => typedStable(tree).tpe)
+
override def transform(tree: Tree): Tree = {
def subst(from: List[Symbol], to: List[Tree]): Tree =
if (from.isEmpty) tree
- else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate.setPos(tree.pos), tree.tpe)
+ else if (tree.symbol == from.head) typedIfOrigTyped(typedStable(to.head).setPos(tree.pos), tree.tpe)
else subst(from.tail, to.tail)
- tree match {
+ val tree1 = tree match {
case Ident(_) => subst(from, to)
case _ => super.transform(tree)
}
+ tree1.modifyType(_.substituteTypes(from, toTypes))
}
- }).transform(tree)
+ }
+ if (containsSym) {
+ if (to.forall(_.isInstanceOf[Ident]))
+ tree.duplicate.substituteSymbols(from, to.map(_.symbol)) // SI-7459 catches `case t => new t.Foo`
+ else
+ substIdentsForTrees.transform(tree)
+ }
+ else tree
}