Program Logics for Homogeneous Generative Run-Time Meta-Programming

This paper provides the first program logic for homogeneous generative run-time meta-programming---using a variant of MiniML by Davies and Pfenning as its underlying meta-programming language. We show the applicability of our approach by reasoning about example meta-programs from the literature. We also demonstrate that our logics are relatively complete in the sense of Cook, enable the inductive derivation of characteristic formulae, and exactly capture the observational properties induced by the operational semantics.


Introduction
Meta-programming (MP) is the generation or manipulation of programs, or parts of programs, by other programs, i.e. in an algorithmic way. Many programming languages, going back at least as far as Lisp, have explicit MP features. These can be classified in various ways such as: generative (program creation), intensional (program analysis), compile-time (happening while programs are compiled), run-time (taking place as part of program execution), heterogeneous (where the system generating or analysing the program is different from the system being generated or analysed), homogeneous (where the systems involved are the same), and lexical (working on simple strings) or syntactical (working on abstract syntax trees). Compilers use MP to compile programs; web system languages such as PHP use MP to produce web pages containing Javascript; Javascript (in common with some other languages) performs MP by dynamically generating strings and then executing them using its eval function. In short, MP is a mainstream activity. ∈ fv(M ) in both cases. We assume Barendregt's variable condition, and tacitly rename bound variables where necessary.
The reduction relation → is unchanged from Pcf for the Pcf-fragment of Pcf dp , and adapted to Pcf dp as follows. First we define reduction contexts, by extending those for Pcf with a construct for unquoting. A typing environment (Γ, ∆, ...) is a finite map x 1 : α 1 , ..., x k : α k from variables to types. The domain dom(Γ) of Γ is the set {x 1 , ..., x n }, assuming that Γ is x 1 : α 1 , ..., x n : α n . We write ǫ for the empty environment. The typing judgement is written Γ; ∆ ⊢ M : α where we assume that dom(Γ) ∩ dom(∆) = ∅. We write ⊢ M : α for ǫ; ǫ ⊢ M : α. We say a program M is closed if ⊢ M : α. We call ∆ a modal context in Γ; ∆ ⊢ M : α. We say a variable x is modal or modally typed in Γ; ∆ ⊢ M : α if x ∈ dom(∆). Modal variables represent code inside other code, and code to be run. The key type-checking rules are given in Figure 1. Typing for constants and first-order operations is standard.
Noteworthy features of the typing system are that modal variables cannot be λ-or µabstracted, that all free variables in quasi-quotes must be modal, and that modal variables can only be generated by unquotes. [14] gives detailed explanations of this typing system and its relationship to modal logics.
Contextual congruence. By Γ;∆;α (often abbreviated to just ) we denote the usual typed contextual precongruence: if Γ; ∆ ⊢ M i : α for i = 1, 2 then: M 1 Γ;∆;α M 2 iff for all closing context C [·] such that ⊢ C[M i ] : Unit (i = 1, 2) we have We write ≃ for ∩ −1 and call ≃ contextual congruence. Other forms of congruence are possible, but we will use ≃ in the rest of this paper. Our choice means that code can only be observed contextually, i.e. by running it in a context. Hence for example M and λx.M x are contextually indistinguishable if x / ∈ fv(M ), as are 1 + 2 and 3 . This facilitates a smooth integration of the logics for Pcf dp with the logics for Pcf. 3 2.3. Basic lemmas. We now present a collection of simple facts that we use later. Proof. All are straightforward yet laborious when carried out in detail, and can be tackled with standard techniques of operational semantics [16,27]. As just one example, take (0(12)): if for all n we have W n V , but at the same time µg.λx.M V , we could find a closing context C[·] such that C[µg.λx.M ] ⇓ but C[V ] ⇑. But C[µg.λx.M ] ⇓ means that the computation towards a value is of finite length, hence only a finite number of recursive calls were made, so some n must exist, such that C[W n ] ⇓. This in turn means C[V ] ⇓ by our assumptions, contradicting C[V ] ⇑.

Some example programs.
Lifting is an important construct in generative MP, taking a run-time value and converting it into its quasi-quoted equivalent. For example 3 is the lifting of 3, and λx α .x is the lifting of the identity function of type α.
We call a type α basic if it does not contain the function space constructor, i.e. if it has no sub-expressions of the form β → β ′ . In Pcf dp , lifting takes an arbitrary value V of basic type α, and converts it to code V of type α . Note that we cannot simply write λx. x because modal variables (i.e. variables free in code) cannot be λ-abstracted. For α = Int the function is defined as follows: lift Int def = µg.λn Int .if n ≤ 0 then 0 else let x = g(n − 1) in x + 1 .
Note that lift Int works properly only on non-negative integers. Note also that lift Int 3 evaluates to 0 + 1 + 1 + 1 , not 3 . In more expressive meta-programming languages such as Converge the corresponding program would evaluate to 3 , which is more efficient, although 0 + 1 + 1 + 1 and 3 are observationally indistinguishable in Pcf dp .
Lifting is easily extended to Unit and Bool, but not to function types, because of Pcf dp 's inability to abstract modal variables. For basic types α we can define lifting as follows.
We reason about lift Int in Section 4.
Another example is the function eval, a function of type α → α for running code [14]. This function is essentially a wrapper around unquoting: eval def = λx α .let y = x in y.
The last example in this section is the well-known power generative MP program which creates a function that raises a number to a given power [31]. Although somewhat contrived, this function shows how generative MP can be used for efficiency purposes: rather than using run-time recursion on every call, HGRTMP turns this into a fixed expression. In essence, if a program contains many applications (λna.a n ) 3, it makes sense to specialise such applications to λa.a × a × a. A simple encoding of power in Pcf dp is the following: This function has type ⊢ power : Int → Int → Int . This type says that power takes an integer and returns code. That code, when run, is a function from integers to integers. power can can be used as follows:

