Skip to content

Conversation

@michaelmcinerney
Copy link
Contributor

No description provided.

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
@michaelmcinerney
Copy link
Contributor Author

Advice for reviewing: since there were some lemmas moved to the new file CorresWhile and then some of those lemmas were updated, it would be best to review the commits which touch that file individually. Otherwise, things are probably small and separate enough for it to be reviewed all together. I think the commit messages should mostly be fine, though I will change the tag I have in one from lib/monads to monads.

@lsf37
Copy link
Member

lsf37 commented Feb 2, 2026

Before I start reviewing the commits, one thing that stands out is that there are too many of them that can/should be grouped. E.g. most of the commits that don't have a body description could be bullet points in a larger commit that groups them. I haven't yet looked closely enough to suggest which ones should be grouped, but I'm sure there are a few themes of things that go together.

Comment on lines +2662 to +2666
lemma list_length_wf_helper:
"wf {((r :: 'b list, s :: 'a), (r', s')). length r < length r'}"
apply (insert wf_inv_image[where r="{(m, n). m < n}" and f="\<lambda>(r :: 'b list, s :: 'a). length r"])
apply (fastforce intro: wf simp: inv_image_def wf_def)
done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This holds for any type that supports the size function, in particular all datatypes (length is an abbreviation for size on lists). I'd also suggest a different name, maybe:

lemma wf_fst_size:
  "wf {((r :: 'b :: size, s :: 'a), (r', s')). size r < size r'}"
  apply (insert wf_inv_image[where r="{(m, n). m < n}" and f="λ(r :: 'b, s :: 'a). size r"])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lemma was just moved from Refine, so I was hesitant to change the name, but I can do that and make it support size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the punishment for good deeds is swift sometimes :-). If it's old, then let's maybe go with a nice name for the lemma and a lemmas old_name = new_name statement afterwards with a FIXME for cleanup later. It will still be directly applicable to length, so nothing else should need to change.

@michaelmcinerney
Copy link
Contributor Author

I'm not sure of much that can be grouped. The best that I can think of is to put the noop lemmas for monadic_rewrite into one commit, and then to put heap_ls_cong and sym_heap_cong into one commit, but I don't even like those ideas too much. I could squash the commit that generalises corres_whileLoop_abs_ret into the commit about the new corres rules for mapM, but this is a generalisation that is independently useful. I could go to the other extreme and put everything that doesn't have a body description into one giant commit that says "here are some useful rules I wrote" but that seems much worse than having too many commits.

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Allows the concrete guard to refer to the abstract initial
value/return value.

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
This allows for a more general relation to be used

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Introduce the type headEndList to model
doubly-linked lists, and use this type within the
definition of list_queue_relation and related
definitions.

Definitions and lemmas about list_queue_relation are
now in Lib.

Compatibility with the Haskell type TcbQueue is provided
by a series of abbreviations.

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
This adds the rule monadic_rewrite_symb_exec_l'', which
generalises monadic_rewrite_symb_exec_l' by allowing a
side-condition when showing that the symbolically
executed function preserves all properties of the state.
The previous rule is obtained via a lemmas statement.

Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
@michaelmcinerney michaelmcinerney force-pushed the michaelm-rules_for_Lib_Jan26 branch from 70cd9c9 to c03b79f Compare February 3, 2026 02:48
@michaelmcinerney
Copy link
Contributor Author

I have grouped a few things together to try to help reduce the number of commits, but it is still a lot. These did arise over more than six months of intensive work on the IPC queues, covering AInvs, Refine, and CRefine. I can't think of any further reduction unless we wanted commits of unrelated rules. I have kept the commit that simply moves things from ExtraCorres to the new file CorresWhile because it seems like the better way to present the history and for reviewing, but I could incorporate that into the commit for the new corres rule for mapM_x/whileLoop if that is preferable.

lemma set_object_noop_rewrite:
"monadic_rewrite F E (ko_at obj ptr) (set_object ptr obj) (return ())"
apply (clarsimp simp: set_object_def)
apply monadic_rewrite_symb_exec_l+
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can supply a method to monadic_rewrite_symb_exec_l, in this case <wpsimp wp: get_object_wp set_object_wp> should work, which would save you the line later and the need for a + hopefully.

