Types
Sec is statically typed. Every expression that survives semantic analysis has a resolved type, and that type has language-defined meaning independent of its machine representation.
The type system covers ordinary scalar values, exact decimal arithmetic, temporal values, references, sequences, collections, shaped data, hardware layouts, generic types, semantic units and user-defined nominal types.
Type Principles
Sec's type system follows several language-wide principles:
- types are resolved statically;
- named types preserve nominal identity;
- untyped literals may be shaped by context;
- already typed values are not broadly converted implicitly;
- checked conversions are explicit;
- type semantics are independent of LLVM, MLIR and ABI layout;
- ownership behavior is a separate semantic property of the resolved type.
type CustomerID uint64
type ProductID uint64
let customer: CustomerID := 42
let product: ProductID := customer // invalid
Both named types may eventually use the same machine representation, but that representation does not make them interchangeable.
Type Categories
The central type model includes these broad categories:
| Category | Examples |
|---|---|
| Special | any, void |
| Logical and text scalars | bool, byte, char, rune, string |
| Signed integers | int, int8 ... int256 |
| Unsigned integers | uint, uint8 ... uint256 |
| Binary floating point | float, float32, float64 |
| Exact decimal | decimal, decimal128 |
| Temporal | date, time, datetime, duration |
| Fixed sequences | T[N] |
| Owning sequences | T[] |
| Borrowed slices | ref T[], ref mut T[] |
| Safe references | ref T, ref mut T |
| Raw addresses | RawPtr[T] |
| Function values | fn(T) R, mut fn() R, -> fn() R |
| Collections | list, map, set |
| Shaped types | vector, matrix, tensor, tensor_view |
| Hardware layout | bit, bit[N], register[N] |
| Core nominal generics | Result[T, E], Option[T], Task[T], Thread[T] |
| User-defined nominal types | named types, structs, enums, unions, interfaces, units and registers |
This page introduces the families. Their complete behavior is defined in the specialized manual chapters.
Builtin and Compiler-Known Types
Fundamental language types and first-class compiler-known type constructors use lowercase names and require no import.
any
bool
byte
char
error
rune
string
void
int
int8
int16
int32
int64
int128
int256
uint
uint8
uint16
uint32
uint64
uint128
uint256
float
float32
float64
decimal
decimal128
date
time
datetime
duration
bit
register
list
map
set
vector
matrix
tensor
tensor_view
Some compiler-known nominal types retain uppercase names:
Result[T, E]
Option[T]
Iterator[T]
Task[T]
Thread[T]
Shape[Rank]
Strides[Rank]
TensorLayout[Rank]
MemorySpace
RawPtr[T]
Compiler knowledge does not make these types structurally interchangeable. Their identity and semantics are still defined by their owning type rules.
Special Types
void
void represents the absence of a returned value:
fn Notify(message: string) void {
fmt.Println(message)
}
void is not an ordinary storable value type. Variables and
stored fields cannot have type void.
Specific compiler-known generic forms may use it where their own rules permit:
Result[void, IOError]
RawPtr[void]
any
any is Sec's compiler-known general-value type. It does not
turn Sec into a dynamically typed language.
Using any does not permit unchecked implicit conversion,
unrestricted member access, hidden reflection or arbitrary runtime type coercion.
Narrowing a value from any requires defined type-safe semantics.
error
error is the compiler-known root of Sec's typed error system.
It is not a general object base class.
enum IOError error {
OpenError
ReadError
}
The detailed error type model is described in the Error Handling chapters.
Logical and Text Types
bool
bool has exactly two logical values:
true
false
Conditions require a boolean expression:
if count > 0 {
Process()
}
Numeric values do not implicitly become boolean values:
if count { // invalid
}
Numeric-to-boolean conversion is explicit and infallible:
let ready := bool(count)
Zero converts to false; non-zero converts to true.
byte
byte is the compiler-known byte scalar type. It is intended
for raw byte-oriented data such as binary buffers, protocols and FFI.
let marker: byte := 0x7F
Equal storage width does not automatically make byte identical
to another fixed-width integer type for every semantic or ABI purpose.
char
char is Sec's character scalar type:
let ch := 'A'
let newline := '\n'
The numeric family suffix for char is t:
let zero := 0t
let a := 65t
let hexA := 0x41t
rune
rune is Sec's Unicode scalar type:
let omega := 0x03A9r
let face := 0x1F600r
let r: rune := 'A'
char and rune are distinct types.
Conversion between them follows explicit conversion and representability rules.
string
string is Sec's compiler-known string type:
let name := "Sec"
let message := $"Hello {name}"
A string is not an implicit array of char, rune or
byte. Conversion and traversal use explicit string/core operations.
Array slicing syntax does not apply directly to string.
Checked substring extraction belongs to the public string API.
Integer Types
Sec provides signed and unsigned integer families.
Signed integers
int
int8
int16
int32
int64
int128
int256
Unsigned integers
uint
uint8
uint16
uint32
uint64
uint128
uint256
Explicit-width integer types have the width stated by the type name. Use these types when storage size, binary layout, protocol representation, interoperability, or another exact-width requirement is part of the program's semantics.
Target-sized integers
int and uint are target-sized integer types.
let index: int := 0
let size: uint := 4096
Their physical width is selected by the target profile. Code that requires an exact external or ABI width should use an explicit-width type:
int32
uint64
int128
int and uint, but it does not change their signedness
or ordinary language semantics.
Binary Floating-Point Types
Sec provides:
float
float32
float64
float32 and float64 request explicit binary
floating-point widths. float is the general binary
floating-point family type.
let small: float32 := 1.5
let wide: float64 := 1.5
let general := 1.5g
The g suffix selects the binary floating-point family.
It does not select a fixed width.
Use an explicit width when layout, an external data format or an ABI requires one.
Exact Decimal Types
Sec provides two exact base-10 decimal types:
decimal
decimal128
An unsuffixed fractional or exponent literal defaults to decimal
when no stronger context selects another compatible type:
let value := 3.14
let scientific := 1e3
Both values infer as exact decimal values.
The m suffix explicitly selects the exact decimal family:
let whole := 10m
let exact := 3.14m
let scientific := 1.25e-3m
let wide: decimal128 := 8m
Exact decimal literals are interpreted from their source spelling. They are not first converted through binary floating point.
decimal32 and decimal64 are not canonical Sec types.
Temporal Types
Sec provides four compiler-known temporal value types:
date
time
datetime
duration
| Type | Meaning |
|---|---|
date | A calendar date |
time | A time of day |
datetime | A combined date and time value |
duration | An elapsed amount of time |
These are first-class Sec types, not aliases for strings or integers. They do not implicitly convert to or from numeric types.
Detailed calendar, timezone, precision, parsing, formatting and arithmetic rules belong to the temporal type rules rather than being inferred from their names.
Literal Typing
Literal tokens preserve their source information until semantic context resolves their type.
Default inference
| Literal form | Default type |
|---|---|
10 | int |
3.14 | decimal |
1e3 | decimal |
'A' | char |
"Sec" | string |
true | bool |
Numeric family suffixes
i signed integer family
u unsigned integer family
g binary floating-point family
m exact decimal family
t char
r rune
Examples:
8i
8u
8g
8m
65t
65r
Family suffixes normally select a scalar family, not a fixed width. Context may still select a concrete width:
let small: int8 := 8i
let wide: int256 := 8i
let smallFloat: float32 := 8g
let wideFloat: float64 := 8g
let exact: decimal128 := 8m
Context shaping
An untyped literal may be shaped directly by its target context:
let small: int8 := 10
let wide: uint256 := 10
let ratio: float64 := 3.14
let exact: decimal128 := 3.14
let percent: Percent := 50
Context shaping is not the same operation as converting an already typed runtime value.
Variables and Type Inference
Variables are immutable by default:
let a := 9
let name := "Sec"
An explicit type may be supplied:
let a: int := 9
let ratio: float64 := 3.14
Mutability is explicit:
let mut counter := 0
counter += 1
A mutable declaration may omit the initializer only when the declared type is defaultable:
let mut value: int
An immutable declaration requires an initializer:
let value: int // invalid
Multiple declarators
let a := 9, b := "hello", c := true
Each declarator is checked and inferred independently.
Type-first declarations
int mut: x
int mut: a, b, c
int: minimum := 0, maximum := 100
Sec also supports immutable parenthesized type-first declaration groups:
TokenType (
ILLEGAL := "ILLEGAL",
EOF := "EOF",
IDENT := "IDENT",
)
This is a declaration group, not construction of a TokenType value.
Type Identity
Type identity is stronger than representation identity.
Named types
type Percent int range 0..100
type Age int range 0..130
Percent, Age and int are different types.
Generic types
list[int]
list[string]
Pair[int, string]
Pair[string, int]
Generic arguments participate in concrete type identity.
Arrays
int[4]
int[5]
Array length participates in type identity.
References
ref Buffer
ref mut Buffer
Reference mode participates in type identity.
Function values
fn(int) bool
fn(int) int
fn(ref Buffer) void
fn(ref mut Buffer) void
mut fn() int
-> fn() Resource
Parameter types, ownership modes, callable capability and return type all participate in callable type identity.
Conversions
Sec does not broadly convert already typed values implicitly.
type Percent int range 0..100
let raw: int := 90
let value: Percent := raw // invalid
The general explicit conversion syntax is:
TargetType(value)
Examples:
int32(value)
decimal(value)
bool(value)
char(value)
rune(value)
Percent(value)
Conversions are checked. They must not silently truncate, wrap, lose precision, violate a contract or construct an invalid scalar value.
A conversion that may fail requires try:
let small := try int32(largeValue)
let r := try rune(codePoint)
let p := try Percent(raw)
When compile-time information proves the conversion valid, the compiler may eliminate the runtime check. The source-level semantics remain checked.
Defaultability
Every type is classified as either:
Defaultable
NonDefaultable
A defaultable type has one compiler-resolved valid default value. A non-defaultable type requires explicit initialization in any context that would otherwise need a default.
int mut: value
This is valid because int is defaultable.
Named types, constrained types, structs, unions, generic instances and other compound types determine defaultability from their own semantic rules.
Arrays, Owning Sequences and Slices
Sec uses postfix sequence syntax.
Fixed array
T[N]
Examples:
int[4]
byte[256]
The array length is part of the type.
Owning unsized sequence
T[]
Example:
Packet[]
Borrowed slice
ref T[]
ref mut T[]
Examples:
ref byte[]
ref mut int[]
A slice is a non-owning reference to sequence storage. Array, sequence, slice, indexing and bounds behavior are described in the collection chapters.
References and Raw Pointers
Safe references
ref T
ref mut T
ref T provides shared access.
ref mut T provides exclusive mutable access.
Safe references do not own the referenced value. Ordinary Sec source does not expose lifetime parameters.
Raw pointers
RawPtr[T]
Examples:
let address: RawPtr[byte]
let opaque: RawPtr[void]
RawPtr[T] is an unchecked low-level address. It is not a safe
reference, does not imply ownership and does not extend lifetime.
Function Types
Functions and lambdas may exist as typed values.
fn(int) bool
fn(int, int) int
fn(string) void
Parameter names are not part of function-type identity.
Callable capability and parameter ownership are part of the type model:
fn() int
mut fn() int
-> fn() Resource
fn(ref Buffer) void
fn(ref mut Buffer) void
The detailed callable, lambda and closure model is described in the function chapters.
First-Class Collections
Sec has compiler-known collection type constructors:
list[T]
list[T, Capacity]
map[K, V]
map[K, V, Capacity]
set[T]
set[T, Capacity]
They require no import and are not aliases for standard-library generic types. Compiler knowledge allows collection semantics to participate directly in allocation, ownership, indexing, analysis and lowering.
More specialized containers remain normal nominal library types.
First-Class Shaped Types
Sec includes compiler-known shaped type constructors:
vector[T, N]
matrix[T, Rows, Columns]
tensor[T, Dimensions...]
tensor[T, Shape[Rank]]
tensor_view[T, Rank]
Examples:
vector[float64, 3]
matrix[float32, 4, 4]
tensor[float32, 3, 224, 224]
tensor_view[float32, 3]
Compile-time-known and runtime-shaped tensors are distinct forms.
tensor_view is a non-owning view type form rather than an owning tensor.
Safe source-level views use reference syntax:
ref tensor_view[float32, 3]
ref mut tensor_view[float32, 3]
Shape, rank, stride, layout and memory-space semantics are described in the shaped-types chapter and rulebook.
Hardware Layout Types
Sec provides compiler-known hardware-oriented forms:
bit
bit[N]
register[N]
Example:
type Control register[8] {
Enabled: bit
Mode: bit[3]
_: bit[4]
}
These are language-level layout forms, not ordinary standard-library generics. Their widths and field placement participate in compile-time validation.
Unit-Bearing Numeric Types
A unit is semantic quantity identity. It is not itself a numeric representation. A unit-bearing numeric type combines a numeric carrier with a unit descriptor.
unit Meter physical
unit Second physical
unit Item uint other
let exact: decimal<Meter> := 10
let measured: float64<Meter> := 10
let count: <Item> := 4
A unit declaration may define a default carrier, but that carrier is not part of unit identity. The same unit may therefore appear with more than one suitable numeric carrier where the unit rules permit it.
Compound unit-only expressions such as <m/s> default to
decimal.
Result and Option
Result[T, E] and Option[T] are nominal core generic types.
Result[Data, IOError]
Option[User]
Result[T, E] is the foundation of explicit typed error handling.
Its error type must be error or a concrete Sec error type.
Result[Data, IOError] // valid
Result[Data, error] // valid
Result[Data, string] // invalid
Option[T] represents explicit optionality.
Safe references themselves are non-null, so optional reference semantics use
Option rather than nullable ref.
Types and Ownership
Type compatibility does not by itself determine whether a value is copied, moved or borrowed.
The compiler separately classifies semantic operations such as:
- copy;
- move;
- borrow;
- mutation;
- destruction;
- SSA reuse where no source-level transfer occurs.
Copyability, move-only behavior, destruction requirements and borrow rules are semantic properties of the resolved type.
Must-use and discardability
A type may also carry separate rules about whether produced values must be used and whether explicit terminal discard is permitted.
Result[int, IOError]
is must-use, while explicit discard may still be legal under the discard rules. These properties are distinct from copyability and physical representation.
Summary
- Sec is statically typed.
- Every semantically valid expression has a resolved type.
- Representation does not define type identity.
- Named types are nominal and remain distinct from their underlying types.
- Untyped literals may be shaped by context.
- Already typed values do not broadly convert implicitly.
- Checked conversions use
TargetType(value)and requiretrywhen failure is possible. intanduintare target-sized; fixed-width integer types extend through 256 bits.- Unsuffixed fractional and exponent literals default to exact
decimal. decimal128is the wider exact decimal type.date,time,datetimeanddurationare first-class temporal types.- Arrays and sequences use postfix
T[N]andT[]syntax. - Safe references use
ref Tandref mut T. RawPtr[T]is an unchecked non-owning address type.- Functions, collections, shaped data and hardware layouts are part of the type system.
- Units add semantic quantity identity independently of numeric representation.
ResultandOptionare nominal core generic types.- Ownership, copying, moving and destruction are semantic properties associated with resolved types.