rocq/theories/Stdio.v
A logical stdout protocol, not a native FILE representation (milestone 3b).
Accepted bytes become ordered output obligations. Delivery and error-induced discard settle exact prefixes, preserving their interleaving. Outstanding obligations are NOT physical buffer contents: a direct-output path can accept and deliver in succession. Delivery means descriptor acceptance, not device durability. The error flag is not yet connected to native ferror.
This protocol has no memory effects or allocator. Native buffering, allocation/fallback, capability roots and shutdown require separate realization obligations. In particular, a protocol step cannot mint write authority or establish effects.
A C stream's orientation: not yet chosen, bytes, or wide characters. The first output fixes it.
| | | Unoriented | ||
| | | ByteOriented | ||
| | | WideOriented |
The logical state of stdout: its orientation, the outstanding output obligations (bytes accepted but not yet delivered or discarded, in order), and the sticky error flag.
| orientation | ||
| outstanding | : listZ | |
| stream_error | : bool |
A well-formed stream state q: every outstanding obligation is a byte, and a stream with any is byte-oriented.
One protocol action: accept bytes as new obligations, deliver bytes to the descriptor, discard bytes after an output error, or record an output error that settled nothing.
| | | Accept | bytes : listZ | |
| | | Deliver | bytes : listZ | |
| | | Discard | bytes : listZ | |
| | | OutputError |
One protocol step, from state q by an action:
- accept appends bytes to the obligations of a stream that is not wide, orienting it to bytes;
- deliver and discard consume a nonempty exact prefix of the obligations, discard also setting the error flag;
- an output error only sets the error flag.
The error indicator is sticky, not a ban on later output. Discard here means output-error abandonment, not seek/purge or fclose. A zero-progress failure records an error without settling any bytes.
A fixed action has one logical target; an exact consumed prefix fixes the remaining queue, without promising that the action occurs.
Explore proof 3 tactics
Initial goal
intros q action left right Hl Hr
inversion Hl ; subst ; inversion Hr ; subst ; try reflexivity
2 openfocused goal
all: match goal withH : ++ = ++ |- _⇒apply app_inv_head in H ; subst ; reflexivityend
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A finite sequence of stream_steps, from q by actions to q'.
The bytes an action accepts: an accept's, otherwise none.
The bytes an action settles: a delivery's or a discard's, otherwise none.
All bytes the actions accept, in order.
All bytes the actions settle, delivered or discarded, in order.
One step's accounting: the old obligations followed by what the action accepts equal what it settles followed by the new obligations.
Explore proof 2 tactics
Initial goal
intros q action q′ H
destruct H ; simpl ; try reflexivity ; apply app_nil_r
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
For a whole stream_run, the old obligations followed by all accepted bytes equal all resolved bytes followed by the remaining obligations. Every old or new obligation is either resolved in first-in, first-out order or still outstanding. Resolutions include BOTH delivery and abandonment; replacing them by delivery alone would promise lossless output.
Explore proof 9 tactics
Initial goal
intros q actions q′ H
induction H
2 openfocused goal
–simpl
2 opendeferred goal
apply app_nil_r
deferred goal
–unfold accepted , resolved in *
simpl
rewrite app_assoc , stream_step_accounting_ _ _ H
rewrite <- app_assoc , IHstream_run , app_assoc
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A step keeps the stream state well formed (stream_wf).
Explore proof 8 tactics
Initial goal
intros q action q′ H Hbytes Horient
destruct H ; split ; simpl in * ; auto
3 openfocused goal
focused goal
–apply Forall_app
3 opendeferred goal
deferred goal
auto
2 opendeferred goal
deferred goal
–apply Forall_app in Hbytes
2 opendeferred goal
tauto
deferred goal
–apply Forall_app in Hbytes
tauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
So does a whole run.
Explore proof 5 tactics
Initial goal
intros q actions q′ H
induction H ; auto |
intros Hwf
apply IHstream_run
eapply stream_step_wf ; eauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Once set, the error flag stays set through any run.
Explore proof 5 tactics
Initial goal
intros q actions q′ H
induction H ; auto
intros Herr
apply IHstream_run
destruct H ; simpl in * ; auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The events of one puts call that took the actions: one cumulative StreamBytes event on stdout with all the bytes accepted, even when that is none. Delivery and discard actions remain internal observations; no descriptor-write event is added.
A puts of the string bytes, from stream state q, runs the actions to q' and returns result: the stream is not wide; the actions accept a prefix of the string and its newline; and the result is a nonnegative int if all was accepted, or else EOF (2^32 - 1), which is also allowed after full acceptance.
A puts_protocol run, given a c_string argument and a final stdio_ready world, meets puts_post with puts_observation as its trace. This proves only the existing acceptance and result postcondition. String memory and final stdio readiness are independent premises; the native representation, footprints and effects are NOT derived from this pure protocol. Nor is native execution coverage claimed.
Explore proof 8 tactics
Initial goal
intros bytes q actions result q′ m p w born m′ w′ _ & _ & tail & Hbytes & Hresult Hstr Hready
exists p, bytes, acceptedactions, tail, result
split ; reflexivity |
split ; exact Hstr |
split ; exact Hbytes |
split ; exact Hresult |
split ; reflexivity |
split ; reflexivity | exact Hready
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Observable protocol boundaries
stdout at startup: unoriented, nothing outstanding, no error.
The initial stdout is well formed.
Explore proof 3 tactics
Initial goal
unfold stream_wf , stdout_initial
simpl
split ; constructor | congruence
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts of hello from the initial stdout may accept hello and its newline and return 6, delivering nothing yet: they stay outstanding.
Explore proof 8 tactics
Initial goal
split ; discriminate |
split
2 openfocused goal
–eapply stream_next ; apply stream_accept ; discriminate | | constructor
2 opendeferred goal
repeat constructor ; lia
deferred goal
–exists [ ]
split ; reflexivity |
left
split ; reflexivity | lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Its observation is the one event of hello and a newline.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Prior output precedes this call; some is delivered, some discarded on error, and later output still succeeds with the error flag set.
Explore proof 5 tactics
Initial goal
eapply stream_next ; apply stream_accept ; discriminate | repeat constructor ; lia |
eapply stream_next ; apply stream_deliver65 66; 67; 68 false ; discriminate |
eapply stream_next ; apply stream_discard66; 67 68 false ; discriminate |
eapply stream_next ; apply stream_accept ; discriminate | repeat constructor ; lia |
eapply stream_next ; apply stream_deliver68; 69 [ ] true ; discriminate | constructor
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Output errors that settle nothing leave the obligations in place and set the error flag.
Explore proof 2 tactics
Initial goal
eapply stream_next ; constructor |
eapply stream_next ; constructor
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts may return EOF having accepted nothing, after an output error.
Explore proof 7 tactics
Initial goal
split ; discriminate |
split
2 openfocused goal
–eapply stream_next ; constructor
deferred goal
–exists 10
split ; reflexivity |
right
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts cannot succeed having accepted the string without its newline.
Explore proof 1 tactics
Initial goal
intros _ & _ & tail & Hbytes & → _ | Hresult ; discriminate
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A wide-oriented stream accepts no bytes.
Explore proof 3 tactics
Initial goal
intros pending err bytes q′ H
inversion H
contradiction
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Delivery must start with the oldest obligation.
Explore proof 2 tactics
Initial goal
intros q′ H
inversion H ; subst ; simpl in * ; discriminate
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A value that is not a byte cannot be accepted.
Explore proof 4 tactics
Initial goal
intros q q′ H
inversion H as q0 bytes Ho Hb | | |
inversion Hb
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts on a wide-oriented stream is no protocol run, even one that does nothing and returns EOF.
Explore proof 3 tactics
Initial goal
intros bytes Ho _
apply Ho
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts may return EOF having accepted part of its string.
Explore proof 8 tactics
Initial goal
split ; discriminate |
split
2 openfocused goal
–eapply stream_next ; apply stream_accept ; discriminate | repeat constructor ; lia |
2 opendeferred goal
eapply stream_next ; constructor
deferred goal
–exists 66; 10
split ; reflexivity |
right
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
puts may return EOF having accepted its whole string and newline.
Explore proof 9 tactics
Initial goal
split ; discriminate |
split
2 openfocused goal
–eapply stream_next ; apply stream_accept ; discriminate | repeat constructor ; lia |
2 opendeferred goal
eapply stream_next ; apply stream_accept ; discriminate | repeat constructor ; lia |
2 opendeferred goal
eapply stream_next ; constructor
deferred goal
–exists [ ]
split ; reflexivity |
right
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The observation gathers all accepted bytes into one event, skipping deliveries, and is one empty event when nothing was accepted.
Explore proof 1 tactics
Initial goal
split ; reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Resolutions keep discards in their place among deliveries: moving the discarded bytes after the delivered ones gives another order.