Separation logic for OO Stephan van Staden. Introduction OO languages are popular and widely used We need to reason about OO programs Some (problematic)

Slides:



Advertisements
Similar presentations
Chapter 13 Abstraction and inheritance. This chapter discusses n Implementing abstraction. u extension u inheritance n Polymorphism/dynamic binding. n.
Advertisements

Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 18 Program Correctness To treat programming.
Automated Theorem Proving Lecture 1. Program verification is undecidable! Given program P and specification S, does P satisfy S?
Semantics Static semantics Dynamic semantics attribute grammars
Verifying Executable Object-Oriented Specifications with Separation Logic Stephan van Staden, Cristiano Calcagno, Bertrand Meyer.
Automated Software Verification with a Permission-Based Logic 20 th June 2014, Zürich Malte Schwerhoff, ETH Zürich.
CSE115: Introduction to Computer Science I Dr. Carl Alphonce 219 Bell Hall
1 Inheritance. 2 One class inherits from another if it describes a specialized subset of objects Terminology: inheritschild class subclass –the class.
ECI 2007: Specification and Verification of Object-Oriented Programs Lecture 2 Courtesy: K. Rustan M. Leino and Wolfram Schulte.
Lecture 2 Towards a Verifying Compiler: Logic of Object oriented Programs Wolfram Schulte Microsoft Research Formal Methods 2006 Objects, references, heaps,
Inheritance and Class Hierarchies Chapter 3. Chapter 3: Inheritance and Class Hierarchies2 Chapter Objectives To understand inheritance and how it facilitates.
On the Relationship Between Concurrent Separation Logic and Assume-Guarantee Reasoning Xinyu Feng Yale University Joint work with Rodrigo Ferreira and.
Data Abstraction and Object- Oriented Programming CS351 – Programming Paradigms.
Ch 13. Features Found Only in Java Timothy Budd Oregon State University.
Reasoning about Multiple Related Abstractions with MultiStar Stephan van Staden, Cristiano Calcagno Chair of Software Engineering.
Cs164 Prof. Bodik, Fall Symbol Tables and Static Checks Lecture 14.
Inheritance and Polymorphism CS351 – Programming Paradigms.
Subclasses and Subtypes CMPS Subclasses and Subtypes A class is a subclass if it has been built using inheritance. ▫ It says nothing about the meaning.
Crowfoot: a verifier for higher order store programs Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus University of Sussex VMCAI 2012.
OOPs Object oriented programming. Based on ADT principles  Representation of type and operations in a single unit  Available for other units to create.
1 Abstraction  Identify important aspects and ignore the details  Permeates software development programming languages are abstractions built on hardware.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
07 Coding Conventions. 2 Demonstrate Developing Local Variables Describe Separating Public and Private Members during Declaration Explore Using System.exit.
CSE 425: Object-Oriented Programming I Object-Oriented Programming A design method as well as a programming paradigm –For example, CRC cards, noun-verb.
APCS Java AB 2004 Review of CS1 and CS2 Review for AP test #1 Sources: 2003 Workshop notes from Chris Nevison (Colgate University) AP Study Guide to go.
Chapter 2 Introducing Interfaces Summary prepared by Kirk Scott.
ISBN Chapter 3 Describing Semantics -Attribute Grammars -Dynamic Semantics.
OOP: Encapsulation,Abstraction & Polymorphism. What is Encapsulation Described as a protective barrier that prevents the code and data being randomly.
Verification of Programs with Inspector Methods Bart Jacobs and Frank Piessens Dept. CS, K.U.Leuven, Belgium.
Objects & Dynamic Dispatch CSE 413 Autumn Plan We’ve learned a great deal about functional and object-oriented programming Now,  Look at semantics.
A Universe-Type-Based Verification Technique for Mutable Static Fields and Methods Alexander J Summers Sophia Drossopoulou Imperial College London Peter.
Recap form last time How to do for loops map, filter, reduce Next up: dictionaries.
Types in programming languages1 What are types, and why do we need them?
ISBN Chapter 3 Describing Semantics.
Inheritance. Inheritance - Introduction Idea behind is to create new classes that are built on existing classes – you reuse the methods and fields and.
1 OOP - An Introduction ISQS 6337 John R. Durrett.
OOPs Object oriented programming. Abstract data types  Representationof type and operations in a single unit  Available for other units to create variables.
Object-Oriented Programming © 2013 Goodrich, Tamassia, Goldwasser1Object-Oriented Programming.
Inheritance and Class Hierarchies Chapter 3. Chapter 3: Inheritance and Class Hierarchies2 Chapter Objectives To understand inheritance and how it facilitates.
Inheritance and Class Hierarchies Chapter 3. Chapter Objectives  To understand inheritance and how it facilitates code reuse  To understand how Java.
Classes, Interfaces and Packages
Programming in java Packages Access Protection Importing packages Java program structure Interfaces Why interface Defining interface Accessing impln thru.
CSSE501 Object-Oriented Development. Chapter 10: Subclasses and Subtypes  In this chapter we will explore the relationships between the two concepts.
CS5205Semantics1 CS5205: Foundation in Programming Languages Semantics Static Semantics Dynamic Semantics Operational Semantics Big-step Small-Step Denotational.
Object Design More Design Patterns Object Constraint Language Object Design Specifying Interfaces Review Exam 2 CEN 4010 Class 18 – 11/03.
 Description of Inheritance  Base Class Object  Subclass, Subtype, and Substitutability  Forms of Inheritance  Modifiers and Inheritance  The Benefits.
