Skip to content

Fold constant bitwise and shift operations, treat others as unknown - #351

Merged
CatarinaGamboa merged 1 commit into
mainfrom
fix/350-bitwise-operators
Oct 6, 2026
Merged

CatarinaGamboa merged 1 commit into
mainfrom
fix/350-bitwise-operators

Conversation

@CatarinaGamboa

Copy link
Copy Markdown
Collaborator

Fixes #350.

Problem

A bitwise or shift operator in a refined argument crashed the verifier: getOperatorFromKind had no case for |, &, ^, <<, >> or >>>, so the null operator reached a string switch (Cannot invoke "String.hashCode()"). Bit flags combined with | are the standard way to pass JDK mode constants, so correct code such as new ZipFile(f, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE) could not be verified.

Example

static void open(@Refinement("_ == 1 || _ == 5") int mode) {}

open(ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); // was: crash; now: verifies (1 | 4 == 5)
open(READ | WRITE);                            // static final 1 | 2 == 3: Refinement Error
open(1 | extra);                               // unknown operand: Refinement Error (cannot prove)

Change

  • StaticConstants.foldIntegral evaluates integral compile-time constant expressions (literals, static final constants from source or by reflection, and arithmetic, bitwise and shift operators over them) with Java's semantics for the expression's type (int vs long overflow and shift masking).
  • OperationsChecker: where a binary operator has no counterpart in the logic, a constant expression is folded to a literal; anything else is a fresh, unconstrained value of its type, as null comparisons already are. A compound assignment such as x |= y leaves x unknown. The non-short-circuit &, | and ^ on booleans map to and, or and not-equal.
  • Unchanged for every operator that was already supported.

Tests

  • CorrectBitwiseConstantArgument: literals, static final constants, JDK constants by reflection, nested, shifts and xor, |=, boolean &/|/^.
  • ErrorBitwiseConstantArgument: a folded constant that violates the refinement.
  • ErrorBitwiseUnknownOperand: a non-constant operand is reported as not provable instead of crashing.

mvn test: all tests pass (369).

Found while building verification exercises from real Java bugs (a passport decoder passed OPEN_DELETE without OPEN_READ; the corrected program hit this crash).

🤖 Generated with Claude Code

…ixes #350)

Bitwise and shift operators had no case in getOperatorFromKind, so passing e.g. 1 | 4 as a refined
argument reached a string switch with a null operator and crashed (String.hashCode() on null).
A compile-time constant expression (literals, static final constants, JDK constants such as
ZipFile.OPEN_READ | ZipFile.OPEN_DELETE) is now folded to its value with Java's semantics; any other
bitwise/shift operation is an unconstrained value of its type, so a refinement that depends on it is
reported as not provable. Non-short-circuit &, | and ^ on booleans map to and, or and not-equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CatarinaGamboa
CatarinaGamboa merged commit ad083df into main Oct 6, 2026
1 check passed
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.

Verifier crashes on a bitwise or shift operator in a refined argument (open(1 | 4))

2 participants