Theory Analysis_Run_Sound

theory Analysis_Run_Sound
  imports Analysis_Run
begin

section ‹What a configuration's table owes its report›

text ‹
  Every configuration hands the table its registration solved to
  const‹report_of›, which takes the check column from one call of
  const‹classify_checks_verdicts› and the diagnostics from one call of
  const‹arithmetic_diagnostics› over that table. So one argument serves every
  configuration.

  The argument asks two things of the table, bundled as ‹covered_table›. A
  store the program reaches at a point lies in the entry the table filed there under
  ∗‹some› context (‹table_covers›), and a point has finitely many contexts.
  Of the classifier it asks soundness in both directions (‹sound_classifier›),
  which does not depend on the table and is proved once for the combined state.
  Given both, the report built from the table is sound; the theory
  ‹Analysis_Report› states how. The context-free
  configuration's table is the instance at the end of this theory; the contextual
  ones follow in the theory after it.
›

subsection ‹Every check column is a contextual verdict report›

lemma finite_intra_prog_cfg [simp]: "finite (intra (prog_cfg p))"
  unfolding prog_cfg_def by (simp add: compile_prog_finite)

text ‹
  Forgetting the labels, the check column is the contextual verdict report itself.
›

lemma result_checks_of_verdicts:
  "map (λchk. (check_point chk, check_exp chk, check_verdict chk)) (result_checks_of g r classify)
     = classify_checks_verdicts g r classify"
  unfolding result_checks_of_def classify_checks_verdicts_def classify_checks_ctx_def
    point_verdict_def
  by (simp add: comp_def case_prod_beta image_image)

text ‹
  Where a result has checks: one per compiled const‹EA_Check› edge, at that edge's
  source node and with its label and condition, in the order the graph lists its edges.
›

definition check_sites :: "cfg ⇒ (pp × check_label × exp) list" where
  "check_sites g =
     map (λ(u, a, v). (u, ea_check_label a, ea_check_cond a))
       (filter (λ(u, a, v). is_EA_Check a) (cfg_intra_list g))"

lemma result_checks_of_sites:
  "map (λchk. (check_point chk, check_label chk, check_exp chk)) (result_checks_of g r classify)
     = check_sites g"
  unfolding result_checks_of_def check_sites_def
  by (simp add: comp_def case_prod_beta)

lemma check_sites_memI [intro]:
  assumes "finite (intra g)" and "(v, EA_Check l cnd, w) ∈ intra g"
  shows "(v, l, cnd) ∈ set (check_sites g)"
  using assms unfolding check_sites_def by force

subsection ‹A store that reaches a point sits in one of that point's contexts›

text ‹
  The table claim at a point is existential in the context: a store is described
  by the entry filed under ∗‹some› context the point was solved at --- one its
  own call history is admitted at --- and in general not by every such entry,
  since another activation's entry need not describe this store at all.
›

definition table_covers ::
    "('v ⇒ store set) ⇒ ('c, 'v) solved_table ⇒ pp ⇒ store ⇒ bool" where
  "table_covers gm r v s ⟷ (∃ctx st. lookup_table r v ctx = Lifted st ∧ s ∈ gm st)"

lemma table_coversI [intro]:
  "lookup_table r v ctx = Lifted st ⟹ s ∈ gm st ⟹ table_covers gm r v s"
  unfolding table_covers_def by blast

text ‹
  The published contextual soundness bounds one activation bucket by the table
  entry filed under that bucket's context. A caller holding a store knows only
  that the store reaches the point at all, so it needs the buckets to exhaust
  the point --- the union direction, which is where a context policy pays for
  being total. Both readings are settled in
  theory‹Voblint_CFG.Activation_Trace_Collect›: a functional policy has the union outright,
  a relational one --- such as the entry-state policy --- earns it from the
  existence of a context for every valid activation trace. const‹Bot› cannot be the entry
  found, since it concretizes to no store at all.
›

lemma lookup_table_covers_of_activation:
  fixes r :: "('c, 'v) solved_table"
  assumes union: "𝒞⇘𝒢,g,S⇙ v ⊆ (⋃c. 𝒜⇘𝒢,adm,rc,g,S⇙ v c)"
      and sound: "⋀ctx. 𝒜⇘𝒢,adm,rc,g,S⇙ v ctx
                    ⊆ gamma_lift gm (lookup_table r v ctx)"
      and mem: "s ∈ 𝒞⇘𝒢,g,S⇙ v"
  obtains ctx st where "s ∈ 𝒜⇘𝒢,adm,rc,g,S⇙ v ctx"
    and "lookup_table r v ctx = Lifted st" and "s ∈ gm st"
