Skip to content

0. How to read this document

This is the language’s design, and the contract the compiler is built against: where the two disagree, the compiler is wrong. Every change to it is a revision row in §21. It is still meant to be argued with. Three markers appear throughout:

  • [DECIDED] — settled. Implementation may proceed against it.
  • [PROPOSED] — my recommendation, written as if decided so you can judge it in context. Reject freely; nothing depends on it yet.
  • [OPEN] — a genuine fork that needs your call. All of them are collected in §18.

Every code sample here is intended to be a valid Dazzle program (or a deliberately invalid one, marked as such). They double as the compiler’s first test corpus.

If you read nothing else, read this. Everything after it is detail.

// no imports: dazzle.toml names what every file can use — `std`, `engine`, `rl` (§3.2)
pub struct Buffer<T> { // private by default; pub / pub(module) / (nothing)
m_data: own ^mut T // own ^mut T this struct frees it -> Buffer is move-only
m_len: u32 // fields default-initialise; no uninitialised memory
tie @items = m_data // a public name for the memory it owns (§6.7)
Buffer(cap: u32) { ... } // constructor; no allocator: the compiler places memory
pub fn length(&self) -> u32 { return m_len } // &self = reads
pub fn resize(&mut self, n: u32) kills self@items { ... } // &mut self = mutates
}
impl<T> Drop for Buffer<T> { // destructor -> deterministic, C++ handles it
fn drop(&mut self) { ... }
}
impl<T: Copy> Copy for Buffer<T> { // copy constructor -> copyable when T is
fn copy(&mut self, other: &Self) { ... }
}
Concept Syntax One-line rule
Refer to a value &T &mut T ^T ^mut T own ^mut T &/&mut are non-null and stack-only; ^ is a C pointer, read-only unless ^mut; own ^ means you free it; ^T → &T needs an or clause, &T → ^T is free, and reading or writing through a pointer is unsafe
Copy vs move let b = a / let b = move(a) if the type owns anything, copying needs an impl Copy; otherwise move()
Invalidation fn push(&mut self, v: T) kills self@items every function says which ties (@items) it may end; a reference into a killed tie is an error to use; aliasing stays legal (§6.7)
Memory List<T>() no allocator in ordinary code: the compiler places each value — the scope’s region, the caller’s result, else the heap (§14.6)
Absence T? postfix, binds to the type name. ^T? is a pointer to an optional; an optional pointer is just ^T
Failure fn f(…) !-> T ! marks the signature, never a type. There is no T! to store, annotate, or point at
Handle anything fallible let v = f() or { e -> … } one clause for T!, T?, and ^T alike
Propagate f() or return only where the enclosing signature can carry the failure
Fall back f() or Textures::Missing “on failure use this instead” — the common case in game code
Sum type union S { A{…} B{} C } + switch tagged, exhaustive, no fallthrough; bare C carries struct C
Interface trait X + impl X for Y bound <T: X> = static dispatch; ^X = fat pointer, object stays the same size; an impl lives with the trait or with the type, so a module can add a trait to a type it did not write
Generics fn f<T: Ord>(…) checked once at the definition, then monomorphized by Dazzle
Module one dazzle.toml = one module folders mean nothing; name = "engine::render" for nesting
Metaprogramming macro m -> decl { … }, invoked #m …; an expr or stmt macro m!(…) declarative pattern → expand, so the LSP can complete inside it
Reflection typeinfo_of<T>() one static table; your structs carry zero extra bytes
Compiles to C++17, -fno-exceptions -fno-rtti with #line back to .dz, so debuggers and profilers show your source

The five rules that shape everything else: private by default · no implicit numeric conversions · every value is initialised · moved-from values reset to defaults · a reference lives only where references live — a local, a parameter, a result, or a view struct — never in an ordinary field or a container (this is why no struct carries a lifetime, and ties, §6.7, are enough).

And the promise they add up to: a program with no unsafe has no undefined behaviour on one thread (§1.1).