44 Proves error handling correctness and error-free operation equivalence.
55
66 This extends the pure functional model to handle real-world error cases.
7+
8+ Proof status:
9+ - 5 of 6 decision procedures proved constructively (from Filesystem semantics).
10+ - is_empty_dir_dec remains axiomatic: requires Classical or finite-map refactor.
11+ - safe_* definitions use nested `if` on sumbool (not negb, which requires bool).
12+ - functional_extensionality from filesystem_model transitive import.
713 *)
814
915Require Import String.
@@ -38,68 +44,142 @@ Inductive OperationResult (A : Type) : Type :=
3844Arguments Success {A}.
3945Arguments Error {A}.
4046
41- (** * Safe Operations with Error Handling *)
47+ (** * Decision Procedures
48+
49+ Must appear before the safe_* definitions that use them.
50+
51+ Four decision procedures are proved constructively by inspecting the
52+ Filesystem function (Path -> option FSNode) directly.
53+ path_eq_dec follows from list_eq_dec + String.string_dec.
54+ is_empty_dir_dec is axiomatic — see justification below. *)
55+
56+ (** path_exists is decidable by case analysis on (fs p). *)
57+ Lemma path_exists_dec : forall p fs, {path_exists p fs} + {~ path_exists p fs}.
58+ Proof .
59+ intros p fs.
60+ unfold path_exists.
61+ destruct (fs p) as [node|].
62+ - left. exists node. reflexivity.
63+ - right. intros [node H]. discriminate.
64+ Defined .
65+
66+ (** is_directory is decidable by inspecting the node_type field. *)
67+ Lemma is_directory_dec : forall p fs, {is_directory p fs} + {~ is_directory p fs}.
68+ Proof .
69+ intros p fs.
70+ unfold is_directory.
71+ destruct (fs p) as [[ntype perms]|].
72+ - destruct ntype.
73+ + (* File *) right. intros [perms' H]. inversion H.
74+ + (* Directory *) left. exists perms. reflexivity.
75+ - right. intros [perms H]. discriminate.
76+ Defined .
77+
78+ (** is_file is decidable by inspecting the node_type field. *)
79+ Lemma is_file_dec : forall p fs, {is_file p fs} + {~ is_file p fs}.
80+ Proof .
81+ intros p fs.
82+ unfold is_file.
83+ destruct (fs p) as [[ntype perms]|].
84+ - destruct ntype.
85+ + (* File *) left. exists perms. reflexivity.
86+ + (* Directory *) right. intros [perms' H]. inversion H.
87+ - right. intros [perms H]. discriminate.
88+ Defined .
89+
90+ (** has_write_permission is decidable by inspecting the node and its writable flag. *)
91+ Lemma has_write_permission_dec : forall p fs, {has_write_permission p fs} + {~ has_write_permission p fs}.
92+ Proof .
93+ intros p fs.
94+ unfold has_write_permission.
95+ destruct (fs p) as [node|].
96+ - destruct (writable (permissions node)) eqn:Hw.
97+ + left. exists node. split; [reflexivity | assumption].
98+ + right. intros [node' [Heq Hwr]].
99+ inversion Heq. subst. rewrite Hw in Hwr. discriminate.
100+ - right. intros [node [H _]]. discriminate.
101+ Defined .
102+
103+ (** Path equality is decidable: Path = list string, decided by list_eq_dec + string_dec. *)
104+ Lemma path_eq_dec : forall p1 p2 : Path, {p1 = p2} + {p1 <> p2}.
105+ Proof .
106+ apply list_eq_dec.
107+ apply String.string_dec.
108+ Defined .
109+
110+ (** is_empty_dir is NOT decidable constructively with the current model.
111+
112+ is_empty_dir p fs requires:
113+ forall child : Path, path_prefix p child -> child <> p -> ~ path_exists child fs.
114+
115+ With Filesystem = Path -> option FSNode (a function over an infinite domain),
116+ there is no finite enumeration of all Path values, so this universal
117+ quantification cannot be discharged constructively.
118+
119+ Justification: in the operational model, every reachable filesystem is
120+ produced by a finite sequence of mkdir / rmdir / create_file / delete_file
121+ operations, so the set of inhabited paths is always finite in practice.
122+ Constructive decidability requires changing the Filesystem representation
123+ to a finite map (e.g., list (Path * FSNode) or MSetAVL), at which point
124+ is_empty_dir can be decided by scanning the entries.
125+
126+ Migration path: switch Filesystem to FMaps.t FSNode and reprove. *)
127+ Axiom is_empty_dir_dec : forall p fs, {is_empty_dir p fs} + {~ is_empty_dir p fs}.
128+
129+ (** * Safe Operations with Error Handling
130+
131+ safe_* definitions use nested `if` on sumbool values.
132+ Using `negb (sumbool_val)` is a type error (negb : bool -> bool, not sumbool)
133+ so we invert the condition by nesting the positive branch first. *)
42134
43135(** Safe mkdir - returns error codes instead of requiring preconditions *)
44136Definition safe_mkdir (p : Path ) (fs : Filesystem) : OperationResult Filesystem :=
45137 if path_exists_dec p fs then
46- Error EEXIST
47- else if negb (path_exists_dec (parent_path p) fs) then
48- Error ENOENT
49- else if negb (is_directory_dec (parent_path p) fs) then
50- Error ENOTDIR
51- else if negb (has_write_permission_dec (parent_path p) fs) then
52- Error EACCES
53- else
54- Success (mkdir p fs).
138+ Error EEXIST (* p already exists *)
139+ else if path_exists_dec (parent_path p) fs then
140+ if is_directory_dec (parent_path p) fs then
141+ if has_write_permission_dec (parent_path p) fs then
142+ Success (mkdir p fs)
143+ else Error EACCES
144+ else Error ENOTDIR
145+ else Error ENOENT. (* parent not found *)
55146
56147(** Safe rmdir - returns error codes *)
57148Definition safe_rmdir (p : Path ) (fs : Filesystem) : OperationResult Filesystem :=
58- if negb ( path_exists_dec p fs) then
59- Error ENOENT
60- else if negb (is_directory_dec p fs) then
61- Error ENOTDIR
62- else if negb (is_empty_dir_dec p fs) then
63- Error ENOTEMPTY
64- else if negb (has_write_permission_dec (parent_path p) fs) then
65- Error EACCES
66- else if path_eq_dec p root_path then
67- Error EACCES (* Cannot remove root *)
68- else
69- Success (rmdir p fs) .
149+ if path_exists_dec p fs then
150+ if is_directory_dec p fs then
151+ if is_empty_dir_dec p fs then
152+ if has_write_permission_dec (parent_path p) fs then
153+ if path_eq_dec p root_path then
154+ Error EACCES (* cannot remove root *)
155+ else
156+ Success (rmdir p fs)
157+ else Error EACCES
158+ else Error ENOTEMPTY
159+ else Error ENOTDIR
160+ else Error ENOENT .
70161
71162(** Safe create_file - returns error codes *)
72163Definition safe_create_file (p : Path ) (fs : Filesystem) : OperationResult Filesystem :=
73164 if path_exists_dec p fs then
74165 Error EEXIST
75- else if negb (path_exists_dec (parent_path p) fs) then
76- Error ENOENT
77- else if negb (is_directory_dec (parent_path p) fs) then
78- Error ENOTDIR
79- else if negb (has_write_permission_dec (parent_path p) fs) then
80- Error EACCES
81- else
82- Success (create_file p fs).
166+ else if path_exists_dec (parent_path p) fs then
167+ if is_directory_dec (parent_path p) fs then
168+ if has_write_permission_dec (parent_path p) fs then
169+ Success (create_file p fs)
170+ else Error EACCES
171+ else Error ENOTDIR
172+ else Error ENOENT.
83173
84174(** Safe delete_file - returns error codes *)
85175Definition safe_delete_file (p : Path ) (fs : Filesystem) : OperationResult Filesystem :=
86- if negb (path_exists_dec p fs) then
87- Error ENOENT
88- else if negb (is_file_dec p fs) then
89- Error EISDIR
90- else if negb (has_write_permission_dec (parent_path p) fs) then
91- Error EACCES
92- else
93- Success (delete_file p fs).
94-
95- (** * Decision Procedures (axiomatized for now) *)
96-
97- Axiom path_exists_dec : forall p fs, {path_exists p fs} + {~ path_exists p fs}.
98- Axiom is_directory_dec : forall p fs, {is_directory p fs} + {~ is_directory p fs}.
99- Axiom is_file_dec : forall p fs, {is_file p fs} + {~ is_file p fs}.
100- Axiom has_write_permission_dec : forall p fs, {has_write_permission p fs} + {~ has_write_permission p fs}.
101- Axiom is_empty_dir_dec : forall p fs, {is_empty_dir p fs} + {~ is_empty_dir p fs}.
102- Axiom path_eq_dec : forall p1 p2 : Path, {p1 = p2} + {p1 <> p2}.
176+ if path_exists_dec p fs then
177+ if is_file_dec p fs then
178+ if has_write_permission_dec (parent_path p) fs then
179+ Success (delete_file p fs)
180+ else Error EACCES
181+ else Error EISDIR
182+ else Error ENOENT.
103183
104184(** * Error Handling Correctness Theorems *)
105185
@@ -113,7 +193,6 @@ Proof.
113193 - (* -> *)
114194 unfold safe_mkdir.
115195 intros H.
116- (* Check each condition *)
117196 destruct (path_exists_dec p fs); [discriminate|].
118197 destruct (path_exists_dec (parent_path p) fs); [|discriminate].
119198 destruct (is_directory_dec (parent_path p) fs); [|discriminate].
@@ -193,7 +272,6 @@ Proof.
193272 intros p fs Hnoexist Hnoparent.
194273 unfold safe_mkdir.
195274 destruct (path_exists_dec p fs); [contradiction|].
196- simpl.
197275 unfold parent_exists in Hnoparent.
198276 destruct (path_exists_dec (parent_path p) fs); [contradiction|].
199277 reflexivity.
@@ -219,7 +297,6 @@ Proof.
219297 intros p fs Hexists Hnotdir.
220298 unfold safe_rmdir.
221299 destruct (path_exists_dec p fs); [|contradiction].
222- simpl.
223300 destruct (is_directory_dec p fs); [contradiction|].
224301 reflexivity.
225302Qed .
@@ -233,9 +310,7 @@ Proof.
233310 intros p fs Hisdir Hnotempty.
234311 unfold safe_rmdir.
235312 destruct (path_exists_dec p fs).
236- - simpl.
237- destruct (is_directory_dec p fs); [|contradiction].
238- simpl.
313+ - destruct (is_directory_dec p fs); [|contradiction].
239314 destruct (is_empty_dir_dec p fs); [contradiction|].
240315 reflexivity.
241316 - exfalso.
276351 ✓ Correctness: Success iff preconditions hold
277352 ✓ Error code correctness: Each error matches specific violation
278353 ✓ Reversibility preserved under error handling
354+ ✓ 5/6 decision procedures proved constructively (Lemma, not Axiom)
355+ ✗ is_empty_dir_dec: justified axiom, requires finite-map Filesystem for proof
279356
280357 This enables:
281358 - Realistic implementation with proper error reporting
0 commit comments