proof: close :'s compiling_word_id tracking, the last easily-closeable defining-words gap

Adds compiling_word_id :: nat option to vm_state, modelling vm->compiling_word
(include/vm.h:421). forth_colon_entry_half now sets it from latest_id on success
and forces it to None on the pinned-conflict failure path, matching the real C's
unconditional `vm->compiling_word = de;` before its own NULL check in
vm_enter_compile_mode (src/vm.c:232-264).

: is now closed through entry creation + compiling_word tracking, same point as
CREATE/VARIABLE/CONSTANT. Remaining gap for : is the same DF write (gap b,
vm_align+HERE capture) those three already closed but not yet composed in here.

All 52 theories verify clean (isabelle build -D proof/, ~48s).

Part of the pre-Artemis closeout pass (FABRIC-2.md 5.2). PROOFS included per
Captain Bob's 2026-08-14 instruction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-15 06:03:10 -04:00
co-authored by Claude Sonnet 5
parent b9e8fdc3ae
commit ee3a2e57aa
3 changed files with 83 additions and 32 deletions
+58 -21
View File
@@ -70,10 +70,10 @@ begin
remains unmodelled per word: `vm_create_word` itself parses no
input (name arrives pre-parsed, but the parse producing that
name is the TIB/input-subsystem gap, present throughout this
suite), does not capture the entry into any per-VM
`vm->compiling_word`-style tracking field (none exists in
vm_state), and callers still mutate the returned `DictEntry*`
directly afterward for flags beyond what `dict_insert_entry`'s
suite; `:` alone captures the entry into `compiling_word_id`,
CLOSED 2026-08-15, see `forth_colon_entry_half` below), and
callers still mutate the returned `DictEntry*` directly
afterward for flags beyond what `dict_insert_entry`'s
`init_flags` parameter already captures. This is still a
materially bigger gap than the raw-pointer-navigation gap
already flagged in dictionary_manipulation_words.c (which only
@@ -398,7 +398,7 @@ lemma colon_guard_not_full_colon: True
file header finding (a)/(b). Named so the omission is greppable.\<close>
by simp
(* ── : entry-creation half, gap (a) PARTIALLY CLOSED 2026-08-14 ──────────
(* ── : entry-creation half, gap (a) CLOSED 2026-08-15 ─────────────────────
`vm_enter_compile_mode` (src/vm.c:232-264) does forth_colon_guard's
mode/state effect FIRST, then `vm_create_word(...)`, then
`de->flags |= WORD_SMUDGED` on the fresh entry (vm.c:251) -- unlike
@@ -407,15 +407,24 @@ lemma colon_guard_not_full_colon: True
`dict_insert_entry 0`. Composed after forth_colon_guard: only valid
when the nested-`:` guard did not already error (real C: vm_create_word
is never reached if `defining_word_colon`'s own nested check fired,
since that returns before calling vm_enter_compile_mode at all). Still
NOT modelled beyond this: name parse, `vm->compiling_word` tracking
(no vm_state counterpart -- would need a new field, not attempted
here), vm_align+HERE capture, and the DF write of the threaded-body
start address (gap b). *)
since that returns before calling vm_enter_compile_mode at all).
`vm->compiling_word = de;` (vm.c:245) is unconditional -- it runs
BEFORE the `if (!de) { vm->error = 1; return; }` check right after it,
so compiling_word is set to the fresh entry on success and to NULL
(mirrored here as `None`) on failure, in both cases. Modelled via
`compiling_word_id` (StarForth_Base.thy), set from `latest_id vm1`
after `dict_insert_entry` (which sets `latest_id := Some wid` only on
the non-pinned-conflict path and leaves it untouched on the error
path) -- explicitly forced to `None` on the pinned_conflict branch so a
stale `latest_id` from an earlier successful definition can never leak
through as a false "compiling_word" on this failure. Still NOT modelled:
vm_align+HERE capture, and the DF write of the threaded-body start
address (gap b). *)
definition forth_colon_entry_half :: "string \<Rightarrow> bool \<Rightarrow> vm_state \<Rightarrow> vm_state" where
"forth_colon_entry_half name pinned_conflict vm =
dict_insert_entry name WORD_SMUDGED pinned_conflict (forth_colon_guard vm)"
(let vm1 = dict_insert_entry name WORD_SMUDGED pinned_conflict (forth_colon_guard vm)
in vm1\<lparr>compiling_word_id := (if pinned_conflict then None else latest_id vm1)\<rparr>)"
lemma colon_entry_half_requires_guard_to_pass:
assumes "vm_mode vm \<noteq> ModeCompile" "\<not> pinned_conflict"
@@ -430,24 +439,36 @@ lemma colon_entry_half_still_compile_mode:
using assms by (simp add: forth_colon_entry_half_def forth_colon_guard_def
dict_insert_entry_def Let_def)
lemma colon_entry_half_sets_compiling_word:
assumes "\<not> pinned_conflict"
shows "compiling_word_id (forth_colon_entry_half name pinned_conflict vm) =
Some (word_id_next (forth_colon_guard vm))"
using assms by (simp add: forth_colon_entry_half_def forth_colon_guard_def
dict_insert_entry_def Let_def)
lemma colon_entry_half_conflict_clears_compiling_word:
assumes "pinned_conflict"
shows "compiling_word_id (forth_colon_entry_half name pinned_conflict vm) = None"
using assms by (simp add: forth_colon_entry_half_def)
lemma colon_entry_half_not_full_colon: True
\<comment> \<open>Still NOT modelled: name parse (`name` is a caller-supplied parameter
here, not derived from `forth_parse_word` -- see forth_colon_full
below), vm->compiling_word tracking (no vm_state field), vm_align+HERE
capture, DF write of the threaded-body start address (gap b). See
section header.\<close>
below), vm_align+HERE capture, DF write of the threaded-body start
address (gap b). See section header.\<close>
by simp
(* ── `:`, full composition, gap (a)+parse CLOSED 2026-08-15 ──────────────
(* ── `:`, full composition, gap (a)+parse+compiling_word CLOSED 2026-08-15
`defining_word_colon` (defining_words.c:407-426): nested-`:` guard
FIRST (checked before any parse -- real C order), THEN parse name, THEN
`vm_enter_compile_mode` (mode/state effect + WORD_SMUDGED entry
creation, already `forth_colon_entry_half`). `forth_parse_word` never
touches `vm_mode`, so checking the guard before parsing and reusing
`forth_colon_entry_half` (which re-derives the same guard internally
via `forth_colon_guard`) afterward is sound -- the mode it observes is
unchanged by the intervening parse. Still NOT modelled: vm->
compiling_word tracking, vm_align+HERE capture, DF write (gap b). *)
creation + compiling_word_id, already `forth_colon_entry_half`).
`forth_parse_word` never touches `vm_mode`, so checking the guard
before parsing and reusing `forth_colon_entry_half` (which re-derives
the same guard internally via `forth_colon_guard`) afterward is sound
-- the mode it observes is unchanged by the intervening parse. Still
NOT modelled: vm_align+HERE capture, DF write of the threaded-body
start address (gap b) -- this is the last remaining gap for `:`. *)
definition forth_colon_full :: "nat \<Rightarrow> bool \<Rightarrow> vm_state \<Rightarrow> vm_state" where
"forth_colon_full max_len pinned_conflict vm =
@@ -496,6 +517,22 @@ proof -
ultimately show ?thesis using nm_nonempty by auto
qed
lemma colon_full_success_sets_compiling_word:
assumes "vm_mode vm \<noteq> ModeCompile"
assumes "dropWhile is_ws (drop (input_pos vm) (input_buffer vm)) \<noteq> []"
assumes "max_len \<ge> 2"
assumes "\<not> pinned_conflict"
shows "compiling_word_id (forth_colon_full max_len pinned_conflict vm) \<noteq> None"
proof -
obtain nm vm1 where parse_eq: "forth_parse_word max_len vm = (nm, vm1)" by fastforce
hence nm_nonempty: "nm \<noteq> ''''"
using forth_parse_word_success_nonempty[OF assms(2) assms(3)] by (metis fstI)
have "forth_colon_full max_len pinned_conflict vm = forth_colon_entry_half nm pinned_conflict vm1"
using parse_eq nm_nonempty assms(1) by (simp add: forth_colon_full_def)
thus ?thesis
using colon_entry_half_sets_compiling_word[OF assms(4), of nm vm1] by simp
qed
(* ── ; ( -- ) : compile-mode guard only ──────────────────────────────────── *)
(* C: error unless vm->mode == MODE_COMPILE; else calls vm_exit_compile_mode,
entirely unmodelled (finds/compiles EXIT, flips WORD_SMUDGED/WORD_COMPILED,