Skip to content

Memory Layout

By default, the Zolo compiler may reorder struct fields (@repr(zolo)) for internal optimisation. When field order and alignment are contracts — FFI with C/Rust, GPU uniform buffers, binary protocols — you declare the layout explicitly with attributes:

@repr(C), @repr(packed), @repr(transparent), @align(N), and @size(N).

13-repr-align-size.zolo
Playground
// Feature: `@repr` / `@align` / `@size` — struct layout control
// Syntax:
//   @repr(C | packed | transparent | zolo)   on the struct
//   @align(N)   on a field (N must be a power of two)
//   @size(N)    on a field (forces minimum byte size)
// When to use: FFI with C/Rust plugins, GPU uniform buffers, binary
//   protocols, layout-sensitive newtypes. Default `@repr(zolo)` lets
//   the compiler reorder fields, which is unsuitable for binary stability.
//
// See `specs/repr-align-size.md` for the full specification.

// 1. @repr(C) — fields kept in declaration order, C-compatible layout.
@repr(C)
struct PacketHeader {
    version: int,
    flags: int,
    length: int,
    seq: int,
}

let hdr = PacketHeader { version: 1, flags: 0, length: 256, seq: 42 }
print("hdr.seq = {hdr.seq}")

// 2. @repr(packed) — no padding between fields.
//    Useful for wire protocols where every byte matters.
@repr(packed)
struct WireFrame {
    magic: int,
    payload: int,
}

let w = WireFrame { magic: 0xCAFE, payload: 100 }
print("w.magic = {w.magic}")

// 3. @repr(transparent) — single-field newtype-like with identical
//    binary layout to its inner field.
@repr(transparent)
struct Handle {
    raw: int,
}

let h = Handle { raw: 42 }
print("h.raw = {h.raw}")

// 4. @align(N) on a field — force alignment under @repr(C).
//    Useful for GPU uniform buffers where vec3 must be 16-byte aligned.
@repr(C)
struct CameraUniform {
    @align(16) view_proj: float,
    @align(16) view_pos:  float,
    @align(16) time:      float,
}

let cam = CameraUniform { view_proj: 1.0, view_pos: 0.0, time: 0.5 }
print("cam.time = {cam.time}")

// 5. @size(N) on a field — pad to a minimum byte size.
@repr(C)
struct PaddedHeader {
    @size(64) header: int,
    payload: int,
}

let p = PaddedHeader { header: 1, payload: 2 }
print("p.payload = {p.payload}")

// 6. Errors caught at lint/check time (uncomment to see):
//
//   @repr(unknown_policy)
//   struct X { x: int }
//     warning[unknown-repr]: unknown @repr policy `unknown_policy`
//
//   @repr(transparent)
//   struct Two { a: int, b: int }
//     error[transparent-multi-field]: @repr(transparent) requires exactly one field
//
//   @repr(C)
//   struct Bad {
//       @align(3) x: int     // not a power of two
//       @align(8192) y: int  // exceeds 4096
//   }
//     error[align-not-power-of-two]
//     error[align-too-large]
//
//   struct DefaultRepr {
//       @align(8) x: int   // @align without @repr — meaningless
//   }
//     error[layout-in-default-repr]: `@align` requires an explicit
//                                    `@repr(C|packed|transparent)` on the struct

Quick reference:

  • @repr(C) — fields in declaration order, compatible with C.
  • @repr(packed) — no padding between fields (network protocol, binary file).
  • @repr(transparent) — requires exactly one field; layout identical to the inner type.
  • @align(N) — on a field, forces alignment to N bytes (power of two, max 4096).
  • @size(N) — on a field, guarantees a minimum size of N bytes.

@align and @size require the struct to have an explicit @repr (not zolo). The compiler emits an error at check time if the combination is invalid.

See also

enespt-br