Introduction

Sec is a statically typed, compiled programming language for native software. It is designed to cover ordinary application development and low-level systems programming with one coherent language model.

The central design idea is simple: the programmer should express semantic intent, while the compiler performs the bookkeeping and analysis required to prove that the program is valid. Sec deliberately accepts a sophisticated compiler in order to keep ordinary source code understandable.

Sec 0.1: This manual describes the language model. Compiler implementation progress is tracked separately in Implementation Status.

What Sec Is

Sec is designed for predictable native programs without requiring garbage collection, hidden reference counting, implicit exceptions, or a mandatory general-purpose runtime.

It combines high-level language features with low-level control. A program may use nominal types, contracts, units, generics, tagged unions, interfaces, properties, collections and structured error handling while still targeting operating systems, embedded systems, microcontrollers and bare-metal hardware.

Sec is influenced by many language traditions, including C, C++, C#, F#, Go, Rust, Zig, Ada, Vale and Odin. Those languages are sources of experience, not normative definitions for Sec. Familiar syntax may therefore have different semantics when Sec's own consistency or safety model requires it.

Intent and Proof

Sec tries to make syntax explicit when the programmer is making a real semantic choice, and implicit when the compiler can safely derive the bookkeeping.

Source code is expected to make choices such as these visible:

  • mutability;
  • fallible operations;
  • unsafe operations;
  • borrowing mode;
  • ownership transfer from reusable values at consuming boundaries;
  • external storage and hardware semantics;
  • units and contracts that carry domain meaning.

By contrast, Sec avoids requiring annotations whose only purpose is to repeat information the compiler already knows or can prove.

Design principle: Explicit intent, implicit bookkeeping.

The same principle applies to safety checks. Sec prefers to prove a condition at compile time. When proof is impossible but the language guarantee can be checked at runtime, the compiler may emit a check. When neither proof nor checking can provide the required guarantee, the program is rejected.

prove when possible
check when necessary
reject when neither can provide the required guarantee

Type System

Type identity in Sec is semantic, not merely representational. Two values may use the same machine representation and still be different types.

type CustomerID uint64
type ProductID uint64

let customer: CustomerID := 42
let product: ProductID := customer    // invalid

Named types are nominal. The compiler does not implicitly convert a typed runtime value merely because another type has a compatible underlying representation.

Contracts belong to types

Contracts describe valid values of a named type rather than ad-hoc restrictions on individual variables.

type Percent int range 0..100

let complete: Percent := 100
let invalid: Percent := 101            // compile-time error

Untyped literals may take their type from context. Converting an already typed runtime value is a different operation and is explicit.

let raw: int := ReadPercentage()
let value := try Percent(raw)

A checked conversion that the compiler can prove valid does not need a runtime check. The language semantics remain checked either way.

Units are semantic identity

Units are independent of their numeric carrier. The same unit may be represented by an exact decimal, a floating-point value or an integer where the declaration permits it.

let exactDistance: decimal<m> := 10
let measuredDistance: float64<m> := 10
let counterDistance: int64<m> := 10

Unit algebra is checked semantically. Numeric representation does not define unit meaning, and unit compatibility does not erase named-type identity.

Parameterized types

Generic arguments and compile-time shape information participate in type identity.

list[int]
Result[Data, IOError]

int[4]
byte[256]

vector[float64, 3]
matrix[float32, 4, 4]

Fixed arrays and dynamic owning sequences use postfix sequence syntax: T[N] and T[]. Borrowed sequence views use ref T[] or ref mut T[].

Error Handling

Errors are explicit typed values. Sec does not use hidden exception propagation as its ordinary error model.

The compiler-known lowercase type error is the common root of Sec error types. It is specific to the error system; it is not a general object base class and does not introduce ordinary inheritance.

enum IOError error {
    OpenError
    ReadError
    WriteError
}

A precise operation should normally expose its concrete error type:

fn Read() Result[Data, IOError]

An API that intentionally forwards heterogeneous Sec errors may instead use the open error channel:

fn RunPipeline() Result[Data, error]

Result[T, E] is must-use. Failure cannot silently disappear. try unwraps successful values and either handles or propagates the failure according to the surrounding context.

fn Load() Result[Config, IOError] {
    let data := try Read()
    let config := try Parse(data) {
        Err(error) => return Err(error)
    }

    return Ok(config)
}

match is the general exhaustive pattern-matching construct. try is the concise path when successful execution should continue normally and only failure needs special handling.

Memory Model

Sec uses deterministic ownership and compile-time analysis as the foundation for memory and resource safety. The language separates several concepts that are often conflated in lower-level programming:

  • a value;
  • a materialized object;
  • a binding;
  • a Place that identifies a usable program location;
  • storage and storage lifetime;
  • ownership responsibility;
  • borrowing authority;
  • reference validity and provenance;
  • destruction and storage reclamation.

Ownership is semantic responsibility, not possession of an address. Moving a value transfers ownership; it does not inherently mean that bytes are relocated. Borrowing grants temporary authority without transferring ownership.

fn Inspect(value: ref Buffer) void {
    // shared access
}

fn Modify(value: ref mut Buffer) void {
    // exclusive mutable access
}

Safe references do not own their target. ref T provides shared access and ref mut T provides exclusive mutable access. Ordinary Sec source does not expose lifetime parameters.

Allocation, initialization, destruction and storage reclamation are separate operations. In particular, allocation provides storage; it does not by itself create a valid object. Destruction ends lifecycle responsibility; reclamation concerns storage.

