Repository navigation
Java reference-equality semantics for de-interned strings and boxes - #79
Closed
nils-loose wants to merge 3 commits into
Closed
nils-loose wants to merge 3 commits into
nils-loose wants to merge 3 commits into
Conversation
Objects.equals(null, x) is true iff x is null, whatever x's content, so the model no longer returns a PlaceHolder (which recorded a missing invocation and, for symbolic x, a context loss). The reference-semantic-change check moves out: reference comparisons no longer go through Objects.equals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NoCache transformer de-interns string literals, intern() results and the boxing caches, and the IF_ACMP rewrite compensated by comparing every String and box by value. That made new String(s) == s true and new Integer(5) == new Integer(5) true, and it called the uninstrumented Util helper with symbolic arguments, a context loss on every such comparison. The copies SWAT creates now register themselves in RefIdentity (a weak identity set). refEquals compares two canonical instances (literal copies, interned or pooled strings, valueOf copies and cached boxes) by value, through the modelled equals/unboxing calls, with the cache range for boxes; everything else is plain identity. RefIdentity is an ignored invocation, and ignored invocations no longer scan their arguments for symbolic values. Pooled strings from uninstrumented code are flagged as a reference semantic change when they are symbolic, since their pooling may depend on the value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The flag means a reference comparison depended on symbolic values that SWAT does not encode, so the search may have missed a side of it; a SAFE verdict is as unreliable as a VIOLATION. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
SWAT de-interns string literals and the boxing caches (
Integer.valueOfetc.) so that the shadow heap keeps them apart. To make this work, theIF_ACMPrewrite compared every String and box by value (Objects.equals). That got Java's==wrong:new String(s) == sandnew Integer(5) == new Integer(5)came out true. The rewrite also called the uninstrumentedUtil.shouldUseValueEqualitywith symbolic arguments, so every such comparison caused a context loss.refEquals(UtilInstrumented, newRefIdentity):valueOfresult,intern()result) in a weak identity set.valueOfcopies and cached boxes) compare by value. The comparison goes through the modelledequals/ unboxing calls, so the symbolic state still sees the dependence on the values. For boxes it includes the cache range (-128..127;0..127forCharacter).new String(..),new Integer(..), nondet strings and all other objects.String.intern()is no longer dropped. It is replaced withUtilInstrumented.intern, which returns a registered copy, or the string itself when that string is (or becomes) the pooled instance.RefIdentityis an ignored invocation. Ignored invocations also no longer scan their arguments for symbolic values, which removes the object-graph walk on every comparison.Objects.equalswith a null operand returns a concrete result instead of a PlaceHolder.reference_semantic_changenow means "a reference comparison was decided by symbolic values that SWAT does not encode". It is set only for a symbolic pooled string that comes from uninstrumented code. The driver now downgrades SAFE on this flag as well, not only VIOLATION.Util.shouldUseValueEqualityis removed, and so is theuserDeInternedcheck inObjectsInvocation. TheuserDeInternedfield inStringValueis left in place and is now unused. StringValue / StringBuilderValue / StringInvocation are not touched.Measurement
--sv-comp, 300 s CPU,--mem15g, no SA, compared withbaseline-results.jsonl.Tests
RefEqualsSpec(15 cases) covers the concrete semantics: literals,new String, nondet-like strings, pooled JDK strings, intern, box cache range,new Integer, mixed types and null.Known limits
intern()on a string whose value was probed, it gets the probe instead of its own string.AutoBoxCacheMax.🤖 Generated with Claude Code