1717import java .util .ArrayList ;
1818import java .util .Arrays ;
1919import java .util .HashMap ;
20+ import java .util .HashSet ;
2021import java .util .List ;
2122import java .util .Map ;
23+ import java .util .Set ;
2224import java .util .stream .Collectors ;
2325
2426import liquidjava .diagnostics .errors .LJError ;
@@ -53,6 +55,8 @@ public class TranslatorToZ3 implements AutoCloseable {
5355 * appears symbolically in counterexample models.
5456 */
5557 private final List <BoolExpr > staticConstantAxioms = new ArrayList <>();
58+ private final List <BoolExpr > referenceIdentityAxioms = new ArrayList <>();
59+ private final Set <Set <String >> referenceIdentityPairs = new HashSet <>();
5660
5761 public TranslatorToZ3 (liquidjava .processor .context .Context context ) {
5862 TranslatorContextToZ3 .translateVariables (z3 , context .getContext (), varTranslation );
@@ -67,6 +71,8 @@ public Solver makeSolverForExpression(Expr<?> e) {
6771 Solver solver = z3 .mkSolver ();
6872 for (BoolExpr axiom : staticConstantAxioms )
6973 solver .add (axiom );
74+ for (BoolExpr axiom : referenceIdentityAxioms )
75+ solver .add (axiom );
7076 solver .add ((BoolExpr ) e );
7177 return solver ;
7278 }
@@ -253,9 +259,68 @@ public Expr<?> makeEquals(Expr<?> e1, Expr<?> e2) {
253259 return z3 .mkFPEq (toFP (e1 ), toFP (e2 ));
254260 if (e1 instanceof RealExpr || e2 instanceof RealExpr )
255261 return z3 .mkEq (toReal (e1 ), toReal (e2 ));
262+ if (isReference (e1 ) && isReference (e2 ))
263+ addReferenceIdentityAxioms (e1 , e2 );
264+ if (!e1 .getSort ().equals (e2 .getSort ()) && isReference (e1 ) && isReference (e2 )) {
265+ Expr <?> view = supertypeView (e2 , e1 .getSort ());
266+ if (view != null )
267+ e2 = view ;
268+ else {
269+ view = supertypeView (e1 , e2 .getSort ());
270+ if (view != null )
271+ e1 = view ;
272+ }
273+ }
256274 return z3 .mkEq (e1 , e2 );
257275 }
258276
277+ private static boolean isReference (Expr <?> expression ) {
278+ return expression .getSort ().getSortKind () == Z3_sort_kind .Z3_UNINTERPRETED_SORT ;
279+ }
280+
281+ private Expr <?> supertypeView (Expr <?> expression , Sort sort ) {
282+ String name = exprToNameTranslation .get (expression );
283+ if (name == null )
284+ return null ;
285+ for (Expr <?> view : varSuperTypes .getOrDefault (name , List .of ()))
286+ if (view .getSort ().equals (sort ))
287+ return view ;
288+ return null ;
289+ }
290+
291+ /**
292+ * Each reference has separate constants for its Java type views. Identity must agree across every shared view,
293+ * including under negation: changing the view of an alias cannot change equality or inequality.
294+ */
295+ private void addReferenceIdentityAxioms (Expr <?> e1 , Expr <?> e2 ) {
296+ String name1 = exprToNameTranslation .get (e1 );
297+ String name2 = exprToNameTranslation .get (e2 );
298+ if (name1 == null || name2 == null || name1 .equals (name2 ) || !referenceIdentityPairs .add (Set .of (name1 , name2 )))
299+ return ;
300+ Map <Sort , Expr <?>> left = referenceViews (name1 , e1 );
301+ Map <Sort , Expr <?>> right = referenceViews (name2 , e2 );
302+ BoolExpr identity = null ;
303+ for (Map .Entry <Sort , Expr <?>> view : left .entrySet ()) {
304+ Expr <?> other = right .get (view .getKey ());
305+ if (other == null )
306+ continue ;
307+ BoolExpr equality = z3 .mkEq (view .getValue (), other );
308+ if (identity == null )
309+ identity = equality ;
310+ else
311+ referenceIdentityAxioms .add (z3 .mkEq (identity , equality ));
312+ }
313+ }
314+
315+ private Map <Sort , Expr <?>> referenceViews (String name , Expr <?> nativeView ) {
316+ Map <Sort , Expr <?>> views = new HashMap <>();
317+ views .put (nativeView .getSort (), nativeView );
318+ for (Expr <?> view : varSuperTypes .getOrDefault (name , List .of ()))
319+ if (isReference (view ))
320+ views .put (view .getSort (), view );
321+ return views ;
322+ }
323+
259324 @ SuppressWarnings ({ "unchecked" , "rawtypes" })
260325 public Expr <?> makeLt (Expr <?> e1 , Expr <?> e2 ) {
261326 if (e1 instanceof FPExpr || e2 instanceof FPExpr )
0 commit comments