Liang, Introduction to Java Programming, Eighth Edition, (c) 2011 Pearson Education, Inc. All rights reserved Fall 2013 Chapter 10 Thinking.
Geoff Holmes and Bernhard Pfahringer COMP206-08S General Programming 2.
Chapter 10 Thinking in Objects
Modern Programming Tools And Techniques-I
Enhancing Modular OO Verification with Separation Logic
Names and Attributes Names are a key programming language feature
Lecture 12 Inheritance.
Chapter 10 Thinking in Objects
Chapter 10 Thinking in Objects
Matching Logic An Alternative to Hoare/Floyd Logic
Chapter 3: Using Methods, Classes, and Objects
Nested class.
Object Oriented Programming
Object-Oriented Programming
Stateful Manifest Contracts
Chapter 9 Thinking in Objects
Java – Inheritance.
Chapter 9 Thinking in Objects
Verified Subtyping with Traits and Mixins
CISC/CMPE320 - Prof. McLeod
Introduction to Data Structure
CS 112 Programming 2 Lecture 02 Abstract Classes & Interfaces (2)
11.1 The Concept of Abstraction
Presentation transcript:

Separation logic for OO Stephan van Staden

Introduction OO languages are popular and widely used We need to reason about OO programs Some (problematic) features of OO languages: Shared mutable state Inheritance (subtyping and overriding) – Determining what a method call does is difficult! Complicated method lookup scheme that relies on dynamic info. We are interested in static verification... 2

Reasoning about OO programs We can use separation logic to reason about shared mutable state But it is not enough! We need to accommodate and control inheritance (Many published OO proof systems cannot reason about simple programming patterns, or support them in a very complicated way) We will look at a state of the art separation logic for OO by Parkinson and Bierman (“Separation Logic, Abstraction and Inheritance”, proceedings POPL 2008) 3

Outline 1.Shared mutable state 1.Memory model 2.Simple statements & proof rules 2.Inheritance 1.Abstract predicate families 2.Method specification and verification 4

1. Shared mutable state 5

1.1 OO memory model State =def Stack x DType x Heap Stack =def Var -> Value Value =def Ref u Int u... DType =def Ref -> fin Type (dynamic type info) Heap =def Field -> fin Value (location granularity = field) Field =def Ref x Attributename (S, D, H) ⊧ e.f ↦ e’ =def H([e] S, f) = [e’] S (different!) (S, D, H) ⊧ e : C =def D([e] S ) = C (S, D, H) ⊧ e = e’ =def [e] S = [e’] S (S, D, H) ⊧ P * Q =def  H 1, H 2. H 1 ⊥ H 2, H = H 1 u H 2, (S, D, H 1 ) ⊧ P, (S, D, H 2 ) ⊧ Q 6

1.2 Simple instructions & proof rules Field mutation {x.f ↦ _} x.f := y {x.f ↦ y} Field lookup {x.f ↦ e} y := x.f {x.f ↦ e * y = e}, provided y is not the same as x, and y does not occur free in e Rules for variable assignment, sequential composition, conditional, loop, etc. are the familiar ones Later: method calls (a bit complicated due to inheritance) 7

Structural rules Frame: {P} s {Q}. {P*R} s {Q*R} provided modifies(s)  FV(R) = {} Note: modifies(x.f := y) = {} Auxiliary Variable Elimination: {P} s {Q}. {  v. P} s {  v. Q} provided v does not occur free in s Consequence,... 8

2. Inheritance 9

2.1 Abstract predicate families (apfs) OO programming is based on encapsulated data-centered abstractions Parkinson and Bierman’s system embraces this, resulting in simple reasoning that accommodates OO (esp. inheritance) well Apfs are heavily used in method specs 10

