Theory DG_Local_State_Exec_Refinement
theory DG_Local_State_Exec_Refinement
imports
DG_Local_State_Exec
"Voblint_Framework.DG_Reader_Transport"
begin
unbundle default_st_syntax
section ‹The mathematical run an executable D/G run represents›
text ‹
The executable construction above computes on association-list states; the
soundness statements are about function-valued ones. This theory is the
bridge: it fixes a domain whose executable transfer agrees with its abstract
transfer on the represented functions (‹dg_domain_exec›) and derives, from
those three agreements alone, that a whole D/G specification is sound at the
executable carrier -- with no separate abstract-carrier run to compare against.
›
subsection ‹The function one return combine represents›
text ‹
The one fact about the executable state representation this theory needs
that no lifter supplies: merging caller against callee and then writing the
return value represents the mathematical \<^const>‹combine_collect_abs› of
the two represented functions. Everything else transports through
\<^const>‹transfer_lift›/\<^const>‹transfer_lift2› naturality.
›
lemma default_st_to_fun_combine_assign:
"ρ⇘𝒢⇙
(combine_assign_default_st 𝒢 dst y⟨location_of 𝒢 ret_var⟩
(combine_default_st x y))
= combine⇧♯ 𝒢 dst (ρ⇘𝒢⇙ x) (ρ⇘𝒢⇙ y)"
unfolding default_st_to_fun_def
by (auto simp add: combine_collect_abs_def fun_eq_iff location_of_def
split: option.splits)
subsection ‹Routed-domain compatibility, independent of any routing context›
text ‹
Every routed domain instance needs the same shapes
-- the compiled edge, enter and combine trees commuting under
\<^const>‹default_st_to_fun›, plus \<^locale>‹dg_reader_commute_gen› at that
same reader -- from its own executable/abstract transfer-commute facts,
citing this theory's packaging theorems verbatim. Nothing in that derivation
is domain-specific beyond the two primitive commute facts a domain's own
executable-transfer soundness development already proves
(‹sign_tf.tf_st_for_commute›, ‹ivl_tf.tf_st_for_commute›, ...): this locale states
the derivation once, so a domain interprets it instead of restating it. The
locale is deliberately free of any routing context (‹route›, ‹Seed›,
‹Global›, a solver): those are context-owned and solver-owned respectively,
not domain-owned.
›
text ‹
\<^locale>‹dg_reader_commute_gen›'s instance at the same reader on both sides needs no
domain fact at all: \<^const>‹default_st_to_fun› is already carrier-polymorphic and
‹sup›-homomorphic (‹default_st_to_fun_sup›,
‹Voblint_Exec.Default_St_Transfer›), so this
is a free-standing fact, not part of the ‹dg_domain_exec› locale below -- keeping it
outside means citing it never drags in that locale's ‹empty_pred›/transfer obligations.
›
lemma dg_reader_commute_gen_lifted_for:
"dg_reader_commute_gen
(ρ⇘𝒢⇙ :: _ default_st lifted ⇒ _) (ρ⇘𝒢⇙ :: _ default_st lifted ⇒ _)"
by unfold_locales (simp_all add: map_lift_sup)
text ‹
‹dg_domain_exec› relates an executable transfer on ‹default_st›
to the abstract state specification: on live states the step, the entry
transfer and the emptiness test agree with their abstract counterparts after
readback through ‹default_st_to_fun›.
›
locale dg_domain_exec =
fixes 𝒢 :: "vname ⇒ bool"
and empty_pred :: "'a::numeric_domain default_st ⇒ bool"
and tf_st :: "edge_action ⇒ 'a default_st ⇒ 'a default_st"
and enter_st :: "call_info ⇒ 'a default_st ⇒ 'a default_st"
and sk :: "'a abs_state ⇒ 'a abs_state"
and asn :: "vname ⇒ exp ⇒ 'a abs_state ⇒ 'a abs_state"
and sp :: "special_call ⇒ vname ⇒ 'a abs_state ⇒ 'a abs_state"
and br :: "exp ⇒ bool ⇒ 'a abs_state ⇒ 'a abs_state"
and bd :: "pname ⇒ 'a abs_state ⇒ 'a abs_state"
and rt :: "exp option ⇒ pname ⇒ 'a abs_state ⇒ 'a abs_state"
and en :: "call_info ⇒ 'a abs_state ⇒ 'a abs_state"
and ev :: "analysis_event ⇒ 'a abs_state ⇒ 'a abs_state"
assumes tf_st_commute:
"⋀a s. live_default_st 𝒢 s ⟹
ρ⇘𝒢⇙ (tf_st a s)
= local_spec_step sk asn sp br bd rt ev a (ρ⇘𝒢⇙ s)"
and enter_st_commute:
"⋀ci s. ρ⇘𝒢⇙ (enter_st ci s)
= en ci (ρ⇘𝒢⇙ s)"
and empty_pred_exact:
"⋀s. empty_pred s = is_empty_state (ρ⇘𝒢⇙ s)"
begin
text ‹With ‹𝒢› fixed, ‹γ s› is the carrier's concretization, for a plain and
for a lifted carrier state alike.›
adhoc_overloading gamma_S == "default_st_gamma 𝒢"
adhoc_overloading gamma_S == "gamma_lift (default_st_gamma 𝒢)"
abbreviation reader :: "'a default_st lifted ⇒ 'a abs_state lifted" where
"reader ≡ map_lift ρ⇘𝒢⇙"
text ‹Each field's equation on the represented function, once. These are the only inputs the tree
commutes below take: a local-only transfer compiles to a single answer, so
its transport is exactly the pure equation on the function it wraps.›
text ‹
Unlike ‹enter_st_commute›/‹combine›'s field equations, ‹tf_st_commute› only
holds on live inputs (a domain's ‹branch_st›-style raw result need not
match its abstract ‹branch›'s on a dead one), so this cannot go through the
blanket ‹transfer_lift_commute›. ‹normalized_lift› supplies liveness
directly instead: production only ever feeds ‹tf_st› a ‹d› that is
itself ‹Bot› or the (always-normalized, ‹transfer_lift_normalized›) result
of a prior step.
›
lemma step_lift_commute:
assumes norm: "normalized_lift empty_pred d"
shows "reader (transfer_lift empty_pred (tf_st a) d)
= transfer_lift is_empty_state (local_spec_step sk asn sp br bd rt ev a) (reader d)"
using norm proof (cases d)
case Bot
then show ?thesis by (simp add: transfer_lift_def)
next
case (Lifted s)
with norm have "live_default_st 𝒢 s"
by (simp add: live_default_st_def empty_pred_exact)
then show ?thesis
unfolding Lifted
by (simp add: transfer_lift_def normalize_lift_def tf_st_commute empty_pred_exact)
qed
lemma enter_lift_commute:
"reader (transfer_lift empty_pred (enter_st ci) d)
= transfer_lift is_empty_state (en ci) (reader d)"
proof (rule transfer_lift_commute)
show "⋀s. ρ⇘𝒢⇙ (enter_st ci s)
= en ci (ρ⇘𝒢⇙ s)"
by (simp add: enter_st_commute)
show "⋀s. empty_pred s = is_empty_state (ρ⇘𝒢⇙ s)"
by (rule empty_pred_exact)
qed
text ‹The env stage merges into the assign stage: both collapse on the same two
\<^const>‹Bot› cases, so the composed combine is a single \<^const>‹transfer_lift2›
and transports by the same naturality lemma as every other field.›
lemma transfer_lift2_combine_env_st_lifted:
"transfer_lift2 empty_pred g (combine_env_st_lifted dc de) de
= transfer_lift2 empty_pred (λx y. g (combine_default_st x y) y) dc de"
by (cases dc; cases de) (simp_all add: combine_env_st_lifted_def transfer_lift2_def)
lemma combine_lift_commute:
"reader (transfer_lift2 empty_pred
(λenv0 de0. combine_assign_default_st 𝒢 dst
de0⟨location_of 𝒢 ret_var⟩ env0)
(combine_env_st_lifted dc de) de)
= transfer_lift2 is_empty_state (combine⇧♯ 𝒢 dst) (reader dc) (reader de)"
unfolding transfer_lift2_combine_env_st_lifted
proof (rule transfer_lift2_commute)
show "⋀x y. ρ⇘𝒢⇙
(combine_assign_default_st 𝒢 dst y⟨location_of 𝒢 ret_var⟩
(combine_default_st x y))
= combine⇧♯ 𝒢 dst (ρ⇘𝒢⇙ x) (ρ⇘𝒢⇙ y)"
by (rule default_st_to_fun_combine_assign)
show "⋀s. empty_pred s = is_empty_state (ρ⇘𝒢⇙ s)"
by (rule empty_pred_exact)
qed
subsection ‹Tree-level transport of the two specifications›
text ‹
The routed spine's transport hypotheses are commutes of compiled sub-trees.
Both specifications are
local-only, so each such commute reduces to the corresponding field equation
above.
›
abbreviation spec_st :: "('x,'k,unit,'a default_st lifted,'a default_st lifted) dg_spec" where
"spec_st ≡ exec_dg_spec 𝒢 empty_pred tf_st enter_st"
abbreviation spec_abs :: "('x,'k,unit,'a abs_state lifted,'a abs_state lifted) dg_spec" where
"spec_abs ≡ lifted_state_dg_spec 𝒢 is_empty_state sk asn sp br bd rt en ev"
lemma Henter_lifted_for:
"dg_reader_commute_gen.dg_enter_st_commute reader reader σ_st
(enter⇧♯ spec_st ci (mk_dg_man d (λ_. gk)))
(enter⇧♯ spec_abs ci (mk_dg_man (reader d) (λ_. gk)))"
unfolding dgs_enter_exec_dg_spec dgs_enter_lifted_state_dg_spec
by (rule dg_reader_commute_gen.dg_enter_st_commute_local_enter_transfer
[OF dg_reader_commute_gen_lifted_for])
(simp add: enter_lift_commute)
lemma Hcomb_lifted_for:
"dg_reader_commute_gen.dg_tree_st_commute reader reader σ_st
(sp_compile_with (λx. DG x bot)
(dg_spec_combine_transfer spec_st ci (mk_dg_man d (λ_. gk)) de))
(sp_compile_with (λx. DG x bot)
(dg_spec_combine_transfer spec_abs ci (mk_dg_man (reader d) (λ_. gk)) (reader de)))"
unfolding dg_spec_combine_transfer_exec_dg_spec
dg_spec_combine_transfer_lifted_state_dg_spec
by (rule dg_reader_commute_gen.dg_tree_st_commute_local_combine_transfer
[OF dg_reader_commute_gen_lifted_for,
where F = "transfer_lift2 is_empty_state (combine⇧♯ 𝒢 (ci_dst ci))"])
(rule combine_lift_commute)
subsection ‹Soundness at the executable carrier, pulled back through its functions›
text ‹
The framework is carrier-agnostic, so nothing forces it to be instantiated at
‹'a abs_state lifted›: with the carrier's own concretization
\<^const>‹default_st_gamma›, the executable Base-style spec is itself a
\<^locale>‹analysis_contract›, and the field equations above are all that the proof
needs. An instance that interprets the routed spine at ‹spec_st› with this
concretization feeds it the solver's own table and never transports a solved
system between carriers.
›
definition gamma_exec :: "'a default_st lifted ⇒ 'a default_st lifted ⇒ store set" where
"gamma_exec d g = γ d"
lemma gamma_exec_Bot [simp]: "gamma_exec Bot g = {}"
by (simp add: gamma_exec_def)
text ‹
Entry is not part of \<^locale>‹analysis_contract›, so a routed instance needs it
separately. This is the same fact the collapse below proves for its own entry
obligation, exported once because every routed instance over this carrier
discharges its entry coverage from it.
›
lemma entered_st:
assumes tf_sound: "sound_nonrelational_transfer 𝒢 sk asn sp br bd rt en ev"
and s: "s ∈ γ d"
shows "call_enter 𝒢 (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci)) s
∈ γ (transfer_lift empty_pred (enter_st ci) d)"
using s unfolding gamma_lift_default_st_gamma_to_fun enter_lift_commute
by (intro transfer_lift_sound_mem[OF _ is_empty_state_gamma_state_empty])
(simp add: call_enter_CallEdge
sound_nonrelational_transfer.tf_sound_enter_entry_for[OF tf_sound])
theorem entry_pairs_cover_st:
assumes tf_sound: "sound_nonrelational_transfer 𝒢 sk asn sp br bd rt en ev"
and sin: "s ∈ γ d"
shows "entry_pairs_cover (λd'. γ d') s
(call_enter 𝒢 (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci)) s)
[(d, transfer_lift empty_pred (enter_st ci) d)]"
by (rule entry_pairs_coverI
[where cont = d and entry = "transfer_lift empty_pred (enter_st ci) d"])
(simp_all add: sin entered_st[OF tf_sound sin])
text ‹
The executable analysis as a component, sound for the carrier's
concretization. The proof maps each state to its function through ‹reader›; the
contract below is its one-line consequence.
›
theorem exec_local_spec_sound:
assumes tf_sound: "sound_nonrelational_transfer 𝒢 sk asn sp br bd rt en ev"
shows "sound_local_spec 𝒢 (λd. γ d) (exec_local_spec 𝒢 empty_pred tf_st enter_st)"
proof -
have step: "edge_collect a (γ⇩⊥ (reader d)) ⊆ γ⇩⊥ (reader (transfer_lift empty_pred (tf_st a) d))"
for a d
proof (cases "normalized_lift empty_pred d")
case True
show ?thesis
unfolding step_lift_commute[OF True]
by (rule transfer_lift_sound_collect
[OF sound_nonrelational_transfer.step_sound_for[OF tf_sound]
edge_collect_empty_set is_empty_state_gamma_state_empty])
next
case False
then obtain s where "d = Lifted s" "empty_pred s" by (cases d) simp_all
then show ?thesis by (simp add: empty_pred_exact is_empty_state_gamma_state_empty)
qed
have comb: "combine_collect 𝒢 (ci_dst ci) s t ∈ γ⇩⊥ (reader (transfer_lift2 empty_pred
(λenv0 de0. combine_assign_default_st 𝒢 (ci_dst ci)
de0⟨location_of 𝒢 ret_var⟩ env0)
(combine_env_st_lifted dc de) de))"
if "s ∈ γ⇩⊥ (reader dc)" "t ∈ γ⇩⊥ (reader de)" for s t dc de ci
unfolding combine_lift_commute
by (rule
transfer_lift2_sound_mem[OF combine_collect_sound is_empty_state_gamma_state_empty that])
have mono: "∀x y. x ≤ y ⟶ γ⇩⊥ (reader x) ⊆ γ⇩⊥ (reader y)"
by (meson gamma_lift_mono gamma_state_mono map_lift_default_st_to_fun_mono)
show ?thesis
unfolding sound_local_spec_def gamma_lift_default_st_gamma_to_fun
using mono subset_trans[OF edge_collect_mono[OF Int_lower1] step]
entered_st[OF tf_sound, unfolded gamma_lift_default_st_gamma_to_fun] comb
by (auto simp: exec_local_spec_def)
qed
theorem analysis_contract_st:
assumes tf_sound: "sound_nonrelational_transfer 𝒢 sk asn sp br bd rt en ev"
shows "analysis_contract spec_st (λd e. gamma_exec d (e ())) 𝒢"
unfolding exec_dg_spec_def gamma_exec_def[abs_def]
by (rule dg_spec_of_contract[OF exec_local_spec_sound[OF tf_sound]])
end
unbundle no default_st_syntax
end