proof -
  from mem union obtain ctx where a: "s ∈ 𝒜⇘𝒢,adm,rc,g,S⇙ v ctx" by blast
  with sound have g: "s ∈ gamma_lift gm (lookup_table r v ctx)" by blast
  show ?thesis
  proof (cases "lookup_table r v ctx")
    case Bot
    with g show ?thesis by simp
  next
    case (Lifted st)
    with g a show ?thesis by (intro that [of ctx st]) simp_all
  qed
qed

subsection ‹The table contract and the classifier contract›

text ‹
  What a configuration's table has to satisfy for its report to be sound. Each
  configuration below and in the theory after this one is an instance of
  ‹covered_table›, and owes nothing but two facts: the table exhausts the
  reachable stores, over finitely many contexts per point. The classifier owes
  soundness in both directions (‹sound_classifier›); ‹sound_table› is the two
  together.
›

definition arithmetic_safe_at :: "cfg ⇒ pp ⇒ store ⇒ bool" where
  "arithmetic_safe_at g v s ⟷
     (∀es. (v, es) ∈ set (arithmetic_expression_sites g) ⟶
       (∀e ∈ set es. ∀divisor ∈ expression_divisors e. ⟦divisor⟧e s ≠ 0))"

text ‹
  A solved table ‹r› covers program ‹p›: every store the collecting semantics
  reaches at ‹v› lies in ‹gm› of the state ‹r› holds at ‹v› in
  some context, and each point has finitely many contexts.
›
locale covered_table =
  fixes p :: imp_prog
    and r :: "('c, 'v) solved_table"
    and gm :: "'v ⇒ store set"
  assumes finite_contexts: "⋀v. finite (table_contexts r v)"
      and covers: "⋀v s. s ∈ 𝒞⇘declared_global p,prog_cfg p,cinit_stores (declared_global p)⇙ v
                      ⟹ table_covers gm r v s"

text ‹
  ‹classify› is sound for ‹gm›: ‹Check_Proved› means the condition holds at
  every store in ‹gm d›, ‹Check_Refuted› that it fails at every one.
›
locale sound_classifier =
  fixes classify :: "exp ⇒ 'v ⇒ check_result"
    and gm :: "'v ⇒ store set"
  assumes proved: "⋀cnd d t. classify cnd d = Check_Proved ⟹ t ∈ gm d
                      ⟹ truthy (⟦cnd⟧e t)"
      and refuted: "⋀cnd d t. classify cnd d = Check_Refuted ⟹ t ∈ gm d
                      ⟹ ¬ truthy (⟦cnd⟧e t)"

lemma mcp_sound_classifier: "sound_classifier (mcp_classify as) (mcp_gamma_v as)"
  by unfold_locales (fact mcp_classify_proved, fact mcp_classify_refuted)

text ‹
  A covered table read through a sound classifier. Inside it, verdicts and
  diagnostics computed from ‹r› hold at every collected store.
›
locale sound_table = covered_table p r gm + sound_classifier classify gm
  for p :: imp_prog and r :: "('c, 'v) solved_table"
    and classify :: "exp ⇒ 'v ⇒ check_result" and gm :: "'v ⇒ store set"
begin

text ‹
  A point the arithmetic diagnostics do not name is safe at every store the
  collecting semantics admits there: no divisor evaluated at it is zero.
›

lemma arithmetic_safe:
  assumes absent: "∀d ∈ set (arithmetic_diagnostics (prog_cfg p) r classify).
      diagnostic_point d ≠ v"
    and mem: "s ∈ 𝒞⇘declared_global p,prog_cfg p,cinit_stores (declared_global p)⇙ v"
  shows "arithmetic_safe_at (prog_cfg p) v s"
proof -
  from covers[OF mem] obtain ctx st where
    look: "lookup_table r v ctx = Lifted st" and gst: "s ∈ gm st"
    unfolding table_covers_def by blast
  have safe: "⋀es e divisor. (v, es) ∈ set (arithmetic_expression_sites (prog_cfg p)) ⟹
      e ∈ set es ⟹ divisor ∈ expression_divisors e ⟹ ⟦divisor⟧e s ≠ 0"
  proof -
    fix es e divisor
    assume site: "(v, es) ∈ set (arithmetic_expression_sites (prog_cfg p))"
      and expr: "e ∈ set es" and div: "divisor ∈ expression_divisors e"
    obtain obligations obligation where
      obs: "(v, obligations) ∈ set (arithmetic_sites (prog_cfg p))"
      and ob: "obligation ∈ set obligations"
      and divisor: "arithmetic_divisor obligation = divisor"
      by (rule arithmetic_sites_divisor[OF site expr div])
    have verdict: "point_verdict r classify v (arithmetic_condition obligation)
                     = Lifted Check_Proved"
      using arithmetic_diagnostics_absent[OF obs ob absent]
        point_verdict_not_dead[OF finite_contexts look, of classify]
      by blast
    have classified: "classify (arithmetic_condition obligation) st = Check_Proved"
      by (rule point_verdict_decided[OF verdict _ look]) simp
    from proved[OF classified gst] show "⟦divisor⟧e s ≠ 0"
      by (auto simp: arithmetic_condition_def divisor split: if_splits)
  qed
  show ?thesis unfolding arithmetic_safe_at_def using safe by blast