Abstract predicates Abstract predicate: name, definition and scope. Within the scope, one can freely change between the name and definition. Outside the scope, one can use the name only atomically Examples: list, tree, etc. Since sep logic predicates describe data, abstract predicates are encapsulated data abstractions. Fit OO remarkably well! For simplicity: single class as the scope. Think “interface” and “implementation” of a predicate. Clients use interfaces 11

The abstraction boundary is a class, but the abstractions themselves are not dictated by classes Examples: An abstract predicate List can be implemented with Node objects class List can also implement a Stack abstract predicate class Student can implement a Person predicate 12

The “family” part of apfs Different classes (and in particular subclasses) can implement abstractions differently They can provide different definitions, or entries, for the same predicate, hence predicate family. The definition that applies is based on the dynamic type of the first predicate argument. Apfs as “dynamically dispatched predicates” Example: class Cell defines x.Val Cell (n) as x.val ↦ n. Val is an apf Val Cell is class Cell’s entry of the apf Val 13

class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods e.g. get, set } Within the scope of class Cell only, we can use the assumptions (in the rule of consequence when verifying the methods of Cell): FtoE:  x,n. x : Cell => [x.Val(n) x.Val Cell (n)] EtoD:  x,n. x.Val Cell (n) x.val ↦ n Arity reduction:  x. x.Val() x.Val(_) Arity reduction allows subclasses to add arguments to apfs. Arguments are added on the right, and arity reduction drops arguments from the right 14

ReCell does not know the definition of x.Val Cell (n), yet it defines its entry of apf Val in terms of it ReCell adds an argument to the apf Val. In the scope of ReCell:  x. x.Val() x.Val(_)  x,n. x.Val(n) x.Val(n, _) class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods e.g. get, set } class ReCell inherit Cell { // apf definitions define x.Val ReCell (n, b) as x.Val Cell (n) * x.bak ↦ b // field declarations: a backup value bak: int // methods: new, overridden,... } 15

2.2 Method specification & verification 16

Static & dynamic specs Two types of method calls in OO languages: Statically dispatched/bound calls Examples: super calls in Java, x.C::m(a) in C++ Dynamically dispatched/bound calls Example: x.m(a) Specify each method with a static and a dynamic spec Use static spec to verify statically dispatched calls. Static spec describes in detail what the body does Use dynamic spec to verify dynamically dispatched calls. Dynamic spec is more abstract – it gives the idea behind the method that all subclasses must respect 17

Example class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } introduce get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} static {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} { Res := this.val } } 18

Client reasoning Assume the constructor Cell(x: int) has dynamic spec: {true}_{this.Val(x)} {true} x := new Cell(3) {x.Val(3)} y := new Cell(4) {x.Val(3) * y.Val(4)} x.set(5) {x.Val(5) * y.Val(4)} n := y.get() {x.Val(5) * y.Val(4) * n=4} m := x.val {???} class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } introduce get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} static {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} { Res := this.val } } 19

Verifying a newly introduced method Two proof obligations: 1.Body verification Verify that the body satisfies the static spec, using the apf assumptions of the containing class and all method specs {this.Val Cell (_)} {this.val ↦ _} this.val := x {this.val ↦ x} {this.Val Cell (x)} 2.Dynamic dispatch Verify the consistency of the static and dynamic specs. In particular, check under the apf assumptions that the dynamic spec with “this : Cell” added to the precondition follows from the static spec. {this.Val Cell (_)}_{this.Val Cell (x)} ==> {this : Cell * this.Val(_)}_{this.Val(x)} define x.Val Cell (n) as x.val ↦ n // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } 20

Specification refinement {P}_{Q} ==> {P’}_{Q’} also means that: the specification {P}_{Q} is stronger than {P’}_{Q’} whenever a statement satisfies {P}_{Q}, it must also satisfy {P’}_{Q’} A proof of {P}_{Q} ==> {P’}_{Q’} uses the structural rules of sep logic (Frame, AuxVarElim, Consequence,...) to establish {P’}_{Q’} under the assumption {P}_{Q} For example, {this.Val Cell (_)}_{this.Val Cell (x)} ==> {this : Cell * this.Val(_)}_{this.Val(x)} Proof: Assumption {this.Val Cell (_)}_{this.Val Cell (x)} Frame rule {this : Cell * this.Val Cell (_)}_{this : Cell * this.Val Cell (x)} Consequence {this : Cell * this.Val(_)}_{this : Cell * this.Val(x)} Consequence {this : Cell * this.Val(_)}_{this.Val(x)} Remember the apf assumption of class Cell:  x,n. x : Cell => [x.Val(n) x.Val Cell (n)] 21

