-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Ycheck failure in pattern matching against a value of type Nothing
#5067
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The problem is in the desugaring of the
|
Here's another one that fails with -Ycheck:
After desugaring:
In this case because of |
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
If we decide to accept matching against |
Note that class Test {
def test = {
val Some(x) = null
val Some(y) = ???
}
} Test.scala:3: error: constructor cannot be instantiated to expected type;
found : Some[A]
required: Null
val Some(x) = null
^
Test.scala:4: error: constructor cannot be instantiated to expected type;
found : Some[A]
required: Nothing
val Some(y) = ??? I don't see a good reason to allow it |
I don't have a strong opinion either way. Allowing it does seems to remove a special case.
|
IMO this should be accepted by -Ycheck. In fact calling any method of any class should be accepted on a receiver of type val x: Null = null
val y: String = x
println(y.substring(1)) which is obviously valid. Now let's assume I have some phase that makes types more precise, and hence produces: val x: Null = null
val y: Null = x
println(y.substring(1)) it should still be valid. Yes, the code is guaranteed to throw an NPE when it gets there, but it is still a valid type-safe tree. We had a similar issue in the IR checker of Scala.js at some point, which was discovered by the optimizer specializing some types. We relaxed the IR checker to consider any method call on |
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
@abeln I agree with @sjrd for the same reason (LSP); the current behavior also violates type preservation/subject reduction. But fixing it seems tricky enough to warrant a special issue. The change doesn't break existing code but removes compiler errors some people might expect (tho I don't know any valid usecases, I can imagine people exploiting this); nevertheless we should try this change. |
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
Fix #5067: handle scrutinee of bottom type in pattern matcher
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
… type `Nothing` When desugaring pattern matching code for expressions where the matched value has type `Null` or `Nothing`, we used to generate code that's type-incorrect. Example: ``` val Some(x) = null ``` got desugared into ``` val x: Nothing = matchResult1[Nothing]: { case val x1: Null @unchecked = null: Null @unchecked if x1.ne(null) then { case val x: Nothing = x1.value.asInstanceOf[Nothing] return[matchResult1] x: Nothing } else () return[matchResult1] throw new MatchError(x1) } ``` There were two problems here: 1) `x1.ne(null)` 2) `x1.value` In both cases, we're trying to invoke methods that don't exist for type `Nothing` (and #2 doesn't exist for `Null`). This commit changes the desugaring so we generate a no-op for unapply when the value matched has type `Nothing` or `Null`. This works because the code we used to generate is never executed (because the `x1.ne(null)`) check.
Given
test.scala
dotc -Ycheck:patternMatcher test.scala
fails withThe text was updated successfully, but these errors were encountered: