No final das contas, estamos falando de tempo de compilação versus tempo de execução.
Erros de tempo de compilação, se você pensar sobre isso, acabam resultando no compilador capaz de determinar quais problemas você tem no seu programa antes mesmo de ser executado. Obviamente, não é um compilador de "linguagem arbitrária", mas voltarei a isso em breve. O compilador, em toda a sua infinita sabedoria, não lista todos os problemas que podem ser determinados pelo compilador. Isso depende parcialmente de quão bem o compilador foi gravado, mas a principal razão para isso é que muitas coisas são determinadas em tempo de execução .
Erros de tempo de execução, como você está bem familiarizado, tenho certeza que sou, são qualquer tipo de erro que ocorre durante a execução do próprio programa. Isso inclui dividir por zero, exceções de ponteiro nulo, problemas de hardware e muitos outros fatores.
A natureza dos erros de tempo de execução significa que você não pode antecipar os erros em tempo de compilação. Se você pudesse, eles quase certamente seriam verificados em tempo de compilação. Se você puder garantir que um número seja zero no tempo de compilação, poderá executar certas conclusões lógicas, como dividir qualquer número por esse número resultará em um erro aritmético causado pela divisão por zero.
Como tal, de uma maneira muito real, o inimigo de garantir programaticamente o funcionamento adequado de um programa está executando verificações de tempo de execução em vez de compilar verificações de tempo. Um exemplo disso pode estar executando uma conversão dinâmica para outro tipo. Se isso for permitido, você, o programador, está substituindo essencialmente a capacidade do compilador de saber se isso é uma coisa segura a se fazer. Algumas linguagens de programação decidiram que isso é aceitável, enquanto outras pelo menos o alertam em tempo de compilação.
Outro bom exemplo pode ser permitir que os nulos façam parte do idioma, pois podem ocorrer exceções de ponteiro nulo se você permitir nulos. Alguns idiomas eliminaram esse problema completamente, impedindo que variáveis não declaradas explicitamente consigam manter valores nulos a serem declarados sem a atribuição imediata de um valor (como o Kotlin, por exemplo). Embora não seja possível eliminar um erro de tempo de execução da exceção do ponteiro nulo, é possível impedir que isso aconteça removendo a natureza dinâmica do idioma. No Kotlin, você pode forçar a possibilidade de manter valores nulos, é claro, mas nem é preciso dizer que esse é um metafórico "compradores devem tomar cuidado", pois você deve explicitamente explicá-lo como tal.
Você poderia conceitualmente ter um compilador que pudesse verificar erros em todos os idiomas? Sim, mas provavelmente seria um compilador desajeitado e altamente instável, no qual seria necessário fornecer necessariamente o idioma que está sendo compilado anteriormente. Ele também não sabia muitas coisas sobre o seu programa, assim como os compiladores para idiomas específicos sabem algumas coisas sobre ele, como o problema de interrupção, como você mencionou. Como se vê, é impossível obter muitas informações que podem ser interessantes para aprender sobre um programa. Isso foi comprovado, portanto, não é provável que mude tão cedo.
Voltando ao seu ponto principal. Os métodos não são automaticamente seguros para threads. Existe uma razão prática para isso: métodos de segurança de encadeamento também são mais lentos, mesmo quando não estão sendo usados. O Rust decide que eles podem eliminar problemas de tempo de execução, tornando os métodos seguros por padrão, e essa é a escolha deles. Mas tem um custo.
Pode ser possível provar matematicamente a correção de um programa, mas seria com a ressalva que você teria literalmente zero recursos de tempo de execução no idioma. Você seria capaz de ler este idioma e saber o que ele faz sem surpresas. A linguagem provavelmente pareceria muito matemática por natureza, e isso provavelmente não é coincidência. A segunda ressalva é que erros de tempo de execução ainda acontecem, o que pode não ter nada a ver com o próprio programa. Portanto, o programa pode ser provada correta, assumindo uma série de pressupostos sobre o computador que está sendo executado em são precisas e não fazer a mudança, o que, claro, sempre que acontecer de qualquer maneira e muitas vezes.