From 3d5758ca705be7f304d0a09f749e5742ec37231b Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 22 Feb 2013 22:04:58 +0100 Subject: SI-7171 Consider prefix when assessing type finality. `Type#isFinalType` determines if a type could have a non-bottom subtype. This property is exploited by the pattern matcher to flag impossible patterns. This check was ignoring the type's prefix, and incorrectly deemed that `T#A` in `trait T { final class A }` was a final type. But it could have been subtyped by `U#A` where `U` <:< `T`, or, more simply, by `T.this.A`. Now, type finality requires that the prefix is stable. The existing test cases in neg/patmat-type-check.scala still correctly flag incompatiblities. `isFinalType` is also used by some code that massages pattern matches post specialization. That is actually either broken or obsolete under virtpatmat, I've opened SI-7172 to invesigate that. It is also used by GenICode to determine whether to emit the appropriate equality checks that are correct in the face of boxing. It is possible that this change will force the slow path in some rare cases, but it won't affect correctness. --- src/reflect/scala/reflect/internal/Types.scala | 4 ++-- test/files/neg/t7171.check | 7 +++++++ test/files/neg/t7171.flags | 1 + test/files/neg/t7171.scala | 11 +++++++++++ test/files/neg/t7171b.check | 10 ++++++++++ test/files/neg/t7171b.flags | 1 + test/files/neg/t7171b.scala | 15 +++++++++++++++ test/files/run/t7171.scala | 22 ++++++++++++++++++++++ 8 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t7171.check create mode 100644 test/files/neg/t7171.flags create mode 100644 test/files/neg/t7171.scala create mode 100644 test/files/neg/t7171b.check create mode 100644 test/files/neg/t7171b.flags create mode 100644 test/files/neg/t7171b.scala create mode 100644 test/files/run/t7171.scala diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fb493fabd8..546df8a207 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -391,8 +391,8 @@ trait Types extends api.Types { self: SymbolTable => * This is assessed to be the case if the class is final, * and all type parameters (if any) are invariant. */ - def isFinalType = - typeSymbol.isFinal && (typeSymbol.typeParams forall symbolIsNonVariant) + def isFinalType: Boolean = + typeSymbol.isFinal && (typeSymbol.typeParams forall symbolIsNonVariant) && prefix.isStable /** Is this type completed (i.e. not a lazy type)? */ def isComplete: Boolean = true diff --git a/test/files/neg/t7171.check b/test/files/neg/t7171.check new file mode 100644 index 0000000000..8bdf08129b --- /dev/null +++ b/test/files/neg/t7171.check @@ -0,0 +1,7 @@ +t7171.scala:2: error: The outer reference in this type test cannot be checked at run time. + final case class A() + ^ +t7171.scala:9: error: The outer reference in this type test cannot be checked at run time. + case _: A => true; case _ => false + ^ +two errors found diff --git a/test/files/neg/t7171.flags b/test/files/neg/t7171.flags new file mode 100644 index 0000000000..464cc20ea6 --- /dev/null +++ b/test/files/neg/t7171.flags @@ -0,0 +1 @@ +-Xfatal-warnings -unchecked \ No newline at end of file diff --git a/test/files/neg/t7171.scala b/test/files/neg/t7171.scala new file mode 100644 index 0000000000..534b2070a3 --- /dev/null +++ b/test/files/neg/t7171.scala @@ -0,0 +1,11 @@ +trait T { + final case class A() + + // Was: + // error: scrutinee is incompatible with pattern type; + // found : T.this.A + // required: T#A + def foo(a: T#A) = a match { + case _: A => true; case _ => false + } +} diff --git a/test/files/neg/t7171b.check b/test/files/neg/t7171b.check new file mode 100644 index 0000000000..bd6b2bcfb4 --- /dev/null +++ b/test/files/neg/t7171b.check @@ -0,0 +1,10 @@ +t7171b.scala:2: error: The outer reference in this type test cannot be checked at run time. + final case class A() + ^ +t7171b.scala:8: error: The outer reference in this type test cannot be checked at run time. + case _: A => true; case _ => false + ^ +t7171b.scala:13: error: The outer reference in this type test cannot be checked at run time. + case _: A => true; case _ => false + ^ +three errors found diff --git a/test/files/neg/t7171b.flags b/test/files/neg/t7171b.flags new file mode 100644 index 0000000000..464cc20ea6 --- /dev/null +++ b/test/files/neg/t7171b.flags @@ -0,0 +1 @@ +-Xfatal-warnings -unchecked \ No newline at end of file diff --git a/test/files/neg/t7171b.scala b/test/files/neg/t7171b.scala new file mode 100644 index 0000000000..53c7787f8b --- /dev/null +++ b/test/files/neg/t7171b.scala @@ -0,0 +1,15 @@ +trait T { + final case class A() +} + +final class U extends T { + // this match should also not be deemed impossible + def foo(a: U#A) = a match { + case _: A => true; case _ => false + } + + // this match should also not be deemed impossible + def bar(a: T#A) = a match { + case _: A => true; case _ => false + } +} diff --git a/test/files/run/t7171.scala b/test/files/run/t7171.scala new file mode 100644 index 0000000000..97585b9860 --- /dev/null +++ b/test/files/run/t7171.scala @@ -0,0 +1,22 @@ +trait T { + final case class A() + + // Was: + // error: scrutinee is incompatible with pattern type; + // found : T.this.A + // required: T#A + def foo(a: T#A) = a match { + case _: A => true; case _ => false + } +} + +object Test extends App { + val t1 = new T {} + val t2 = new T {} + val a1 = new t1.A() + val a2 = new t1.A() + assert(t1.foo(a1)) + // as noted in the unchecked warning (tested in the corresponding neg test), + // the outer pointer isn't checked + assert(t1.foo(a2)) +} -- cgit v1.2.3