Envzn Intermediate Representation (EvIR) — Specification and Lowering
Generated — do not edit by hand. This document is produced by a generator from the compiler's own spec-trace, which it emits as it traces every construct. Regenerate after any trace change; the companion ENVZN_CONSTITUTION.md (Article II) summarises it and must stay in sync.
Envzn compiles through its own intermediate representation, the EvIR. The front end tokenizes and parses source into an AST and analyzes it; the analyzed tree is then built into EvIR nodes (build_ir), and the EvIR is lowered to C++20 (compiler/evir/emitter.py), which clang++ turns into a native executable. Section A specifies the EvIR node set in fourteen logical groups; Section B records how each node lowers to C++, covering all 531 traced mappings.
Section A — The EvIR
The node set of the Envzn intermediate representation, grouped by role. Every concrete node defined in compiler/evir/nodes.py appears in exactly one group; the abstract roots IrExpr and IrStmt are category framing, not elements.
Part A.i — Literals and constants
The leaf value nodes — the constants a program writes directly. Each carries its resolved type and lowers to the corresponding C++ literal or kernel value; the absence leaves (EMPTY, none) carry no payload and stand for an unoccupied class or pipe slot.
IrIntLit— an integer literal, typed by the analyzer (the context-free default isint64)IrBinaryLit— a literal WRITTEN as a bit pattern (0xC3,0b1100); a subclass of IrIntLit by value, typedbinaryfor an octet and by written width beyond it — the node, not a field, says it is a bit pattern (I.D(b))IrFloatLit— a floating-point literal (defaultfloat64)IrBoolLit— a boolean literal —TRUEorFALSEIrCharLit— achar32code-point literalIrStringLit— an interned immutableStringliteralIrArrayLit— an array literal,[a, b, c]IrStatusLit— aSTATUSliteral —SUCCESS,PARTIAL_SUCCESS, orFAILURE(message, code)IrEmpty— theEMPTYabsence value of a class-typed slotIrNone— thenonevalue of an unoccupied pipe / optional slot
Part A.ii — References and access paths
The nodes that name an existing value rather than compute a new one — reads of locals, the receiver, data fields, and indexed elements. Each resolves to a place the surrounding node reads from or writes to, and the access form (owning handle, borrow, value-array slot) decides how it lowers.
IrVarRef— a read of a local, parameter, or constantIrSelf— the receiverSELFinside a methodIrFieldRead— a data-field read through.(owning, reference, or chain receiver)IrIndex— an indexed accesscoll[i]over an array, value-array, or collectionIrMultiIndex— a dense N-D subscripta[i, j, …]— fused column-major access on theEvNdArraycompanion; the element may be an OBJECT, whose cell is an owning handle
Part A.iii — Operators and conversions
Computation over values: the arithmetic, comparison, bitwise, and logical operators, and the explicit AS / INTO conversions together with the CONVERSIONS hosts that define them. Conversions are always explicit and checked; there is no implicit cast.
IrBinary— a binary operator — arithmetic, comparison, bitwise, shift, orBEQUALSIrUnary— a unary operator — negate,NOT,BNOT, factorial, increment/decrementIrConvert— anAS/INTOconversion at a use siteIrPrimCast— a primitive numeric cast (static_cast<T>) — the int/float-arm coercion behindnumberconstruction and wideningIrCoerce— an IMPLICIT scalar conversion the analyzer admitted, stated on the IR (Phase 4d.1) —kind(compiler.coercion.CoerceKind) names WHICH clause (widen, reinterpret, a provably-safe narrow, an octet range check or offset shim, a folded constant) admitted the crossing with no explicitAS/INTOIrDuplicate— aDUPLICATE valuedeep copy. The value-vs-reference SHAPE decision is made in build_ir, not the emitter: a BORROW operand (a=@REFERENCE local, a handle-ABI borrow param, or the IrElementRef transition build_ir inserts for aDUPLICATE arr[i]storage cell) lowers toENVZN::_ev_duplicate_ref<T>(operand); every other operand (owning handle / value / primitive) binds the by-valueENVZN::_ev_duplicate(value)(owning handle deep-clones, value/primitive value-copies).IrConversions— aCONVERSIONShost declaring theAS/INTOoperators for a typeIrConversionOp— a singleASorINTOoperator definitionIrOpaqueOp— anopaque[T]->wrap/->unwrapstatic-dispatch operationIrGetPointee— a representation-shape bridge: OWNING → BORROW, loweringinner.get()IrAddressOf— a representation-shape bridge: VALUE_REF → BORROW, lowering&innerIrDeref— a representation-shape bridge: BORROW → VALUE, lowering(*inner)IrElementRef— a representation-shape bridge: a storage-array cell → BORROW referent pointer, lowering_ev_ref_of(recv.data()[index]). The mutable subscript yields a non-copyable_Refwrite proxy, so the element is reached through.data()[index];_ev_ref_ofsplits shape (&elemvalue /.get()referent owning). build_ir inserts it forDUPLICATE arr[i](the inline form ofREFERENCE r =@ arr[i]; DUPLICATE r).IrRefOf— a reference-source bridge: a value → BORROW referent pointer via_ev_ref_of(inner). The=@bind of a genericTYPE_PARAMsource (resolves per-instantiation —.get()for a class T,&for a primitive T) and the general reference-source fallback.IrPipeSlotRef— a reference-source bridge: anArray[T]wrapper subscript success-slot → BORROW pointer via_EvGet<0>(inner). The wrapperarr[i]yields the pipe-XOR(REFERENCE T | STATUS)tuple;=@binds the success-slot reference pointer directly.IrMutRefCast— a reference-source bridge: a VALUE_REF (const T&value-reference) → BORROW pointer, const-stripped so a MUTABLE REFERENCET*field slot also accepts it, viaconst_cast<...>(&inner).IrWeakLock— the FALLIBLE deref of aWEAK REFERENCEhandle: weak handle →(REFERENCE T | EMPTY), lowering to_ev_weak<T>::lock()plus a validity test (C++) or anis_aliveload and branch on the referent preamble (LLVM). RESERVED, PENDING IMPLEMENTATION (2026-09-14) — not yet constructed andWEAK REFERENCEhas no token, but it has a committed consumer in the nullable foreign key of ORMapping; the node exists so the lock operation has an IR anchor rather than being re-derived in the emitter fromMatchSubject.WEAK.
Part A.iv — Calls, construction, and lambdas
The nodes that invoke behaviour or build instances — resolved method and static calls, heap construction through INIT and SUPER, and lambdas. Overload resolution is settled before lowering, so each call node already names its concrete target.
IrCall— a method or namespace/static call, overload resolved by argument typesIrCreate— a heap-classCREATE— allocation reached through an owning handleIrNdAlloc— an implicit dense N-D constructionfloat64[m,n]/float64[3,4]—ENVZN::EvNdArray<elem, RANK>(dims…), a selector resolving to_NdArray(blittable) or_NdObjectArray(object)IrNdWiden— a fixed→spine N-D widening — a compile-time fixed-shape array (EvNdArrayFixed) materialized as a runtime spineEvNdArray<elem, RANK>when it flows into afloat64[,]binding; total, one-directionalIrInit— anINITconstructor — its initializer list and bodyIrSuperInit— aSUPER(...)base-constructor call, emitted in the initializer list (sub-node)IrLambda— a lambda — expression form, block form, or no-parameter task body
Part A.v — Bindings and assignment
The nodes that introduce or update storage. The binding operator carried by each one — copy, move, or non-owning reference — is part of its meaning and decides whether the right-hand value is copied, moved, or borrowed as it lowers.
IrLet— a local declaration with its binding operator (=,:=,=@)IrAssign— an assignment to an existing local, field, or referenceIrMultiAssign— a multi-value assignment from a comma- or pipe-shaped returnIrIndexAssign— an indexed writecoll[i] = v(move or clone, per form)
Part A.vi — Control flow
The structured-control nodes — blocks, conditionals, loops, MATCH, and compile-time type narrowing — together with the clause and arm components they are built from. Control flow is always reducible; there is no arbitrary jump.
IrBlock— a brace-delimited statement sequence and its scopeIrIf— a conditional with its then / else-if / else clausesIrClause— a single condition-and-body clause within anIForWHENchain (sub-node)IrLoop— a loop — countedFOR, rangeFOR IN,WHILE, orLOOPIrNdForEach— a dense N-D coordinate walkFOR (i, j) IN grid— nested loops, contiguous axis innermostIrBreak— aBREAKout of the enclosing loopIrContinue— aCONTINUEto the next iterationIrMatch— aMATCHover a class,STATUS, or enum with exhaustive armsIrMatchArm— a singleWHENarm of aMATCH, with its binding (sub-node)IrWhenImplements— a compile-timeWHEN ... IMPLEMENTStype-narrowing block
Part A.vii — Returns and expression statements
How a method hands values back and how an expression runs for its effect. The return nodes carry the single-, comma-, and pipe-XOR-shaped results, applying any required clone or move as the value leaves the callee.
IrReturn— aRETURN, applying any required clone or move to the value as it leavesIrMultiReturn— a return populating several slots (comma or pipe-XOR shape)IrMultiSlot— a single named slot within a multi-value return (sub-node)IrExprStmt— an expression evaluated for its effect as a statement
Part A.viii — Recoverable failure and presence
The STATUS / pipe-XOR machinery and the presence tests. These nodes express the language's recoverable-failure vocabulary — consuming a pipe-XOR call in an IF or WHILE, reading its success or failure slots, and testing STATUS or class-value presence — kept strictly apart from the unrecoverable path.
IrPipeIf— anIFwhose condition is a fallible pipe-XOR producer (a pipe-returning call, a fallibleAS/INTO, aSETS_ERRNOFOREIGN call, or a collection/__op_index__mirror subscript), narrowing$=/$!in its branches. A bare value-arrayT[]/T[N]/T[N+]subscript is NOT a producer — it is a direct bounds-checked read — soIF arr[i] THENover a value array is an ordinary booleanIrIf, not this nodeIrPipeWhile— aWHILEdriven by a pipe-XOR callIrPipeRead— a read of a pipe result slot —$=,$=[i],$!,$#IrIsStatus— anIS SUCCESS/IS FAILURE/IS PARTIAL_SUCCESStestIrIsValid— anIS VALID/IS NOT VALIDpresence test on a class value
Part A.ix — Unrecoverable failure and assertions
The PANIC / RECOVER path for unrecoverable error, and the ASSERT family for programmer-error checks that halt. Distinct from the recoverable STATUS vocabulary of the previous group: these unwind or abort rather than return a value.
IrPanic— aPANICof anErrorsubclassIrTry— aTRYwith itsRECOVERandFINALLYclausesIrRecover— a singleRECOVERclause binding a caughtErrortype (sub-node)IrAssert— anASSERT/ASSERT!programmer-error check (halts on failure)IrUnreachable— anUNREACHABLE!always-on unconditional halt — diverging (satisfies definite-return)
Part A.x — Concurrency
The structured-concurrency nodes: the CONCURRENT scope that joins before it unwinds, the PARALLEL task launched within it, and the SYNCHRONIZED critical section. Each lowers onto the C++ threading runtime with its lifetime guarantees intact.
IrConcurrent— aCONCURRENTstructured-scope block (RAII join-before-unwind)IrParallel— aPARALLELtask launch within aCONCURRENTscopeIrSynchronized— aSYNCHRONIZED(lock)critical section
Part A.xi — Program structure
The declaration nodes that give a module its shape — classes, namespaces, interfaces, structs, groups, enums — and the members within them: methods, signatures, parameters, and fields. These are the top-level nodes a module is a sequence of.
IrClass— aCLASSdeclaration — fields, methods, layout, and destructorIrNamespace— aNAMESPACE— a stateless host of free functions plus CONSTANT data; lowers to a C++namespaceIrInterface— anINTERFACEdeclaration — pure-virtual method signaturesIrStruct— aSTRUCT— a public-field data record with generated constructionIrUnion— a kernel-internalUNIONraw-storage block (one typed arm live, no discriminant) lowering to a C++union— backs thenumbervalue-classIrGroup— aGROUP— a tagged set of named constantsIrEnum— anENUMdeclaration and its casesIrMethod— a method definition — signature, body,MODIFY/const,OVERRIDEIrMethodSig— a method signature without a body (interface or abstract) (sub-node)IrParam— a parameter — plain,REFERENCE,MUTABLE REFERENCE,MOVE, or variadicIrField— a data field — owning,REFERENCE,SHARED MUTABLE REFERENCE, orCONSTANT
Part A.xii — Foreign and unsafe
The typed foreign-function boundary and the UNSAFE gate around raw-handle access. The FOREIGN and BIND nodes reach genuine C symbols with their types stated; UNSAFE marks the only scope in which a raw foreign handle may be read.
IrForeignExpr— an inlineFOREIGNexpression blockIrForeignStmt— an inlineFOREIGNstatement blockIrForeignBind— aFOREIGN BINDto a C function — a typed FFI callIrForeignConstant— aFOREIGN BINDto a C constantIrForeignTypeBind— aFOREIGN_TYPE BINDto a C struct or opaque handleIrUnsafe— anUNSAFE { }block gating raw-handle access
Part A.xiii — Debug instrumentation
The debug-only capture nodes. They are present in the IR under a debug build and elided otherwise, and never change the meaning of the program they observe.
IrSnapshot— aSNAPSHOTdebug capture of a valueIrStacktrace— aSTACKTRACEdebug statementIrBinding— one in-scope name (scope-capture unit consumed byIrSnapshot) (sub-node)
Part A.xiv — Types and lifetime
The type representation every node carries, and the scope-exit destruction markers that drive deterministic cleanup. ResolvedType is the resolved type a node was given by the analyzer; IrDrop records where an owned value's life ends on the NORMAL path, and IrCleanupPad records the same for the EXCEPTIONAL one.
ResolvedType— the resolved type a node carries — primitive, class handle, value-array, or collectionIrDrop— a scope-exit destruction marker (RAII; the C++ destructor emits it implicitly) (sub-node)IrCleanupPad— the drops an EXCEPTION passing through a point must run — one row per DISTINCT live set (deduplicated, so the count matches a landing-pad count rather than a call count), innermost-first in reverse declaration order. Carried as a method-level table (cleanup_padson IrMethod/IrInit/IrClass) plus a per-statementpad_idstamp, because the live set changes at every declaration rather than per lexical scope and an IF arm is a bare statement tuple with no node to hang a field on. RESERVED, INERT (2026-09-14) — no producer;drop_insertionwill supply it as a fifth caller of the_frames_to_unwindcomputation that already answers this for four other exit kinds, andcxx_emitclears it beside the drops it already strips, so golden does not move. (sub-node)
Section B — Lowering EvIR to C++
How each EvIR node lowers to C++20, organised by the same fourteen groups. Each row is one traced mapping from that spec-trace: the node, the variant or concept that distinguishes it, the C++ shape it emits, and the governing constitution reference. Structural sub-nodes (clauses, arms, slots) carry no standalone lowering and are noted inline under their parent group.
Part B.i — Literals and constants
IrIntLit — an integer literal, typed by the analyzer (the context-free default is int64)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| integer literal | 5 |
I.D.ii |
| int literal 128bit | (((unsigned __int128)68719476736ull << 64) \| (unsigned __int128)0ull) |
— |
| int literal plain | 1234 |
— |
| int literal unsigned ull | 9223372036854775808ull |
— |
IrFloatLit — a floating-point literal (default float64)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| float literal | 3.5 |
I.D.ii |
| float literal | 1.41 |
— |
IrBoolLit — a boolean literal — TRUE or FALSE
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| boolean literal | true |
I.D.i, I.C |
| bool literal false | U'\n' |
— |
| bool literal true | true |
— |
IrCharLit — a char32 code-point literal
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| char literal | U'x' |
I.D.i, I.C |
| char literal escape | U'z' |
— |
| char literal printable | U'A' |
— |
| char literal universal | U'A' |
— |
IrStringLit — an interned immutable String literal
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| string literal | ENVZN::_ValueArray<char32_t>{0x68, 0x69} |
I.D.vii, I.C |
| string literal | make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0xA}) |
— |
| string literal empty | make_ev_unique<String>(ENVZN::_ValueArray<char32_t>()) |
— |
IrArrayLit — an array literal, [a, b, c]
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| array literal | ENVZN::_ValueArray<T>{ <e0>, <e1>, ... } |
— |
IrStatusLit — a STATUS literal — SUCCESS, PARTIAL_SUCCESS, or FAILURE(message, code)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| cstring literal | "boom" |
— |
| status message | _STATUS_FAILURE("oops") |
— |
| status msg encode | ENVZN::_utf8_from_string(this->failReason) |
— |
| status msg pipe move | std::move(std::get<1>(__pipe_1).message) |
— |
| status success | _STATUS_SUCCESS() |
— |
| status with code | _STATUS_FAILURE("boom", 42) |
— |
| status empty message | _STATUS_SUCCESS() |
— |
IrEmpty — the EMPTY absence value of a class-typed slot
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| empty literal | {} |
— |
IrNone — the none value of an unoccupied pipe / optional slot
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| none literal | nullptr |
— |
Defined in the IR, lowering not yet spec-traced (node present in
compiler/evir/nodes.py; seecompiler/evir/emitter.pyfor current emission):IrBinaryLit.
Part B.ii — References and access paths
IrVarRef — a read of a local, parameter, or constant
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| rvalue read of var | n |
I.F, I.F.i |
| constant field scalar | static constexpr double ZERO = 0.0; |
— |
| constant field string | inline static const String GREETING{ENVZN::_ValueArray<char32_t>{0x68, 0x65, 0x6C, 0x6C, 0x6F}}; |
— |
| constant field value array | static constexpr ENVZN::_ValueArrayConst<uint8_t, 6> masks = {{ 255, 127, 63, 31, 15, 7 }}; |
— |
| constant value float32 suffix | 3.14f |
— |
| constant value float64 int promote | 0.0 |
— |
| constant value string codepoints | 0x68, 0x65, 0x6C, 0x6C, 0x6F |
— |
| constant value struct braceinit | {4, Stage::END} |
— |
| field enum case | State::END |
— |
| kernel foreign constant | const int32_t _ev_fbc_F_OK = F_OK; |
— |
| varref constant qualified | Math::PI |
— |
| varref narrowed handle | joined |
— |
| varref plain | pDay |
— |
| constant value float32 int promote | result |
— |
| constant value string empty | result |
— |
| enum bare | result |
— |
| struct nested enum | result |
— |
| varref narrowed optional class | result |
— |
| varref narrowed value optional | result |
— |
IrSelf — the receiver SELF inside a method
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| self ref | this |
— |
IrFieldRead — a data-field read through . (owning, reference, or chain receiver)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| chain receiver owning field | (*this->left) |
— |
| chain receiver reference field | (*this->src) |
— |
| field array capacity | (scratch.capacity()) |
— |
| field array length | (f.length()) |
— |
| field constant qualified | Constants::ANSWER |
— |
| field foreign constant | _ev_fbc_SOCK_STREAM |
— |
| field foreign handle | (p)->ai_next |
— |
| field recv pointer deref strip | this->inner->v |
— |
| field self | this->s |
— |
| field text length | (url->length()) |
— |
| field value | q.key |
— |
| field var pointer | h->key |
— |
IrIndex — an indexed access coll[i] over an array, value-array, or collection
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| chain receiver handle storage | (*got) |
— |
| index default | s[t] |
— |
| index fixed array | sbo[0] |
— |
| index op index | b->__op_index__(i) |
— |
| assign from index move | this->data[index] |
— |
| index array subscript | this->data[index] |
— |
| index pipe slot error | this->data[index] |
— |
| index text op index | this->data[index] |
— |
IrMultiIndex — a dense N-D subscript a[i, j, …] — fused column-major access on the EvNdArray companion; the element may be an OBJECT, whose cell is an owning handle
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| ndarray | a(i, j) |
I.J |
Part B.iii — Operators and conversions
IrBinary — a binary operator — arithmetic, comparison, bitwise, shift, or BEQUALS
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| binary bit equals float64 | (std::bit_cast<uint64_t>(a) == std::bit_cast<uint64_t>(c)) |
— |
| binary bitwise shift | (a & b) |
— |
| binary default | (i + a) |
— |
| binary floordiv | ENVZN::_ev_floordiv<int32_t>(7, 2) |
— |
| binary lrotate | std::rotl(x, k) |
— |
| binary operator method dispatch | _ev_unique<ENVZN::TimeDuration>(a->__op_plus__(b)) |
— |
| binary set algebra rewrap | _ev_unique<Set<int32_t, _ev_unique<DefaultHasher<int32_t>>>>(a->unionWith(b)) |
— |
| power float pow | std::pow(static_cast<double>(2), static_cast<double>((-1))) |
— |
| power integer ipow | ev_ipow(static_cast<int32_t>(3), static_cast<int32_t>(2)) |
— |
| value operand handle deref | (*x) |
— |
| value operand subscript cast | static_cast<double>(f[0]) |
— |
| binary bit equals float32 | (this->length + 1) |
— |
| binary bit equals int | (this->length + 1) |
— |
| binary coalesce | (this->length + 1) |
— |
| binary coalesce empty | (this->length + 1) |
— |
| binary hash combine | (this->length + 1) |
— |
| binary rrotate | (this->length + 1) |
— |
| binary set algebra | (this->length + 1) |
— |
| binary wrapping | ev_wrapmul_u64(a, b) |
I.F.ii(a.iv) |
IrUnary — a unary operator — negate, NOT, BNOT, factorial, increment/decrement
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| unary bitwise not | (~a) |
— |
| unary default | (-7) |
— |
| unary factorial | ev_factorial(5) |
— |
| unary postdecrement | (i--) |
— |
| unary postincrement | (i++) |
— |
| unary preincrement | (++i) |
— |
| unary predecrement | (!(i == j)) |
— |
IrConvert — an AS / INTO conversion at a use site
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| conv fn pipe slot | bool value{}; |
— |
| convert class wrap | _ev_unique<String>(CharConverter::_cv_into__char8__String(c8)) |
— |
| enum to int cast | static_cast<int32_t>(cat) |
— |
| convert no conversion | _ev_unique<String>(NumberConverter::_cv_into__int32__String(index)) |
— |
| complex | ComplexConversions::_cv_into__complex__float64(realC) |
I.D.viii, I.D.i(g) |
| complex | _ev_unique<String>(ComplexConversions::_cv_into__complex__String(pc)) |
I.D.viii |
| number | NumberConversions::_cv_as__number__int32(n7) |
I.D.viii |
IrPrimCast — a primitive numeric cast (static_cast<T>) — the int/float-arm coercion behind number construction and widening
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| number int | static_cast<int64_t>(3) |
I.D.i(g) |
IrCoerce — an IMPLICIT scalar conversion the analyzer admitted, stated on the IR (Phase 4d.1) — kind (compiler.coercion.CoerceKind) names WHICH clause (widen, reinterpret, a provably-safe narrow, an octet range check or offset shim, a folded constant) admitted the crossing with no explicit AS/INTO
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| widen | {value} (WIDEN/REINTERPRET/NARROW_*/CONST_FITS into a non-binary dest, bare — 4d.1 identity lowering); a binary dest: static_cast<binary>(… |
I.F / I.D.i(c.i) / §28.3 |
IrConversions — a CONVERSIONS host declaring the AS/INTO operators for a type
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| conversions host | namespace ProbeConv { /* AS/INTO operator free functions */ } |
I.D.viii |
IrConversionOp — a single AS or INTO operator definition
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| conversion operator | String into_String(Color v) { ... } // free fn in host namespace |
I.D.viii |
IrOpaqueOp — an opaque[T]->wrap / ->unwrap static-dispatch operation
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| opaque wrap | _opq_wrap<int32_t>(n) // payload boxed behind void* |
I.D.v |
Defined in the IR, lowering not yet spec-traced (node present in
compiler/evir/nodes.py; seecompiler/evir/emitter.pyfor current emission):IrDuplicate,IrGetPointee,IrAddressOf,IrDeref,IrElementRef,IrRefOf,IrPipeSlotRef,IrMutRefCast,IrWeakLock.
Part B.iv — Calls, construction, and lambdas
IrCall — a method or namespace/static call, overload resolved by argument types
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| call arg handle deref | *pk2 |
— |
| class return wrap | _ev_unique<String>(clone()) |
— |
| foreign arg opaque array | (void*)((sniHost).data()) |
— |
| foreign arg passthrough | R_OK |
— |
| foreign arg string | reinterpret_cast<const char*>(ENVZN::_utf8_from_string(s).data()) |
— |
| foreign arg unsafe handle | static_cast<void*>(this->native) |
— |
| foreign arg value array | (hay).data() |
— |
| foreign bind arg load outparam | &res |
— |
| foreign bind arg opaque peek | _opq_peek<SSL*>(this->ssl) |
— |
| foreign bind call load buffer | ([&]() -> int32_t { int32_t _tmp_0_cap = (slot).capacity(); char8_t* _tmp_0 = new char8_t[_tmp_0_cap]; int32_t _r = ::read(0, _tmp_0, 1); … |
— |
| foreign bind call plain | ::cos(x) |
— |
| foreign bind call string callee owned | ([&]() -> _ev_unique<String> { const char* _r = ::gai_strerror(rc); return _r ? ENVZN::_string_from_utf8(_r) : _ev_unique<String>(); }()) |
— |
| foreign bind call string caller owned | ([&]() -> _ev_unique<String> { const char* _r = ::ev_path_list_dir(reinterpret_cast<const char*>(ENVZN::_utf8_from_string(this->value).dat… |
— |
| method inline splice | ({ auto&& _inl_101_recv = (data); (_inl_101_recv->_data); }) |
— |
| method inline trailing move | (std::move(_inl_3_v)); |
— |
| method inline void guard | (void)0; |
— |
| methodcall arg adopt clone | _ev_adopt_clone<_ev_unique<HttpHeader>>(hc->clone()) |
— |
| methodcall arg constref clone | _ev_unique<Person>(p->clone()) |
— |
| methodcall arg move | std::move(a) |
— |
| methodcall arg template move | std::move(w) |
— |
| methodcall array clear | this->data.clear() |
— |
| methodcall array length | (r6.length()) |
— |
| methodcall array remove | arr.remove(0) |
— |
| methodcall conversion helper | isDigit(c) |
— |
| methodcall conversion helper class wrap | _ev_unique<String>(decimalDigits(v)) |
— |
| methodcall cxx member | this->native.join() |
— |
| methodcall format arg class tostring | _ev_unique<String>((bob) ? (bob)->toString() : ENVZN::_string_from_utf8("EMPTY").release()) |
— |
| methodcall format arg dynbytebuffer | _ev_unique<ByteBuffer>((dbb).toByteBuffer()) |
— |
| methodcall format arg dynstring | _ev_unique<String>((name).toString()) |
— |
| methodcall format arg unrenderable | ENVZN::_string_from_utf8("[??]") |
— |
| methodcall namespace | IntUtil::square(7) |
— |
| methodcall namespace class wrap | _ev_unique<String>(IntUtil::label(42)) |
— |
| methodcall opaque deepcopy | _opq_clone(args[idx]) |
— |
| methodcall opaque loaded | value.loaded() |
— |
| methodcall opaque unwrap | _opq_unwrap<bool>(value) |
— |
| methodcall opaque unwrap class | _opq_unwrap_class<String>(value) |
— |
| methodcall self implicit | burn() |
— |
| methodcall string format formatter | _ev_unique<String>(Formatter().format(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x24, 0x31}), _opq_pack(cv))) |
— |
| methodcall string format wrap | _ev_unique<String>(tmpl->formatWith(BinaryMode::HEX_SPACED, args)) |
— |
| methodcall string isempty | (em->isEmpty()) |
— |
| methodcall variadic opaque pack | ds.at(4) |
— |
| string arg handle deref | *e |
— |
| call arg opaque move | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| foreign arg bytebuffer | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall arg interface upcast | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall array capacity | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall array iterator synth | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall foreign legacy | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall generic | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall opaque wrap | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| methodcall super | _ev_unique<ArrayIterator<T>>(iterator()) |
— |
| complex | Complex(Number(static_cast<int64_t>(3)), Number(static_cast<int64_t>(4))).__op_plus__(Complex(Number(static_cast<int64_t>(5)), Number(stat… |
I.D.i(g) |
| complex | Complex(Number(static_cast<int64_t>(3)), Number(static_cast<int64_t>(4))).__op_minus__(Complex(Number(static_cast<int64_t>(1)), Number(sta… |
I.D.i(g) |
| complex | Complex(Number(static_cast<int64_t>(1)), Number(static_cast<int64_t>(2))).__op_times__(Complex(Number(static_cast<int64_t>(3)), Number(sta… |
I.D.i(g) |
| complex | Complex(Number(static_cast<int64_t>(1)), Number(static_cast<int64_t>(0))).__op_divide__(Complex(Number(static_cast<int64_t>(0)), Number(st… |
I.D.i(g) |
| complex | Complex(Number(static_cast<int64_t>(3)), Number(static_cast<int64_t>(4))).negate() |
I.D.i(g) |
| complex | sum.equals(Complex(Number(static_cast<int64_t>(8)), Number(static_cast<int64_t>(6)))) |
I.D.i(g), I.F.ii.c |
| complex | c.real() |
I.D.i(g) |
| complex | c.imaginary() |
I.D.i(g) |
| complex | c.conjugate() |
I.D.i(g) |
| complex | c.magnitude() |
I.D.i(g) |
| complex | Complex(Number(static_cast<int64_t>(1)), Number(static_cast<int64_t>(0))).phase() |
I.D.i(g) |
| number | drift.isClose(Number(static_cast<double>(0.3)), 1.5e-08, 1e-12) |
I.F.ii.c |
| number | sum.equals(Number(static_cast<int64_t>(7))) |
I.D.i(g), I.F.ii.c |
| number | n.kind() |
I.D.i(g) |
IrCreate — a heap-class CREATE — allocation reached through an owning handle
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| construct heap class | make_ev_unique<Widget>(5) |
I.F.iii, I.K.v, II.D |
| call arg byref local | nums |
— |
| call arg move | std::move(r) |
— |
| create class | make_ev_unique<Box>() |
— |
| create qualified class | make_ev_unique<Logger::NullSink>() |
— |
| create struct | FloatingDecimal64() |
— |
| create value class | DynamicString() |
— |
| create arg struct constref clone | make_ev_unique<ArrayIterator<T>>(this->data) |
— |
| create arg struct move | make_ev_unique<ArrayIterator<T>>(this->data) |
— |
| create qualified struct | make_ev_unique<ArrayIterator<T>>(this->data) |
— |
| create qualified struct pod | make_ev_unique<ArrayIterator<T>>(this->data) |
— |
| create struct pod | make_ev_unique<ArrayIterator<T>>(this->data) |
— |
| complex | Complex(Number(static_cast<int64_t>(5)), Number(static_cast<int64_t>(2))) |
I.C(f), I.D.i(g) |
| number int | Number(static_cast<int64_t>(3)) |
I.D.i(g) |
| number float | Number(static_cast<double>(3.14)) |
I.D.i(g) |
IrNdAlloc — an implicit dense N-D construction float64[m,n] / float64[3,4] — ENVZN::EvNdArray<elem, RANK>(dims…), a selector resolving to _NdArray (blittable) or _NdObjectArray (object)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| ndarray | ENVZN::EvNdArray<double, 2, ENVZN::EvArrayMode::_GROWABLE>(m, n) |
I.J |
IrNdWiden — a fixed→spine N-D widening — a compile-time fixed-shape array (EvNdArrayFixed) materialized as a runtime spine EvNdArray<elem, RANK> when it flows into a float64[,] binding; total, one-directional
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| ndarray | ENVZN::EvNdArray<elem, RANK, _GROWABLE>(<fixed>) — kernel converting ctor, element-wise (clone for object cells); NOT a memcpy |
I.J.i |
IrInit — an INIT constructor — its initializer list and body
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| constructor (INIT) | Probe(const ENVZN::EvArray<_ev_unique<String>>& argv) { } |
I.K.v |
IrLambda — a lambda — expression form, block form, or no-parameter task body
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| lambda block | [&](<params>) { /* body */ } |
— |
| lambda expr | [&](<params>) { /* body */ } |
— |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrSuperInit— aSUPER(...)call, emitted in theIrInitinitializer list.
Part B.v — Bindings and assignment
IrLet — a local declaration with its binding operator (=, :=, =@)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| local var, no init | int32_t n = 5; |
I.E, II.N |
| primitive copy var->var | int32_t m = n; |
I.G, I.J.ii |
| move-init local | <owner><<Class>> w = <init>; |
I.G, I.I.i, I.J.ii |
| refsource array cell | const String* piece0 = _ev_ref_of(r9[0]); |
— |
| refsource field class | m->left.get() |
— |
| refsource field param | _ev_ref_of(e->key) |
— |
| refsource pipe slot | const ENVZN::EvArray<char8_t>* got = &std::get<0>(__pipe_2); |
— |
| refsource self param | _ev_ref_of(this->key) |
— |
| refsource self reference field | this->active |
— |
| refsource var storage refvar | const_cast<std::remove_const_t<std::remove_reference_t<decltype(source)>>*>(&source) |
— |
| vardecl constant scalar | static constexpr uint64_t n_div_5 = 3689348814741910323; |
— |
| vardecl default | _ev_unique<Person> noOne = {}; |
— |
| vardecl fixed array nogrow | ENVZN::EvArray<DynamicString> fixed(12, true); |
— |
| vardecl foreign handle default | struct addrinfo * res = nullptr; |
— |
| vardecl from field move | _ev_unique<RBNode<K, V>> x = std::move(h->left); |
— |
| vardecl from var move | _ev_unique<Payload> second = std::move(first); |
— |
| vardecl general | Pair q = p; |
— |
| vardecl handle mode | const int32_t* v = box->value(); |
— |
| vardecl interface upcast call | _ev_unique<Cloneable> c = _ev_unique<Cloneable>(original->clone()); |
— |
| vardecl interface upcast create | _ev_unique<Dictionary<__int128, int32_t, _ev_unique<DefaultHasher<__int128>>>> d = _ev_unique<Dictionary<__int128, int32_t, _ev_unique<Def… |
— |
| vardecl parametric adopt clone | K k2 = _ev_adopt_clone<K>(key->clone()); |
— |
| vardecl reference deref | int32_t v = *(b->value()); |
— |
| vardecl storage capacity | ENVZN::EvArray<char32_t> b = char32(16); |
— |
| vardecl value class init | DynamicString a = DynamicString(); |
— |
| refsource array element | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource field storage | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource field value | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource iter ref | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource none | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource self | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource var param | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource var storage | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| refsource var value | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl constant array | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl constant string | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl from cell clone | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl from cell move | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl from index value | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl from refvar clone | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl group deref | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl optional field clone | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
| vardecl value class default | _ev_unique<ArrayIterator<T>> result = make_ev_unique<ArrayIterator<T>>(this->data); |
— |
IrAssign — an assignment to an existing local, field, or reference
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| reassign primitive | n = 5; |
I.G, I.I.i, I.J.ii |
| assign chain default | spec.width = 0; |
— |
| assign chain field move | h->right = std::move(x->left); |
— |
| assign chain var move | x->left = std::move(h); |
— |
| assign default | ok = false; |
— |
| assign from cell move | value = std::move(this->data.mutable_data()[0]); |
— |
| assign from field move | value = std::move(popped->value); |
— |
| assign handle deref | v = *(r); |
— |
| assign handle flip | local = holder->storedRef(); |
— |
| assign handle rebind | handle = this->guarded.get(); |
— |
| assign interface upcast call | it = _ev_unique<ReferenceIterator<V>>((({ auto&& _inl_87_recv = (other); (std::move(_ev_unique<ReferenceIterator<V>>(_inl_87_recv->data->i… |
— |
| assign mutref writethrough | m = 99; |
— |
| assign parametric adopt clone | k2 = _ev_adopt_clone<K>(this->key->clone()); |
— |
| assign string field clone | result = _ev_unique<String>((this->source)->clone()); |
— |
| assign uptr move | result = std::move(paths); |
— |
| class init field default | this->id = i; |
— |
| init param scalar | this->inner = make_ev_unique<Inner>(7); |
— |
| refsource self storage | &this->data |
— |
| refsource var class | this->src = s.get(); |
— |
| self assign chain default | this->holder->node->isRed = true; |
— |
| self assign chain field move | this->head->next = std::move(taken->next); |
— |
| self assign chain var move | this->head->next = std::move(newNode); |
— |
| self assign constref clone | this->label = _ev_unique<String>(label->clone()); |
— |
| self assign default | this->i = 0; |
— |
| self assign move | this->head = h; |
— |
| self assign opaque wrap | this->ctx = _opq_wrap<SSL_CTX*>(::SSL_CTX_new(::TLS_client_method())); |
— |
| self assign reference | this->src = s.get(); |
— |
| self assign string constref clone | this->name = n; |
— |
| self assign strong smr | this->core = core; |
— |
| assign chain reference | this->length = (this->length + 1); |
— |
| assign chain string move | this->length = (this->length + 1); |
— |
| assign copy construct | this->length = (this->length + 1); |
— |
| assign deref copy | this->length = (this->length + 1); |
— |
| assign parametric move | this->length = (this->length + 1); |
— |
| self assign array | this->length = (this->length + 1); |
— |
| self assign chain reference | this->length = (this->length + 1); |
— |
| self assign parametric adopt clone | this->length = (this->length + 1); |
— |
| self assign string move | this->length = (this->length + 1); |
— |
IrMultiAssign — a multi-value assignment from a comma- or pipe-shaped return
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| multiassign extra existing | p0 = std::get<1>(_ret_5); |
— |
| multiassign extra typed | auto _ret_1 = d->lookup(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x6F, 0x70, 0x65})); int32_t v = std::get<0>(_ret_1); _E… |
— |
| multiassign first existing | prodHi = std::get<0>(_ret_4); |
— |
| multiassign first typed | auto _ret_1 = d->lookup(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x6F, 0x70, 0x65})); int32_t v = std::get<0>(_ret_1); _E… |
— |
| multiassign slot get | auto _ret_0 = labels->lookup(alpha); int32_t va = std::get<0>(_ret_0); _EvStatus sa = std::get<1>(_ret_0); |
— |
| multiassign slot move get | auto _ret_0 = _ev_unique<ENVZN::String>(d->remove(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x61}))); _ev_unique<String> rk = st… |
— |
| multiassign tuple temp | auto _ret_0 = d->lookup(b); int32_t got = std::get<0>(_ret_0); _EvStatus gs = std::get<1>(_ret_0); |
— |
| multiassign extra ref | auto _ret_4 = umul128(a, b); |
— |
| multiassign first ref | auto _ret_4 = umul128(a, b); |
— |
IrIndexAssign — an indexed write coll[i] = v (move or clone, per form)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| indexed assign class clone | result[rlen] = _ev_unique<String>(tail->clone()); |
— |
| indexed assign default | ab[0] = 65; |
— |
| indexed assign parametric move | this->data[(j + 1)] = std::move(key); |
— |
| indexed assign none | this->data[(this->data.length())] = b; |
— |
Part B.vi — Control flow
IrBlock — a brace-delimited statement sequence and its scope
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| block stmt | { _ev_unique<ArrayIterator<int32_t>> iter = _ev_unique<ENVZN::ArrayIterator<int32_t>>(scores->iterator()); while (iter->next()) { const in… |
— |
IrIf — a conditional with its then / else-if / else clauses
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| if stmt | if (s) { if (d) { if (v) { if (i) { Stdio.printline(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x73, 0x74, 0x72, 0x69, 0x6E, 0x67… |
— |
IrLoop — a loop — counted FOR, range FOR IN, WHILE, or LOOP
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| for counted | for (int32_t i = 1; i < 6; ++i) { _EvStatus s = pool->submit(make_ev_unique<SquareJob>(i, results)); } |
— |
| forin concrete iterator | [' {', ' auto _ev_iter = src.iterator();', ' while (_ev_iter->hasNext()) {'] |
— |
| repeat loop | [' for (int32_t _rep_6 = 0; _rep_6 < (18); ++_rep_6) {'] |
— |
| while loop | while (m > 0) { char32_t d = (48 + (m % 10)); tmp.prepend(d); m = (m / 10); } |
— |
| cstyle for | while ((i + 3) <= n) { |
— |
| dowhile loop | while ((i + 3) <= n) { |
— |
| foreach range for | while ((i + 3) <= n) { |
— |
| forin range for | while ((i + 3) <= n) { |
— |
| forin virtual iterator | while ((i + 3) <= n) { |
— |
| loop iter | while ((i + 3) <= n) { |
— |
| until loop | while ((i + 3) <= n) { |
— |
IrNdForEach — a dense N-D coordinate walk FOR (i, j) IN grid — nested loops, contiguous axis innermost
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| ndarray | nested for-loops, axis 0 innermost (column-major) |
I.J |
IrBreak — a BREAK out of the enclosing loop
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| break stmt | break; |
— |
IrContinue — a CONTINUE to the next iteration
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| continue stmt | (emits — first-gen C++ not captured for this row) |
— |
IrMatch — a MATCH over a class, STATUS, or enum with exhaustive arms
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| status match bind | _ev_unique<String> msg = ENVZN::_string_from_utf8(s.message); |
— |
| status match cond | s.kind == _EvStatus::StatusKind::SUCCESS |
— |
| valuematch enum qualify | if (d == Direction::NORTH) { ok = false; } else if (d == Direction::EAST) { } else if (d == Direction::SOUTH) { ok = false; } else if (d =… |
— |
| valuematch equality | if (d == Direction::NORTH) { ok = false; } else if (d == Direction::EAST) { } else if (d == Direction::SOUTH) { ok = false; } else if (d =… |
— |
| match group bind | (emits — first-gen C++ not captured for this row) |
— |
| match group when | (emits — first-gen C++ not captured for this row) |
— |
| match optional bind | (emits — first-gen C++ not captured for this row) |
— |
| match optional none | (emits — first-gen C++ not captured for this row) |
— |
| match optional when | (emits — first-gen C++ not captured for this row) |
— |
| match weak bind | (emits — first-gen C++ not captured for this row) |
— |
| match weak none | (emits — first-gen C++ not captured for this row) |
— |
| match weak when | (emits — first-gen C++ not captured for this row) |
— |
| valuematch condition | (emits — first-gen C++ not captured for this row) |
— |
IrWhenImplements — a compile-time WHEN ... IMPLEMENTS type-narrowing block
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| when implements block | if constexpr (std::is_base_of_v<Cloneable, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond class | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond group | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond implements | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond inoperative | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond primitive | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
| when implements cond primitive set | if constexpr (std::is_base_of_v<I, T>) {\n /* then arm */\n} else {\n /* else arm */\n} |
— |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrClause— a condition-and-body clause, lowered within itsIrIf/IrWhenImplements;IrMatchArm— aWHENarm, lowered within itsIrMatch.
Part B.vii — Returns and expression statements
IrReturn — a RETURN, applying any required clone or move to the value as it leaves
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| auto clone class field | return _STATUS_SUCCESS(); |
— |
| auto clone header | return _STATUS_SUCCESS(); |
— |
| auto clone return | return _STATUS_SUCCESS(); |
— |
| auto clone value | return _STATUS_SUCCESS(); |
— |
| class default ctor | return _STATUS_SUCCESS(); |
— |
| class field plain | return combine(sh, sr); |
— |
| class field reference | return _STATUS_FAILURE("WorkerPool round-trip produced the wrong sum"); |
— |
| class header bare | return _STATUS_SUCCESS(); |
— |
| class header bases | return _STATUS_FAILURE("cloneInheritanceSmoke step 1"); |
— |
| class init ctor | return _STATUS_FAILURE("bug103Smoke: inlined bare-name CallStmt lost receiver"); |
— |
| class init ctor empty | return _STATUS_SUCCESS(); |
— |
| class init super | return _STATUS_FAILURE("cloneInheritanceSmoke step 1"); |
— |
| conversions namespace | return _STATUS_SUCCESS(); |
— |
| cpp name method keyword | return _STATUS_SUCCESS(); |
— |
| init param array | return _STATUS_FAILURE("subscription did not outlive its broker"); |
— |
| init param heap | return _STATUS_FAILURE("at least one assertion failed"); |
— |
| init param heap mutable | return _STATUS_FAILURE("refSmoke step 1"); |
— |
| interface destructor | return combine(sh, sr); |
— |
| interface header | return combine(sh, sr); |
— |
| interface method pure virtual | return combine(sh, sr); |
— |
| interface ret class pointer | return combine(sh, sr); |
— |
| method return slot class | _ev_unique<String> result; |
— |
| method return slot reference | return this->guarded; |
— |
| method return slot value | return true; |
— |
| method sig class pointer | return _STATUS_SUCCESS(); |
— |
| method sig multireturn tuple | return true; |
— |
| method signature | return _STATUS_FAILURE("bug103Smoke: inlined bare-name CallStmt lost receiver"); |
— |
| pipe return tuple | return std::make_tuple(v, s); |
— |
| refsource self class | return this->stored; |
— |
| refsource self value | return this->inner; |
— |
| return clone field | return make_ev_unique<String>(*this->name).release(); |
— |
| return clone release | return e.release(); |
— |
| return clone value class | return new DynamicString(std::move(result)); |
— |
| return plain | return i; |
— |
| return reference | return this->inner; |
— |
| return void | return; |
— |
| struct copy assign | return pr.key->equals(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x61, 0x6D, 0x65})); |
— |
| struct copy ctor | return pr.key->equals(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x61, 0x6D, 0x65})); |
— |
| struct default ctor | return pr.key->equals(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x61, 0x6D, 0x65})); |
— |
| struct field | return _STATUS_SUCCESS(); |
— |
| struct header | return pr.key->equals(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x61, 0x6D, 0x65})); |
— |
| struct move defaults | return pr.key->equals(make_ev_unique<String>(ENVZN::_ValueArray<char32_t>{0x6E, 0x61, 0x6D, 0x65})); |
— |
| struct nested group | return _STATUS_SUCCESS(); |
— |
| struct value ctor | return _STATUS_SUCCESS(); |
— |
| auto clone array | return result; |
— |
| return clone field optional | return result; |
— |
| return clone local optional | return result; |
— |
IrMultiReturn — a return populating several slots (comma or pipe-XOR shape)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| interface ret multireturn | return std::make_tuple(<v1>, <v2>, ...); |
— |
| multireturn tuple | return std::make_tuple(<v1>, <v2>, ...); |
— |
IrExprStmt — an expression evaluated for its effect as a statement
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| call arg class passthrough | testValueAccess(r); |
— |
| expr stmt | (i++); |
— |
| stmt call | bump(); |
— |
| string arg move | pass(label); |
— |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrMultiSlot— a return slot, lowered within itsIrMultiReturntuple.
Part B.viii — Recoverable failure and presence
IrPipeIf — an IF whose condition is a fallible pipe-XOR producer (a pipe-returning call, a fallible AS/INTO, a SETS_ERRNO FOREIGN call, or a collection/__op_index__ mirror subscript), narrowing $= / $! in its branches. A bare value-array T[]/T[N]/T[N+] subscript is NOT a producer — it is a direct bounds-checked read — so IF arr[i] THEN over a value array is an ordinary boolean IrIf, not this node
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| call arg reference return deref | {\n auto __pipe_1 = v->find(needle->data);\n if (std::get<1>(__pipe_1).kind == _EvStatus::StatusKind::SUCCESS) {\n /* then: $RETURNED = st… |
— |
| convert call | { auto __pipe_3 = NumberConverter::_cv_into__String__int64(n64); if (std::get<1>(__pipe_3).kind == _EvStatus::StatusKind::SUCCESS) { v64 =… |
— |
| foreign bind call sets errno | ([&]() -> std::tuple<int32_t, _EvStatus> { auto _r = ::fsync(this->fd); if (_r == -1) { int _e = errno; return {_r, _STATUS_FAILURE(std::s… |
— |
| if pipe | { auto __pipe_1 = s->pop(); if (std::get<1>(__pipe_1).kind == _EvStatus::StatusKind::SUCCESS) { int32_t v = std::get<0>(__pipe_1); r->expe… |
— |
| if pipe convert | { auto __pipe_3 = NumberConverter::_cv_into__String__int64(n64); if (std::get<1>(__pipe_3).kind == _EvStatus::StatusKind::SUCCESS) { v64 =… |
— |
| pipe call variadic pack | { auto __pipe_1 = s->pop(); if (std::get<1>(__pipe_1).kind == _EvStatus::StatusKind::SUCCESS) { int32_t v = std::get<0>(__pipe_1); r->expe… |
— |
IrPipeWhile — a WHILE driven by a pipe-XOR call
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| while pipe | while (true) {\n auto __pipe_1 = it->next();\n if (!(std::get<1>(__pipe_1).kind == _EvStatus::StatusKind::SUCCESS)) break;\n const _ev_und… |
— |
| while pipe fallback | while (true) { |
— |
IrPipeRead — a read of a pipe result slot — $=, $=[i], $!, $#
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| pipe result errcode | std::get<1>(__pipe_5).code |
— |
| pipe result status | ENVZN::_string_from_utf8(std::get<1>(__pipe_1).message) |
— |
| pipe slot move | std::move(std::get<0>(__pipe_3)) |
— |
| pipe slot reference | std::get<0>(__pipe_7) |
— |
| pipe slot value | std::get<0>(__pipe_7) |
— |
| pipe call bare | std::get<0>(__pipe_1) |
— |
| pipe result error | std::get<0>(__pipe_1) |
— |
IrIsStatus — an IS SUCCESS / IS FAILURE / IS PARTIAL_SUCCESS test
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| is status | (s.kind == _EvStatus::StatusKind::FAILURE) |
— |
IrIsValid — an IS VALID / IS NOT VALID presence test on a class value
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| isvalid class optional | (h == nullptr) |
— |
| isvalid foreign handle | (p != nullptr) |
— |
| isvalid param present | (!_ev_is_present(value)) |
— |
| isvalid handle or ref | (this->head->next == nullptr) |
— |
| isvalid value optional | (this->head->next == nullptr) |
— |
Part B.ix — Unrecoverable failure and assertions
IrPanic — a PANIC of an Error subclass
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| throw stmt | throw <Class>(<args>); |
— |
IrTry — a TRY with its RECOVER and FINALLY clauses
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| entry main | try {\n /* body */\n} catch (const <E>& <name>) {\n /* catch */\n} |
— |
| try catch | try {\n /* body */\n} catch (const <E>& <name>) {\n /* catch */\n} |
— |
| try catch clause | try {\n /* body */\n} catch (const <E>& <name>) {\n /* catch */\n} |
— |
| try catch finally | try {\n /* body */\n} catch (const <E>& <name>) {\n /* catch */\n} |
— |
| try finally | try {\n /* body */\n} catch (const <E>& <name>) {\n /* catch */\n} |
— |
IrAssert — an ASSERT / ASSERT! programmer-error check (halts on failure)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| assert statement | if (!(n == 5)) { std::abort(); } // message elided |
I.M |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrRecover— aRECOVERclause, lowered within itsIrTry.Defined in the IR, lowering not yet spec-traced (node present in
compiler/evir/nodes.py; seecompiler/evir/emitter.pyfor current emission):IrUnreachable.
Part B.x — Concurrency
IrConcurrent — a CONCURRENT structured-scope block (RAII join-before-unwind)
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| concurrent scope | {\n ENVZN::_ev_scope __ev_scope_N;\n /* body (PARALLEL → __ev_scope_N.spawn) */\n} |
— |
IrParallel — a PARALLEL task launch within a CONCURRENT scope
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| parallel spawn | __ev_scope_N.spawn([&]() {\n /* task body */\n}); |
— |
| parallel unreachable | __ev_scope_N.spawn([&]() {\n /* task body */\n}); |
— |
IrSynchronized — a SYNCHRONIZED(lock) critical section
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| sync target owned | {\n std::lock_guard<std::mutex> __ev_sync_1(this->lock->_native);\n /* body */\n} |
— |
| sync target self | {\n std::lock_guard<std::mutex> __ev_sync_N(<mutex>._native);\n /* body */\n} |
— |
| synchronized block | {\n std::lock_guard<std::mutex> __ev_sync_N(<mutex>._native);\n /* body */\n} |
— |
| sync target expr | {\n std::lock_guard<std::mutex> __ev_sync_N(<mutex>._native);\n /* body */\n} |
— |
| sync target handle | {\n std::lock_guard<std::mutex> __ev_sync_N(<mutex>._native);\n /* body */\n} |
— |
| sync target param | {\n std::lock_guard<std::mutex> __ev_sync_N(<mutex>._native);\n /* body */\n} |
— |
Part B.xi — Program structure
IrClass — a CLASS declaration — fields, methods, layout, and destructor
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| kernel fwd class | class String; |
— |
| namespace block | ['namespace IntUtil {'] |
— |
| namespace forward decl | inline bool isUpper(char32_t c); |
— |
| parent base lowered | Hasher<K> |
— |
| singleton instance decl | inline _Math Math; |
— |
IrNamespace — a NAMESPACE — a stateless host of free functions plus CONSTANT data; lowers to a C++ namespace
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| namespace | namespace Math { inline float64 sqrt(...); ... } |
I.J.ii.d |
IrInterface — an INTERFACE declaration — pure-virtual method signatures
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| interface ret reference | (emits — first-gen C++ not captured for this row) |
— |
IrStruct — a STRUCT — a public-field data record with generated construction
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| class destructor | ~File() { |
— |
| kernel fwd struct | struct STATUS; |
— |
| struct pod alias | using DateTimeFields = ::_EV_ENVZN_DateTimeFields; |
— |
| struct pod global | ['struct _EV_ENVZN_DateTimeFields {'] |
— |
| class destructor call | (emits — first-gen C++ not captured for this row) |
— |
IrGroup — a GROUP — a tagged set of named constants
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| decls group forward | (emits — first-gen C++ not captured for this row) |
— |
| group decl | (emits — first-gen C++ not captured for this row) |
— |
IrEnum — an ENUM declaration and its cases
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| enum declaration | enum class Color : int32_t { RED, GREEN, BLUE }; |
I.K.viii |
IrMethod — a method definition — signature, body, MODIFY/const, OVERRIDE
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| method sig reference | (emits — first-gen C++ not captured for this row) |
— |
IrParam — a parameter — plain, REFERENCE, MUTABLE REFERENCE, MOVE, or variadic
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| variadic opaque | ENVZN::EvArray<<elem>> <name> |
I.I.vi, II.N |
| variadic | std::initializer_list<<elem>> <name> |
I.I.vi, II.N |
| value-class by const ref | const <Class>& <name> |
I.I.vi, II.N |
| by const ref | const <type>& <name> |
I.I.vi, II.N |
| value-class, MOVE | <Class>&& <name> |
I.I.vi, II.N |
| value-class | <Class>& <name> |
I.I.vi, II.N |
| brace, MODIFY | <type>& <name> |
I.I.vi, II.N |
| brace | const <type>& <name> |
I.I.vi, II.N |
| heap class, MOVE | <owner><<Class>> <name> |
I.I.vi, II.N |
| heap class, MODIFY | <owner><<Class>>& <name> |
I.I.vi, II.N |
| heap class | <Class>* <name> |
I.I.vi, II.N |
| cxx reserved | <type>& <name> |
I.I.vi, II.N |
| array, MODIFY | <type>& <name> |
I.J.i, II.N |
| array | const <type>& <name> |
I.J.i, II.N |
| primitive scalar | <type><ref?> <name> |
I.I.vi, II.N |
| call arg parametric adopt clone | _ev_adopt_clone<T>(value->clone()) |
— |
| call arg parametric move | std::move(key) |
— |
| interface param template | const K& key |
— |
| arg deref into ref param | (emits — first-gen C++ not captured for this row) |
— |
| call arg parametric constref clone | (emits — first-gen C++ not captured for this row) |
— |
| init param array mutable | (emits — first-gen C++ not captured for this row) |
— |
| init param brace | (emits — first-gen C++ not captured for this row) |
— |
| init param brace mutable | (emits — first-gen C++ not captured for this row) |
— |
IrField — a data field — owning, REFERENCE, SHARED MUTABLE REFERENCE, or CONSTANT
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| class field strong smr | _ev_shared<WorkerPoolCore> core{}; |
— |
| cpp name field rename | start |
— |
| field recv pointer | h.left->left |
— |
| field var refvar | n.key |
— |
| struct pod field | int32_t day; |
— |
| auto clone optional field | (emits — first-gen C++ not captured for this row) |
— |
| call arg byref self field | (emits — first-gen C++ not captured for this row) |
— |
| class field foreign | (emits — first-gen C++ not captured for this row) |
— |
| class init field fixed array | (emits — first-gen C++ not captured for this row) |
— |
| field self narrowed handle | (emits — first-gen C++ not captured for this row) |
— |
| field self narrowed optional class | (emits — first-gen C++ not captured for this row) |
— |
| field self narrowed value optional | (emits — first-gen C++ not captured for this row) |
— |
| string arg field clone | (emits — first-gen C++ not captured for this row) |
— |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrMethodSig— a pure-virtual signature, lowered within itsIrInterface.Defined in the IR, lowering not yet spec-traced (node present in
compiler/evir/nodes.py; seecompiler/evir/emitter.pyfor current emission):IrUnion.
Part B.xii — Foreign and unsafe
IrForeignExpr — an inline FOREIGN expression block
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| inline foreign block (expr) | 42 // inline FOREIGN block: cpp_text emitted verbatim into the expression |
I.N |
IrForeignStmt — an inline FOREIGN statement block
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| foreign block stmt | // <verbatim C++ from the FOREIGN { ... } block, emitted unchanged>\nENVZN::_throw_string_oob("ByteBufferView: range out of bounds"); |
— |
IrForeignBind — a FOREIGN BIND to a C function — a typed FFI call
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| foreign bind (C function) | // no decl emitted; use sites lower to a direct ::ev_probe_fn(args) call |
I.N |
IrForeignConstant — a FOREIGN BIND to a C constant
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| foreign bind constant | PROBE_CONST // bound int32; resolved via a generated link-time macro-shim symbol |
I.N |
IrForeignTypeBind — a FOREIGN_TYPE BIND to a C struct or opaque handle
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| foreign type bind (opaque handle) | using ProbeHandle = void*; // newtype over opaque; concrete form -> <cpp_type> * |
I.N |
IrUnsafe — an UNSAFE { } block gating raw-handle access
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| unsafe block | {\n /* UNSAFE body — no runtime guard */\n} |
— |
Part B.xiii — Debug instrumentation
IrSnapshot — a SNAPSHOT debug capture of a value
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| snapshot array | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot block | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot class recurse | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot collection | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot handle | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot primitive | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
| snapshot text | ENVZN::_ev_snapshot(<var>); // -debug only |
— |
IrStacktrace — a STACKTRACE debug statement
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| stacktrace stmt | ENVZN::_ev_stacktrace(); // -debug only |
— |
Defined in the IR, lowering not yet spec-traced (node present in
compiler/evir/nodes.py; seecompiler/evir/emitter.pyfor current emission):IrBinding.
Part B.xiv — Types and lifetime
ResolvedType — the resolved type a node carries — primitive, class handle, value-array, or collection
| Variant / concept | C++ lowering | Const. |
|---|---|---|
| scalar | int8_t |
I.D.i, II.N |
| scalar | int32_t |
I.D.i, II.N |
| scalar | int64_t |
I.D.i, II.N |
| scalar | double |
I.D.i, II.N |
| scalar | bool |
I.D.i, II.N |
| scalar | char32_t |
I.D.i, II.N |
| scalar | uint8_t |
I.D.i, II.N |
| numeric group | EvNumeric |
I.J.v, II.N |
| named group | EvGroup_<Name> |
I.J.v, II.N |
| fixed dim, int literal | <N> |
I.J.i, II.N |
| fixed dim, named const | <DIM_NAME> |
I.J.i, II.N |
| SHARED CLASS owner | _ev_shared |
I.I.ii, II.N |
| default owner | _ev_unique |
I.I.ii, II.N |
| UNSAFE.HANDLE/MUTEX/CONDVAR | <_UnsafeHandle<T>> |
I.I.viii, I.D.v, II.N |
| SBO inline array | ENVZN::EvArray<<elem>, <N>> |
I.J.i, II.N |
| fixed single-dim array | ENVZN::EvArray<<elem>> |
I.J.i, II.N |
| multi-dim fixed array | std::array<<elem>, <N>> |
I.J.i, II.N |
| qualified array | ENVZN::EvArray<<ns::Name>> |
I.J.i, II.N |
| optional module class | <owner><<ns::Name>> |
I.K.i, I.N, II.N |
| optional module struct | std::optional<<ns::Name>> |
I.J.iv, II.N |
| module struct | <ns::Name> |
I.J.iv, I.N, II.N |
| module class | <owner><<ns::Name>> |
I.K.i, I.N, II.N |
| set iterator | ENVZN::_cxx_setiter<<inner>> |
— |
| map iterator | ENVZN::_cxx_mapiter<<K>, <V>> |
— |
| reserved type | <CXX_RESERVED_TYPES[name]> |
— |
| void | void |
I.K.vi, II.N |
| unbounded array | ENVZN::EvArray<<elem>> |
I.J.i, II.N |
| optional class | <owner><<Class>> |
I.D.iv |
| optional interface | <owner><<Interface>> |
I.D.iv |
| optional template param | _ev_optional_t<<T>> |
I.D.iv |
| optional primitive | std::optional<<inner>> |
I.D.iv |
| status | _EvStatus |
I.M.i, II.N |
| value class | DynamicString |
I.D.vi, I.K.i, II.N |
| heap class | <owner><<Class>> |
I.I.i, I.I.ii, I.K.i, II.N |
| fallback | <name> |
I.D.ix |
| class name, parametric | <Base><<args>> |
I.I.i, I.I.ii, I.K.i, II.N |
| class name | <Base> |
I.I.i, I.I.ii, I.K.i, II.N |
| array elem, parametric class | <Name><<args>> |
I.J.i, II.N |
| array elem, class | <Name> |
I.J.i, II.N |
| reference to class | const <Class>* <name> = <src>.get() |
I.I.iv, I.I.v, II.N |
| reference to T | <const? >_ev_underlying_t<<T>>* |
I.I.iv, I.I.v, II.N |
| reference generic | <const? ><lowered>* |
I.I.iv, II.N |
| ndarray | ENVZN::EvNdArray<elem, RANK, MODE, Dims…> (one selector: ev_is_blittable_v<elem> picks _NdArray vs _NdObjectArray; the Dims pack picks fix… |
I.J |
Sub-nodes (structural components, lowered as part of their parent — no standalone form):
IrDrop— a scope-exit drop, emitted implicitly by the C++ destructor (RAII);IrCleanupPad— an unwind-path drop set, cleared before emit alongside IrDrop.
Section C — The lowering model and implementation notes
Curated narrative — not generated from the TSV. Section A specifies the EvIR node set and Section B records the per-node C++ mappings. This section explains the lowering model those mappings sit inside: the compilation pipeline, the ownership-handle representation, the statement and abstraction lowering, concurrency, the foreign-function boundary, the generated entry point, diagnostics, and kernel-build facts. It carries the narrative that was Article II of the constitution before the EvIR specification was split into this document.
Part C.i — The compilation pipeline
The canonical compiler is a linear pipeline from Envzn source to a native executable. Source text is first tokenized into a stream of tokens carrying position information. The token stream is then parsed, by recursive descent, into an abstract syntax tree. The tree is then analyzed — the analyzer resolves types, checks the move and lock and reference rules, and verifies interface satisfaction, and it is the analyzer's passes, rather than any separate tool, that serve as the language's lint layer. The analyzed tree is then built into EvIR (build_ir) and the EvIR is emitted as C++20 (compiler/evir/emitter.py). The emitted C++ is finally handed to the backend, which invokes clang++ with -std=c++20 to produce the executable.
Around that per-translation-unit pipeline sits the module driver, which performs the work C.vi describes. It loads manifests, resolves dependencies in topological order, merges a module's source files into one compilation unit, emits the combined C++, and invokes the backend with the dependent libraries linked. All diagnostics are reported before C++ emission is attempted, so that a downstream C++ error never obscures an Envzn one. The canonical compiler is the Python implementation under bin/; a second, trimmer compiler and an in-progress self-hosted compiler exist, and where they diverge from the canonical one the divergence is treated as catching-up work, not as a second opinion.
Part C.ii — Types and memory: the ownership-handle model
The primitive types lower to fixed-width C++ types. The signed and unsigned integers from 8 to 64 bits become the <cstdint> exact-width types. The 128-bit int128 and uint128 lower to the compiler built-ins __int128 and unsigned __int128, which need no header. No libc path renders or parses a 128-bit integer, so the kernel does both through a hand-rolled decimal-and-base conversion. float32 and float64 become float and double, boolean becomes bool, and byte and binary become uint8_t. The char family lowers as three distinct C++ types, deliberately not C++'s 8-bit char — char8 becomes char8_t, char16 becomes char16_t, and char32 (the canonical name char resolves to) becomes char32_t. A character literal lowers to a UTF-32 literal, so 'a' emits as U'a'. These aliases are emitted at global scope rather than inside the kernel namespace, because they are language-level primitives and a developer expects int32 to mean the same thing in every module.
A class type lowers to a heap allocation reached through an owning handle. The default owning handle is _ev_unique<T>, a non-copyable smart handle: its non-copyability at the C++ type level is what makes an accidental second owner a compile error in the emitted code, not merely in the analyzer. The allocation is split in two — a separate T allocation and a small separate control block, _ev_ctrl<T>, of roughly a dozen bytes. The control block carries an atomic alive flag, an atomic weak_count, and an atomic strong_count. The alive flag records whether the T is still valid. The weak_count counts the _ev_weak<T> observers, each incrementing it on construction or copy and decrementing it on destruction. The control block is freed only when that count reaches zero, which guarantees no observer ever reads a deallocated control block. A type that is the referent of an escaping strong reference is shared-eligible, and its strong owners are _ev_shared<T>. That is a copyable handle: it increments strong_count on copy or bind, decrements it on drop, and destroys the T when the strong count reaches zero. A _ev_unique<T> and a _ev_shared<T> are the two owning shapes; a given binding is one or the other, never both, and the analyzer's disposition decision of I.I.v chooses which. Only shared-eligible types pay for the strong handle — every other class stays _ev_unique<T> at zero refcount cost. Primitives, String, STATUS, enum cases, and STRUCT fields are value types and are emitted inline. Stack-frame exit releases owned heap objects in reverse declaration order through the ordinary RAII chain.
A REFERENCE T and a MUTABLE REFERENCE T lower to raw C++ pointers — T const* and T* respectively — and carry no control block and no reference count. This is sound precisely because the reference checker of I.I.v has already proved, at compile time, that the reference cannot outlive its referent and cannot alias a conflicting access. The runtime therefore needs no check, and a reference costs exactly a pointer. The exception is a reference field the checker has classified as strong rather than as a borrow — an escaping SHARED MUTABLE REFERENCE — which lowers to a _ev_shared<T> owner. Reference parameters never escape, so they are always borrows and always raw pointers. The parameter-passing rules of I.I.vi lower as follows:
| Envzn parameter | C++ lowering |
|---|---|
T for a class type (a plain, read-only borrow) |
T* |
String, ByteBuffer (a plain, read-only borrow) |
const T* |
MODIFY T for a class type |
_ev_unique<T>& (the callee may rebind the handle) |
REFERENCE T |
const T* |
MUTABLE REFERENCE T |
T* |
MOVE T for a class type |
_ev_unique<T> by value (call site emits std::move) |
MOVE DynamicString, MOVE DynamicByteBuffer |
T&& rvalue reference (call site emits std::move) |
T[] |
const ENVZN::EvArray<…>& (resolves _ValueArray<…> or _ObjectArray<…> per element type) |
DynamicString, DynamicByteBuffer |
T& (callee may mutate via methods; the reference cannot outlive the call) |
STRUCT S |
S by value |
primitive, STATUS, enum |
by value |
A borrow is a raw pointer, not an owning handle. The earlier ABI passed a class parameter as const _ev_unique<T>& — an owning handle by const reference — which is a category error, because a borrow does not own what it observes, and it made legal programs uncompilable: the only upcast on an owning handle is a move, which a borrow must not perform, so a concrete handle could not be passed to a parameter of a base or interface type, and a borrowed element could not be passed at all. A raw pointer upcasts implicitly, binds a borrowed referent, and keeps EMPTY representable as nullptr, so IS VALID still works and the memory-safety floor of I.A is not traded away.
An ordinary class borrow is T* rather than const T* deliberately. The old const _ev_unique<T>& was const on the handle, not on the pointee — operator-> handed back a non-const T* — so a borrowed parameter has always been able to call the instance's MODIFY methods, and constifying the pointee would smuggle a new restriction into a bug fix. The text types are the exception: String and ByteBuffer are immutable by the type convention of I.D.vi and expose no MODIFY method, so that concern cannot arise and their borrow is const T* — which is also what allows a read-only REFERENCE local, itself a const T*, to bind to one.
Part C.iii — Statements, expressions, and control flow
Most expressions lower directly. The arithmetic and comparison operators become their C++ equivalents, as do the keyword bitwise, shift, and boolean operators. A derived operator — one a type gained by implementing an operator interface of I.K.iv — lowers to a call of the interface method the compiler derived it from. A CREATE lowers to a heap allocation through _ev_unique<T>, followed by the matching INIT call. A := move lowers to a C++ move of the handle; a = value copy to direct initialization, or to the explicit CREATE or clone() the source contained; and a =@ reference to taking the address of the place expression.
A STATUS lowers to the _EvStatus struct, which carries the outcome, a message String, and an int32_t code defaulting to 0. The pipe-XOR shape of I.M lowers to a C++ std::tuple of the success slots followed by the trailing _EvStatus — RETURNS (A a, B b | STATUS s) becomes a std::tuple<A, B, _EvStatus>. The consumption form lowers to an if whose condition tests the tuple's _EvStatus slot, with the result tokens resolving to std::get reads of that temporary: $=[i] is std::get<i>(temp), $# is std::get<status_idx>(temp).code, and $! is std::get<status_idx>(temp).message. MATCH lowers according to its subject — an enum or GROUP to a switch or std::variant visitation, a possibly-EMPTY value to a presence test over its two arms, and a condition-form MATCH to an if/else if chain.
FOR IN lowers by the shape of the iterable, in two forms that share one surface. A class collection or a user class implementing Iterator OF T takes the iterator form: a loop FOR T x IN coll { body } becomes, in effect, an owned iterator obtained from coll->iterator(), a WHILE over hasNext(), and a per-iteration next() whose pipe-XOR result binds x as a non-owning reference into the collection:
{
Iterator OF T _it := coll->iterator()
WHILE _it->hasNext() {
IF _it->next() THEN {
REFERENCE T x =@ $=
body
} ELSE {
PANIC Error("FOR/IN invariant: next() yielded a failure after hasNext() was true")
}
}
}
A raw array — T[], T[N], or T[N+], as distinct from the Array[T] wrapper class — takes the index form instead. The loop is driven by an index over the array buffer, with the loop variable bound to a borrow pointer into that buffer, and the iterable expression evaluated exactly once (it may be side-effecting). The reason is that the shared loop body is lowered for a REFERENCE variable it dereferences, for every element kind including value elements, and a value array's by-value next() cannot hand back a stable pointer to dereference. The two forms are not observably different to a program: both visit every element in order and bind a non-owning reference to it.
The implicit iterator of the first form is owned by the loop and dropped at loop exit, and that lowering serves a kernel collection and a user class that implements Iterator OF T alike. As an optimization with no observable effect, the compiler may inline the iterator's method bodies in place of virtual dispatch when the static type of the implicit iterator resolves to a FINAL CLASS. A program may not depend on whether the inlining fires.
The IDENTITY(subject) reflection intrinsic (I.K.iii(e)) introduces no IR node of its own — it reuses IrCreate. The analyzer computes the Identity field values from the subject's static type and binding. The IR builder then synthesizes an IrCreate of the kernel STRUCT Identity, whose arguments are those values as literal nodes: IrStringLit, IrBoolLit, and an IrArrayLit of IrStringLit for the lenses ancestor list. It then lowers exactly like any struct value-constructor call. No new node, and no emit-phase decision: by construction the value is legal and fixed.
Part C.iv — Abstractions
A class lowers to a C++ class. Its data fields become members and its methods become member functions. The MODIFY distinction of I.K.vi lowers to the C++ const qualifier: a read-only Envzn method emits as a const member function, a MODIFY method as a non-const one. The const-correctness Envzn enforces at its own level is thereby carried into the emitted code. INIT becomes a constructor and CLEANUP, together with the field-destruction chain, becomes the destructor. FINAL emits the C++ final specifier, which both forbids subclassing and lets the C++ compiler devirtualize and inline calls — the mechanism behind the optional iterator inlining of C.iii.
An interface lowers to an abstract C++ class of pure virtual functions, and a class that implements one gains a vtable. A call through an interface-typed reference is an ordinary virtual call — one pointer indirection — and that is the whole runtime cost of the polymorphic collections of I.K.vii. A GROUP lowers to a std::variant over its member types; a group member that recurses through Box lowers that Box to a _ev_unique indirection, which is what breaks the otherwise self-referential variant. An enum lowers by form: a simple enum to a C++ enumeration, a raw-value enum to one carrying its primitive values, and an associated-value enum to a tagged representation that a MATCH destructures.
The library aggregates are implemented internally with C++ templates, which is how Envzn offers parameterized collections without exposing user-defined generics. Array[T] backs onto the T[] storage primitive — its INTERNAL T[] data field lowers to EvArray<T>, which resolves the _ValueArray<T> value-array for a blittable T and the _ObjectArray<_ev_unique<T>> object-array for a class T. An array literal lowers to a C++ brace-init {…} for a value-element array, since _ValueArray has an initializer_list constructor. A non-empty object-element array literal cannot: an array of move-only owning handles has no initializer_list form. It lowers instead through the free factory _ev_object_array_of(e0, e1, …), which move-appends each element onto a fresh _ObjectArray through its existing grow-assign. An empty literal is a plain {} for either backing. Dictionary is a pure INTERFACE Dictionary[K, V, H] with pure-Envzn implementations behind it (ChainedHashDictionary, RedBlackTreeDictionary, ProHashDictionary); the compiler no longer special-cases it, and hashing is pluggable through the H (Hasher) type parameter rather than a C++ functor. Set[V, H] is likewise a pure-Envzn class wrapping a Dictionary, so neither Set nor Dictionary backs onto a C++ unordered container. The iterators the collections hand out are concrete FINAL classes, one per collection, and their FINAL-ness is what permits the loop-body inlining of C.iii.
Part C.v — Concurrency
CONCURRENT { … } lowers to a structured task scope — an _ev_task_scope RAII object over the block body that joins every task it launched before it unwinds. A PARALLEL { … } block lowers to a task launched on a std::thread registered with the innermost enclosing scope, and the task body emits as the callable the thread runs. The capture restrictions of I.L.vii are enforced in the analyzer before emission, so the emitted lambda captures only what the model permits. SYNCHRONIZED(lock) { … } lowers to a std::lock_guard over the lock's native mutex, held for the body and released automatically on every exit path — fall-through, RETURN, BREAK, or a PANIC unwinding through it.
Channel[T] lowers to a typed, bounded message queue with its synchronization state; Broker[T] to the lock-free publish-index-and-per-subscriber-ring structure of I.L.iv; Mutex[T] to a mutex guarding interior storage reachable only inside its SYNCHRONIZED block; Future[T] to an eventually-ready cell sharing the pipe-XOR consumption shape; Atomic[T] to a std::atomic over the primitive width; and WorkerPool[T] to the kernel's pool class built over those primitives. The low-level synchronization primitives the kernel needs — a mutex, a condition variable, an atomic, a raw thread handle — are not expressible as ordinary Envzn classes, because the underlying C++ standard-library types delete their copy and move operations. The kernel reaches them through the FOREIGN_TYPE BIND mechanism of C.vi, as a small family of _cxx-prefixed types: _cxxmutex, _cxxcondvar, _cxxatomic[T], _cxxrawthread. Each binds the corresponding C++ standard type by value, and each emits its parameters by reference rather than by value, precisely because those C++ types cannot be copied.
Part C.vi — Modules, linkage, and the foreign-function boundary
Each module's emitted C++ is wrapped in a namespace. A non-kernel module emits as namespace modulename { using namespace ENVZN; … }, and the using directive is what lets kernel types keep their bare names inside the module's emitted code. The kernel itself emits inside namespace ENVZN, with the one exception of the primitive numeric aliases, which stay at global scope as C.ii records. The module driver compiles a module in five steps. It loads the manifest for identity, target, dependencies, and foreign metadata. It resolves and recursively compiles the dependencies in topological order. It merges the source files into one compilation unit, where a duplicate type name across two files is an error. It emits the combined C++, and invokes clang++ -std=c++20 with the dependent libraries linked. An executable target produces a binary, and the compiler appends a generated int main() under the rules of C.vii. A shared_library target produces a .dylib together with a generated .headers file that lists every public, non-INTERNAL declaration and so forms the authoritative public surface a dependent compiles against.
The foreign-function boundary lowers as follows. A FOREIGN BIND declaration of a C function is type-checked against its Envzn signature alone; the compiler never reads C header content. In the ordinary case it lowers to a direct call of the bare C symbol, so a binding named open lowers FOREIGN::open(...) to ::open(...). The compiler synthesizes the conversion at each boundary type. Three markers change the lowering. The LOAD parameter modifier declares a value-array parameter write-through, so the C function writes through the storage pointer. A binding marked SETS_ERRNO(sentinel) emits a wrapper that turns an errno into a STATUS FAILURE, making it a pipe-XOR producer. A return marked $*, or $(freer()), emits a wrapper that copies the returned C string and frees the raw pointer, so the free is compiler-generated and inseparable from the call. FOREIGN is an Envzn-side greppability marker only — no FOREIGN C++ namespace is emitted. Every type at the boundary is an Envzn type mapped to the C ABI by a fixed table:
| Envzn type | C ABI type | Boundary behavior |
|---|---|---|
int8 … int64, uint8 … uint64 |
int8_t … uint64_t |
direct |
float32 / float64 |
float / double |
direct |
boolean |
bool |
direct |
byte |
uint8_t |
direct; a single C char binds as byte |
String (parameter) |
const char* |
a scoped, NUL-terminated buffer that lives exactly as long as the call |
String (return) |
const char* |
copied into a fresh String; a null result becomes EMPTY |
ByteBuffer (parameter) |
element pointer + a separate uint64 length |
Envzn-owned; the C side borrows it for the call |
opaque |
void* |
an opaque handle the Envzn side stores and passes back but never dereferences |
an Envzn STRUCT |
the compiler-emitted struct, by value | every field must transitively be a boundary type |
ByteBuffer is parameter-only: RETURNS ByteBuffer is a compile error, because a single C return value cannot carry both a pointer and a length. A C library struct — struct stat, struct sockaddr — has a layout fixed by system headers, and never crosses the boundary. A C function that needs one is reached through a module-supplied native shim, which speaks only boundary types in its own signature. FOREIGN BIND CONSTANT binds a single integer macro (e.g. O_RDONLY) through a small C shim that re-exports it as a named symbol. The LOAD shape table and the boundary rules are Article I.N(h) of the Constitution; the manifest conventions and the V2 boundary extensions are outside this spec.
Part C.vii — The generated entry point
Every executable module names one entry class, and the compiler generates the main() that runs it. The entry class is named in a sibling file, entrypoint.json, carrying a single class field. Each of the following is a compile error: an executable module that lacks the file, a shared-library module that has it, a malformed file, or a named class that does not exist, does not implement ThreadStarter, or does not declare INIT(String[] argv).
{ "class": "Main" }
The entry class implements ThreadStarter, the single-method interface — METHOD start() RETURNS STATUS — that the program's main thread and every spawned worker thread share. The entry class receives the process arguments through INIT(String[] argv). The body of that INIT is restricted by diagnostic E6037 to plain self-field assignment statements, so it cannot quietly become a second entry point. The language guarantees that start() is the first line of user-controlled logic. The compiler appends an int main() that builds a String[] from the operating system's argv, constructs the entry class with it, and calls start(). It translates the returned STATUS to a POSIX exit code: SUCCESS to 0, PARTIAL_SUCCESS and FAILURE to 1. The whole sequence is wrapped in a C++ try with three narrowing catch arms: Envzn's Error first, then std::exception, then a catch-all. Each writes a diagnostic to standard error and exits 1, so an uncaught exception produces a message rather than a silent std::terminate.
Part C.viii — Error detection and reporting
The compiler detects errors at every stage of the pipeline, and the stage at which an error is found determines both its category and how the compiler recovers from it well enough to keep finding more.
| Stage | Category | Recovery |
|---|---|---|
| Tokenization | lexical errors — an unterminated quote, an invalid escape | skip to the next line |
| Parsing | structural errors — an unexpected token, a malformed header | panic-mode resynchronization to the next statement boundary |
| Analysis | type errors — a mismatch, an undefined name, an unimplemented interface | report and continue with the other declarations |
| Analysis | move, lock, and reference errors — a use after move, a mutation under an iterator lock | report, and suggest a clone() or a lock scope where one is clear |
| Emission | code-generation errors — an unsupported construct | report; emit a stub where possible |
Every diagnostic carries four things: a source location of file, line, and column; an error level, either error, which blocks compilation, or warning, which does not; a quoted snippet of the offending line with a caret beneath the span; and, where the fix is clear, a suggestion. A diagnostic is identified by a stable E#### code, and the codes named throughout Article I are those identifiers. All errors are reported before C++ emission is attempted, so that a downstream clang++ error cannot mask the Envzn error that caused it.
A location is a range, not a point: start_line/start_col and end_line/end_col, 1-based and counted in Unicode code points. A point is the degenerate range whose end equals its start, so a diagnostic that knows only where a problem begins is expressible without a second shape. The frame is drawn only where the source is readable and the column is known; where either is missing the diagnostic prints its location line alone rather than a partial frame, because a diagnostic must never fail while reporting a failure. The caret is placed by display column — a tab advances to the next multiple of eight, and an East-Asian Wide or emoji glyph occupies two cells — which is not the stored column on any line containing either.
Where a single literal edit resolves the diagnostic, it is offered as a help: line beneath the remedy, and the same edit is machine-applicable. Most diagnostics have none, and that is the expected outcome: the remedy for a type error or a missing contract is a decision, not a substitution.
error [E1174] parse:numeric-literal-malformed: `0x` must be followed by hex digits
╭─ examples/hello.ev:6:19
6 │ int32 h = 0x
│ ^
╰─
at examples/hello.ev:6:19
help: replace with `0x1F`
The same diagnostics are available as machine-readable records under --diagnostics=json, shaped as LSP Diagnostic objects — 0-based lines and UTF-16 character offsets, with any fix carried as a TextEdit. Everything Envzn-specific is namespaced under evzn. in the record's data field, which LSP defines as opaque passthrough, so an editor consumes the stream without a translation layer. The document is always emitted, even when empty, so a consumer can distinguish a clean build from a compiler that stopped before it could report.
Part C.ix — Kernel implementation notes
The kernel — the ENVZN module — is compiled as a shared library that every Envzn program links against. It publishes a single canonical header, which the generated C++ of every module includes.
The kernel is authored as Envzn source — roughly one hundred ten .ev files — and the compiler emits from them a per-class header apiece, alongside the hand-written floor and its native glue, all of which the canonical header includes. This is the result of the demagic effort, a staged project to replace hand-written C++ kernel class bodies with emitted ones. A collection or utility that was once a block of C++ in the kernel header is now an ordinary .ev file the compiler lowers like any other. The canonical header retains only the host runtime glue that genuinely cannot be authored in Envzn — the formatting and hashing helpers, the four-form String and Binary primitive machinery, and the low-level _cxx* concurrency types of C.v. The kernel holds to one hard rule: it has no dependency on any external package, and an optional system dependency — OpenSSL, SQLite, a regex library — belongs in the module that consumes it, never in the kernel.
End of specification. Section A specifies 92 EvIR nodes in 14 groups; Section B records 518 TSV-traced lowering rows; Section C is curated narrative. Regenerate Sections A and B with the spec-spec generator.