Theory Rel_Order_Domain

theory Rel_Order_Domain
  imports "Voblint_Framework.DG_Spec_Sound" "Voblint_Framework.DG_Indexed_Generator"
    "Voblint_Framework.State_Restriction" "Voblint_Domain.Order_Lattice"
begin

section ‹A minimal relational analysis for const‹analysis_contract››

text ‹
  The analysis over typ‹relc›, the order lattice of
  theory‹Voblint_Domain.Order_Lattice›. The purpose of this file is not a useful
  analysis. It demonstrates that a non-‹abs_state› carrier discharges
  locale‹analysis_contract› with zero changes to the DG framework. Every transfer
  below is deliberately the most imprecise sound choice (forget on assign, havoc on
  call) except for a precise ‹assume_step›/‹assume_not_step› pair, which is
  enough to make the carrier genuinely relational.
›

subsection ‹Local and global state together›

definition gammaDG_relc :: "relc ⇒ relc ⇒ store set" where
  "gammaDG_relc d g = γ d ∩ γ g"

lemma gammaDG_relc_top [simp]: "gammaDG_relc ⊤ ⊤ = UNIV"
  unfolding gammaDG_relc_def by simp

subsection ‹Refining bare-variable comparisons›

text ‹
  Only guards between two bare variables refine a ‹relc›. On the true branch
  ‹assume_step› adds ‹(x, y)› for a guard implying that ‹x› is at most ‹y›;
  every other guard leaves the state unchanged.
›

definition assume_step :: "exp ⇒ relc ⇒ relc" where
  "assume_step b d =
     (case d of
        RelBot ⇒ RelBot
      | RelC ps ⇒
          (case b of
             Less (V x) (V y) ⇒ RelC (insert (x, y) ps)
           | LessEq (V x) (V y) ⇒ RelC (insert (x, y) ps)
           | Greater (V x) (V y) ⇒ RelC (insert (y, x) ps)
           | GreaterEq (V x) (V y) ⇒ RelC (insert (y, x) ps)
           | Eq (V x) (V y) ⇒ RelC (insert (x, y) (insert (y, x) ps))
           | _ ⇒ RelC ps))"

lemma assume_step_sound[intro]:
  assumes "s ∈ γ d" "truthy (⟦b⟧e s)"
  shows "s ∈ γ (assume_step b d)"
  using assms
  by (cases d; cases b) (auto simp: assume_step_def split: exp.splits if_splits)

text ‹The false branch records the reversed order.  Equality records both
  directions, on the true branch of ‹Eq› or the false branch of ‹NotEq›.
  Disequality alone cannot be represented by a conjunction of weak orders.›

definition assume_not_step :: "exp ⇒ relc ⇒ relc" where
  "assume_not_step b d =
     (case d of
        RelBot ⇒ RelBot
      | RelC ps ⇒
          (case b of
             Less (V x) (V y) ⇒ RelC (insert (y, x) ps)
           | LessEq (V x) (V y) ⇒ RelC (insert (y, x) ps)
           | Greater (V x) (V y) ⇒ RelC (insert (x, y) ps)
           | GreaterEq (V x) (V y) ⇒ RelC (insert (x, y) ps)
           | NotEq (V x) (V y) ⇒ RelC (insert (x, y) (insert (y, x) ps))
           | _ ⇒ RelC ps))"

lemma assume_not_step_sound[intro]:
  assumes "s ∈ γ d" "¬ truthy (⟦b⟧e s)"
  shows "s ∈ γ (assume_not_step b d)"
  using assms
  by (cases d; cases b) (auto simp: assume_not_step_def split: exp.splits if_splits)

subsection ‹The transfer functions›

text ‹Every step except the comparison refinements above
  is sound by forgetting or by leaving the carrier untouched -- deliberately
  imprecise: no closure, no normalization, havoc-based calls.›

definition relc_skip :: "relc ⇒ relc ⇒ relc × relc" where
  "relc_skip d g = (g, d)"

definition relc_assign :: "vname ⇒ exp ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_assign x e d g = (forget_relc x g, forget_relc x d)"

definition relc_body :: "pname ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_body p d g = (g, d)"

text ‹
  ‹return› reuses the same forget-based imprecision ‹relc_assign› already
  applies to every ordinary assignment: with an expression, forget ‹ret_var›;
  without one, behave like ‹skip›.
›
definition relc_return :: "exp option ⇒ pname ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_return e p d g =
     (case e of None ⇒ relc_skip d g
      | Some a ⇒ relc_assign ret_var a d g)"

definition relc_special :: "special_call ⇒ vname ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_special sc x d g = (forget_relc x g, forget_relc x d)"

