Theory Voblint_Examples_Sign.Example_Sign_DG_CallString_K2
theory Example_Sign_DG_CallString_K2
imports
Example_Sign_DG_CallString_K1
begin
section ‹A computed 2-call-string context, routed by truncated call history›
text ‹
The ‹k = 2› sibling of \<^theory>‹Voblint_Examples_Sign.Example_Sign_DG_CallString_K1›, same
‹sign_nest› program and same Base-style storage: ‹g›'s single call site is reached from
two different ‹f› activations. At ‹k = 1› both collapse into one merged context and
‹g›'s entry parameter joins to ‹STop›; at ‹k = 2› the call string also records which
‹f› call led there, so the two activations stay separate and ‹g›'s entry parameter is
‹SPos› in one context, ‹SNeg› in the other. Only the bound changes --- the
specification, the transport, and the routed locale are the ones ‹k = 1› already fixed.
›
subsection ‹The routed equation system and its computed solution›
text ‹Reuses \<^type>‹call_string_gk› from \<^theory>‹Voblint_Framework.Call_String_Context› rather
than minting its own global-key type: the key shape never depended on ‹k›, only the
‹Seed› payload's context length did.›
definition sign_nest_2_eqs ::
"(pp × cfg_node list, call_string_gk,
(sign default_st lifted, sign default_st lifted) dg_state) eqsT" where
"sign_nest_2_eqs =
routed_node_rhs intra_predecessor_addr_list call_site_list (λ_. Global) (cs_route 2)
(λctx' src a. dg_spec_edge_program sign_nest_S_st a src (λ_. Global))
(routed_call_program sign_nest_S_st (λ_. Global) Seed (static_resolve sign_nest_cfg)
(λd. d = Bot))
(routed_entry_seed_programs Seed)
sign_nest_cfg Bot (Lifted cinit_sign_st) Bot"
definition sign_nest_2_sol ::
"(pp × cfg_node list) set
× (pp × cfg_node list + call_string_gk
⇒ (sign default_st lifted, sign default_st lifted) dg_state)" where
"sign_nest_2_sol = TD_side_always_join_Interp_solve sign_nest_2_eqs
(cfg_exit sign_nest_cfg, [])"
lemma sign_nest_2_terminates:
"TD_side_always_join_Interp_solve_c sign_nest_2_eqs (cfg_exit sign_nest_cfg, []) ≠ None"
by eval
subsection ‹Coverage›
text ‹The full solved node set, computed once; every membership fact below is a
‹simp› lookup into this literal set instead of a separate ‹eval› re-derivation.›
definition sign_nest_2_nodes :: "(pp × cfg_node list) set" where
"sign_nest_2_nodes = {
(FunctionEntry (STR ''main''), []), (Statement 5, []), (Statement 6, []), (Statement 7, []),
(FunctionEntry (STR ''f''), [Statement 5]), (Statement 2, [Statement 5]),
(Statement 3, [Statement 5]), (FunctionResult (STR ''f''), [Statement 5]),
(FunctionEntry (STR ''f''), [Statement 6]), (Statement 2, [Statement 6]),
(Statement 3, [Statement 6]), (FunctionResult (STR ''f''), [Statement 6]),
(FunctionEntry (STR ''g''), [Statement 2, Statement 5]), (Statement 0, [Statement 2, Statement 5]),
(FunctionResult (STR ''g''), [Statement 2, Statement 5]),
(FunctionEntry (STR ''g''), [Statement 2, Statement 6]), (Statement 0, [Statement 2, Statement 6]),
(FunctionResult (STR ''g''), [Statement 2, Statement 6]),
(Statement 4, [Statement 5]), (Statement 4, [Statement 6]),
(Statement 1, [Statement 2, Statement 5]), (Statement 1, [Statement 2, Statement 6]),
(FunctionResult (STR ''main''), [])}"
lemma sign_nest_2_nodes_eq: "fst sign_nest_2_sol = sign_nest_2_nodes"
unfolding sign_nest_2_sol_def sign_nest_2_eqs_def sign_nest_2_nodes_def by eval
lemma entry_covered_2: "(cfg_entry sign_nest_cfg, []) ∈ fst sign_nest_2_sol"
unfolding sign_nest_entry sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma sign_nest_fwd_closed_all_2:
"∀(u, c)∈fst sign_nest_2_sol. ∀(u', a, v)∈intra sign_nest_cfg.
u = u' ⟶ (v, c) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_sol_def sign_nest_2_eqs_def by eval
lemma sign_nest_fwd_closed_2:
assumes "(u, ctx) ∈ fst sign_nest_2_sol" and "(u, a, v) ∈ intra sign_nest_cfg"
shows "(v, ctx) ∈ fst sign_nest_2_sol"
using sign_nest_fwd_closed_all_2 assms by fastforce
text ‹Unlike ‹k = 1›, ‹g›'s call site is now covered at two ∗‹distinct› two-element
contexts, one per ‹f› activation that reaches it.›
lemma enter_callers_only_root_main_2:
"∀(p, ctx)∈fst sign_nest_2_sol.
(p = Statement 5 ∨ p = Statement 6) ⟶ ctx = []"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma enter_callers_g_2:
"∀(p, ctx)∈fst sign_nest_2_sol.
p = Statement 2 ⟶ ctx = [Statement 5] ∨ ctx = [Statement 6]"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma callee_covered_fpos_2: "(FunctionEntry (STR ''f''), [Statement 5]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma callee_covered_fneg_2: "(FunctionEntry (STR ''f''), [Statement 6]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma callee_covered_g_fpos_2:
"(FunctionEntry (STR ''g''), [Statement 2, Statement 5]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma callee_covered_g_fneg_2:
"(FunctionEntry (STR ''g''), [Statement 2, Statement 6]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma covered_ret6_2: "(Statement 6, []) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma covered_ret7_2: "(Statement 7, []) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma covered_ret3_fpos_2: "(Statement 3, [Statement 5]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
lemma covered_ret3_fneg_2: "(Statement 3, [Statement 6]) ∈ fst sign_nest_2_sol"
unfolding sign_nest_2_nodes_eq sign_nest_2_nodes_def by simp
section ‹The solver's post-solution›
text ‹
As at ‹k = 1›: termination of ‹sign_nest_2_eqs› gives a solve domain, and the
solver's ‹partial_post_solution› gives ‹sign_nest_2_pp_st›.
›
lemma sign_nest_2_solve_dom:
"TD_side_always_join_Interp.solve_dom TYPE(call_string_gk)
TYPE((sign default_st lifted, sign default_st lifted) dg_state)
sign_nest_2_eqs (cfg_exit sign_nest_cfg, [])"
by (rule TD_side_always_join_Interp.solve_dom_of_solve_c[OF sign_nest_2_terminates])
lemma sign_nest_2_pp_st:
"part_post_solution sign_nest_2_eqs (cfg_exit sign_nest_cfg, [])
(snd sign_nest_2_sol) (fst sign_nest_2_sol)"
using TD_side_always_join_Interp.partial_post_solution
[OF sign_nest_2_solve_dom, of "fst sign_nest_2_sol" "snd sign_nest_2_sol"]
unfolding sign_nest_2_sol_def by simp
abbreviation sigma_2 ::
"pp × cfg_node list + call_string_gk
⇒ (sign default_st lifted, sign default_st lifted) dg_state" where
"sigma_2 ≡ snd sign_nest_2_sol"
section ‹Activation-indexed collecting soundness for the 2-call-string-routed solution›
text ‹
‹sign_ctx_sg_2› reads the solved local state at each key; the interpretation
‹sign_nest_2_cs› instantiates ‹call_string_routed_context› at ‹k = 2›.
›
abbreviation sign_ctx_sg_2 ::
"pp × cfg_node list + call_string_gk ⇒ sign default_st lifted" where
"sign_ctx_sg_2 ≡ solved_local_reader (fst sign_nest_2_sol) sigma_2"
text ‹Unlike ‹k = 1›, ‹call_fwd›'s ‹Statement 2› case now genuinely splits: ‹g›'s call site
is covered at two distinct one-element contexts, and ‹take 2› keeps them apart after
routing, so each needs its own coverage witness. Everything else the routed locale asks
for is discharged generically at \<^const>‹cs_route›, exactly as at ‹k = 1›.›
sign_nest_2_cs: call_string_routed_context
sign_nest_S_st sign_nest_gamma sign_nest_gs sign_nest_pi sign_nest_procs 2
Bot "Lifted cinit_sign_st" Bot
sigma_2 "fst sign_nest_2_sol" "(cfg_exit sign_nest_cfg, [])" sign_ctx_sg_2
"λd. d = Bot"
"gamma_lift (default_st_gamma sign_nest_gs)"
proof (unfold_locales, unfold sign_nest_cfg_compile,
goal_cases CmbWf ExtraWf FinE PP SgCov SgUncov Fwd IsBotBot IsBotSound
EnterComplete CallFwd CombFwd)
case CmbWf
show ?case by (rule sp_wf_routed_call_program) (simp add: sign_nest_S_st_def)
next
case ExtraWf
then show ?case by (rule sp_wf_routed_entry_seed_programs)
next
case FinE
show ?case by (rule sign_nest.finite_intra)
next
case PP
show ?case
by (rule post_bounded_of_part_post_solution
[OF sign_nest_2_pp_st[unfolded sign_nest_2_eqs_def]])
next
case (SgCov v c)
show ?case using SgCov by (simp add: sign_nest_gamma_def)
next
case (SgUncov v c)
show ?case using SgUncov by simp
next
case (Fwd u a v c)
show ?case using Fwd(1,3) by (rule sign_nest_fwd_closed_2)
next
case IsBotBot show ?case by simp
next
case (IsBotSound d gv) then show ?case by (simp add: sign_nest_gamma_eq)
next
case (EnterComplete u ctx dst pars args p cont s)
let ?ci = "call_info_of (CallEdge dst pars args) p"
let ?caller = "dg_local (sigma_2 (Inl (u, ctx)))"
have cov: "entry_pairs_cover (λd. sign_nest_gamma d (dg_global (sigma_2 (Inr Global)))) s
(call_enter sign_nest_gs (CallEdge dst pars args) s)
[(?caller, transfer_lift sign_nest_empty_pred (generic_enter_st_for sign_ops sign_nest_gs ?ci)
?caller)]"
using sign_nest_domain.entry_pairs_cover_st
[OF sign_tf.is_sound_nonrelational_transfer, where ci = ?ci and d = ?caller]
EnterComplete(3)
by (simp add: sign_nest_gamma_eq sign_nest_domain.gamma_exec_def)
show ?case
unfolding sign_nest_S_st_def dgs_enter_exec_dg_spec
using enter_runs_local_enter_transfer enter_deps_local_enter_transfer cov by fastforce
next
case (CallFwd u ctx dst pars args p cont)
note covU = CallFwd(1) and ce = CallFwd(2)
from ce sign_nest_calls_shape have
"(u = Statement 2 ∧ p = (STR ''g'') ∧ cont = Statement 3) ∨
(u = Statement 5 ∧ p = (STR ''f'') ∧ cont = Statement 6) ∨
(u = Statement 6 ∧ p = (STR ''f'') ∧ cont = Statement 7)"
by fastforce
then consider
(c1) "u = Statement 2" "p = (STR ''g'')"
| (c2) "u = Statement 5" "p = (STR ''f'')"
| (c3) "u = Statement 6" "p = (STR ''f'')"
by blast
thus ?case
proof cases
case c1
from covU c1 enter_callers_g_2 have "ctx = [Statement 5] ∨ ctx = [Statement 6]" by fastforce
thus ?thesis
proof
assume "ctx = [Statement 5]"
thus ?thesis using c1 callee_covered_g_fpos_2 by (simp add: cs_route_def)
next
assume "ctx = [Statement 6]"
thus ?thesis using c1 callee_covered_g_fneg_2 by (simp add: cs_route_def)
qed
next
case c2
have ctx0: "ctx = []" using covU c2 enter_callers_only_root_main_2 by fastforce
thus ?thesis using c2 callee_covered_fpos_2 by (simp add: cs_route_def)
next
case c3
have ctx0: "ctx = []" using covU c3 enter_callers_only_root_main_2 by fastforce
thus ?thesis using c3 callee_covered_fneg_2 by (simp add: cs_route_def)
qed
next
case (CombFwd cl c1 dst pars args p cont)
show ?case
using CombFwd enter_callers_only_root_main_2 enter_callers_g_2
covered_ret3_fpos_2 covered_ret3_fneg_2 covered_ret6_2 covered_ret7_2
sign_nest_calls_shape
by fastforce
qed
section ‹The headline theorem: 2-call-string activation collecting soundness›
text ‹
‹sign_nest_2_activation_collect_sound›, from ‹sign_nest_2_cs› and the
initial-store bound shared with ‹k = 1›.
›
theorem sign_nest_2_activation_collect_sound:
"𝒜⇘sign_nest_gs,context_policy_of_fun (cs_context 2),[],sign_nest_cfg,
cinit_stores sign_nest_gs⇙ v ctx
⊆ gamma_lift (default_st_gamma sign_nest_gs)
(sign_ctx_sg_2 (Inl (v, ctx)))"
by (rule sign_nest_2_cs.routed.activation_collect_dg_sound[unfolded sign_nest_cfg_compile,
OF entry_covered_2 sign_nest_cinit_le_cinit_sign_st])
(simp add: le_fun_def)
section ‹The precision comparison: k=2 keeps ‹g›'s two activations separated where k=1 merges them›
text ‹
A concrete, executable precision statement for this program and this D/G analysis: the
1-call-string context collapses ‹g›'s two activations to one context and their join at
‹STop› loses all sign information, while the 2-call-string context keeps them apart and each
retains its own exact sign. This is not a general "call strings always improve precision"
claim --- it is the concrete forcing witness the sign lattice's finiteness lets us compute
and verify outright, in the style of the paper's context-sensitivity discussion (Tilscher/
Grass/Seidl, "Verifying a Solver for Mixed Flow-Sensitive Analyses").
›
lemma sign_k2_g_entry_fpos:
"sign_nest_lookup
(dg_local (sigma_2 (Inl (FunctionEntry (STR ''g''), [Statement 2, Statement 5]))))
(STR ''p'') = SPos"
unfolding sign_nest_2_sol_def sign_nest_2_eqs_def by eval
lemma sign_k2_g_entry_fneg:
"sign_nest_lookup
(dg_local (sigma_2 (Inl (FunctionEntry (STR ''g''), [Statement 2, Statement 6]))))
(STR ''p'') = SNeg"
unfolding sign_nest_2_sol_def sign_nest_2_eqs_def by eval
theorem sign_k2_strictly_more_precise_than_k1_at_g:
"sign_nest_lookup
(dg_local (sigma_2 (Inl (FunctionEntry (STR ''g''), [Statement 2, Statement 5]))))
(STR ''p'')
< sign_nest_lookup
(dg_local (sigma_1 (Inl (FunctionEntry (STR ''g''), [Statement 2]))))
(STR ''p'')"
"sign_nest_lookup
(dg_local (sigma_2 (Inl (FunctionEntry (STR ''g''), [Statement 2, Statement 6]))))
(STR ''p'')
< sign_nest_lookup
(dg_local (sigma_1 (Inl (FunctionEntry (STR ''g''), [Statement 2]))))
(STR ''p'')"
by (simp_all add: sign_nest_1_g_entry_merged sign_k2_g_entry_fpos sign_k2_g_entry_fneg
less_sign_def sign_le_refl)
end