Theory Voblint_Analysis_Relational.Rel_Order_Local
theory Rel_Order_Local
imports Rel_Order_Domain "Voblint_Framework.MCP_Spec"
begin
section ‹The order carrier as a component that asks›
text ‹
\<^const>‹rel_order_spec› reads and publishes the global channel, so it is not a
local specification and cannot join the combination of cooperating analyses. This
theory gives the same carrier a local-only form. Intraprocedurally it is the
order analysis of \<^theory>‹Voblint_Analysis_Relational.Rel_Order_Domain›, with
one addition: after an assignment it asks the query channel how the assigned value
compares with the other variables, and records every order the answer fixes.
At calls it is deliberately coarse, entering and returning with no facts, so
that the component studies cooperation and not relational call boundaries.
›
subsection ‹Answering queries from the order›
text ‹
A pair ‹(x, y)› means ‹s x ≤ s y›. It therefore decides ‹x <= y› and
‹y >= x› as true, ‹y < x› and ‹x > y› as false, and with the reverse pair
also ‹x == y› as true and ‹x != y› as false. A comparison evaluates to ‹1›
when true and ‹0› when false, so the answers are the exact integers ‹1› and ‹0›. Every
other query is answered with ‹⊤›, the claim that holds of every store.
›
text ‹
‹relc_le d a b› holds when ‹a› and ‹b› are variables whose order ‹d› records,
or the same variable. The reflexive case lets a copy ‹x = y› learn both orders
between ‹x› and ‹y› from the channel's own answer to ‹y <= y›.
›
fun var_of :: "exp ⇒ vname option" where
"var_of (V x) = Some x"
| "var_of _ = None"
definition relc_le :: "relc ⇒ exp ⇒ exp ⇒ bool" where
"relc_le d a b =
(case (var_of a, var_of b) of (Some x, Some y) ⇒ x = y ∨ relc_has x y d | _ ⇒ False)"
definition relc_eval :: "relc ⇒ exp ⇒ answer" where
"relc_eval d e =
(case e of
LessEq a b ⇒ if relc_le d a b then answer_of_int 1 else ⊤
| GreaterEq a b ⇒ if relc_le d b a then answer_of_int 1 else ⊤
| Less a b ⇒ if relc_le d b a then answer_of_int 0 else ⊤
| Greater a b ⇒ if relc_le d a b then answer_of_int 0 else ⊤
| exp.Eq a b ⇒ if relc_le d a b ∧ relc_le d b a then answer_of_int 1 else ⊤
| NotEq a b ⇒ if relc_le d a b ∧ relc_le d b a then answer_of_int 0 else ⊤
| _ ⇒ ⊤)"
fun relc_qry :: "relc ⇒ channel" where
"relc_qry d (EvalInt e) = relc_eval d e"
lemma relc_has_sound: "s ∈ γ d ⟹ relc_has x y d ⟹ s x ≤ s y"
by (cases d) auto
lemma var_of_SomeD: "var_of a = Some x ⟹ a = V x"
by (cases a) simp_all
lemma relc_le_sound: "s ∈ γ d ⟹ relc_le d a b ⟹ ⟦a⟧⇩e s ≤ ⟦b⟧⇩e s"
by (auto simp: relc_le_def split: option.splits dest!: var_of_SomeD
dest: relc_has_sound)
lemma relc_eval_sound:
"s ∈ γ d ⟹ ⟦e⟧⇩e s ∈ gamma_answer (relc_eval d e)"
by (cases e) (auto simp: relc_eval_def
dest: relc_le_sound intro: order_antisym)
lemma relc_qry_sound: "s ∈ γ d ⟹ eval_holds q (relc_qry d q) s"
by (cases q) (simp add: relc_eval_sound)
subsection ‹Learning orders from the query channel›
text ‹
At ‹x = e› the question is asked about the state before the assignment, where
‹e› evaluates to the new value of ‹x› and every other variable keeps its
value. The exact answer ‹1› to ‹e <= y› therefore makes ‹(x, y)› true
afterwards, and ‹y <= e› makes ‹(y, x)› true. The candidate partners are the
variables in ‹ys›, a parameter so that the construction stays executable.
›
definition relc_learn :: "channel ⇒ vname list ⇒ vname ⇒ exp ⇒ relc ⇒ relc" where
"relc_learn ch ys x e d =
(case d of
RelBot ⇒ RelBot
| RelC ps ⇒ RelC (ps
∪ set (map (λy. (x, y))
(filter (λy. y ≠ x ∧ answer_const (ch (EvalInt (LessEq e (V y)))) = Some 1) ys))
∪ set (map (λy. (y, x))
(filter (λy. y ≠ x ∧ answer_const (ch (EvalInt (LessEq (V y) e))) = Some 1) ys))))"
lemma relc_learn_sound:
assumes "s(x := ⟦e⟧⇩e s) ∈ γ d"
and "eval_query.channel_holds ch s"
shows "s(x := ⟦e⟧⇩e s) ∈ γ (relc_learn ch ys x e d)"
proof (cases d)
case RelBot
with assms(1) show ?thesis by simp
next
case (RelC ps)
have up: "⟦e⟧⇩e s ≤ s y" if "answer_const (ch (EvalInt (LessEq e (V y)))) = Some 1" for y
using eval_holds_constD[OF eval_query.channel_holdsD[OF assms(2)] that]
by (simp split: if_splits)
have down: "s y ≤ ⟦e⟧⇩e s" if "answer_const (ch (EvalInt (LessEq (V y) e))) = Some 1" for y
using eval_holds_constD[OF eval_query.channel_holdsD[OF assms(2)] that]
by (simp split: if_splits)
show ?thesis
using assms(1) RelC up down by (auto simp: relc_learn_def)
qed
subsection ‹The component›
text ‹
The order analysis as a component of the combined state, over the variables
‹ys› it relates. It starts from the conservative local specification: skip,
body and events keep the relation, and the first return stage keeps the
caller's. It supplies the operations without a default -- assignments ask and
learn, the callee starts from the empty relation, and the return keeps
nothing, which is sound and imprecise by design -- and overrides the handler
and the branch, where it decides more than the default.
›
definition relc_ret :: "exp option ⇒ relc ⇒ relc" where
"relc_ret eo d = (case eo of None ⇒ d | Some a ⇒ forget_relc ret_var d)"
definition order_spec :: "vname list ⇒ relc local_spec" where
"order_spec ys = (conservative_local_spec
(λch x e d. relc_learn ch ys x e (forget_relc x d))
(λch sc x d. forget_relc x d)
(λch eo p d. relc_ret eo d)
(λch ci p. [(fst p, ⊤)])
(λch' ci d de. ⊤))
⦇ls_query := (λch. relc_qry), ls_branch := (λch b pol d. relc_branch_step b pol d)⦈"
theorem order_spec_sound: "sound_local_spec 𝒢 gamma_relc (order_spec ys)"
proof -
have special: "sound_special gamma_relc (λch sc x d. forget_relc x d)"
unfolding sound_special_def
proof (intro allI impI)
fix ch d s sc x t
assume "s ∈ γ (d :: relc)" "t ∈ special_step sc x s"
then show "t ∈ γ (forget_relc x d)" by (cases sc) auto
qed
have base: "sound_local_spec 𝒢 gamma_relc (conservative_local_spec
(λch x e d. relc_learn ch ys x e (forget_relc x d)) (λch sc x d. forget_relc x d)
(λch eo p d. relc_ret eo d) (λch ci p. [(fst p, ⊤)]) (λch' ci d de. ⊤))"
by (rule sound_conservative_local_spec[OF gamma_relc_mono _ special])
(auto simp: sound_assign_def sound_return_def sound_enter_def relc_ret_def
sound_combine_env_identity split: option.splits intro!: relc_learn_sound)
show ?thesis
unfolding order_spec_def
by (intro sound_local_spec_update_branch[OF sound_local_spec_update_query[OF base]])
(auto simp: sound_query_def sound_branch_def relc_branch_step_def relc_qry_sound)
qed
lemma single_entry_order_spec: "single_entry (order_spec ys)"
by (simp add: single_entry_def order_spec_def conservative_local_spec_def)
end