Built-In Types¶
Accepted
Accepted for the V1 primitive type inventory, built-in type forms, comptime numeric types, primitive values, and explicit V1/V2 boundaries.
This page records the V1 compiler-recognized type vocabulary. It separates primitive built-in types from built-in type forms and from prelude or standard-library declarations.
Prelude declarations such as Box(T), Range(T), RangeInclusive(T), Allocator, Namespace, and Ordering are not primitive types. The primitive numeric factory declarations named below are the exception: they are prelude declarations whose bodies call public compiler primitive-type-construction intrinsics and return primitive numeric Type values. Standard-library declarations such as std.Result(T, E) and std.text.String are outside the built-in type set.
Primitive Types¶
V1 has target-independent arbitrary-width integer types:
i1..i128
u1..u128
isize
usize
The canonical source-level constructors for integer primitive type identities are prelude declarations:
fn SignedInteger(comptime bits: comptime_int) Type
fn UnsignedInteger(comptime bits: comptime_int) Type
These functions are prelude declarations whose bodies call public compiler primitive-type-construction intrinsics. The compiler validates supported widths, constructs the canonical primitive Type identity, and owns layout, ABI, literal typing, arithmetic lowering, and target behavior. The factories take comptime_int so the prelude can define primitive integer aliases before aliases such as u8 exist. The prelude owns the public factory names and aliases:
const i32 = SignedInteger(32)
const u64 = UnsignedInteger(64)
isize and usize are target-selected aliases for SignedInteger(pointer width) and UnsignedInteger(pointer width).
The alias and factory spellings name the same type identity. i32 is not a distinct wrapper around SignedInteger(32).
All iN and uN widths in the inclusive range 1..128 are valid type identities, including non-power-of-two widths such as i13 and u24. Signed integers use two's-complement representation. For example, i1 has values -1 and 0, while u1 has values 0 and 1.
Runtime storage or code generation for non-standard integer widths is target-gated. Standard widths i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, and usize are always part of the V1 runtime baseline. Other widths such as u1, i1, u7, i13, or u65 require the selected target/backend to report bit-precise integer support. If the target cannot lower a required non-standard width, the compiler rejects that runtime use with a target-support error instead of synthesizing helper structs as part of V1.
The V1 C backend should lower non-standard widths through C23 _BitInt(N) / unsigned _BitInt(N) when the selected C compiler and target support it. If _BitInt support is missing or does not cover the requested width, the C backend reports the target-support error. The V1 C backend is not required to implement a portable helper-library lowering for arbitrary widths.
Primitive arithmetic operator availability and result types are defined by the prelude arithmetic contracts. Base V1 arithmetic operators do not silently wrap unsigned results and do not synthesize wider mathematical-range result types. Same-type primitive integer arithmetic preserves the operand type; mixed concrete integer arithmetic is accepted only when one operand type can represent every value of the other, and the result is that existing wider operand type.
Overflow, underflow, divide-by-zero, and signed minimum negation cases that remain after result selection are checked in Checked safety mode and trap on failure. In Unchecked safety mode, programs must not rely on operations whose required check was omitted. Explicit wrapping, checked-overflow, saturating, Euclidean division, and Euclidean modulo APIs are deferred. Safety modes and build-profile presets are defined in Target and Safety Modes.
Numeric coercion and explicit conversion rules are documented in Numeric Types.
V1 has these runtime floating-point types:
f32
f64
The canonical source-level constructor for floating-point primitive type identities is a prelude declaration:
fn Float(comptime bits: comptime_int) Type
Float(bits) calls compiler primitive type construction in the same way as the integer factories. V1 accepts Float(32) and Float(64):
const f32 = Float(32)
const f64 = Float(64)
The alias and factory spellings name the same type identity.
f16, f80, and f128 are deferred to V2.
Boolean values use a distinct primitive type:
bool
bool is not an alias for u1, does not support integer arithmetic, and does not implicitly coerce to or from integer types.
void is a zero-size singleton type with exactly one value, also spelled void. void is a normal sized type and may appear in storage, arrays, optionals, and generic code.
never is the bottom type for expressions that cannot complete normally. It has no values and may coerce to any expected type. V1 allows never in expression, return, and type-reflection positions, but rejects or defers storage forms such as var x: never, [4]never, ?never, and Box(never).
opaque is an unsized erased pointee type. It cannot be stored inline, passed by value, returned by value, or directly dereferenced. It is valid behind indirection such as *opaque and *const opaque. Pointer erasure to opaque and typed recovery from opaque are documented in Reference and Mutability.
Type is the compiler-known comptime type of type and reflection values.
Error is the top error-set Type value. error { ... } constructs a closed error-set type value:
const ReadError = error {
FileNotFound,
PermissionDenied,
}
fn read(path: Path) []u8!Error
When explanation needs an annotation, use Type:
const ReadError: Type = error { FileNotFound }
Enum type expressions construct closed tag-only enum type values:
const Ordering = enum {
less,
equal,
greater,
}
Plain enums and enum unions are language-defined type forms, not primitive scalar types. See Enums.
Comptime Numeric Types¶
Integer literals and Unicode code point literals have type comptime_int until context resolves them to a concrete integer type:
123
0xff
'a'
'\n'
'\u{1f4a9}'
'😀'
A Unicode code point literal denotes one Unicode scalar value, not a UTF-8 byte sequence. It coerces to an integer type only when representable.
comptime_int is an exact but bounded comptime-only integer type in V1. Portable V1 code may rely on at least the signed 256-bit range -(2^255) through 2^255 - 1. A compiler may support a larger deterministic range, but a comptime integer expression that exceeds the compiler-supported range is a compile error. comptime_int arithmetic uses the same integer division and remainder semantics as concrete primitive integers; division or remainder by zero is a compile error during comptime evaluation.
Floating-point literals have type comptime_float until context resolves them to a concrete float type. V1 comptime_float uses target-independent IEEE binary64 semantics for portable behavior. Resolving a comptime_float value to a concrete floating-point type may round to the destination format when the finite value is in range; finite overflow is a compile error.
Primitive numeric suffixes give numeric literals an explicit concrete type:
15u8
48_000usize
1.0f32
Suffixes do not bypass representability checks. Base-prefixed integers and primitive numeric suffix source spelling are documented in Literals.
comptime_int and comptime_float are valid only for compile-time-known values, explicit comptime parameters, and comptime constants. They are not runtime storage, field, array element, ordinary parameter, or return types.
V1 has no default integer literal type. A literal used for runtime storage must resolve from context or an explicit annotation.
Primitive Values¶
true and false are the two values of type bool.
void is the sole value of type void and may infer its type:
var x = void
null is the optional absence value. It has no standalone runtime type and requires optional context:
var x: ?i32 = null
Context-free null is invalid:
var y = null // error
undefined is a context-only primitive value form for unspecified storage:
var x: i32 = undefined
Context-free undefined is invalid:
var y = undefined // error
Reading an undefined value before assignment is illegal behavior and should be diagnosed when statically obvious. undefined is not valid for comptime_int, comptime_float, Type, error, or never.
String literals have type []const u8 in V1. Direct non-ASCII source text contributes its UTF-8 bytes unchanged, and \u{NNNNNN} contributes the UTF-8 encoding of one Unicode scalar value. \xNN contributes one arbitrary byte, so string literals may contain byte sequences that are not valid UTF-8. Catalyst has no primitive string, str, or String type. Owned text belongs in std.text.
Built-In Type Forms¶
Pointers are built-in type forms:
*T
*const T
*opaque
*const opaque
*dyn C
*const dyn C
Pointer types are sized runtime types. Pointer mutability is shallow and part of the pointer type. Pointers do not own pointee storage. Full address-of, dereference, field, slice, and mutability semantics are documented in Reference and Mutability.
Fixed arrays and slices are built-in type forms:
[N]T
[]T
[]const T
[N]T is a fixed-size array type with N compile-time-known elements of type T. []T and []const T are slice view types. Full literal, coercion, indexing, slicing, and layout rules are documented in Arrays, Slices, Ranges, and Indexing.
Optional types use the built-in form:
?T
?T represents absence or a T payload. Full optional typing, presence coercion, binding, and ownership rules are documented in Optional Types and Optional Bindings.
Function signatures are built-in type forms:
fn(...) Return
*const fn(...) Return
fn(...) Return is the compile-time-known function item type. *const fn(...) Return is the runtime function pointer type. Function declaration syntax, function type equality, signature compatibility, reflection, and callable-value scope are documented in Functions and Function Reflection.
Error-return types are built-in type forms:
T!E
T!
They represent a fallible success type T with explicit or inferred error-set information. Full try, catch, inference, and error compatibility rules are documented in Errors.
Dynamic contract objects use the built-in form:
dyn C
dyn C is an unsized erased type for a dyn-safe semantic contract application C. It must be used behind indirection or ownership such as *dyn C, *const dyn C, or Box(dyn C). Full dispatch, dyn-safety, vtable, ownership, and metadata rules are documented in Contract Dispatch and Ownership.
Excluded from Built-Ins¶
Range syntax is compiler syntax, but Range(T) and RangeInclusive(T) are prelude value types.
Namespace, module(...), and include(...) are prelude/module-loading surface, not primitive type names.
Runtime callable value equality is not a V1 built-in conformance. Function signature equality exists for reflection through Type.Function.equals.