text ‹A check observes its condition but never refines the state, matching
  ‹relc_skip›'s own imprecision.›
definition relc_event :: "analysis_event ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_event ev d g = (g, d)"

text ‹
  ‹assume_step›/‹assume_not_step› stay separate, genuinely asymmetric
  operations (‹x < y› vs.\ its mirror ‹y ≤ x› insert different pairs, not
  the same formula under a polarity flag); only the interface-level dispatch
  consolidates them into one polarity-parametrized branch operation.
›
definition relc_branch_step :: "exp ⇒ bool ⇒ relc ⇒ relc" where
  "relc_branch_step b pol d = (if pol then assume_step b d else assume_not_step b d)"

text ‹The relational counterpart of ‹bfilter_sound›, in the same shape: refining
  at a guard keeps every store at which the guard has the required truth value.›
lemma relc_branch_step_sound [intro]:
  assumes "s ∈ γ d" "truthy (⟦b⟧e s) = pol"
  shows "s ∈ γ (relc_branch_step b pol d)"
  using assms by (cases pol) (auto simp: relc_branch_step_def)

definition relc_branch :: "exp ⇒ bool ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_branch b pol d g = (g, relc_branch_step b pol d)"

definition relc_enter :: "call_info ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_enter ci dc g = (⊤, ⊤)"

text ‹
  The caller continuation is the identity.  This carrier discards every caller
  relation at its environment merge anyway, so there is no call-side
  invalidation for a continuation to express: filtering before a merge that
  already returns term‹⊤ :: relc› would be indistinguishable from not filtering.
  Keeping it identity leaves this instance the least interesting sound one, which
  is its purpose.
›
definition relc_caller_cont :: "call_info ⇒ relc ⇒ relc ⇒ relc" where
  "relc_caller_cont ci dc g = dc"

definition relc_combine_env :: "call_info ⇒ relc ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_combine_env ci dc de g = (⊤, ⊤)"

definition relc_combine_assign ::
  "call_info ⇒ relc ⇒ relc ⇒ relc × relc ⇒ relc × relc"
where
  "relc_combine_assign ci de g merged = merged"

text ‹
  Unlike the local-only domains, this one really uses the global channel:
  const‹relc_special› forgets the assigned name on both halves, and entry
  and the environment merge reset the shared relation to term‹⊤ :: relc›.
  Its transfers are therefore written as a read-compute-publish sequence.

  ‹relc_transfer› is the adapter that turns one of this file's
  ‹d ⇒ g ⇒ (g, d)› operations into a manager transfer: query the shared
  relation, run the operation, publish its global half, answer with its local
  half. The manager interface does not require that pairing shape; here it is
  one domain's private convenience, which is the point.
›

definition relc_transfer ::
  "(relc ⇒ relc ⇒ relc × relc) ⇒ ('x,'k,unit,relc,relc) man_transfer"
where
  "relc_transfer f m =
     do {
       g ← man_global m ();
       let r = f (man_local m) g;
       _ ← man_sideg m () (fst r);
       sp_return (snd r)
     }"

text ‹Entry at this carrier: the same global read and publication, answering the one
  alternative whose continuation is the caller value unchanged.›

definition relc_enter_transfer ::
  "(relc ⇒ relc ⇒ relc × relc) ⇒ ('x,'k,unit,relc,relc) man_enter_transfer"
where
  "relc_enter_transfer f m =
     do {
       g ← man_global m ();
       let r = f (man_local m) g;
       _ ← man_sideg m () (fst r);
       sp_return [(man_local m, snd r)]
     }"

definition relc_combine_transfer ::
  "(relc ⇒ relc ⇒ relc ⇒ relc × relc) ⇒ ('x,'k,unit,relc,relc) man_combine_transfer"
where
  "relc_combine_transfer f m de =
     do {
       g ← man_global m ();
       let r = f (man_local m) de g;
       _ ← man_sideg m () (fst r);
       sp_return (snd r)
     }"

text ‹The observations of a compiled ‹relc_transfer›: its answer is the operation's
  local half, and what it publishes at the routed key is the global half. These are
  what locale‹analysis_contract› is stated against.›

lemma traverse_relc_transfer [simp]:
  "dg_local (traverse_program (transfer_program (relc_transfer f) src (λ_. gk)) τ)
     = snd (f (dg_local (τ src)) (dg_global (τ (Inr gk))))"
  by (cases src)
     (simp_all add: transfer_program_def transfer_program_at_def relc_transfer_def
        mk_dg_man_def dg_read_at_def dg_read_global_def dg_sideg_def sp_bind_assoc
        Let_def)

