summaryrefslogtreecommitdiff
path: root/chapter19/src/main/java
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-03 14:03:48 -0700
committerCliff Click <cliffc@acm.org>2026-10-03 14:03:48 -0700
commit22df891893d30e9906d0b14fc3ae5d8e67d500b2 (patch)
tree2bda142f92febc8b26f1cabd4bb0c46d1864f34b /chapter19/src/main/java
parentc3958e34c0de21df52e57e47e4f47ffe6464f138 (diff)
downloadSimple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.tar.gz
Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.zip
Chapter renumber, and split ch 17
Renumber chapters 10a, 10b, 11, 12, 13, 14 -> 10, 11, 13, 12, [11,14]. Split ch 17 into a 17a and 17b; shuffle around im/mutable syntax to be consistent, and present it more slowly. THe new immutable syntax is pushed through all later chapters
Diffstat (limited to 'chapter19/src/main/java')
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/Parser.java106
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/CallNode.java2
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/LoadNode.java7
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/ScopeNode.java11
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/StoreNode.java2
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/type/Field.java2
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java45
8 files changed, 123 insertions, 56 deletions
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
index 8caa7d69..b020ac4c 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
@@ -158,6 +158,10 @@ public class Parser {
* Parses a function body, assuming the header is parsed.
*/
private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, String... ids) {
+ return parseFunctionBody(sig, loc, null, ids);
+ }
+
+ private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String... ids) {
// Stack parser state on the local Java stack, and unstack it later
Node oldctrl = ctrl().keep();
Node oldmem = _scope.mem().keep();
@@ -194,7 +198,7 @@ public class Parser {
// All args, "as-if" called externally
for( int i=0; i<ids.length; i++ ) {
Type t = sig.arg(i);
- _scope.define(ids[i], t, false, new ParmNode(ids[i],i+2,t,fun,con(t)).peephole(), loc);
+ _scope.define(ids[i], t, finals==null ? t instanceof TypeNil : finals.get(i), new ParmNode(ids[i],i+2,t,fun,con(t)).peephole(), loc);
}
// Parse the body
@@ -595,24 +599,23 @@ public class Parser {
// outside the constructor need to check the final bit.
if( _scope.in(def._idx)._type!=Type.TOP && def._final &&
// Inside a constructor, final assign is OK, outside nope
- !(_scope.inCon() && def._idx >= _scope._lexSize.last()) )
+ !_scope.canInit(def) )
throw error("Cannot reassign final '"+name+"'");
// Lift expression, based on type
- Node lift = liftExpr(expr.keep(), def.type(), def._final);
+ Node lift = liftExpr(expr.keep(), def.type());
// Update
_scope.update(name,lift);
// Return un-lifted expr
return expr.unkeep();
}
- // Make finals deep; widen ints to floats; narrow wide int types.
+ // Apply declared access permissions; widen floats and narrow integers.
// Early error if types do not match variable.
- private Node liftExpr( Node expr, Type t, boolean xfinal ) {
+ private Node liftExpr( Node expr, Type t ) {
assert !(expr._type instanceof TypeMemPtr tmp) || !tmp.isFRef();
- // Final is deep on ptrs
- if( xfinal && t instanceof TypeMemPtr tmp ) {
- t = tmp.makeRO();
+ // Read-only access is deep; a fixed binding alone does not remove access.
+ if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) {
expr = peep(new ReadOnlyNode(expr));
}
// Auto-widen int to float
@@ -620,7 +623,7 @@ public class Parser {
// Auto-narrow wide ints to narrow ints
expr = zsMask(expr,t);
// Type is sane
- if( expr._type!=Type.BOTTOM && !expr._type.shallowISA(t) )
+ if( expr._type!=Type.BOTTOM && (!expr._type.shallowISA(t) || !expr._type.accessISA(t)) )
throw error("Type " + expr._type.str() + " is not of declared type " + t.str());
return expr;
}
@@ -648,36 +651,44 @@ public class Parser {
return require(n,";");
}
- /** Parse final: [!]var['=' asgn]
+ // Widen a reassignable binding without discarding the initializer's
+ // permissions. Lattice glb includes readonly memory and is too broad here.
+ private Type inferredType(Type t) {
+ if( t instanceof TypeMemPtr tmp ) {
+ Type base = TYPES.get(tmp._obj._name);
+ TypeMemPtr ptr = (TypeMemPtr)base;
+ return (tmp._ro ? ptr.makeRO() : ptr).makeNullable();
+ }
+ return t.glb();
+ }
+
+ // The binding modifier sits immediately before the name. It never
+ // changes permissions on the referenced object.
+ private boolean bindingFinal(Type t) {
+ if( match("!") ) return false;
+ if( match("~") ) return true;
+ return t==Type.TOP || t instanceof TypeNil;
+ }
+
+ /** Parse final: [!|~]var['=' asgn]
*/
private Node parseDeclaration(Type t) {
assert t!=null;
// Has var/val instead of a user-declared type
boolean inferType = t==Type.TOP || t==Type.BOTTOM;
- boolean hasBang = match("!");
+ boolean xfinal = bindingFinal(t);
Lexer loc = loc();
String name = requireId();
// Optional initializing expression follows
- boolean xfinal = false;
Node expr;
if( match("=") ) {
expr = parseAsgn();
- // TOP means val and val is always final
- xfinal = (t==Type.TOP) ||
- // BOTTOM is var and var is always not-final
- (t!=Type.BOTTOM &&
- // no Bang AND
- !hasBang &&
- // not-null (expecting null to be set to not-null)
- expr._type != Type.NIL &&
- // Pointers are final by default; int/flt are not-final by default.
- (t instanceof TypeNil));
// var/val, then type comes from expression
if( inferType ) {
if( expr._type==Type.NIL )
throw error("a not-null/non-zero expression");
t = expr._type;
- if( !xfinal ) t = t.glb(); // Widen if not final
+ if( !xfinal ) t = inferredType(t); // Widen if not final
}
if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
_code.link(tfp)._name = name;
@@ -693,8 +704,8 @@ public class Parser {
// Nullable pointers get a NIL; not-null get a TOP which
// signals that they *must* be initialized in the constructor.
case TypeNil tn -> tn.nullable() ? NIL : con(Type.TOP);
- case TypeInteger ti -> ZERO;
- case TypeFloat tf -> con(TypeFloat.FZERO);
+ case TypeInteger ti -> xfinal ? con(Type.TOP) : ZERO;
+ case TypeFloat tf -> con(xfinal ? Type.TOP : TypeFloat.FZERO);
// Bottom signals type inference: they must be initialized in
// the constructor and that's when we'll discover the type.
case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); }
@@ -702,10 +713,7 @@ public class Parser {
}
// Lift expression, based on type
- Node lift = liftExpr(expr, t, xfinal);
-
- if( xfinal && t instanceof TypeMemPtr tmp )
- t = tmp.makeRO();
+ Node lift = liftExpr(expr, t);
// Lift type to the declaration. This will report as an error later if
// we cannot lift the type.
@@ -769,12 +777,14 @@ public class Parser {
// t = int|i8|i16|i32|i64|u8|u16|u32|u64|byte|bool | flt|f32|f64 | val | var | struct[?]
private Type type() {
int old1 = pos();
+ boolean writable = match("!");
+ boolean readonly = !writable && match("~");
// Only type with a leading `{` is a function pointer...
if( peek('{') ) return typeFunPtr();
// Otherwise you get a type name
String tname = _lexer.matchId();
- if( tname==null ) return null;
+ if( tname==null ) return posT(old1);
// Convert the type name to a type.
Type t0 = TYPES.get(tname);
@@ -783,6 +793,13 @@ public class Parser {
return posT(old1);
if( t0 == Type.BOTTOM || t0 == Type.TOP ) return t0; // var/val type inference
Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) :t0; // Null: assume a forward ref type
+ // A qualifier before the name applies to the struct reference, not
+ // to an enclosing array. Arrays have their own [] / [~] qualifier.
+ if( t1 instanceof TypeMemPtr tmp ) {
+ if( readonly || !writable ) t1 = tmp.makeRO();
+ } else if( writable || readonly )
+ return posT(old1);
+
// Nest arrays and '?' as needed
Type t2 = t1;
while( true ) {
@@ -791,6 +808,9 @@ public class Parser {
throw error("Type "+t0+" cannot be null");
if( tmp.nullable() ) throw error("Type "+t2+" already allows null");
t2 = tmp.makeNullable();
+ } else if( match("[~]") ) {
+ TypeMemPtr ary = typeAry(t2);
+ t2 = ary.makeFrom(ary._obj.makeRO()).makeRO();
} else if( match("[]") ) {
t2 = typeAry(t2);
} else
@@ -802,7 +822,7 @@ public class Parser {
// Check valid forward ref, after parsing all the type extra bits.
// Cannot check earlier, because cannot find required 'id' until after "[]?" syntax
int old2 = pos();
- match("!");
+ if( !match("!") ) match("~");
String id = _lexer.matchId();
if( !(peek(',') || peek(';') || match("->")) )
return posT(old1);
@@ -810,7 +830,7 @@ public class Parser {
if( id==null )
return posT(old1); // Reset lexer to reparse
// Yes a forward ref, so declare it
- TYPES.put(tname,t1);
+ TYPES.put(tname,((TypeMemPtr)t1).withAccess(false));
return t2;
}
@@ -820,7 +840,15 @@ public class Parser {
throw error("Arrays of reference types must always be nullable");
String tname = "["+t.str()+"]";
Type ta = TYPES.get(tname);
- if( ta != null ) return (TypeMemPtr)ta;
+ if( ta != null ) {
+ TypeStruct ary = ((TypeMemPtr)ta)._obj;
+ // Keep the original shallow struct reference. Resolving a recursive
+ // struct must not expand its array element type on each lookup.
+ if( t instanceof TypeMemPtr ptr && (ptr._obj._fields==null || !ptr._obj.isAry()) &&
+ ary._fields[1]._type instanceof TypeMemPtr old )
+ t = old.withAccess(ptr._ro);
+ return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias));
+ }
// Need make an array type.
TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS());
assert ts.str().equals(tname);
@@ -1090,7 +1118,7 @@ public class Parser {
};
// Convert to float ops, or narrow int types; error if not declared type.
// Also, if postfix LHS is still keep()
- return liftExpr(peep(op.widen()),t,false);
+ return liftExpr(peep(op.widen()),t);
}
@@ -1220,6 +1248,8 @@ public class Parser {
private void mem(Node n) { _scope.mem(n); }
private void store(String name, int alias, Type glb, Node ptr, Node off, Node val, boolean init, Node ctrl) {
+ if( !init && ptr._type instanceof TypeMemPtr view && view._ro && ctrl()._type!=Type.XCONTROL )
+ throw error("Cannot modify final field '"+name+"'");
Node prior = mem().keep();
Node st = new StoreNode(loc(),name,alias,glb,prior,ptr,off,val,init);
st.setDef(0,ctrl);
@@ -1259,7 +1289,7 @@ public class Parser {
// Sanity check field name for existing
TypeMemPtr tmp = (TypeMemPtr)TYPES.get(ptr._obj._name);
if( tmp == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr + "'");
- TypeStruct base = tmp._obj;
+ TypeStruct base = ptr._obj.isAry() ? ptr._obj : tmp._obj;
int fidx = base.find(name);
if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
@@ -1280,7 +1310,7 @@ public class Parser {
// Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment
if( matchOpx('=','=') ) {
Node val = parseAsgn().keep();
- Node lift = liftExpr( val, tf, f._final );
+ Node lift = liftExpr( val, tf );
store(name,f._alias,tf,expr.unkeep(),off.unkeep(),lift,false,base.isAry() ? ctrl() : null);
return val.unkeep(); // "obj.a = expr" returns the expression while updating memory
@@ -1337,11 +1367,13 @@ public class Parser {
private Node func() {
Ary<Type> ts = new Ary<>(Type.class);
Ary<String> ids = new Ary<>(String.class);
+ java.util.BitSet finals = new java.util.BitSet();
_lexer.skipWhiteSpace();
Lexer loc = loc(); // First argument location
while( true ) {
Type t = type(); // Arg type
if( t==null ) break;
+ finals.set(ids.size(),bindingFinal(t));
String id = requireId();
ts .push(t ); // Push type/arg pairs
ids.push(id);
@@ -1350,7 +1382,7 @@ public class Parser {
require("->");
// Make a concrete function type, with a fidx
TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
- ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry());
+ ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry());
return con(ret._fun.sig());
}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter19/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 7300bfe7..22cef22d 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -141,7 +141,7 @@ public class CallNode extends CFGNode {
// Check for args
for( int i=0; i<tfp.nargs(); i++ )
- if( !arg(i+2)._type.isa(tfp.arg(i)) )
+ if( !arg(i+2)._type.isa(tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) )
return Parser.error( "Argument #"+i+" isa "+arg(i+2)._type+", but must be a "+tfp.arg(i), _loc);
return null;
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter19/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 070789de..1991c62c 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -54,8 +54,9 @@ public class LoadNode extends MemOpNode {
Type t = MemMergeNode.contents(mem(),_alias,this);
// Update declared forward ref to the actual.
if( _declaredType.isFRef() && t instanceof TypeMemPtr tmp && !tmp.isFRef() )
- _declaredType = tmp;
- return err()==null ? _declaredType.join(t) : _declaredType;
+ _declaredType = tmp.withAccess(((TypeMemPtr)_declaredType)._ro);
+ Type rez = err()==null ? _declaredType.join(t) : _declaredType;
+ return ptr()._type.isFinal() || _declaredType.isFinal() ? rez.makeRO() : rez;
}
@Override
@@ -212,7 +213,7 @@ public class LoadNode extends MemOpNode {
// Read-Only is a deep property, and cannot be cast-away
private Node castRO(Node rez) {
- if( ptr()._type.isFinal() && !rez._type.isFinal() )
+ if( (ptr()._type.isFinal() || _declaredType.isFinal()) && !rez._type.isFinal() )
return new ReadOnlyNode(rez).peephole();
return rez;
}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter19/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index fae45121..881f65d6 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -124,6 +124,15 @@ public class ScopeNode extends Node {
}
+ // Allocation fields live in the scope immediately outside the constructor
+ // block. Nested blocks retain that permission; surrounding bindings do not.
+ public boolean canInit(Var v) {
+ for( int i=_kinds._len-1; i>0; i-- )
+ if( _kinds.at(i)==Kind.Constructor )
+ return v._idx >= _lexSize.at(i-1) && v._idx < _lexSize.at(i);
+ return false;
+ }
+
public boolean inCon() { return _kinds.last() == Kind.Constructor; }
// Is v outside any current function scope?
@@ -208,7 +217,7 @@ public class ScopeNode extends Node {
: loop.setDef(v._idx,PhiNode.make(v._name, v.lazyGLB(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
setDef(v._idx,old);
}
- assert !v._final || st==null;
+ assert !v._final || st==null || canInit(v);
if( st!=null ) setDef(v._idx,st); // Set new value
return v;
}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter19/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 8ac79bf1..0d39980a 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -106,7 +106,7 @@ public class StoreNode extends MemOpNode {
if( ptr()._type == Type.TOP )
return null; // Dead store
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
- if( tmp._obj.field(_name)._final && !_init )
+ if( (tmp._ro || tmp._obj.field(_name)._final) && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
Type t = val()._type;
//return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter19/src/main/java/com/seaofnodes/simple/type/Field.java
index ffa9d2a6..c93f82e7 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -34,7 +34,7 @@ public class Field extends Type {
public Field makeFrom( Type type ) {
return type == _type ? this : new Field(_fname,type,_alias,_final).intern();
}
- @Override public Field makeRO() { return _final ? this : make(_fname,_type.makeRO(),_alias,true); }
+ @Override public Field makeRO() { return isFinal() ? this : make(_fname,_type.makeRO(),_alias,true); }
@Override public boolean isFinal() { return _final && _type.isFinal(); }
public static final Field TEST = make("test",Type.NIL,-2,false);
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter19/src/main/java/com/seaofnodes/simple/type/Type.java
index 847235dd..1ce5dbd0 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -202,6 +202,10 @@ public class Type {
/** Compute greatest lower bound in the lattice */
public Type glb() { return Type.BOTTOM; }
+ // Writable array slots are invariant in their element access permissions.
+ // Otherwise an alias could insert a readonly pointer into a writable slot.
+ public boolean accessISA(Type dst) { return true; }
+
// Is forward-reference
public boolean isFRef() { return false; }
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter19/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 9ea24b97..61b020cb 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -25,20 +25,41 @@ public class TypeMemPtr extends TypeNil {
// (TOP ,true ) - a nil
public final TypeStruct _obj;
+ // Deep read-only access through this pointer; independent of slot finality.
+ public boolean _ro;
+
private TypeMemPtr(byte nil, TypeStruct obj) {
super(TMEMPTR,nil);
assert obj!=null;
_obj = obj;
+ _ro = obj==TypeStruct.BOT;
}
static TypeMemPtr make(byte nil, TypeStruct obj) { return new TypeMemPtr(nil, obj).intern(); }
public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); }
public static TypeMemPtr make(TypeStruct obj) { return new TypeMemPtr((byte)2, obj).intern(); }
- public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj); }
+ public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj).withAccess(_ro); }
public TypeMemPtr makeNullable() { return makeFrom((byte)3); }
- @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj); }
- @Override public TypeMemPtr makeRO() { return makeFrom(_obj.makeRO()); }
- @Override public boolean isFinal() { return _obj.isFinal(); }
+ @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil,_obj).withAccess(_ro); }
+ @Override public TypeMemPtr makeRO() { return withAccess(true); }
+ @Override public boolean isFinal() { return _ro; }
+
+ public TypeMemPtr withAccess(boolean ro) {
+ if( _ro==ro ) return this;
+ TypeMemPtr ptr = new TypeMemPtr(_nil, _obj);
+ ptr._ro = ro;
+ return ptr.intern();
+ }
+
+ // Writable slots can load and store references, so their element access
+ // must agree in both directions. A read-only outer view can weaken access.
+ @Override public boolean accessISA(Type dst) {
+ if( !(dst instanceof TypeMemPtr ptr) ) return true;
+ if( _ro && !ptr._ro ) return false;
+ if( ptr._ro || _obj._fields==null || ptr._obj._fields==null || !_obj.isAry() || !ptr._obj.isAry() ) return true;
+ Type a = _obj._fields[1]._type, b = ptr._obj._fields[1]._type;
+ return a.accessISA(b) && b.accessISA(a);
+ }
// An abstract pointer, pointing to either a Struct or an Array.
// Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
@@ -47,43 +68,43 @@ public class TypeMemPtr extends TypeNil {
public static final TypeMemPtr NOTBOT = make((byte)2,TypeStruct.BOT);
public static final TypeMemPtr TEST= make((byte)2, TypeStruct.TEST);
- public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); }
+ public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); ts.add(TEST.makeRO()); }
@Override
public TypeNil xmeet(Type t) {
TypeMemPtr that = (TypeMemPtr) t;
- return TypeMemPtr.make(xmeet0(that), (TypeStruct)_obj.meet(that._obj));
+ return TypeMemPtr.make(xmeet0(that), (TypeStruct)_obj.meet(that._obj)).withAccess(_ro | that._ro);
}
@Override
- public TypeMemPtr dual() { return TypeMemPtr.make( dual0(), _obj.dual()); }
+ public TypeMemPtr dual() { return TypeMemPtr.make( dual0(), _obj.dual()).withAccess(!_ro); }
// RHS is NIL; do not deep-dual when crossing the centerline
- @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); }
+ @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj).withAccess(_ro); }
// True if this "isa" t up to named structures
@Override public boolean shallowISA( Type t ) {
if( !(t instanceof TypeMemPtr that) ) return false;
if( this==that ) return true;
- if( xmeet0(that)!=that._nil ) return false;
+ if( xmeet0(that)!=that._nil || (_ro && !that._ro) ) return false;
if( _obj==that._obj ) return true;
if( _obj._name.equals(that._obj._name) )
return true; // Shallow, do not follow matching names, just assume ok
throw Utils.TODO(); // return _obj.shallowISA(that._obj);
}
- @Override public TypeMemPtr glb() { return make((byte)3,_obj.glb()); }
+ @Override public TypeMemPtr glb() { return make((byte)3,_obj.glb()).withAccess(true); }
// Is forward-reference
@Override public boolean isFRef() { return _obj.isFRef(); }
@Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
- @Override int hash() { return _obj.hashCode() ^ super.hash(); }
+ @Override int hash() { return (_ro ? 8192 : 0) ^ _obj.hashCode() ^ super.hash(); }
@Override boolean eq(Type t) {
TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
- return _obj == ptr._obj && super.eq(ptr);
+ return _ro==ptr._ro && _obj == ptr._obj && super.eq(ptr);
}
@Override public String str() {