rocq/theories/HelloConv.v
GNU Hello's conversion of its greeting through the program logic of Logic.v (docs/logic.md): libc's mbrtowc, and gnulib's rpl_mbrtowc and rpl_mbsrtowcs around it, which main calls to turn the greeting into wide characters.
mbrtowc(out, src, n, state) writes three cells the program owns: the wide character at out, the conversion state and errno. Its triple (wp_mbrtowc) takes the three as whole blocks, with their life, bytes and aux slots, and hands them back holding whatever the memory holds after the call, which conv_result describes: Campaign 4's mbrtowc_result read through the owned bytes, under the audited codec. ContractFrame.v's prefix_agrees_out keeps every other piece agreeing, though the call's roots now meet the program's objects.
The world is Libc.v's libc_world. The triple is generic in the world interpretation wi, given that wi makes libc ready, fixes the codec and the errno cell, keeps libc's private roots, releases and capabilities apart from the program's objects (libc_private), and survives the call's writes into the three blocks. The audited codec premise (Haudit) stays explicit, as docs/logic.md records: it is not derived from the locale.
mbrtowc's triple
The Iris instance of the registry and wi.
The expressions of the language.
mbrtowc has Campaign 4's contract, whose state predicate valid admits the zero state and holds only of bytes.
The world interpretation makes libc ready, with the errno cell errno, keeps the codec piece current (codec_current) and libc's private state apart. A conversion learns the codec from the codec piece, which setlocale's setter set (wp_setlocale_set).
The audited codec premise: the world's codec is one of the audited profiles, C or C.UTF-8.
The world interpretation survives a return of mbrtowc into the output, state and errno blocks the program owns, holding whatever the memory holds there afterwards.
Owning two blocks, they are of different objects: each life is owned once.
Explore proof 7 tactics
Initial goal
rewrite / block_ps / pts ! big_sepL_cons
iIntros [Hl _] [Hl′ _]
iDestruct pointsto_ne with Hl Hl′ as %Hne
iPureIntro
intros ←
apply Hne
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
The arguments are admitted (mbrtowc_pre) where the program owns the output, state and errno blocks and the source's life and bytes, of four different objects, the state's bytes valid.
Proof 125 steps Qed
A return of the call keeps every piece but the three blocks' bytes and slots agreeing (prefix_agrees_out), so that, the blocks holding what the memory holds after it, the ghost heap agrees; the live objects stay apart and the runtime ready.
Proof 65 steps Qed
Campaign 4's mbrtowc_result, for 0 < n and the audited codec, read through the owned bytes: conv_result.
Proof 106 steps Qed
mbrtowc(out, src, n, state)'s triple, from mbrtowc_contract: owning the output, state and errno blocks, of 4, 8 and 4 bytes with their slots, and the source's life and bytes ss, of four different objects, where vo, vs, vn and vt read as out, src, n and state, n is positive and within ss, and the state's bytes are valid, the call binds d to its result r and hands the blocks back holding new bytes and slots, which conv_result describes.
Proof 71 steps Qed
rpl_mbrtowc's triple
The genv links Hello, whose world interpretation keeps every lent string where lent_at says: hard_locale's premises (HelloLocale.v).
setlocale's query, as hard_locale needs it.
strlen, memcpy and strcmp have their contracts.
The world interpretation survives a copy, a byte store and a four-byte store into a block the program owns, and an allocation.
Enter a block of rpl_mbrtowc.
A pure instruction.
rpl_mbrtowc from its block L11, where pr.1 is the output pointer out, whichever entry chose it: the call to mbrtowc, then the fallback outside a hard locale.
Proof 119 steps Qed
An invalid or incomplete sequence.
Not a hard locale: the first byte stands for itself.
A hard locale: mbrtowc's answer.
A character, a NUL or nothing to stand for: mbrtowc's answer.
rpl_mbrtowc(out, src, n, state) for a non-null out, from wp_mbrtowc and HelloLocale.v's hard_locale_spec: owning the output, state and errno blocks, the source's n bytes or more, the codec piece, the literals "C" and "POSIX", and the name of LC_CTYPE (category 0) lent, the call returns what rpl_result says, and hands every block back.
Proof 17 steps Qed
rpl_mbsrtowcs's window
The memchr contract, for strnlen1.
Enter a block of rpl_mbsrtowcs.
Read byte k of the window in block blk, after the first: a flight to q + k, a half check against the first check, a load and its test. Leaves the branch on the test, with the byte named x.
The temporaries the converting loop keeps, and their values: the destination cursor, the source cursor, the count left, the state, srcp, its check, and the destination's address.
End the window at L37, with the window's length n in pr.8.
rpl_mbsrtowcs's window, blocks L29 to L57: with the source cursor q at a string bs, the loop reads up to four bytes, stopping at the NUL, and then asks strnlen1 for at most 12 more; pr.8 is then min(len + 1, 16), and the loop's other temporaries are as they were.
Proof 119 steps Qed
The first byte, through a check.
The second byte.
The third byte.
The fourth byte.
Four bytes before the NUL: strnlen1 counts the rest, up to 12.
A string's pieces are a block from its offset: its life and its bytes with the NUL, and no aux slots.
Explore proof 4 tactics
Initial goal
rewrite / SL.string / string_ps / block_ps
cbn [ aux_ps ]
rewrite app_nil_r
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
An object's life, bytes and aux slots from offset o are its block from o.
Explore proof 2 tactics
Initial goal
rewrite / block_ps / pts big_sepL_cons big_sepL_app / live / bytes / auxes / pts big_sepL_singleton
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
errno's lookup has Campaign 4's contract.
The world interpretation survives a pointer store into a cell the program owns.
__errno_location() returns the errno cell and changes nothing: HelloClose.v's wp_close_errno over libc_world. A local rule, for promotion to a libc logic module.
Proof 30 steps Qed
What rpl_mbsrtowcs's caller learns when it returns r, with dest's final bytes ws, errno's bytes xe and the length l of the source: EILSEQ, -1 with 84 in errno; or r wide characters, none zero, then a zero one.
rpl_mbsrtowcs's converting loop, from block L28, by Löb induction: after i wide characters from the first j bytes of the string at p, with the state valid and in its initial shift state, every path returns what mbs_post says; the abort after an incomplete sequence is never reached.
Proof 188 steps Qed
Characters are left to write.
The window over the suffix from byte j.
The next output word, and the source as a block.
Never incomplete: the abort is not reached.
EILSEQ: the cursor goes back through srcp, errno gets 84, and -1 returns.
The NUL: null goes back through srcp, and the count of characters returns.
A character: both cursors move on, and the loop goes round.
rpl_mbsrtowcs
rpl_mbsrtowcs's entry binds its four arguments.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
rpl_mbsrtowcs(dest, srcp, len, ps) as main calls it: dest a fresh buffer of len wide characters, srcp a cell holding the string p, len the string's length and its NUL, and ps a non-null state, valid and in its initial shift state. The entry allocates a local it never uses, keeps ps, loads the string through srcp, a read-only literal or not, and goes to the converting loop (mbs_loop) with nothing written yet; the counting loop, for a null dest, is not reached. The call returns what mbs_post says.
Proof 54 steps Qed
The local for the counting loop, never used here.
ps is not null.
The string through srcp.
dest is not null: the converting loop.
HelloConvOn over a Logic instance of its own.