summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-05-23 12:26:43 +0200
committerJason Zaugg <jzaugg@gmail.com>2014-06-10 17:30:28 +0200
commit7046d73e2a1d88273a8382f27a4fb0af6f87db3b (patch)
tree14a2ea05fde37f519323647336f9f380b9beb70a /src
parentddb29a8105bc3b692bc129cbd8ed111baae7076d (diff)
downloadscala-7046d73e2a1d88273a8382f27a4fb0af6f87db3b.tar.gz
scala-7046d73e2a1d88273a8382f27a4fb0af6f87db3b.tar.bz2
scala-7046d73e2a1d88273a8382f27a4fb0af6f87db3b.zip
SI-8611 Avoid accidental patmat unification with refinement types
In the enclosed test, t8611a.scala, the patterns `O.{A, B}` were incorrect treated as equivelent by the combination of `uniqueTpForTree` and `Const.uniqueTpForTree`. `uniqueTpForTree` used `Type#narrow` to try to create a distinct type for each new pattern tree it encountered. However, narrowing a `RefinedType` does not create a distinct type as we are used to when narrowing, e.g. a class type. // Type def narrow: Type = if (phase.erasedTypes) this else { val cowner = commonOwner(this) refinedType(this :: Nil, cowner, EmptyScope, cowner.pos).narrow } // CompoundType override def narrow: Type = typeSymbol.thisType This commit creates a fresh existential type symbol rather than trying to use `narrow`. I've included a unit test to show the sublteties of narrowing refinment types.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala25
1 files changed, 16 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index fde0aca584..0899507bab 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -505,7 +505,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
}
- import global.{ConstantType, Constant, SingletonType, Literal, Ident, singleType}
+ import global.{ConstantType, Constant, EmptyScope, SingletonType, Literal, Ident, refinedType, singleType, TypeBounds, NoSymbol}
import global.definitions._
@@ -538,23 +538,30 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
private val trees = mutable.HashSet.empty[Tree]
// hashconsing trees (modulo value-equality)
- private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type =
- // a new type for every unstable symbol -- only stable value are uniqued
- // technically, an unreachable value may change between cases
- // thus, the failure of a case that matches on a mutable value does not exclude the next case succeeding
- // (and thuuuuus, the latter case must be considered reachable)
- if (!t.symbol.isStable) t.tpe.narrow
+ private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = {
+ def freshExistentialSubtype(tp: Type): Type = {
+ // SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation.
+ NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe
+ }
+
+ if (!t.symbol.isStable) {
+ // Create a fresh type for each unstable value, since we can never correlate it to another value.
+ // For example `case X => case X =>` should not complaing about the second case being unreachable,
+ // if X is mutable.
+ freshExistentialSubtype(t.tpe)
+ }
else trees find (a => a.correspondsStructure(t)(sameValue)) match {
case Some(orig) =>
- debug.patmat("unique tp for tree: "+ ((orig, orig.tpe)))
+ debug.patmat("unique tp for tree: " + ((orig, orig.tpe)))
orig.tpe
case _ =>
// duplicate, don't mutate old tree (TODO: use a map tree -> type instead?)
- val treeWithNarrowedType = t.duplicate setType t.tpe.narrow
+ val treeWithNarrowedType = t.duplicate setType freshExistentialSubtype(t.tpe)
debug.patmat("uniqued: "+ ((t, t.tpe, treeWithNarrowedType.tpe)))
trees += treeWithNarrowedType
treeWithNarrowedType.tpe
}
+ }
}
sealed abstract class Const {