summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2014-12-18 10:07:23 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2014-12-18 10:07:23 -0800
commitecc63690f11c0c72bff62c79277761b0a04f259c (patch)
tree72abc8e4d759eb812dea0bbd8068cfea33e042fe
parent640c95500e73f811fb86c4267afc504ff3be1e7b (diff)
parent5b8327b807c40cab867065cf695191a36c9210ee (diff)
downloadscala-ecc63690f11c0c72bff62c79277761b0a04f259c.tar.gz
scala-ecc63690f11c0c72bff62c79277761b0a04f259c.tar.bz2
scala-ecc63690f11c0c72bff62c79277761b0a04f259c.zip
Merge pull request #4122 from retronym/ticket/7459-2
SI-7459 Handle pattern binders used as prefixes in TypeTrees.
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala38
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala8
-rw-r--r--test/files/pos/t7459a.scala18
-rw-r--r--test/files/pos/t7459b.scala12
-rw-r--r--test/files/pos/t7459c.scala18
-rw-r--r--test/files/pos/t7459d.scala8
-rw-r--r--test/files/pos/t7704.scala10
-rw-r--r--test/files/run/t7459a.scala14
-rw-r--r--test/files/run/t7459b-optimize.flags1
-rw-r--r--test/files/run/t7459b-optimize.scala21
-rw-r--r--test/files/run/t7459b.scala21
-rw-r--r--test/files/run/t7459c.scala16
-rw-r--r--test/files/run/t7459d.scala15
-rw-r--r--test/files/run/t7459f.scala12
17 files changed, 219 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
index 3223d99ad0..87037b7ccc 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
@@ -266,7 +266,7 @@ trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchT
// the type of the binder passed to the first invocation
// determines the type of the tree that'll be returned for that binder as of then
final def binderToUniqueTree(b: Symbol) =
- unique(accumSubst(normalize(CODE.REF(b))), b.tpe)
+ unique(accumSubst(normalize(gen.mkAttributedStableRef(b))), b.tpe)
// note that the sequencing of operations is important: must visit in same order as match execution
// binderToUniqueTree uses the type of the first symbol that was encountered as the type for all future binders
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index 25f7224667..0678ec52e7 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -248,7 +248,10 @@ trait MatchTranslation {
if (caseDefs forall treeInfo.isCatchCase) caseDefs
else {
val swatches = { // switch-catches
- val bindersAndCases = caseDefs map { caseDef =>
+ // SI-7459 must duplicate here as we haven't commited to switch emission, and just figuring out
+ // if we can ends up mutating `caseDefs` down in the use of `substituteSymbols` in
+ // `TypedSubstitution#Substitution`. That is called indirectly by `emitTypeSwitch`.
+ val bindersAndCases = caseDefs.map(_.duplicate) map { caseDef =>
// generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there)
// if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this)
val caseScrutSym = freshSym(pos, pureType(ThrowableTpe))
@@ -518,7 +521,7 @@ trait MatchTranslation {
// reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component
override protected def tupleSel(binder: Symbol)(i: Int): Tree = {
val accessors = binder.caseFieldAccessors
- if (accessors isDefinedAt (i-1)) REF(binder) DOT accessors(i-1)
+ if (accessors isDefinedAt (i-1)) gen.mkAttributedStableRef(binder) DOT accessors(i-1)
else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 647719bf3e..971a019e4e 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -167,8 +167,17 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
val usedBinders = new mutable.HashSet[Symbol]()
// all potentially stored subpat binders
val potentiallyStoredBinders = stored.unzip._1.toSet
+ def ref(sym: Symbol) =
+ if (potentiallyStoredBinders(sym)) usedBinders += sym
// compute intersection of all symbols in the tree `in` and all potentially stored subpat binders
- in.foreach(t => if (potentiallyStoredBinders(t.symbol)) usedBinders += t.symbol)
+ in.foreach {
+ case tt: TypeTree =>
+ tt.tpe foreach { // SI-7459 e.g. case Prod(t) => new t.u.Foo
+ case SingleType(_, sym) => ref(sym)
+ case _ =>
+ }
+ case t => ref(t.symbol)
+ }
if (usedBinders.isEmpty) in
else {
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
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index aaa75b5ee1..6671183ff0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3776,8 +3776,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case TypeRef(pre, sym, args) =>
if (sym.isAliasType && containsLocal(tp) && (tp.dealias ne tp)) apply(tp.dealias)
else {
- if (pre.isVolatile)
- InferTypeWithVolatileTypeSelectionError(tree, pre)
+ if (pre.isVolatile) pre match {
+ case SingleType(_, sym) if sym.isSynthetic && isPastTyper =>
+ debuglog(s"ignoring volatility of prefix in pattern matcher generated inferred type: $tp") // See pos/t7459c.scala
+ case _ =>
+ InferTypeWithVolatileTypeSelectionError(tree, pre)
+ }
mapOver(tp)
}
case _ =>
diff --git a/test/files/pos/t7459a.scala b/test/files/pos/t7459a.scala
new file mode 100644
index 0000000000..5107715e06
--- /dev/null
+++ b/test/files/pos/t7459a.scala
@@ -0,0 +1,18 @@
+trait SpecialException extends Throwable
+
+object Test {
+ def run() {
+ try {
+ ???
+ } catch {
+ case e: SpecialException => e.isInstanceOf[SpecialException]
+ case e =>
+ }
+
+ // OKAY
+ // (null: Throwable) match {
+ // case e: SpecialException => e.isInstanceOf[SpecialException]
+ // case e =>
+ // }
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/t7459b.scala b/test/files/pos/t7459b.scala
new file mode 100644
index 0000000000..a4b4fd07a9
--- /dev/null
+++ b/test/files/pos/t7459b.scala
@@ -0,0 +1,12 @@
+import scala.concurrent._
+import scala.util._
+
+
+class Test {
+ (null: Any) match {
+ case s @ Some(_) => ???
+ case f @ _ =>
+ () => f
+ ???
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/t7459c.scala b/test/files/pos/t7459c.scala
new file mode 100644
index 0000000000..dc2605abe6
--- /dev/null
+++ b/test/files/pos/t7459c.scala
@@ -0,0 +1,18 @@
+object Test {
+ trait Universe {
+ type Type
+ type TypeTag[A] >: Null <: TypeTagApi[A]
+ trait TypeTagApi[A] { def tpe: Type }
+ }
+ trait JavaUniverse extends Universe
+
+ trait Mirror[U <: Universe] {
+ def universe: U
+ }
+ (null: Mirror[_]).universe match {
+ case ju: JavaUniverse =>
+ val ju1 = ju
+ val f = {() => (null: ju.TypeTag[Nothing]).tpe }
+ }
+ trait M[A]
+}
diff --git a/test/files/pos/t7459d.scala b/test/files/pos/t7459d.scala
new file mode 100644
index 0000000000..7843156885
--- /dev/null
+++ b/test/files/pos/t7459d.scala
@@ -0,0 +1,8 @@
+class Test {
+ (null: Any) match {
+ case s @ Some(_) => ???
+ case f @ _ =>
+ () => f
+ ???
+ }
+}
diff --git a/test/files/pos/t7704.scala b/test/files/pos/t7704.scala
new file mode 100644
index 0000000000..cae88d3324
--- /dev/null
+++ b/test/files/pos/t7704.scala
@@ -0,0 +1,10 @@
+class Attr { type V ; class Val }
+class StrAttr extends Attr { type V = String }
+class BoolAttr extends Attr { type V = Boolean }
+
+object Main {
+ def f(x: Attr) = x match {
+ case v: StrAttr => new v.Val
+ case v: BoolAttr => new v.Val
+ }
+}
diff --git a/test/files/run/t7459a.scala b/test/files/run/t7459a.scala
new file mode 100644
index 0000000000..e9653c6e79
--- /dev/null
+++ b/test/files/run/t7459a.scala
@@ -0,0 +1,14 @@
+class LM {
+ class Node[B1]
+ case class CC(n: LM)
+
+ // crash
+ val f: (LM => Any) = {
+ case tttt =>
+ new tttt.Node[Any]()
+ }
+}
+
+object Test extends App {
+ new LM().f(new LM())
+}
diff --git a/test/files/run/t7459b-optimize.flags b/test/files/run/t7459b-optimize.flags
new file mode 100644
index 0000000000..49d036a887
--- /dev/null
+++ b/test/files/run/t7459b-optimize.flags
@@ -0,0 +1 @@
+-optimize
diff --git a/test/files/run/t7459b-optimize.scala b/test/files/run/t7459b-optimize.scala
new file mode 100644
index 0000000000..605890962c
--- /dev/null
+++ b/test/files/run/t7459b-optimize.scala
@@ -0,0 +1,21 @@
+class LM {
+ class Node[B1]
+
+ // crash
+ val g: (CC => Any) = {
+ case CC(tttt) =>
+ new tttt.Node[Any]()
+ }
+
+ val h: (Some[CC] => Any) = {
+ case Some(CC(tttt)) =>
+ new tttt.Node[Any]()
+ }
+}
+
+object Test extends App {
+ new LM().g(new CC(new LM()))
+ new LM().h(Some(new CC(new LM())))
+}
+case class CC(n: LM)
+
diff --git a/test/files/run/t7459b.scala b/test/files/run/t7459b.scala
new file mode 100644
index 0000000000..605890962c
--- /dev/null
+++ b/test/files/run/t7459b.scala
@@ -0,0 +1,21 @@
+class LM {
+ class Node[B1]
+
+ // crash
+ val g: (CC => Any) = {
+ case CC(tttt) =>
+ new tttt.Node[Any]()
+ }
+
+ val h: (Some[CC] => Any) = {
+ case Some(CC(tttt)) =>
+ new tttt.Node[Any]()
+ }
+}
+
+object Test extends App {
+ new LM().g(new CC(new LM()))
+ new LM().h(Some(new CC(new LM())))
+}
+case class CC(n: LM)
+
diff --git a/test/files/run/t7459c.scala b/test/files/run/t7459c.scala
new file mode 100644
index 0000000000..144c5d793b
--- /dev/null
+++ b/test/files/run/t7459c.scala
@@ -0,0 +1,16 @@
+class LM {
+ class Node[B1]
+
+ // crash
+ val g: (CC => Any) = {
+ case CC(tttt) =>
+ tttt.## // no crash
+ new tttt.Node[Any]()
+ }
+}
+
+object Test extends App {
+ new LM().g(new CC(new LM()))
+}
+case class CC(n: LM)
+
diff --git a/test/files/run/t7459d.scala b/test/files/run/t7459d.scala
new file mode 100644
index 0000000000..3263701f9d
--- /dev/null
+++ b/test/files/run/t7459d.scala
@@ -0,0 +1,15 @@
+class LM {
+ class Node[B1]
+ case class CC(n: LM)
+
+ // crash
+ val f: (LM => Any) = {
+ case tttt =>
+ val uuuu: (tttt.type, Any) = (tttt, 0)
+ new uuuu._1.Node[Any]()
+ }
+}
+
+object Test extends App {
+ new LM().f(new LM())
+}
diff --git a/test/files/run/t7459f.scala b/test/files/run/t7459f.scala
new file mode 100644
index 0000000000..63e2109560
--- /dev/null
+++ b/test/files/run/t7459f.scala
@@ -0,0 +1,12 @@
+object Test extends App {
+ class C
+
+ case class FooSeq(x: Int, y: String, z: C*)
+
+ FooSeq(1, "a", new C()) match {
+ case FooSeq(1, "a", x@_* ) =>
+ //println(x.toList)
+ x.asInstanceOf[x.type]
+ assert(x.isInstanceOf[x.type])
+ }
+}