Lecture notes 20210428 Function Call
Require Import PL.Imp.
Require Import Coq.ZArith.ZArith.
Require Import Coq.Lists.List.
Import ListNotations.
Local Open Scope Z.
Require Import Coq.ZArith.ZArith.
Require Import Coq.Lists.List.
Import ListNotations.
Local Open Scope Z.
Definition glob_var := nat.
Definition local_var := nat.
Definition func := nat.
We distinguish local variables and global variables.
Inductive aexp : Type :=
| ANum (n : Z)
| AIdGlob (X : glob_var) (* <-- new *)
| AIdLocal (X : local_var) (* <-- new *)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp).
Inductive bexp : Type :=
| BTrue
| BFalse
| BEq (a1 a2 : aexp)
| BLe (a1 a2 : aexp)
| BNot (b : bexp)
| BAnd (b1 b2 : bexp).
| ANum (n : Z)
| AIdGlob (X : glob_var) (* <-- new *)
| AIdLocal (X : local_var) (* <-- new *)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp).
Inductive bexp : Type :=
| BTrue
| BFalse
| BEq (a1 a2 : aexp)
| BLe (a1 a2 : aexp)
| BNot (b : bexp)
| BAnd (b1 b2 : bexp).
We also have assignment for local variables and for global variables. For
function calls, we consider a very simple setting: no function arguments and
no return value.
Inductive com : Type :=
| CSkip
| CAssGlob (X: glob_var) (a : aexp) (* <-- new *)
| CAssLocal (X: local_var) (a : aexp) (* <-- new *)
| CCall (F: func) (* <-- new *)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com).
Definition prog: Type := list (func * com).
| CSkip
| CAssGlob (X: glob_var) (a : aexp) (* <-- new *)
| CAssLocal (X: local_var) (a : aexp) (* <-- new *)
| CCall (F: func) (* <-- new *)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com).
Definition prog: Type := list (func * com).
Program states are local variables' values and global variables' values.
Definition local_state: Type := local_var -> Z.
Definition glob_state: Type := glob_var -> Z.
Record state: Type := {
glob_of_state: glob_state;
local_of_state: local_state;
}.
Definition glob_state: Type := glob_var -> Z.
Record state: Type := {
glob_of_state: glob_state;
local_of_state: local_state;
}.
Module Denotation_With_Call.
Although denotational semantics of expressions are similar to our original
setting, we still need to redefine them since the syntax is changed.
Definition query_var_local (X: local_var) : state -> Z :=
fun st ⇒ (local_of_state st) X.
Definition query_var_glob (X: glob_var) : state -> Z :=
fun st ⇒ (glob_of_state st) X.
Fixpoint aeval (a : aexp) : state -> Z :=
match a with
| ANum n ⇒ constant_func n
| AIdLocal X ⇒ query_var_local X
| AIdGlob X ⇒ query_var_glob X
| APlus a1 a2 ⇒ (aeval a1 + aeval a2)%Func
| AMinus a1 a2 ⇒ (aeval a1 - aeval a2)%Func
| AMult a1 a2 ⇒ (aeval a1 * aeval a2)%Func
end.
Fixpoint beval (b : bexp) : state -> Prop :=
match b with
| BTrue ⇒ Sets.full
| BFalse ⇒ Sets.empty
| BEq a1 a2 ⇒ Func.test_eq (aeval a1) (aeval a2)
| BLe a1 a2 ⇒ Func.test_le (aeval a1) (aeval a2)
| BNot b1 ⇒ Sets.complement (beval b1)
| BAnd b1 b2 ⇒ Sets.intersect (beval b1 ) (beval b2)
end.
fun st ⇒ (local_of_state st) X.
Definition query_var_glob (X: glob_var) : state -> Z :=
fun st ⇒ (glob_of_state st) X.
Fixpoint aeval (a : aexp) : state -> Z :=
match a with
| ANum n ⇒ constant_func n
| AIdLocal X ⇒ query_var_local X
| AIdGlob X ⇒ query_var_glob X
| APlus a1 a2 ⇒ (aeval a1 + aeval a2)%Func
| AMinus a1 a2 ⇒ (aeval a1 - aeval a2)%Func
| AMult a1 a2 ⇒ (aeval a1 * aeval a2)%Func
end.
Fixpoint beval (b : bexp) : state -> Prop :=
match b with
| BTrue ⇒ Sets.full
| BFalse ⇒ Sets.empty
| BEq a1 a2 ⇒ Func.test_eq (aeval a1) (aeval a2)
| BLe a1 a2 ⇒ Func.test_le (aeval a1) (aeval a2)
| BNot b1 ⇒ Sets.complement (beval b1)
| BAnd b1 b2 ⇒ Sets.intersect (beval b1 ) (beval b2)
end.
Most definitions in com's denotations are not interesting.
Definition if_sem
(b: bexp)
(then_branch else_branch: state -> state -> Prop)
: state -> state -> Prop
:=
BinRel.union
(BinRel.concat (BinRel.test_rel (beval b)) then_branch)
(BinRel.concat (BinRel.test_rel (beval (BNot b))) else_branch).
Fixpoint iter_loop_body (b: bexp)
(loop_body: state -> state -> Prop)
(n: nat): state -> state -> Prop :=
match n with
| O ⇒
BinRel.test_rel (beval (BNot b))
| S n' ⇒
BinRel.concat
(BinRel.test_rel (beval b))
(BinRel.concat
loop_body
(iter_loop_body b loop_body n'))
end.
Definition loop_sem (b: bexp) (loop_body: state -> state -> Prop):
state -> state -> Prop :=
BinRel.omega_union (iter_loop_body b loop_body).
(b: bexp)
(then_branch else_branch: state -> state -> Prop)
: state -> state -> Prop
:=
BinRel.union
(BinRel.concat (BinRel.test_rel (beval b)) then_branch)
(BinRel.concat (BinRel.test_rel (beval (BNot b))) else_branch).
Fixpoint iter_loop_body (b: bexp)
(loop_body: state -> state -> Prop)
(n: nat): state -> state -> Prop :=
match n with
| O ⇒
BinRel.test_rel (beval (BNot b))
| S n' ⇒
BinRel.concat
(BinRel.test_rel (beval b))
(BinRel.concat
loop_body
(iter_loop_body b loop_body n'))
end.
Definition loop_sem (b: bexp) (loop_body: state -> state -> Prop):
state -> state -> Prop :=
BinRel.omega_union (iter_loop_body b loop_body).
The only interesting case is function call. But obviously, function calls'
behaviour depends on callee function (被调用函数) behavior, i.e. the
denotation of CCall f should be computed based on f's function body's
denotation.
Section CEVAL.
Here, we assume that callee is all callee functions' denotations.
Variable callee: func -> glob_state -> glob_state -> Prop.
Fixpoint ceval (c: com): state -> state -> Prop :=
match c with
| CSkip ⇒ BinRel.id
| CAssLocal X E ⇒
fun st1 st2 ⇒
glob_of_state st2 = glob_of_state st1 ∧
local_of_state st2 X = aeval E st1 ∧
∀Y, X ≠ Y -> local_of_state st1 Y = local_of_state st2 Y
| CAssGlob X E ⇒
fun st1 st2 ⇒
local_of_state st2 = local_of_state st1 ∧
glob_of_state st2 X = aeval E st1 ∧
∀Y, X ≠ Y -> glob_of_state st1 Y = glob_of_state st2 Y
| CCall F ⇒
fun st1 st2 ⇒
local_of_state st1 = local_of_state st2 ∧
callee F (glob_of_state st1) (glob_of_state st2)
| CSeq c1 c2 ⇒ BinRel.concat (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
end.
End CEVAL.
Fixpoint ceval (c: com): state -> state -> Prop :=
match c with
| CSkip ⇒ BinRel.id
| CAssLocal X E ⇒
fun st1 st2 ⇒
glob_of_state st2 = glob_of_state st1 ∧
local_of_state st2 X = aeval E st1 ∧
∀Y, X ≠ Y -> local_of_state st1 Y = local_of_state st2 Y
| CAssGlob X E ⇒
fun st1 st2 ⇒
local_of_state st2 = local_of_state st1 ∧
glob_of_state st2 X = aeval E st1 ∧
∀Y, X ≠ Y -> glob_of_state st1 Y = glob_of_state st2 Y
| CCall F ⇒
fun st1 st2 ⇒
local_of_state st1 = local_of_state st2 ∧
callee F (glob_of_state st1) (glob_of_state st2)
| CSeq c1 c2 ⇒ BinRel.concat (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
end.
End CEVAL.
What is the type of ceval? It is NOT simply a function from com to a
binary relation. The variable callee is also one of its arguments.
Check ceval.
You may already find that callee functions' behavior also depends on their
own inside calls. Thus, we define iter_func_body to be program behavior
with a limit depth of call.
Fixpoint iter_func_body (n: nat) (p: prog):
func -> glob_state -> glob_state -> Prop :=
match n with
| O ⇒ fun _ _ _ ⇒ False
| S n' ⇒ fun f gst1 gst2 ⇒
∃c st1 st2,
List.In (f, c) p ∧
(∀X, local_of_state st1 X = 0%Z) ∧
gst1 = glob_of_state st1 ∧
gst2 = glob_of_state st2 ∧
ceval (iter_func_body n' p) c st1 st2
end.
Definition feval (p: prog): func -> glob_state -> glob_state -> Prop :=
fun f gst1 gst2 ⇒
∃n, iter_func_body n p f gst1 gst2.
End Denotation_With_Call.
func -> glob_state -> glob_state -> Prop :=
match n with
| O ⇒ fun _ _ _ ⇒ False
| S n' ⇒ fun f gst1 gst2 ⇒
∃c st1 st2,
List.In (f, c) p ∧
(∀X, local_of_state st1 X = 0%Z) ∧
gst1 = glob_of_state st1 ∧
gst2 = glob_of_state st2 ∧
ceval (iter_func_body n' p) c st1 st2
end.
Definition feval (p: prog): func -> glob_state -> glob_state -> Prop :=
fun f gst1 gst2 ⇒
∃n, iter_func_body n p f gst1 gst2.
End Denotation_With_Call.
Module Small_Step_Semantics_Calling_Stack.
Now we define a calling-stack-based small step semantics. Here, every
element in a calling stack describes local stack.
Inductive aexp_halt: aexp -> Prop :=
| AH_num : ∀n, aexp_halt (ANum n).
Inductive astep : state -> aexp -> aexp -> Prop :=
| AS_IdLocal : ∀st X,
astep st
(AIdLocal X) (ANum (local_of_state st X))
| AS_IdGlobal : ∀st X,
astep st
(AIdGlob X) (ANum (glob_of_state st X))
| AS_Plus1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(APlus a1 a2) (APlus a1' a2)
| AS_Plus2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(APlus a1 a2) (APlus a1 a2')
| AS_Plus : ∀st n1 n2,
astep st
(APlus (ANum n1) (ANum n2)) (ANum (n1 + n2))
| AS_Minus1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(AMinus a1 a2) (AMinus a1' a2)
| AS_Minus2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(AMinus a1 a2) (AMinus a1 a2')
| AS_Minus : ∀st n1 n2,
astep st
(AMinus (ANum n1) (ANum n2)) (ANum (n1 - n2))
| AS_Mult1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(AMult a1 a2) (AMult a1' a2)
| AS_Mult2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(AMult a1 a2) (AMult a1 a2')
| AS_Mult : ∀st n1 n2,
astep st
(AMult (ANum n1) (ANum n2)) (ANum (n1 * n2)).
Inductive bexp_halt: bexp -> Prop :=
| BH_True : bexp_halt BTrue
| BH_False : bexp_halt BFalse.
Inductive bstep : state -> bexp -> bexp -> Prop :=
| BS_Eq1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
bstep st
(BEq a1 a2) (BEq a1' a2)
| BS_Eq2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
bstep st
(BEq a1 a2) (BEq a1 a2')
| BS_Eq_True : ∀st n1 n2,
n1 = n2 ->
bstep st
(BEq (ANum n1) (ANum n2)) BTrue
| BS_Eq_False : ∀st n1 n2,
n1 ≠ n2 ->
bstep st
(BEq (ANum n1) (ANum n2)) BFalse
| BS_Le1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
bstep st
(BLe a1 a2) (BLe a1' a2)
| BS_Le2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
bstep st
(BLe a1 a2) (BLe a1 a2')
| BS_Le_True : ∀st n1 n2,
n1 ≤ n2 ->
bstep st
(BLe (ANum n1) (ANum n2)) BTrue
| BS_Le_False : ∀st n1 n2,
n1 > n2 ->
bstep st
(BLe (ANum n1) (ANum n2)) BFalse
| BS_NotStep : ∀st b1 b1',
bstep st
b1 b1' ->
bstep st
(BNot b1) (BNot b1')
| BS_NotTrue : ∀st,
bstep st
(BNot BTrue) BFalse
| BS_NotFalse : ∀st,
bstep st
(BNot BFalse) BTrue
| BS_AndStep : ∀st b1 b1' b2,
bstep st
b1 b1' ->
bstep st
(BAnd b1 b2) (BAnd b1' b2)
| BS_AndTrue : ∀st b,
bstep st
(BAnd BTrue b) b
| BS_AndFalse : ∀st b,
bstep st
(BAnd BFalse b) BFalse.
Definition cstack: Type := list (com * local_state).
Inductive start_with_call: com -> func -> com -> Prop :=
| SWC_Call: ∀f, start_with_call (CCall f) f CSkip
| SWC_Seq: ∀c1 f c12 c2,
start_with_call c1 f c12 ->
start_with_call (CSeq c1 c2) f (CSeq c12 c2).
Section CSTEP.
Variable p: prog.
Inductive cstep : (com * cstack * state) -> (com * cstack * state) -> Prop :=
| CS_AssLocalStep : ∀st s X a a',
astep st a a' ->
cstep
(CAssLocal X a, s, st)
(CAssLocal X a', s, st)
| CS_AssLocal : ∀st1 st2 s X n,
glob_of_state st2 = glob_of_state st1 ->
local_of_state st2 X = n ->
(∀Y, X ≠ Y -> local_of_state st1 Y = local_of_state st2 Y) ->
cstep
(CAssLocal X (ANum n), s, st1)
(CSkip, s, st2)
| CS_AssGlobStep : ∀st s X a a',
astep st a a' ->
cstep
(CAssGlob X a, s, st)
(CAssGlob X a', s, st)
| CS_AssGlob : ∀st1 st2 s X n,
local_of_state st2 = local_of_state st1 ->
glob_of_state st2 X = n ->
(∀Y, X ≠ Y -> glob_of_state st1 Y = glob_of_state st2 Y) ->
cstep
(CAssGlob X (ANum n), s, st1)
(CSkip, s, st2)
| CS_SeqStep : ∀st s c1 c1' st' c2,
cstep
(c1, s, st)
(c1', s, st') ->
cstep
(CSeq c1 c2, s, st)
(CSeq c1' c2, s, st')
| CS_Seq : ∀st s c2,
cstep
(CSeq CSkip c2, s, st)
(c2, s, st)
| CS_IfStep : ∀st s b b' c1 c2,
bstep st b b' ->
cstep
(CIf b c1 c2, s, st)
(CIf b' c1 c2, s, st)
| CS_IfTrue : ∀st s c1 c2,
cstep
(CIf BTrue c1 c2, s, st)
(c1, s, st)
| CS_IfFalse : ∀st s c1 c2,
cstep
(CIf BFalse c1 c2, s, st)
(c2, s, st)
| CS_While : ∀st s b c1,
cstep
(CWhile b c1, s, st)
(CIf b (CSeq c1 (CWhile b c1)) CSkip, s, st)
| CS_Call : ∀st1 st2 s c1 f body c2, (* <--- new *)
start_with_call c1 f c2 ->
glob_of_state st2 = glob_of_state st1 ->
(∀X, local_of_state st2 X = 0%Z) ->
List.In (f, body) p ->
cstep
(c1, s, st1)
(body, (c2, local_of_state st1) :: s, st2)
| CS_Skip : ∀st1 st2 c l s , (* <--- new *)
glob_of_state st2 = glob_of_state st1 ->
local_of_state st2 = l ->
cstep
(CSkip, (c, l) :: s, st1)
(c, s, st2).
End CSTEP.
End Small_Step_Semantics_Calling_Stack.
| AH_num : ∀n, aexp_halt (ANum n).
Inductive astep : state -> aexp -> aexp -> Prop :=
| AS_IdLocal : ∀st X,
astep st
(AIdLocal X) (ANum (local_of_state st X))
| AS_IdGlobal : ∀st X,
astep st
(AIdGlob X) (ANum (glob_of_state st X))
| AS_Plus1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(APlus a1 a2) (APlus a1' a2)
| AS_Plus2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(APlus a1 a2) (APlus a1 a2')
| AS_Plus : ∀st n1 n2,
astep st
(APlus (ANum n1) (ANum n2)) (ANum (n1 + n2))
| AS_Minus1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(AMinus a1 a2) (AMinus a1' a2)
| AS_Minus2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(AMinus a1 a2) (AMinus a1 a2')
| AS_Minus : ∀st n1 n2,
astep st
(AMinus (ANum n1) (ANum n2)) (ANum (n1 - n2))
| AS_Mult1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
astep st
(AMult a1 a2) (AMult a1' a2)
| AS_Mult2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
astep st
(AMult a1 a2) (AMult a1 a2')
| AS_Mult : ∀st n1 n2,
astep st
(AMult (ANum n1) (ANum n2)) (ANum (n1 * n2)).
Inductive bexp_halt: bexp -> Prop :=
| BH_True : bexp_halt BTrue
| BH_False : bexp_halt BFalse.
Inductive bstep : state -> bexp -> bexp -> Prop :=
| BS_Eq1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
bstep st
(BEq a1 a2) (BEq a1' a2)
| BS_Eq2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
bstep st
(BEq a1 a2) (BEq a1 a2')
| BS_Eq_True : ∀st n1 n2,
n1 = n2 ->
bstep st
(BEq (ANum n1) (ANum n2)) BTrue
| BS_Eq_False : ∀st n1 n2,
n1 ≠ n2 ->
bstep st
(BEq (ANum n1) (ANum n2)) BFalse
| BS_Le1 : ∀st a1 a1' a2,
astep st
a1 a1' ->
bstep st
(BLe a1 a2) (BLe a1' a2)
| BS_Le2 : ∀st a1 a2 a2',
aexp_halt a1 ->
astep st
a2 a2' ->
bstep st
(BLe a1 a2) (BLe a1 a2')
| BS_Le_True : ∀st n1 n2,
n1 ≤ n2 ->
bstep st
(BLe (ANum n1) (ANum n2)) BTrue
| BS_Le_False : ∀st n1 n2,
n1 > n2 ->
bstep st
(BLe (ANum n1) (ANum n2)) BFalse
| BS_NotStep : ∀st b1 b1',
bstep st
b1 b1' ->
bstep st
(BNot b1) (BNot b1')
| BS_NotTrue : ∀st,
bstep st
(BNot BTrue) BFalse
| BS_NotFalse : ∀st,
bstep st
(BNot BFalse) BTrue
| BS_AndStep : ∀st b1 b1' b2,
bstep st
b1 b1' ->
bstep st
(BAnd b1 b2) (BAnd b1' b2)
| BS_AndTrue : ∀st b,
bstep st
(BAnd BTrue b) b
| BS_AndFalse : ∀st b,
bstep st
(BAnd BFalse b) BFalse.
Definition cstack: Type := list (com * local_state).
Inductive start_with_call: com -> func -> com -> Prop :=
| SWC_Call: ∀f, start_with_call (CCall f) f CSkip
| SWC_Seq: ∀c1 f c12 c2,
start_with_call c1 f c12 ->
start_with_call (CSeq c1 c2) f (CSeq c12 c2).
Section CSTEP.
Variable p: prog.
Inductive cstep : (com * cstack * state) -> (com * cstack * state) -> Prop :=
| CS_AssLocalStep : ∀st s X a a',
astep st a a' ->
cstep
(CAssLocal X a, s, st)
(CAssLocal X a', s, st)
| CS_AssLocal : ∀st1 st2 s X n,
glob_of_state st2 = glob_of_state st1 ->
local_of_state st2 X = n ->
(∀Y, X ≠ Y -> local_of_state st1 Y = local_of_state st2 Y) ->
cstep
(CAssLocal X (ANum n), s, st1)
(CSkip, s, st2)
| CS_AssGlobStep : ∀st s X a a',
astep st a a' ->
cstep
(CAssGlob X a, s, st)
(CAssGlob X a', s, st)
| CS_AssGlob : ∀st1 st2 s X n,
local_of_state st2 = local_of_state st1 ->
glob_of_state st2 X = n ->
(∀Y, X ≠ Y -> glob_of_state st1 Y = glob_of_state st2 Y) ->
cstep
(CAssGlob X (ANum n), s, st1)
(CSkip, s, st2)
| CS_SeqStep : ∀st s c1 c1' st' c2,
cstep
(c1, s, st)
(c1', s, st') ->
cstep
(CSeq c1 c2, s, st)
(CSeq c1' c2, s, st')
| CS_Seq : ∀st s c2,
cstep
(CSeq CSkip c2, s, st)
(c2, s, st)
| CS_IfStep : ∀st s b b' c1 c2,
bstep st b b' ->
cstep
(CIf b c1 c2, s, st)
(CIf b' c1 c2, s, st)
| CS_IfTrue : ∀st s c1 c2,
cstep
(CIf BTrue c1 c2, s, st)
(c1, s, st)
| CS_IfFalse : ∀st s c1 c2,
cstep
(CIf BFalse c1 c2, s, st)
(c2, s, st)
| CS_While : ∀st s b c1,
cstep
(CWhile b c1, s, st)
(CIf b (CSeq c1 (CWhile b c1)) CSkip, s, st)
| CS_Call : ∀st1 st2 s c1 f body c2, (* <--- new *)
start_with_call c1 f c2 ->
glob_of_state st2 = glob_of_state st1 ->
(∀X, local_of_state st2 X = 0%Z) ->
List.In (f, body) p ->
cstep
(c1, s, st1)
(body, (c2, local_of_state st1) :: s, st2)
| CS_Skip : ∀st1 st2 c l s , (* <--- new *)
glob_of_state st2 = glob_of_state st1 ->
local_of_state st2 = l ->
cstep
(CSkip, (c, l) :: s, st1)
(c, s, st2).
End CSTEP.
End Small_Step_Semantics_Calling_Stack.
Hoare logic
- proving commands correct based on assumption of callees;
- establishing these assumption based on themselves.
Gamma ⊢ {{ P }} c1 {{ Q }} ->
Gamma ⊢ {{ Q }} c2 {{ R }} ->
Gamma ⊢ {{ P }} c1 ;; c2 {{ R }} .
Gamma ⊢ {{ Q }} c2 {{ R }} ->
Gamma ⊢ {{ P }} c1 ;; c2 {{ R }} .
Gamma ⊢ {{ P AND [[b]] }} c1 {{ Q }} ->
Gamma ⊢ {{ P AND NOT [[b]] }} c2 {{ Q }} ->
Gamma ⊢ {{ P }} If b Then c1 Else c2 EndIf {{ Q }} .
Gamma ⊢ {{ P AND NOT [[b]] }} c2 {{ Q }} ->
Gamma ⊢ {{ P }} If b Then c1 Else c2 EndIf {{ Q }} .
Gamma ⊢ {{ P AND [[b]] }} c1 {{ P }} ->
Gamma ⊢ {{ P }} While b Do c1 EndWhile {{ P AND NOT [[b]] }} .
Gamma ⊢ {{ P }} While b Do c1 EndWhile {{ P AND NOT [[b]] }} .
(P, f, Q) In Gamma ->
Local (L) ->
Gamma ⊢ {{ P AND L }} Call f {{ Q AND L }} .
Local (L) ->
Gamma ⊢ {{ P AND L }} Call f {{ Q AND L }} .
Gamma ⊢ {{ P }} c {{ Q }}
(* 2021-05-07 20:38 *)