"monadic_rewrite F E (\<lambda>s. s = s') (put s') (return ())"
by (clarsimp simp: monadic_rewrite_def put_def return_def)


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding newlines, while it looks other text bits don't have them (same after monadic_rewrite_noop) above. Might be that an argument to have sections instead makes sense, but I'm not sure if this file does that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does sort of have sections that are begun with text lines. I'm not sure whether that qualifies as something where we'd like the two newlines before a new section. I followed the same approach for the RCorres file, actually, so maybe we should decide

lemma corres_underlying_return_dc_rhs:
"corres_underlying srel nf nf' dc P P' f g
\<longleftrightarrow> corres_underlying srel nf nf' dc P P' f (do g; return () od)"
by (force simp: corres_underlying_def bind_def return_def)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule seems unusual, can you say why the add_noop_lhs rules are not good enough here? Is it due to having to go forwards in some proof? But then why the equivalence? Also, the name doesn't fit with the other rules, all the other ones refer to adding "noop" for return, which is certainly a choice, but good to be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The noop_rhs rules will assume that the function to which we'd like to add the return will itself have unit as the return type. Here I'm wanting something more general. The fact that we have dc as the return relation means that we are free to disregard the value that's returned.

apply (rule no_fail_pre)
apply (fastforce intro: no_fail_grab_asm)
apply fastforce
done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can one-line this, or two-line this (assuming semicolon-unfriendliness here):

by (rule no_fail_pre)
   (fastforce intro: no_fail_grab_asm)+

apply clarsimp
apply (frule_tac ls1=ls and t1="hd ls" and s=s and t'1=t and Q1="\<lambda>val. val = nexts_of s t"
in post_by_hoare[OF body_heap_upd])
apply fastforce
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can combine these fastforces into one and semicolon it if you want to reduce size, but this is also ok

