Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.
Run the following program at input −2. It takes the left branch and the assertion succeeds. That run says nothing about the right branch. Run it at input 4, and the right branch reaches a failed assertion. 𝐢𝐧𝐩𝐮𝐭 𝑥;𝐢𝐟 𝑥≤0 𝐭𝐡𝐞𝐧 𝑦:=𝑥+1 𝐞𝐥𝐬𝐞 𝑦:=𝑥−1;𝐚𝐬𝐬𝐞𝐫𝐭 𝑦≤2.(𝖥𝗈𝗋𝗄) An interval execution beginning with 𝑥 ∈[ −∞, +∞] joins the two stores at the end of the conditional. It may retain a safe over-approximation of 𝑦, but it no longer carries the concrete equation relating 𝑥 to the selected branch. To generate the failing test, we instead keep one store per path and a formula describing the inputs on that path.
This chapter fixes a deliberately small calculus, SymImp-DL. Its constraints are decidable by checkable difference-logic certificates. That choice puts the proof boundary in the open: an untrusted solver may propose a model, a negative cycle, or unknown; only the first two are accepted after local checking.
Concrete commands and symbolic integers
Let 𝑥,𝑦,𝑧 range over a finite set 𝖵𝖺𝗋, 𝑘 over integers, and 𝛼𝑖 over input symbols. Concrete right-hand sides and guards are 𝑎::=𝑘∣𝑥+𝑘,𝑔::=𝑎1−𝑎2≤𝑘. The notation includes 𝑥 ≤𝑘 by taking 𝑎2 =0, and 𝑥 =𝑦 +𝑘 as two inequalities. Commands are 𝑐::=𝐬𝐤𝐢𝐩∣𝐢𝐧𝐩𝐮𝐭 𝑥∣𝑥:=𝑎∣𝑐;𝑐∣𝐢𝐟 𝑔 𝐭𝐡𝐞𝐧 𝑐 𝐞𝐥𝐬𝐞 𝑐∣𝐚𝐬𝐬𝐞𝐫𝐭 𝑔∣𝐰𝐡𝐢𝐥𝐞 𝑔 𝐝𝐨 𝑐. Assignments are intentionally only copy-plus-constant. This closure property ensures that substituting a symbolic store into a guard again yields difference logic.
A concrete store is a total map 𝑠 :𝖵𝖺𝗋 →ℤ. An input oracle 𝐼 :ℕ →ℤ fixes a run, and a cursor 𝑛 records the next unread input. We write ⟨𝑐,𝑠,𝐼,𝑛⟩ ⟶𝖼⟨𝑐′,𝑠′,𝐼,𝑛′⟩; failed assertions step to 𝖿𝖺𝗂𝗅(𝑠,𝐼,𝑛). Assignment, input, conditionals, assertions, and while-unfolding have the expected rules; sequencing propagates a step and erases a leading 𝐬𝐤𝐢𝐩. Appendix A displays the complete rules.
A symbolic integer is 𝑡::=𝑘∣𝛼𝑖+𝑘. A symbolic store 𝜎 :𝖵𝖺𝗋 →𝖲𝖳𝖾𝗋𝗆 maps variables to such terms. A valuation 𝜌 :ℕ →ℤ evaluates terms by [[𝑘]]𝜌 =𝑘 and [[𝛼𝑖 +𝑘]]𝜌 =𝜌(𝑖) +𝑘. Symbolic evaluation [[𝑎]]𝜎 replaces the variable in 𝑎 by its symbolic term and adds the constant.
If 𝑠(𝑥) =[[𝜎(𝑥)]]𝜌 for every 𝑥, then [[[[𝑎]]𝜎]]𝜌=[[𝑎]]𝑠. The corresponding statement holds for difference guards.
Referenced from 5 locations
Proof of Lemma 69.1 — Expression correspondence
Proof. For a constant, both sides are 𝑘. For 𝑥 +𝑘, [[[[𝑥+𝑘]]𝜎]]𝜌𝑠𝑦𝑚𝑏𝑜𝑙𝑖𝑐𝑒𝑣𝑎𝑙𝑢𝑎𝑡𝑖𝑜𝑛=[[𝜎(𝑥)]]𝜌+𝑘𝑠𝑡𝑜𝑟𝑒ℎ𝑦𝑝𝑜𝑡ℎ𝑒𝑠𝑖𝑠=𝑠(𝑥)+𝑘𝑐𝑜𝑛𝑐𝑟𝑒𝑡𝑒𝑒𝑣𝑎𝑙𝑢𝑎𝑡𝑖𝑜𝑛=[[𝑥+𝑘]]𝑠. Subtract the two expression equalities to obtain the guard case. ◻
For the input 4, begin with 𝜎0(𝑥) =𝜎0(𝑦) =0. After input, 𝜎1 =𝜎0[𝑥 ↦𝛼0]. On the right branch, 𝜎2 =𝜎1[𝑦 ↦𝛼0 −1]. With 𝜌(0) =4, its concrete instance has 𝑠(𝑥) =4 and 𝑠(𝑦) =3.
★☆☆ Calculate the left-branch symbolic store of 𝖥𝗈𝗋𝗄, and instantiate it at 𝜌(0) = −2.
Referenced from 3 locations
Paths and the symbolic machine
A path condition Φ is a finite conjunction of symbolic difference atoms; the empty conjunction is ⊤. Negation stays inside the language: over integers, ¬(𝑡−𝑢≤𝑘)means𝑢−𝑡≤−𝑘−1.(𝗇𝖾𝗀) A symbolic state is ⟨𝑐,𝜎,Φ,𝑛⟩. The transition relation ⟶𝗌 does not ask whether a newly extended condition is satisfiable. Consequently no solver result can delete a state during the semantic proof.
The characteristic rules are 𝑋⟨𝐢𝐧𝐩𝐮𝐭 𝑥,𝜎,Φ,𝑛⟩⟶𝗌⟨𝐬𝐤𝐢𝐩,𝜎[𝑥↦𝛼𝑛],Φ,𝑛+1⟩S−Input 𝑋⟨𝑥:=𝑎,𝜎,Φ,𝑛⟩⟶𝗌⟨𝐬𝐤𝐢𝐩,𝜎[𝑥↦[[𝑎]]𝜎],Φ,𝑛⟩S−Assign 𝑋⟨𝐢𝐟 𝑔 𝐭𝐡𝐞𝐧 𝑐1 𝐞𝐥𝐬𝐞 𝑐2,𝜎,Φ,𝑛⟩⟶𝗌⟨𝑐1,𝜎,Φ∧[[𝑔]]𝜎,𝑛⟩S−If−T 𝑋⟨𝐢𝐟 𝑔 𝐭𝐡𝐞𝐧 𝑐1 𝐞𝐥𝐬𝐞 𝑐2,𝜎,Φ,𝑛⟩⟶𝗌⟨𝑐2,𝜎,Φ∧¬[[𝑔]]𝜎,𝑛⟩S−If−F. An assertion has a successful successor with Φ ∧[𝑔]𝜎 and a terminal obligation 𝖿𝖺𝗂𝗅(𝜎,Φ ∧¬[𝑔]𝜎,𝑛). A while has the same two forks as an if: the true successor is 𝑐;𝐰𝐡𝐢𝐥𝐞 𝑔 𝐝𝐨 𝑐, and the false successor is 𝐬𝐤𝐢𝐩. The sequencing rules are shared with the concrete machine. Thus a symbolic transition denotes a path choice, not a guess about satisfiability.
Write (𝜌,𝐼)R𝗌𝗒𝗆⟨⟨𝑐,𝜎,Φ,𝑛⟩,⟨𝑐,𝑠,𝐼,𝑛⟩⟩ when 𝜌 ⊧𝖣𝖫Φ, 𝑠(𝑥) =[[𝜎(𝑥)]]𝜌 for every variable, and 𝐼(𝑗) =𝜌(𝑗) for every 𝑗. Failure states are represented by the same store, path, and cursor clauses.
Referenced from 3 locations
Full oracle agreement makes S-Input a genuine lock-step rule. For a terminal model, model-to-test constructs the oracle 𝐼𝜌(𝑗) =𝜌(𝑗); only its consumed prefix is emitted as the finite test.
Suppose a symbolic state ̂𝑞 represents 𝑞, ̂𝑞 ⟶𝗌̂𝑞′, and 𝜌 satisfies the path condition of ̂𝑞′. Then 𝑞 ⟶𝖼𝑞′ for a 𝑞′ represented by ̂𝑞′.
Referenced from 3 locations
Proof of Theorem 69.3 — One-step simulation
Proof. Proceed by cases on the displayed symbolic rule. S-Assign uses lemma 69.1; S-Input extends input agreement at the old cursor. In S-If-T, satisfaction of the new conjunct and expression correspondence make the concrete guard true, selecting the same branch. Equation (neg) gives the false case. Assertions use the same argument and agree on success or failure. Sequence and while cases rebuild the corresponding concrete rule. Store and path components not changed by a rule retain their representation clauses. ◻
If ̂𝑞0 ⟶∗𝗌̂𝑞𝑚, 𝜌 satisfies the final path condition, and ̂𝑞0 represents 𝑞0, then 𝑞0 ⟶∗𝖼𝑞𝑚 with the same terminal command or failed assertion, and ̂𝑞𝑚 represents 𝑞𝑚.
Referenced from 3 locations
Proof of Corollary 69.4 — Finite simulation and path soundness
Proof. Induct on the finite symbolic derivation. Path conditions grow by conjunction, so satisfaction of the final condition implies satisfaction of every prefix. Apply theorem 69.3 at the last step. ◻
For 𝖥𝗈𝗋𝗄, the four terminal obligations are branchassertionpath condition 𝑥≤0pass𝛼0≤0∧𝛼0+1≤2𝑥≤0fail𝛼0≤0∧𝛼0≥2𝑥>0pass𝛼0≥1∧𝛼0−1≤2𝑥>0fail𝛼0≥1∧𝛼0−1≥3. The models −2,1,4 cover the first, third, and fourth rows. The second row is infeasible.
For a loop-free command, unpruned symbolic exploration produces a finite tree. Every terminating concrete execution from an agreeing initial state is represented by a leaf whose path condition its input valuation satisfies.
Referenced from 3 locations
Proof of Theorem 69.5 — Loop-free finite-path coverage
Proof. Induct on the concrete derivation. Deterministic concrete guard evaluation selects exactly one of the two symbolic successors, and lemma 69.1 proves that its added atom is satisfied. Assignment, input, assertion, and sequence follow their matching rules. Without while, each successor contains a proper command subterm after the finite administrative sequence steps, so the finitely branching tree is finite. ◻
★★☆ Derive both successors of the conditional in 𝖥𝗈𝗋𝗄. Give a model of each feasible assertion obligation and show the concrete replay.
Referenced from 3 locations
A certificate boundary for difference logic
Normalize a difference atom to 𝑢 −𝑣 ≤𝑘, allowing a distinguished zero symbol 0. Its graph edge is 𝑣𝑘→𝑢. A certificate is a list of existing edge indices that forms a directed cycle. The trusted checker verifies incidence, closure, and a strictly negative sum.
If the local checker accepts a negative-cycle certificate for Φ, then there is no integer valuation 𝜌 such that 𝜌 ⊧𝖣𝖫Φ.
Referenced from 3 locations
Proof of Lemma 69.6 — Negative-cycle certificate soundness
Proof. Let the checked cycle be 𝑣0𝑘0⟶𝑣1⋯𝑘𝑟−1←←←←←←←→𝑣𝑟 =𝑣0. Satisfaction gives 𝜌(𝑣𝑖+1) −𝜌(𝑣𝑖) ≤𝑘𝑖. Sum the inequalities: 0=𝜌(𝑣𝑟)−𝜌(𝑣0)≤∑𝑖<𝑟𝑘𝑖<0, a contradiction. This proof uses only the checker facts, not the algorithm that proposed the edge list. ◻
The infeasible left-failure condition contains 𝛼0 −0 ≤0 and 0 −𝛼0 ≤ −2. Their two graph edges form a cycle of weight −2, so the checker may prune it. A zero-weight cycle is not a certificate.
If exploration prunes only states carrying a locally accepted negative-cycle certificate, theorem 69.5 still holds.
Referenced from 2 locations
Proof of Theorem 69.7 — Certificate-checked pruning preserves coverage
Proof. The leaf representing a concrete execution has a satisfying valuation by the coverage induction. By lemma 69.6, no prefix of that leaf can carry an accepted certificate. Hence the exploration never prunes the representing branch. ◻
Satisfiability evidence has the opposite shape. A proposed integer map is accepted only after evaluating every atom. It then determines the consumed input prefix, and the concrete machine is replayed deterministically.
If the model checker accepts 𝜌 for a terminal symbolic path and concrete replay agrees with its recorded branches and outcome, the input prefix 𝜌(0),…,𝜌(𝑛 −1) is a concrete test exhibiting that outcome.
Referenced from 2 locations
Proof of Theorem 69.8 — Model-to-test correctness
Proof. The model check establishes the satisfaction premise of corollary 69.4. That corollary produces the concrete execution. The replay check independently compares every branch and the terminal assertion, so a malformed trace or an unrelated model cannot be reported as the test for this path. ◻
An adapter result therefore has three trusted interpretations: 𝗆𝗈𝖽𝖾𝗅(𝜌)↦check atoms and replay,𝗎𝗇𝗌𝖺𝗍(𝐶)↦check 𝐶,𝗎𝗇𝗄𝗇𝗈𝗐𝗇↦retain the path. King introduced symbolic execution as a program-testing method; the modern substitution and path-simulation pattern appears explicitly in the rules and correctness/completeness theorems of de Boer and Bonsangue [Kin76, dBB21]. WiSE mechanizes a richer reachability connection in Coq and separates soundness from the exhaustiveness assumptions of bug search [CS23]. Our theorem is the local SymImp-DL proof, not a transfer of either richer result.
★★☆ An erroneous S-Assign rule stores the source variable 𝑥 instead of [𝑥 +1]𝜎 for 𝑦 :=𝑥 +1. Give a one-step counterexample to simulation, then repair the rule and identify the line of lemma 69.1 that the repaired proof uses.
Referenced from 3 locations
Explosion, subsumption, and guarded merging
A sequence of 𝑚 independent conditionals has 2𝑚 syntactic leaves. In conjunctive SymImp-DL we may safely discard a state ⟨𝑐,𝜎,Φ2,𝑛⟩ when a retained state has the same 𝑐,𝜎,𝑛 and a checked implication Φ2 ⊧𝖣𝖫Φ1: every store represented by the discarded state is already represented by the first. For difference logic, each demanded implication may be checked by adding the complement of its consequent atom and accepting a negative-cycle certificate.
The subsumption test above preserves the represented union of concrete states.
Referenced from 2 locations
Proof of Proposition 69.9 — Subsumption soundness
Proof. If 𝜌 ⊧𝖣𝖫Φ2, the checked implication gives 𝜌 ⊧𝖣𝖫Φ1. Equality of command, store, and cursor leaves every other clause of definition 69.2 unchanged. ◻
General merging does not fit that representation. The separate extension SymImp-DL∨ admits Boolean path formulas and guarded terms 𝗂𝗍𝖾(𝐵,𝑡1,𝑡2). Two states at the same command and cursor merge as (𝜎1,Φ1),(𝜎2,Φ2)⟼∨(𝑥↦𝗂𝗍𝖾(Φ1,𝜎1(𝑥),𝜎2(𝑥)),Φ1∨Φ2).
The merged SymImp-DL∨ state represents exactly the union of the two input states, provided their path conditions are disjoint.
Referenced from 2 locations
Proof of Proposition 69.10 — Guarded-merge representation
Proof. Evaluate the disjunction. If Φ1 holds, disjointness excludes Φ2, and every guarded store component evaluates to 𝜎1(𝑥). Otherwise satisfaction forces Φ2, and it evaluates to 𝜎2(𝑥). Conversely, a valuation represented by either input satisfies the disjunction and the corresponding guard. ◻
The unguarded merge Φ1 ∨Φ2 with a single arbitrarily selected store is unsound. From branches 𝑥 =0,𝑦 =0 and 𝑥 =1,𝑦 =1, selecting the first store under the disjunction admits input 𝑥 =1 paired with 𝑦 =0, a state in neither branch. No simulation or coverage theorem transfers from SymImp-DL to the extension until its guarded-expression cases are proved.
★★★ At a common command and store, decide whether 𝑥 ≤0 ∧𝑦 ≤1 subsumes 𝑥 ≤ −1 ∧𝑦 ≤1. Give the negative-cycle implication certificate. Then explain why changing only one symbolic store invalidates the test.
Referenced from 3 locations
Loops and concolic guidance
Loops require three different contracts.
A depth bound explores only paths with at most that many loop unfoldings. It is path fuel, not evidence that the source loop terminates.
A user invariant 𝐽 is checked by initiation Φ ⇒𝐽, preservation 𝐽 ∧𝑔 ⇒𝗐𝗉(𝑐,𝐽), and exit 𝐽 ∧¬𝑔 ⇒𝑄. The conclusion is the stated partial-correctness postcondition 𝑄, not complete test generation.
Unbounded exploration retains every generated path but has no search termination claim.
Concolic execution carries a concrete oracle beside the symbolic state. It follows the concrete branch while recording the symbolic atom. To schedule an alternate, keep the prefix before branch 𝑗, negate its atom, obtain checked model 𝜌′, and replay from the beginning.
If the checked model 𝜌′ satisfies the retained prefix and negated branch atom, deterministic replay follows that prefix and takes the opposite branch at 𝑗.
Referenced from 2 locations
Proof of Theorem 69.11 — Concolic alternate-input justification
Proof. Apply expression correspondence at every prefix guard. Each has the recorded truth value. At 𝑗, equation (neg) gives the opposite truth value. Determinism fixes the replayed control path through that point. ◻
This is the feedback loop used by DART and CUTE [GKS05, SMA05]. Complete branch coverage follows only when the chosen exploration schedule is fair over the finite loop-free tree and the constraint procedure eventually returns a checked model or certificate for every supported condition. Timeouts and unknown invalidate completeness, not soundness.
From paths to intervals
There is an explicit abstraction to the interval domain of chapter 25. For a finite family of symbolic states at one command, concretize each by its path condition and symbolic store, take their union, then take the least interval box containing that union. For 𝖥𝗈𝗋𝗄, the two post-branch symbolic relations are 𝑥≤0∧𝑦=𝑥+1,𝑥≥1∧𝑦=𝑥−1. Their interval hull is 𝑥,𝑦 ∈[ −∞, +∞]. The implication 𝑥 ≥4 ⇒𝑦 ≥3, which isolates the failed assertion, is lost. The abstraction is sound because every represented store lies in its hull; it is deliberately incomplete because the hull contains stores satisfying neither relation. Symbolic execution and abstract interpretation are related by this map, not identified as implementation variants.
Chapter seminar
The Kappa companion at artifacts/ch69-symimp-dl/ implements the finite SymImp-DL command and path AST, a fuel-bounded symbolic work-list explorer, concrete replay, model checking, and certificate-edge incidence plus negative-cycle checking. Its Agda companion states the store-representation relation and proves expression correspondence for the copy-plus-constant fragment; appendix E records the mechanized boundary.
The first two problems form the practical route. The remaining problems test which hypotheses would have to change in a larger executor.
Suggested first pass.
Replay 𝖥𝗈𝗋𝗄, inspect the rejected zero-weight cycle, and trace one concolic branch reversal before changing the language.
★★★ Practical project.symimp-dl-certificate-checker Run the accepted corpus. Add a command 𝐡𝐚𝐯𝐨𝐜[𝑙,𝑢]𝑥, give its concrete and symbolic rules, and extend the one-step simulation proof. Mutate the cycle checker to accept weight zero and confirm that the unchanged oracle rejects it. Finally record a solver unknown and verify that the path remains scheduled.
Referenced from 4 locations
★★★ Construct the 𝑚-conditional family with 2𝑚 feasible paths. Compare depth-first, breadth-first, and concolic-negation schedules on the first four new branches; state which fairness property each completeness argument needs.
Referenced from 3 locations
★★★ Prove the assignment case for SymImp-DL∨ guarded stores. Give one additional unguarded-merge counterexample in which the path condition is preserved but a correlation between two variables is lost.
Referenced from 3 locations
★★★ Extend the representation relation with a finite heap and commands 𝑥 :=[𝑝], [𝑝] :=𝑥. State the address-definedness premises required by expression correspondence and prove the read case of simulation. Do not assume separation-logic entailment.
Referenced from 3 locations
The proved core ends at conjunctive difference constraints, finite symbolic derivations, local certificate/model checking, and deterministic replay. General SMT completeness, unrestricted-loop coverage, separation-logic heaps, concurrency, quantified theories, and floating point require new semantics or solver theorems. Symbolic execution produces paths and tests; it does not by itself produce a residual executable program.