aboutsummaryrefslogtreecommitdiff
path: root/AGENTS
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-20 01:29:11 +0000
committer_ov4 <ov4@ov4.org>2026-10-20 01:29:11 +0000
commit81e18a089ccc5ef4869d6b5a53f49e4696d33dd7 (patch)
tree1eef2688db191cc59ca8de2d612fd7e8619157a7 /AGENTS
parent7a090c08a7406c3bd364ee78e0f8a581c29e58a6 (diff)
docs(agents): align dispatch and object lifetime guidance with code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'AGENTS')
-rw-r--r--AGENTS/ARCH.md9
-rw-r--r--AGENTS/CONVENTIONS.md27
-rw-r--r--AGENTS/EXTENDING.md12
-rw-r--r--AGENTS/PITFALLS.md12
-rw-r--r--AGENTS/SUBSYSTEMS.md11
5 files changed, 43 insertions, 28 deletions
diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md
index f6e5d30..26fdf7f 100644
--- a/AGENTS/ARCH.md
+++ b/AGENTS/ARCH.md
@@ -71,7 +71,7 @@ Homebrew bottles also receive local Cellar/opt links and Mach-O relocation/signi
Package hashes skip unchanged extractions; a changed package set rebuilds the
managed root. Downloads belong to the explicit dependency commands and static LLD
helper in `doc/build.md`, not to CMake. The Windows Pandoc ZIP is downloaded directly from its official
-release because MSYS2 has no Pandoc package. Only the Linux build is verified.
+release because MSYS2 has no Pandoc package.
Generated IR uses the C++ exception ABI; native compilation links its runtime
automatically. Manually linking emitted `.ll` files still requires a C++ driver
@@ -80,8 +80,9 @@ TRY/CATCH/ENDTRY and bare THROW are compiler extensions.
Runtime checks print the source line and error reason to stderr before throwing
anonymous exceptions; the final handler in generated main
prints `Unhandled exception. Program terminated.` to stderr and returns 1.
-Exception execution with the embedded ORC JIT is verified on Linux with LLVM 23;
-Windows/macOS await CI verification.
+Nightly CI runs compiler tests, including embedded exception execution, on
+Linux, Windows and macOS with LLVM 23. Platform-specific changes require
+successful checks on the native runner.
Driver behavior (`tools/driver/main.cc`):
@@ -172,7 +173,6 @@ LLVM 23 executable has only libc, libm and the system loader as dynamic dependen
macOS links with `-dead_strip_dylibs` to omit unused dylibs inherited
from LLVM's support interface, including Homebrew's Z3. Keep the nightly package's
non-system dependency check; required dylibs are not removed by this option.
-Windows and macOS embedded execution await CI verification; Linux EMBEDDED is verified.
On MinGW, `LLVMWindowsManifest` explicitly uses static libxml2, iconv and zlib
plus the system bcrypt library. XML definitions are loaded with `--whole-archive`
to satisfy dllimport references in the prebuilt LLVM archive. `-static` alone
@@ -319,6 +319,7 @@ do not modify the real release for local verification.
| `lib/CodeGen/CodeGen.cc` | Initialization, symbols, call arguments and main() compilation |
| `lib/CodeGen/ExpressionGen.cc`, `StatementGen.cc`, `ControlFlowGen.cc` | Expression/statement dispatch and control-flow emission |
| `lib/CodeGen/OperatorGen.cc`, `include/cps/Operator.h` | Exact global/member operator selection, calls and temporary receivers; shared operator spellings |
+| `lib/CodeGen/ConstructorGen.cc` | Construction, copying, assignment, destruction and deferred object cleanup |
| `lib/CodeGen/GlobalGen.cc` | Primitive GLOBAL declaration and constant-initializer passes |
| `lib/CodeGen/ConstantGen.cc` | Literal constants, declaration conflicts and lexical binding restoration |
| `lib/CodeGen/ValueTypeGen.cc`, `OutputGen.cc`, `Builtins.h` | Expression typing, coercion/declarations, scalar output and the shared builtin table |
diff --git a/AGENTS/CONVENTIONS.md b/AGENTS/CONVENTIONS.md
index 76f67c1..f964a50 100644
--- a/AGENTS/CONVENTIONS.md
+++ b/AGENTS/CONVENTIONS.md
@@ -28,8 +28,8 @@ applies the sign and i64 range check, including the minimum value. String/char l
the stream must outlive the lexer. Lookahead, line number and error state belong to
each instance. The driver opens the source file; stdin is not used for compilation.
-**Types travel as uppercase `std::string`** (`"INTEGER"`, `"REAL"`, ..., plus
-user-defined names) — never an enum. `TypeSystem` resolves the string to
+**Type names travel as `std::string`** (uppercase builtins such as `"INTEGER"`
+and `"REAL"`; user-defined names retain their spelling) — never an enum. `TypeSystem` resolves the string to
`TypeInfo{LLVMType, Kind, per-kind payload}`. The parser's `ParseTypeName` accepts *any*
identifier as a type name except `VOID` (rejected outside a RETURNS clause via its
`AllowVoid` flag); other unknown types only fail (softly) in CodeGen.
@@ -52,8 +52,9 @@ same-layout records too). Their LLVM representations are indistinguishable from
builtins, so values of these kinds must NEVER reach `ArithmeticHandler` or
`coerceValueToType` without a prior name-level check — `CodeGen::emitCoercedExpr`
(assignments, BYVAL args, RETURN, IF/WHILE/REPEAT/FOR operands, file operands,
-builtin args) and the binary-op interception at the top of `emitExpr`'s
-`BinaryExprAST` branch are the only doors. `coerceValueToType` hard-errors
+builtin args) and the typed `CodeGen::emitBinaryOp` dispatcher in `ExpressionGen.cc`
+guard these paths. The dispatcher resolves overloads and handles C/user kinds
+before the builtin STRING/arithmetic paths. `coerceValueToType` hard-errors
("Internal: ...") if a user-kind target reaches it.
**Enum value names are a flat, globally reserved namespace** owned by `TypeSystem`
@@ -111,9 +112,11 @@ previously visible top-level constants (ScopeID 0). Locals may shadow globals. C
scope bookkeeping and array metadata around each function body. Arrays also
restore metadata at control-flow block exits.
-**Dispatch is by `dynamic_cast` chain, not virtual methods.** `CodeGen::emitStmt` and
-`emitExpr` walk a linear if-chain of casts. **A statement type missing from the chain is
-silently ignored** — no error. Adding an AST node always means touching these chains.
+**Dispatch is by `dynamic_cast` chain, not virtual methods.** `CodeGen::emitStmtImpl`
+in `StatementGen.cc` and `emitExpr` in `ExpressionGen.cc` walk linear if-chains of
+casts. **A statement type missing from the chain is silently ignored** — no error.
+Adding an AST node always means touching these chains. Emit statements through
+`emitStmt`, whose wrapper cleans up statement temporaries after dispatch.
**Compiler error style: print an `error:` line, set the component's error flag, and
keep compiling.** No exceptions, no exit mid-compile. `Lexer`/`Parser`/`CodeGen` each
@@ -136,10 +139,12 @@ continues; the failure is always diagnosed and reflected in the exit code.
function's pseudocode signature (`FuncSig`: return type name + per-param `ParamSig`
{type name, mode, array rank/bounds}); `CodeGen::marshalCallArgs` — the single
call-marshalling path for both call-expr and call-stmt — requires BYREF arguments to be
-bare variables of *exactly* the declared type (their alloca is passed), admits BYVAL
-arguments through `emitCoercedExpr`, and dispatches whole-array arguments to
-`ArrayHandler::emitArrayArgument`. RETURN values go through `emitCoercedExpr` against
-the declared return type. Top-level prototypes are pre-registered (after the TYPE
+bare variables of *exactly* the declared type (their `symbolAddress` is passed), admits BYVAL
+arguments through `emitOwnedValue`, and dispatches whole-array arguments to
+`ArrayHandler::emitArrayArgument`. RETURN also uses `emitOwnedValue` against the
+declared return type. Class lvalues invoke a same-class BYREF OVERLOAD NEW when
+defined, otherwise they copy bits; class rvalues transfer ownership. Other values
+use `emitCoercedExpr`. Top-level prototypes are pre-registered (after the TYPE
pre-pass) before statement emission, so call-before-definition is fine; a call with
no registered signature is a compile error. OVERLOAD signatures append canonical
parameter types to the link name; ordinary names except MAIN get no mangling.
diff --git a/AGENTS/EXTENDING.md b/AGENTS/EXTENDING.md
index 3161407..a34cfa6 100644
--- a/AGENTS/EXTENDING.md
+++ b/AGENTS/EXTENDING.md
@@ -9,7 +9,9 @@ unique_ptr children, move ctor, raw-ptr getters) → `ParseXxx` in
the matching `Parser*.cc` file + branch in `ParseStatementImpl` (block statements parse their body with
`ParseBlock({terminator tokens})`; control-flow parsers live in `ParserControlFlow.cc`; comma-separated expression lists use
`ParseExprList(close, allowEmpty)`; add the new statement's leading keyword to
-`syncToStatementStart`) → `dynamic_cast` branch in `CodeGen::emitStmt`. Emit statement bodies through `emitScopedBlock`
+`syncToStatementStart`) → `dynamic_cast` branch in `CodeGen::emitStmtImpl` in
+`StatementGen.cc`. Keep calls through the `emitStmt` wrapper so statement
+temporaries are cleaned up. Emit statement bodies through `emitScopedBlock`
so constants cannot escape their lexical block. Guard every
fall-through branch in control-flow codegen with
`if (!Builder->GetInsertBlock()->hasTerminator())` — a RETURN in the body may have
@@ -20,12 +22,14 @@ already terminated the block.
`emitCall`, the return type in `getExprTypeInfo`, and the reject-user-redefinition check
all read it), then implement IR emission as a method on the fitting handler
(String/StringConversion) or inline, and add the name intercept in `CodeGen::emitCall` — null-check every emitted argument before use, like the existing
-branches do. Builtins are plain identifiers — nothing is needed in lexer/parser.
+branches do. `emitCall` lives in `ExpressionGen.cc`. Builtins are plain identifiers — nothing is needed in lexer/parser.
**New binary operator:** token (if multi-char) → one line in `BinopPrecedence`
(Parser ctor; levels: OR=3, AND=5, comparisons=10, `+ - &`=20, `* / DIV MOD`=40;
-precedence must be > 0) → branch in the shared `CodeGen::emitBinaryOp` value dispatcher or its `ArithmeticHandler::emitBinaryOp` leaf, which dispatches
-on raw LLVM operand types. Exceptions in `CodeGen::emitBinaryOp`: `&` coerces operands
+precedence must be > 0) → branch in the shared `CodeGen::emitBinaryOp` value dispatcher
+in `ExpressionGen.cc` or its `ArithmeticHandler::emitBinaryOp` leaf, which dispatches
+on raw LLVM operand types. Preserve overload selection and C/user-kind checks
+before reaching that leaf. Exceptions in `CodeGen::emitBinaryOp`: `&` coerces operands
to STRING then calls `StringHandler::emitConcat`; STRING comparisons require two
STRING operands by pseudocode type then call `StringHandler::emitCompare`.
diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md
index 4c72a4f..178e173 100644
--- a/AGENTS/PITFALLS.md
+++ b/AGENTS/PITFALLS.md
@@ -93,14 +93,17 @@ test programs.
inputs. MAIN and builtins cannot be overloaded. NEW supports overloads; DECLARE,
fields and arrays needing default construction require a matching no-argument NEW.
Private access is checked after selection, never used to select a public fallback.
-- **OPERATOR extension.** Requires OVERLOAD FUNCTION, with supported unary/binary
+- **OPERATOR extension.** Unary/binary operators require OVERLOAD FUNCTION, with supported
arity and at least one record/class/enum operand. Pointer aliases alone do not
qualify. Exact matching considers visible globals and left-class members
together; ties are errors, including private/public ties. Comparison and logic
result types are declared, not assumed BOOLEAN; implicit STRING to BOOLEAN is
rejected. CASE comparisons must return BOOLEAN. AND/OR overloads evaluate both
- operands. Temporary member receivers have function-scoped cleanup, not C++
- full-expression lifetimes. Explicit calls use OPERATOR+(a,b) or a.OPERATOR+(b).
+ operands. Temporary member receivers are destroyed at statement end, including
+ exception exits; ordinary DECLARE objects remain function-scoped. Explicit calls
+ use OPERATOR+(a,b) or a.OPERATOR+(b). Assignment uses member-only
+ OVERLOAD PROCEDURE OPERATOR<- with one explicit parameter; it is selected before
+ fallback same-class copying. Keyword NEW retains its allocation/rebinding path.
- **CLASS subset (§10).** `CLASS`/`ENDCLASS`/`PUBLIC`/`PRIVATE` are reserved.
CLASS is a top-level record-derived value type with optional per-member access
keywords (no DECLARE). Omitting access defaults to public, per the guide.
@@ -144,7 +147,8 @@ test programs.
Native binaries must link the C++ runtime with LLVM 23's `clang++`
(`clang++-23` on Debian/Ubuntu).
Unwinding does not free heap strings/arrays or close files. Embedded ORC
- execution with LLVM 23 is verified on Linux, Windows and macOS.
+ execution with LLVM 23 is exercised by nightly CI on Linux, Windows and macOS;
+ check the native job results when changing platform-specific code.
- **C type extensions.** C_STRING is a raw char pointer, not a STRING: explicitly
cast to STRING for printing or builtins. C aliases share canonical CIdentity;
diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md
index 08301a3..fc83fac 100644
--- a/AGENTS/SUBSYSTEMS.md
+++ b/AGENTS/SUBSYSTEMS.md
@@ -67,7 +67,8 @@ resolves INCLUDE_OBJ paths, reads defined global symbols with `llvm::object`
libc/libm inputs, copied to aligned buffers), rejects missing symbols, and passes objects
to `compileNative`, `runEmbedded` (`addObjectFile`) or lli (`-extra-object=`).
On Windows, `runEmbedded` adds a msvcrt.dll search generator ahead of process symbols.
-Linux EMBEDDED and PATH (lli) runs are verified locally; Windows and macOS pass CI (extern-libc).
+Nightly CI runs extern-libc and the object-file fixtures in EMBEDDED mode on
+Linux, Windows and macOS; PATH execution depends on the external lli runtime.
**User types** (`TypeSystem` + `Designator.cc` + `TypeCheckGen.cc`):
`Designator.cc` owns the type pre-pass and lvalue access; `TypeCheckGen.cc` owns
@@ -171,15 +172,15 @@ Generated main owns the final catch target, prints the generic uncaught message,
and returns 1. Native binaries require the C++ runtime; EmbeddedRunner defines
its statically linked ABI symbols with ORC `absoluteSymbols`, while LLJIT with
JITLink manages unwind frames (generic EH-frame on Linux, compact-unwind or
-EH-frame on macOS, COFF SEH registration on Windows x86_64). Linux embedded
-execution is verified; Windows/macOS await CI verification.
+EH-frame on macOS, COFF SEH registration on Windows x86_64). Nightly CI exercises
+embedded exception execution on all three platforms.
This does not change heap-string/array lifetimes or automatically close files.
**Files** (`FileHandler`): text-file runtime for §9.1.
`FileHandler.cc` owns setup, lookup/open and emission entry points;
`FileRuntimeIO.cc` builds the read/write/close/EOF helpers. StringHandler-shaped leaf —
holds Context/Builder/Module/RuntimeCheck/ExceptionHandler refs only, never sees
-CodeGen, AST nodes or the symbol table; `CodeGen::emitStmt`'s four file branches (and the EOF builtin
+CodeGen, AST nodes or the symbol table; `CodeGen::emitStmtImpl`'s four file branches (and the EOF builtin
intercept) evaluate/coerce operands and hand over `llvm::Value*`s plus the statement
line. Calls into throwing helpers go through ExceptionHandler. On first file use,
`ensureRuntime()` synthesizes the whole runtime once — the
@@ -195,6 +196,6 @@ Builder (RuntimeCheck emits through the same one), saving/restoring the insert b
**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.
-**Class copies and temporaries.** Callers construct BYVAL class arguments with a same-class BYREF OVERLOAD NEW; the callee's RegisterCopy hook takes ownership. emitOwnedValue constructs lvalue copies and keeps pending arguments Alive until marshalling succeeds. RETURN copies lvalues and transfers rvalues. StackObject::Temporary uses statement cleanup and deferred temporary-only cleanup before local handlers, while ordinary objects remain function-scoped. Function-result method receivers are materialized by the same path as operator receivers.
+**Class copies and temporaries.** Callers construct BYVAL class lvalue arguments with a same-class BYREF OVERLOAD NEW when defined, otherwise they copy bits; the callee's RegisterCopy hook takes ownership. emitOwnedValue constructs lvalue copies and keeps pending arguments Alive until marshalling succeeds. RETURN copies lvalues and transfers rvalues. Whole records and BYVAL arrays retain shallow copies of class fields/elements. StackObject::Temporary uses statement cleanup and deferred temporary-only cleanup before local handlers, while ordinary objects remain function-scoped. Function-result method receivers are materialized by the same path as operator receivers. Assignment first selects an exact member OPERATOR<-; without one, it stores the same-class value directly rather than calling a copy constructor.
**Experimental library** (`pseudocode-lib/`): the default build compiles each .cc with the host compiler into build/pseudocode-lib/*.o and copies .cps units beside them. `_EXP_STRING` is a #LIB class with a single public internal void* handle owning std::string. The `_exp_string_impl_*` C ABI uses checked operations that print __cps_user_line and throw 0 on std::exception. Class copy/assignment methods prevent handle sharing; functions accept BYVAL copies. OUTPUT uses C stdout buffers. Install/release do not yet ship these artifacts.