Skip to content

Treat null literals as carrying no information instead of failing - #311

Merged
CatarinaGamboa merged 2 commits into
mainfrom
fix/301-null-literals
Oct 2, 2026
Merged

CatarinaGamboa merged 2 commits into
mainfrom
fix/301-null-literals

Conversation

@CatarinaGamboa

Copy link
Copy Markdown
Collaborator

Null literals (x = null, x == null, x != null, f(null)) no longer raise Null literals are not supported and stop verification of the rest of the file.
In OperationsChecker, a null literal now yields new Predicate() (true), like String literals already do, and a binary operation with a null operand also yields true (otherwise e.g. name == null became name == true and failed with a Z3 sort mismatch). No null reasoning is added.

Fixes #301

Testing:

  • New CorrectNullLiterals.java: null-initialised local, == null / != null in if, null passed to an unrefined method, the X s = null; try { ... } finally { if (s != null) ... } shape, plus an unrelated int refinement that still verifies.
  • Manually checked that a failing refinement inside an if (name == null) branch is still reported as a Refinement Error.
  • mvn test: 339 tests, 0 failures (existing ErrorEnumNull still fails as expected).

🤖 Generated with Claude Code

Refs #301: null literals and comparisons with null no longer raise
"Null literals are not supported"; they produce a true predicate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Treating `x == null` / `x != null` as the constant `true` made the
negated condition `false`, so else branches of `||` conditions were
considered dead, `&&` else branches gained a false fact, and ternaries
and boolean locals took a fixed value. A null comparison now becomes a
fresh unconstrained boolean variable; other operands keep their facts.

Adds ErrorNullLiterals (negative test) and extends CorrectNullLiterals.

Refs #301

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CatarinaGamboa

Copy link
Copy Markdown
Collaborator Author

Review: there was a soundness bug. A null comparison was modelled as the constant true, so its negation was false:

  • in if (s == null || y > 0) {...} else {...} the else branch was treated as dead, so a violation inside it was never reported;
  • in if (s != null && y > 0) ... else the else path gained the false fact y <= 0;
  • (o == null) ? -1 : 1 was treated as always -1, and boolean b = o == null as always true.

All of these passed verification on 5bf4604.

Fix (cedbdec): a comparison with null now becomes a fresh unconstrained boolean variable (reusing createFreshValue(..., new Predicate())), both at the top level and when nested inside &&/||. The other operands keep their facts, e.g. s == null && y > 0 still gives y > 0 in the then-branch.

Tests:

  • new ErrorNullLiterals.java with 5 expected Refinement Errors: else of ==, else of ||, else of &&, ternary, boolean local. Only 1 of the 5 was reported before the fix.
  • CorrectNullLiterals.java extended with a && case and a ternary case.
  • mvn test: 340 tests, 0 failures.

🤖 Generated with Claude Code

@rcosta358 rcosta358 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@CatarinaGamboa
CatarinaGamboa merged commit 03a15b1 into main Oct 2, 2026
1 check passed
CatarinaGamboa added a commit that referenced this pull request Oct 2, 2026
Review fixes for the loop-condition change (#306):
- a continue reaches the for update without the rest of the body, so when
  the body has one, the update no longer sees the body's path conditions
  or assignments (`for (..; ..; use(n)) { if (n <= 0) continue; ... }`
  was accepted)
- a loop also changes fields (through any call) and the state of objects
  it calls state-changing methods on; these are now havocked too, since
  the assumed condition could otherwise combine with their stale values
  (accepted programs that main rejected)
- drop the null check on the condition, dead since null literals carry no
  information (#311)
- move the i++ path-condition fix out (it is not needed for loops and is a
  separate pre-existing bug in if branches)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

null literals are rejected, so any null check stops verification

2 participants