Program Verification with Flow-Effect Types - CiteSeerX

2 downloads 8367 Views 231KB Size Report
crux of the type closure algorithm lies in how recursive calls are limited in depth ..... the same call site, app-merge computes the closure for each of them in ...
Program Verification with Flow-Effect Types Paritosh Shroff1 , Christian Skalka2 , and Scott F. Smith1 1

Department of Computer Science, Johns Hopkins University {pari, scott}@cs.jhu.edu 2 Department of Computer Science, University of Vermont [email protected]

Abstract. This paper develops a flow sensitive type system for higher order programming languages. Flow-effect types are a novel form of type that combine the notion of temporal ordering inherent in type effect systems, with subtype constraint systems which focus on unordered dataflow. The resulting system achieves a high level of precision by cutting very close to the operational behavior of programs. The na¨ıve type closure algorithm completely simulates expression computation, and so is undecidable. The main result of the paper is development of a sound and decidable closure algorithm for flow-effect types.

1

Introduction

Type theory has advanced in many directions from simple type systems, to include polymorphic types, linear types, singleton types, subtypes, dependent types, effect types, metatypes, constraint types, etc. In this space of advances, some of them can be viewed as more directly reflecting the operational semantics in type information than others. Subtyping reflects the directionality of data propagation in the operational semantics of expressions [19], and subtype constraint systems [1] even more directly follow the operational semantics: every data movement in the operational semantics introduces a corresponding subtyping constraint. Type effect systems [28] incorporate information about sideeffecting operations into types. Singleton types denote the single run-time value of computation, rather than an abstraction of that value. These direct properties contrast with the more logical properties of dependent and linear types. In this paper we define flow-effect types, a further refinement in this direction of lifting direct operational behavior into the type system. As the name indicates, flow-effect types can be viewed as a combination of subtyping (data flow) and type effect systems. Subtype constraint systems [1] define a clear level of program abstraction: “what values may flow from here to there, ignoring the order in which the flows may occur?” As can be seen in this statement, the type analysis is precise up to ordering, and so it may be classified as a flow-insensitive program analysis. Trace-based effect systems [2, 24] are type effect systems that produce ordered sequences of effects, as opposed to an unordered set of effects of standard type

effect systems [28]. The incorporation of the ordering information brings some flow-sensitivity to the types; but, the effects themselves are primitive atoms. Flow-effect type systems synthesize concepts from subtype constraints and trace-based type effects, by defining the effects in effect types be ordered sequences of subtype constraints. With clear modeling of both data- and controlflow information in the types, flow-effect types are significantly more fine-grained. In fact, flow-effect types represent so much of the program’s information that they appear quite different from types in the traditional sense, bearing more superficial resemblance to A-normalized [13] linear [29] let-expressions. It could even be argued whether they are “types” at all, but given their origins type theory and shared methodology we call them so. Trace-based type effects impart flow-sensitivity to the flow-effect type system. Flow-sensitivity implies that the order of operations is taken into account when tracking data-flow, so a mutable variable type assignment in a flow-sensitive analysis will reflect its most recently assigned value only, and not all possible assignments. So for example the following is typable: let x = ref 5 in x := true; x ∧ false For functional evaluation, flow-effect types achieve higher-order parametric polymorphism, so programs like (λid . id (5) + 1; id (true) ∧ false) (λx. x) are typable. This example is not ML-typable because it supports parametric let-polymorphism only. Flow-effect types can be thought of as idealized expressions, and their type closure as idealized expression computation, similar to an abstract interpretation [8]. In fact, the na¨ıve closure algorithm so closely mimics expression computation that it is not always terminating – nearly all underlying computational structure is embodied in types, and hence diverging programs will have diverging type closures. Non-diverging programs, however, pose no problems and the closure can essentially run the program. So, the problem of finding a decidable closure can be reduced to preventing diverging programs from leading to a nonterminating closure. Since programs are finite, diverging programs must execute some pieces of code infinitely often. In a functional programming language, the only way to execute code infinitely often is via unbounded recursion, hence any divergent computation must be a recursive computation with unbounded stack size. The crux of the type closure algorithm lies in how recursive calls are limited in depth by using a novel prune-rerun technique, which limits stack size during closure. 1.1

Contributions and Overview

In this paper we make the following contributions: 1. We formally develop the notion of flow-effect types.

2. We present a sound and decidable closure algorithm, called Ω-closure, for flow-effect types. 3. We present various extensions to flow-effect types, showing how they can scale up to greater expressiveness and more realistic languages. For ease of presentation, and to focus on the core ideas, we initially leave out parametric polymorphism, singleton types, and state. In Section 2 we study λflow , a simple purely functional language. We present flow-effect types for λflow along with a na¨ıve closure algorithm. Section 3 develops the sound and decidable Ω-closure algorithm. Section 4 presents various extensions to Ω-closure, including higher-order parametric polymorphism, singleton types, and mutable state. With these extensions we can type complex recursive programs that require polymorphic recursion in traditional type systems, and precisely infer recursive data structures. We believe the combination of singletons, flow-sensitivity, and parametric polymorphism gives enough information in the types to make automatic verification of some program properties feasible. We present these extensions informally via examples; formal details including proofs can be found in a technical report [23]. Section 5.1 relates our results to previous work. We have implemented the full Ω-closure algorithm in OCaml. The source code is downloadable at http://www.cs.jhu.edu/~pari/floweffecttypes/. 1.2

Project Goals

This paper represents the initial step in a larger project. In order to put the paper in context we briefly mention our long-term goals. Flow-effect types are ultimately targeted at full languages such as ML and Java, and the extensions discussed here are one step towards richer languages. Also, we do not view flow-effect types so much as a type-checker or program analyzer, but rather as an automated program verifier [11, 30, 4]. This is reflected in the Assert (x = y) flow-effect type, which when combined with singleton types allows program equivalences to be verified by the type system.

2

The λflow Language

Our programming language model, called λflow , is a pure functional language with arithmetic, booleans and conditional branching. λf x. e is a lambda term with f as the recursive variable. Throughout the paper we will drop the subscript f when it is not used. Recursion can also be encoded via e.g. the Y -combinator. Figure 1 gives the expression syntax and Figure 2 gives a regular substitution based, small step operational semantics for λflow , where e [ e0 / x ] denotes the capture avoiding substitution of all free occurrences of x in e with e0 . 2.1

Flow-Effect Types for λflow

Figure 3 gives the grammar for flow-effect types and Figure 4 the corresponding translation rules. As alluded to in the introduction, flow-effect type representations are quite different from standard type representations – they are

x, y, z, f i ::= . . . | −2 | −1 | 0 | 1 | 2 | . . . b ::= true | false v ::= x | i | b | λf x. e e ::= v | e + e | e = e | e e | if e then e else e R ::= • | R + e | i + R | R = e | i = R | R e | (λf x. e) R | if R then e1 else e2

variable integer boolean value expression reduction context

Fig. 1. λflow Language Syntax Grammar

plus i3 = i 1 + i 2 i1 + i2 −→ i3 if-true if true then e1 context e −→ e0 R[e] −→ R[e0 ]

equal b = (i1 = i2 ) i1 = i2 −→ b else e2 −→ e1 reflex e −→0 e

app (λf x. e) v −→ e [ (λf x. e) / f ] [ v / x ] if-false if false then e1 else e2 −→ e2 trans e −→n−1 e0 e0 −→ e00 e −→n e00

Fig. 2. λflow Operational Semantics Rules

essentially idealized A-normal expression forms with explicit ordering of atomic computation steps. In the introduction we mentioned that these types represent a synthesis of subtype constraints and trace-based type effects; here is how. The lambda type Λf x. τ is a function type where the body τ is the effect part, and the “Let x  δ In τ ” within the body denotes data flow, where x  δ is the equivalent to subtyping constraint δ

Suggest Documents