Theory Context_Space_Finite
theory Context_Space_Finite
imports "Voblint_Framework.Call_String_Context" "Voblint_Compile.Compile_Wellformed"
begin
section ‹Which context policies can have a finite solved key set›
text ‹
A published result table is well formed only if its key set is finite
(‹wf_solved_table›, in the framework's result layer). A key is a
‹(node, context)› pair, so one way to get there is to exhibit a fixed finite set the
keys are drawn from and appeal to ‹finite_subset›. This theory builds that set, one per
context policy.
What it builds is a candidate space, not a reachability or a termination result. Nothing
below says which keys a solver run actually produces, and nothing below bounds how often
the value at one key is raised -- an interval unknown can ascend forever inside a
one-element key space. Both halves of the containment are therefore hypotheses of every
result here, not conclusions:
▪ ‹fst ` vars ⊆ cfg_nodes (compile_prog Pi ps)› --- the keys are nodes of the program
the solver was given. There is no theorem here connecting the vendored solver's
returned key set to the unknowns its equation system mentions.
▪ ‹snd ` vars› lies in the policy's context space --- routing never left it. For call
strings that is a length-and-alphabet condition, and truncation alone does not
establish it: a starting context whose elements are not nodes of this program stays
short under \<^const>‹cs_route› without ever entering the space.
What differs between policies is how cheaply the second hypothesis can be met. Call
strings bound the length for free: \<^const>‹cs_route› truncates every context to length at
most ‹k› (‹cs_route_length›, \<^theory>‹Voblint_Framework.Call_String_Context›), and a
compiled program's CFG has finitely many nodes (‹cfg_nodes_finite›,
\<^theory>‹Voblint_CFG.CFG_Def›), so with the standard library's own
‹finite_lists_length_le› the space is finite for every ‹k› and every domain alike. The
monovariant policy makes the hypothesis vacuous: its context type is \<^typ>‹unit›, so
there is no context space to bound and the node half is the whole story.
An entry-state context is a domain value rather than a bounded-length list over a finite
alphabet, so it needs a separate argument. A finite value domain together with a fixed
formals count supplies one -- ‹sign list› at the callee's arity is a finite set -- but an
infinite-height domain such as ‹ivl› does not, and an ordinary widening policy bounds
values rather than the context identities keyed by them. Bounding those needs a policy
that controls key creation or merges keys (a gas budget, a tabulation cap), with real
precision consequences that nothing here specifies. This development stops at the two
policies whose context space needs no such decision.
The concrete analyses reach a finite key set by an unrelated route: they discharge their
own obligation from the vendored solver's ‹finite_stabl_solve›, which yields a finite
stable set from a terminating solve. Neither argument implies the other -- a bounded
candidate space does not make a solve terminate, and a terminating solve says nothing
about the space its keys were drawn from.
›
lemma call_strings_bounded_finite:
assumes "finite A"
shows "finite {cs::call_string. set cs ⊆ A ∧ length cs ≤ k}"
using assms by (rule finite_lists_length_le)
theorem compiled_call_strings_finite:
"finite {cs::call_string. set cs ⊆ cfg_nodes (compile_prog Pi ps) ∧ length cs ≤ k}"
using cfg_nodes_finite[OF compile_prog_finite[THEN conjunct1] compile_prog_finite[THEN conjunct2]]
by (rule call_strings_bounded_finite)
text ‹
The practical corollary a ‹call_string_routed_context› instance can cite directly:
every ‹(node, call-string)› pair a ‹k›-bounded call-string analysis over a compiled
program could ever solve for lies in a fixed finite set, so ‹vars› --- whatever a
particular solver run actually populates it with --- is finite because it lies in a
finite superset, not merely because the run happened to terminate.
›
theorem compiled_call_string_vars_finite:
assumes nodes: "fst ` vars ⊆ cfg_nodes (compile_prog Pi ps)"
and ctxs: "snd ` vars ⊆ {cs::call_string. set cs ⊆ cfg_nodes (compile_prog Pi ps)
∧ length cs ≤ k}"
shows "finite vars"
proof -
have "vars ⊆ cfg_nodes (compile_prog Pi ps)
× {cs::call_string. set cs ⊆ cfg_nodes (compile_prog Pi ps) ∧ length cs ≤ k}"
using nodes ctxs by(auto; force)
moreover have "finite (cfg_nodes (compile_prog Pi ps)
× {cs::call_string. set cs ⊆ cfg_nodes (compile_prog Pi ps) ∧ length cs ≤ k})"
using cfg_nodes_finite compile_prog_finite compiled_call_strings_finite
by (blast intro: finite_cartesian_product)
ultimately show ?thesis by (rule finite_subset)
qed
text ‹
The seed-key space follows the same shape: one \<^const>‹Global› slot plus one \<^const>‹Seed›
slot per (callee-entry node, call-string) pair. ‹seed_pp› ranges over \<^typ>‹pp›, which is
\<^typ>‹cfg_node› (\<^theory>‹Voblint_CFG.CFG_Def›) -- a call-string seed is keyed by the
callee's \<^const>‹FunctionEntry› node, not the raw procedure name -- so this reuses
‹cfg_nodes_finite› again rather than needing a separate finiteness fact about ‹ps›.
›
theorem compiled_call_string_gk_finite:
"finite ({Global} ∪ (⋃p ∈ cfg_nodes (compile_prog Pi ps). Seed p `
{cs::call_string. set cs ⊆ cfg_nodes (compile_prog Pi ps) ∧ length cs ≤ k}))"
using compiled_call_strings_finite[of Pi ps k]
cfg_nodes_finite[OF compile_prog_finite[of Pi ps, THEN conjunct1]
compile_prog_finite[of Pi ps, THEN conjunct2]]
by auto
section ‹The monovariant policy›
text ‹
The same result for the monovariant ‹route_unit› policy, where it needs less: a context is
\<^typ>‹unit›, so the context factor is a singleton and the node bound is the
entire hypothesis. Stated over ‹fst ` vars› rather than a product, since with
one context there is nothing for a product to say.
›
theorem compiled_unit_vars_finite:
fixes vars :: "(pp × unit) set"
assumes "fst ` vars ⊆ cfg_nodes (compile_prog Pi ps)"
shows "finite vars"
proof -
have "vars ⊆ cfg_nodes (compile_prog Pi ps) × (UNIV :: unit set)"
using assms by auto
moreover have "finite (cfg_nodes (compile_prog Pi ps) × (UNIV :: unit set))"
using cfg_nodes_finite compile_prog_finite by force
ultimately show ?thesis by (rule finite_subset)
qed
end