Generate a high-level TLA+ model from source code (C, C++, Rust, etc.)...
Create a TLA+ specification from source code by understanding the code's intent, abstracting implementation details, and focusing on the essential concurrent/distributed behavior.
TLA+ models should capture what the system does, not how it does it:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Phase 1: Understand the Code ā
ā ā What problem does it solve? ā
ā ā What are the key functions and their purposes? ā
ā ā What state is being managed? ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Phase 2: Identify Abstractions ā
ā ā What are the essential state variables? ā
ā ā What are the atomic actions? ā
ā ā What concurrency/ordering matters? ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Phase 3: Write TLA+ Specification ā
ā ā Define constants and variables ā
ā ā Write Init and actions ā
ā ā Define Next as disjunction of actions ā
ā ā Check specification syntax with TLC parser SANY ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Phase 4: Propose Properties ā
ā ā Safety invariants ā
ā ā Safety properties (what must be true at all times) ā
ā ā Liveness properties (what must eventually happen) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Read the code and answer in English:
For each significant function, document:
| Function | Purpose (in English) | Side Effects | Concurrency Notes |
|---|---|---|---|
functionName |
What it does | State changes | Thread-safe? Atomic? |
Focus on:
Skip:
List all mutable state that affects the system's behavior:
| State Variable | Type | Purpose | Accessed By |
|---|---|---|---|
varName |
int/enum/struct | What it tracks | Which functions |
Look for:
Recognize common patterns in the code:
| Pattern | Indicators | TLA+ Abstraction |
|---|---|---|
| State machine | Enum state, switch statements | pc variable with transitions |
| Reference counting | refCount, addRef/release |
Counter variable |
| Producer-consumer | Queue with enqueue/dequeue | Sequence variable |
| Lock-based | Mutex lock/unlock | Optional: model explicitly or abstract |
| Lock-free | Atomics, CAS operations | Atomic state transitions |
| Connection lifecycle | Connect/disconnect, states | State machine per connection |
High-level modeling principles:
std::vector<T> ā finite set or sequenceuint32_t refCount ā Nat (natural number)| Source Code | TLA+ Abstraction |
|---|---|
| Class with state machine | Variables + PC states |
| enum State | Set of constants {State1, State2, ...} |
std::atomic<T> |
Variable (atomicity is implicit in TLA+) |
compare_exchange |
Guarded action with atomic state change |
| Thread/process | Element of Threads set, may have own pc[thread] |
shared_ptr<T> |
Logical pointer (present/absent), or reference count |
| Mutex-protected region | Single atomic action (if treating as atomic) |
| Queue | Seq(Element) with Append/Head/Tail |
| Counter | Nat with increment/decrement |
Include:
Exclude:
---------------------------- MODULE ModuleName ----------------------------
EXTENDS Naturals, Sequences, FiniteSets
\* Configuration constants
CONSTANTS
Threads, \* Set of threads/processes
MaxValue \* Bounds for model checking
\* State constants (if using state machine)
CONSTANTS
State_Init,
State_Active,
State_Done
VARIABLES
state, \* Current state of the system
counter, \* Example counter variable
pc \* Program counter for each thread (if multi-threaded)
vars == << state, counter, pc >>
TypeOk ==
/\ state \in {State_Init, State_Active, State_Done}
/\ counter \in Nat
/\ counter <= MaxValue
/\ pc \in [Threads -> PCStates]
Init ==
/\ state = State_Init
/\ counter = 0
/\ pc = [t \in Threads |-> PC_Start]
For each significant operation identified in Phase 1:
\* Action: Description of what this action models. This description should describe the effect of the action on the state of the system and role of this code in the overall system behavior.
\* Source: functionName() in source.cpp
ActionName(thread) ==
/\ pc[thread] = PC_Ready \* Guard: when can this happen?
/\ state = State_Active \* Additional guards
/\ counter' = counter + 1 \* State changes
/\ pc' = [pc EXCEPT ![thread] = PC_Next]
/\ UNCHANGED << state >> \* Explicitly unchanged variables
Action naming conventions:
Next ==
\/ \E t \in Threads : ActionName(t)
\/ \E t \in Threads : AnotherAction(t)
\/ SystemWideAction
\* Stuttering step (optional, for liveness)
Spec == Init /\ [][Next]_vars
If available, check specification syntax with TLA+ MCP tool tlaplus_mcp_sany_parse.
Safety invariants are state exspressions that must be true in every state:
\* No negative counter
CounterNonNegative == counter >= 0
\* Mutual exclusion
MutualExclusion ==
Cardinality({t \in Threads : pc[t] = PC_Critical}) <= 1
\* State consistency
StateConsistency ==
state = State_Done => counter > 0
Check safety invariants with TLA+ MCP tool tlaplus_mcp_tlc_check.
Common safety patterns:
| Pattern | Invariant |
|---|---|
| No overflow | counter <= MaxValue |
| Mutual exclusion | At most one thread in critical section |
| No resource leak | Resources acquired = resources released |
| State validity | State machine only in valid states |
Safety properties are temporal properties that state what must be true at all times:
\* Actions happen in order
OrderedActions ==
[][pc = PC_Step1 => (pc' = PC_Step2)]_pc
Check safety properties with TLA+ MCP tool tlaplus_mcp_tlc_check.
Liveness properties state what must eventually happen:
\* Every thread eventually completes
EventualCompletion ==
\A t \in Threads : <>(pc[t] = PC_Done)
\* Counter eventually increases
EventualProgress ==
counter < MaxValue ~> counter >= MaxValue
\* If disconnect is requested, it eventually completes
DisconnectCompletes ==
[](disconnectRequested => <>(state = State_Disconnected))
Check liveness properties with TLA+ MCP tool tlaplus_mcp_tlc_check.
After completing the phases, present:
## Code Analysis Summary
### Purpose
[One paragraph describing what the code does]
### Key Functions
- `function1`: [purpose]
- `function2`: [purpose]
### State Variables
- `var1`: [what it tracks]
- `var2`: [what it tracks]
### Concurrency Model
[How threads/processes interact]
Complete, runnable TLA+ module.
## Proposed Properties
### Safety Invariants
1. **InvariantName**: [what it ensures]
2. **InvariantName**: [what it ensures]
### Liveness Properties
1. **PropertyName**: [what must eventually happen]
### Recommended Checks
- [ ] Run TLC with small bounds first
- [ ] Verify TypeOk holds
- [ ] Check for deadlock
Source code pattern:
std::atomic<int32_t> counter {0};
bool reserve() {
if (counter.fetch_add(1) >= 0) {
return true;
} else {
release();
return false;
}
}
void release() {
if (counter.fetch_sub(1) == threshold) {
finish();
}
}
TLA+ abstraction:
VARIABLES counter, finished
Reserve(thread) ==
/\ counter >= 0
/\ counter' = counter + 1
/\ UNCHANGED finished
Release(thread) ==
/\ counter > 0
/\ counter' = counter - 1
/\ finished' = IF counter' = 0 THEN TRUE ELSE finished
\* Safety: counter never goes negative while active
CounterSafety == finished = FALSE => counter >= 0
Threads = {T1, T2}, MaxValue = 3 for initial checksState_Connecting not S1| Pitfall | Solution |
|---|---|
| Model too detailed | Focus on state changes, not implementation steps |
| Missing UNCHANGED | Every variable must be primed or in UNCHANGED |
| Unbounded state | Add finite bounds for model checking |
| Implicit assumptions | Make all preconditions explicit guards |
| Modeling syntax, not semantics | Understand what code does, not how it's written |