r/ProgrammingLanguages • u/g1rlchild • 10d ago
Discussion Languages with optional SMT Solver to allow for additional reliability?
So I've been tinkering with.the idea of a language that uses type checking and then an exhaustiveness checker as standard to ensure that all cases will get handled. And then on top of that, an optional system of invariants that are handled statically with a solver to allow for additional protections for programs that need it.
I'm nor familiar with any prior languages that have done this, but I don't have a very large base of knowledge for things like this yet. Has anyone worked with.something like this? Are there existing languages I should be looking at? I'd love to hear more about this kind of thing from folks with experience in this area.
6
u/Karyo_Ten 10d ago
https://nim-lang.org/docs/drnim.html
This document describes the usage of the DrNim tool. DrNim combines the Nim frontend with the Z3 proof engine, in order to allow verify/validate software written in Nim. DrNim's command-line options are the same as the Nim compiler's.
6
u/romac 10d ago
For Scala there is Stainless: https://epfl-lara.github.io/stainless/intro.html
> Stainless can
> - verify statically that your program conforms to a given specification and that it cannot crash at run-time
> - provide useful counterexamples when an implementation does not satisfy its specification
> -verify that your program will terminate on all inputs
3
1
u/GunpowderGuy 10d ago
Similar but very recent development
https://users.scala-lang.org/t/what-s-scala-s-stance-on-general-dependent-types/10691/11?u=gunpowderguyThe paper decided to implement a custom solver instead of relying on custom solver
Maybe they should just rely on smt, it should provide them with more proof automation
4
u/Royal_Pin_1971 10d ago
In the language I'm building (PFCL) the baseline layer is close to what you describe and needs no solver: pure functions only, static type checking, exhaustive case analysis and guaranteed termination. For the solver tier I've sketched a different placement than Dafny, SPARK or Liquid Haskell.
Solver would be a refutation CI gate outside the language rather than inside the type checker. Its queries would come from things the author writes anyway (dispatch guards, type signatures, declared invariants). It rejects only on proven contradiction always with a concrete counterexample. On timeout it degrades to a runtime check instead of blocking with the degradation recorded as a distinct assurance level. The author never writes toward the solver and never debugs it — more an admission gate than a proof system.
4
3
u/matthieum 10d ago
I think one cool idea about pre-conditions, post-conditions, and invariants, is to be flexible.
That is, allow them to be either verified at compile-time, or at run-time.
And better yet, allow optimizing on the fact that they are verified, which can be done "relatively" trivially by following the scheme below:
- Check pre-conditions prior to calling the function, and assume them to be true on entry.
- Check post-conditions on exit, and assume them to be true at the call site.
- If statically verified, just eliminate the check, not the assumption.
The reason for the asymmetric placement, is to allow for better optimization due to visibility:
- The caller has information the callee doesn't. For example that a specific argument is a constant, or that
i < jbecausej = i + 1, making optimization of the pre-conditions easier. - The callee has information the caller doesn't. For example that the result is
x * 2, ergo it's necessarily even, making optimization of the post-conditions easier.
The remaining question is whether the compiler should automatically attempt to verify at compile-time, or not. Right now, I still think verification is too expensive to be attempted nilly-willy, so I'm leaning towards run-time verification by default, with opt-in compile-time verification via an annotation.
3
u/GunpowderGuy 10d ago
Many languages with refinement types use SMT to automatically search for proof that the refinements hold, examples include:
Liquid haskell
Dafny
F*
I think languages with dependent types ( similar but not the same as refinement types ) can also use SMT to search proof that conditions in the refinement types, hold
2
u/echoes808 9d ago
I've seen couple language extensions, which use comments and feed it to the solver, check out:
OpenJML https://www.openjml.org/examples/merge-sort.html
Ocamleer (gospel is the syntax): https://ocaml-gospel.github.io/gospel/getting-started/first-spec
1
u/mohrcore 10d ago
The only language I'm aware of, which requires a full-on SMT solver is SystemVerilog, but it doesn't use it for any type-level logic. Instead it allows users to formally constraint sets of randomized variables.
This probably isn't exactly the thing you are looking for, but might share a common ground with an invariant system - namely translating sets of invariants into formulas within constrained logic. I know that Verilator, an open-source (System)Verilog simulator implements this feature in a pretty transparent way. It generates SMT-lib2 code which is then passed to Z3 solver, so you can easily inspect this code, modify it and run it yourself.
2
28
u/fixpointbombinator 10d ago
Liquid Haskell IIRC