Skip to main content

Keywords

Verum distinguishes reserved keywords — which cannot ever be used as identifiers — from contextual keywords, which are keywords only where the grammar expects them and ordinary identifiers elsewhere.

Reserved — only 3

These may never appear as identifiers:

KeywordUse
letVariable binding (let x = ...).
fnFunction definition (fn foo() { ... }).
isType-definition separator (type T is …); pattern test.

The parser recognises these three tokens unconditionally.

let let = 42; // SYNTAX ERROR — `let` is reserved
let fn = foo; // SYNTAX ERROR — `fn` is reserved
let is = true; // SYNTAX ERROR — `is` is reserved

let fn_pointer = foo; // fine — a name that BEGINS with a keyword
let is_valid = true; // fine, for the same reason

A keyword is a whole token. fn_pointer and is_valid are ordinary identifiers, and the compiler accepts them.

Contextual — 109

Contextual keywords take the keyword role only when the grammar expects them. In any other position they are ordinary identifiers.

For example, async is a keyword only immediately before fn or at the start of a block expression:

async fn fetch() { ... } // keyword
let async = 42; // identifier — legal
print(async); // identifier — legal

Below is the full contextual keyword list, grouped by purpose. The authority is the lexer: crates/verum_lexer/src/token.rs carries 112 #[token("…")] keywords, three of them reserved, so 109 are contextual. This page names every one.

Visibility

pub public internal protected private

Used at the start of items. private is explicit (matching the absence of any visibility).

Declarations

type module mount link implement context protocol
extends const static meta ffi extern pattern
using layer

Each introduces a top-level or contained item; see the corresponding item production in the grammar. link is the second spelling of mount — the grammar's mount_stmt accepts either. using [...] declares the contexts a function demands, and is the keyword the whole context system turns on; layer groups provide statements into a named bundle (layer AppLayer { provide Ctx = expr; }).

volatile is a pointer-type modifier rather than an item keyword — *volatile UInt32, for MMIO — and belongs with the type system below.

Control flow

if else match return for while loop break continue in

in appears in for p in iter, provide X = v in { ... }, and quantifier bindings (forall x in S. P).

Async and structured concurrency

async await spawn defer errdefer try
yield throws select nursery recover finally
biased on_cancel
  • biased — marker on select for prioritised branch order.
  • on_cancel — handler attached to a nursery block.

Function modifiers

pure mut const unsafe move ref default cofix
  • cofix — coinductive fixpoint; see language/copatterns.
  • default is contextual: a method marked default inside implement is overridable by specialisations. Elsewhere — including as a variable name — it's an ordinary identifier.

Metaprogramming

meta quote lift stage
  • quote { ... } — quasi-quotation.
  • lift(expr) — cross-stage lift.
  • stage — appears only in $(stage N){ expr } and quote(N){ ... }.

Paths and self

self super crate Self
  • self — the current instance (lowercase), or the current module inside paths.
  • Self — the implementing type (uppercase), inside protocols and impl blocks.
  • super — the parent module.
  • crate — the cog's root module.

Contracts and verification

where requires ensures invariant decreases result
  • requires — precondition.
  • ensures — postcondition; result refers to the return value.
  • invariant / decreases — loop contracts.

Proof DSL

theorem lemma axiom corollary proof
calc have show suffices obtain
by qed induction cases contradiction
forall exists tactic from implies
assumption

Tactic names are keywords too, not identifiers:

auto blast simp smt ring field omega trivial

See language/proof-dsl and reference/tactics.

Type system

some (* existential: some T: Protocol *)
dyn (* dynamic dispatch: dyn Protocol *)
unknown (* top type — the dual of `!` *)
universe (* universe polymorphism: universe u *)
Type (* kind / type-of-types: Type, Type(0), Type(u) *)
Prop (* universe of proof-irrelevant propositions *)
Level (* universe level kind: u: Level *)
max (* level arithmetic: max(u, v) *)
imax (* impredicative max for Π into Prop *)
view (* pattern-level view operator *)
affine (* affine type modifier: type affine Foo is … *)
linear (* linear type modifier: type linear Foo is … *)
stream (* stream literal / pattern prefix *)
tensor (* tensor literal + type prefix: tensor<3, 4> Float32 *)
checked (* &checked reference, capability-ref modifier *)
with (* capability type: T with [Read, Write] *)
gen (* generator expression prefix *)
set (* set comprehension prefix *)
typeof (* runtime type of an expression *)

See the following language pages for semantics:

Values

true false null
  • null — the Maybe.None of raw pointer types; use Maybe.None in safe code.

The bottom type

! (* never type — used in type position: `fn diverge() -> !` *)

! is a token in both expression (negation / bitwise-not) and type position (never). The parser distinguishes by context.

Pseudo-keywords

default

Contextual keyword inside implement blocks:

implement Display for T {
default fn fmt(&self) -> Text { ... } // `default` is a keyword here
}

let default = 42; // `default` is an identifier here

self / Self

Lowercase self is a value parameter; Self is the implementing type.

implement Foo for Bar {
fn method(&self) -> Self { ... } // Self = Bar
}

in, as

Both contextual. in appears in patterns/quantifiers/provide; as appears in casts, aliases, and mount renames.

Both are keywords everywhere, not only where the grammar expects them:

let as = 5; // error<E071>: `as` is a reserved keyword
let in = 5; // error<E071>: `in` is a reserved keyword

for x in 0..10 { ... } // `in` as keyword
let y = 3.0 as Int; // `as` as cast operator

They are listed here because no production names them — see reserved by the lexer below.

Reserved by the lexer

Twenty further words are taken by the lexer and refused as identifiers, even though the productions above do not name them. The grammar carries them as lexer_only_reserved:

as · checked · cofix · coinductive · extern · false · in · inductive · inject · lift · move · private · quote · ref · throw · true · typeof · unknown · view · with

let move = 5; // error<E071>: `move` is a reserved keyword
let view = 5; // error<E071>: `view` is a reserved keyword
let other = 5; // fine

So "three reserved keywords" is a statement about the grammar's reserved_keyword production, not about the names an author may bind. Twenty-three words are refused in a binding position.

Why so few reserved?

Only the three most essential tokens (let, fn, is) are fully reserved. Everything else can be an identifier in a context where the grammar does not expect it. This is a deliberate choice: keywords scale with the language's vocabulary, but the author's naming latitude should not.

You can name a variable async, impl, move, or pattern with no quoting and no escape syntax — the compiler disambiguates by position.

Operators that look like keywords

These tokens are not keywords — they are operators with spellings that resemble keywords:

TokenKind
isReserved keyword; pattern test operator.
asContextual keyword; type cast operator.
.awaitPostfix operator (dot + keyword).
inContextual keyword; not an operator.

Grammar reference

The authoritative keyword list is the keyword production in the grammar reference:

keyword = reserved_keyword | primary_keyword | control_flow
| async_keywords | modifiers | ffi_keywords
| module_keywords | additional_keywords | proof_keywords
| lexer_only_reserved ;

Plus the token production for keyword-like literals (true, false, null) and the type production for type-system keywords (Self, Type, Level, unknown).

See also

  • Syntax — how keywords appear in context.
  • Operators — operator-like tokens.
  • Grammar — the full grammar.