Gérard Berry

Algorithmes, machines et langages

Année 2014-2015

Prouver les programmes : pourquoi, quand, comment ?

Troisième leçon : Les méthodes générales : assertions, réécriture, interprétation abstraite, logiques et assistants de preuve

Il y a deux principaux types de méthodes formelles pour la preuve de programme : les méthodes générales, qui s'adressent à tous les types de programmes et seront présentées dans ce cours, et celles de la vérification de modèles (model-checking), qui s'intéressent principalement aux programmes à espaces d'états finis et aux circuits électroniques et seront traitées dans le cours no 5 du 25 mars 2015.

La nécessité de traiter les programmes comme des objets mathématiques à part entière a été reconnue par A. Turing dès 1949. Il a alors introduit la notion d'assertion associant un prédicat logique à un point de contrôle du programme, ainsi que l'importance de la notion d'ordre bien fondé pour montrer la terminaison des programmes. Son approche par assertions est la première que nous traiterons dans le cours. Elle a été étendue et perfectionnée par R. Floyd, C.A.R. Hoare, E.W. Dijkstra, et bien d'autres, pour aboutir à une théorie et une pratique complètes de la définition logique des langages de programmation et de la vérification de programmes, applicables initialement aux programmes séquentiels impératifs. La notion d'assertion a été ensuite étendue en celle de contrat (assume/guarantee) à respecter par les fonctions ou modules d'un programme, applicable aussi aux langages objets et aux langages parallèles.

En 1963, dans un article fondateur militant pour le traitement mathématique de la programmation et introduisant une brochette remarquable de nouveaux concepts, J. McCarthy a introduit l'idée bien différente d'utiliser la réécriture de termes comme outil applicable à la fois pour l'optimisation de programmes et leur vérification formelle. Cette approche, la seconde que nous étudierons, a eu une longue descendance dans les systèmes de vérification (Boyer&More, ACL2, PVS, Key, ProVerif, etc.), et continue d'irriguer les autres approches. Elle a eu des succès remarquables en circuits, en avionique, en sécurité, etc.

La troisième approche historique est celle de la sémantique dénotationnelle des langages introduite par Scott et Strachey vers 1970. Ici, un programme est interprété comme une fonction dans un espace topologique ordonné par un ordre d'information, et toutes les fonctions sont rendues totales par l'ajout explicite d'éléments indéfinis. Une théorie générale du point fixe dans les espaces ordonnés permet d'interpréter de façon uniforme les boucles, la récursion et la programmation fonctionnelle d'ordre supérieur. Au début des années 1970, cette théorie a été à l'origine du pionnier des assistants de preuve de programmes, LCF, créé par Milner et al. Mais le traitement explicite de l'indéfini s'est révélé trop compliqué, et les assistants de preuve ont ensuite suivi un autre chemin. La sémantique dénotationnelle a cependant eu un succès considérable dans une autre approche de la vérification, l'interprétation abstraite créé par P. et R. Cousot en 1977. L'idée est ici de travailler avec des assertions portant non plus sur les valeurs exactes calculées, mais sur une abstraction de celles-ci, comme la preuve par 9 le fait pour détecter des erreurs dans la multiplication. De nombreux domaines abstraits ont été développés, ainsi que des méthodes algorithmiques générales de combinaison de ces domaines et d'accélération des calculs de points fixes. L'interprétation abstraite est maintenant développée industriellement. Elle a permis de vérifier des propriétés critiques de très gros programmes, comme l'absence d'erreurs à l'exécution dans le code de pilotage de l'Airbus A380. Elle est également utilisée pour l'évaluation du temps de calcul maximal de logiciels embarqués et pour accélérer les calculs dans d'autres types de système de vérification.

La quatrième approche traitée dans ce cours est la vérification par assistants de preuves logiques. L'idée est ici de traduire le problème de vérification informatique en un problème purement logique, et de fournir une aide à la vérification à travers un système de tactiques et d'interaction homme-machine permettant d'organiser les preuves logiques à grand échelle, augmenté d'automatisations partielles pour des sous-domaines spécifiques utilisant par exemple des techniques de réécriture. Les assistants actuels traitent plusieurs types de logique, allant du calcul des prédicats de premier ordre augmenté par la théorie des ensembles (Rodin pour Event B, etc.) ou par la logique temporelle (TLA+), jusqu'aux calculs d'ordre supérieur (HOL, Isabelle, Coq, etc.). Ce cours présentera sommairement les ateliers de premier ordre, les ordres supérieurs étant traités dans le cours suivant. Nous prendrons l'exemple des formalismes B et Event-B de J.-R. Abrial (orateur du dernier séminaire du 1er avril 2015). L'atelier B a été utilisé pour la spécification, la programmation et la vérification formelle de logiciels critiques pour la conduite du RER A, de la ligne 14 (Meteor) du métro parisien, et de plusieurs autres systèmes ferroviaires. Event-B et son système Rodin sont une évolution de B vers les systèmes événementiels qui sont ubiquitaires dans l'informatique embarquée.