Rule: Unsafe code transfers specific proof obligations to the programmer. It does not disable type checking, ownership, error handling, visibility, control-flow validation or other Sec semantics.

Abstraction Without Losing Control

Sec does not treat high-level abstraction and low-level programming as opposites. A source-level abstraction is useful when it removes repetitive error-prone work while preserving semantically important behavior.

Language feature What it replaces or makes explicit
Register fields Manual masks, shifts and repeated bit-layout arithmetic
Units Unchecked numeric conventions for domain quantities
Result and try Hidden error channels and implicit exception propagation
Properties Repeated controlled-access and validation plumbing
Safe references Ordinary unchecked raw-pointer access
Deterministic destruction Repeated manual cleanup on every control-flow path

Behavior lives in impl

Stored representation belongs to the type declaration. Behavior and associated definitions belong to impl.

type Counter struct {
    value: int,
}

impl Counter {
    fn Increment() void {
        self.value += 1
    }

    fn Value() int {
        return self.value
    }
}

Instance methods receive compiler-provided implicit self. Canonical Sec methods do not declare ref self or ref mut self in their parameter lists.

A type has one primary implementation entry point. Additional same-module behavior may be organized in impl extends Type fragments. All fragments form one combined member surface.

impl extends Counter {
    fn IsZero() bool {
        return self.value == 0
    }
}

Explicit lifecycle construction

new selects lifecycle construction through init. It does not mean heap allocation.

impl Connection {
    init(address: Address) ConnectError {
        // establish a valid Connection
    }
}

let connection := try new Connection(address)

Conversion and lifecycle construction are separate operations: Type(value) is conversion; new Type(args...) is construction.

One Language Across Targets

Sec is intended for both hosted and freestanding environments. Core language semantics remain the same across desktop and server operating systems, embedded operating systems, microcontrollers and bare-metal targets.

A target profile may legitimately lack facilities such as heap allocation, threads, operating-system services, a particular ABI feature or runtime support. That may restrict which programs or libraries are available, but it must not silently redefine the meaning of the language.

The language avoids requiring a runtime whenever practical. Runtime validation does not automatically imply a general-purpose runtime; a check may lower directly to ordinary machine instructions or target-specific support.

Design principle: Target differences may change available facilities, not the meaning of core Sec constructs.

Compiler Responsibility

Sec intentionally places substantial responsibility in the compiler. The programmer should not have to reproduce compiler-internal reasoning in source code.

The compiler model includes analyses such as:

  • type and contract analysis;
  • ownership, copy and move analysis;
  • borrowing, lifetime and reference validation;
  • escape and closure analysis;
  • effect and control-flow analysis;
  • definite assignment;
  • destruction and cleanup planning;
  • stack and recursion analysis;
  • concurrency, data-race and deadlock analysis;
  • ISR and platform validation;
  • compile-time evaluation;
  • unit analysis;
  • ABI and FFI validation;
  • optimization-legality analysis.

Diagnostics are part of that model. A compiler error should explain not only that a program is invalid, but which rule was violated, where the relevant values or declarations came from, why the conflict exists and, when useful, how it can be resolved.

Semantic analysis establishes Sec semantics. Later compiler stages preserve and lower those facts; the backend does not invent ownership, unit, lifetime, allocation or safety meaning from machine representation.

Example Program

This small example combines nominal typing, contracts, a struct, implicit self, typed errors and checked conversion.

module demo

import "fmt"

type Percent int range 0..100

enum InputError error {
    InvalidCompletion
}

type User struct {
    Name: string,
    Completion: Percent,
}

impl User {
    fn IsComplete() bool {
        return self.Completion == Percent(100)
    }
}

fn MakeUser(name: string, rawCompletion: int) Result[User, InputError] {
    let completion := try Percent(rawCompletion) {
        Err(_) => return Err(InputError.InvalidCompletion)
    }

    return Ok(User {
        Name: name,
        Completion: completion,
    })
}

fn main() Result[void, error] {
    let user := try MakeUser("Anna", 90)

    if user.IsComplete() {
        fmt.Println($"{user.Name}: complete")
    } else {
        fmt.Println($"{user.Name}: {user.Completion}")
    }

    return Ok()
}

Percent is a distinct named type whose contract belongs to the type. The runtime integer is converted explicitly because the conversion may fail. MakeUser exposes a precise error channel, while main deliberately accepts the open error channel and can propagate the concrete error without erasing its identity.

Language Status

Sec is currently being specified and implemented as version 0.1. The language manual describes the intended programmer-facing semantics. The normative rulebooks define the detailed language and compiler contracts, while implementation progress is tracked separately.

This separation matters. A feature may be fully specified before every compiler stage or target backend has implemented it.

Use the Implementation Status page when you need to know what the current compiler can execute end-to-end.

Non-Goals

Sec deliberately avoids several common language directions:

  • no general class inheritance hierarchy;
  • no ordinary hidden exception mechanism;
  • no mandatory garbage collector;
  • no hidden reference counting requirement;
  • no automatic heap promotion merely to repair escaping values;
  • no implicit expensive copying or resource acquisition;
  • no assumption that new means heap allocation;
  • no null safe references;
  • no programmer-written lifetime parameters in ordinary source code;
  • no globally implicit interface conformance;
  • no backend-dependent reinterpretation of source-language safety rules.

The goal is not minimal language power. The goal is a language whose important semantics remain visible, predictable and explainable.