Subclassing class ReCell inherit Cell { // apf definitions define x.Val ReCell (n, b) as x.Val Cell (n) * x.bak ↦ b // field declarations: a backup value bak: int // methods override set(x: int) dynamic {this.Val(v, _)}_{this.Val(x, v)} static {this.Val ReCell (v, _)}_{this.Val ReCell (x, v)} { local t: int t := this.Cell::get(); this.Cell::set(x); this.bak := t } inherit get(): int dynamic {this.Val(v, b)}_{this.Val(v, b) * Res = v} static {this.Val ReCell (v, b)}_{this.Val ReCell (v, b) * Res = v} } class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } introduce get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} static {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} { Res := this.val } } 22

Verifying an overridden method The three proof obligations use the apf assumptions of the child class: 1.Body verification 2.Dynamic dispatch 3.Behavioural subtyping Verify that the dynamic spec of the method in the child class is stronger than the one in the parent class Example: {this.Val(v, _)}_{this.Val(x, v)} ==> {this.Val(_)}_{this.Val(x)} Proof: Assumption: {this.Val(v, _)}_{this.Val(x, v)} AuxVarElim: {  v. this.Val(v, _)}_{  v. this.Val(x, v)} Consequence: {this.Val(_, _)}_{this.Val(x, _)} Consequence: {this.Val(_)}_{this.Val(x)} Remember the apf assumption of class ReCell:  x,n. x.Val(n) x.Val(n, _) class ReCell inherit Cell {... override set(x: int) dynamic {this.Val(v, _)}_{this.Val(x, v)} static {this.Val ReCell (v, _)}_{this.Val ReCell (x, v)} { local t: int t := this.Cell::get(); this.Cell::set(x); this.bak := t }... } 23

Verifying an inherited method The three proof obligations use the apf assumptions of the child class: 1.Behavioural subtyping 2.Dynamic dispatch 3.Inheritance Verify that the static specification of the method in the child class follows from the one in the parent class Example: {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} ==> {this.Val ReCell (v, b)}_{this.Val ReCell (v, b) * Res = v} Proof: Assumption: {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} Frame: {this.Val Cell (v) * this.bak ↦ b}_{this.Val Cell (v) * Res = v * this.bak ↦ b} Consequence: {this.Val ReCell (v, b)}_{this.Val ReCell (v, b) * Res = v} class ReCell inherit Cell { define x.Val ReCell (n, b) as x.Val Cell (n) * x.bak ↦ b... inherit get(): int dynamic {this.Val(v, b)}_{this.Val(v, b) * Res = v} static {this.Val ReCell (v, b)}_{this.Val ReCell (v, b) * Res = v} } 24

Static/dynamic specs - reflection Only dynamic specs are involved in behavioural subtyping Apfs are a great enabler of behavioural subtypes With static specs, child classes never need to see the code of parents. Good for modularity 25

Copy-and-paste inheritance Is this a “proper” use of inheritance? Can one ever hope to verify such code? class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } introduce get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} static {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} { Res := this.val } } class DCell inherit Cell { override set(x: int) { this.Cell::set(2*x) } } 26

Surprise! No problem for verification class DCell inherit Cell { // apf definitions define x.Val DCell (n) as false define x.DVal DCell (n) as x.Val Cell (n) // methods override set(x: int) dynamic {this.Val(_)}_{this.Val(x)} also {this.DVal(_)}_{this.DVal(2*x)} static {this.DVal DCell (_)}_{this.DVal DCell (2*x)} { this.Cell::set(2*x) } inherit get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} also {this.DVal(v)}_{this.DVal(v) * Res = v} static {this.DVal DCell (v)}_{this.DVal DCell (v) * Res = v} } class Cell { // apf definitions define x.Val Cell (n) as x.val ↦ n // field declarations val: int // methods introduce set(x: int) dynamic {this.Val(_)}_{this.Val(x)} static {this.Val Cell (_)}_{this.Val Cell (x)} { this.val := x } introduce get(): int dynamic {this.Val(v)}_{this.Val(v) * Res = v} static {this.Val Cell (v)}_{this.Val Cell (v) * Res = v} { Res := this.val } } 27

The key insight: define x.Val DCell (n) as false The proof obligations (e.g. behavioural subtyping) are trivialized DCell ensures that no client will ever have a Val predicate for a Dcell object. Therefore, in the “Val-world”, DCell is not a subtype of Cell (that is, a variable of static type Cell that satisfies Val will not point to a DCell object) Apfs specify logical inheritance 28

Conclusion Separation logic for reasoning about shared mutable state Apfs and static/dynamic method specs allow flexible handling of inheritance The combination (Parkinson & Bierman’s system) suits the OO paradigm well. Modular and intuitive. Can verify common design patterns Implemented in tools: jStar, VeriFast This was just the basics – there is much more in the paper, and several extensions also exist 29