lemma sides_relc_transfer [simp]:
  "dg_global (sides_of_program (transfer_program (relc_transfer f) src (λ_. gk)) τ (Inr gk))
     = fst (f (dg_local (τ src)) (dg_global (τ (Inr gk))))"
  by (cases src)
     (simp_all add: transfer_program_def transfer_program_at_def relc_transfer_def
        mk_dg_man_def dg_read_at_def dg_read_global_def dg_sideg_def sp_bind_assoc
        Let_def)

text ‹The order analysis never asks, so the query channel the generator installs
  leaves its transfers unchanged.›

lemma relc_transfer_outer_man [simp]:
  "relc_transfer f (outer_man Q m) = relc_transfer f m"
  by (simp add: relc_transfer_def)

definition rel_order_spec :: "('x,'k,unit,relc,relc) dg_spec" where
  "rel_order_spec = local_dg_spec_template⦇
     dgs_skip := relc_transfer relc_skip,
     dgs_assign := (λx e. relc_transfer (relc_assign x e)),
     dgs_special := (λsc x. relc_transfer (relc_special sc x)),
     dgs_branch := (λb pol. relc_transfer (relc_branch b pol)),
     dgs_body := (λp. relc_transfer (relc_body p)),
     dgs_return := (λe p. relc_transfer (relc_return e p)),
     dgs_enter := (λci. relc_enter_transfer (relc_enter ci)),
     dgs_event := (λev. relc_transfer (relc_event ev)),
     dgs_combine_env := (λci. relc_combine_transfer (relc_combine_env ci))
   ⦈"

text ‹Expand the template and transfer in this code equation so the argument-free
  specification is a record of functions in generated ML. This avoids the value
  restriction on record updates without inlining the specification into its callers.›

lemmas [code] =
  rel_order_spec_def[unfolded local_dg_spec_template_def relc_transfer_def, simplified]

text ‹Every field reads the shared slot, publishes once and answers, so the
  specification runs its continuation once wherever the generator runs it.›

lemma dg_spec_wf_rel_order_spec [intro, simp]: "dg_spec_wf rel_order_spec"
proof (unfold dg_spec_wf_def, intro conjI allI impI)
  fix a :: edge_action and d :: relc and unknown_of :: "unit ⇒ 'b" and A
  show "sp_wf (dg_spec_step rel_order_spec a ((mk_dg_man d unknown_of)⦇man_ask := A⦈))"
    by (cases a) (auto simp: rel_order_spec_def relc_transfer_def Let_def mk_dg_man_def)
next
  fix d :: relc and unknown_of :: "unit ⇒ 'b" and A q
  show "sp_wf (dgs_query rel_order_spec ((mk_dg_man d unknown_of)⦇man_ask := A⦈) q)"
    by (simp add: rel_order_spec_def)
next
  fix ci and d :: relc and unknown_of :: "unit ⇒ 'b"
  show "sp_wf (enter♯ rel_order_spec ci (mk_dg_man d unknown_of))"
    by (auto simp: rel_order_spec_def relc_enter_transfer_def Let_def)
next
  fix ci and d :: relc and unknown_of :: "unit ⇒ 'b" and ex :: relc
  show "sp_wf (dg_spec_combine_transfer rel_order_spec ci (mk_dg_man d unknown_of) ex)"
    by (auto simp: rel_order_spec_def dg_spec_combine_transfer_def relc_combine_transfer_def
        local_combine_transfer_def Let_def)
qed

named_theorems rel_order_simps

declare
  relc_branch_def     [rel_order_simps]
  relc_branch_step_def    [rel_order_simps]
  relc_skip_def       [rel_order_simps]
  relc_body_def       [rel_order_simps]
  relc_return_def     [rel_order_simps]
  relc_event_def      [rel_order_simps]
  rel_order_spec_def     [rel_order_simps]
  gammaDG_relc_def        [rel_order_simps]
  relc_assign_def     [rel_order_simps]
  relc_special_def    [rel_order_simps]

subsection ‹Per-edge soundness›

text ‹
  Each edge operation over-approximates the concrete edge semantics on
  ‹gammaDG_relc›, the stores both the local and the global component describe.
  Assignments and special calls forget every pair that mentions the target.
›

lemma relc_skip_sound[intro]:
  "edge_collect EA_Nop (gammaDG_relc d g) ⊆
     (case relc_skip d g of (g', d') ⇒ gammaDG_relc d' g')"
  unfolding relc_skip_def by simp

lemma relc_assign_sound[intro]:
  "edge_collect (EA_Assign x e) (gammaDG_relc d g) ⊆
     (case relc_assign x e d g of (g', d') ⇒ gammaDG_relc d' g')"
