diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-12-18 10:07:23 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-12-18 10:07:23 -0800 |
commit | ecc63690f11c0c72bff62c79277761b0a04f259c (patch) | |
tree | 72abc8e4d759eb812dea0bbd8068cfea33e042fe | |
parent | 640c95500e73f811fb86c4267afc504ff3be1e7b (diff) | |
parent | 5b8327b807c40cab867065cf695191a36c9210ee (diff) | |
download | scala-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.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala | 38 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 8 | ||||
-rw-r--r-- | test/files/pos/t7459a.scala | 18 | ||||
-rw-r--r-- | test/files/pos/t7459b.scala | 12 | ||||
-rw-r--r-- | test/files/pos/t7459c.scala | 18 | ||||
-rw-r--r-- | test/files/pos/t7459d.scala | 8 | ||||
-rw-r--r-- | test/files/pos/t7704.scala | 10 | ||||
-rw-r--r-- | test/files/run/t7459a.scala | 14 | ||||
-rw-r--r-- | test/files/run/t7459b-optimize.flags | 1 | ||||
-rw-r--r-- | test/files/run/t7459b-optimize.scala | 21 | ||||
-rw-r--r-- | test/files/run/t7459b.scala | 21 | ||||
-rw-r--r-- | test/files/run/t7459c.scala | 16 | ||||
-rw-r--r-- | test/files/run/t7459d.scala | 15 | ||||
-rw-r--r-- | test/files/run/t7459f.scala | 12 |
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]) + } +} |