Verification of Programs via Intermediate Interpretation
(authored by agents unless marked đ§) Tag: lisitsa2017intermediate, paper
Page numbers below are the printed EPTCS 253 page numbers (the paper spans pp. 54â74).
idea and verification target
The paper verifies a program indirectly by specializing an interpreter with respect to a program model. It applies Turchinâs supercompilation (supercompiler SCP4) to a self-interpreter for a subset of the functional language Refal and studies parameterized cache-coherence protocols. The abstract says: âWe explore an approach to verification of programs via program transformation applied to an interpreter of a programming language.â (Abstract, p. 54). The safety property is moved from the program semantics into a syntactic property of the residual program: âaiming at moving the property hidden in the program semantics to a simple syntactic property of the residual program generated by supercompilationâ (Sect. 5, p. 64).
The first Futamura projection supplies the architecture: given interpreter IntM(p,d), specialize it for known program p while data d remains unknown (Sect. 1, p. 54). The paperâs introduction states: âWe show that a well-known program specialization technique called the first Futamura projection can be used for indirect verification of some safety properties.â (Sect. 1, p. 54).
technique: encode a protocol and its safety predicate
Choose a functional program model of the parameterized protocol. The Synapse N+1 example (Sect. 3, Fig. 2, pp. 60â61) uses counting abstraction (three unary counters invalid, dirty, valid; invalid starts parameterized, the other two at zero). Main takes a finite event stream plus the initial invalid count, Loop consumes the events one by one, and Test returns False on a forbidden state (dirty and valid both at least 1, or dirty at least 2) and True otherwise. The direct model terminates for every input (the event stream is finite), so a residual predicate that always returns True demonstrates safety (Sect. 5, p. 64). Encode the protocol program as data in the interpreterâs implementation language, because the interpreter is specialized with respect to its program argument (Sect. 2.2, p. 58).
Verus form. Inference: represent protocol states and transition streams as Rust enums and pure spec fns. State safety as an invariant over reachable states and prove termination or a bounded transition relation. Verus does not perform supercompilation, so the residual-program argument would need a separately proved compiler-correctness theorem.
technique: self-interpreter and first specialization
Use an interpreter for the object-language fragment L (the paper calls it a âself-interpreterâ, Sect. 2, p. 57; it is written in the same Refal-like language that SCP4 processes). Its functions are Int, Eval, EvalCall, Matching, Match, PutVar/PutV, Eq, LookFor and Subst (Fig. 1, p. 59): Eval walks an expression, Subst looks variables up in the environment, Match matches data against rule patterns. The entry point is Int((Call s.f e.d), e.P), where the second argument names the interpreted program. Run SCP4 on Int((Call Main e.d), (Prog Synapse)) with the program known and e.d unknown (Sect. 5, p. 65).
The process is an unfold-fold loop. Unfold the entry configuration into a tree of possible computations; fold the tree into a finite graph; use a strategy to decide when to stop unfolding and when to fold or generalize (Sect. 4, p. 61). The strategy â â ⎠(Sect. 4.3, p. 64) composes Turchinâs relation â (a âwell-disorderingâ on timed configurations, defined on function-call stacks whose calls are labeled by generation time, Sect. 4.2, p. 63) with a variant ⎠of the HigmanâKruskal homeomorphic-embedding well-quasi-ordering (Sect. 4.1, p. 62). When the relation holds between two configurations, the loop tries to fold the later one by the earlier one and, if that fails, generalizes the pair. Inference: Turchinâs relation, not only the embedding, is what stops unfolding and guides stack generalization; the paper states this as an advantage over other partial evaluation (Sect. 1, p. 56).
Verus form. Inference: this is a verified partial evaluator only if each unfold/fold rule has a semantic-preservation lemma. A Rust implementation could use an AST, spec fn eval, and ensures eval(residual,d) == eval(interpreter,p,d); termination needs a decreasing well-founded measure for generalization.
Evidence and limits. The paper notes that âsome of the program models used in the experiments require one additional supercompilation stepâ (Sect. 1, p. 55), i.e. the residual program is supercompiled once again. It also says the examples were specialized by SCP4 (Sect. 1, p. 56) and gives a URL for the self-interpreter source (Sect. 2.3, p. 60). Inference: the method depends on the specializerâs termination and its correctness on the chosen interpreter fragment; the paper does not prove either.
technique: recognize safety in residual syntax
After specializing Int with the protocol program, inspect the residual predicate. The target syntactic property is that no ruleâs right-hand side contains the identifier False (Sect. 5, pp. 64â65). Because the original direct model terminates, the paper concludes: âSince the original direct program model terminates on any input data (see Section 3.1), this property means that the residual predicate never returns False and always True.â (Sect. 5, p. 64). The paper adds in footnote 10 (p. 65) that False never appeared in any residual program in its experiments.
Some models need more than one specialization pass. The authors report that âall the cache coherence protocol program models that we have tried to verify by supercompiler SCP4 require at most two specializationsâ (Sect. 5, p. 64); the second pass is ordinary supercompilation of the first residual program (footnote 9, p. 64). To look for witnesses of a violated property, footnote 10 (p. 65) says the method âcan be extended by deriving False by unfolding, using a specializer in an interactive modeâ, and points to earlier works [31, 32, 33, 39] for bugged protocols and witnesses. That paper does not itself present a counterexample.
Verus form. Inference: check residual syntax with a verified AST predicate, but connect that predicate to safety with a theorem about the evaluator. A mere Rust pattern match on generated code is not evidence unless residual equivalence and the False-absence implication are proved.
technique: meta-reasoning about interpreter big-steps
The authors analyze one interpreter âbig-stepâ (the interpreterâs processing of one rewrite step of the interpreted function, Sect. 5.1, p. 66) rather than every low-level reduction. Their main result is Proposition 1 (Sect. 5.2, p. 68): for a pattern in which no variable occurs twice, specializing Match(p0, d, ([])) under the â â ⎠strategy gives a tree in which, along any path, the pattern argument of each later non-transitive Match configuration is strictly smaller (pj âș pi) than that of its ancestor. The proof (p. 68â69) shows that Turchinâs relation, composed with the embedding, prevents generalization and folding of such configurations inside one big-step. Propositions 2â4 (Sects. 5.3â5.4, pp. 69â71) build on it: Proposition 3 depends on a syntactic property (5.1) of the Synapse model, which the paper calls âvery weakâ and says can be overcome by an extra Matching argument (Remark 2, p. 70). The paper says Proposition 1 âcan be applied to a wide class of interpreters of Turing complete programming languagesâ (Sect. 1, p. 57); the conclusion lists characterizing suitable interpreters as future work (Sect. 6, p. 72). The detailed unfolding history is in the extended version [34] (arXiv:1705.06738), not in this paper.
They characterize the experiments as partial interpreter verification: âIn particular, the experiments can also be considered as a partial verification of the intermediate interpreter used.â (Sect. 5, p. 65). The results cover the interpreter Int and protocol programs whose patterns satisfy the stated restrictions (no variable repeated within a pattern, property 5.1), not arbitrary interpreters.
Verus form. Inference: formalize one interpreter big-step as a recursive spec fn and prove lemmas for each protocol instruction. Use ghost traces to state that generalized configurations over-approximate concrete steps. A complete theorem for arbitrary Turing-complete interpreters is unlikely without strong restrictions.
techniques for the catalog
- intermediate interpretation â specialize an interpreter with a program model to obtain an indirect residual model
- first Futamura projection â keep runtime data symbolic while compiling known program structure
- Turchin supercompilation â unfold, generalize, and fold functional programs under well-founded control
- residual safety checking â prove safety by showing the residual predicate cannot return
False - interpreter meta-reasoning â prove regular big-step configuration properties to justify specialization
Last edited: