diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-05-23 12:26:43 +0200 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-06-10 17:30:28 +0200 |
commit | 7046d73e2a1d88273a8382f27a4fb0af6f87db3b (patch) | |
tree | 14a2ea05fde37f519323647336f9f380b9beb70a /src | |
parent | ddb29a8105bc3b692bc129cbd8ed111baae7076d (diff) | |
download | scala-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.scala | 25 |
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 { |