A logic for total correctness
Our logic is a Hoare logic with pre-and post-conditions in the tradition of logics for ML-like languages [3,4,20,21]. In this section we define its syntax and semantics.
3.1. Syntax and types. Expressions, ranged over by e, e ′ , ... and formulae A, B, ... of the logic are given by the grammar below, using the types and variables of Pcf: Our logical language is an extension of first-order logic with equality (and axioms for arithmetic e.g. Peano arithmetic or some set theory). Other quantifiers, logical constants like T, F and propositional connectives like ⊃ (implication) are defined by de Morgan duality. Quantifiers range over values of appropriate type. Constants c and operations op are those of Section 2.2.
Our logic extends that of Pcf [18,19,20] with a new code evaluation predicate u = m {A}. It says that u, which must be of type α , denotes (up to contextual congruence) a quasi-quoted program M , such that whenever M is unquoted and executed, it converges to a value; if that value is denoted by m then A makes a true statement about that value. We recall from [18,19,20] that u • e = m{A} says that (assuming u is of the function type) u denotes a function, which, when fed with the value denoted by e, terminates and yields another value. If we name this latter value m, A holds. The variable m is an anchor in both u • e = m{A} and u = m {A}, bound within scope A. The free variables of e and A, written fv(e) and fv(A), respectively, are defined by the following clauses: In the presentation below we often use the following abbreviations and conventions: − A -x means that x / ∈ fv(A). − x ⇓ means ∃y α .x = y, assuming that x has type α, y is fresh and not modally typed.
This abbreviation is interesting primarily when x is modally typed.  In the last two cases we assume that if x = u then e ′ must be a variable.
The judgements for total correctness are of the form The variable m is the anchor of the judgement, is a bound variable with scope B, and cannot be modal. The judgement is to be understood as follows: if A holds, then M terminates to a value (more precisely, the closure of M with arbitrary values meeting the precondition 4 ), and if we denote that value by m, then B holds. In other words, our judgements are entirely conventional for total correctness program logics. If a variable x occurs freely in A or in B, but not in M , then x is an auxiliary variable of the judgement {A} M : m {B}.
Typing expressions, formulae and judgements. Program logics are typed (although for simple programming languages, types can be implicit), and ours is no exception. We use the following typing judgements. − For expressions, the typing judgement is Γ; ∆ ⊢ e : α. − For formulae, the typing judgement is Γ; ∆ ⊢ A. − For judgements, the typing judgement is Γ; The typing rules for all three judgements are given in Figure 2. Several points are worth noting. − The anchor in u = m {A} is modal, while it is not modal in u • e = m{A} and in judgements. − Normal quantification ∀x.A quantifies only non-modal variables x. From now on, we assume all occurring programs, expressions, formulae and judgements to be well-typed.
Examples of assertions & judgements. We continue with a few simple examples to help explain the use of our logic.
− The assertion m = 3 , which is short for m = x {x = 3} says that m denotes code which, when executed, will evaluate to 3. It can be used to make the following assertion on the program 1 + 2 : Let Ω α be a non-terminating program of type α (we usually drop the type subscript).
When we quasi-quote Ω, the judgement {T} Ω : m {T} says (qua precondition) that Ω is a terminating program. Indeed, that is the strongest statement we can make about Ω in a logic for total correctness, cf. Section 5. − The assertion ∀x Int .m •x = y{y = x } says that m denotes a terminating function which receives an integer and returns code which evaluates to that integer. Later, we use this assertion when reasoning about lift Int which has the following specification: says that u denotes a function which receives an integer n as argument, to return code which when evaluated and fed another integer x, computes the power x n , provided n ≥ 0. We can then show that can be used to specify the evaluation function from Section 2:

3.2.
Models and the satisfaction relation. This subsection formally presents the semantics of our logic. We begin with the notion of model. Our models are conventional, with the key difference from the models of Pcf-logics [20] being that modal variables denote possibly non-terminating programs. Let Γ, ∆ be two contexts with disjoint domains (the idea is that ∆ is modal while Γ is not). A model of type Γ; ∆ is a pair (ξ, σ) such that: − ξ is a map from dom(Γ) to closed values such that ⊢ ξ(x) : Γ(x); − σ is a map from dom(∆) to closed programs ⊢ σ(x) : ∆(x). We use the following conventions in our subsequent presentation: We can now present the semantics of expressions. Let Γ; ∆ ⊢ e : α and assume that (ξ, σ) is a Γ; ∆-model, we define [[e]] (ξ,σ) by the following inductive clauses: The satisfaction relation for formulae has the following shape. Let Γ; ∆ ⊢ A and assume that (ξ, σ) is a Γ; ∆-model.
The concept of upwards-closedness is important in the context of completeness and defined as follows. Let A be a formula typeable under Γ, u : α; ∆ ⊢ A. We say A is upwards closed at u if whenever V W then also for all suitable ξ and σ.
For defining the semantics of judgements, we need to explain what it means to apply a model η def = (ξ, σ) to a program M , written M η. We also refer to M η as the closure of M with η. That is defined as usual, using the following inductive clauses, where we assume that free variables are not caught when a model is moved under a binder: We record the following simple fact for subsequent use. This is the standard notion for total correctness, adapted to the present logic.
A note on models. The reader might wonder why our notion of model uses values (which always terminate) as denotations for non-modal variables, but general programs (which may not terminate) for modal variables. The answer is a combination of two factors: − Our logic is part of a tradition of constructing Hoare logics, where models provide denotations for the free variables of the program that a judgement is about. Moreover, the type of the denotation should be the same as the type of the corresponding free variable. This simple model-building heuristic has proven to be robust for a wide variety of programming languages [5], and we decided to build our logic for Pcf dp in the same way. − Although our logic is for total correctness, we can still make assertions about nonterminating programs, and programs that contain non-terminating sub-programs, for example: Since judgements like {T} λx.Ω : u {T} are already derivable in the logic for total correctness for Pcf, the question arises as to how models used in logics for Pcf need only values as denotations? The answer is that there is a substantial difference between quasiquotes and λ-abstractions in how the (non-terminating) sub-programs they harbour are accessed. The only way Ω can be executed in λx.Ω is by application. This does not involve creating a new free variable bound to Ω (which would need a denotation in a corresponding model).
In contrast, when unquoting a Pcf dp quasi-quote, e.g. let x = Ω in M, then x is free (as well as modal) in M , and will be bound to Ω, but without attempting to evaluate Ω. This is quite different from evaluating e.g. let x = λx.Ω in M , as we can see when comparing the evaluation of both terms side-by-side.
.Ω/x] Models must accommodate this behaviour, and allowing modal variables to denote nonvalue programs does just this.

