Checkpoint: Read an Unsafe Call as a Proof Ledger

The language boundary and the five CULPA requirements become useful only when they are read as one connected argument. For CULPA, an unsafe call is not just a marked line; it is a small proof ledger whose entries identify the operation, the condition it needs, the object or value at stake, and the evidence that reaches the call. This checkpoint uses that ledger to compare different call sites and to make a concrete judgment about what a useful analyzer must preserve.

The unsafe call has four questions

The first question is what operation is being performed. A raw-pointer dereference, an unchecked range access, a type reinterpretation, and an unchecked string conversion do not ask for identical evidence. The operation determines which documented safety requirements are relevant. Starting with the unsafe keyword alone loses that information before the investigation begins.

The second question is what must be true. The requirement may be that an index stays within a range, a pointer is non-null, an address is correctly aligned, a memory region contains initialized values, or a pointer follows the required aliasing discipline. Several conditions can apply at once. The point is not to choose one label as the “real” explanation, but to identify every condition whose failure could make this particular operation invalid.

The third question is which object or value owns the condition. A length is meaningful only relative to a particular allocation. An alignment property is meaningful relative to the type that will interpret the address. Initialization concerns a particular range of bytes and the legal values of a particular type. A string’s encoding guarantee belongs to the bytes behind that string, not to every buffer produced later from its characters. If the object changes while data moves through the program, the original evidence may no longer apply.

The fourth question is what happened to the evidence before the call. A safeguard can be absent, established, or established and then invalidated. A bound comparison may establish that an index is safe for one collection. A constructor may establish alignment for one memory object. A safe type may provide an implicit invariant. But a cast, mutation, reallocation, or deallocation can change the state that made the safeguard valid. A proof ledger must record that history rather than treating a guarantee as a permanent property of a variable name.

These questions turn a vague code-review instinct into a testable judgment. If an unsafe call has a non-null pointer but no evidence that its range fits the allocation, the missing proof is InBound. If the range is valid but its bytes have not been initialized as legal values, the missing proof is Initialization. If a safeguard existed and a later cast destroyed the invariant, the issue is not merely “no check was found”; the evidence was revoked. The distinction determines what a maintainer should repair and what an analyzer should report.

The ledger also clarifies the difference between an unsafe permission and a bug finding. Entering an unsafe block answers a syntactic question: may this class of operation be performed here? The ledger asks a semantic question: do the required facts hold for this object on this path? A failed ledger entry is a reason to investigate.