Korrekte Software: Grundlagen und Methoden Vorlesung 1 vom 06.04.17: Einführung
Serge Autexier, Christoph Lüth
Universität Bremen
Sommersemester 2017
Organisatorisches
I Veranstalter:
Christoph Lüth christoph.lueth@dfki.de
MZH 4186, Tel. 59830
Serge Autexier serge.autexier@dfki.de
Cartesium 2.11, Tel. 59834
I Termine:
I Montag, 14 – 16, MZH 6210
I Donnerstag, 14 – 16, MZH 1110
I Webseite:
http://www.informatik.uni-bremen.de/~cxl/lehre/ksgm.ss17
Prüfungsformen
I 10 Übungsblätter (geplant)
I Prüfungsform 1:
I Bearbeitung derÜbungsblätter,
I Fachgespräch,
I Noteaus den Übungsblättern.
I Prüfungsform 2:
I Mind. ausreichende Bearbeitung der Übungsblätter (50%),
I mündliche Prüfung,
I Noteaus der Prüfung.
Warum Korrekte Software?
Software-Disaster I: Therac-25
Bekannte Software-Disaster II: Ariane-5
Bekannte Software-Disaster III: Airbus A400M
Inhalt der Vorlesung
Themen
Korrekte Software im Lehrbuch:
I Spielzeugsprache
I Wenig Konstrukte
I Kleine Beispiele
Korrekte Software im Einsatz:
I Richtige Programmiersprache
I Mehr als nur ganze Zahlen
I Skalierbarkeit — wie können große Programme verifiziert werden?
Inhalt
I Grundlagen:
I DerHoare-Kalkül — Beweis der Korrektheit von Programmen
I Bedeutung von Programmen:Semantik
I Erweiterung der Programmkonstrukte und des Hoare-Kalküls:
1. Referenzen (Zeiger)
2. Funktion und Prozeduren (Modularität) 3. ReicheDatenstrukturen(Felder,struct)
I Übungsbetrieb:
I Betrachtete Programmiersprache: “C0” (erweiterte Untermenge von C)
I Entwicklung eines Verifikationswerkzeugs in Scala
I Beweise mit Princess (automatischerTheorembeweiser)
Einige Worte zu Scala
I Ascalable language
I Rein objektorientiert
I Funktional
I Eine “JVM-Sprache”
I Seit 2004 von Martin Odersky, EPFL Lausanne (http://www.scala-lang.org/).
I Seit 2011 kommerziell durch Lightbend Inc. (formerly Typesafe)
Scala am Beispiel: 01-GCD.scala
Was sehen wir hier?
d e f gcdLoop ( x : Long , y : Long ) : Long = {
v a r a = x v a r b = y w h i l e ( a 6= 0 ) {
v a l temp = a
a = b % a b = temp }
r e t u r n b }
d e f gcd ( x : Long , y : Long ) : Long = i f ( y == 0 ) x e l s e gcd ( y , x % y )
I Variablen, veränderlich ( var )
I Mit Vorsicht benutzen!
I Werte, unveränderlich ( val )
I while -Schleifen
I Unnötig!
I Rekursion
I Endrekursion wird optimiert
I Typinferenz
I Mehr als Java, weniger als Haskell
I Interaktive Auswertung
Scala am Beispiel: 01-GCD.scala
Was sehen wir hier?
d e f gcdLoop ( x : Long , y : Long ) : Long = {
v a r a = x v a r b = y w h i l e ( a 6= 0 ) {
v a l temp = a
a = b % a b = temp }
r e t u r n b }
d e f gcd ( x : Long , y : Long ) : Long = i f ( y == 0 ) x e l s e gcd ( y , x % y )
I Variablen, veränderlich ( var )
I Mit Vorsicht benutzen!
I Werte, unveränderlich ( val )
I while -Schleifen
I Unnötig!
I Rekursion
I Endrekursion wird optimiert
I Typinferenz
I Mehr als Java, weniger als Haskell
I Interaktive Auswertung
Scala am Beispiel: 01-GCD.scala
Was sehen wir hier?
d e f gcdLoop ( x : Long , y : Long ) : Long = {
v a r a = x v a r b = y w h i l e ( a 6= 0 ) {
v a l temp = a
a = b % a b = temp }
r e t u r n b }
d e f gcd ( x : Long , y : Long ) : Long = i f ( y == 0 ) x e l s e gcd ( y , x % y )
I Variablen, veränderlich ( var )
I Mit Vorsicht benutzen!
I Werte, unveränderlich ( val )
I while -Schleifen
I Unnötig!
I Rekursion
I Endrekursion wird optimiert
I Typinferenz
I Mehr als Java, weniger als Haskell
I Interaktive Auswertung
Scala am Beispiel: 01-GCD.scala
Was sehen wir hier?
d e f gcdLoop ( x : Long , y : Long ) : Long = {
v a r a = x v a r b = y w h i l e ( a 6= 0 ) {
v a l temp = a
a = b % a b = temp }
r e t u r n b }
d e f gcd ( x : Long , y : Long ) : Long = i f ( y == 0 ) x e l s e gcd ( y , x % y )
I Variablen, veränderlich ( var )
I Mit Vorsicht benutzen!
I Werte, unveränderlich ( val )
I while -Schleifen
I Unnötig!
I Rekursion
I Endrekursion wird optimiert
I Typinferenz
I Mehr als Java, weniger als Haskell
I Interaktive Auswertung
Scala am Beispiel: 02-Rational-1.scala
Was sehen wir hier?
c l a s s R a t i o n a l ( n : I n t , d : I n t ) { r e q u i r e ( d 6= 0 )
p r i v a t e v a l g = gcd ( n . abs , d . a b s )
v a l numer = n / g
v a l denom = d / g
d e f t h i s( n : I n t ) = t h i s( n , 1 ) d e f add ( t h a t : R a t i o n a l ) : R a t i o n a l =
new R a t i o n a l (
numer ∗ t h a t . denom + t h a t . numer
∗ denom , denom ∗ t h a t . denom )
o v e r r i d e d e f t o S t r i n g = numer +" / "+
denom
p r i v a t e d e f gcd ( a : I n t , b : I n t ) : I n t =
i f ( b == 0 ) a e l s e gcd ( b , a % b ) }
I Klassenparameter
I Konstruktoren ( this )
I Klassenvorbedingungen ( require )
I private Werte und Methoden
I Methoden, Syntax für Methodenanwendung
I override (nicht optional)
I Overloading
I Operatoren
I Companion objects ( object )
Scala am Beispiel: 02-Rational-1.scala
Was sehen wir hier?
c l a s s R a t i o n a l ( n : I n t , d : I n t ) { r e q u i r e ( d 6= 0 )
p r i v a t e v a l g = gcd ( n . abs , d . a b s )
v a l numer = n / g
v a l denom = d / g
d e f t h i s( n : I n t ) = t h i s( n , 1 ) d e f add ( t h a t : R a t i o n a l ) : R a t i o n a l =
new R a t i o n a l (
numer ∗ t h a t . denom + t h a t . numer
∗ denom , denom ∗ t h a t . denom )
o v e r r i d e d e f t o S t r i n g = numer +" / "+
denom
p r i v a t e d e f gcd ( a : I n t , b : I n t ) : I n t =
i f ( b == 0 ) a e l s e gcd ( b , a % b )
I Klassenparameter
I Konstruktoren ( this )
I Klassenvorbedingungen ( require )
I private Werte und Methoden
I Methoden, Syntax für Methodenanwendung
I override (nicht optional)
I Overloading
I Operatoren
I Companion objects ( object )
Algebraische Datentypen: 03-Expr.scala
Was sehen wir hier?
a b s t r a c t c l a s s E x p r
c a s e c l a s s Var ( name : S t r i n g ) e x t e n d s E x p r
c a s e c l a s s Number ( num : D o u b l e ) e x t e n d s E x p r
c a s e c l a s s UnOp ( o p e r a t o r : S t r i n g , a r g : E x p r ) e x t e n d s E x p r c a s e c l a s s BinOp ( o p e r a t o r : S t r i n g ,
l e f t : Expr , r i g h t : E x p r ) e x t e n d s E x p r
d e f e v a l ( e x p r : E x p r ) : D o u b l e = e x p r
match {
c a s e v : Var ⇒ 0 // Variables evaluate to 0
c a s e Number ( x ) ⇒ x
c a s e BinOp ("+", e1 , e2 ) ⇒ e v a l ( e1 ) + e v a l ( e2 )
c a s e BinOp ("∗", e1 , e2 ) ⇒ e v a l ( e1 )
∗ e v a l ( e2 )
c a s e UnOp ("−", e ) ⇒ − e v a l ( e ) }
v a l e = BinOp ("∗", Number ( 1 2 ) , UnOp ("−", BinOp ("+",
Number ( 2 . 3 ) ,
Number ( 3 . 7 ) ) ) )
I case class erzeugt
I Factory-Methode für Konstruktoren
I Parameter als implizite val
I abgeleitete Implementierung für toString , equals
I . . . und pattern matching ( match )
I Pattern sind
I case 4 => Literale
I case C(4) => Konstruktoren
I case C(x) => Variablen
I case C(_) => Wildcards
I case x: C => getypte pattern
I case C(D(x: T, y), 4)
=> geschachtelt
Korrekte Software 15 [25]
Algebraische Datentypen: 03-Expr.scala
Was sehen wir hier?
a b s t r a c t c l a s s E x p r
c a s e c l a s s Var ( name : S t r i n g ) e x t e n d s E x p r
c a s e c l a s s Number ( num : D o u b l e ) e x t e n d s E x p r
c a s e c l a s s UnOp ( o p e r a t o r : S t r i n g , a r g : E x p r ) e x t e n d s E x p r c a s e c l a s s BinOp ( o p e r a t o r : S t r i n g ,
l e f t : Expr , r i g h t : E x p r ) e x t e n d s E x p r
d e f e v a l ( e x p r : E x p r ) : D o u b l e = e x p r
match {
c a s e v : Var ⇒ 0 // Variables evaluate to 0
c a s e Number ( x ) ⇒ x
c a s e BinOp ("+", e1 , e2 ) ⇒ e v a l ( e1 ) + e v a l ( e2 )
c a s e BinOp ("∗", e1 , e2 ) ⇒ e v a l ( e1 )
∗ e v a l ( e2 )
c a s e UnOp ("−", e ) ⇒ − e v a l ( e ) }
v a l e = BinOp ("∗", Number ( 1 2 ) , UnOp ("−", BinOp ("+",
I case class erzeugt
I Factory-Methode für Konstruktoren
I Parameter als implizite val
I abgeleitete Implementierung für toString , equals
I . . . und pattern matching ( match )
I Pattern sind
I case 4 => Literale
I case C(4) => Konstruktoren
I case C(x) => Variablen
I case C(_) => Wildcards
I case x: C => getypte pattern
I case C(D(x: T, y), 4)
=> geschachtelt
Implementierung algebraischer Datentypen
Haskell:
data T = C1 | ... | Cn
I Ein Typ T
I Konstruktoren erzeugen Datentyp
Scala:
T
C1 . . . Cn
-
I Varianten als Subtypen
I Problem und Vorteil:
Erweiterbarkeit
I sealed verhindert Erweiterung
Implementierung algebraischer Datentypen
Haskell:
data T = C1 | ... | Cn
I Ein Typ T
I Konstruktoren erzeugen Datentyp
Scala:
T
C1 . . . Cn
-
I Varianten als Subtypen
I Problem und Vorteil:
Erweiterbarkeit
I sealed verhindert Erweiterung
Das Typsystem
Das Typsystem behebt mehrere Probleme von Java:
I Werte vs. Objekte
I Scala vs. Java
I NULL references
Vererbungshierarchie
Quelle: Odersky, Spoon, Venners:Programming in Scala
Parametrische Polymorphie
I Typparameter (wie in Haskell, Generics in Java), Bsp. List[T]
I Problem: Vererbung und Polymorphie
I Ziel: wenn S < T , dann List[S] < List[T]
I Does not work— 04-Ref.hs
I Warum?
I Funktionsraum nicht monoton im ersten Argument
I SeiX ⊆Y, dannZ −→X ⊆Z −→Y, aberX −→Z 6⊆Y −→Z
I SondernY −→Z ⊆X −→Z
Parametrische Polymorphie
I Typparameter (wie in Haskell, Generics in Java), Bsp. List[T]
I Problem: Vererbung und Polymorphie
I Ziel: wenn S < T , dann List[S] < List[T]
I Does not work— 04-Ref.hs
I Warum?
I Funktionsraum nicht monoton im ersten Argument
I SeiX ⊆Y, dann Z −→X ⊆Z −→Y, aberX −→Z 6⊆Y −→Z
I SondernY −→Z ⊆X −→Z
Typvarianz
class C[+T]
I Kovariant
I Wenn S < T , dann C[S] <
C[T]
I Parametertyp T nur im
Wertebereich von Methoden
class C[T]
I Rigide
I Kein Subtyping
I Parametertyp T kannbeliebig verwendet werden
class C[-T]
I Kontravariant
I Wenn S < T , dann C[T] <
C[S]
I Parametertyp T nur im
Definitionsbereich von Methoden Beispiel:
c l a s s F u n c t i o n [−S , +T ] { d e f a p p l y ( x : S ) : T }
Traits: 05-Funny.scala
Was sehen wir hier?
I Trait (Mix-ins): abstrakte Klassen, Interfaces; Haskell: Typklassen
I „Abstrakte Klasse ohne Oberklasse“
I Unterschied zu Klassen:
I Mehrfachvererbung möglich
I Keine feste Oberklasse ( super dynamisch gebunden)
I Nützlich zur Strukturierung (Aspektorientierung)
I Nützlich zur Strukturierung:
thin interface+trait=rich interface Beispiel:05-Ordered.scala,05-Rational.scala
Komprehension mit for : 06-For.scala
v a l l 1 =
L i s t ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ) f o r { x← l 1 ;
i f ( x % 2 == 0 ) } y i e l d 2∗x+1
v a l l 1= L i s t . r a n g e ( 1 , 9 ) d e f h a l f ( x : I n t ) :
O p t i o n [ I n t ] =
i f ( x%2 == 0 ) Some ( x / 2 ) e l s e None
f o r { x← l 1 ; y← h a l f ( x ) } y i e l d y
I For-Schleife iteriert über Liste:
I Generatoren, Filter, Result
I Für andere Datentypen: Option
I Für beliebige Datentypen T mit d e f map [ B ] ( f : ( A) ⇒
B) : T [ B ] = ? ? ? d e f f l a t M a p [ B ] ( f : ( A)⇒
T [ B ] ) : T [ B ] = ? ? ?
Komprehension mit for : 06-For.scala
v a l l 1 =
L i s t ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ) f o r { x← l 1 ;
i f ( x % 2 == 0 ) } y i e l d 2∗x+1
v a l l 1= L i s t . r a n g e ( 1 , 9 ) d e f h a l f ( x : I n t ) :
O p t i o n [ I n t ] =
i f ( x%2 == 0 ) Some ( x / 2 ) e l s e None
f o r { x← l 1 ; y← h a l f ( x ) } y i e l d y
I For-Schleife iteriert über Liste:
I Generatoren, Filter, Result
I Für andere Datentypen: Option
I Für beliebige Datentypen T mit d e f map [ B ] ( f : ( A) ⇒
B) : T [ B ] = ? ? ? d e f f l a t M a p [ B ] ( f : ( A)⇒
T [ B ] ) : T [ B ] = ? ? ?
Was wir ausgelassen haben. . .
I Gleichheit: == (final), equals (nicht final), eq (Referenzen)
I ImpliziteParameter und Typkonversionen
I Stringinterpolation, XML
I Nebenläufigkeit(Aktoren, Futures)
I TypsichereMetaprogrammierung
I Dassimple build tool sbt
I Scala-Plugin für IntelliJ
I Der JavaScript-Compiler scala.js
Zusammenfassung
I Objekt-orientiert:
I Veränderlicher, gekapselterZustand
I Subtypenund Vererbung
I KlassenundObjekte
I Funktional:
I UnveränderlicheWerte
I Parametrische und Ad-hocPolymorphie
I Funktionen höherer Ordnung
I Hindley-MilnerTypinferenz
Zusammenfassung
I Zum Lernen von Scala: 0. Übungsblatt
I Keine Punkte, aber Kurzbewertung wenn gewünscht.
I Nächste Woche:
I Reprise der Hoare-Logik
I Semantik
I Erste Gehversuche mit dem Analysewerkzeug
Korrekte Software: Grundlagen und Methoden Vorlesung 2 vom 10.04.17: Die Floyd-Hoare-Logik
Serge Autexier, Christoph Lüth
Universität Bremen
Sommersemester 2017
Organisatorisches
Die Übung am Donnerstag, 13.04.17, muss leider ausfallen!
Fahrplan
I Einführung
I Die Floyd-Hoare-Logik
I Operationale Semantik
I Denotationale Semantik
I Äquivalenz der Operationalen und Denotationalen Semantik
I Korrektheit des Hoare-Kalküls
I Vorwärts und Rückwärts mit Floyd und Hoare
I Funktionen und Prozeduren
I Referenzen und Speichermodelle
I Verifikationsbedingungen Revisited
I Vorwärtsrechnung Revisited
I Programmsicherheit und Frame Conditions
I Ausblick und Rückblick
Floyd-Hoare-Logik: Idee
I Was wird hier berechnet?
I Wie können wir dasbeweisen?
I Wir berechnen symbolisch, welche Werte Variablen über den
Programmverlauf annehmen.
p= 1 ; c= 1 ;
w h i l e ( c <= n ) { p := p ∗ c ; c := c + 1 ; }
I Um Aussagen über ein Program zu beweisen, benötigen wir einen Formalismus (eineLogik), die es erlaubt, Zusicherungen über Werte von Variablen zu bestimmten Ausführungszeitpunkten (im Programm) aufzuschreibenund zu beweisen.
I Dazu müssen wir auch dieBedeutung (Semantik) des Programmes definieren — die Frage “Was tut das Programm” mathematischexakt beantworten.
Floyd-Hoare-Logik: Idee
I Was wird hier berechnet?p =n!
I Wie können wir dasbeweisen?
I Wir berechnen symbolisch, welche Werte Variablen über den
Programmverlauf annehmen.
p= 1 ; c= 1 ;
w h i l e ( c <= n ) { p := p ∗ c ; c := c + 1 ; }
I Um Aussagen über ein Program zu beweisen, benötigen wir einen Formalismus (eineLogik), die es erlaubt, Zusicherungen über Werte von Variablen zu bestimmten Ausführungszeitpunkten (im Programm) aufzuschreibenund zu beweisen.
I Dazu müssen wir auch dieBedeutung (Semantik) des Programmes definieren — die Frage “Was tut das Programm” mathematischexakt beantworten.
Floyd-Hoare-Logik: Idee
I Was wird hier berechnet?p =n!
I Wie können wir dasbeweisen?
I Wir berechnen symbolisch, welche Werte Variablen über den
Programmverlauf annehmen.
{1≤n}
p= 1 ; c= 1 ;
w h i l e ( c <= n ) { p := p ∗ c ; c := c + 1 ; }
{p=n!}
I Um Aussagen über ein Program zu beweisen, benötigen wir einen Formalismus (eineLogik), die es erlaubt, Zusicherungen über Werte von Variablen zu bestimmten Ausführungszeitpunkten (im Programm) aufzuschreibenund zu beweisen.
I Dazu müssen wir auch dieBedeutung (Semantik) des Programmes definieren — die Frage “Was tut das Programm” mathematischexakt beantworten.
Floyd-Hoare-Logik: Idee
I Was wird hier berechnet?p =n!
I Wie können wir dasbeweisen?
I Wir berechnen symbolisch, welche Werte Variablen über den
Programmverlauf annehmen.
{1≤n}
p= 1 ; c= 1 ;
w h i l e ( c <= n ) { p := p ∗ c ; c := c + 1 ; }
{p=n!}
I Um Aussagen über ein Program zu beweisen, benötigen wir einen Formalismus (eineLogik), die es erlaubt, Zusicherungen über Werte von Variablen zu bestimmten Ausführungszeitpunkten (im Programm) aufzuschreibenund zu beweisen.
I Dazu müssen wir auch dieBedeutung (Semantik) des Programmes definieren — die Frage “Was tut das Programm” mathematischexakt beantworten.
Semantik von Programmiersprachen
Drei wesentliche Möglichkeiten:
I Operationale Semantikbeschreibt die Bedeutung eines Programmes, indem die Ausführung auf einer abstrakten Maschine beschrieben wird.
I Denotationale Semantikbildet jedes Programm auf ein mathematisches Objekt (meist ein partielle Funktion zwischen Systemzuständen) ab.
I Axiomatische Semantikbeschreibt die Bedeutung eines Programmes durch Beweisregeln, mit welchem sich gültige Eigenschaften herleiten lassen. Das prominenteste Beispiel hierzu ist die Floyd-Hoare-Logik.
Drei Semantiken — Eine Sicht
p = 1;
c := 1;
while (c <= n) { p= p * c;
c= c + 1;
}
Operational
Axiomatisch Denotational
Programm
I Jede Semantik ist eine Sichtauf das Program.
I Diese Semantiken sollten alleäquivalentsein. Wir müssen sie also in Beziehung setzen, und zeigen dass sie diegleiche Sichtergeben.
I Für die axiomatische Semantik (die
Floyd-Hoare-Logik) ist das die Frage derKorrektheit der Regeln.
Floyd-Hoare-Logik
I Grundbaustein der Floyd-Hoare-Logik sindZusicherungen der Form {P}c{Q}(Floyd-Hoare-Tripel), wobei P dieVorbedingung ist,c das Programm, undQ dieNachbedingung.
I Die Logik hat sowohllogische Variablen (zustandsfrei), und
Programmvariablen(deren Wert sich über die Programmausführung ändert).
I Die Floyd-Hoare-Logik hat eine wesentlichesPrinzipand einenTrick.
I DasPrinzipist die Abstraktion vom Programmzustand durch eine logische Sprache; insbesondere wird dieZuweisung durch Substitution modelliert.
I DerTrickbehandelt Schleifen: Iteration im Programm entspricht Rekursion in der Logik. Ein Beweis ist daher induktiv, und benötigt eine Induktionsannahme — eineInvariante.
Unsere Programmiersprache
Wir betrachten einen Ausschnitt der ProgrammierspracheC (C0).
Ausbaustufe 1 kennt folgende Konstrukte:
I Typen: int;
I Ausdrücke: Variablen, Literale (für ganze Zahlen), arithmetische Operatoren (für ganze Zahlen), Relationen (==,!=,<=, . . . ), boolsche Operatoren (&&, ||);
I Anweisungen:
I Fallunterscheidung (if. . .else. . . ), Iteration (while), Zuweisung, Blöcke;
I Sequenzierung und leere Anweisung sind implizit
C0: Ausdrücke und Anweisungen
Aexp a::=N|Loc |a1+a2 |a1−a2 |a1∗a2 |a1/a2 Bexp b ::=0|1|a1 ==a2 |a1! =a2
|a1 <=a2 |!b |b1&&b2 |b1||b2
Exp e :=Aexp |Bexp Stmt c ::= Loc=Exp;
| if( b ) c1 else c2
| while ( b ) c
| {c∗}
Floyd-Hoare-Tripel
Partielle Korrektheit (|={P}c{Q})
c ist partiell korrekt, wenn für alle Zustände σ, dieP erfüllen:
wenn die Ausführung vonc mitσ in σ0 terminiert, dann erfülltσ0 Q
Totale Korrektheit (|= [P]c[Q])
c ist total korrekt, wenn für alle Zustandeσ, dieP erfüllen:
die Ausführung vonc mit σ in σ0 terminiert, und σ0 erfülltQ.
I Folgendesgilt:|={true} while(1){ } {true}
I Folgendes giltnicht:|= [true] while(1){ }[true]
Regeln der Floyd-Hoare-Logik
I Die Floyd-Hoare-Logik erlaubt es, Zusicherungen der Form
` {P}c{Q} syntaktischherzuleiten.
I DerKalkül der Logik besteht aus sechs Regeln der Form
` {P1}c1{Q1}. . .` {Pn}cn{Qn}
` {P}c{Q}
I Für jedes Konstrukt der Programmiersprache gibt es eine Regel.
Regeln der Floyd-Hoare-Logik: Zuweisung
` {P[e/x]}x =e{P}
I Eine Zuweisungx=e ändert den Zustand so dass an der Stellex jetzt der Wert vone steht. Damit nachher das PrädikatP gilt, muss also vorherdas Prädikat gelten, wenn wirx durch e ersetzen.
I Es ist völlig normal (aber dennoch falsch) zu denken, die Substitution gehöre eigentlich in die Nachbedingung.
I Beispiele:
x = 5 {x <10}
x = x+ 1
Regeln der Floyd-Hoare-Logik: Zuweisung
` {P[e/x]}x =e{P}
I Eine Zuweisungx=e ändert den Zustand so dass an der Stellex jetzt der Wert vone steht. Damit nachher das PrädikatP gilt, muss also vorherdas Prädikat gelten, wenn wirx durch e ersetzen.
I Es ist völlig normal (aber dennoch falsch) zu denken, die Substitution gehöre eigentlich in die Nachbedingung.
I Beispiele:
{5<10⇐⇒(x <10)[5/x]}
x = 5 {x <10}
x = x+ 1
Regeln der Floyd-Hoare-Logik: Zuweisung
` {P[e/x]}x =e{P}
I Eine Zuweisungx=e ändert den Zustand so dass an der Stellex jetzt der Wert vone steht. Damit nachher das PrädikatP gilt, muss also vorherdas Prädikat gelten, wenn wirx durch e ersetzen.
I Es ist völlig normal (aber dennoch falsch) zu denken, die Substitution gehöre eigentlich in die Nachbedingung.
I Beispiele:
{5<10⇐⇒(x <10)[5/x]}
x = 5 {x <10}
x = x+ 1 {x <10}
Regeln der Floyd-Hoare-Logik: Zuweisung
` {P[e/x]}x =e{P}
I Eine Zuweisungx=e ändert den Zustand so dass an der Stellex jetzt der Wert vone steht. Damit nachher das PrädikatP gilt, muss also vorherdas Prädikat gelten, wenn wirx durch e ersetzen.
I Es ist völlig normal (aber dennoch falsch) zu denken, die Substitution gehöre eigentlich in die Nachbedingung.
I Beispiele:
{5<10⇐⇒(x <10)[5/x]}
x = 5 {x <10}
{x <9⇐⇒x+ 1<10}
x = x+ 1 {x <10}
Regeln der Floyd-Hoare-Logik: Fallunterscheidung und Sequenzierung
` {A&&b}c0{B} ` {A&&¬b}c1{B}
` {A} if(b) c0 else c1{B}
I In der Vorbedingung desif-Zweiges gilt die Bedingung b, und im else-Zweig gilt die Negation ¬b.
I Beide Zweige müssem mit derselben Nachbedingung enden.
` {A}c{B} ` {B} {cs} {C}
` {A} {c cs} {C}
I Hier wird ein ZwischenzustandB benötigt.
Regeln der Floyd-Hoare-Logik: Iteration
` {A∧b}c{A}
` {A} while(b) c{A∧ ¬b}
I Iteration korrespondiert zuInduktion.
I Bei (natürlicher) Induktion zeigen wir, dass diegleicheEigenschaft P für 0 gilt, und dass wenn sie fürP(n) gilt, daraus folgt, dass sie für P(n+ 1) gilt.
I Analog dazu benötigen wir hier eineInvarianteA, die sowohl vorals auchnach dem Schleifenrumpf gilt.
I In derVorbedingungdes Schleifenrumpfeskönnen wir die Schleifenbedingungb annehmen.
I DieVorbedingungder Schleife ist die InvarianteA, und die Nachbedingungder Schleife istA und die Negation der Schleifenbedingungb.
Regeln der Floyd-Hoare-Logik: Weakening
A0 =⇒A ` {A}c{B} B=⇒B0
` {A0}c{B0}
c
All possible program states
A B
c
All possible program states B' A'
I |={A}c{B}: Ausführung von c startet in Zustand, in dem Agilt, und endet (ggf) in Zustand, in demB gilt.
I Zustandsprädikate beschreiben Mengen von Zuständen:P ⊆Q gdw.
P =⇒Q.
I Wir könnenAzuA0 einschränken (A0 ⊆A oderA0 =⇒A), oderB zu B0 vergrößern (B ⊆B0 oderB=⇒B0), und erhalten |={A0}c{B0}.
Regeln der Floyd-Hoare-Logik: Weakening
A0 =⇒A ` {A}c{B} B=⇒B0
` {A0}c{B0}
c
All possible program states
A B
c
All possible program states A' B'
I |={A}c{B}: Ausführung von c startet in Zustand, in dem Agilt, und endet (ggf) in Zustand, in demB gilt.
I Zustandsprädikate beschreiben Mengen von Zuständen:P ⊆Q gdw.
P =⇒Q.
I Wir könnenAzuA0 einschränken (A0 ⊆A oderA0 =⇒A), oder B zu B0 vergrößern (B ⊆B0 oderB=⇒B0), und erhalten |={A0}c{B0}.
Überblick: die Regeln des Floyd-Hoare-Kalküls
` {P[e/x]}x =e{P}
` {A} { } {A}
` {A}c{B} ` {B} {cs} {C}
` {A} {c cs} {C}
` {A∧b}c0{B} ` {A∧ ¬b}c1{B}
` {A} if(b) c0 else c1{B}
` {A∧b}c{A}
` {A}while(b) c{A∧ ¬b}
A0=⇒A ` {A}c{B} B=⇒B0
` {A0}c{B0}
Eigenschaften der Floyd-Hoare-Logik
Korrektheit
Wenn ` {P}c{Q}, dann |={P}c{Q}
I Wenn wir eine Korrektheitsaussage herleiten können, dann gilt sie auch.
I Wird gezeigt, indem wir|={P}c{Q} durch die anderen Semantiken definieren, und zeigen, dass alle Regeln diese Gültigkeit erhalten.
Relative Vollständigkeit
Wenn |={P}c{Q}, dann ` {P}c{Q} (bis auf Weakening)
I Wenn eine Korrektheitsaussage nicht beweisen werden kann (aber sie stimmt), dann liegt das immer daran, dass einelogische Aussage(in einer Anwendung der Weakening-Regelx) nicht bewiesen werden kann.
I Das ist zu erwarten: alle interessanten Logiken sind unvollständig.
Wie wir Floyd-Hoare-Beweise aufschreiben
// {P}
// {P1} x= e ; // {P2} // {P3}
w h i l e ( x< n ) { // {P3∧x<n}
// {P4} z= a ; // {P3} }
// {P3∧ ¬(x<n)}
// {Q}
I Beispiel zeigt: ` {P}c{Q}
I Programm wird mit gültigen Zusicherungen annotiert.
I Vor einer Zeile steht die Vorbedingung, danach die Nachbedingung.
I Implizite Anwendung der Sequenzenregel.
I Weakening wird notiert durch mehrere Zusicherungen, und mussbewiesenwerden.
I Im Beispiel:P=⇒P1,
P2=⇒P3,P3∧x<n=⇒P4, P3∧ ¬(x <n) =⇒Q.
Warum Verifikation?
Hier sind Varianten des Fakultätsbeispiels.
Welche sind korrekt?
// {1≤n}
p = 1 ; c = 1 ;
w h i l e ( c<=n ) { c = c +1;
p = p∗c ; }
// {p=n!}
// {1≤n}
p = 1 ; c = 1 ;
w h i l e ( c<n ) { c = c +1;
p = p∗c ; }
// {p=n!}
//{1≤N∧n=N}
p = 1 ;
w h i l e (0<n ) { p = p∗n ; n = n−1;
}
//{p=N!}
Eine Handvoll Beispiele
// {y =Y} x= 1 ;
w h i l e ( y != 0 ) { y= y−1;
x= 2∗x ; }
// {x = 2Y}
// {a≥0∧b≥0}
r= a ; q= 0 ;
w h i l e ( b <= r ) { r= r−b ;
q= q +1;
}
// {a=b∗q+r∧0≥r∧r <b}
//{0≤a}
t= 1 ; s= 1 ; i = 0 ;
w h i l e ( s <= a ) { t= t+ 2 ;
s= s+ t ; i = i + 1 ; }
//{i2≤a∧a<(i+ 1)2}
Eine Handvoll Beispiele
// {y =Y ∧y≥0}
x= 1 ;
w h i l e ( y != 0 ) { y= y−1;
x= 2∗x ; }
// {x = 2Y}
// {a≥0∧b≥0}
r= a ; q= 0 ;
w h i l e ( b <= r ) { r= r−b ;
q= q +1;
}
// {a=b∗q+r∧0≥r∧r <b}
//{0≤a}
t= 1 ; s= 1 ; i = 0 ;
w h i l e ( s <= a ) { t= t+ 2 ;
s= s+ t ; i = i + 1 ; }
//{i2≤a∧a<(i+ 1)2}
Zusammenfassung
I Floyd-Hoare-Logik zusammengefasst:
I Die Logik abstrahiert über konkrete Systemzustände durchZusicherungen (Hoare-Tripel|={P}c{Q}).
I Zusicherungen sind boolsche Ausdrücke, angereichert durch logische Variablen und Programmvariablen.
I Wir können partielle Korrektheitsaussgen der Form|={P}c{Q} herleiten (oder totale,|= [P]c[Q]).
I Zuweisungen werden durch Substitution modelliert, d.h. die Menge der gültigen Aussagen ändert sich.
I Für Iterationen wird eineInvariantebenötigt (dienichthergeleitet werden kann).
I Die Korrektheit hängt sehr davon ab, wieexaktwir die Semantik der Programmiersprache beschreiben können.
Korrekte Software: Grundlagen und Methoden Vorlesung 3 vom 20.04.17: Operationale Semantik
Serge Autexier, Christoph Lüth
Universität Bremen
Sommersemester 2017
Fahrplan
I Einführung
I Die Floyd-Hoare-Logik
I Operationale Semantik
I Denotationale Semantik
I Äquivalenz der Operationalen und Denotationalen Semantik
I Korrektheit des Hoare-Kalküls
I Vorwärts und Rückwärts mit Floyd und Hoare
I Funktionen und Prozeduren
I Referenzen und Speichermodelle
I Verifikationsbedingungen Revisited
I Vorwärtsrechnung Revisited
I Programmsicherheit und Frame Conditions
I Ausblick und Rückblick
Zutaten
// GGT(A,B)
i f ( a == 0 ) r = b ; e l s e {
w h i l e ( b != 0 ) { i f ( a <= b )
b = b − a ; e l s e a = a − b ; }
r = a ; }
I Programme berechnenWerte
I Basierend auf
I Werte sindVariablenzugewiesen
I Evaluation vonAusdrücken
I Folgt dem Programmablauf
Unsere Programmiersprache
Wir betrachten einen Ausschnitt der ProgrammierspracheC (C0).
Ausbaustufe 1 kennt folgende Konstrukte:
I Typen: int;
I Ausdrücke: Variablen, Literale (für ganze Zahlen), arithmetische Operatoren (für ganze Zahlen), Relationen (==,!=,<=, . . . ), boolsche Operatoren (&&, ||);
I Anweisungen:
I Fallunterscheidung (if. . .else. . . ), Iteration (while), Zuweisung, Blöcke;
I Sequenzierung und leere Anweisung sind implizit
Semantik von C0
I Die (operationale) Semantik einer imperativen Sprache wie C0 ist ein Zustandsübergang: das System hat einen impliziten Zustand, der durch Zuweisung vonWerten an Adressengeändert werden kann.
I Konkretes Beispiel:n= 3
p ? c ? n 3
p 1 c ? n 3
p 1 c 1 n 3
p 1 c 1 n 3
. . .
p 6 c 4 n 3
p = 1 ; c = 1 ;
w h i l e( c <= n ) { p = p ∗ c ; c = c + 1 ; }
Systemzustände
I Ausdrücke werten zuWerten V (hier ganze Zahlen) aus.
I AdressenLocsind hier Programmvariablen (Namen)
I EinSystemzustandbildet Adressen auf Werte ab: Σ =Loc*V
I Ein Programm bildet einen Anfangszustandmöglicherweiseauf einen Endzustand ab (wenn esterminiert).
I Zusicherungen sind Prädikate über dem Systemzustand.
C0: Ausdrücke und Anweisungen
Aexp a::=N|Loc |a1+a2 |a1−a2 |a1∗a2 |a1/a2 Bexp b ::=0|1|a1 ==a2 |a1! =a2
|a1 <=a2 |!b |b1&&b2 |b1||b2
Exp e :=Aexp |Bexp Stmt c ::= Loc=Exp;
| if( b ) c1 else c2
| while ( b ) c
| {c∗}
Eine Handvoll Beispiele
// {y =Y ∧y≥0}
x = 1 ;
w h i l e ( y != 0 ) { y = y−1;
x = 2∗x ; }
// {x = 2Y}
// {a≥0∧b≥0}
r = b ; q = 0 ;
w h i l e ( b <= r ) { r = r−y ;
q = q +1;
}
// {a=b∗q+r∧r <b}
p = 1 ; c = 1 ;
w h i l e ( c<=n ) { c = c +1;
p = p∗c ; }
//{p=n!}
//{0≤a}
t = 1 ; s = 1 ; i = 0 ;
w h i l e ( s <= a ) { t = t + 2 ; s = s + t ; i = i + 1 ; }
//{i2≤a∧a<(i+ 1)2}
Operationale Semantik: Arithmetische Ausdrücke
Ein arithmetischer Ausdruck a wertet unter gegebenen Zustandσ zu einer ganzen Zahl n (Wert) aus oder zu einem Fehler⊥.
I Aexpa::=N |Loc|a1+a2 |a1−a2 |a1∗a2 |a1 / a2
I Zustände bilden Adressen/Programmvariablen auf Werte ab (σ)
ha, σi →Aexp n|⊥
Regeln:
hn, σi →Aexp n X ∈Loc,X ∈Dom(σ), σ(X) =v
hX, σi →Aexp v
X ∈Loc,X 6∈Dom(σ) hX, σi →Aexp ⊥
Operationale Semantik: Arithmetische Ausdrücke
Ein arithmetischer Ausdruck a wertet unter gegebenen Zustandσ zu einer ganzen Zahl n (Wert) aus oder zu einem Fehler⊥.
I Aexpa::=N |Loc|a1+a2 |a1−a2 |a1∗a2 |a1 / a2
I Zustände bilden Adressen/Programmvariablen auf Werte ab (σ)
ha, σi →Aexp n|⊥
Regeln:
hn, σi →Aexp n
X ∈Loc,X ∈Dom(σ), σ(X) =v hX, σi →Aexp v
X ∈Loc,X 6∈Dom(σ) hX, σi →Aexp ⊥
Operationale Semantik: Arithmetische Ausdrücke
Ein arithmetischer Ausdruck a wertet unter gegebenen Zustandσ zu einer ganzen Zahl n (Wert) aus oder zu einem Fehler⊥.
I Aexpa::=N |Loc|a1+a2 |a1−a2 |a1∗a2 |a1 / a2
I Zustände bilden Adressen/Programmvariablen auf Werte ab (σ)
ha, σi →Aexp n|⊥
Regeln:
hn, σi →Aexp n X ∈Loc,X ∈Dom(σ), σ(X) =v
hX, σi →Aexp v
X ∈Loc,X 6∈Dom(σ) hX, σi →Aexp ⊥
Operationale Semantik: Arithmetische Ausdrücke
I Aexpa::=N |Loc|a1+a2 |a1−a2 |a1∗a2 |a1 / a2
ha, σi →Aexp n|⊥
ha1, σi →Aexp n1 ha2, σi →Aexp n2 ni ∈N,n Summe n1 undn2 ha1+a2, σi →Aexp n
ha1, σi →Aexp n1 ha2, σi →Aexp n2 falls n1 =⊥oder n2 =⊥ ha1+a2, σi →Aexp ⊥
ha1, σi →Aexp n1 ha2, σi →Aexp n2 ni ∈N,n Diff.n1 und n2 ha1−a2, σi →Aexp n
ha1, σi →Aexp n1 ha2, σi →Aexp n2 fallsn1 =⊥oder n2 =⊥ ha1−a2, σi →Aexp ⊥
Operationale Semantik: Arithmetische Ausdrücke
I Aexpa::=N |Loc|a1+a2 |a1−a2 |a1∗a2 |a1 / a2
ha, σi →Aexp n|⊥
ha1, σi →Aexp n1 ha2, σi →Aexp n2 ni ∈N,n Summe n1 undn2 ha1+a2, σi →Aexp n
ha1, σi →Aexp n1 ha2, σi →Aexp n2 falls n1 =⊥oder n2 =⊥ ha1+a2, σi →Aexp ⊥
ha1, σi →Aexp n1 ha2, σi →Aexp n2 ni ∈N,n Diff.n1 und n2 ha1−a2, σi →Aexp n
ha1, σi →Aexp n1 ha2, σi →Aexp n2 falls n1 =⊥oder n2 =⊥ ha1−a2, σi →Aexp ⊥