corres_whileLoop_abs_ret[where P="\<lambda>_. P" for P, where P'="\<lambda>_. P'" for P', simplified]
corres_whileLoop_abs_ret[where P="\<lambda>_. P" for P, where P'="\<lambda>_ _. P'" for P', simplified]

lemma corres_mapM_x_whileLoop_strong:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at this and the non-strong version, it takes a fair bit of cognitive power to figure out the difference, maybe a comment to point future readers to would help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can do that.

apply (wpsimp wp: mapM_gets_the_wp simp: comp_def)
by (simp add: map_equality_iff)

lemma mapM_gets_the_sp':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a non-prime version of this somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, there is. I think it's currently unused (at least once the IPC queues work is merged), so I could remove it (maybe later).

"\<lbrace>\<lambda>s. \<forall>vs. ((\<forall>t \<in> set ts. \<exists>val. f t s = Some val) \<and> vs = map (\<lambda>t. the (f t s)) ts) \<longrightarrow> P vs s\<rbrace>
mapM (\<lambda>t. gets_the (f t)) ts
\<lbrace>P\<rbrace>"
unfolding comp_def
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it; the mapM_gets_the_wp with comp_def expanded only ends up having a different precondition. This means that either this precondition implies that one, or vice versa, or they're equivalent. So I'd expect one of these to become a one-liner weakening of the other, without a need for duplicating the induction proof.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I can get the original version as a one-line proof from this new version (the other way around doesn't work). Thanks


lemma in_inv_by_hoareD':
"\<lbrakk> \<And>P. \<lbrace>P and Q\<rbrace> f \<lbrace>\<lambda>_ . P\<rbrace>; (x,s') \<in> fst (f s); Q s \<rbrakk> \<Longrightarrow> s' = s"
by (auto simp: valid_def) blast
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make any sense to make the in_inv_by_hoareD an instantiation of this one via lemmas? I'm ok keeping it like this in this case, as it's not much longer and shows the lemma

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did think about that, but wasn't sure whether I should mess with ancient rules like that one. I can try that out if people have a preference for the lemmas statement.

apply (erule whileLoop_terminates.induct)
apply (fastforce intro: whileLoop_terminates.intros)
apply (rename_tac r s)
apply (case_tac "C r s")
Copy link
Member

@Xaphiosis Xaphiosis Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

; fastforce intro: whileLoop_terminates.intros ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does work. The old proof is slightly more "informative" in that it tells you which of those rules is used for which case. I sort of like that for these Lib rules, but I can definitely go with this.

lemma hoare_ex_with_conj:
"\<lbrakk>\<lbrace>P\<rbrace> f \<lbrace>\<lambda>rv s. \<exists>x. Q x rv s\<rbrace>; \<lbrace>P\<rbrace> f \<lbrace>\<lambda>rv s. \<forall>x. Q x rv s \<longrightarrow> R x rv s\<rbrace>\<rbrakk>
\<Longrightarrow> \<lbrace>P\<rbrace> f \<lbrace>\<lambda>rv s. \<exists>x. Q x rv s \<and> R x rv s\<rbrace>"
by (fastforce simp: valid_def)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this name is strange... it's similar to the idea behind the rule context_conjI but under an exists... I'd suggest hoeare_ex_context_conj or something like that


lemma split_list':
"x \<in> set xs \<Longrightarrow> \<exists>ys zs. xs = ys @ [x] @ zs"
by (fastforce dest: split_list)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, surprised this isn't in HOL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's split_list, which is x ∈ set xs ⟹ ∃ys zs. xs = ys @ x # zs, but I wanted this version for some godforsaken reason I have now forgotten (but which I can investigate if you'd like).

Copy link
Member

@Xaphiosis Xaphiosis Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine, but maybe throw in a (* split_list uses x # *) or something

Copy link
Member

@Xaphiosis Xaphiosis Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn, github won't let me type x space #

"queue"), for the specification of doubly-linked lists.\<close>

datatype 'a head_end_list =
HeadEndList (he_ls_head : "'a option") (he_ls_end : "'a option")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you guys already discuss naming of this? I've heard of queues before, and of doubly-linked lists, but have never heard of a "head end list". I think the comment is confusing me more than it helps; maybe it's just too contracted. We're defining a datatype here that only has two potential "pointers" in it, and then we're relating that concept to a queue, which is a kind of extraction of a list by following the pointers in memory? Is that right? If it is, then the name makes sense, but it's a lot to intuit.

Looking at list_queue_relation below, there's mappings of ('a ⇀ 'a), which I'm expecting are in practice projections on the heap that are heap pointers, so the key insight is that where normally we talk about lists with 'a as the member type of the list, here we're only really talking about indices that can be used for indexing something else to get an actual list? Did I interpret this correctly?

I'm getting the feeling a bit more documentation could help here. For example, checking if a head_end_list is empty only checks the head... which assumes we're always operating under an assumption of validity w.r.t. ... something? queue_end_valid takes another list, so that gets a bit confusing.

Any thoughts? I think the definitions are fine, but I think a little hand-holding for the reader would help. Especially if someone comes in from lists and is expecting the 'a to be the usual...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gerwin and I discussed the naming of this on the work channel on Mattermost. This particular data structure where 'a is machine_word is used in the Haskell and C and called TcbQueue/tcb_queue_t. I'm no whizz with data structures (at least of the PLT/type theory/CS kind) so I'm not sure of the history of the name tcb_queue or whatever, but Gerwin insisted that a queue is something completely different and so we cannot use "queue" here. I thought that a "list" would be something different from this head/end thing, but that's the name Gerwin wanted. I mentioned that we use "queue" for the bound variables and in list_queue_relation itself, as well as in the datatypes in the Haskell and C, but Gerwin said that it would be too much of a pain to change all that now.

You have the right idea. We can extract the actual list we want from the heap of next pointers by starting at the head of this head_end_list and the end of this extracted list should be the end of the head_end_list. The use of the schematic type variable in the definition of list_queue_relation is to make it just as general as heap_ls, so that we can have a nice library for doubly-linked lists. I have some abbreviations in the skel file somewhere where the type variable is made to be machine_word and I call it TcbQueue.

I can definitely add some more comments. I'd have to think about exactly what they would be, especially since there's some confusion over the terminology used. As far as the headEndListEmpty definition, this mirrors the sorts of checks that are performed in the Haskell/C, although I think if we were writing something like this in Isabelle, checking whether it's equal to the emptyHeadEndList would be "nicer". We always do have list_queue_relation when we work with these head/end lists, and because this is always used for refinement, we always do have an abstract list from the abstract function or abstract state somewhere so that we can write things like list_queue_relation or queue_end_valid with that abstract list.

@Xaphiosis
Copy link
Member

It's really good you moved these over, it's good to consolidate the branches and especially Lib.

As we discussed, I'd welcome the possibility of future commits being grouped together more (e.g. HeapList with HeapList, monadic rewrite with monadic rewrite) to aid in reviewing. I'll let Gerwin handle the commit consolidation since that's already going. That said, it is a collection of stuff from a long period of work, so a few more commits than the usual PR will be ok.

Minor commit nitpicks:

  • lib: add CorresWhile file for whileLoop rules : file -> theory
  • Introduce the type headEndList : type is head_end_list, constructor is HeadEndList (and h is in that case a typo)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants