Skip to content

Relax comparison between Null and reference types in explicit nulls #23308

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

SuperCl4sh
Copy link
Contributor

An up-to-date version of #19258 with merge conflicts resolved.

@SuperCl4sh
Copy link
Contributor Author

cc @olhotak @HarrisL2

@SuperCl4sh SuperCl4sh force-pushed the updated-relaxed-equal-nulls branch from 04432cb to c909ba2 Compare June 3, 2025 19:47
Comment on lines 4 to 13
def f6(s: String | Null): String = s match {
case s2 => s2 // error
case null => "other" // error
case null => "other"
case s3 => s3
}

def f7(s: String | Null): String = s match {
case null => "other"
case null => "other" // error
case null => "other"
case s3 => s3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this test. If we matched null value, then it is clear the following null cases are unreachable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is caused by the change here:
https://github.com/scala/scala3/pull/23308/files#diff-9678fa10ff9f7a2cdf71755535b6ef49afd31880e9d159ed4c40a39921d43454R972

That change was in the original PR #19258 . I wonder if it has been subsumed by other changes since then and might no longer be necessary. Without that change, what would break?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running scalac tests/explicit-nulls/neg/flow-match.scala -Yexplicit-nulls with checkReachability(m) produces the same compiler output as withoutMode(Mode.safeNulls) { checkReachability(m) }:

-- [E007] Type Mismatch Error: tests\explicit-nulls\neg\flow-match.scala:5:15 --
5 |    case s2 => s2 // error
  |               ^^
  |               Found:    (s2 : String | Null)
  |               Required: String
  |
  | longer explanation available when compiling with `-explain`
1 error found

Deleting f6 and re-running causes both changes to warn about unreachability in f7:

-- [E030] Match case Unreachable Warning: tests\explicit-nulls\neg\flow-match.scala:7:9 
7 |    case null => "other"
  |         ^^^^
  |         Unreachable case
1 warning found

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an error, just a warning. We can change the test case to remove f7 entirely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, or keep it in as a positive part of the test. (No need to make it a separate pos test file.)
#23308 (comment)

@olhotak
Copy link
Contributor

olhotak commented Jun 6, 2025

Before this PR, all three errors in flow-match come from the typer phase:

-- [E007] Type Mismatch Error: tests/explicit-nulls/neg/flow-match.scala:5:15 --
5 |    case s2 => s2 // error
  |               ^^
  |               Found:    (s2 : String | Null)
  |               Required: String
  |
  | longer explanation available when compiling with `-explain`
-- [E172] Type Error: tests/explicit-nulls/neg/flow-match.scala:6:9 ------------
6 |    case null => "other" // error
  |         ^^^^
  |         Values of types Null and String cannot be compared with == or !=
-- [E172] Type Error: tests/explicit-nulls/neg/flow-match.scala:12:9 -----------
12 |    case null => "other" // error
   |         ^^^^
   |        Values of types Null and String cannot be compared with == or !=
3 errors found

After this PR, the two errors on lines 6 and 12 understandably go away.

The error on line 5 prevents the compiler from getting past typer, so match reachability checking never happens.

The test was not intended for match reachability checking but to test for the flow-sensitive analysis in typer.

We should split the test into two, one that errors only in typer, and one that tests only match reachability checking.

@olhotak
Copy link
Contributor

olhotak commented Jun 6, 2025

Here are the proposed two tests. Note that the second one needs to go in warn, not neg.

// Test flow-typing when NotNullInfos are from cases

object MatchTest {
  def f6(s: String | Null): String = s match {
    case s2 => s2 // error
    case s3 => s3 // OK since not null
  }

  def f7(s: String | Null): String = s match {
    case null => "other"
    case s3 => s3 // OK since not null
  }
}
// Test unreachable matches in presence of nulls

object MatchTest2 {
  def f6(s: String | Null): String = s match {
    case s2 => s2.nn
    case null => "other" // warn
    case s3 => s3 // warn
  }

  def f7(s: String | Null): String = s match {
    case null => "other"
    case null => "other" // warn
    case s3: String => s3
    case s4 => s4.nn // warn
  }
}

@@ -969,5 +969,11 @@ object SpaceEngine {

def checkMatch(m: Match)(using Context): Unit =
if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
if reachabilityCheckable(m.selector) then checkReachability(m)
if reachabilityCheckable(m.selector) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed with @noti0na1 and agreed that this change should be removed. It is outdated and should no longer be necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PRs may be related to this: #21850 #21934

@@ -0,0 +1,54 @@
//> using options -Xfatal-warnings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go in warn instead of enabling fatal warnings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants