Refinement Type Inference Via Horn Constraint Optimization⋆
Total Page:16
File Type:pdf, Size:1020Kb
Refinement Type Inference via Horn Constraint Optimization? Kodai Hashimoto and Hiroshi Unno University of Tsukuba fkodai, [email protected] Abstract. We propose a novel method for inferring refinement types of higher-order functional programs. The main advantage of the proposed method is that it can infer maximally preferred (i.e., Pareto optimal) refinement types with respect to a user-specified preference order. The flexible optimization of refinement types enabled by the proposed method paves the way for interesting applications, such as inferring most-general characterization of inputs for which a given program satisfies (or vi- olates) a given safety (or termination) property. Our method reduces such a type optimization problem to a Horn constraint optimization problem by using a new refinement type system that can flexibly rea- son about non-determinism in programs. Our method then solves the constraint optimization problem by repeatedly improving a current solu- tion until convergence via template-based invariant generation. We have implemented a prototype inference system based on our method, and obtained promising results in preliminary experiments. 1 Introduction Refinement types [5, 20] have been applied to safety verification of higher-order functional programs. Some existing tools [9, 10, 16{19] enable fully automated verification by refinement type inference based on invariant generation tech- niques such as abstract interpretation, predicate abstraction, and CEGAR. The goal of these tools is to infer refinement types precise enough to verify a given safety specification. Therefore, types inferred by these tools are often too specific to the particular specification, and hence have limited applications. To remedy the limitation, we propose a novel refinement type inference method that can infer maximally preferred (i.e., Pareto optimal) refinement types with respect to a user-specified preference order. For example, let us con- sider the following summation function (in OCaml syntax) let rec sum x = if x = 0 then 0 else x + sum (x - 1) A refinement type of sum is of the form (x : fx : int j P (x)g) ! fy : int j Q(x; y)g. Here, P (x) and Q(x; y) respectively represent pre and post conditions of sum. Note that the postcondition Q(x; y) can refer to the argument x as well as the return value y. Suppose that we want to infer a maximally-weak predicate for ? This work was supported by Kakenhi 25730035, 23220001, 15H05706, and 25280020. P and maximally-strong predicate for Q within a given underlying theory. Our method allows us to specify such preferences as type optimization constraints: maximize(P ); minimize(Q): Here, maximize(P ) (resp. minimize(Q)) means that the set of the models of P (x) (resp. Q(x; y)) should be maximized (resp. minimized). Our method then infers a Pareto optimal refinement type with respect to the given preferences. In general, however, this kind of multi-objective optimization involves a trade-off among the optimization constraints. In the above example, P may not be weakened without also weakening Q. Hence, there often exist multiple optima. Actually, all the following are Pareto optimal refinement types of sum.1 (x : fx : int j x = 0g) ! fy : int j y = xg (1) (x : fx : int j trueg) ! fy : int j y ≥ 0g (2) (x : fx : int j x < 0g) ! fy : int j falseg (3) Our method further allows us to specify a priority order on the predicate variables P and Q. If P is given a higher priority over Q (we write P @ Q), our method infers the type (2), whereas we obtain the type (3) if Q @ P . Interestingly, (3) expresses that sum is non-terminating for any input x < 0. The flexible optimization of refinement types enabled by our method paves the way for interesting applications, such as inferring most-general characteri- zation of inputs for which a given program satisfies (or violates) a given safety (or termination) property. Furthermore, our method can infer an upper bound of the number of recursive calls if the program is terminating, and can find a minimal-length counterexample path if the program violates a safety property. Internally, our method reduces such a refinement type optimization prob- lem to a constraint optimization problem where the constraints are expressed as existentially quantified Horn clauses over predicate variables [1, 11, 19]. The constraint generation here is based on a new refinement type system that can reason about (angelic and demonic) non-determinism in programs. Our method then solves the constraint optimization problem by repeatedly improving a cur- rent solution until convergence. The constraint optimization here is based on an extension of template-based invariant generation [3,7] to existentially quantified Horn clause constraints and prioritized multi-objective optimization. The rest of the paper is organized as follows. Sections 2 and 3 respectively formalize our target language and its refinement type system. The applications of refinement type optimization are explained in Section 4. Section 5 formalizes Horn constraint optimization problems and the reduction from type optimiza- tion problems. Section 6 proposes our Horn constraint optimization method. Section 7 reports on a prototype implementation of our method and the results of preliminary experiments. We compare our method with related work in Sec- tion 8 and conclude the paper in Section 9. An extended version of the paper with proofs is available online [8]. 1 Here, we use quantifier-free linear arithmetic as the underlying theory and consider only atomic predicates for P and Q. E[op(ne)] −!D E[ op (ne)] (E-Op) E[let x = v in e] −!D E[[v=x]e] J K (E-Let) D(f) = λx:e jxj = jvj e e e (E-App) E[let x = ∗8 in e] −!D E[[n=x]e] E[f v] −!D E[[v=x]e] e e e (E-Rand9) if n = 0 then i = 1 else i = 2 (E-If) E[let x = ∗9 in e] −!D E[[n=x]e] E[ifz n then e1 else e2] −!D E[ei] (E-Rand8) Fig. 1. The operational semantics of our language L 2 Target Language L This section introduces a higher-order call-by-value functional language L, which is the target of our refinement type optimization. The syntax is defined as follows. m (programs) D ::= ffi 7! λxei:eigi=1 (expressions) e ::= x j e1 e2 j n j op(e1; : : : ; ear(op)) j ifz e1 then e2 else e3 j let x = e1 in e2 j let x = ∗8 in e j let x = ∗9 in e (values) v ::= n j f ve (eval. contexts) E ::= [ ] j E e j v E j ifz E then e1 else e2 j let x = E in e Here, x and f are meta-variables ranging over variables. n and op respectively represent integer constants and operations such as + and ≥. ar(op) expresses the arity of op. We write xe (resp. ve) for a sequence of variables xi (resp. values vi) and jxej for the length of xe. For simplicity of the presentation, the language L has integers as the only data type. We encode Boolean values true and false m respectively as non-zero integers and 0. A program D = ffi 7! λxei:eigi=1 is a mapping from variables fi to expressions λxei:ei, where λx:ee is an abbreviation of λx : : : : λx :e. We define dom(D) = ff ; : : : ; f g and ar(f ) = jx j. A value 1 jxej 1 m i ei f ve is required to satisfy 1 ≤ jvej < ar(f). The call-by-value operational semantics of L is given in Figure 1. Here, op represents the integer function denoted by op. Both expressions let x = ∗8 Jin eK and let x = ∗9 in e generate a random integer n, bind x to it, and evaluate e. They are, however, interpreted differently in our refinement type system (see Section 3). We support these expressions to model various non-deterministic behaviors caused by, for example, user inputs, inputs from communication chan- ∗ nels, interrupts, and thread schedulers. We write −!D to denote the reflexive and transitive closure of −!D. 3 Refinement Type System for L In this section, we introduce a refinement type system for L that can reason about non-determinism in programs. We then formalize refinement type optimization problems (in Section 3.1), which generalize ordinary type inference problems. The syntax of our refinement type system is defined as follows. (refinement types) τ ::= fx j φg j (x : τ1) ! τ2 (type environments) Γ ::= ; j Γ; x : τ j Γ; φ (formulas) φ ::= t1 ≤ t2 j > j ? j :φ j φ1 ^ φ2 j φ1 _ φ2 j φ1 ) φ2 (terms) t ::= n j x j t1 + t2 j n · t (predicates) p ::= λx.φe An integer refinement type fx j φg equipped with a formula φ for type refinement represents the type of integers x that satisfy φ. The scope of x is within φ. We often abbreviate fx j >g as int. A function refinement type (x : τ1) ! τ2 represents the type of functions that take an argument x of the type τ1 and return a value of the type τ2. Here, τ2 may depend on the argument x and the scope of x is within τ2. For example, (x : int) ! fy j y > xg is the type of functions whose return value y is always greater than the argument x. We often write fvs(τ) to denote the set of free variables occurring in τ. We define dom(Γ ) = fx j x : τ 2 Γ g and write Γ (x) = τ if x : τ 2 Γ . In this paper, we adopt formulas φ of the quantifier-free theory of linear integer arithmetic (QFLIA) for type refinement.