qed

end

text ‹
  The shape every registration publishes its soundness in, with its context policy
  ‹adm› and root context ‹rc›: every valid activation trace carries a context, and
  each activation bucket is bounded by the entry filed under its context. The
  buckets then exhaust the point, which is all a ‹covered_table› needs.
›

locale activation_table =
  fixes p :: imp_prog
    and r :: "('c, 'v) solved_table"
    and gm :: "'v ⇒ store set"
    and adm :: "'c context_policy"
    and rc :: 'c
  assumes has_context:
      "⋀t. t ∈ 𝒯⇘declared_global p,prog_cfg p,cinit_stores (declared_global p)⇙
         ⟹ ∃c. activation_context_rel (declared_global p) adm rc (prog_cfg p) t c"
    and bucket_bound:
      "⋀u ctx. 𝒜⇘declared_global p,adm,rc,prog_cfg p,cinit_stores (declared_global p)⇙ u ctx
         ⊆ gamma_lift gm (lookup_table r u ctx)"
    and finite_table: "finite_solved_table r"
begin

lemma covered: "covered_table p r gm"
proof (rule covered_table.intro)
  show "finite (table_contexts r v)" for v by (rule finite_table_contexts [OF finite_table])
  have union: "𝒞⇘declared_global p,prog_cfg p,cinit_stores (declared_global p)⇙ u
                 ⊆ (⋃c. 𝒜⇘declared_global p,adm,rc,prog_cfg p,
                             cinit_stores (declared_global p)⇙ u c)" for u
    by (rule equalityD1 [OF node_collect_eq_Union_activation_of_has_context [OF has_context]])
  show "table_covers gm r v s"
    if "s ∈ 𝒞⇘declared_global p,prog_cfg p,cinit_stores (declared_global p)⇙ v"
    for v s
    by (meson lookup_table_covers_of_activation [OF union bucket_bound that] table_coversI)
qed

end

subsection ‹The context-free configuration›

text ‹
  The unit registration keys every activation at the one context ‹()›, a
  function of the call site and the caller's context, so the buckets exhaust a point
  outright and each is bounded by its entry.
›

lemma mcp_rule_table:
  assumes wf: "wf_program_compile_input p"
    and cov: "mcp_rule.terminates as r (declared_global p) p"
  shows "activation_table p (mcp_rule.result as r (declared_global p) p)
           (mcp_gamma_v (activation as)) (context_policy_of_fun (λu c t. ())) ()"
proof (rule activation_table.intro, goal_cases)
  case (1 t)
  show ?case
    by (rule exI, subst activation_context_rel_of_fun_iff [OF 1]) (rule refl)
next
  case (2 u ctx)
  show ?case
    using mcp_rule.fun_route_activation_collect_sound_of_terminates [OF _ wf cov]
    unfolding mcp_rule.gamma_reader_eq_lookup by (simp add: route_unit_def)
next
  case 3
  show ?case
    using mcp_rule.vars_finite_of_terminates [OF cov]
    by (simp add: finite_solved_table_def dg_pipeline.result_def
        dg_pipeline.sol_vars_def)
qed

text ‹The same with program globals on the shared channel.›

lemma mcp_split_rule_table:
  assumes wf: "wf_program_compile_input p"
    and cov: "mcp_split_rule.terminates as r (declared_global p) p"
  shows "activation_table p (mcp_split_rule.result as r (declared_global p) p)
           (mcp_gamma_v (activation as)) (context_policy_of_fun (λu c t. ())) ()"
proof (rule activation_table.intro, goal_cases)
  case (1 t)
  show ?case
    by (rule exI, subst activation_context_rel_of_fun_iff [OF 1]) (rule refl)
next
  case (2 u ctx)
  show ?case
    using mcp_split_rule.fun_route_activation_collect_sound_of_terminates [OF _ wf cov]
    unfolding mcp_split_rule.gamma_reader_eq_lookup by (simp add: route_unit_def)
next
  case 3
  show ?case
    using mcp_split_rule.vars_finite_of_terminates [OF cov]
    by (simp add: finite_solved_table_def dg_pipeline.result_def
        dg_pipeline.sol_vars_def)
qed

end