From 7046d73e2a1d88273a8382f27a4fb0af6f87db3b Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 23 May 2014 12:26:43 +0200 Subject: 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. --- test/files/run/t8611a.flags | 1 + test/files/run/t8611a.scala | 16 +++++++ test/files/run/t8611b.flags | 1 + test/files/run/t8611b.scala | 54 +++++++++++++++++++++++ test/files/run/t8611c.flags | 1 + test/files/run/t8611c.scala | 21 +++++++++ test/junit/scala/reflect/internal/TypesTest.scala | 35 +++++++++++++++ 7 files changed, 129 insertions(+) create mode 100644 test/files/run/t8611a.flags create mode 100644 test/files/run/t8611a.scala create mode 100644 test/files/run/t8611b.flags create mode 100644 test/files/run/t8611b.scala create mode 100644 test/files/run/t8611c.flags create mode 100644 test/files/run/t8611c.scala create mode 100644 test/junit/scala/reflect/internal/TypesTest.scala (limited to 'test') diff --git a/test/files/run/t8611a.flags b/test/files/run/t8611a.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611a.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611a.scala b/test/files/run/t8611a.scala new file mode 100644 index 0000000000..99304df762 --- /dev/null +++ b/test/files/run/t8611a.scala @@ -0,0 +1,16 @@ +trait K +trait L + +object O { + type LK = K with L + val A: LK = new K with L + val B: LK = new K with L +} + +object Test extends App { + val scrut: O.LK = O.B + scrut match { + case O.A => ??? + case O.B => // spurious unreachable + } +} diff --git a/test/files/run/t8611b.flags b/test/files/run/t8611b.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611b.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611b.scala b/test/files/run/t8611b.scala new file mode 100644 index 0000000000..2df17c9ca0 --- /dev/null +++ b/test/files/run/t8611b.scala @@ -0,0 +1,54 @@ +sealed trait KrafsDescription + +abstract class NotWorkingEnum extends Enumeration { + + type ExtendedValue = Value with KrafsDescription + + def Enum(inDescription: String): ExtendedValue = { + new Val(nextId) with KrafsDescription { + } + } +} + +abstract class WorkingEnum extends Enumeration { + + type ExtendedValue = Value + + def Enum(inDescription: String): ExtendedValue = { + new Val(nextId) { + } + } +} + +object NotWorkingTab extends NotWorkingEnum { + val a = Enum("A") + val b = Enum("B") +} + +object WorkingTab extends WorkingEnum { + val a = Enum("A") + val b = Enum("B") +} + +object Test extends App { + testGris() + testWorking() + + def testGris() { + val pipp = NotWorkingTab.b + pipp match { + case NotWorkingTab.a => ??? + case NotWorkingTab.b => + case _ => ??? + } + } + + def testWorking() { + val stuff = WorkingTab.a + stuff match { + case WorkingTab.a => + case WorkingTab.b => ??? + case _ => ??? + } + } +} diff --git a/test/files/run/t8611c.flags b/test/files/run/t8611c.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611c.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611c.scala b/test/files/run/t8611c.scala new file mode 100644 index 0000000000..2bd17f29a5 --- /dev/null +++ b/test/files/run/t8611c.scala @@ -0,0 +1,21 @@ +trait K +trait L + +object O { + type LK = K with L +} + +object Test extends App { + local + + def local = { + val A: O.LK = new K with L + val B: O.LK = new K with L + val scrut: O.LK = A + scrut match { + case B if "".isEmpty => ??? + case A => + case B => ??? + } + } +} diff --git a/test/junit/scala/reflect/internal/TypesTest.scala b/test/junit/scala/reflect/internal/TypesTest.scala new file mode 100644 index 0000000000..95194ef0a4 --- /dev/null +++ b/test/junit/scala/reflect/internal/TypesTest.scala @@ -0,0 +1,35 @@ +package scala.reflect.internal + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import scala.tools.nsc.symtab.SymbolTableForUnitTesting + +@RunWith(classOf[JUnit4]) +class TypesTest { + + object symbolTable extends SymbolTableForUnitTesting + import symbolTable._, definitions._ + + @Test + def testRefinedTypeSI8611(): Unit = { + def stringNarrowed = StringTpe.narrow + assert(stringNarrowed != stringNarrowed) + assert(!(stringNarrowed =:= stringNarrowed)) + + def boolWithString = refinedType(BooleanTpe :: StringTpe :: Nil, NoSymbol) + assert(boolWithString != boolWithString) + assert(boolWithString =:= boolWithString) + + val boolWithString1 = boolWithString + val boolWithString1narrow1 = boolWithString1.narrow + val boolWithString1narrow2 = boolWithString1.narrow + // Two narrowings of the same refinement end up =:=. This was the root + // cause of SI-8611. See `narrowUniquely` in `Logic` for the workaround. + assert(boolWithString1narrow1 =:= boolWithString1narrow2) + val uniquelyNarrowed1 = refinedType(boolWithString1narrow1 :: Nil, NoSymbol) + val uniquelyNarrowed2 = refinedType(boolWithString1narrow2 :: Nil, NoSymbol) + assert(uniquelyNarrowed1 =:= uniquelyNarrowed2) + } +} -- cgit v1.2.3