As someone who was working on this decades ago [1][2], here's a useful recap.
- Back then, CPU power for the prover was a big problem. That's been fixed.
We were too early in the days of a 1 MIPS VAX.
- Prover theory is much better. We were using the Oppen-Nelson prover, the original SAT system. Now that's routine technology.
- Loose languages are a problem. You have to nail down all "undefined behavior", and
either detect it and forbid it, or give it precise semantics.
- Integrate the verification annotation into the programming language.
Not as comments. It should be at least syntax and type checked when the
program is compiled, and it should use essentially the same syntax as the language to which it is attached. Otherwise the annotations get out of date and nobody uses them.
- Break big assertions into lots of little assertions. Don't AND them. This improves
the diagnostics.
- Debug the verification in the source language, by adding asserts. The
verification system usually treats asserts as a proof goal, and assumes they are true for
the code that follows. You narrow down the problem into a small area in that way.
If you need to prove something hard, get to the point where you have "assert(a); assert(b);", and need to prove that A implies B. Then you use an offline prover.
Don't work on the code problems in a prover directly.
- You're not done until you get 100%. If you write "assert(false);", there is only one
error but the verification is totally meaningless.
- Don't get carried away with the formalism. Verification systems tend to be built
by people who think formalism is cool. That is a negative for getting work done.
- Undecidability and the halting problem are not issues. If your program is anywhere
near undecidable, it's broken. Microsoft took the position with their Static Driver Verifier that if the verifier can't decide termination easily, it doesn't get to be a signed kernel driver.
- Some things are hard to specify, and some things aren't. A database is an example of
a complicated system that's not too hard to specify. The specification is a full table
search of giant arrays. The implementation doesn't do it that way, but it's supposed to
behave as if it does. The other extreme would be a GUI program.
[1] http://www.animats.com/papers/verifier/verifiermanual.pdf
[2] https://github.com/John-Nagle/pasv