Lecture notes 20190521
More Semantics 1 Control Flow
Require Import PL.Imp9.
The language
Inductive com : Type :=
| CSkip
| CAss (X: var) (a : aexp)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com)
| CBreak (* <--- new *)
| CCont (* <--- new *)
.
| CSkip
| CAss (X: var) (a : aexp)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com)
| CBreak (* <--- new *)
| CCont (* <--- new *)
.
In this lecture, we discover how to defines its Hoare logic, denotational
semantics and small step semantics.
Obviously, the functionalities of Break and Skip are different. But they
look the same if only considering their modification on program states: neither
of them modify program states. The difference between them is about determining
which command will be executed next. This is called control flow.
We start from denotational semantics.
Denotational Semantics
Module Denotation_With_ControlFlow.
Program's denotation is defined as a ternary relation. Specifically,
st1, ek, st2 belongs to the denotation of program c if and only if
executing c from st1 may terminate at state st2 with exit kind ek.
Inductive exit_kind: Type :=
| EK_Normal
| EK_Break
| EK_Cont.
Definition skip_sem: state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Normal.
Definition asgn_sem (X: var) (E: aexp): state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st2 X = aeval E st1 ∧
∀Y, X ≠ Y → st1 Y = st2 Y ∧
ek = EK_Normal.
| EK_Normal
| EK_Break
| EK_Cont.
Definition skip_sem: state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Normal.
Definition asgn_sem (X: var) (E: aexp): state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st2 X = aeval E st1 ∧
∀Y, X ≠ Y → st1 Y = st2 Y ∧
ek = EK_Normal.
Obviously, skip commands and assignment commands can only terminate normally.
Definition break_sem: state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Break.
Definition cont_sem: state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Cont.
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Break.
Definition cont_sem: state → exit_kind → state → Prop :=
fun st1 ek st2 ⇒
st1 = st2 ∧ ek = EK_Cont.
In contrast, CBreak and CCont will never terminate will normal exit.
Definition seq_sem (d1 d2: state → exit_kind → state → Prop)
: state → exit_kind → state → Prop
:=
fun st1 ek st3 ⇒
(∃st2, d1 st1 EK_Normal st2 ∧ d2 st2 ek st3) ∨
(d1 st1 ek st3 ∧ ek ≠ EK_Normal).
: state → exit_kind → state → Prop
:=
fun st1 ek st3 ⇒
(∃st2, d1 st1 EK_Normal st2 ∧ d2 st2 ek st3) ∨
(d1 st1 ek st3 ∧ ek ≠ EK_Normal).
For sequential composition, the second command will be executed if and only
if the first one terminates normally.
Definition if_sem (b: bexp) (d1 d2: state → exit_kind → state → Prop)
: state → exit_kind → state → Prop
:=
fun st1 ek st2 ⇒
(d1 st1 ek st2 ∧ beval b st1) ∨
(d2 st1 ek st2 ∧ ¬beval b st1).
: state → exit_kind → state → Prop
:=
fun st1 ek st2 ⇒
(d1 st1 ek st2 ∧ beval b st1) ∨
(d2 st1 ek st2 ∧ ¬beval b st1).
The semantics of if commands is trivial. Next, we define the semantics of
loops. Remember, a loop itself will never terminate by break or continue
although a loop body may break and terminates whole loop's execution.
Fixpoint iter_loop_body
(b: bexp)
(loop_body: state → exit_kind → state → Prop)
(n: nat)
: state → state → Prop
:=
match n with
| O ⇒
fun st1 st2 ⇒
st1 = st2 ∧ ¬beval b st1
| S n' ⇒
fun st1 st3 ⇒
((∃st2,
(loop_body) st1 EK_Normal st2 ∧
(iter_loop_body b loop_body n') st2 st3) ∨
(loop_body) st1 EK_Break st3 ∨
(∃st2,
(loop_body) st1 EK_Cont st2 ∧
(iter_loop_body b loop_body n') st2 st3)) ∧
beval b st1
end.
Definition loop_sem (b: bexp) (loop_body: state → exit_kind → state → Prop)
: state → exit_kind → state → Prop
:=
fun st1 ek st2 ⇒
∃n, (iter_loop_body b loop_body n) st1 st2 ∧ ek = EK_Normal.
Fixpoint ceval (c: com): state → exit_kind → state → Prop :=
match c with
| CSkip ⇒ skip_sem
| CAss X E ⇒ asgn_sem X E
| CSeq c1 c2 ⇒ seq_sem (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
| CBreak ⇒ break_sem
| CCont ⇒ cont_sem
end.
End Denotation_With_ControlFlow.
(b: bexp)
(loop_body: state → exit_kind → state → Prop)
(n: nat)
: state → state → Prop
:=
match n with
| O ⇒
fun st1 st2 ⇒
st1 = st2 ∧ ¬beval b st1
| S n' ⇒
fun st1 st3 ⇒
((∃st2,
(loop_body) st1 EK_Normal st2 ∧
(iter_loop_body b loop_body n') st2 st3) ∨
(loop_body) st1 EK_Break st3 ∨
(∃st2,
(loop_body) st1 EK_Cont st2 ∧
(iter_loop_body b loop_body n') st2 st3)) ∧
beval b st1
end.
Definition loop_sem (b: bexp) (loop_body: state → exit_kind → state → Prop)
: state → exit_kind → state → Prop
:=
fun st1 ek st2 ⇒
∃n, (iter_loop_body b loop_body n) st1 st2 ∧ ek = EK_Normal.
Fixpoint ceval (c: com): state → exit_kind → state → Prop :=
match c with
| CSkip ⇒ skip_sem
| CAss X E ⇒ asgn_sem X E
| CSeq c1 c2 ⇒ seq_sem (ceval c1) (ceval c2)
| CIf b c1 c2 ⇒ if_sem b (ceval c1) (ceval c2)
| CWhile b c ⇒ loop_sem b (ceval c)
| CBreak ⇒ break_sem
| CCont ⇒ cont_sem
end.
End Denotation_With_ControlFlow.
Module Hoare_logic.
Import Assertion_S.
Notation "d [ X ⟼ a ]" := (assn_sub X a ((d)%assert)) (at level 10, X at next level) : assert_scope.
Notation "a0 [ X ⟼ a ]" := (aexp_sub X a ((a0)%imp)) (at level 10, X at next level) : imp_scope.
Parameter hoare_triple: Assertion →
com →
Assertion * (* Normal Postcondition *)
Assertion * (* Break Postcondition *)
Assertion → (* Continue Condition *)
Prop.
Notation "{{ P }} c {{ Q }} {{ QB }} {{ QC }}" :=
(hoare_triple
P
c
(Q%assert: Assertion, QB%assert: Assertion, QC%assert: Assertion))
(at level 90, c at next level).
Import Assertion_S.
Notation "d [ X ⟼ a ]" := (assn_sub X a ((d)%assert)) (at level 10, X at next level) : assert_scope.
Notation "a0 [ X ⟼ a ]" := (aexp_sub X a ((a0)%imp)) (at level 10, X at next level) : imp_scope.
Parameter hoare_triple: Assertion →
com →
Assertion * (* Normal Postcondition *)
Assertion * (* Break Postcondition *)
Assertion → (* Continue Condition *)
Prop.
Notation "{{ P }} c {{ Q }} {{ QB }} {{ QC }}" :=
(hoare_triple
P
c
(Q%assert: Assertion, QB%assert: Assertion, QC%assert: Assertion))
(at level 90, c at next level).
This Hoare triple says: if command c is started in a state satisfying
assertion P, and if c eventually terminates normally / by break / by
continue in some final state, then this final state will satisfy assertion
Q / QB / QC.
All proof rules need to be slightly modified:
Axiom hoare_seq : ∀(P Q R RB RC: Assertion) (c1 c2: com),
{{P}} c1 {{Q}} {{RB}} {{RC}} →
{{Q}} c2 {{R}} {{RB}} {{RC}} →
{{P}} CSeq c1 c2 {{R}} {{RB}} {{RC}}.
Axiom hoare_skip : ∀P,
{{P}} CSkip {{P}} {{False}} {{False}}.
Axiom hoare_break : ∀P,
{{P}} CSkip {{False}} {{P}} {{False}}.
Axiom hoare_cont : ∀P,
{{P}} CSkip {{False}} {{False}} {{P}}.
Axiom hoare_if : ∀P Q QB QC b c1 c2,
{{ P AND [[b]] }} c1 {{Q}} {{QB}} {{QC}} →
{{ P AND NOT [[b]] }} c2 {{Q}} {{QB}} {{QC}} →
{{ P }} CIf b c1 c2 {{Q}} {{QB}} {{QC}}.
Axiom hoare_while : ∀I P b c,
{{ I AND [[b]] }} c {{I}} {{P}} {{I}} →
{{ I }} CWhile b c {{ P OR (I AND NOT [[b]]) }} {{False}} {{False }}.
Axiom hoare_asgn_fwd : ∀P (X: var) E,
{{ P }}
CAss X E
{{ EXISTS x, P [X ⟼ x] AND
[[AId X]] == [[ E [X ⟼ x] ]] }}
{{False}}
{{False}}.
Axiom hoare_asgn_bwd : ∀P (X: var) E,
{{ P [ X ⟼ E] }} CAss X E {{ P }} {{False}} {{False}}.
Axiom hoare_consequence : ∀(P P' Q Q' QB QB' QC QC' : Assertion) c,
P ⊢ P' →
{{P'}} c {{Q'}} {{QB'}} {{QC'}}→
Q' ⊢ Q →
QB' ⊢ QB →
QC' ⊢ QC →
{{P}} c {{Q}} {{QB}} {{QC}}.
End Hoare_logic.
{{P}} c1 {{Q}} {{RB}} {{RC}} →
{{Q}} c2 {{R}} {{RB}} {{RC}} →
{{P}} CSeq c1 c2 {{R}} {{RB}} {{RC}}.
Axiom hoare_skip : ∀P,
{{P}} CSkip {{P}} {{False}} {{False}}.
Axiom hoare_break : ∀P,
{{P}} CSkip {{False}} {{P}} {{False}}.
Axiom hoare_cont : ∀P,
{{P}} CSkip {{False}} {{False}} {{P}}.
Axiom hoare_if : ∀P Q QB QC b c1 c2,
{{ P AND [[b]] }} c1 {{Q}} {{QB}} {{QC}} →
{{ P AND NOT [[b]] }} c2 {{Q}} {{QB}} {{QC}} →
{{ P }} CIf b c1 c2 {{Q}} {{QB}} {{QC}}.
Axiom hoare_while : ∀I P b c,
{{ I AND [[b]] }} c {{I}} {{P}} {{I}} →
{{ I }} CWhile b c {{ P OR (I AND NOT [[b]]) }} {{False}} {{False }}.
Axiom hoare_asgn_fwd : ∀P (X: var) E,
{{ P }}
CAss X E
{{ EXISTS x, P [X ⟼ x] AND
[[AId X]] == [[ E [X ⟼ x] ]] }}
{{False}}
{{False}}.
Axiom hoare_asgn_bwd : ∀P (X: var) E,
{{ P [ X ⟼ E] }} CAss X E {{ P }} {{False}} {{False}}.
Axiom hoare_consequence : ∀(P P' Q Q' QB QB' QC QC' : Assertion) c,
P ⊢ P' →
{{P'}} c {{Q'}} {{QB'}} {{QC'}}→
Q' ⊢ Q →
QB' ⊢ QB →
QC' ⊢ QC →
{{P}} c {{Q}} {{QB}} {{QC}}.
End Hoare_logic.
Module Small_Step_Semantics.
The small step semantics for break and continue is based on control
stack. Specifically, every element in control stack describe a loop (loop
condition and loop body) and a command after loop.
Definition cstack: Type := list (bexp * com * com).
In order to define a small step for control flow, it is useful to define
the following properties:
- a command c starts with break: start_with_break c;
- a command c starts with continue: start_with_break c;
- a command c is equivalent with a sequential composition of loop CWhile b c1 and another command c2: start_with_loop c b c1 c2.
Inductive start_with_break: com → Prop :=
| SWB_Break: start_with_break CBreak
| SWB_Seq: ∀c1 c2,
start_with_break c1 →
start_with_break (CSeq c1 c2).
Inductive start_with_cont: com → Prop :=
| SWC_Cont: start_with_cont CCont
| SWC_Seq: ∀c1 c2,
start_with_cont c1 →
start_with_cont (CSeq c1 c2).
Inductive start_with_loop: com → bexp → com → com → Prop :=
| SWL_While: ∀b c, start_with_loop (CWhile b c) b c CSkip
| SWL_Seq: ∀c1 b c11 c12 c2,
start_with_loop c1 b c11 c12 →
start_with_loop (CSeq c1 c2) b c11 (CSeq c12 c2).
| SWB_Break: start_with_break CBreak
| SWB_Seq: ∀c1 c2,
start_with_break c1 →
start_with_break (CSeq c1 c2).
Inductive start_with_cont: com → Prop :=
| SWC_Cont: start_with_cont CCont
| SWC_Seq: ∀c1 c2,
start_with_cont c1 →
start_with_cont (CSeq c1 c2).
Inductive start_with_loop: com → bexp → com → com → Prop :=
| SWL_While: ∀b c, start_with_loop (CWhile b c) b c CSkip
| SWL_Seq: ∀c1 b c11 c12 c2,
start_with_loop c1 b c11 c12 →
start_with_loop (CSeq c1 c2) b c11 (CSeq c12 c2).
Now, we are ready to define a small step semantics with control stack.
Inductive com': Type :=
| CNormal (s: cstack) (c: com): com'
| CLoopCond (s: cstack) (b: bexp): com'.
Inductive cstep : (com' * state) → (com' * state) → Prop :=
| CS_AssStep : ∀st s X a a',
astep st a a' →
cstep
(CNormal s (CAss X a), st)
(CNormal s (CAss X a'), st)
| CS_Ass : ∀st1 st2 s X n,
st2 X = n →
(∀Y, X ≠ Y → st1 Y = st2 Y) →
cstep
(CNormal s (CAss X (ANum n)), st1)
(CNormal s CSkip, st2)
| CS_SeqStep : ∀st s c1 c1' st' c2,
cstep
(CNormal s c1, st)
(CNormal s c1', st') →
cstep
(CNormal s (CSeq c1 c2), st)
(CNormal s (CSeq c1' c2), st')
| CS_Seq : ∀st s c2,
cstep
(CNormal s (CSeq CSkip c2), st)
(CNormal s c2, st)
| CS_IfStep : ∀st s b b' c1 c2,
bstep st b b' →
cstep
(CNormal s (CIf b c1 c2), st)
(CNormal s (CIf b' c1 c2), st)
| CS_IfTrue : ∀st s c1 c2,
cstep
(CNormal s (CIf BTrue c1 c2), st)
(CNormal s c1, st)
| CS_IfFalse : ∀st s c1 c2,
cstep
(CNormal s (CIf BFalse c1 c2), st)
(CNormal s c2, st)
| CS_While : ∀st s c b c1 c2, (* <-- new *)
start_with_loop c b c1 c2 →
cstep
(CNormal s c, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
| CS_WhileStep : ∀st s b b' b'' c1 c2, (* <-- new *)
bstep st b' b'' →
cstep
(CLoopCond (cons (b, c1, c2) s) b', st)
(CLoopCond (cons (b, c1, c2) s) b'', st)
| CS_WhileTrue : ∀st s b c1 c2, (* <-- new *)
cstep
(CLoopCond (cons (b, c1, c2) s) BTrue, st)
(CNormal (cons (b, c1, c2) s) c1, st)
| CS_WhileFalse : ∀st s b c1 c2, (* <-- new *)
cstep
(CLoopCond (cons (b, c1, c2) s) BFalse, st)
(CNormal s c2, st)
| CS_Skip : ∀st s b c1 c2, (* <-- new *)
cstep
(CNormal (cons (b, c1, c2) s) CSkip, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
| CS_Break : ∀st s b c1 c2 c, (* <-- new *)
start_with_break c →
cstep
(CNormal (cons (b, c1, c2) s) c, st)
(CNormal s c2, st)
| CS_Cont : ∀st s b c1 c2 c, (* <-- new *)
start_with_cont c →
cstep
(CNormal (cons (b, c1, c2) s) c, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
.
End Small_Step_Semantics.
(* Thu May 23 18:25:50 UTC 2019 *)
| CNormal (s: cstack) (c: com): com'
| CLoopCond (s: cstack) (b: bexp): com'.
Inductive cstep : (com' * state) → (com' * state) → Prop :=
| CS_AssStep : ∀st s X a a',
astep st a a' →
cstep
(CNormal s (CAss X a), st)
(CNormal s (CAss X a'), st)
| CS_Ass : ∀st1 st2 s X n,
st2 X = n →
(∀Y, X ≠ Y → st1 Y = st2 Y) →
cstep
(CNormal s (CAss X (ANum n)), st1)
(CNormal s CSkip, st2)
| CS_SeqStep : ∀st s c1 c1' st' c2,
cstep
(CNormal s c1, st)
(CNormal s c1', st') →
cstep
(CNormal s (CSeq c1 c2), st)
(CNormal s (CSeq c1' c2), st')
| CS_Seq : ∀st s c2,
cstep
(CNormal s (CSeq CSkip c2), st)
(CNormal s c2, st)
| CS_IfStep : ∀st s b b' c1 c2,
bstep st b b' →
cstep
(CNormal s (CIf b c1 c2), st)
(CNormal s (CIf b' c1 c2), st)
| CS_IfTrue : ∀st s c1 c2,
cstep
(CNormal s (CIf BTrue c1 c2), st)
(CNormal s c1, st)
| CS_IfFalse : ∀st s c1 c2,
cstep
(CNormal s (CIf BFalse c1 c2), st)
(CNormal s c2, st)
| CS_While : ∀st s c b c1 c2, (* <-- new *)
start_with_loop c b c1 c2 →
cstep
(CNormal s c, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
| CS_WhileStep : ∀st s b b' b'' c1 c2, (* <-- new *)
bstep st b' b'' →
cstep
(CLoopCond (cons (b, c1, c2) s) b', st)
(CLoopCond (cons (b, c1, c2) s) b'', st)
| CS_WhileTrue : ∀st s b c1 c2, (* <-- new *)
cstep
(CLoopCond (cons (b, c1, c2) s) BTrue, st)
(CNormal (cons (b, c1, c2) s) c1, st)
| CS_WhileFalse : ∀st s b c1 c2, (* <-- new *)
cstep
(CLoopCond (cons (b, c1, c2) s) BFalse, st)
(CNormal s c2, st)
| CS_Skip : ∀st s b c1 c2, (* <-- new *)
cstep
(CNormal (cons (b, c1, c2) s) CSkip, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
| CS_Break : ∀st s b c1 c2 c, (* <-- new *)
start_with_break c →
cstep
(CNormal (cons (b, c1, c2) s) c, st)
(CNormal s c2, st)
| CS_Cont : ∀st s b c1 c2 c, (* <-- new *)
start_with_cont c →
cstep
(CNormal (cons (b, c1, c2) s) c, st)
(CLoopCond (cons (b, c1, c2) s) b, st)
.
End Small_Step_Semantics.
(* Thu May 23 18:25:50 UTC 2019 *)