Axioms and rules.
We have now ready to present the rules and axioms of our logic.
Axioms. The axioms come in two forms: those that are germane to Pcf dp 's metaprogramming extensions, and those that are not. All axioms for the Pcf logics of [18,19,20] remain valid, and are listed in Appendix A for completeness. Here we present only the axioms for the logical constructs not already available in the logics for Pcf, i.e. for the code evaluation predicate x = m {A}.
Tacitly, we assume typability of all axioms. That means not only that all axioms must be typable, but conversely also that whenever an axiom is typable, it is a valid axiom. The axioms are given in Figure 3. The presentation uses the following abbreviations: Ext q (xy) stands for ∀a.(x = z {z = a} ≡ y = z {z = a}). Axiom (q1) says that if the quasi-quote denoted by x makes A true (assuming the program in that quasi-quote is denoted by y), and in the same way makes B true, then it also makes A ∧ B true, and vice versa. Axiom (q2) says that if the quasi-quote denoted by x contains a program, denoted by y, and makes ¬A true, then it cannot be the case that under the same conditions A holds. The reverse implication is false, because ¬x = m {A} is also true when x denotes a quasi-quote whose contained program is diverging. But in this case, x = m {¬A} is still false due to lacking termination. Next is (q3): x = m {A} says in particular that x denotes a quasi-quote containing a terminating program, so ¬x = m {B} can only be true because B is false. Axioms (q4, q5) let us move formulae and quantifiers in and out of code-evaluation formulae, as long as free variables do not become bound in the process nor bound variables become free. Axiom (q6) allows us to weaken the assertion inside the code evaluation predicate. The reverse implication is trivially false. The axiom (term) formalises that denotations of non-modal variables always terminate. The axiom (term q ) enables us explicitly to express as a logical formula the fact that x = m {A} guarantees that the code denoted by x terminates. The axiom (q α ) may appear confusing on first sight, but it states something simple: namely that we can easily nest code evaluation predicates. The equality m = n relates the two anchors. The axiom (div) simply states that not every quasi-quote holds code that terminates when executed. The code-extensionality axiom (ext q ) formalises what it means for two quasi-quotes to be equal: they must contain observationally indistinguishable code. The corresponding axiom (ext) for functions can be found in Appendix A together with other axioms for the Pcf-part of the language. Note that it is vital for x and y to be non-modal. The direction Ext q (xy) ⊃ x = y is unsound otherwise, because Ext q (xy) cannot distinguish between e.g. appropriately typed Ω and Ω .
Rules. The rules of inference can be found in Figures [18,19,20]) and used without further comment. All rules are typed. The typing of rules follows the corresponding typing of the programs occurring in the judgements, but with additions to account for auxiliary variables. Rather than detailing the typing for all rules, we exhibit an example. The typing rule for the unquote-construct is this: Figure 4: Pcf dp inference rules for total correctness.
The corresponding typing for [Unquote + ] is rather similar: All rules in Figure 5 and most rules in Figure 4 are standard and unchanged from [18,19,20] with three significant exceptions, explained next.
[Var] adds x ⇓, i.e. ∃a.x = a in the precondition. By construction of our models, x ⇓ is trivially true if x is non-modal. If x is modal, the situation is different because x may denote a non-terminating program. In this case x ⇓ constrains x so that it really denotes a value, as is required in a logic for total correctness.
[Quote] says that M always terminates (because the conclusion's precondition is simply T). Moreover, if u denotes the result of evaluating M , i.e. M itself, then, assuming A holds (i.e., given the premise, if M terminates), u contains a terminating program, denoted m, making B true. Clearly, in a logic for total correctness, if M is not a terminating program, A will be equivalent to F, in which case, [Quote] does not make a non-trivial assertion about M beyond stating that it terminates.
[Unquote + ] is similar to the usual rule for let x = M in N which is easily derivable using [Abs, App]: The rule for let x = M in N is more difficult because a quasi-quote always terminates, but the code it contains may not. Moreover, even if M evaluates to a quasi-quote containing a divergent program, the overall expression may still terminate, because N uses the destructed quasi-quote in a way that cannot detect divergence. An example is as follows: holds. If M evaluates to a quasi-quote containing a divergent program, B would be equivalent to F. This is because in a logic for total correctness, m = x {C} means that the quasi-quote denoted by m must contain a converging program. Hence the only way that as a whole is equivalent to T, i.e. conveys no information Hence, since x does not occur freely in E, the denotation of x is not constrained by the left premise, hence the termination behaviour of N cannot depend on x. In other words N uses whatever x denotes in a way that makes the termination or otherwise of N independent of x. The additional formula E enables us easily to carry information from the conclusion of the assertion for M to the premise of the assertion about N .
Finally, the requirement m = · ⊃ m = x in the precondition of the assertion for N makes the following fact available for reasoning about N : whenever M evaluates to a quasi-quote M ′ , then M ′ is bound to x. This fact is not used in the reasoning about example programs in this paper. However, it appears to be vital for proving completeness, see Proposition 5.5 in Section 5. 5 In reasoning about programs we typically use the following simpler rule.
In many derivations, E is simply T and omitted. Clearly, [Unquote] is easily derivable The rule [Conseq-Kl] is slightly more elaborate than Hoare's original rule of consequence, present already in [17], and repeated below for comparison: [Conseq] is usually sufficient in practise. But for proving relative completeness in Section 5, [Conseq-Kl], going back at least as far Kleymann [25], is more convenient. Using [Conseq-Kl], Hoare's [Conseq] is easily derivable. We note that the rules for programs in the Pcf-fragment of Pcf dp are the same as those in the logic for Pcf [20], apart from a slightly different presentation. The only apparent difference is in the respective rules for variables (with Pcf dp on the left, Pcf on the right): However, this is misleading for two reasons: − For non-modal variables x, by axiom (term), x ⇓ always holds, so A[x/m] can be inferred trivially from A[x/m] ∧ x ⇓ and vice versa. − We could have split the Pcf dp rule for variables into two as follows: x Indeed that is what we will do later in Section 5. The reason for using a combined rule in this Section is economy of presentation. The ability to reason about the Pcf-fragment in our logic for Pcf dp is significant for two reasons. First, on the theoretical side, it shows that adding MP features is a modular extension of our base language and base logic, raising the intriguing question if modularity can be retained in situations where the base language has rich effects like state or exceptions, or where the MP features are more extensive (e.g. compile-time meta-programming), or allow MP on open code (that is, code with free variables). Secondly, on the pragmatic side, it makes life easier, because the specification and verification of programs and program parts that do not use MP features do not have to pay a price in terms of additional complexity vis-a-vis the logic for Pcf.

Soundness.
We now establish that the axioms and rules introduced in the previous subsection are sound.
Proofs for axioms and rules not relating to Pcf dp 's meta-programming extensions are straightforward extensions of the corresponding proofs for Pcf-logics like [4,20,22,38] and mostly omitted. Before embarking on proofs, we collect facts that will be useful later.
Proof. The content of this proposition is straightforward, hence proofs are omitted.
Proof [of Theorem 3.2.1]. The proof of axioms (q1) -(q5) for quasi-quotes are essentially just trivial instances of first-order logical laws and omitted, except that we explicitly prove (q3) as a representative example.
To establish (q3), let η def = (ξ, σ) be an appropriately typed model. Then we reason as follows: 1 The reverse implication is similar.
The axiom (term) is immediate from the definition of models.
The reverse implication is immediate. The soundness of (div) is immediate from the construction of the model and the satisfaction relation: (div) states that not every quasi-quote contains a terminating program, e.g. Ω .
For the reverse implication we assume that x and y are both of type α , and that η = (ξ, σ).
η |= Ext q (xy) which means there are two cases for any chosen value V of appropriate type, where η ′ = (ξ · a : V, σ). For [Var] we have two subcases, depending on whether the variable under assertion is modal or not. The latter case is trivial, so we deal only with the former (which is also easy). In this case the rule is typed as follows: Then in particular η |= ∃a α .x = a. By the semantics of quantification, we know that some value V must exist with (ξ · a : V, σ · x : M ) |= x = a. Thus by definition of the interpretation of equality, Hence, since xη = M immediately xη ⇓ V which means the program under assertion terminates. From (3.1) we also get that This concludes the case of [Quote].
Finally, we establish the soundness of [Unquote + ]. Choose a Γ; ∆-model η def = (ξ, σ) such that η |= A. By the (IH) we know that Now we have two cases: We start with the former: , (IH) Now we can consider the reductions of (let x = M in N )η: By Line 10 and Proposition 3.3.2 we know that (ξ · u : W, σ) |= D since m, x / ∈ fv(D). Now we consider the second case η ′ |= B. In this simpler case we reason as follows: As in the previous case we now consider the reductions of (let x = M in N )η: By Line 7 and Proposition 3.3.2 we know that (ξ · u : W, σ) |= D since m, x / ∈ fv(D). This concludes the reasoning for [Unquote + ].
We now verify the structural rule [∧-⊃] to demonstrate that the soundness of the structural rules (which have already been shown sound in the context of Pcf [20]) is not affected by our extension of Pcf with facilities for meta-programming. The proofs for the other structural rules are similarly straightforward.
Let η = (ξ, σ) be an appropriately typed model. Then we reason as follows: Note that as with Pcf, the condition that the program be a value in [∧-⊃] cannot be dropped in a logic for total correctness, because Finally, we prove sound [Rec], the rule for recursion in a total correctness setting. It's compellingly simple form was first given in [19]. Here we need to show that the addition of MP facilities does not void soundness. This is straightforward.
This last proof is unchanged from the soundness proof for [Rec] in Pcf, although 'under the hood' some used propositions need additional work to do with the generalised language and notion of model. This is also true for all other rules and axioms that involve only Pcf syntax.

Reasoning examples
We now put our logic to use by reasoning about some of the programs introduced in Section 2. The derivations use the abbreviations of Section 3 and and often omit steps that are trivial, or irrelevant from the perspective of meta-programming.  {T} let x = 1 + 2 in x + 3 : m {m = 6 } We derive the assertion in small steps to demonstrate how to apply our logical rules.
Example 4.4. We now show that when a quasi-quote containing a non-terminating subprogram is destructed, but the resulting sub-program is not used, the overall program still terminates. This reflects the operational semantics in Section 2.
{T} let x = Ω in 1 + 2 : m {m = 3 } The derivation follows: Example 4.5. This example extracts a non-terminating program from a quasi-quote, and injects it into a new quasi-quote. Our total-correctness logic cannot say anything non-trivial about the resulting quasi-quote (cf. Example 2): The derivation is straightforward.
The examples below make use of the following convenient forms of the recursion rule and Example 4.6. We now reason about lift Int from Section 2. In the proof we assume that i, n range over non-negative integers. Let A u n def = u • n = m{m = n }. We now establish the following assertion from Section 3: Example 4.7. We close this section by reasoning about the staged power function from Section 2. Assuming that i, j, k, n range over non-negative integers, we define B u n def = u • n = m{m = y {∀j.y • j = j n }}. In the derivation, we provide less detail than in previous proofs for readability. for all appropriate A, B? Relative completeness means that in the presence of an oracle for the ambient theory of arithmetic, e.g. Peano arithmetic or ZFC set-theory (used with [Conseq]), the logic can syntactically derive all semantically true assertions, and reasoning about programs does not need to concern itself with models. Another way of saying this is that in relatively complete program logics, the expressive power of the ambient theory of arithmetic is the only source of incompleteness. 6 The second question investigates if the program logic makes the same distinctions as the observational congruence. In other words, is the following characterisation true? Observational completeness means that the operational semantics (given by the contextual congruence) and the axiomatic semantics given by logic cohere with each other. We believe that observational completeness is a key property of program logics because it guarantees that any operationally relevant program property can be expressed.
If a logic is observationally complete, we may ask the third question above about characteristic formulae: Such formulae are called characteristic. If characteristic formulae always exist, the semantics of each program can be expressed succinctly in the logic, using just a pair of formulae, and we call the logic descriptively complete [19]. The reason we use the contextual precongruence from Section 2 in the definition of characteristic formulae above, and not the congruence ≃, is that our logic is for total correctness, and cannot express program divergence. More precisely, the following holds: This indicates that in logics for total correctness, pairs A, B talk about upwards-closed sets of programs. Upwards-closed sets with a least member (up to ≃) are especially nice, and for each such set, its least element can be seen as representing the set.
Proof strategy. We prove the three completeness theorems promised at the beginning of this section following ideas developed in [5,19,21,38], but adapted to the present logic. The proofs are broken down into the following steps where we: (1) make precise the relevant notion of characteristic formula.
(2) present an inference system for characteristic formulae.
(3) prove that the inference system computes characteristic formulae. (4) show that the characteristic formulae are derivable using the rules and axioms of Section 3. (5) use characteristic formulae to prove observational completeness. (6) employ characteristic formulae to prove relative completeness. 5.1. Formalising characteristic formulae. We now precisely define we mean by characteristic formulae. Our definition is split into three parts, one guaranteeing the soundness of characteristic formulae, one to do with termination, and one that is about divergence-related aspects of program behaviour.   A TCAP of M denotes a set of programs whose minimum element (up to ≃) is M , and in that sense characterises that behaviour uniquely up to . As mentioned above, characterisation up to ≃ is not possible in a logic for total correctness. Logics of partial correctness suffer from a dual problem because they cannot express convergence. To achieve logical characterisation up to ≃ in a single pair of formulae, we need both total and partial correctness, ideally combined into a logic of general correctness, see e.g. [5].
An inference system for TCAPs. The definition of TCAPs is semantic. We now present an algorithm that enables us to derive TCAPs for each Pcf dp -program by induction on the typing derivation. The rules are given in Figure 6 and follow ideas from [5,19,21,38]. Rulenames are derived from those in Figure 4 but with a superscript (e.g. [Var t ] instead of [Var]). The assertion language is that of Section 3 with one extension: quantification over modal variables. That means the assertions are now generated by the following extended grammar: We call ∀x α .A modal universal quantification, where the bound variable x ranges over arbitrary programs, not just values. For ∀x α .A to be well-formed, x must be modal and of type α in A, and modal quantification is typed as follows: Γ; ∆, x : α ⊢ A Γ; ∆ ⊢ ∀x α .A The semantics of modal quantification is given by the following: Since the addition of modal quantification does not change our notions of model and satisfaction relation, all proofs in Section 3 stay valid.
The existential modal quantifier ∃x α .A is given by de Morgan duality. We often drop type annotations in modal quantifiers, e.g. writing ∀x .A. Axiomatising modal quantification uses the standard axioms for first-order quantifiers with the following addition: ¬∀x .
x ⇓ This axiom states that not all modal variables denote terminating programs, which is immediately true from the model. We write ⊢ tcap {A} M : u {B} to indicate that the assertion {A} M : u {B} is derivable using the rules of Figure 6 only (i.e. without application of rules from Figures 4 and 5). As before, we assume that assertions, programs and rules are well-typed, and newly introduced variables are always fresh.
Before presenting proofs, we make a small observation: the pre-and postcondition pairs in Figure 6 constrain exactly the free variables of a program, together with the anchor:  Figure 6 are either unchanged from the corresponding rules in Figure 4 or have already been used in some of [5,19,21,38]. We now give an informal explanation of the rules not already in Figure 4.
[ [Op t ] computes all TCAPs for operands in the premise. As an operation (e.g. addition) terminates exactly when all operands terminate, the precondition of the rule's conclusion is simply the conjunction of all preconditions for operands. The postcondition of the rule conclusion states that the result of the computation is the operation applied to some operands, and each operand is constrained by the postconditions of the rule premises. Depending on the operations used, additional constraints might be needed in the precondition: for example division M/N requires N to evaluate to a non-zero value.
[App t ] works as follows. In a call-by-value language an application M N terminates if: the evaluations of both M and N terminate to V and W , respectively; and, in addition, the application V W itself terminates. The first two requirements are stated by putting A 1 ∧ A 2 into the precondition of the conclusion on the rule. Here A i is obtained recursively by computing the TCAPs of M and N , so e.g. [If t ] makes the following assertion. A conditional terminates exactly when the condition terminates and the branch chosen by the conditional does, too. This is formalised by: As exactly one of B[t/m] and B[f/m] is true and exactly one is false, one implication is vacuously true, and the other requires the corresponding A i to hold, giving the correct termination condition. For the same reason exactly one of must be false, and one must hold exactly when the corresponding B i holds. Since these two formulae are connected by an outer disjunction, the postcondition of the rule's conclusion give exactly the behaviour of the program.
[Unquote t ] This rule is the main intellectual novelty of the present section. Clearly, let x = M in N terminates exactly when: − M evaluates to some M ′ , and The former is reflected in the precondition of the conclusion of the rule by adding A 1 , which controls the termination of M . The second condition is more complicated, because it is possible that M evaluates to e.g. Ω , and yet N [Ω/x] terminates, for example in let x = Ω in x This case is covered by the clause ∀x .A 2 . We see here the reason for using modal quantification. If the quantifier were to range over values only, programs such as which do not terminate, would cause trouble without modal quantification, because only when x is bound to a non-terminating term would N diverge. The TCAP for x at u is (x ⇓, x = u), making ∀x.x ⇓, unlike ∀x .x ⇓, trivially true, leading to the erroneous precondition T for (5.1).
One may also ask, why not use a simpler precondition like in the conclusion of [Unquote t ]? The answer is that this would also be too weak for completeness. To see why, consider the program: 3) is clearly sound, but the precondition too weak to capture the full meaning of the program let x = Ω in 8.
Next we look at the postcondition. It says that the result of evaluating let x = M in N is, among other things, as described by B 2 , which is the postcondition of N at u. However, by Observation 5.2, B 2 contains x as free variable. We hide it with a modal existential quantifier. But x cannot be arbitrary, as it is the result of unquoting what M evaluates to. Note that the postcondition B 1 speaks about M named m. So x is the unquoting of m. We cannot assert m = x {...}, because that would stipulate that M evaluates to a term that, when unquoted, terminates, which cannot be guaranteed (e.g. if M is Ω ). To deal with this issue, we explicitly require that x is the unquoting of m, provided m denotes a terminating meta-program: which means, if M converges to a quasi-quote M ′ and M ′ converges, say to V , then x describes this value V . Finally, we hide m by an existential quantifier, and constrain m by B 1 . Note that the conditional constraining of x in (5.4) does not hold if M diverges, or converges to e.g. Ω . In the former case, the precondition must be (equivalent to) F, because the whole program diverges. In the latter case, a logic for total correctness cannot make an interesting assertion about the use of x in N . Proof. We begin with (1). Assume that η = (ξ, σ) |= {A} N : u {B}. There are two cases.
For (2) we begin with the (if) direction. Assume η |= {E} N : u {B} where E ⊃ A and η |= E. Hence N η ⇓ V with (ξ · u : V, σ) |= B by soundness. From E ⊃ A we get η |= A, but then by (closure-2 ) it must be the case that M η V which in turn implies M η N η since V ≃ N η by Proposition 2.1.0 (6).
For the reverse direction, suppose (A, B) is a TCAP for M at u. We must show that (closure-2 ) holds. So let η = (ξ, σ) |= A and (ξ · u : V, σ) |= B, with V being closed and appropriately typed. Define:  Var t : We proceed as follows.
Var t m : This case is exactly like the previous, except that the last line is omitted.
Op t : We treat the special case of addition. 1 App t : The proof for this rule is the sole place in this paper where the (q α ) axiom is used.
It is an open question as to whether this axiom is strictly needed, but we have not yet managed without it.
If t : In the derivation of this rule we make unusually heavy tacit use of the [Conseq] rule.
Unquote t : This is the last step in our proof. The derivation uses [Unquote + ], the only use of that rule in the paper. It is unclear if the simpler version of [Unquote + ] presented in Section 3 is strong enough to carry out this part of the proof. 1 It remains to justify Line 9. Rewriting in the equivalent form ((∀x .A 2 ) ∨ A 2 ) ⊃ A 2 lets us see immediately that Line 9 is true. Proposition 5.5 together with the soundness of the rules in Figure 4 immediately implies the soundness of the TCAP rules. We record this fact: Corollary 5.6. The TCAP rules in Figure 6 are sound. Next we deal with [Unquote t ], the most complicated case. We begin with soundness. Let A be the formula Now we have two cases, here is the first.
For all appropriate programs U : We now consider reductions where we set η def = (ξ, σ).
Using this fact, we continue to reason as follows. Define We need to show that Since by (3) we have (ξ · m : M ′ , σ) |= B 1 and x, u / ∈ fv(B 1 ) by Observation 5.2, we can apply Proposition 3.3.3 to get η ′′ |= B 1 . By similar reasoning we get η ′′ |= B 2 from (8). Hence η ′′ |= B 1 ∧ B 2 . That leaves the implication. Assume η ′′ |= m = · . By definition that means there is a value W such that M ′ ⇓ W and (ξ · u : V · m : M ′ , σ · x : W ) |= m = x By Proposition 2.1.0(6) then M ′ ≃ W , hence we can apply Proposition 3.3.2, giving us the required (5.5), which in turn implies which finishes the soundness proof for this case. 35 We now consider the second case.
The rest of this case is essentially identical to the corresponding reasoning for the first case, and omitted. Now we establish (MTC). Choose a model (ξ, σ) and assume that We will show that (ξ, σ) |= A. The reverse implication is part of soundness. Notice that this assumption implies the existence of a reduction sequence as follows.
We have two cases. First assume that M ′ ⇑. By (5.8) and Lemma 2.1.0(10) we know that the following holds.
By (IH) A 2 is an MTC for N at u, so we can reason as follows.
The second case is that M ′ ⇓. We proceed as follows.
We conclude this case by proving (closure-2 ). Let η def = (ξ, σ) be an appropriately typed model such that: This means in particular that there are M m and M x such that (5.10) We first note that since it must be the case that: This is an immediate consequence of the definition of the satisfaction relation for m = · and m = x . At the same time, trivially: Taking those two facts together, we see that the following holds. (5.11) Since u, x / ∈ fv(B 1 ), we can use (5.10) and Proposition 3.3.2 to conclude that which, in turn, enables us to use the (IH), so by (closure-2 ) we know that In a similar way we establish that (ξ · u : V, σ · x : M x ) |= B 2 (5.14) Now we have to distinguish the following two cases.
In the first case clearly This together with (5.13) means Since → ⊆ ≃ ⊆ (Theorem 2.1.0(6)), and is transitive, we can thus conclude to: (let x = M in N )η V as required. Now we consider the second case η |= ∀m.
The rest of this case is handled exactly like the previous case, concluding the proof of (closure-2 ). Relative completeness is equally easy to justify. We start from the following assumption.

Proofs of
|= {A} M : u {B} Using the rules in Figure 6, we obtain a TCAP (A ′ , B ′ ) for M at u such that holds. With these assumptions, the proof of Theorem 5.3.3 has the following form:
We first establish that η |= A ′ . Since u / ∈ fv(A), we know from Proposition 3.3.2 that with η ′ def = (ξ, σ) also η ′ |= A. This fact together with the assumption |= {A} M : u {B} means that M η ′ ⇓ V for some closed value V . As (A ′ , B ′ ) is a TCAP for M at u, (MTC) holds so it must also be the case that η ′ |= A ′ . Applying u / ∈ fv(A) with Proposition 3.3.2 again we now obtain η |= A ′ as required. This shows that A ⊃ A ′ .

Examples of characteristic formulae
In this section we look at some example inferences for TCAPs. To make the derivations more readable, we will make simplifications such as writing T for T ∧ T. Note that these simplifications are not admissible using the inference system of Figure 6 only. Example 6.1. We begin with a simple program 2 + 3. 1 Clearly the conclusion in Line (3) As expected, the conclusion in Line (3) It is easy to see that simplifies to T via ∀mn.(m • n ⇓ ⊃ m • n ⇓). The postcondition can be simplified to u = 3 as follows: 1 Example 6.5. The TCAP in the previous example turned out to be logically equivalent to a very simple assertion, albeit only after simplification starting with rather large formulae. This simplification was possible because both parts of the application were concrete terms.
In an application like gx this is not the case as we show now, even when neither g nor x are modal.
Here the (simplified) TCAP explicitly assumes that the application converges, and states that the result of the program is simply the result of the application.
Example 6.6. We use the previous example to derive the TCAP for ω. Our preceding discussion indicated that {T} ω : u {T} is the strongest assertion we can make about a program such as ω in a logic for total correctness. This is borne out by the derivation to follow.
Example 6.7. We build on Example 13 to derive the TCAP for Ω. As Ω diverges, the precondition of the TCAP must be falsity. Clearly ∀m.m • () ⇓ is false, because not every function is terminating. This is intuitively obvious, and follows formally from the axiom (div) in Figure 7. Consequently, the TCAP for Ω is logically equivalent to {F} Ω : u {T} as expected.
Example 6.8. We will now look at examples involving MP. 1 The result is not surprising because [Quote t ] is unchanged from Figure 4.
We now explain the last two simplification steps. First (∀x .x ⇓) must be equivalent to F because not all denotations of the modal variable x terminate. This is formalised by Axiom (div m ) from Section 5. The formula ∀m.(m = 3 ⊃ m = x {x ⇓} is equivalent to T because m = 3 is a shorthand for m = x {x = 3}, and clearly x = 3 implies x ⇓. This justifies Line 4. Regarding the last line, clearly m = 3 implies m = · , so ∃mx .(m = x ∧ m = 3 ∧ n = x) holds. Using Axioms (q1) and (q4) allows us to obtain ∃mx .(x = 3 ∧ n = x), which in turn simplifies to n = 3 as required. Example 6.11. We continue with an example where quasi-quotes divergent code gets unquoted, and then re-quoted without further use.

1
{T} Ω : m {T} Ex. 16 {T} let x = Ω in x : u {T} 5 Line 6 follows because clearly ∀x .T is equivalent to T. Finally, the simplification in Line 6 is immediate, because everything implies T.
Example 6.12. We continue by determining the TCAP for let x = Ω in x, which is a divergent program, which we expect to be equivalent to We infer the TCAP as follows: {F} let x = Ω in x : u {T} 3 We now explain the simplifications leading to Line 4. By the axiom (div m ) which says that not all modal variables denote a terminating program, we know that ∀x .x ⇓ is false. Likewise ∀m.(T ⊃ m = x {x ⇓}) is equivalent to ∀m.m = x {x ⇓} which claims that any quasi-quote must contain a terminating program, which is false by the axiom (div) in Figure 3. Consequently, the precondition in Line 3 is equivalent to F. Simplification of the postcondition to T is immediate.

Conclusion
We have proposed a program logic for an HGRTMP language, and established key metalogical properties like completeness and the correspondence between axiomatic and operational semantics. We are not aware of previous work on program logics for meta-programming. So far, only typing systems for statically enforcing program properties have been investigated; the two most expressive are Ωmega [29] and Concoqtion [15]. Both use indexed typed to achieve expressivity. Ωmega is a call-by-value variant of Haskell with generalised algebraic datatypes (GADTs) and an extensible kind system. In Ωmega, GADTs can express easily datatypes representing object-programs, whose meta-level types encode the object-level types of the programs represented. Tagless interpreters can directly be expressed and typed for these object programs. Ωmega is expressive enough to encode the MetaML typing system together with a MetaML interpreter in a type-safe manner. Concoqtion is an extension of MetaOCaml and uses the term language of the theorem prover Coq to define index types, specify index operations, represent the properties of indexes and construct proofs. Basing indices on Coq terms opens all mathematical insight available as Coq libraries to use in typing meta-programs. Types in both languages are not as expressive with respect to properties of meta-programs themselves as our logics, which capture exactly the observable properties. Nevertheless, program logic and type-theory are not mutually exclusive; on the contrary, reconciling both in the context of meta-programming is an important open problem. Pcf dp lacks the ability, vital for realistic meta-programming, to manipulate open code, i.e. code with free variables. The λ • -calculus [13] is a small language for HGRTMP where code with free variables can be manipulated. As with Pcf dp (without recursion), there is a Curry-Howard correspondence: λ • is a proof calculus for a temporal logic. Due to its simplicity, λ • is an ideal object of study to see how the logic presented here can be generalised to open code. The simplicity of λ • has a price: the calculus cannot be directly extended with a construct expressing the evaluation of generated code. A more ambitious target that allows the manipulation of terms with free variables, but also the evaluation of generated code, is Taha's and Nielsen's system of environment classifiers [32], which also forms the basis of MetaOCaml, the most widely studied meta-programming language in the MetaML tradition. Moreover, [34] presents a Curry-Howard correspondence between a typing system closely related to that of [32] and a modal logic. We believe that a logical account of meta-programming with open code is a key challenge in bringing program logics to realistic meta-programming languages.
A different challenge is to add state to Pcf dp and extend the corresponding logics. We expect the logical treatment of state given in [4,38] to extend smoothly to a metaprogramming setting. The main question is what typing system to use to type stateful meta-programming. The system used in MetaOCaml, based on [32], is unsound in the presence of state due to a form of scope extrusion. Recent versions of MetaOCaml add a dynamic check to detect this behaviour. As an alternative to dynamic typing, the Java-like meta-programming language Mint [37] simply prohibits the sharing of state between different meta-programming stages, resulting in a statically sound typing system. Yet another approach is given in [24] where a two-level HGRTMP language is introduced with delimited control operators and a restriction of side effects during code generation to the scope of generated binders. That guarantees well-typedness. We believe that these approaches can all be made to coexist with modern logics for higher-order state [4,38].
Relatedly, [14] presents a unstaging translation from Pcf dp to Pcf. The key idea is that a quasi-quote M is turned into a thunk λ().M ′ where M ′ is the translation of M . Consequently the type α is translated to Unit → α ′ where α ′ is the translation to α. What are the properties of this translation? Is it fully abstract? The translation can be extended to translating Pcf dp assertions and proofs into the logic for Pcf. Would this latter translation be logically fully abstract in the sense of [26]? If unstaging is fully abstract, then it should be possible to recover the logic presented here from the logic for Pcf and the translation.
Reasoning about HGRTMP using unstaging translations looks promising. In [10] a complex HGRTMP language that allows the manipulation of open code and the capture of free variables is unstaged. However, logical reasoning about meta-programs in the target language of an unstaging translation incurs a cost: it leads to larger formulae and proofs in comparison with reasoning about the meta-programs directly using the source language. Moreover, this cost is paid in every reasoning process. In contrast, the cost of developing a logic for the meta-programming language is paid only once. An additional question is whether unstaging translations are fully abstract for more complicated HGRTMP languages.
A technical issue we left open is to do with the size of characteristic formulae. The inference system in Section 5 may lead to an exponential blow up of TCAPs vis-a-vis the programs they are derived from. We believe that it is possible to give an alternative inference system for TCAPs such that the size of the TCAP is linear, i.e. O(n), in the size of the program. In [7,19] this is achieved for logics of partial correctness for Pcf-like languages, and in [8] for a simple imperative language.
Finally we have a question about modal quantification: 'normal' reasoning about Pcf dpprograms using the rules and axioms of Section 3 appears to be possible entirely without modal quantification. Can we abolish modal quantification altogether? If not, why is the lack of modal quantification no issue in practise?