LEARNING TO INFER RUN-TIME INVARIANTS FROM SOURCE CODE

Abstract

Source code is notably different from natural language in that it is meant to be executed. Experienced developers infer complex "invariants" about run-time state while reading code, which helps them to constrain and predict program behavior. Knowing these invariants can be helpful; yet developers rarely encode these explicitly, so machine-learning methods don't have much aligned data to learn from. We propose an approach that adapts cues within existing if-statements regarding explicit run-time expectations to generate aligned datasets of code and implicit invariants. We also propose a contrastive loss to inhibit generation of illogical invariants. Our model learns to infer a wide vocabulary of invariants for arbitrary code, which can be used to detect and repair real bugs. This is entirely complementary to established approaches, which either use logical engines that scale poorly, or run-time traces that are expensive to obtain; when present, that data can complement our tool, as we demonstrate in conjunction with Daikon, an existing tool. Our results show that neural models can derive useful representations of run-time behavior directly from source code.

1. INTRODUCTION

Software maintenance requires reading a lot of code. Experienced developers are adept at this, garnering rich semantics just from this "static" (viz, without running the code) inspection to find complex bugs, predict a function's outputs from its inputs, and learn new coding patterns. They strongly rely on generic assumptions about the program's run-time behavior; e.g., that a list index never escapes the list bounds and strictly increases. Such "invariants" capture general, yet relevant constraints on the program's expected run-time behavior. Automatically inferring invariants can help both developers and tools: first, they can be used to detect bugs where explicit assumptions are incorrect or implicit ones ought to be explicit; second, invariants can guide myriad other tools, such as test-case generators (Artzi et al., 2006) . However, inferring invariants is not tractable in general and sound approximations don't scale beyond very small programs. Instead, popular tools either use dynamic trace data from real executions (esp. Daikon (Ernst et al., 2007) ), which requires costly instrumentation, or focuses on highly constrained cases such as loops (Sharma et al., 2013a; Padhi et al., 2016) . 1: A snippet that demonstrates how explicitly guarded code is often equivalent to code with salient implicit, invariant-like conditions. The code on the right was a real (bug) that was patched by adding the conditional check on the left. We synthesize such samples to train our model by selectively removing if-statements. Our model correctly predicted this repair.

