Type System

The C! type system is the foundation of its security guarantees. It combines linear types, affine types, refined types, and sum types to eliminate entire classes of bugs at compile time.

Linear Types

A linear type must be used exactly once. This is the core mechanism that prevents double-spend, use-after-free, and resource leaks. When a value of a linear type is consumed (passed to a function, moved into a structure), it cannot be used again.

linear_example.cb
// Token is a linear type — must be used exactly once
fn transfer(token: own Token, to: Address) -> Receipt {
    ledger.move(token, to)  // token consumed here
}

fn example() {
    let t = mint_token();
    transfer(t, alice);  // OK: t is consumed
    transfer(t, bob);    // COMPILE ERROR: t already consumed
}

The compiler tracks ownership of every linear value through every code path, including branches and loops. Forgetting to use a linear value is also a compile error, preventing resource leaks.

Affine Types

An affine type can be used at most once. Unlike linear types, it is safe to drop an affine value without using it. This is useful for optional cleanup or cancellation tokens.

affine_example.cb
// CancelToken is affine — use at most once
fn start_task() -> borrow CancelToken {
    let cancel = spawn_background_work();
    return cancel  // caller may or may not use it
}

Refined Types

Refined types carry constraints as part of the type itself. A String{len: 1..100} is not just a string — it is provably between 1 and 100 characters. The compiler enforces these constraints.

refined_example.cb
type User {
    name: String{len: 1..100},
    age: u8{range: 0..150},
    email: Email,  // built-in validated type
}

Sum Types

Sum types (also known as tagged unions or algebraic data types) let you define a type that can be one of several variants. The compiler ensures you handle all variants, eliminating null pointer errors and unhandled cases.

sum_type_example.cb
type Result<T, E> = Ok(T) | Err(E)

fn process(r: Result<User, DbError>) {
    match r {
        Ok(user)  => greet(user),
        Err(err)  => log_error(err),
    }  // exhaustive — missing a case is a compile error
}

Intent Annotations

Intent annotations are structured metadata that describe what a function should do, not how. They serve a dual purpose: they allow any AI to instantly understand the codebase, and they allow the compiler to formally verify correctness.

intent_example.cb
#[intent("Transfer tokens between accounts,
         ensuring sender has sufficient balance,
         atomic — both sides update or neither does")]
#[invariant(total_supply_unchanged)]
#[pre(balances[from] >= amount)]
#[post(balances[to] == old(balances[to]) + amount)]
fn transfer(
    from: Address,
    to: Address,
    amount: u256
) {
    // Implementation verified against annotations
}

AI Workflow: An AI agent reads the #[intent(...)] annotation, generates the function body, and the compiler verifies the implementation satisfies all #[pre], #[post], and #[invariant] conditions. If it compiles, the AI got it right.

Actor Model

C! uses the actor model for concurrency. Each actor owns its state exclusively, and communication happens through typed messages. There is no shared mutable state, making data races impossible by construction.

actor_example.cb
actor Counter {
    state count: u64 = 0

    on Increment(by: u64) {
        count += by;
        reply count
    }

    on GetCount {
        reply count
    }
}

// Supervision tree for fault tolerance
supervisor AppSupervisor {
    strategy: OneForOne,
    children: [Counter, Logger, Cache]
}

Deployment Targets

C! compiles a single codebase to multiple targets without code changes:

  • Native binaries — High-performance executables for servers and CLI tools
  • WebAssembly (WASM) — Run in browsers and edge environments with near-native speed
  • Blockchain bytecode — Deploy smart contracts to EVM-compatible chains

Full-stack shared types: Your User type defined once is used identically in your backend API handler, your frontend WASM component, and your on-chain contract. No serialization bugs, no schema drift.

Smart Contracts

C! treats smart contracts as first-class citizens. The linear type system makes reentrancy attacks impossible by construction — once state is consumed in a transaction, it cannot be accessed again until the transaction completes.

contract.cb
#[contract(evm)]
actor Vault {
    state balances: Map<Address, u256>

    #[payable]
    on Deposit() {
        balances[msg.sender] += msg.value;
    }

    on Withdraw(amount: u256) {
        verify!(balances[msg.sender] >= amount);
        balances[msg.sender] -= amount;
        // State updated BEFORE external call
        // Reentrancy impossible: state is linear
        send(msg.sender, amount);
    }
}

Standard Library

The C! standard library provides core types and utilities, all built on the linear type system:

Module Description
std::collections Vec, Map, Set with ownership-aware APIs
std::io File, network, and stream I/O with linear handles
std::crypto Hashing, signing, verification primitives
std::net HTTP client/server, WebSocket, gRPC
std::json Type-safe JSON serialization/deserialization
std::test Testing framework with property-based testing
std::async Async runtime built on the actor model

Ready to try C!?

Follow our getting started guide to install the compiler and write your first program.