ADR 0008 — C struct types are imported from the codebase's headers
Inherited. ADR 0008 was accepted before CeeSafe's fork (Decision 0006), when the language had its earlier name. In this record, "the reference" is that language and project and "the reference compiler" its compiler: read them as CeeSafe and
ceesafec. Links to documents CeeSafe did not keep are given as text.
- Status: Accepted (project owner, 2026-10-24: "take the freedom of modifying [profiles.md §3.2] to suit a better implementation. The reference in fact is multi-platform; it should work with the Linux codebase also"). The design below is the agent's under that instruction; the owner may revise it.
- Scope: how CeeSafe code uses struct types that a C codebase defines: CosmoOS kernel and boot-protocol structs, Linux kernel structs, C library structs. It replaces the rule of
profiles.md§3.2 that kernel structs are "never re-declared" and are seen only as opaque types "with size and alignment queried at build time".
Context
The old rule protected against a real hazard: a struct's layout in a kernel depends on the kernel's configuration (CONFIG_* adds and removes fields, e.g. lockdep's class in spinlock_t), on the architecture, and on the ABI version (CosmoOS module ABI 11). A layout written down in the reference would be right for one build and silently wrong for another. But the rule also left the reference unable to use such a struct: an opaque type can be passed along, not read. C6's survey found that three of its five candidates (the framebuffer validator, the module ELF validator, GPT) are blocked for exactly this reason, and every driver or subsystem the reference will touch later takes kernel structs. The reference is also meant to work with other C codebases than CosmoOS, the Linux kernel above all, where the same hazard holds at a much larger scale (layouts change with every .config).
Decision
- Imported C types. Inside an
extern "C"block,struct name { field: Type; … }declares a view of a C type that a header defines, and@header("path.h")on the block names the headers that define it. The C spelling isstruct name, or the@ctype("name_t")attribute's for a typedef. A declaration without fields (struct name;) imports the type opaquely. - The layout is the header's, never the reference's. the reference knows no size, alignment or offset of an imported type. Generated C includes the headers and uses the real type, so every field access is the C compiler's member access, compiled under the codebase's own flags. One CeeSafe source works for every configuration and architecture that has the fields it names.
- Only the fields the reference uses are declared, with their C names, and each is checked. Generated C asserts at compile time that every declared field exists and has exactly the declared type (
_Static_asserton_Genericof the member and on its size). A configuration that removes or changes a field fails to compile, naming the field; it never miscompiles. - Imported types live behind pointers and references. Since the reference does not know their size, values of them are not CeeSafe values: no locals, parameters or fields of the type itself, no copies;
*T,*mut T,borrow Tandborrow mut Tonly. Field places through them are read and written like any field (embedded imported structs are places too). Ownership of such objects stays with the C API that provides them (kernel objects, boot data), which is what the kernel-memory design wants anyway. - Nothing is platform-specific. The same mechanism imports CosmoOS's
struct cosmoboot_info, Linux'sstruct file, or the C library'sstruct stat, each from that platform's headers under that platform's flags. A profile only decides which headers and flags a build uses. @repr(C)mirrors stay for types the reference owns and exports (type-system §28.5), where the reference defines the layout and C code includesceesafe_exports.h.
Consequences
profiles.md§3.2,cosmoos/abi.mdandrisks.mdP-3 change accordingly; the research audit (research/cosmoos-kernel-audit.md, implication 26) is left as written, since this satisfies it: types still come from the headers of the exact ABI.- The interpreter cannot run code that touches an imported type's fields (it has no layout); such code is an interpreter fault, as C calls are (type-system §28.6).
- Field types must be types C and the reference both spell (integers,
bool, raw pointers, imported types, and@repr(C)structs); C arrays, bit-fields, unions and anonymous members need later additions. - It is specified in
type-system.md§31 and implemented with the first component that needs it (C6b, 2026-10-24). The first implementation leaves out embedded imported structs (a field whose type is an imported type itself): fields are integers,booland raw pointers. Embedded members are a later addition, with arrays and the rest above.