-
Notifications
You must be signed in to change notification settings - Fork 116
Rules for Lib (January '26) #958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
|
Advice for reviewing: since there were some lemmas moved to the new file |
|
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. |
| 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 |
There was a problem hiding this comment.
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"])
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
I'm not sure of much that can be grouped. The best that I can think of is to put the noop lemmas for |
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>
70cd9c9 to
c03b79f
Compare
|
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 |
| 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+ |
There was a problem hiding this comment.
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) | ||
|
|
||
|
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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': |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
|
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:
|
No description provided.