Sistemas de tipo estático são um tipo de análise estática, mas existem muitas análises estáticas que geralmente não são codificadas em sistemas de tipo. Por exemplo:
A verificação de modelo é uma técnica de análise e verificação para sistemas concorrentes que permite provar que seu programa é bem-comportado em todas as intercalações de encadeamentos possíveis.
A análise do fluxo de dados reúne informações sobre os possíveis valores das variáveis, que podem determinar se algum cálculo é redundante ou se algum erro não é contabilizado.
A interpretação abstrata modela de maneira conservadora os efeitos de um programa, geralmente de maneira que a análise termine - os verificadores do tipo podem ser implementados de maneira semelhante aos intérpretes abstratos.
A lógica de separação é uma lógica de programa (usada, por exemplo, no analisador Infer ), que pode ser usada para raciocinar sobre os estados do programa e identificar problemas como desreferências de ponteiros nulos, estados inválidos e vazamentos de recursos.
A programação baseada em contrato é um meio de especificar pré-condições, pós-condições, efeitos colaterais e invariantes. Ada tem suporte nativo para contratos e pode verificar alguns deles estaticamente.
Os compiladores de otimização fazem muitas análises pequenas para criar estruturas de dados intermediárias para uso durante a otimização - como SSA, estimativas de custos inline, informações de emparelhamento de instruções e assim por diante.
Outro exemplo de análise estática não declarativa é encontrado no typechecker Hack , em que construções normais de fluxo de controle podem refinar o tipo de uma variável:
$x = get_value();
if ($x !== null) {
$x->method(); // Typechecks because $x is known to be non-null.
} else {
$x->method(); // Does not typecheck.
}
E por falar em “refinar”, de volta à terra dos sistemas de tipos , os tipos de refinamento (como usados no LiquidHaskell ) emparelham tipos com predicados que são garantidos para as instâncias do tipo “refinado”. E os tipos dependentes levam isso adiante, permitindo que os tipos dependam dos valores. O "olá mundo" da digitação dependente geralmente é a função de concatenação da matriz:
(++) : (a : Type) -> (m n : Nat) -> Vec a m -> Vec a n -> Vec a (m + n)
Aqui, ++
toma dois operandos do tipo Vec a m
e Vec a n
, sendo vetores com tipo a
e comprimento de elemento m
e n
, respectivamente, que são números naturais ( Nat
). Retorna um vetor com o mesmo tipo de elemento cujo comprimento é m + n
. E essa função comprova essa restrição abstratamente, sem conhecer os valores específicos de m
e n
, portanto, os comprimentos dos vetores podem ser dinâmicos.