| Yes, SPARK provides an explicit escape hatch, but it is meaningfully different from Rust's `unsafe`. In SPARK: you keep the spec in the verifiable world (a SPARK-visible subprogram declaration) and place the implementation outside the proof boundary (for example in a body or unit compiled with `pragma SPARK_Mode(Off)`). The body is visible in source but intentionally opaque to the prover; callers are still proved against the spec and must rely on that contract. In Rust: `unsafe` is a lexical, language-level scope or function attribute that disables certain compiler/borrow checks for the code inside the block or function. The unchecked code remains inline and visible; the language/borrow-checker no longer enforces some invariants there, so responsibility shifts to the programmer at the lexical site. Practical differences reviewers should understand: - Granularity: `unsafe` is lexical (blocks/functions); SPARK's hatch is modular/procedural (spec vs body). - Visibility: Rust's unchecked code is written inline and annotated with `unsafe`; SPARK's unchecked implementation is placed outside the prover but the spec remains visible and provable. - Enforcement model: Rust's safety holes are enforced/annotated by the compiler's type/borrow rules (caller and callee responsibilities are explicit at the site). SPARK enforces sound proofs on the interface; the off-proof body must be shown (by review/tests/docs) to meet the proven contract. - Best practice: keep off-proof bodies tiny, give strong pre/postconditions on the SPARK spec, minimize the exposed surface, and rigorously review and test the non-SPARK implementation. TL;DR: `unsafe` = "this code block bypasses language checks"; SPARK's escape hatch = "this implementation is deliberately outside the prover, but the interface is still what we formally prove against." |
> In [Rust]: you keep the spec in the verifiable world (a [type checker-visible function signature]) and place the implementation outside the proof boundary (for example in a body or unit compiled with `unsafe`). The body is visible in source but intentionally opaque to the [type checker; callers are still proved against the spec and must rely on that contract.
These "practical differences" seem questionable to me as well.
> Granularity: `unsafe` is lexical (blocks/functions); SPARK's hatch is modular/procedural (spec vs body).
`unsafe` can trivially be expanded from a block to a function body, thereby transforming it to a "modular/procedural" hatch. `unsafe` on function signatures is more akin to SPARK's `Assume`, albeit with the precise assumption (hopefully) in the documentation.
> Visibility: Rust's unchecked code is written inline and annotated with `unsafe`
This... doesn't seem like a difference to me? According to you, you can place SPARK's unchecked code "in a body [] compiled with `pragma SPARK_Mode(Off)`". That sure sounds like writing unchecked code "inline" and annotated with SPARK's equivalent of `unsafe` to me.
> SPARK's unchecked implementation is placed outside the prover but the spec remains visible and provable.
You can say an analogous thing about Rust: The unchecked implementation is placed outside the [type checker] but the [function signature] remains visible and [checkable].
> Enforcement model: Rust's safety holes are enforced/annotated by the compiler's type/borrow rules (caller and callee responsibilities are explicit at the site).
This is... confusing? I'm honestly not sure what you're trying to get at.
> SPARK enforces sound proofs on the interface; the off-proof body must be shown (by review/tests/docs) to meet the proven contract.
The Rust equivalent is a non-`unsafe` function signature with an `unsafe` body, and what you say is equally as applicable.
> Best practice: keep off-proof bodies tiny, give strong pre/postconditions on the SPARK spec, minimize the exposed surface, and rigorously review and test the non-SPARK implementation.
This... doesn't describe a difference at all? It's equally as applicable to the analogous Rust construct.
> `unsafe` = "this code block bypasses language checks"
Technically wrong? Bit of a nitpick, but `unsafe` technically does not disable any language checks; it gives you access to unchecked capabilities. It's a slight difference but an important one, since it means that if you have code that doesn't compile and doesn't use unchecked capabilities (e.g., a borrow checking error with regular references), wrapping the code in `unsafe` will not allow your code to compile.
> SPARK's escape hatch = "this implementation is deliberately outside the prover, but the interface is still what we formally prove against."
Again, you can say basically the same thing for non-`unsafe` functions in Rust.