Chapter 8.2. Verification Target Collection
When verification starts, RAPx scans the crate to identify verification targets — functions whose safety contracts need to be checked — and resolves the contracts for each target.
8.2.1 What Gets Verified
In scan mode (the default), a function is selected as a verification target if:
- its body contains an
unsafe { }block, or - it is a method on a struct with
#[rapx::invariant]annotations.
For each target, the collector gathers:
- Every call to an
unsafe fnin the function body, along with the callee's resolved contracts. - Every raw pointer dereference (
*ptr,*ptr = val) in unsafe blocks — these implicitly requireValidPtr+Align+Typed, as if those contracts were declared on a pseudo-callee. - Every
static mutaccess — treated similarly. - Entry assumptions — the facts the verifier can assume when the function is entered:
- The function's own
#[rapx::requires]annotations. - If the function is a method on a struct with
#[rapx::invariant], those invariants are included as entry facts. - Type-level invariants for function parameters (e.g. a
&[T]parameter automatically suppliesValidPtr+NonNull+Allocated+InBound+Typedfor the slice's data pointer).
- The function's own
8.2.2 How Contracts Are Resolved
For each unsafe callee, the verifier needs to know what safety properties it requires. Contracts are resolved in four tiers, tried in order:
-
Direct annotation:
#[rapx::requires(...)]on the callee itself. See Chapter 8.3.2. -
Trait method inheritance: If the callee is a trait method impl and has no direct annotation, its contracts are inherited from
#[rapx::requires]on the trait's method declaration. -
JSON database: For standard library functions that can't be annotated upstream, contracts are loaded from a bundled database. See Chapter 8.3.2.
-
Call-chain inheritance: If the unsafe callee still has no contracts, the verifier looks inside its MIR body for the unsafe functions it calls (its own unsafe callees). If any of those have resolved contracts, they are used as the verification target — the caller only needs to satisfy the contracts of the deepest callee in the chain. This follows a bounded depth. For example, if A calls B, B calls C, and C has
#[rapx::requires], verifying the call to B checks A's state against C's contracts.
If all four tiers produce nothing, the callee's contracts are left as Unknown — the callsite cannot be verified.
8.2.3 Inspecting Targets with --prepare-targets
The --prepare-targets flag prints every verification target collected by the scanner — a function or method whose unsafe operations need to be checked — along with its resolved contracts:
cargo rapx verify --prepare-targets
For the linked_list_nonnull case study, the collector finds 13 targets. Output excerpt (annotations explained below):
23:35:35|RAPx|INFO|: Start analysis with RAPx.
23:35:35|RAPx|INFO|: ============================================================
23:35:35|RAPx|INFO|: [rapx::verify] prepare targets for struct: LinkedList
23:35:35|RAPx|INFO|: ============================================================
23:35:35|RAPx|INFO|: struct invariants: ①
23:35:35|RAPx|INFO|: - Align(head.unwrap_some(), Node<T>)
23:35:35|RAPx|INFO|: - Allocated(head.unwrap_some(), Node<T>, 1)
23:35:35|RAPx|INFO|: - Typed(head.unwrap_some(), Node<T>)
23:35:35|RAPx|INFO|: - Owning(head.unwrap_some())
23:35:35|RAPx|INFO|: - Align(tail.unwrap_some(), Node<T>)
23:35:35|RAPx|INFO|: - Allocated(tail.unwrap_some(), Node<T>, 1)
23:35:35|RAPx|INFO|: - Typed(tail.unwrap_some(), Node<T>)
23:35:35|RAPx|INFO|: - Owning(tail.unwrap_some())
23:35:35|RAPx|INFO|: --- method: new ------------------------------------------------- ②
23:35:35|RAPx|INFO|: return checkpoints: 1 block(s) [0] ③
23:35:35|RAPx|INFO|: unsafe checkpoints: <none>
...
23:35:35|RAPx|INFO|: --- method: drop ------------------------------------------------
23:35:35|RAPx|INFO|: return checkpoints: 1 block(s) [7]
23:35:35|RAPx|INFO|: unsafe callee: std::boxed::Box::<T>::from_raw(*mut T) -> std::boxed::Box<T>
23:35:35|RAPx|INFO|: safety contracts: ④
23:35:35|RAPx|INFO|: - Align(raw, T)
23:35:35|RAPx|INFO|: - Allocated(raw, T, 1, global)
23:35:35|RAPx|INFO|: - Typed(raw, T)
23:35:35|RAPx|INFO|: - Owning(raw)
23:35:35|RAPx|INFO|: - Alias(raw, ret)
23:35:35|RAPx|INFO|: path: shortest path: 0 -> 1 -> 2 -> 3 -> 4 ⑤
23:35:35|RAPx|INFO|: unsafe callee: std::ptr::NonNull::<T>::as_ref(&std::ptr::NonNull<T>) -> &'a T
23:35:35|RAPx|INFO|: safety contracts:
23:35:35|RAPx|INFO|: - Ptr2Ref(self.0, T)
23:35:35|RAPx|INFO|: path: shortest path: 0 -> 1 -> 2
23:35:35|RAPx|INFO|: ============================================================
23:35:35|RAPx|INFO|: [rapx::verify] total: 0 free function(s), 13 method(s), 1 struct(s), 0 trait(s) ⑥
| # | What | Description |
|---|---|---|
| ① | Struct invariants | All targets that receive the struct assume these hold on entry and must preserve them. |
| ② | Target name | Each function or method being verified. |
| ③ | Return checkpoints | Basic blocks where the function returns. |
| ④ | Safety contracts | Resolved contracts for each unsafe callee in the body. |
| ⑤ | Path | Shortest acyclic path from function entry to the callsite. |
| ⑥ | Summary line | Summary of targets to be verified. |