diff options
| author | Cliff Click <cliffc@acm.org> | 2026-11-03 09:34:31 -0800 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-11-03 09:34:31 -0800 |
| commit | 8d92810d9a2545d851d5986715a98fda24bb3c3a (patch) | |
| tree | 1450eeb00da92bafcad6ed1bed7a586df7e974e4 /chapter29/docs/construction-groups.md | |
| parent | bfd9af8f9518a6fe87cbb706813cd87c8dc5ea90 (diff) | |
| download | Simple-ch29-cleanup.tar.gz Simple-ch29-cleanup.zip | |
Early version of safe using partial construcitonch29-cleanup
Diffstat (limited to 'chapter29/docs/construction-groups.md')
| -rw-r--r-- | chapter29/docs/construction-groups.md | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/chapter29/docs/construction-groups.md b/chapter29/docs/construction-groups.md new file mode 100644 index 00000000..c817626e --- /dev/null +++ b/chapter29/docs/construction-groups.md @@ -0,0 +1,131 @@ +# Construction: field types and incomplete references + +Instance constructors use ordinary field Loads and Stores. There are two checks: +whether a field has a value, and whether an object reference is usable outside +construction. Neither requires a collection of constructor-group identities. + +## Field initialization + +New produces private `TypeMem` containing the object's `TypeStruct`, with +`TypeScalar.BOT` for fresh fields. Defaults emit ordinary Stores: zero for an +ordinary integer, null for a nullable pointer, no Store for a required field. +An assignment strongly updates its field in private memory. Phi meets the +incoming memories; a missing live arm survives because `BOT.meet(7) == BOT`. +An initialized unknown integer has `TypeInteger.BOT`, not `TypeScalar.BOT`. +Arrays retain their language-level zero filling. + +One `#selfMem` binding carries this memory through scopes and returns. A Load +checks its incoming field type. Constructor completion checks the merged Return +memory; the explicit constructor receives the declaration initializer's merged +result, including early exits. There is no backwards initialization query, +per-field parser state, call-site snapshot, or store-all instance exit. + +After parsing closes loops and resolves names, `Iter.types` computes an +optimistic fixed point using the existing node types, compute methods, and +worklist, without graph rewrites. Optimism matters: an initialized field carried +unchanged around a loop must not remain stuck at a pessimistic BOT seed. Numeric +fields in loop memory widen as scalar loop values do. Constructor Loads remain +kept until checking, so an unused or folded result cannot hide an illegal read. +BOT describes the incoming field contents, not a taint on later computations. +Conversions and numeric Stores retain their existing declared-type bounds; +their narrowing cannot excuse the original Load, which is checked independently. + +Internal construction memory parameters use actual allocation/initializer call +edges; the hypothetical external caller does not supply their memory. They +retain their memory family when there are no live callers. Function-valued +fields retain their declared signatures in private summaries, avoiding repeated +unfolding of a constructor whose returned memory contains its own function field. +Class singleton initialization keeps its older store-at-exit implementation. + +## A yes/no rule for references + +`self` and incoming `C ^arg` constructor parameters are incomplete references. +`C? ^arg` also accepts null. The qualifier is permitted only on struct parameters +of explicit constructors, not on arrays or ordinary function parameters. + +- An allocation result is incomplete if any actual argument is incomplete. +- With only completed arguments, including null, the result is complete. +- An incomplete reference may be stored into self or passed to a `^` parameter. +- Ordinary calls and stores into other objects require completed references. +- Field lookup on an incomplete object is forbidden, except through self. + Exact local aliases of self share its current private memory. +- An object-reference field read from incomplete self is conservatively + incomplete. There is no inference of initialized-field requirements on `^` + arguments, nor permission to traverse a constructed member before its root + construction finishes. Arrays are already initialized and cannot receive + incomplete elements, so an initialized array field can be used normally. + +For example: + +```simple +struct Child { + Test.Parent? parent; + new Child = { Test.Parent? ^par -> parent = par; }; +}; +struct Parent { + Child child; + int~ payload; + new Parent = { int n -> + child = new Child(self); // Incomplete result: self is incomplete. + // child.parent is forbidden here. + payload = n; + }; +}; +val parent = new Parent(arg); +val local = new Child(parent); // Complete argument, complete result. +return local.parent==parent; // Lookup is allowed. +``` + +Here `Test` names the source unit. After the root Parent constructor completes, +the entire cycle is usable: all members have initialized their own fields, and +none could expose an incomplete reference to ordinary code during construction. +The Child constructor still treats its `^par` as incomplete when the actual +argument is complete; it may retain or forward it, but cannot read `par.payload`. +Field sharpening and inferred contracts are deliberately deferred. + +## Aliases and loops + +Incomplete is a property of a value, not just the current contents of a parser +Var. Consider `while (n--) { use(p); p = self; }`: the backedge introduces an +incomplete value after `use(p)` has been parsed. `TypeMemPtr._partial` carries +that restriction through ordinary forward typing. It is an independent +two-point lattice: meet ORs the restriction and dual complements it. Copies, +conversions, Stores, and Phis preserve it; a merge is incomplete if any live +input is incomplete. A `^` formal stays incomplete even when its known callers +are complete. The receiver's private memory identifies the own-self exception; +another incomplete pointer of the same class has no such permission. + +A small `ConstructNode` wraps each source `new`. Its runtime value is the +allocator result; its type adds the OR of that call's actual argument qualifiers. +This distinguishes `new Child(self)` from `new Child(null)` even when both call +the same constructor. Looking only at the shared callee Return would lose this +distinction. The node disappears after source checking, before lowering. + +A completed receiver gives a completed view of its pointer fields. Construction +may retain incomplete references but cannot publish them, so reaching the root +`new` boundary with complete arguments makes the entire reachable cycle usable. +There is no group identity or traversal to mark members complete. Conversely, +object-reference fields read through incomplete self remain conservative, even +if the particular stored reference was complete. + +The optimistic `Iter.types` solve also handles qualifier cycles. For +`p = null; while (n--) p = new Link(p);`, no input introduces incompleteness, +so `p` stays complete and a subsequent field read is allowed. A backedge carrying +self instead makes the Phi and its uses incomplete. + +`Construction` retains source operands and diagnostic sites until checking; +it stores no flow facts and performs no recursive Load/Phi/Call query. The old +root and allocation bitsets are gone. There are no field-requirement sets, +hidden `^` memory arguments, or runtime group objects. + +`ConstructionGroupTest` exercises cycles, complete/null arguments, prohibited +lookups/publication, aliases, branch merges, complete and incomplete loop +backedges, and declared restrictions with only complete callers, including an +uninlined constructor through machine encoding. `ConstructorCompletionTest`, +`FinalConstructionTest`, and `BoundaryTest` exercise field-memory semantics. +The former inferred-requirement litmus tests are preserved, explicitly deferred, +in `ConstructionRequirementsTest`. Separate compilation of `^` constructors is +still deliberately unsupported. The pointer qualifier has an object-file +representation, but importing and checking the full construction contract is +outside this experiment. Rebuild chapter 29 object files after the type-format +change. |
