Diseño por Contratos Formal y Verificación
Lógica de primer orden, tripletas de Hoare, invariantes de TADs y cálculo de wp en C
3. Contratos de Estructuras de Datos e Invariantes de Clase¶
Introducción¶
Desarrollo¶
Fundamentos Matemáticos del Diseño por Contratos¶
Para sistemas de software de alta integridad o complejidad (como al implementar las estructuras dinámicas internas de un Tipo de Dato Abstracto), las especificaciones en lenguaje natural o aserciones en tiempo de ejecución resultan insuficientes. Se requiere un marco formal sustentado en la lógica matemática para probar de forma rigurosa la correctitud y coherencia de las rutinas de software.
1. Fundamentos de Lógica de Primer Orden¶
La Lógica de Primer Orden (First-Order Logic, FOL) extiende la lógica proposicional clásica introduciendo cuantificadores sobre elementos individuales del dominio de discurso.
Sintaxis y Alfabeto de la LPO¶
Un lenguaje en Lógica de Primer Orden consta de:
Variables: Letras que representan elementos genéricos (ej: ).
Conectores lógicos: Negación (), conjunción (), disyunción (), implicación (), equivalencia ().
Cuantificadores: Universal (, “para todo”) y Existencial (, “existe al menos uno”).
Constantes y Funciones: Elementos fijos (ej: ) o mapeos funcionales (ej: ).
Predicados: Relaciones lógicas que evalúan a verdadero o falso (ej: o la comparación física de orden ).
Variables Libres y Ligadas¶
Una ocurrencia de una variable en una fórmula está ligada si se encuentra dentro del alcance directo de un cuantificador ( o ). De lo contrario, se considera una variable libre. Una fórmula que carece de variables libres se denomina sentencia.
La operación de sustitución representa el reemplazo de todas las ocurrencias libres de la variable en la fórmula por el término , previniendo la colisión de identificadores.
2. Lógica de Hoare y Cálculo de Precondición Más Débil ()¶
La Lógica de Hoare provee un sistema axiomático formal para razonar sobre la corrección de algoritmos imperativos mediante el uso de Tripletas de Hoare:
Donde:
representa la precondición (aserción sobre el estado antes del programa).
representa la secuencia física de instrucciones del programa.
representa la postcondición (aserción que se garantiza verdadera tras la ejecución exitosa de ).
Regla de la Asignación¶
La regla de asignación calcula analíticamente la precondición mínima requerida para asegurar una postcondición tras asignar una expresión a la variable :
Precondición Más Débil (Weakest Precondition - )¶
La precondición más débil describe la condición más general (menos restrictiva) sobre el estado inicial del sistema que garantiza que la ejecución de finalice en un estado que satisfaga la postcondición .
3. Contratos de Estructuras de Datos e Invariantes de Clase¶
En el diseño de Tipos Datos Abstractos (TADs), un invariante de clase o de estructura es una propiedad lógica fundamental que describe la validez interna de la representación física de los datos. Debe ser verdadera tras la construcción del objeto y preservarse antes y después de cada llamada a métodos o funciones públicas del TAD.
1 2 3 4 5 6 7 8typedef struct { int *elementos; int tope; int capacidad; } pila_t; // Invariante de estructura Pila: // elementos != NULL ∧ capacidad > 0 ∧ 0 <= tope <= capacidad
ACSL (ANSI/ISO C Specification Language)¶
ACSL permite escribir anotaciones formales directamente en los comentarios del código de C para su verificación estática mediante analizadores como Frama-C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16/*@ predicate pila_valida(pila_t *p) = \valid(p) && p->elementos != NULL && p->capacidad > 0 && 0 <= p->tope <= p->capacidad && \valid(p->elementos + (0..p->capacidad-1)); */ /*@ requires pila_valida(p); requires p->tope < p->capacidad; assigns p->tope, p->elementos[p->tope]; ensures pila_valida(p); ensures p->tope == \old(p->tope) + 1; ensures p->elementos[p->tope - 1] == dato; */ void push(pila_t *p, int dato);
El Frame Problem y la directiva assigns¶
El Frame Problem consiste en la dificultad de especificar formalmente qué partes del estado del sistema no cambian durante la ejecución de una función. Sin una solución, las especificaciones deberían listar exhaustivamente cada variable del programa que permanece igual.
En ACSL, esto se resuelve mediante la directiva assigns, la cual
especifica con exactitud las únicas variables o posiciones de memoria que la
función tiene permitido modificar. El analizador formal asume de manera
automática que todo lo que no figure en dicha cláusula permanece inalterado.
Ejercicios de Autoevaluación¶
Solution to Exercise 1
La fórmula en LPO utilizando cuantificación universal es:
Solution to Exercise 2
Ligadas: Las variables y son variables ligadas debido a que sus ocurrencias se encuentran bajo el alcance directo de los cuantificadores universal y existencial respectivamente.
Libres: La variable es libre porque no se encuentra bajo el alcance de ningún cuantificador lógico en la fórmula.
Solution to Exercise 3
La sustitución reemplaza la ocurrencia libre de por el término sin alterar la estructura ligada de :
Solution to Exercise 4
Aplicamos la regla de asignación de Hoare de atrás hacia adelante:
Para la última instrucción con postcondición :
Para la instrucción anterior sobre la condición anterior:
Para la primera instrucción :
La precondición obtenida de forma analítica es exactamente , lo cual demuestra la corrección de la terna.
Solution to Exercise 5
Utilizando la regla de la precondición más débil para la asignación:
Solution to Exercise 6
La Regla de Consecuencia permite adaptar tripletas de Hoare preexistentes a contextos más estrictos o específicos.
Debilitar la precondición (): Reemplazar la precondición por una condición que es más restrictiva. Si el programa funciona asumiendo una precondición débil, seguirá siendo correcto con una más fuerte.
Fortalecer la postcondición (): Reemplazar la postcondición por una condición que sea menos restrictiva, garantizando más de lo inicialmente especificado.
Solution to Exercise 7
El Frame Problem es la dificultad de expresar formalmente qué variables y recursos del sistema no se modifican tras la ejecución de una rutina sin tener que listar explícitamente el infinito número de variables inalteradas.
La directiva assigns soluciona esto en ACSL permitiendo declarar un marco
delimitado de variables de escritura. El analizador estático (como Frama-C)
asume automáticamente que cualquier variable o celda del heap que no esté
listada de forma explícita en la directiva assigns permanece inalterada, lo
que simplifica drásticamente el cálculo de las condiciones de verificación.
Solution to Exercise 8
/*@ requires \valid(ptr);
assigns *ptr;
ensures *ptr == 0;
*/
void resetear(int *ptr);Solution to Exercise 9
En un Rectangulo mutable, la operación set_ancho(w) tiene la postcondición
de que el ancho se actualiza a w y el alto permanece inalterado.
Si Cuadrado es un subtipo, para mantener su invariante de estructura (ancho == alto), la operación set_ancho(w) debe forzosamente alterar el alto
también. Esto viola la postcondición heredada de Rectangulo (que garantiza que
el alto no se modifica), rompiendo el Principio de Sustitución de Liskov ya que
un código cliente diseñado para Rectangulo fallaría si se le pasa un objeto de
tipo Cuadrado.
Glosario¶
- Precondición
- Condición que debe cumplirse antes de invocar una función.
- Postcondición
- Garantía que ofrece una función al finalizar si se cumplieron sus precondiciones.
- Invariante
- Propiedad que debe permanecer verdadera durante el ciclo de vida de un objeto o ejecución.
- Invariante de lazo (Loop Invariant)
- Condición o propiedad lógica asociada a una estructura iterativa que permanece verdadera antes de ingresar al lazo, antes y después de cada vuelta, y al salir de este.
Síntesis y Resumen¶
En este apunte se han presentado los conceptos fundamentales del tema.
Referencias y Lecturas Complementarias¶
Meyer, B. Meyer, 1988. Design by Contract. Prentice Hall.
El ensayo original que define e introduce la metodología de Diseño por Contrato en la construcción de software robusto.
Meyer, B. Meyer, 1992. Applying “Design by Contract”. IEEE Computer.
Artículo clásico que analiza de forma compacta y pragmática el uso de precondiciones, postcondiciones e invariantes para el control de errores en tiempo de ejecución.
Meyer, B. Meyer, 1997. Object-Oriented Software Construction (2.ª edición). Prentice Hall.
Consultá los capítulos dedicados a la teoría de contratos, herencia y correctitud de algoritmos, que sientan las bases formales del modelado de invariantes de representación.
- Meyer, B. (1988). Design by Contract. Advances in Object-Oriented Software Engineering.
- Meyer, B. (1992). Applying “Design by Contract.” Computer, 25(10), 40–51. 10.1109/2.161279
- Meyer, B. (1997). Object-Oriented Software Construction (2nd ed.). Prentice Hall.