rocq/theories/Panics.v
Campaign 7's property, never_panics: no run of a program from any admissible input ever reaches a failure (formal.md, Campaign 7).
A failure is what formal.md's panic table rules out: a Fil-C trap that is a capability check failing (a panic, in Filip's meaning), an unchecked access, undefined behaviour, an unreachable reached, or a step that leaves the model. A refusal (out_of_memory, too_big), a return, running out of fuel and a process exit are not failures, and neither is a run that never ends: the property speaks of every state a run reaches, not of how it ends.
This file owns the one classification of endings that both the core machine and Campaign 4's labelled machine use: failure for a core outcome, failure_ending for an ExternalMachine.v ending. The program statement never_panics is over Machine.v's machine for a closed module, one with no external calls; a program that calls libc is stated over the labelled machine with the same failure_ending.
Decisions
| Decision | Why |
|---|---|
| every runtime R, as a functor argument | the proof may not depend on where objects are placed or when the runtime refuses an allocation (formal.md's statement) |
| the run starts ready, an antecedent | until the heap starts above the module's data (rocq.md), as in Campaign 3 |
| every fuel, and every state reached | divergence and out_of_fuel are allowed; fuel plays no part |
| an allocation with a negative extent is a failure | the machine has no step there, so the run left the model |
| the module's initial memory and its entry function must exist, and every admissible input must make a frame | otherwise there is no run to speak of, and the program would hold vacuously |
| no premise about the runtime's safepoints | a safepoint changes no region (Runtime.v's collector_live): Fil-C's collector frees nothing, and a signal handler or a thread that could is the program's code; a module that installs one calls an external with no contract, which ends its run as a failure |
Failures
A trap reason that is a refusal, not a panic: the runtime would not hold an allocation (out_of_memory), or the request is over 2^48 - 1 bytes (too_big), which C's malloc would answer with null. These are mbrock's decisions in formal.md's panic table, and ExternalCalls.v's resource_reason for a call (refusal_resource).
The configuration c fails in the genv ge: its step ends in a failure, is stuck (the run left the model: an external call, code outside the core subset, an ill-formed word), or asks for an allocation with a negative extent, which the machine cannot take. An allocation the runtime refuses is not a failure: it ends the run with trap(out_of_memory), a refusal.
The labelled machine's ending why is a failure: a core failure, a stuck core step, or a contract violation at a call. A process exit is not.
A refusal is a resource ending of a call, and back.
Explore proof 3 tactics
Initial goal
intros why
unfold refusal , resource_reason
tauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A trap that ends a call for lack of resources is not a failure.
Explore proof 2 tactics
Initial goal
intros why H Hf
apply Hf , refusal_resource , H
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The property
The property over any runtime R.
The genv of the module md with its globals, whose free is R's.
The closed module md never panics when its function entry is called with any arguments xs that input admits: its initial memory exists and the entry is a function of the core subset; every admissible xs makes the entry's frame; and, where R is ready in the initial memory at its start, no state a run reaches from that call, with any fuel, fails. A run is Machine.v's: any fresh base R grants, and anything R does at a safepoint. This is run/5's start (Driver's run).
The proof rule for never_panics: an invariant inv of configurations that holds where every admissible call starts, whose every step is safe_step, and that what R does at a due safepoint keeps, where R is ready. Then no state a run reaches fails. Readiness is what makes an allocation's base fresh (R's fresh_allocation); the invariant is the program's own, as Cycle.v's and Buffer.v's assertions at each point.