Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ This repository contains the Ousia compiler workspace (`crates/*`) plus editor t
- External declarations use `extern fun name(args...) -> Type` (no body). In v1 they may appear at top level and inside `namespace` blocks (no bodies, no `comptime`).
- In v2 ABI, `extern fun` signatures cannot use struct parameter or return types; C interop boundaries that move struct-like payloads must use manual `PtrInt` wrapper signatures.
- Struct field lists allow optional trailing commas in both type declarations and struct literals.
- Struct values use byte-value semantics at assignment/call/return boundaries: codegen inserts copy barriers (`calloc` + `memcpy`) so pointer identity is not language-visible at those boundaries.
- Ownership is move-only by default: reading a non-`Copy` value moves it, and subsequent reads fail (`use of moved value ...` / `cannot move from uninitialized value ...` diagnostics).
- `Copy` controls implicit cloning: when a concrete type implements `Copy`, read sites lower through static dispatch to the concrete `Copy.copy(...)` impl; the canonical trait shape is `copy(v: Ref[Self]) -> Self`, and the backend synthesizes a temporary `Ref` wrapper at read sites for ref-style impls (legacy by-value impl params are still accepted for now).
- `Drop` controls deterministic destruction: the resolver inserts `Drop.drop(value)` calls at reassignment overwrite points and lexical scope exits (reverse declaration order), skipping moved/uninitialized bindings.
- Struct equality is universal bytewise comparison: `==` / `!=` lower to `memcmp` over full struct size (including pointer-containing structs).
- The stdlib entrypoint `crates/oac/src/std/std.oa` is now an import aggregator over split files in `crates/oac/src/std/` (`std_ascii.oa`, `std_char.oa`, `std_null.oa`, `std_option_result.oa`, `std_string.oa`, `std_ref.oa`, `std_collections.oa`, `std_set.oa`, `std_vec.oa`, `std_traits.oa`, `std_json.oa`, `std_clib.oa`, `std_io.oa`).
- `crates/oac/src/std/std_traits.oa` defines core traits (`Hash`, `Eq`) with concrete impls for practical key/value types used by stdlib and user generics.
- `crates/oac/src/std/std_traits.oa` defines core traits (`Hash`, `Eq`, `Copy`, `Drop`) and concrete scalar `Copy` impls (`Bool`, `U8`, `I32`, `I64`, `FP32`, `FP64`, `Char`, `AsciiChar`).
- The split stdlib now exposes namespaced helper APIs where applicable: JSON parsing helpers are called via `Json.*` (for example `Json.json_kind`, `Json.parse_json_document_result`, `Json.parse_json_document_value_result`).
- `crates/oac/src/std/std_json.oa` now defines a structured JSON value surface (`JsonValue`, `JsonMembers`, `JsonValues`) plus typed lookup helpers (`Json.object_get`, `Json.array_get`, `Json.value_string`, `Json.value_number`) so callers can parse into and inspect object/array trees.
- JSON booleans in the structured value surface now use payload form `JsonValue.Bool(Bool)` (replacing separate `True`/`False` variants), and `JsonKind` now reports booleans as `JsonKind.Bool`.
Expand All @@ -81,7 +83,7 @@ This repository contains the Ousia compiler workspace (`crates/*`) plus editor t
- The stdlib now also defines generic `Vec[T]` in `crates/oac/src/std/std_vec.oa` as a persistent vector-style container with APIs `new`, `with_capacity`, `push`, `pop` (`PopResult`), `get` (`Lookup`), `set` (`SetResult`), `len`, `capacity`, `reserve`, and `clear`.
- The stdlib now also defines `Io` wrappers in `crates/oac/src/std/std_io.oa` over `Clib` file-descriptor calls with explicit result enums (`IoError`, `IoReadResult`, `IoWriteResult`) and namespaced helpers `Io.read_all`, `Io.write_all`, `Io.read_file`, and `Io.write_file`.
- C interop in std is exposed through namespaced calls (`Clib.*`) and declared in `crates/oac/src/std/std_clib.oa` as `namespace Clib { extern fun ... }`; resolver keeps namespaced internal keys (`Clib__name`) while codegen emits declared extern symbol names for linking (for example `malloc`).
- Built-in `Void` is available for C-style procedure signatures; in v1 only `extern fun` may return `Void`, and `Void` is rejected as a parameter type.
- Built-in `Void` is available for procedure-style signatures; `Void` is rejected as a parameter type, and both extern and non-extern functions may return `Void`.
- Built-in `U8` is available as an unsigned byte-like numeric type (`U8/U8` arithmetic and comparisons are allowed with no implicit coercions).
- The resolver also exposes `PtrInt` as a standard numeric alias hardcoded to `I64` (for pointer-sized integer use sites).
- Runtime pointer memory helpers are compiler builtins: `load_u8(addr: PtrInt) -> U8`, `load_i32(addr: PtrInt) -> I32`, `load_i64(addr: PtrInt) -> I64`, `load_bool(addr: PtrInt) -> Bool`, `store_u8(addr: PtrInt, value: U8) -> Void`, `store_i32(addr: PtrInt, value: I32) -> Void`, `store_i64(addr: PtrInt, value: I64) -> Void`, and `store_bool(addr: PtrInt, value: Bool) -> Void`.
Expand Down
11 changes: 9 additions & 2 deletions agents/02-compiler-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ Important enforced invariants include:
- namespace call lowering is also used for generic-specialized helpers (`Alias.fn(args)` resolving to generated `Alias__fn` symbols)
- trait calls are v1 namespaced calls (`Trait.method(value, ...)`) that type-check against trait signatures and resolve to concrete impl function names (`Trait__Type__method`)
- trait coherence is enforced globally: duplicate `impl Trait for Type` is rejected, and missing impls for generic bounds are rejected at specialization time
- special lifecycle-trait signatures are enforced: `Copy.copy(Ref[Self]) -> Self` and `Drop.drop(Self) -> Void`
- ownership analysis/rewriting runs during resolve for user runtime functions:
- move-only default for non-`Copy` locals (use-after-move diagnostics)
- implicit-copy eligibility for types with concrete `Copy` impls
- inserted `Drop.drop(...)` statements at reassignment overwrite points and lexical scope exits
- built-in `U8`/`FP32`/`FP64` exist alongside integer primitives; unsuffixed decimal literals type-check as `FP32`, and `f64`-suffixed decimal literals type-check as `FP64`
- arithmetic/comparison on numerics requires matching widths/types (`U8/U8`, `I32/I32`, `I64/I64`, `FP32/FP32`, `FP64/FP64`), with no implicit int/float coercions
- `U8` comparisons/codegen are unsigned (`ult/ule/ugt/uge`), and `U8` division lowers to unsigned division (`udiv`)
Expand All @@ -142,7 +147,7 @@ Important enforced invariants include:
- resolver builtins also include pointer-memory helpers `load_u8(addr: PtrInt) -> U8`, `load_i32(addr: PtrInt) -> I32`, `load_i64(addr: PtrInt) -> I64`, `load_bool(addr: PtrInt) -> Bool`, `store_u8(addr: PtrInt, value: U8) -> Void`, `store_i32(addr: PtrInt, value: I32) -> Void`, `store_i64(addr: PtrInt, value: I64) -> Void`, and `store_bool(addr: PtrInt, value: Bool) -> Void`
- `extern fun` declarations are signature-only (`extern` cannot be `comptime` and extern functions must not have bodies)
- v2 ABI restriction: `extern fun` signatures cannot use struct parameter or return types; use manual `PtrInt` wrappers at C ABI boundaries when struct-like payloads are needed
- `Void` is restricted in v1: function parameters cannot be `Void`, and only `extern fun` may return `Void`
- `Void` is restricted in v1: function parameters cannot be `Void`, but both extern and non-extern functions may return `Void`
- declaration-based stdlib invariants (for example `AsciiChar` range checks over wrapped `Char.code`) are synthesized and registered during resolve like user-declared invariants
- consistent return types inside a function
- `main` must be either `fun main() -> I32`, `fun main(argc: I32, argv: I64) -> I32`, or `fun main(argc: I32, argv: PtrInt) -> I32`
Expand All @@ -162,7 +167,9 @@ Important enforced invariants include:
- Includes builtins and interop helpers (for example integer ops, print, string utilities) plus user/std-declared extern call targets.
- Extern calls emit symbol names from signature metadata; namespace externs (for example `Clib.malloc`) therefore call raw declared extern symbols (for example `malloc`) while keeping namespaced lookup keys internal.
- Struct literals allocate zero-initialized storage via `calloc` before field stores, so padding bytes are deterministic for bytewise equality.
- Struct assignment, struct call arguments, and struct returns insert copy barriers (`calloc` + `memcpy`) to enforce by-value byte-copy semantics at language boundaries.
- Struct assignment/call/return no longer inject unconditional clone barriers.
- Value reads lower through static `Copy.copy(...)` calls only when the resolved concrete type implements `Copy`; codegen passes either the value directly (legacy by-value impl signatures) or a synthesized temporary `Ref` wrapper (ref-style impl signatures).
- Resolver-inserted `Drop.drop(...)` statements lower as ordinary `Void` call statements in codegen.
- Struct `==` / `!=` lower through `memcmp(lhs, rhs, size)` and compare the result with zero.
- Handles expression lowering and control-flow generation.
- Trait calls are lowered with static dispatch only (resolved concrete impl symbols), with no runtime dictionaries or vtables.
Expand Down
10 changes: 8 additions & 2 deletions agents/03-language-semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ Observed in parser/IR implementation:
- `main` must use one of these signatures: `fun main() -> I32`, `fun main(argc: I32, argv: I64) -> I32`, or `fun main(argc: I32, argv: PtrInt) -> I32`.
- Assignments bind variable type to expression type.
- Struct literals are zero-initialized before field stores (`calloc`) so bytewise struct equality has deterministic padding bytes.
- Struct values use by-value byte-copy semantics at assignment/call/return boundaries (implemented with clone barriers in codegen, not pointer-identity semantics).
- Ownership is move-only by default: reading a non-`Copy` binding moves it and invalidates that binding until reassigned.
- For types with a concrete `Copy` impl, read sites implicitly clone by lowering to static `Copy.copy(...)` calls; codegen passes either the value directly (legacy by-value impl signatures) or a synthesized temporary `Ref` wrapper (ref-style impl signatures).
- Deterministic destruction uses `Drop`: resolver inserts `Drop.drop(...)` calls before overwriting initialized bindings and at scope exits (reverse declaration order); moved/uninitialized bindings are not dropped.
- Struct `==` / `!=` are universal bytewise comparisons (`memcmp` over struct size), including pointer-containing structs.
- Numeric binary ops are strict and same-type only: `U8/U8`, `I32/I32`, `I64/I64`, `FP32/FP32`, `FP64/FP64` (no implicit int/float coercions).
- `U8` relational operators (`<`, `>`, `<=`, `>=`) use unsigned comparisons in codegen.
Expand All @@ -71,13 +73,16 @@ Observed in parser/IR implementation:
- `extern fun` declarations cannot be marked `comptime` and must not define a body.
- In v2 ABI, `extern fun` declarations cannot use struct parameter types or struct return types; use `PtrInt` wrappers for manual ABI bridging.
- `Void` cannot be used as a function parameter type.
- In v1, only `extern fun` may return `Void`.
- Non-extern functions may return `Void`.
- Assignment statements cannot bind variables to `Void`-typed expressions.
- `Void`-return calls are statement-only (cannot be used as expression values).
- Namespace calls are syntactic sugar for internal function names using `Namespace__function` lowering, while preserving existing enum constructor call syntax `Enum.Variant(...)`.
- Namespace call lowering also applies to generic-specialized helpers when matching mangled symbols exist (`Alias.helper(...)` -> `Alias__helper`).
- Traits are static-only in v1: method calls use `Trait.method(value, ...)` and are resolved to concrete impl symbols (`Trait__Type__method`) before backend lowering.
- Trait method signatures must take `Self` as the first parameter type in v1.
- Special lifecycle-trait shapes are enforced exactly:
- `trait Copy { fun copy(v: Ref[Self]) -> Self }`
- `trait Drop { fun drop(v: Self) -> Void }`
- Impl coherence is global: exactly one `impl Trait for Type` is allowed in a program.
- Impl method sets/signatures must match trait declarations exactly (arity, parameter types after `Self` substitution, return type); missing/extra methods are compile errors.
- `impl` methods cannot be `extern` or `comptime` in v1.
Expand All @@ -86,6 +91,7 @@ Observed in parser/IR implementation:
- Generic expansion is ahead-of-type-checking and ahead-of-codegen: backend/proving/invariant passes still operate on concrete non-generic IR/function symbols.
- Imports are file-local-only and flat: import paths must be string literals naming `.oa` files in the same directory.
- The built-in stdlib is composed through flat imports from `crates/oac/src/std/std.oa` into split sibling files under `crates/oac/src/std/` (including `std_clib.oa` extern bindings and `std_traits.oa` trait/impl declarations), then merged into one global scope before user type-checking (including stdlib invariant declarations).
- `std_traits.oa` defines `Copy` and `Drop` alongside `Hash`/`Eq`; scalar types (`Bool`, `U8`, `I32`, `I64`, `FP32`, `FP64`, `Char`, `AsciiChar`) ship concrete `Copy` impls in std.
- The split stdlib includes generic `Option[T]` / `Result[T,E]` in `crates/oac/src/std/std_option_result.oa` with namespaced constructors/predicates/unwrapping helpers.
- The split stdlib includes generic `Ref[T]` and `Mut[T]` (in `crates/oac/src/std/std_ref.oa`) for pointer-wrapper value semantics (`from_ptr`, `ptr`, `is_null`, `add_bytes`), with typed read-only dereference helpers (`U8Ref.read`, `I32Ref.read`, `I64Ref.read`, `PtrIntRef.read`, `BoolRef.read`) and typed mutable helpers (`U8Mut.*`, `I32Mut.*`, `I64Mut.*`, `PtrIntMut.*`, `BoolMut.*`) that use builtin stores.
- Stdlib `HashTable` is now a bounded generic (`HashTable[K: Hash + Eq, V]`) with separate-chaining semantics while routing key hashing/equality through `Hash.hash(k)` and `Eq.equals(a, b)`.
Expand Down
6 changes: 3 additions & 3 deletions agents/04-testing-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ Key tests:
- `crates/oac/src/flat_imports.rs` tests assert flat import resolution: merge behavior, same-directory path constraints, and cycle detection.
- `crates/oac/src/flat_imports.rs` merge coverage also includes imported test declaration propagation.
- `crates/oac/src/ir.rs` includes a regression test that stdlib split files are loaded through `crates/oac/src/std/std.oa` imports.
- That regression currently asserts representative split-stdlib symbols including JSON (`Json__parse_json_document`), trait symbols (`Hash::hash`, `Eq::equals`, and synthesized impl functions like `Hash__I32__hash`), ASCII helpers (`AsciiChar`, `AsciiChar__from_code`), char/null/string helpers (`Char__from_code`, `Null__value`, `String__from_literal_parts`, `String__from_heap_parts`, `String__equals`, `String__starts_with`, `String__ends_with`, `String__slice_clamped`), option/result generics (`Option`, `Result` generic declarations), set/vector generics (`HashSet`, `Vec` generic declarations), IO/result types (`IoError`, `IoReadResult`, `IoWriteResult`), `Io` namespace functions (`Io__read_all`, `Io__write_all`, `Io__read_file`, `Io__write_file`), ref/mut helpers (`U8Ref`, `I32Ref`, `I64Ref`, `PtrIntRef`, `BoolRef`, `U8Mut`, `I32Mut`, `I64Mut`, `PtrIntMut`, `BoolMut`, plus `*Ref__read` and `*Mut__write` functions), C externs (`Clib__malloc`, `Clib__free`, `Clib__memcmp`), and standard aliases/types (`PtrInt`, `U8`, `Void`, `Bytes`, std-defined `String` enum).
- That regression currently asserts representative split-stdlib symbols including JSON (`Json__parse_json_document`), trait symbols (`Hash::hash`, `Eq::equals`, `Copy::copy`, `Drop::drop`, and synthesized impl functions like `Hash__I32__hash`), ASCII helpers (`AsciiChar`, `AsciiChar__from_code`), char/null/string helpers (`Char__from_code`, `Null__value`, `String__from_literal_parts`, `String__from_heap_parts`, `String__equals`, `String__starts_with`, `String__ends_with`, `String__slice_clamped`), option/result generics (`Option`, `Result` generic declarations), set/vector generics (`HashSet`, `Vec` generic declarations), IO/result types (`IoError`, `IoReadResult`, `IoWriteResult`), `Io` namespace functions (`Io__read_all`, `Io__write_all`, `Io__read_file`, `Io__write_file`), ref/mut helpers (`U8Ref`, `I32Ref`, `I64Ref`, `PtrIntRef`, `BoolRef`, `U8Mut`, `I32Mut`, `I64Mut`, `PtrIntMut`, `BoolMut`, plus `*Ref__read` and `*Mut__write` functions), C externs (`Clib__malloc`, `Clib__free`, `Clib__memcmp`), and standard aliases/types (`PtrInt`, `U8`, `Void`, `Bytes`, std-defined `String` enum).
- The same regression also asserts stdlib invariant registration/synthesis for `AsciiChar` and `Bytes` (`struct_invariants[...]` metadata plus synthesized `__struct__*__invariant__<key>` functions).
- `crates/oac/src/ir.rs` also validates accepted `main` signatures (`main()`, `main(argc: I32, argv: I64)`, and `main(argc: I32, argv: PtrInt)`).
- `crates/oac/src/ir.rs` includes alias coverage for `PtrInt` behaving as `I64` in function calls/equality and type-definition mapping.
- `crates/oac/src/ir.rs` also validates namespace call resolution/type-checking by lowering to mangled function names (`TypeName__helper`).
- `crates/oac/src/ir.rs` also validates trait-system behavior: duplicate impl rejection, impl signature mismatch rejection, missing bound impl failures at specialization, and trait-call dispatch/type-check through concrete impl symbols.
- `crates/oac/src/ir.rs` also validates `Void`/extern constraints: accepted statement calls to `Void` externs, rejection of `Void` parameters, and rejection of non-extern `Void` returns.
- `crates/oac/src/ir.rs` also validates ownership/lifecycle constraints: `Copy`/`Drop` trait-shape enforcement, move diagnostics (`use of moved value ...`, `cannot move from uninitialized value ...`), acceptance of repeated reads for `Copy` types, accepted statement calls to `Void` externs, and rejection of `Void` parameters.
- `crates/oac/src/ir.rs` also validates v2 extern ABI restrictions by rejecting struct parameters/returns in `extern fun` signatures with diagnostics that direct users to `PtrInt` wrappers.
- `crates/oac/src/ir.rs` also includes FP32 resolve/type-check regression coverage (FP32 arithmetic + comparison in `main`).
- `crates/oac/src/ir.rs` also includes FP64 resolve/type-check regression coverage (FP64 arithmetic + comparison in `main`).
Expand Down Expand Up @@ -170,7 +170,7 @@ Key tests:
- `crates/oac/src/qbe_backend.rs` also has a unit test that asserts `U8` lowers with unsigned comparison/division ops (`cultw`, `udiv`).
- `crates/oac/src/qbe_backend.rs` also has a unit test that asserts pointer-memory builtins lower to expected QBE ops (`loadub`, `loadw`, `loadl`, compare-to-zero for bool, `storeb`, `storew`, `storel`).
- `crates/oac/src/qbe_backend.rs` also has a unit test that asserts char literals lower through `call $Char__from_code`.
- `crates/oac/src/qbe_backend.rs` also has a unit regression that asserts struct by-value barriers and equality lowering emit `calloc`/`memcpy` clones plus `call $memcmp` (`qbe_codegen_structs_use_copy_barriers_and_memcmp_equality`).
- `crates/oac/src/qbe_backend.rs` also has a unit regression that asserts move-only struct behavior with preserved memcmp equality lowering (`qbe_codegen_structs_are_move_only_and_keep_memcmp_equality`).
- `crates/qbe-rs/src/tests.rs` includes coverage for FP32/FP64 constant formatting (`s_<literal>`, `d_<literal>`) and ordered float compare formatting (`clts`, `cgtd`).
- Execution fixtures now include dedicated prove/assert coverage:
- `prove_pass.oa`
Expand Down
6 changes: 6 additions & 0 deletions crates/oac/execution_tests/basic_struct_and_field_access.oa
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ struct Foo {
b: I32,
}

impl Copy for Foo {
fun copy(v: Foo) -> Foo {
return v
}
}

fun main() -> I32 {
s = Foo struct { a: 10, b: 1000, }
print(s.a)
Expand Down
6 changes: 6 additions & 0 deletions crates/oac/execution_tests/enum_basic.oa
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ enum Color {
Green,
}

impl Copy for Color {
fun copy(v: Color) -> Color {
return v
}
}

fun main() -> I32 {
c = Color.Red
d = Color.Green
Expand Down
Loading
Loading