We present a new approach to automated reasoning about higher-order programs by endowing symbolic execution with a notion of higher-order, symbolic values.To validate our approach, we use it to develop and evaluate a system for verifying and refuting behavioral software contracts of components in a functional language, which we call soft contract verification. In doing so, we discover a mutually beneficial relation between behavioral contracts and higher-order symbolic execution. Contracts aid symbolic execution by providing a rich language of specifications that can serve as the basis of symbolic higher-order values; the theory of blame enables modular verification and leads to the theorem that verified components can't be blamed; and the run-time monitoring of contracts enables soft verification whereby verified and unverified components can safely interact and verification is not an all-or-nothing proposition. Conversely, symbolic execution aids contracts by providing compile-time verification which increases assurance and enables optimizations; automated test-case generation for contracts with counter-examples; and engendering a virtuous cycle between verification and the gradual spread of contracts.Our system uses higher-order symbolic execution, leveraging contracts as a source of symbolic values including unknown behavioral values, and employs an updatable heap of contract invariants to reason about flow-sensitive facts. Whenever a contract is refuted, it reports a concrete counterexample reproducing the error, which may involve solving for an unknown function. The approach is able to analyze first-class contracts, recursive data structures, unknown functions, and control-flow-sensitive refinements of values, which are all idiomatic in dynamic languages. It makes effective use of an offthe-shelf solver to decide problems without heavy encodings. Our counterexample search is sound and relatively complete with respect to a first-order solver for base type values. Therefore, it can form the basis of automated verification and bug-finding tools for higher-order programs. The approach is competitive with a wide range of existing tools-including type systems, flow analyzers, and model checkers-on their own benchmarks. We have built a tool which analyzes programs written in Racket, and report on its effectiveness in verifying and refuting contracts.
Higher-order symbolic execution for contract verification and refutation3 what a modular analysis means in this setting is tricky, but again contracts provide the answer. By re-using the concept of blame from Findler and Felleisen (2002), we define the errors that we rule out as exactly those that blame the portion of the program under consideration. This crucial distinction will become especially important when considering the behavior of unknown higher-order values.To demonstrate the first step in applying our approach, consider the following contrived, but illustrative example. Let positive? and negative? be predicates for positive and negative integers. Contracts ...