Skip to content

NSNull: fix === returning false when both operands are nil#5486

Open
ayush-that wants to merge 1 commit into
swiftlang:mainfrom
ayush-that:fix/nsnull-identity-operator
Open

NSNull: fix === returning false when both operands are nil#5486
ayush-that wants to merge 1 commit into
swiftlang:mainfrom
ayush-that:fix/nsnull-identity-operator

Conversation

@ayush-that

Copy link
Copy Markdown

Fixes #5248.

The hack in ===(NSNull?, NSNull?) returns false when either operand is nil, so nil === nil returns false. Replace the guard with an explicit switch on the optional pair: (nil, nil) is true, mixed-nil is false, and two non-nil NSNull instances remain identical (preserving the singleton behavior).

Also add a matching !== overload so the symmetric operator agrees with === for this signature. Without it, Swift's default !== for AnyObject? returns true for two distinct NSNull instances even though the custom === says they are identical.

The new operator is a small public-API addition. Happy to drop it and land only the bug fix if a swift-evolution discussion is preferred.

Tests

Added test_identityOperator() covering the full truth table (nil/nil, mixed, non-nil/non-nil for both === and !==). Existing test_alwaysEqual() still passes; added one supplemental !== assertion to it.

Verified on Linux via swiftlang/swift:nightly-main-jammy:

Test Suite 'TestNSNull' passed
   Executed 3 tests, with 0 failures (0 unexpected) in 0.106s

The current `===(NSNull?, NSNull?)` overload uses
`guard let _ = lhs, let _ = rhs` and falls through to `return false`
when either operand is nil. That makes `nil === nil` return false,
contradicting the standard identity-operator contract. Two non-nil
NSNull instances still compare ===, so the existing test_alwaysEqual
case continues to pass.

Replace the guard with a switch over the optional pair so the cases
are explicit: (nil, nil) is true, mixed-nil is false, and two non-nil
NSNull instances remain identical (preserving the singleton-like
behavior). Add a matching !== overload so the symmetric operator
agrees with === for the NSNull? signature. Without it, Swift's
default !== for AnyObject? would return true for two distinct NSNull
instances even though === says they are identical.

Resolves swiftlang#5248.
@ayush-that ayush-that requested a review from a team as a code owner May 28, 2026 09:26
Copilot AI review requested due to automatic review settings May 28, 2026 09:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes the === operator overload for NSNull? so that nil === nil returns true (previously returned false), and adds a matching !== overload. Includes new tests covering identity comparisons.

Changes:

  • Rewrite === for NSNull? to correctly handle the (nil, nil) case.
  • Add a !== operator overload for NSNull?.
  • Add test_identityOperator and minor cleanups in TestNSNull.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
Sources/Foundation/NSNull.swift Corrects === semantics for optional NSNull and adds !==.
Tests/Foundation/TestNSNull.swift Adds coverage for the new identity operator behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/Foundation/NSNull.swift
@parkera

parkera commented May 28, 2026

Copy link
Copy Markdown
Contributor

In Objective-C, NSNull's allocator will always return the singleton. There is no way to get two distinct instances of NSNull.

@itingliu itingliu requested a review from parkera June 16, 2026 21:31
@parkera

parkera commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@swift-ci test

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.

Inconsistencies with NSNull and ===

3 participants