From 0ac0c9e10287972ac3af5a4ee43d26f0d5b318fe Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 30 Oct 2026 16:55:15 -0700 Subject: Canonicalize aggregate memory duals and preserve field finality --- .../java/com/seaofnodes/simple/type/TypeMem.java | 18 +++++++-------- .../java/com/seaofnodes/simple/BoundaryTest.java | 10 +++++++++ .../test/java/com/seaofnodes/simple/TypeTest.java | 26 ++++++++++++++++++++++ .../java/com/seaofnodes/simple/type/TypeMem.java | 18 +++++++-------- .../java/com/seaofnodes/simple/BoundaryTest.java | 10 +++++++++ .../test/java/com/seaofnodes/simple/TypeTest.java | 26 ++++++++++++++++++++++ 6 files changed, 90 insertions(+), 18 deletions(-) diff --git a/chapter28a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/chapter28a/src/main/java/com/seaofnodes/simple/type/TypeMem.java index 4234a5e5..a0079eae 100644 --- a/chapter28a/src/main/java/com/seaofnodes/simple/type/TypeMem.java +++ b/chapter28a/src/main/java/com/seaofnodes/simple/type/TypeMem.java @@ -37,11 +37,10 @@ public class TypeMem extends Type { _t = t; _one = one; _clz = clz; - // Public bulk memory is an aggregate of otherwise-unsplit aliases; - // a single final bit cannot describe it. Keep the lattice-dual high - // form distinct, and retain finality for private singleton memory and - // precise alias slices. - _final = t!=null && alias==1 && !one && !t.isHigh() ? false : xfinal; + // Alias 1 represents an aggregate, including a private object's fields. + // Its fields carry finality individually. Canonicalize the unused bit + // with _one so normalization commutes with dual, including named structs. + _final = alias==1 ? one : xfinal; assert _escFs == null && _escAs == null; _escFs = XInt.intern(fidxs); @@ -126,9 +125,10 @@ public class TypeMem extends Type { int[] fidxs = XInt.meet( _escFs, that._escFs ); int[] aliases= XInt.meet( _escAs, that._escAs ); // Singleton and class facts are kept only when both sides agree. - // Finality is a low-side fact, and flips on the dual side. - boolean highAlias1 = (this._alias==1 && this._t.isHigh()) || (that._alias==1 && that._t.isHigh()); - boolean xfinal = highAlias1 ? (_final & that._final) : (_final | that._final); + // An aggregate has no field-wide finality. If meeting with it selects + // a precise alias, retain that slice's finality without mixing in the + // aggregate's canonical placeholder bit. + boolean xfinal = _alias==1 ? that._final : that._alias==1 ? _final : (_final | that._final); return make(alias, mt, _one & that._one, _clz & that._clz, xfinal, fidxs, aliases); } @@ -141,7 +141,6 @@ public class TypeMem extends Type { TypeMem d = malloc(_alias,null,!_one,!_clz,!_final, XInt.dual(_escFs), XInt.dual(_escAs)); (_dual = d)._dual = this; // Cross link duals d._t = _t._terned ? _t.dual() : _t.rdual(); - if( d._alias==1 && !d._one ) { assert !d._t.isHigh(); d._final = false; } return d; } @@ -166,6 +165,7 @@ public class TypeMem extends Type { } @Override boolean cycle_eq(Type t) { + if( t._type!=TMEM ) return false; TypeMem that = (TypeMem) t; // Invariant return _alias == that._alias && _one == that._one && _clz == that._clz && _final == that._final && _escFs == that._escFs && _escAs == that._escAs && _t.cycle_eq(that._t); } diff --git a/chapter28a/src/test/java/com/seaofnodes/simple/BoundaryTest.java b/chapter28a/src/test/java/com/seaofnodes/simple/BoundaryTest.java index 68d8175f..4e3fbb1b 100644 --- a/chapter28a/src/test/java/com/seaofnodes/simple/BoundaryTest.java +++ b/chapter28a/src/test/java/com/seaofnodes/simple/BoundaryTest.java @@ -9,6 +9,16 @@ import static org.junit.Assert.*; // The new module boundary must preserve the preceding chapters' examples. public class BoundaryTest { @Test public void testLocalConstruction() throws java.io.IOException { + // Nested factories must preserve memory duals during construction, + // for both direct fields and getters on the returned instance. + for( String body : new String[]{"int x=7;", "int _x=7; val get={ -> _x; };" } ) + for( long seed : new long[]{1,13,123} ) { + String src = "struct Outer { struct _Hidden { "+body+ + " }; val make={ -> new _Hidden(); }; }; return new Outer().make()."; + CodeGen factory = new CodeGen(src+(body.startsWith("int x") ? "x;" : "get();"),seed,true) + .driver(CodeGen.Phase.TypeCheck); + assertEquals("7",Eval2.eval(factory,0)); + } String src="struct Counter { int x; int y; int z; new Counter={int n -> while(x _x; };" } ) + for( long seed : new long[]{1,13,123} ) { + String src = "struct Outer { struct _Hidden { "+body+ + " }; val make={ -> new _Hidden(); }; }; return new Outer().make()."; + CodeGen factory = new CodeGen(src+(body.startsWith("int x") ? "x;" : "get();"),seed,true) + .driver(CodeGen.Phase.TypeCheck); + assertEquals("7",Eval2.eval(factory,0)); + } String src="struct Counter { int x; int y; int z; new Counter={int n -> while(x