prometeu-studio/discussion/lessons/DSC-0058-multi-frontend-serializable-ir/LSN-0060-data-contract-first-for-serializable-irbackend.md
bQUARKz 701f5b0339
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.73% (17590/28493) * Branch Coverage: 52.45% (6778/12924) * Lines of Code: 28493 * Cyclomatic Complexity: 11423 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 624
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
housekeep DSC-0058
2026-07-15 13:12:36 +01:00

83 lines
5.5 KiB
Markdown

---
id: LSN-0060
ticket: multi-frontend-serializable-ir
title: Data contract first for serializable IRBackend
created: 2026-07-15
tags: [compiler, compiler-general, ir, backend, serialization, multi-frontend]
---
# Data contract first for serializable IRBackend
## Original Problem
The compiler already had a common executable handoff: `IRBackend`. PBS emits it, and common backend stages consume it before lowering to `IRVM`.
The remaining risk was subtler than direct PBS coupling. A Java-only handoff can accidentally grow around process-local assumptions: callbacks, services, object identity, mutable global state, unordered collections, lazy values, or direct references between live objects. Those shapes can work inside one JVM and still make a future external frontend or out-of-process boundary expensive to add.
The work needed to keep the handoff serializable by design without choosing JSON, Protobuf, RPC, plugin loading, or a separate process too early.
## Consolidated Decision
`IRBackend` is a data contract first.
Public `IRBackend` contract types must remain modelable as an acyclic, deterministic data graph made from primitives, strings, enums, immutable values, ordered lists, explicit ids, stable symbolic keys, and source attribution values such as file ids and spans.
The public handoff must not expose callbacks, lambdas, service objects, registries, visitors, live compiler services, frontend AST/parser/token/semantic/editorial objects, mutable global state, object identity semantics, unordered output-affecting collections, cyclic public references, process-dependent lazy values, or a selected wire format.
The important boundary is the data shape, not a codec. A future serializer should be able to consume the handoff because the public model is already disciplined, but this decision does not require a serializer to exist now.
## Final Implementation
The implementation updated the compiler-general backend spec with a new `IRBackend` serializability-by-design subsection. The spec now states the allowed and forbidden public shapes and keeps wire format selection explicitly deferred.
The conformance matrix added `G20-4.2.x` rows for the new requirements:
- public `IRBackend` modelability as an acyclic deterministic data graph;
- rejection of callbacks, services, mutable or unordered collections, frontend-owned objects, process-dependent lazy values, cyclic public references, and wire-format commitments;
- use of explicit ids or stable symbolic keys for cross-object references.
The executable guardrails live in `IRBackendExecutableContractTest`:
- the public contract audit covers all current `p.studio.compiler.models` handoff types and nested value types;
- support types such as `FileId`, `ModuleId`, `CallableId`, `IntrinsicId`, `Span`, table reference records, and `ReadOnlyList` are explicitly classified;
- reflection checks inspect public fields, record components, public constructors, public methods, and generic type arguments;
- guardrails reject PBS/frontend exposure, callback shapes, service-like public shapes, mutable/unordered collection contracts, and other non-serializable public exposures;
- deterministic aggregation tests prove stable ordering for functions, synthetic functions, globals, executable functions, module pools, callable signatures, intrinsic pools, reserved metadata, and required capabilities;
- known references are locked to explicit ids or stable keys rather than direct object references.
The audit found no concrete public serialization leak that required changing the production model. That result is meaningful: the current `IRBackend` shape was already close to the target, so the correct implementation was spec clarification plus tests, not a redesign.
## Examples
Good public handoff shapes:
- `ModuleId`, `CallableId`, `IntrinsicId`, and `FileId` for table-scoped references;
- `ReadOnlyList<T>` for ordered collections;
- `ModuleReference`, `CallableSignatureRef`, and `IntrinsicReference` as stable table values;
- `Span` for source attribution;
- enums and records for fixed value surfaces.
Bad public handoff shapes:
- `Map`, `Set`, or mutable collection interfaces where output order matters;
- `Runnable`, `Callable`, `Function`, visitors, or callbacks;
- compiler services or registries exposed as public fields or constructor parameters;
- frontend AST, parser, token, semantic, or editor types;
- direct references to another handoff object where an id or ordered table entry is the stable contract.
## Pitfalls
- Do not confuse "serializable by design" with "serialize it now." A codec is a future implementation detail.
- Do not add typed ids everywhere by taste. Add them when a real public field would otherwise depend on textual identity, object identity, or direct object references.
- Do not treat `ReadOnlyList` as cosmetic. Ordered collections are part of the deterministic contract.
- Do not let a private assembler or aggregator implementation shape leak into public handoff semantics.
- Do not move the rule into PBS specs. PBS owns how it populates the handoff; compiler-general owns the common handoff contract.
## References
- `docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md`
- `docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md`
- `prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java`
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/specs/BackendConformanceMatrixSpecTest.java`
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java`