diff options
Diffstat (limited to 'AGENTS')
| -rw-r--r-- | AGENTS/PITFALLS.md | 16 | ||||
| -rw-r--r-- | AGENTS/SUBSYSTEMS.md | 31 |
2 files changed, 42 insertions, 5 deletions
diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md index 6ef422b..f2f4f7b 100644 --- a/AGENTS/PITFALLS.md +++ b/AGENTS/PITFALLS.md @@ -95,6 +95,20 @@ test programs. elements, BYREF, this and pointer dereferences receive a value copy from NEW; the temporary allocation leaks. Old address-of pointers retain the previous object. Private access and ordinary argument checks apply to automatic construction too. + DELETE is reserved; a parameterless PROCEDURE DELETE defines an ordinary method. + Explicit calls do not recurse into fields or affect Alive tracking. Keyword DELETE + validates an active allocation start before destroying the object and its fields, + then frees memory and leaves aliases dangling; FREE skips all destruction. + DECLARE storage and shallow BYVAL copies are destroyed in reverse declaration order + at normal function exit or when unwinding leaves the frame. Arrays reverse their + flat element order. Repeated DECLARE destroys its previous incarnation first. + Lifetimes are function-scoped, even for declarations inside blocks. NEW rebinding + keeps automatic destruction attached to original storage; heap objects are never + auto-destroyed. Main objects are destroyed only on normal program end. Alive is + cleared before destructor calls, preventing retries if a destructor throws. + Private DELETE access is checked for automatic destruction too. A destructor + throwing during unwinding propagates and interrupts remaining cleanup rather + than terminating as C++ would. - **Exception extensions.** TRY requires exactly one parameterless CATCH and ENDTRY. Bare THROW raises a new anonymous exception even inside CATCH; there is @@ -122,7 +136,7 @@ SET types (`SET`/`DEFINE` are reserved with dedicated errors), random files (OPENFILE...FOR RANDOM/SEEK/GETRECORD/PUTRECORD — TYPE is done, so these are unblocked now; the RANDOM keyword is reserved and gives a dedicated parse error), DATE, string escape sequences, -array-typed record fields, whole-array RETURN, destructors/DELETE +array-typed record fields, whole-array RETURN and class inheritance (INHERITS/SUPER). Compiler fixtures now live in `tests/` (see TESTING.md). Nightly CI runs the compiler suites, packaged EMBEDDED smoke checks and existing plugin tests. The initial standard conflicts and missing diagnostic locations are fixed on diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md index 1d13c57..1621f11 100644 --- a/AGENTS/SUBSYSTEMS.md +++ b/AGENTS/SUBSYSTEMS.md @@ -92,8 +92,22 @@ class parameters; `symbolAddress` loads their current object address for all val lvalue, address-of, receiver and BYREF paths. Field aliases, this and BYREF parameters bind addresses directly. Assignment-only `NewExprAST` uses checked MemoryHandler allocation (at least one byte for method-only classes), zero initialization and -construction, then rebinds reference slots or copies into other destinations. Ordinary assignment always copies. Heap lifetimes -remain unmanaged; destructors, DELETE and INHERITS/SUPER are unsupported. +construction, then rebinds reference slots or copies into other destinations. Ordinary assignment always copies. Heap objects require explicit keyword DELETE; INHERITS/SUPER remain unsupported. + +`ConstructorGen.cc` also owns `typeNeedsDestruction` and recursive `emitDestroy`: +ordinary DELETE method first, then fields in reverse declaration order. Explicit +DELETE method calls remain ordinary calls without field recursion. Keyword DELETE +resolves a class lvalue, validates its allocation start through MemoryHandler before +any destruction, destroys it, then releases it through FREE; aliases remain dangling. +Stack lifetimes use a per-function `StackObjects` list saved/restored around function +emission. DECLARE and shallow BYVAL copies share registration (the copy point is +marked), including arrays and records with class fields. Each record retains the +original address, an entry-initialized Alive flag and, for arrays, a runtime element +count in an entry slot. Array buffer addresses also live in entry slots so deferred +cleanup never references branch-local bound SSA values. Repeated DECLARE destroys +its previous live incarnation first. Alive becomes true only after construction or +copy completes and is cleared before destruction. Rebinding a reference slot with +NEW does not change the original address tracked for automatic destruction. **Runtime checks** (`RuntimeCheck`): array bounds, file misuse, enum ranges, null dereferences and array-parameter bounds contracts are checked at their emit sites. @@ -109,7 +123,16 @@ an entry-block alloca for its exception pointer. A potentially throwing call wit a handler or active catch emits an `invoke` and a landing pad that finishes catches being left and transfers to the nearest handler, or resumes unwinding when only cleanup is needed. CATCH uses `__cxa_begin_catch` and removes its own TRY target before emitting its body; -normal exit and RETURN call `__cxa_end_catch`. Bare THROW always creates a new +normal exit and RETURN call `__cxa_end_catch`. User functions enable cleanup +support before their first call: loop back edges can make later DECLARE sites alive +at earlier invokes. Without a local handler, cleanup landing pads end active catches +and resume unwinding. Returns (before catch cleanup) and cleanup resumes record exit +points with their handler/catch state; CodeGen fills reverse, Alive-guarded destruction +only when the function is fully emitted. Normal-exit destructor calls use that saved +state and may create further cleanup pads. During unwind filling, a reentrancy guard +emits plain calls; a throwing destructor propagates and interrupts remaining cleanup +instead of terminating. Main records only its normal return, leaving its final +uncaught-exception handler free of automatic object destruction. Bare THROW always creates a new anonymous integer exception, using `_ZTIi` internally without exposing a value. Generated main owns the final catch target, prints the generic uncaught message, and returns 1. Native binaries require the C++ runtime; EmbeddedRunner defines @@ -137,4 +160,4 @@ malloc/realloc buffer per line and strips one trailing `\r`; fopen modes are tex nothing is declared or emitted eagerly. Helper bodies are built with the *shared* Builder (RuntimeCheck emits through the same one), saving/restoring the insert block. -**Explicit memory** (`MemoryHandler`): lazily emits internal allocation/find/release/resize helpers and a linked list of live allocation start addresses. Explicit MALLOC/REALLOC and keyword NEW allocations enter the list; STRING/ARRAY internals retain their existing ownership. FREE validates non-null addresses, unlinks and frees the tracking node, releases the block and returns the original address. Zero-byte allocation returns null. REALLOC validates non-null addresses before its zero-size release path; null behaves as MALLOC. Positive-size resizing updates the node only after success, leaving the old block and record intact on failure. Node allocation precedes the user allocation; if the latter fails, its node is freed before the diagnostic and anonymous throw. Helpers use RuntimeCheck and ExceptionHandler, including source-line arguments and per-function unwind preparation. Sizes accept INTEGER and C integers and convert to native size_t. Address reuse can make stale aliases indistinguishable from current allocations; dereferences are not checked against this list. +**Explicit memory** (`MemoryHandler`): lazily emits internal allocation/find/release/resize helpers and a linked list of live allocation start addresses. Explicit MALLOC/REALLOC and keyword NEW allocations enter the list; STRING/ARRAY internals retain their existing ownership. `emitValidate` always calls the find helper, including for null, so keyword DELETE validates before running destructors. FREE validates non-null addresses, unlinks and frees the tracking node, releases the block and returns the original address. Zero-byte allocation returns null. REALLOC validates non-null addresses before its zero-size release path; null behaves as MALLOC. Positive-size resizing updates the node only after success, leaving the old block and record intact on failure. Node allocation precedes the user allocation; if the latter fails, its node is freed before the diagnostic and anonymous throw. Helpers use RuntimeCheck and ExceptionHandler, including source-line arguments and per-function unwind preparation. Sizes accept INTEGER and C integers and convert to native size_t. Address reuse can make stale aliases indistinguishable from current allocations; dereferences are not checked against this list. |
