aeon

Programming Language designed for Program Synthesis with SMT-validation.


Project maintained by alcides Hosted on GitHub Pages — Theme by mattgraham

Quality gates and compiler sessions

CI and release validation

The Python application workflow is also the reusable release gate:

JUnit and trusted-core coverage reports are retained as workflow artifacts. Releases publish the exact wheel and source distribution produced by the wheel gate, rather than rebuilding after tests. Publishing still requires a version tag and the existing PyPI token. Branch protection settings must be updated separately if they require the old build (version) or standalone Ruff job names.

The selected core suite is deliberately small; the full-suite job remains required to cover features outside that cross-version gate. The existing policy for stochastic synthesis skips is unchanged by this CI refactor.

Run the expanded example gate locally (or inspect its coverage without running):

bash run_examples.sh --jobs 2
bash run_examples.sh --list
bash run_examples.sh --shard 0 --shards 16 --timeout-per-mib 120 --max-timeout 600
bash run_examples.sh --failures-from example-results/results.json
uv run python -m aeon --check examples/synthesis/dace/pbe/col_offset.ae

Example checking is not a claim that benchmark holes are solvable, that opaque native implementations are correct, or that accelerator/remote services work. The compiler’s normal compile-time evaluation of closed refinement expressions still applies; --check suppresses runtime execution, not refinement verification.

PSB2 multi-objective examples use one objective per training case (200, or 50 for leaders), rather than confusing output-vector arity with objective count. Their native objective-vector conversion checks the actual length before returning its refined array. Single-objective array getters are functions so that every invocation produces a fresh linear array.

Compilation sessions

CompilationSession owns lazily constructed, typed state for:

Use an explicit session when several low-level operations must share state:

from aeon.compilation.compile import compile_file
from aeon.compilation.session import CompilationOptions, CompilationSession

session = CompilationSession(CompilationOptions(smt_timeout_ms=500, cache_limit=1024))
with session.activate():
    unit, errors = compile_file("Library.ae", write_cache=False)

Top-level compilation entrypoints create a fresh session unless one is already active; recursive imports share the active session. AeonDriver retains its session for execution, export, property testing, and synthesis, and creates a new session for each parse. Nested activation restores the previous owner even after exceptions. Standalone low-level registry APIs retain context-local compatibility state, but that state is not reused by independent compilation entrypoints.

Sessions are not a promise of thread-safe Z3 execution: do not share one session across concurrent threads. Process-wide binder IDs and Z3 measure declaration IDs remain unique identity allocators, not caches of program semantics. On-disk cache serialization and dependency-fingerprint validation are separate concerns; this refactor does not change their trust policy.

Module-interface construction now lives in aeon.compilation.interface, name resolution in aeon.sugar.name_resolution, and solver state in aeon.verification.state. Existing desugaring imports remain compatible. Core substitution recursion reuses these functions’ typed boundaries instead of creating annotated local helpers repeatedly under runtime instrumentation. Liquid substitution also uses a fresh memo per call to retain shared refinement subterms. This avoids expanding a small expression DAG into an exponential tree, without caching results across replacements or compiler sessions.

Core typing contexts extend lexical scopes using shared, immutable chunks of 32 entries instead of copying every outer binder on each extension. Their mutable-sequence interface materializes a private list on structural mutation, so changing a parent or sibling’s entries does not alter an existing scope. Binder objects themselves remain shared, matching the former shallow-list-copy semantics: declaration updates stay visible. Hypothesis tests compare branching scope mutations against independent lists; a structural regression checks that scope extensions actually reuse complete chunks, without a machine-dependent timing assertion.

The surface grammar inlines identity precedence rules, avoiding intermediate parse-tree nodes that would immediately be discarded. Parenthesized expression spans and operator grouping remain covered by the core gate. Enumerative search also checks its time budget between partial-tree expansions: waiting for the next complete candidate must not leave the candidate consumer unable to stop the search. Individual expansion and validation operations remain cooperative.

Hypothesis invariants

The generated suite checks beta-substitution/evaluation, free-variable accounting, capture avoidance in type substitution, alpha-renaming of programs and refinement proofs, cached versus uncached unit interfaces and values, interpreter/Python-export/LLVM agreement, and independent type checking of deterministic synthesized candidates. Arithmetic backend comparisons use bounded integer expressions in the shared backend subset, not floating-point, overflow, native, or GPU behavior. Failed examples are shrunk by Hypothesis.

Run the same fast gate locally:

HYPOTHESIS_PROFILE=ci uv run --extra tests python scripts/run_core_tests.py -q
HYPOTHESIS_PROFILE=nightly uv run --extra tests pytest tests/core_properties_test.py -q

The CI profile uses 40 deterministic examples per property and no wall-clock deadline; the nightly profile uses 200 examples and Hypothesis’s normal example database. The default local profile is unchanged. See the Hypothesis settings reference for reproduction and configuration. Coverage is reported without an arbitrary percentage gate; changes should add tests for the affected verification paths.

Readability review and functional composition

A source review against master 3bfc0041 found useful existing boundaries: compiler phases have separate modules, compilation state has an explicit session owner, and generated tests exercise semantic invariants. The following are maintainability observations, not a full correctness or performance audit.

Area Observation Next step
compilation/link.py Context merges maintained several accumulators even though each output is a direct transformation of the inputs. Precedence rules were implicit in loop order. Express merges as comprehensions and ordered deduplication, with explicit contracts and property tests.
compilation/compile.py::compile_program A 221-line function combines cache lookup, dependency loading, compiler phases, and artifact construction. Extract cache validation and artifact construction as pure helpers before changing orchestration. Preserve error ordering and cached/uncached equivalence.
typechecking/typeinfer.py::_synth A 403-line AST dispatch function includes substantial logic within individual cases. Extract complex cases along typing-rule boundaries, preserving constraint and binder ordering. Keep the dispatch visible.
typechecking/context.py Scope containers isolate structural mutations, but binder objects remain mutable and shared; tests explicitly depend on that behavior. State ownership contracts at boundaries. Freezing binders would require a separate migration, not a mechanical refactor.

Line counts include comments and blank lines and are only navigation aids, not quality scores. Prefer named, typed transformations with visible inputs and outputs. Local mutation remains appropriate for graph traversal, memoization, and solver/session state where it avoids repeated copying or preserves sharing.

The linker merge helpers now accept read-only Sequence inputs and construct fresh result containers. Typing-context merging keeps the first equal entry, not merely the first name, and unions every trust set. Elaboration entries and instances retain input order; constructor maps and metadata use the last value for a repeated key. The result still shares binders and metadata values with its inputs; this is shallow composition, not deep immutability. Hashability remains a requirement for typing entries, as it was with the previous seen set.

tests/link_contexts_test.py checks duplicate precedence, trust union, lexical shadowing, empty inputs, and isolation of result-container mutations. Hypothesis varies duplicate distributions and metadata conflicts. Compilation integration and core property tests cover the surrounding compiler behavior.