Turning Documentation into Executable Checks
CULPA’s first major move is to treat safety documentation as something an analyzer must execute, not merely something a programmer may read. The focus here is the Specification Pool: how it breaks a compound requirement into checkable predicates, how a successful check emits evidence for later operations, and why the resulting rule base still needs human interpretation and maintenance. The reader should come away able to distinguish a documented contract from an executable approximation of that contract.
From a safety sentence to a compositional predicate
Unsafe API documentation often describes a condition at a high level. A phrase such as “the result must be valid at its given type” sounds precise to a human who already knows the surrounding rules, but it compresses several questions. Is the memory allocated? Is the address aligned for the destination type? Has every byte been initialized? Does the bit pattern represent a legal value? Does the operation preserve the aliasing assumptions that later code will make? A static analyzer cannot reliably check the phrase as one undifferentiated fact.
CULPA’s Specification Pool decomposes that kind of description into predicates. A predicate is a condition with identifiable inputs and a result that the analysis can carry forward. The point is not to translate every sentence into a formal theorem. It is to expose the separate properties that determine whether a particular unsafe operation is justified, and to connect those properties to the object and state involved.
In the paper’s schema, a rule has three jobs. It identifies the operation being matched, states the requirements that must hold before the operation, and describes the emissions that become true after successful verification. This separation keeps a contract tied to a concrete call rather than treating it as free-floating documentation. It also gives the analyzer a place to represent change: a function can consume one set of facts, update an object’s abstract state, and emit a different set of facts for the next operation.
A predicate need not be a single numerical comparison. It can ask whether a range is covered by initialized memory, whether a pointer has the right non-null or alignment properties, or whether an operation preserves a type or aliasing invariant. What makes it executable is that the analysis knows what values, ranges, and state transitions the predicate refers to. A rule that merely repeats the word “valid” has not yet made the responsibility checkable.
Vec::set_len makes the idea concrete. A vector has a current length and an allocation capacity. Calling set_len changes which part of the allocation the vector presents as initialized elements. The specification therefore checks two different requirements: the proposed length must not exceed capacity, and the range newly exposed by the length change must already contain initialized, type-valid values. In CULPA’s terminology, one predicate checks the relationship between the vector’s length and capacity, while another checks initialization coverage of the relevant range.
Those checks answer different questions even though they are triggered by one function call. The capacity relationship is a boundary fact.