Silicon's @type Name := { ... } form declares a product type (record) with named fields.
@type Point := { x Int, y Int };
Fields are declared as the definition's param list — the same positional syntax as @fn params, so no new grammar is needed.
- A struct is a heap-allocated record: a contiguous block of memory holding each field in declaration order.
- The struct name (
Point) becomes a constructor function that takes one argument per field and returns ani32pointer to the allocated record. - Fields are laid out at sequential byte offsets.
IntandBoolfields occupy 4 bytes (i32);Floatfields 4 bytes (f32);Int64fields 8 bytes (i64). - The constructor calls
alloc(totalSize)to bump-allocate the record, stores each field at its offset, and returns the pointer.
@type Point := { x Int, y Int }; generates:
(func $Point (param $x i32) (param $y i32) (result i32)
(local $__rec i32)
(local.set $__rec (call $alloc (i32.const 8)))
(i32.store (local.get $__rec) (local.get $x))
(i32.store (i32.add (local.get $__rec) (i32.const 4)) (local.get $y))
(local.get $__rec)
)p := Point(3, 7);
p.x ;; reads field x
p.y ;; reads field y
p.x lowers to (i32.load (local.get $p)).
p.y lowers to (i32.load (i32.add (local.get $p) (i32.const 4))).
Field reads at offset 0 elide the i32.add for efficiency.
p.x = 10;
Lowers to (i32.store (local.get $p) (i32.const 10)).
Struct locals must carry the struct type name in their annotation:
p := Point(3, 7); ;; field access resolves via inferred struct type
The type system tracks which locals hold which struct type via structLocals (in lower.ts) and ctx.structFields (in the typechecker). Without the annotation, field access is not resolvable.
The typechecker registers each @type struct definition as a DistinctOf(name, TypeInt) type alias. This means:
p := Point(3, 7)is accepted without an[UnknownType]error.p.xis resolved by looking upp's type, extracting the struct name, and finding the field's Silicon type inctx.structFields.- The constructor function signature (
Point : (Int, Int) → Point) is registered for call-site checking.
| Layer | File | What it does |
|---|---|---|
| Stratum registration | src/strata/struct.si |
Registers @type struct form and on_lower hook |
| Struct expander | src/strata/defExpanders.ts |
structExpander: builds constructor IRFunction, registers StructLayout |
| Layout registry | src/elaborator/registry.ts |
StructLayout / StructFieldLayout types; structTypes map on ElaboratorRegistry |
| Compiler API | src/compiler-api/index.ts |
Exposes ctx.structTypes to the comptime engine |
| Comptime import | src/comptime/imports.ts |
compiler_expandStruct: calls structExpander.expand from Silicon handler |
| Lowering | src/ir/lower.ts |
structLocals map; field read in lowerNamespace; field write in lowerBinaryOp |
| Type checking | src/types/typechecker.ts |
preRegisterStructType; struct field lookup in typeOfNamespace; ctx.structFields |
- No nested structs (struct fields of struct type) — field lowering assumes all fields are scalar wasm types.
- No struct-typed function return values in the typechecker (return type is
DistinctOf('Point', TypeInt)which isi32at the WASM level — this is correct).