proof -
  have "edge_collect (EA_Assign x e) (gammaDG_relc d g)
      = {s(x := ⟦e⟧e s) | s. s ∈ gammaDG_relc d g}"
    by simp
  also have "... ⊆ γ (forget_relc x d) ∩ γ (forget_relc x g)"
    using forget_relc_sound unfolding gammaDG_relc_def by blast
  finally show ?thesis
    unfolding relc_assign_def gammaDG_relc_def by simp
qed

lemma relc_special_sound[intro]:
  "edge_collect (EA_Special sc x) (gammaDG_relc d g) ⊆
     (case relc_special sc x d g of (g', d') ⇒ gammaDG_relc d' g')"
proof -
  have "edge_collect (EA_Special sc x) (gammaDG_relc d g)
      ⊆ {s(x := v) | s v. s ∈ gammaDG_relc d g}"
    by (cases sc) auto
  also have "... ⊆ γ (forget_relc x d) ∩ γ (forget_relc x g)"
    using forget_relc_sound unfolding gammaDG_relc_def by blast
  finally show ?thesis
    unfolding relc_special_def gammaDG_relc_def by simp
qed

lemma relc_branch_sound_True[intro]:
  "edge_collect (EA_Assume b) (gammaDG_relc d g) ⊆
     (case relc_branch b True d g of (g', d') ⇒ gammaDG_relc d' g')"
proof -
  have "edge_collect (EA_Assume b) (gammaDG_relc d g)
      = {s. s ∈ gammaDG_relc d g ∧ truthy (⟦b⟧e s)}"
    by simp
  also have "... ⊆ γ (assume_step b d) ∩ γ g"
    using assume_step_sound unfolding gammaDG_relc_def by blast
  finally show ?thesis
    unfolding relc_branch_def relc_branch_step_def gammaDG_relc_def by simp
qed

lemma relc_branch_sound_False[intro]:
  "edge_collect (EA_AssumeNot b) (gammaDG_relc d g) ⊆
     (case relc_branch b False d g of (g', d') ⇒ gammaDG_relc d' g')"
proof -
  have "edge_collect (EA_AssumeNot b) (gammaDG_relc d g)
      = {s. s ∈ gammaDG_relc d g ∧ ¬ truthy (⟦b⟧e s)}"
    by simp
  also have "... ⊆ γ (assume_not_step b d) ∩ γ g"
    using assume_not_step_sound unfolding gammaDG_relc_def by blast
  finally show ?thesis
    unfolding relc_branch_def relc_branch_step_def gammaDG_relc_def by simp
qed

lemma relc_return_sound[intro]:
  "edge_collect (EA_Ret e p) (gammaDG_relc d g) ⊆
     (case relc_return e p d g of (g', d') ⇒ gammaDG_relc d' g')"
proof (cases e)
  case None
  then show ?thesis
    by (simp add: relc_return_def relc_skip_def)
next
  case (Some a)
  have "edge_collect (EA_Ret (Some a) p) (gammaDG_relc d g)
      = {s(ret_var := ⟦a⟧e s) | s. s ∈ gammaDG_relc d g}"
    by simp
  also have "... ⊆ γ (forget_relc ret_var d) ∩ γ (forget_relc ret_var g)"
    using forget_relc_sound unfolding gammaDG_relc_def by blast
  finally show ?thesis
    using Some
    by (simp add: relc_return_def relc_assign_def gammaDG_relc_def)
qed

text ‹The edge dispatch as one pure pairing operation, so the specification's
  compiled step reduces to const‹relc_transfer› of it.›

fun relc_step_for :: "edge_action ⇒ relc ⇒ relc ⇒ relc × relc" where
  "relc_step_for EA_Nop = relc_skip"
| "relc_step_for (EA_Assign x e) = relc_assign x e"
| "relc_step_for (EA_Special sc x) = relc_special sc x"
| "relc_step_for (EA_Assume b) = relc_branch b True"
| "relc_step_for (EA_AssumeNot b) = relc_branch b False"
| "relc_step_for (EA_Body p) = relc_body p"
| "relc_step_for (EA_Ret e p) = relc_return e p"
| "relc_step_for (EA_Check l cnd) = relc_event (Check_Event l cnd)"

lemma dg_spec_step_rel_order_spec [simp]:
  "dg_spec_step rel_order_spec a = relc_transfer (relc_step_for a)"
  unfolding rel_order_spec_def by (cases a) simp_all

lemma dgs_enter_rel_order_spec [simp]:
  "enter♯ rel_order_spec ci = relc_enter_transfer (relc_enter ci)"
  unfolding rel_order_spec_def by simp

