diff options
Diffstat (limited to 'chapter29/src/main/java/com')
17 files changed, 156 insertions, 35 deletions
diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Opto.java index f841e75b..6d653835 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Opto.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Opto.java @@ -83,7 +83,7 @@ abstract public class Opto { if( x instanceof ParmNode && x._type.isHighOrConst() ) deps.push(x); // Construction identities may fold after the optimistic solve. // Adds may need sorting after imported node IDs were assigned. - if( x instanceof ConstructNode || x instanceof AddNode ) deps.push(x); + if( x instanceof ConstructNode || x instanceof InitViewNode || x instanceof AddNode ) deps.push(x); // SCCP adds call edges and default arguments, not numeric // operations. Gather unresolved modes on this required walk. if( x instanceof ModeNode mode && mode.mode()==0 ) modes.push(x); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java index 30e1415e..a86cd18a 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java @@ -20,11 +20,13 @@ abstract class TypeCheck { Ary<Node> errs = new Ary<>(Node.class); Ary<ConstructNode> constructs = new Ary<>(ConstructNode.class); + Ary<InitViewNode> views = new Ary<>(InitViewNode.class); code._stop.walk( n -> { if( n.err() != null ) errs.add(n); if( n instanceof CLInitNode ) code.add(n); if( n instanceof ConstructNode con ) constructs.add(con); + if( n instanceof InitViewNode view ) views.add(view); return null; }); @@ -48,11 +50,13 @@ abstract class TypeCheck { if( code._obs != null ) code._obs.error(min, min.err().getMessage()); throw min.err(); } - // Checked construction boundaries have no runtime behavior. Gathered + // Checked construction boundaries and views have no runtime behavior. Gathered // above during the existing error walk; no separate graph pass/solve. for( ConstructNode con : constructs ) if( !con.isDead() ) con.subsume(con.in(1)); + for( InitViewNode view : views ) + if( !view.isDead() ) view.subsume(view.in(1)); } private static int errorRank(Node n) { diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/CallNode.java index 552af499..91d27330 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -246,7 +246,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)) || !arg(i+2)._type.accessISA(tfp.arg(i)) ) { + if( !arg(i+2)._type.isa(tfp.arg(i) instanceof TypeMemPtr p ? p.partialBound() : tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) ) { if( arg(i+2)._type instanceof TypeMemPtr actual && actual._partial && tfp.arg(i) instanceof TypeMemPtr formal && !formal._partial ) return Parser.error("Cannot pass an incomplete object to a parameter without '^' (ordinary function or constructor)",_loc); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ConstructNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ConstructNode.java index 3984e48f..0af37a02 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ConstructNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ConstructNode.java @@ -49,10 +49,17 @@ public class ConstructNode extends Node { return ptr.withPartial(partial); } @Override public Node idealize() { - // Wait for Opto to install closed field types. Afterwards, a proven - // identity with initialized fields has no remaining error obligation. + // Restricting an already restricted view is redundant, including after + // inlining. A partial Parm type alone can later sharpen to complete. + if( _partial && partialView(in(1)) ) return in(1); + // Wait for Opto to install closed field types. Afterwards, a proven + // identity with initialized fields is good, and can be removed. + // Otherwise we're keeping this Node until we error report. return !_partial && CodeGen.CODE._phase.ordinal()>=CodeGen.Phase.Opto.ordinal() && _type==in(1)._type && err()==null ? in(1) : null; } + static boolean partialView(Node n) { + return n instanceof InitViewNode || n instanceof ConstructNode con && con._partial; + } @Override public Parser.ParseException err() { if( in(2)==null || in(2)._type.isHigh() || !(in(1)._type instanceof TypeMemPtr ptr) ) return null; Type mem = in(2)._type; diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java index b9fbc4d2..7616932d 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -46,7 +46,9 @@ public class ConvertNode extends Node { // Shape/access still follow the declaration: switching between // the entire source and destination types is not monotone. if( src instanceof TypeMemPtr actual && actual.notNull() ) ptr = (TypeMemPtr)ptr.nonZero(); - return ptr.withPartial(TypeMemPtr.mayBePartial(src)); + ptr = ptr.withPartial(TypeMemPtr.mayBePartial(src)); + return src instanceof TypeMemPtr actual && actual._partial && actual._obj._name==ptr._obj._name + ? ptr.initialized(actual._obj) : ptr; } if( src.isa(_dst) ) return src; return _dst; diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/EscapeNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/EscapeNode.java index f7a88df1..57340609 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/EscapeNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/EscapeNode.java @@ -9,19 +9,26 @@ import java.util.HashMap; import java.util.IdentityHashMap; /** - * Merge public and private aliases. + * Merge public and private aliases. A borrow orders partial-reader calls + * after constructor Stores without adding the private values to escape sets. * 0 - control * 1 - private pointer * 2 - private alias * 3 - public alias */ public class EscapeNode extends TypeNode { + public final boolean _borrow; public EscapeNode(Field fld, Node self, Node priv, Node pub ) { + this(fld,self,priv,pub,false); + } + public EscapeNode(Field fld, Node self, Node priv, Node pub, boolean borrow ) { super(fld,null,self,priv,pub); + _borrow = borrow; } public EscapeNode(EscapeNode esc ) { super(esc); // Copy constructor + _borrow = esc._borrow; // Except we do not want/need self ptr past Opto. // *NOTE* the weird direct stomp; this is correct because this // constructor does NOT set the backedges, because it is used to @@ -38,6 +45,7 @@ public class EscapeNode extends TypeNode { @Override public Tag serialTag() { return Tag.Escape; } @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + assert !_borrow; // Source ^ interfaces cannot yet be serialized. baos.packed2(types.get(_con)); } static Node make( BAOS bais, Type[] types) { @@ -73,7 +81,14 @@ public class EscapeNode extends TypeNode { // t is meet of pub & priv. Pub memory can be very weak (e.g. alias 1) // and we're no worse than the declared field type - Type t = tpriv.meet(tpub).join(fld._t); + Type bound = fld._t; + if( _borrow && bound instanceof TypeMemPtr ptr && TypeMemPtr.mayBePartial(tpriv) ) + bound = ptr.withPartial(true).partialBound(); + Type t = tpriv.meet(tpub).join(bound); + // Lending memory to a local read-only partial argument is an ordering + // edge, not publication to unknown callers. Keep the public escape sets. + if( _borrow ) + return TypeMem.make(fld._alias,t,false,false,false,mpub._escFs,mpub._escAs); int[] fidxes = XInt.meet(mpriv._escFs, mpub._escFs); int[] aliases = XInt.meet(mpriv._escAs, mpub._escAs); // Keep escapes from the actual private value before narrowing it to @@ -112,11 +127,12 @@ public class EscapeNode extends TypeNode { } @Override public boolean eq(Node n) { + if( _borrow!=((EscapeNode)n)._borrow ) return false; if( fld()._alias!=((EscapeNode)n).fld()._alias ) return false; assert _con == ((EscapeNode)n)._con; return true; } - @Override int hash() { return _con.hashCode(); } + @Override int hash() { return _con.hashCode() ^ (_borrow ? 1024 : 0); } } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/FunNode.java index 6422f7f1..8b6b347e 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -102,6 +102,8 @@ public class FunNode extends RegionNode { // uniform call syntax, including callbacks forwarded by another class. public Type defaultArg(int idx) { Type t = sig().arg(idx); + if( t instanceof TypeMemPtr ptr && ptr._partial && + !(idx==0 && sig().nargs()>1 && sig().arg(1) instanceof TypeMem) ) return ptr.partialBound(); if( idx!=0 || t!=TypePtr.PTR ) return t; TypeStruct clz = (TypeStruct)Parser.TYPES.get(TypeStruct.addClzPrefix(_compunit._cname)); assert clz!=null; diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/InitViewNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/InitViewNode.java new file mode 100644 index 00000000..46988a99 --- /dev/null +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/InitViewNode.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.print.ExprPrinter; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; + +/** A program-point view of self. Memory proves which slots may be read; + * their values still come from ordinary Loads. Erased after TypeCheck. */ +public class InitViewNode extends TypeNode { + public InitViewNode(TypeMemPtr declared, Node self, Node mem) { super(declared,null,self,mem); } + @Override public String label() { return "InitView"; } + @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("initView(").n(in(1)).p(")"); } + @Override public Type compute() { + Type ptr = in(1)._type, mem = in(2)._type; + if( ptr.isHigh() || mem.isHigh() ) return Type.TOP; + if( !(ptr instanceof TypeMemPtr p) ) return ptr; + TypeStruct state = mem instanceof TypeMem m && m._one && m._t instanceof TypeStruct ts + ? ts : p._obj.makeInit(); + // An earlier alias can have fewer initialized slots. The declaration + // supplies field types; this program point's memory supplies the proof. + return p.makeFrom(((TypeMemPtr)_con)._obj).withPartial(true).initialized(state); + } + @Override public Node idealize() { + // Closed declarations and settled memory must prove this view redundant. + // Keep a structural partial restriction: a Parm's current partial type + // alone is not enough, since inlining can substitute a complete caller. + return CodeGen.CODE._phase.ordinal()>=CodeGen.Phase.Opto.ordinal() && + _type==in(1)._type && ConstructNode.partialView(in(1)) ? in(1) : null; + } +} diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/LoadNode.java index caede11f..85ab29f9 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -76,7 +76,6 @@ public class LoadNode extends MemOpNode { return Type.BOTTOM; // Error in is error out if( !(ptr0 instanceof TypeMemPtr tmp) ) return scalar(mem._t); - if( tmp._partial && (!mem._one || mem._clz) ) return TypeScalar.BOT; if( tmp.nullable() ) return Type.BOTTOM; // Load from nil-able is error @@ -87,6 +86,8 @@ public class LoadNode extends MemOpNode { if( pfld == null ) return tmp.isHigh() || tmp._obj.isHigh() ? TypeScalar.TOP : TypeScalar.BOT; + if( tmp._partial && (!mem._one || mem._clz) && pfld._t==TypeScalar.BOT ) return TypeScalar.BOT; + Type t = pfld._t; boolean readonly = t instanceof TypeMemPtr view && view._ro; // Load member of constant array @@ -111,7 +112,10 @@ public class LoadNode extends MemOpNode { t = t.makeRO(); // A completed receiver gives a completed view of its reachable fields. // Through self, keep object references restricted until the root new. - if( t instanceof TypeMemPtr value ) t = value.withPartial(tmp._partial && !value._obj.isAry()); + if( t instanceof TypeMemPtr value ) { + value = value.withPartial(tmp._partial && !value._obj.isAry()); + t = value._partial ? value.partialBound() : value; + } return scalar(t); } @@ -338,7 +342,7 @@ public class LoadNode extends MemOpNode { if( ptr instanceof ConvertNode conv && addDep(conv.val())._type instanceof TypeMemPtr src && src.isa(addDep(conv)._type) && src.accessISA(conv._type) ) ptr = conv.val(); - while( ptr instanceof ReadOnlyNode || ptr instanceof ConstructNode con && con._partial ) { + while( ptr instanceof InitViewNode || ptr instanceof ReadOnlyNode || ptr instanceof ConstructNode con && con._partial ) { addDep(ptr); ptr = ptr.in(1); } @@ -375,7 +379,7 @@ public class LoadNode extends MemOpNode { Parser.ParseException err = super.err(); if( err!=null ) return err; if( (in(0)==null || !in(0)._type.isHigh()) && _type==TypeScalar.BOT && - mem()._type instanceof TypeMem mem && mem._one && !mem._clz ) + mem()._type instanceof TypeMem mem && ((mem._one && !mem._clz) || (ptr()._type instanceof TypeMemPtr p && p._partial)) ) return Parser.error("Field '"+_name+"' might not be initialized",_loc); return null; } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index b453550d..35183380 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -123,7 +123,7 @@ public abstract class MemOpNode extends TypeNode { // Already an error, but better error messages come from elsewhere if( ptr == Type.BOTTOM ) return null; if( ptr.isHigh() ) return null; // Assume it will fall to not-null - if( ptr instanceof TypeMemPtr p && p._partial && + if( !_isLoad && ptr instanceof TypeMemPtr p && p._partial && !(mem()._type instanceof TypeMem m && m._one && !m._clz) ) return Parser.error("Cannot access field '"+_name+"' through an incomplete object",_loc); // Better be a not-nil TMP diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ParmNode.java index 1b7aded1..aea402f6 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -20,7 +20,7 @@ public class ParmNode extends PhiNode { super(label, inputs); _idx = idx; _declaredType = declaredType; - _type = declaredType; + _type = declaredBound(); } public ParmNode(ParmNode parm) { super(parm, parm._label ); _idx = parm._idx; _declaredType = parm._declaredType; } @Override public Tag serialTag() { return Tag.Parm; } @@ -49,6 +49,12 @@ public class ParmNode extends PhiNode { public FunNode fun() { return (FunNode)in(0); } + // The hidden constructor receiver has private memory for checking reads. + // Only borrowed source parameters need an initially empty field promise. + private Type declaredBound() { + return !(_idx==2 && "self".equals(_label)) && _declaredType instanceof TypeMemPtr ptr ? ptr.partialBound() : _declaredType; + } + @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { if( fun()._name!=null && fun()._name.endsWith("<clinit>") && _label.equals("arg") ) @@ -68,7 +74,7 @@ public class ParmNode extends PhiNode { return region()._type==Type.XCONTROL || region()._type==Type.TOP ? (_declaredType instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type; // During parsing Phis have to be computed type pessimistically. if( r.inProgress() || in(nIns()-1)==null ) - return _declaredType; + return declaredBound(); // Set type to local top of the starting type Type t = Type.TOP; for( int i = 1; i < nIns(); i++ ) { @@ -93,7 +99,7 @@ public class ParmNode extends PhiNode { // widening this Parm alone cannot stop f(x-1)+1 from growing its result. if( t instanceof TypeInteger ti && !ti.isHigh() && !ti.isConstant() ) t = ti.same_but_slightly_wider_than(); - t = t.join(_declaredType); + t = t.join(declaredBound()); return t==Type.TOP && _declaredType instanceof TypeMem ? TypeMem.TOP : t; } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 2574d801..010d5f61 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -331,6 +331,8 @@ public class StoreNode extends MemOpNode { public Parser.ParseException err() { Parser.ParseException err = super.err(); if( err != null ) return err; + if( !_init && ptr()._type instanceof TypeMemPtr p && p._partial ) + return Parser.error("Cannot modify a field through an incomplete object outside its constructor",_loc); if( TypeMemPtr.mayBePartial(val()._type) && !(mem()._type instanceof TypeMem mem && mem._one && !mem._clz) ) return Parser.error("Cannot store an incomplete object into an ordinary object",_loc); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java index 0eda2c62..be68e10e 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java @@ -150,7 +150,7 @@ final class Allocations { new ProjNode(P._ref._start,1,ScopeNode.MEM0).peephole()).peephole(); Node[] parms = new Node[nargs]; for( int i = 0; i<nargs; i++ ) - parms[i] = new ParmNode("arg"+i,i+2,atypes[i],fun,ConstantNode.seed(atypes[i]).peephole()).peephole(); + parms[i] = new ParmNode("arg"+i,i+2,atypes[i],fun,ConstantNode.seed(fun.defaultArg(i)).peephole()).peephole(); NewNode nnn = new NewNode(ts,fun,ConFldOffNode.make(ts," len")).init(); Node self = new ProjNode(nnn,0,ts.str()).init(); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Construction.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Construction.java index 2720ad3d..6dec8ac1 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Construction.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Construction.java @@ -2,6 +2,7 @@ package com.seaofnodes.simple.parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; /** * Field initialization is ordinary forward typing: New supplies Scalar.BOT @@ -10,17 +11,18 @@ import com.seaofnodes.simple.type.*; * initialized integer. Loads and merged Returns inspect these types after the * parser closes loops and compute() stabilizes. No backwards initialization walk. * - * References have just a yes/no restriction. Constructor self and ^ parameters - * are incomplete. An allocation result is incomplete iff an actual argument is - * incomplete. Copies and Phis preserve this restriction. Only self may be read - * while incomplete; object-reference fields read from self conservatively remain - * incomplete. Arrays are born initialized and cannot contain incomplete refs. - * Ordinary calls and stores outside self require complete references. + * Constructor self and ^ parameters are incomplete. InitViewNode combines self + * with its current private memory: set fields carry their declared types and + * missing fields carry Scalar.BOT. Local ^ parameters meet these shapes through + * ordinary forward flow; unknown callers promise no initialized fields. Only + * the original constructor may write through an incomplete pointer. * - * This permits cycles to be assembled but not traversed until the root new - * returns. A new with only complete arguments, including null, returns complete. - * A ^ formal is always restricted inside its constructor, even when a caller - * passes a complete argument. No field requirements are inferred. + * Calls borrowing self receive its Stores on the ordinary public memory edge, + * using aliasable Escape slices without publishing its identity. Field values + * come from memory, not the pointer's initialization shape. Incomplete values + * may be retained only in private construction memory. Nested object references + * conservatively promise no initialized fields; arrays are born initialized. + * No field requirements are inferred and no extra memory arguments are added. * * Incompleteness lives in TypeMemPtr, not a parser provenance graph. Ordinary * Phis carry it around loops, conversions preserve it, and ConstructNode types @@ -39,7 +41,7 @@ import com.seaofnodes.simple.type.*; * Reading a pointer field through a completed receiver gives a completed view * of that field too: its object's construction could only retain incomplete * references from the enclosing construction. There is no group ID to clear. - * A read through incomplete self conservatively returns an incomplete reference + * A read through an incomplete pointer conservatively returns an incomplete reference * (except initialized arrays), even when the stored value happens to be complete. * Own-self access is identified by the receiver and its private memory, not by * the nominal class: a ^ argument of the same class cannot use that exception. @@ -53,7 +55,8 @@ import com.seaofnodes.simple.type.*; * Hidden helpers have only allocator callers, while the public constructor * binding denotes the allocator. Private-memory Parms therefore need no * fictitious external default or constructor-specific transfer function. - * There is no extra construction pass; see docs/deferred-construction.md. + * InitView and Construct are erased by the existing TypeCheck walk. There is + * no extra construction pass; see docs/construction-groups.md. */ final class Construction { private final Parser P; @@ -83,8 +86,33 @@ final class Construction { return P._scope.in(P._scope.update(mem,null)); } + Node view(Node ptr) { + return own(ptr) ? new InitViewNode((TypeMemPtr)((ParmNode)_frame._self)._declaredType,ptr,selfMem()).peephole() : ptr; + } + + // Carry the constructor's Stores on the call's ordinary memory edge. + // The borrowed receiver is read-only; this does not publish its identity. + Node callMemory(Ary<Node> args, Node pub) { + if( !_frame._owner ) return pub; + boolean lends = false; + for( int i=2; i<args.size()-1; i++ ) lends |= own(args.at(i)); + if( !lends ) return pub; + Node self = _frame._self; + TypeStruct ts = (TypeStruct)Parser.TYPES.get(((TypeMemPtr)self._type)._obj._name); + Node priv = selfMem().keep(); + for( Field fld : ts._fields ) { + pub.keep(); + Node loan = new EscapeNode(fld,self,priv,pub,true).peephole(); + Node next = P.mergeAlias(pub,fld._alias,loan); + pub.unkeep(); + pub = next; + } + priv.unkeep(); + return pub; + } + boolean own(Node ptr) { - while( ptr instanceof ConvertNode || ptr instanceof ReadOnlyNode || ptr instanceof GuardNode || (ptr instanceof ConstructNode con && con._partial) ) ptr = ptr.in(1); + while( ptr instanceof InitViewNode || ptr instanceof ConvertNode || ptr instanceof ReadOnlyNode || ptr instanceof GuardNode || (ptr instanceof ConstructNode con && con._partial) ) ptr = ptr.in(1); return _frame._owner && ptr==_frame._self; } @@ -97,6 +125,6 @@ final class Construction { void checkSerializable() { if( _borrowed ) - throw P.error("Separate compilation of constructors with '^' parameters is not supported yet"); + throw P.error("Separate compilation of functions with '^' parameters is not supported yet"); } } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Functions.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Functions.java index 5dd66730..14dee33b 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Functions.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Functions.java @@ -74,8 +74,8 @@ final class Functions { Type t = P.types.parseType(); // Arg type if( t==null ) break; if( P.match("^") ) { - if( !constructor || !(t instanceof TypeMemPtr ptr) || ptr._obj.isAry() ) - throw P.error("'^' requires a struct parameter of a constructor"); + if( !(t instanceof TypeMemPtr ptr) || ptr._obj.isAry() ) + throw P.error("'^' requires a struct parameter"); t = ptr.withPartial(true); P.construction._borrowed = true; } @@ -131,6 +131,7 @@ final class Functions { } // Into the call + args.set(1,P.construction.callMemory(args,args.at(1))); CallNode call = (CallNode)new CallNode(site, args.asAry()).peephole(); // Post-call setup CallEndNode cend = (CallEndNode)new CallEndNode(call,TypeRPC.constant(P._code.rpc(P._ref._cname))).peephole(); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java index 737ba4c9..483bd7a8 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java @@ -94,7 +94,7 @@ final class References { int selfx = P._scope.kindFcn(var)._lexSize; // Might be a method call from inside a method, so no explicit "self". // Pass the in-scope "self" - return P.expressions.parsePostfixMethod(rvalue,P._scope.in(P._scope.update(P._scope.var(selfx),null))); + return P.expressions.parsePostfixMethod(P.construction.view(rvalue),P._scope.in(P._scope.update(P._scope.var(selfx),null))); } // Assign-update direct into Scope if( var._final && !P._scope.canInit(var) ) diff --git a/chapter29/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter29/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java index 943710d1..d9e11f8f 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -91,6 +91,25 @@ public class TypeMemPtr extends TypeNil { return partial==_partial ? this : make(_nil,_obj,_one,_pub,_ro,partial); } + // A partial declaration promises no initialized slots. Actual callers may + // supply a sharper shape; the declaration must not manufacture those facts. + public TypeMemPtr partialBound() { return _partial ? makeFrom(_obj.makeInit()) : this; } + + // Project field initialization onto declared field types, not stored + // constants. A pointer field being set does not complete its referent. + public TypeMemPtr initialized(TypeStruct state) { + TypeStruct shape = _obj; + for( Field fld : _obj._fields ) { + Field actual = state.field(fld._fname); + Type t = actual==null ? TypeScalar.BOT : actual._t; + if( t==Type.BOTTOM || t==TypeScalar.BOT ) t = TypeScalar.BOT; + else if( t.isHigh() ) t = TypeScalar.TOP; + else t = fld._t==Type.BOTTOM ? TypeScalar.BOT : fld._t==Type.TOP ? TypeScalar.TOP : fld._t; + shape = shape.replace(fld.makeFrom(t)); + } + return makeFrom(shape); + } + // Conservative on unresolved scalar/pointer families; null is complete. public static boolean mayBePartial(Type t) { return t instanceof TypeMemPtr ptr ? ptr._partial |
