Documentation

Class Model

Class Model

IRx now has a first-class low-level class system designed for Arx to target without turning IRx into a high-level object runtime.

What Classes Add Beyond Structs

Supported Modifiers

IRx currently supports these class-member modifiers:

Current semantics:

Abstract Classes And Methods

Inheritance And MRO

IRx supports multiple inheritance with deterministic C3 linearization.

Layout And Dispatch

Static Members, Constants, And Mutability

Access Control

ABI And Interop

Current Limitations

Examples

Instance Field Plus Method

read_body = astx.Block()
read_body.append(
    astx.FunctionReturn(
        astx.FieldAccess(astx.Identifier("self"), "value")
    )
)
counter = astx.ClassDefStmt(
    name="Counter",
    attributes=[
        astx.VariableDeclaration(name="value", type_=astx.Int32()),
    ],
    methods=[
        astx.FunctionDef(
            prototype=astx.FunctionPrototype(
                name="read",
                args=astx.Arguments(),
                return_type=astx.Int32(),
            ),
            body=read_body,
        )
    ],
)

Base/Derived Override

base_area_body = astx.Block()
base_area_body.append(astx.FunctionReturn(astx.LiteralInt32(1)))
child_area_body = astx.Block()
child_area_body.append(astx.FunctionReturn(astx.LiteralInt32(2)))
measure_body = astx.Block()
measure_body.append(
    astx.FunctionReturn(
        astx.MethodCall(astx.Identifier("shape"), "area", [])
    )
)
base = astx.ClassDefStmt(
    name="Base",
    methods=[
        astx.FunctionDef(
            prototype=astx.FunctionPrototype(
                name="area",
                args=astx.Arguments(),
                return_type=astx.Int32(),
            ),
            body=base_area_body,
        )
    ],
)
child = astx.ClassDefStmt(
    name="Child",
    bases=[astx.ClassType("Base")],
    methods=[
        astx.FunctionDef(
            prototype=astx.FunctionPrototype(
                name="area",
                args=astx.Arguments(),
                return_type=astx.Int32(),
            ),
            body=child_area_body,
        )
    ],
)
measure = astx.FunctionDef(
    prototype=astx.FunctionPrototype(
        name="measure",
        args=astx.Arguments(astx.Argument("shape", astx.ClassType("Base"))),
        return_type=astx.Int32(),
    ),
    body=measure_body,
)

Multiple Inheritance

root = astx.ClassDefStmt(
    name="Root",
    attributes=[astx.VariableDeclaration(name="root", type_=astx.Int32())],
)
left = astx.ClassDefStmt(
    name="Left",
    bases=[astx.ClassType("Root")],
    attributes=[astx.VariableDeclaration(name="left", type_=astx.Boolean())],
)
right = astx.ClassDefStmt(
    name="Right",
    bases=[astx.ClassType("Root")],
    attributes=[astx.VariableDeclaration(name="right", type_=astx.Float64())],
)
child = astx.ClassDefStmt(
    name="Child",
    bases=[astx.ClassType("Left"), astx.ClassType("Right")],
)

The effective MRO is Child -> Left -> Right -> Root, and the shared Root subobject appears once in the flattened layout.

Static Constant Example

limit = astx.VariableDeclaration(
    name="limit",
    type_=astx.Int32(),
    mutability=astx.MutabilityKind.constant,
    scope=astx.ScopeKind.global_,
    value=astx.LiteralInt32(99),
)
limit.is_static = True
counter = astx.ClassDefStmt(
    name="Counter",
    attributes=[limit],
)
read_limit = astx.StaticFieldAccess("Counter", "limit")

For the normative lowering and semantic details, see docs/semantic-contract.md.

Lifecycle And Ownership

Class layouts now begin with lifetime-token, concrete-field-descriptor, and dispatch pointers. The descriptor keeps multiple-inheritance base field views correct even when a base is not a prefix. ClassLifecyclePhase metadata orders base-first field initialization and hooks, deduplicating diamonds. Destruction reverses phases and fields through a concrete callback, including base-typed references. The registered class native feature owns non-atomic reference counts, zeroed allocation, and construction progress behind an opaque token.

Local references, independent parameter slots, retained aliases, expression pins, and owned returns share identity. Owned string fields clone borrows or move fresh results. File fields require exactly one fresh open initializer; acyclic class fields retain references. Analysis rejects cycles through possible subtypes, escaping lifecycle receivers, owned statics/globals, classes in containers/unions/structs/generators, and unmodeled resource fields. Fatal user calls do not gain cross-frame unwinding. See the Arx lifecycle rules for the exact constructor and cleanup boundaries. General function/method keywords and a public File class remain separate follow-ups.

Native-Backed File Builtin

Public File uses this same ClassType/layout/reference lifecycle, not a renamed opaque handle. Its host-registered native initializer checks creation in the caller; its private FileType field is released by normal class drop. Public references may alias and cross normal function boundaries. Native-backed method metadata preserves call-site status checks and receiver pinning. Canonical File subclassing is explicitly rejected in this slice. See the semantic contract.

Named method arguments bind against the statically selected visible signature, not hidden receiver parameters or the runtime override's parameter names. ResolvedMethodCall.call.arguments records the same source-order/ABI-slot plan as ordinary functions. Static, direct-base, and dynamically dispatched calls share this lowering contract; method defaults can retain resolved self access.

Search documentation

Type to search.