Académique Documents
Professionnel Documents
Culture Documents
Ing1Info Preuve de Programmes
Ing1Info Preuve de Programmes
M. ABBASSI Imed
Enseignant chercheur à ISIMM
LR-OASIS, Université de Tunis El Manar, Tunis
imed.abbassi@enit.utm.tn
A propos du cours
Objectifs:
• Apprendre les concepts de base de la preuve de
programmes (Logique Hoare, assertions, etc.).
• Apprendre le langage ACSL (Ansi c specification language).
Prérequis: langage C
Références :
• Hoare, C. A. R. (1983). An axiomatic basis for computer programming.
Communications of the ACM, 26(1), 53-56.
• BURGHARDT, Jochen, GERLACH, Jens, HARTIG, Kerstin, et al. ACSL by
Example. DEVICE-SOFT project publication. Fraunhofer FIRST Institute,
2010.
1
26/01/2024
Plan du cours
Introduction à la preuve de
programmes
2
26/01/2024
Motivation (1/3)
Motivation (2/3)
3
26/01/2024
Motivation (3/3)
4
26/01/2024
Test statique:
• C’est le test d’un artefact de logiciel, par exemple, des
exigences, de conception ou de code, sans l’exécution
de ces artefacts.
• Les tests statiques regroupent, par exemple, les revues
(revue de code, de spécification) et les analyses de code.
• Les revues de code peuvent être exécutées très tôt dans
le processus de développement, car ils n’exigent pas un
logiciel en état de fonctionnement.
• Tout bug détecté par les revues est facilement et
rapidement corrigeable avec moindre coût.
10
5
26/01/2024
11
12
6
26/01/2024
Logique de Hoare
13
13
Prédicats (1)
14
7
26/01/2024
Prédicats (2)
15
15
Prédicats (3)
16
8
26/01/2024
Prédicats (4)
Règles d’équivalence:
• On dit que deux prédicats P et Q sont équivalents si et
seulement si le prédicat P⇔Q est toujours vrai (appelé
tautologie ou axiome).
• ⊥ est l’élément absorbant de la conjonction:
⊥∧x≡⊥
• ⊤ est l’élément absorbant de disjonction:
⊤∨x≡⊤
• Idempotence de la disjonction:
x∨x≡x
• Idempotence de la conjonction:
x∧x≡x
17
17
Prédicats (5)
• La disjonction est :
1. Associative: x∨(y∨z)≡(x∨y)∨z
2. Commutative: x∨y ≡ y∨x
3. Distributive sur la conjonction: x∨(y∧z)≡(x∨y)∧(x∨z)
4. ⊥ est l'élément neutre de la disjonction: ⊥∨x≡x
• La conjonction est :
1. Associative: x∧(y∧z)≡(x∧y)∧z
2. Commutative: x∧y≢y∧x
3. Distributive sur la disjonction: x∧(y∨z)≡(x∧y)∨(x∧z)
4. ⊤ est l'élément neutre de la conjonction: ⊤∧x≡x
• Règle de l’implication: x⇒y ≡ ¬x∨y
• Règle de l’équivalence : x⇔y ≡ (x∧y)∨(¬x∧¬y)
18
18
9
26/01/2024
Prédicats (6)
19
Prédicats (7)
1.
2.
3.
4.
5.
6.
20
20
10
26/01/2024
Prédicats (8)
Conséquence logique:
• Un prédicat G est une conséquence logique d’un
ensemble de prédicats H={H1,…Hn}, et on note
H1,H2,…,Hn ⊨ G, si et seulement si le prédicat
H1∧H2…∧Hn ⇒ G est toujours vrai.
• Quelques exemples:
21
21
Prédicats (9)
22
11
26/01/2024
Prédicats (10)
23
23
24
24
12
26/01/2024
25
25
P’ Exécution de S Q
Q’
P
26
13
26/01/2024
27
Règle de conjonction:
𝑷 𝑺 𝑹,𝑷 𝑺 𝑸
𝑷 𝑺 𝑹∧𝑸
28
28
14
26/01/2024
Règle de boucle:
• Une boucle permet de décrire un traitement itératif. Ce
traitement est souvent conditionné.
• Le comportement d’une boucle while est exprimé par le triplet
Hoare suivant:
𝑃 𝑤ℎ𝑖𝑙𝑒 𝐸 𝑑𝑜 𝑇 𝑑𝑜𝑛𝑒 {𝑄}
• La boucle a un objectif bien défini à attendre. Cet objectif est
basé sur une propriété appelée invariant et notée I.
Définition (Invariant): Un invariant de boucle est un prédicat
logique, reliant les variables du programme, et vérifié avant
l'entrée dans la boucle et à chaque passage dans celle-ci.
29
29
30
30
15
26/01/2024
31
32
32
16
26/01/2024
33
Illustration: WP(a:=a+1;x:=a+1,x>0)
≡ WP(a:=a+1,WP(x:=a+1,x>0))
≡ WP(a:=a+1,a>-1)
≡ a+1>-1
≡ a > -2
34
34
17
26/01/2024
35
35
36
36
18
26/01/2024
37
37
38
19
26/01/2024
Exercice 3:
1) Donnez la règle d’inférence permettant d’établir une
instruction conditionnelle simple (sans branchement sinon).
2) Donnez la règle de calcul de plus faible précondition de
l’instruction conditionnelle simple.
3) Trouvez la précondition P tel que le triplet suivant est
valide:
{P}
if solde > 20 then
solde := solde – montant
{solde >=20}
39
39
Exercice 4:
Considérons le fragment de code suivant permettant de
calculer la somme des éléments d'un tableau d'entiers:
#define n 100
int a[n];
int somme (int x){
int s,i;
s=a[0];
for (i=1; i<n; i++){ s+=a[i];}
return s;
}
40
40
20
26/01/2024
Exercice 5:
Considérons le programme ci-dessous :
#define n 100
int a[n];
int find (int info){
int i, r;
i=0; r=-1;
while (1){
if (a[i] == info){ r=i; break; }
if (i>=n) break;
else i++;
}
return r;
}
41
Exercice 6:
Considérons le programme ci-dessous :
#define n 100
int a[n];
void swap(int i, int j){
int tmp=a[i]; a[i]=a[j]; a[j]=tmp;
}
int main(){
int i=0,j=0,p=0;
for (i=0; i<n; i++){
p=getMinimum(i);
if (i!=p) swap(i,p);
}
}
42
21
26/01/2024
43
Correction d’Exercice 2:
1) Le fragment de programme effectue la permutation les valeurs
de deux variables.
- Une spécification d’un tel fragment se fait sous forme
de pré/post condition.
- On va utiliser deux variables auxiliaires x0 et y0 mémorisant
le valeurs des variables x et y avant l’exécution.
a) Précondition: {x=x0 ∧ y=y0}
b) Post condition: { x=y0 ∧ y=x0}
44
22
26/01/2024
45
45
Correction d’Exercice 3:
1) La sémantique de l’instruction conditionnelle simple est définie
par la règle d’inférence suivante :
∧ ,¬ ∧ ⟹
I =
46
46
23
26/01/2024
Langage ACSL
47
47
Introduction (1/3)
48
48
24
26/01/2024
Introduction (2/3)
49
Introduction (3/3)
50
50
25
26/01/2024
51
52
52
26
26/01/2024
53
54
27
26/01/2024
55
Boucles (1/7)
56
56
28
26/01/2024
Boucles (2/7)
57
Boucles (3/7)
58
29
26/01/2024
Boucles (4/7)
59
59
Boucles (5/7)
60
30
26/01/2024
Boucles (6/7)
Variants de boucles:
• Comme nous l’avons indiqué auparavant, le variant un
moyen pour prouver la correction totale d’un programme
et particulièrement la terminaison d’une boucle.
• Il ne s'agit pas d'une propriété, mais plutôt une
expression faisant intervenir des éléments modifiés par
la boucle et donnant une borne supérieure sur le nombre
d’itérations restant à effectuer à un tour de la boucle.
• C’est donc une expression supérieure à 0 et strictement
décroissante d’un tour de boucle à l’autre.
61
61
Boucles (7/7)
62
62
31
26/01/2024
63
Postcondition:
• La postcondition d’une fonction est précisée avec la clause
ensures.
• Elle peut concerner la valeur de retour d’une fonction qui se
définit avec le mot-clé \result.
Illustration:
• La fonction suivante détermine la valeur absolue d’un entier
reçu en entrée.
• La postcondition est que la valeur retournée doit être
supérieur ou égal à 0.
//@ ensures \result >= 0;
int abs(int val){
if(val < 0) return -val;
return val;
}
64
64
32
26/01/2024
65
65
Précondition:
• Les préconditions sont des propriétés sur les entrées (et
potentiellement sur des variables globales) qui seront
supposées préalablement vraies lors de l’analyse de la
fonction.
• La preuve que celles-ci sont effectivement validées
n’interviendra qu’aux points où la fonction est appelée.
• Une précondition de fonction est spécifiée avec la clause
requires.
• On peut spécifier plusieurs préconditions soit en les combinant
avec les opérateurs logiques, ou en les introduire dans une
nouvelle ligne avec une clause requires.
66
66
33
26/01/2024
67
Comportements:
• Il se peut que, dans certains cas, une fonction peut avoir plusieurs
comportements.
• Ces comportements dépendent des paramètres de la fonction.
• En ACSL, les comportements d’une fonction peuvent définis selon la
syntaxe suivante:
/*@ …..
behavior B1:
assumes H1; // L’hypothèse du comportement B1;
ensures Q1; // La postcondition du comportement B1;
….
behavior BK:
assumes HK ; // L’hypothèse du comportement Bk;
ensures QK ; // La postcondition du comportement Bk;
[complete behaviors;]
[disjoint behaviors;]
*/
68
68
34
26/01/2024
69
70
70
35
26/01/2024
71
Illustration:
/*@ requires valid: \valid(p);
requires valid: \valid(q);
assigns *p,*q;
ensures exchange: *p == \old(*q) && *q == \old(*p);
*/
void swap(int* p, int* q){
int save = *p;
*p = *q;
*q = save;
}
void main(){
int a=10, b=5;
swap(&a,&b);
//@ assert a==5 && b==10;
}
72
72
36
26/01/2024
73
73
74
37
26/01/2024
75
75
76
76
38
26/01/2024
Terminaison (1/2)
77
Terminaison (2/2)
78
39
26/01/2024
Prédicats (1/7)
79
Prédicats (2/7)
80
80
40
26/01/2024
Prédicats (3/7)
81
81
Prédicats (4/7)
Prédicats inductifs:
• Dans certains cas, il est difficile de définir de manière
directe un prédicat nécessitant un raisonnement par
induction.
• Pour cela, la définition inductive doit être utilisée selon la
syntaxe suivante:
/*@ inductive P(type0 arg0,type1 arg1,...) {
case c1 : p1;
...
case ck : pk;
}
*/
Où p1,…,pk désigne des formules logiques.
82
82
41
26/01/2024
Prédicats (5/7)
83
83
Prédicats (6/7)
84
42
26/01/2024
Prédicats (7/7)
85
85
86
43
26/01/2024
87
87
88
88
44
26/01/2024
Lemmes (1/2)
89
Lemmes (2/2)
90
90
45
26/01/2024
Axiomes (1/2)
91
Axiomes (2/2)
axiom fact_0:
\forall integer i; i <= 0 ==> 1 == factorielle(i) ;
axiom fact_n:
\forall integer i; i > 0 ==>
i * factorielle(i-1) == factorielle(i) ;
}
*/
92
92
46