lemma relc_step_sound:
  "edge_collect a (gammaDG_relc d g) ⊆
     (case relc_step_for a d g of (g', d') ⇒ gammaDG_relc d' g')"
  by (cases a) (auto simp add: rel_order_simps split: option.splits)

subsection ‹Call-entry and combine soundness -- havoc-based, both trivial via ‹⊤››

text ‹The composed return pipeline: ‹combine_assign› keeps the template's
  identity default, so the whole combine is the environment merge, which resets both halves
  to term‹⊤ :: relc›.›

lemma dg_spec_combine_transfer_rel_order_spec [simp]:
  "dg_spec_combine_transfer rel_order_spec ci = relc_combine_transfer (relc_combine_env ci)"
  unfolding dg_spec_combine_transfer_def rel_order_spec_def
  by (intro ext)
     (simp add: local_transfer_def local_combine_transfer_def relc_combine_transfer_def)

lemma traverse_relc_combine [simp]:
  "dg_local (traverse_program
             (combine_transfer_program (relc_combine_transfer f) src_cc src_ex (λ_. gk)) τ)
     = snd (f (dg_local (τ src_cc)) (dg_local (τ src_ex)) (dg_global (τ (Inr gk))))"
  by (cases src_cc; cases src_ex)
     (simp_all add: combine_transfer_program_def combine_program_at_def relc_combine_transfer_def
        mk_dg_man_def dg_read_at_def dg_read_global_def dg_sideg_def sp_bind_assoc Let_def)

lemma sides_relc_combine [simp]:
  "dg_global (sides_of_program
            (combine_transfer_program (relc_combine_transfer f) src_cc src_ex (λ_. gk))
            τ (Inr gk))
     = fst (f (dg_local (τ src_cc)) (dg_local (τ src_ex)) (dg_global (τ (Inr gk))))"
  by (cases src_cc; cases src_ex)
     (simp_all add: combine_transfer_program_def combine_program_at_def relc_combine_transfer_def
        mk_dg_man_def dg_read_at_def dg_read_global_def dg_sideg_def sp_bind_assoc Let_def)

subsection ‹The interpretation›

text ‹
  Interpreting ‹analysis_contract› at ‹rel_order_spec› discharges the framework's
  obligations: a well-formed spec, monotone concretization, and sound edge and
  combine transfers, each from the per-edge lemmas above.
›

interpretation rel_order: analysis_contract rel_order_spec "λd e. gammaDG_relc d (e ())" 𝒢
proof (rule analysis_contract_unitI)
  show "dg_spec_wf rel_order_spec" by (rule dg_spec_wf_rel_order_spec)
next
  fix d d' :: relc and g g' :: relc
  assume "d ≤ d'" "g ≤ g'"
  then show "gammaDG_relc d g ⊆ gammaDG_relc d' g'"
    using gamma_relc_mono unfolding gammaDG_relc_def by blast
next
  fix a and τ :: "'a + 'b ⇒ (relc, relc) dg_state" and src gk
  show "edge_collect a (gammaDG_relc (dg_local (τ src)) (dg_global (τ (Inr gk))))
          ⊆ gammaDG_relc
              (edge_out rel_order_spec a src (λ_. gk) τ)
              (dg_global (sides_of_program (dg_spec_edge_program rel_order_spec a src (λ_. gk))
                        τ (Inr gk)))"
    using relc_step_sound[of a "dg_local (τ src)" "dg_global (τ (Inr gk))"]
    by (simp add: dg_spec_edge_program_def split: prod.splits)
next
  fix s t dc de and τ :: "'a + 'b ⇒ (relc, relc) dg_state" and gk ci
  show "⟦s ∈ gammaDG_relc dc (dg_global (τ (Inr gk)));
         t ∈ gammaDG_relc de (dg_global (τ (Inr gk)))⟧ ⟹
          combine_collect 𝒢 (ci_dst ci) s t
            ∈ gammaDG_relc
                (dg_local (traverse_rhs (sp_compile_with (λd. DG d bot)
                   (dg_spec_combine_transfer rel_order_spec ci
                      (mk_dg_man dc (λ_. gk)) de)) τ))
                (dg_global (sides_of_rhs (sp_compile_with (λd. DG d bot)
                   (dg_spec_combine_transfer rel_order_spec ci
                      (mk_dg_man dc (λ_. gk)) de)) τ (Inr gk)))"
    by (simp add: relc_combine_transfer_def mk_dg_man_def dg_read_global_def
        dg_sideg_def sp_bind_assoc Let_def relc_combine_env_def)
qed

end