From c001b888b896989a2c0afa0c24d038502970151c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 28 Jan 2014 16:43:40 -0800 Subject: SI-1503 don't assume unsound type for ident/literal patterns The fix only kicks in under -Xfuture. We also warn under -Xlint. What type should a variable bound to the value matched by a pattern have? To avoid CCEs, it should be a type that's implied by the matching semantics of the pattern. Usually, the type implied by a pattern matching a certain value is the pattern's type, because pattern matching implies instance-of checks. However, Stable Identifier and Literal patterns are matched using `==`, which does not imply a type for the binder that binds the matched value. The change in type checking due to this fix is that programs that used to crash with a CCE (because we blindly cast to the type of the pattern, which a `==` check does not imply) now get a weaker type instead (and no cast). They may still type check, or they may not. To compensate for this fix, change `case x@Foo => x` to `case x: Foo.type => x`, if it's important that `x` have type `Foo.type`. See also: - SI-4577: matching of singleton type patterns uses `eq`, not `==` (so that the types are not a lie). - SI-5024: patmat strips unused bindings, but affects semantics --- test/files/neg/t1503.check | 8 ++++++++ test/files/neg/t1503.flags | 1 + test/files/neg/t1503.scala | 8 ++++++++ test/files/run/t1503.check | 1 + test/files/run/t1503.scala | 20 ++++++++++++++++++++ test/files/run/t1503_future.flags | 1 + test/files/run/t1503_future.scala | 17 +++++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 test/files/neg/t1503.check create mode 100644 test/files/neg/t1503.flags create mode 100644 test/files/neg/t1503.scala create mode 100644 test/files/run/t1503.check create mode 100644 test/files/run/t1503.scala create mode 100644 test/files/run/t1503_future.flags create mode 100644 test/files/run/t1503_future.scala (limited to 'test') diff --git a/test/files/neg/t1503.check b/test/files/neg/t1503.check new file mode 100644 index 0000000000..7adeea20f3 --- /dev/null +++ b/test/files/neg/t1503.check @@ -0,0 +1,8 @@ +t1503.scala:7: warning: The value matched by Whatever is bound to n, which may be used under the +unsound assumption that it has type Whatever.type, whereas we can only safely +count on it having type Any, as the pattern is matched using `==` (see SI-1503). + def matchWhateverCCE(x: Any) = x match { case n @ Whatever => n } + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/t1503.flags b/test/files/neg/t1503.flags new file mode 100644 index 0000000000..e93641e931 --- /dev/null +++ b/test/files/neg/t1503.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings \ No newline at end of file diff --git a/test/files/neg/t1503.scala b/test/files/neg/t1503.scala new file mode 100644 index 0000000000..9877f99d0a --- /dev/null +++ b/test/files/neg/t1503.scala @@ -0,0 +1,8 @@ +object Whatever { + override def equals(x: Any) = true +} + +class Test { + // when left to its own devices, and not under -Xfuture, the return type is Whatever.type + def matchWhateverCCE(x: Any) = x match { case n @ Whatever => n } +} \ No newline at end of file diff --git a/test/files/run/t1503.check b/test/files/run/t1503.check new file mode 100644 index 0000000000..43eceb0229 --- /dev/null +++ b/test/files/run/t1503.check @@ -0,0 +1 @@ +whoops diff --git a/test/files/run/t1503.scala b/test/files/run/t1503.scala new file mode 100644 index 0000000000..1be0e74ac2 --- /dev/null +++ b/test/files/run/t1503.scala @@ -0,0 +1,20 @@ +object Whatever { + override def equals(x: Any) = true +} + +object Test extends App { + // this should make it abundantly clear Any is the best return type we can guarantee + def matchWhatever(x: Any): Any = x match { case n @ Whatever => n } + // when left to its own devices, and not under -Xfuture, the return type is Whatever.type + def matchWhateverCCE(x: Any) = x match { case n @ Whatever => n } + + // just to exercise it a bit + assert(matchWhatever(1) == 1) + assert(matchWhatever("1") == "1") + + try { + matchWhateverCCE("1"): Whatever.type + } catch { + case _: ClassCastException => println("whoops") + } +} \ No newline at end of file diff --git a/test/files/run/t1503_future.flags b/test/files/run/t1503_future.flags new file mode 100644 index 0000000000..112fc720a0 --- /dev/null +++ b/test/files/run/t1503_future.flags @@ -0,0 +1 @@ +-Xfuture \ No newline at end of file diff --git a/test/files/run/t1503_future.scala b/test/files/run/t1503_future.scala new file mode 100644 index 0000000000..1e3daad761 --- /dev/null +++ b/test/files/run/t1503_future.scala @@ -0,0 +1,17 @@ +object Whatever { + override def equals(x: Any) = true +} + +object Test extends App { + // this should make it abundantly clear Any is the best return type we can guarantee + def matchWhatever(x: Any): Any = x match { case n @ Whatever => n } + // when left to its own devices, and not under -Xfuture, the return type is Whatever.type + def matchWhateverCCE(x: Any) = x match { case n @ Whatever => n } + + // just to exercise it a bit + assert(matchWhatever(1) == 1) + assert(matchWhatever("1") == "1") + + assert(matchWhateverCCE(1) == 1) + assert(matchWhateverCCE("1") == "1") +} \ No newline at end of file -- cgit v1.2.3