ADR 0014 — C arrays in imported structs, C member names, and exported constants
- Status: Accepted (project owner, 2026-10-26: "start C9 with the GPT parser", then "You can go ahead with any integration you need"). The design below is the agent's under that instruction; the owner may revise it.
- Scope: the additions to C interoperability that C9 needs (
docs/cosmoos/c9-report.md): CosmoOS's GPT parser (kernel/block/gpt.c) cannot be written in Nova with the API ofkernel/include/kernel/gpt.hwithout them. ADR 0008 named C arrays as a "later addition"; this is it.
Context
gpt.h is an ordinary C header, and its shapes are common in kernel and library headers:
struct gpt_tableholdsuint8_t disk_guid[16]andstruct gpt_part parts[32];struct gpt_partandstruct gpt_build_partholduint8_t type[16], uuid[16]. An imported struct's fields could only be integers,bool, raw pointers, function pointers and embedded imported structs (type-system §31.1, §33.1).- One member is named
type, a Nova keyword. A field had to be declared with its C name. gpt_buildtakesconst struct gpt_build_part *parts, unsigned nparts: an array of imported structs behind a pointer.Ptr.addneeds the pointee's size, which Nova does not know for an imported type (§31.2).gpt.hdeclares data:extern const uint8_t gpt_type_esp[16]andgpt_type_cosmo_root[16]. Nova could export functions only, and had no constant arrays at all (const T: [u8; 2]was N0216).
Decision
- Array fields of imported structs. A field of an imported struct may have type
[E; N], whereEis an integer,bool, or an imported type. Its C member must beCE name[N]: the generated C asserts_Generic(&member, CE (*)[N]: 1, default: 0), which is exact, and that the member has the size of Nova's array. Inside Nova the field is an ordinary array place: index it (bounds-checked againstN), slice it, borrow it, and, whenEis Copy, read and assign it whole. - Arrays of imported types are C-laid-out.
[T; N]withTimported (or C-laid-out) is itself C-laid-out (§33.1): allowed where an imported type by value is (a field, an embedded member, a box's contents), never a value. Indexing it gives a place ofT, used through its fields or borrowed. - Lowering by the same representation. Nova's
[E; N]isstruct { E e[N]; }in C (c-backend.md§2), with the size and alignment ofE[N], and an access through the struct type is an access through an aggregate that contains the member's type (C11 6.5p7). An array field place is(*(nvA… *)&p->member): no copy, and every operation on arrays works unchanged. The generated C asserts the sizes are equal. @cname("name")on a field of an imported struct gives its C member name when that name is not a usable Nova identifier (a keyword:type,match,in). Nova code uses the Nova name.- Pointer arithmetic on pointers to imported types.
add,subandoffseton*T/*mut TwithTimported are allowed (still unsafe, §27.3): C scales by its ownsizeof.read,writeandfromRawstay N0294, since they would copy a value or make a slice of one. - Constant arrays.
const NAME: [E; N] = [ … ];(or[v; N]), withEan integer orbooland every element a constant expression (§10). A use is the array's value, as for any constant. - Exported constants.
@export("symbol")(or@export) on a constant of integer,boolor such array type defines C data:const E symbol[N] = { … };(orconst T symbol = v;), declared innova_exports.hasextern const E symbol[N];. It goes in read-only data, which C4's object checks allow.
Consequences
- type-system §10 (constant arrays), §28.1 and §28.4 (exported constants,
@cname), §31.1 to §31.3 (array fields,@cname, pointer arithmetic) and §33.1 (arrays of imported types) change;c-backend.md§2.3 describes the lowering. - The interpreter has no layout for imported types: accessing an array field, or
addon a pointer to an imported type, is an interpreter fault, as other imported-field accesses are (§31.4). Constant arrays and exported constants work in the interpreter. - A header's
char name[N](plainchar) cannot be declared:CCharis pointee-only (§28.3). Bit-fields, unions, anonymous members and flexible array members remain later additions. - An exported function whose C prototype in the header uses an
enumtype (enum gpt_result gpt_parse(…)) is defined by Nova with the enum's underlying integer type. GCC and Clang make an enum without negative valuesunsigned int, so the two declarations are compatible and the header can be included; a compiler that chose another type would reject the build (it could not miscompile). This is recorded in the C9 report as a property of those compilers, not a Nova rule.