Skip to main content

14  Compile-Time (#!) and Type Reflection

Edva uses a unified compile-time evaluation and structural reflection model introduced by #! and the Type primitive. Type values, field cursors (f), and variant cursors (v) exist strictly at compile time and are erased before LLVM code generation with zero runtime footprint (E2018 / E3129).

14.1 Compile-time evaluation (#!) forms

Construct Syntax Semantics Diagnostic
Compile-time expression val = #! expr Evaluates pure expression expr during compilation and embeds the constant result E3129: runtime-only op in #!
Explicit Type parameter f: #! T: Type => Int
f = #! T => #size T
Specializes f per concrete Type passed at the call site (f(Int)) E2018: Type used as runtime value
Inferred Type parameter f: #! T: Type, T => String
f = #! T, x => T.name
Deduces T automatically from the runtime argument x (f(42) \(\Rightarrow\) T = Int) —
Compile-time constant param f: #! N: Int, Int => Int Specializes f on compile-time constant N —
Type generator Pair: #! T: Type => (a: T, b: T)
#type IntPair Pair(Int)
Compile-time function returning a composite Type, registered via #type —
#use io

Pair: #! T: Type => (first: T, second: T)
#type IntPair Pair(Int)

fact: Int => Int
fact = n => n !> 1 | 1 | n * fact(n - 1)

f5 = #! fact(5)
p = IntPair(10, f5)
io::out $ (p.first, p.second)   // (10, 120)

14.2 Type properties and compile-time intrinsics

Given T: Type, the compiler resolves the following properties as compile-time constants:

Expression Result Type Specification
#size T Int Target ABI byte size of T
#align T Int Target ABI byte alignment of T
T.name String Declared #type alias name (if nominal) or canonical type name ("Int", "String", etc.)
T.zero T Default/zero-initialized value of T (respects field: Type = default on records)
?T Int Field count for records, lane count N for (E * N), variant count for enums, 0 for ()
T.(i) String Variant name of the i-th enum variant (0 <= i < ?T; i must be compile-time constant, E3170)
#use io

#type ServerCfg (host: String = "127.0.0.1", port: Int = 8080, tls: <>)
#type Mode < Fast, Safe >

cfg = ServerCfg.zero
io::out $ (ServerCfg.name, ?ServerCfg, cfg.host, cfg.port, Mode.(0), #size Int, #align Int)

14.3 Structural type-shape guards (T [...])

When the scrutinee of a choice is T: Type, the compiler resolves the matching branch at compile time, prunes all untaken branches before type-checking/codegen, and binds inner component types in the taken branch scope:

Guard Pattern Matched Type Shape Bound Type Variables in Branch Scope
[Int, i32] Exact type equality (comma OR-list) —
[type::is_numeric] Compile-time Type => <> predicate (#use type) —
[()] Unit (()) —
[<>] or [<|>] Flag (<>) —
[<P|>] RamPN option (< P | >) P: positive payload Type
[<|N>] RamNP fallible (< | N >) N: negative payload Type
[<P | N>] RamPP result (< P | N >) P: positive Type, N: negative Type
[E{}] Growable array (E{}) E: element Type
[E[]] Slice (E[]) E: element Type
[(E * ..)] Any homogeneous tuple ((E * N)) E: element Type (?T gives length N)
[(E * 4)] Homogeneous tuple of exact length 4 E: element Type
[K ^ V] / [String ^ V] Hash map (K ^ V) K: key Type, V: value Type
[(..)] Any record type — (unroll fields with T @ f =>)
[<..>] Any nominal enum type — (unroll variants with T @ v =>)

14.4 Compile-time record field (T @ f =>) and enum variant (T @ v =>) unrolling

Inside a #! generic function, iterating over a record Type ([(..)]) or enum Type ([<..>]) with @ unrolls the loop body at compile time across 0..?T:

Cursor Expression Record Field Cursor (T @ f =>) Enum Variant Cursor (T @ v =>)
cursor.idx Int: 0-based field index Int: 0-based variant tag index
cursor.name String: field name (or "0", "1" for tuples) String: declared variant label ("Red", etc.)
cursor.type Type: static type of the field (FT = f.type) Type: payload type, or () for unit variants
f.default < f.type | >: <+ default_val +> if declared, else <--> —
val.(cursor) Reads field f from runtime record val (type f.type) Returns < v.type | > (<+ payload +> / <++> if active tag is v.idx, else <-->)
val'.(f) = expr Writes expr to field f of val' (tracks definite init) —
T(v) / T(v, p) — Constructs runtime enum T for variant v
#use io
#use type

#type Point (x: Int = 1, y: Int = 2)
#type Shape < Dot, Circle(Int), Box(Int) >

inspect_record: #! T: Type, T => String
inspect_record = #! T, rec =>
   T
      [(..)]
         b = {64}
         b += T.name + "("
         T @ f =>
            f.idx > 0 | b += ","
            b += f.name + "=" + "${rec.(f)}"
         b += ")"
         +b
      | ""

enum_tag_name: #! T: Type, T => String
enum_tag_name = #! T, val =>
   T
      [<..>]
         b = {32}
         T @ v =>
            val.(v) | p =>
               b += v.name
         +b
      | ""

p = Point(10, 20)
s = Shape(Circle, 5)
io::out $ (inspect_record(p), enum_tag_name(s))