Los sistemas de tipo estático son un tipo de análisis estático, pero hay muchos análisis estáticos que generalmente no están codificados en sistemas de tipo. Por ejemplo:
La verificación de modelos es una técnica de análisis y verificación para sistemas concurrentes que le permite probar que su programa se comporta bien bajo todos los entrelazados de hilos posibles.
El análisis del flujo de datos recopila información sobre los posibles valores de las variables, que pueden determinar si algunos cálculos son redundantes o si no se tiene en cuenta algún error.
La interpretación abstracta modela de manera conservadora los efectos de un programa, generalmente de tal manera que se garantiza que el análisis termine; los verificadores de tipo pueden implementarse de manera similar a los intérpretes abstractos.
La lógica de separación es una lógica de programa (utilizada, por ejemplo, en el analizador Infer ) que se puede utilizar para razonar sobre los estados del programa e identificar problemas tales como desreferencia de puntero nulo, estados no válidos y pérdidas de recursos.
La programación basada en contratos es un medio para especificar condiciones previas, condiciones posteriores, efectos secundarios e invariantes. Ada tiene soporte nativo para contratos y puede verificar algunos de ellos estáticamente.
Los compiladores de optimización realizan muchos análisis pequeños para construir estructuras de datos intermedios para su uso durante la optimización, como SSA, estimaciones de costos de alineación, información de emparejamiento de instrucciones, etc.
Otro ejemplo de análisis estático no declarativo se encuentra en Hack typechecker, donde las construcciones normales de flujo de control pueden refinar el tipo de una variable:
$x = get_value();
if ($x !== null) {
$x->method(); // Typechecks because $x is known to be non-null.
} else {
$x->method(); // Does not typecheck.
}
Y hablando de "refinamiento", en la tierra de los sistemas de tipos , los tipos de refinamiento (como se usan en LiquidHaskell ) emparejan los tipos con predicados que se garantizan para instancias del tipo "refinado". Y los tipos dependientes van más allá, permitiendo que los tipos dependan de los valores. El "hola mundo" de la tipificación dependiente suele ser la función de concatenación de matriz:
(++) : (a : Type) -> (m n : Nat) -> Vec a m -> Vec a n -> Vec a (m + n)
Aquí, ++
toma dos operandos de tipo Vec a m
y Vec a n
, siendo vectores con tipo de elemento a
y longitudes m
y n
, respectivamente, que son números naturales ( Nat
). Devuelve un vector con el mismo tipo de elemento cuya longitud es m + n
. Y esta función prueba esta restricción de manera abstracta, sin conocer los valores específicos de m
y n
, por lo que las longitudes de los vectores pueden ser dinámicas.