Abstract Functions (abstract func)
Abstract functions are contract-only declarations for verification-driven development. They have requires and ensures clauses, but no body yet, so their postconditions are assumptions until the declaration is refined.
Use abstract when you want to specify a proof helper or interface first, verify callers against that interface, and fill in the implementation later. For the broader signature-only workflow, see Signature-Only Declarations. Use trusted instead when the unchecked boundary is permanent.
Basic Syntax
An abstract function has a signature and contracts, then stops after the final contract semicolon:
public pure abstract func specValue(x : Int) : Int
requires true;
ensures result == x + 1;
There are no braces and no implementation.
For proof-only helpers, prefer ghost abstract func:
public ghost abstract func distance(a : Int, b : Int) : Nat
requires true;
ensures result >= 0;
abstract ghost func is also accepted:
public abstract ghost func chooseGreater(x : Int) : Int
requires true;
ensures result > x;
Example
// Abstract functions are contract-only verifier placeholders.
module {
public pure abstract func oneMore(x : Int) : Int
requires true;
ensures result == x + 1;
public ghost abstract func chooseAtLeast(x : Int) : Int
requires true;
ensures result >= x;
public abstract ghost func chooseGreater(x : Int) : Int
requires true;
ensures result > x;
public lemma abstractPostconditionsAreAvailable(x : Int) : ()
requires true;
ensures oneMore(x) == x + 1;
ensures chooseAtLeast(x) >= x;
ensures chooseGreater(x) > x;
{};
}
The lemma can use the postconditions of all three abstract functions. No body is checked because no body exists yet.
Verification Semantics
Abstract functions are not magic trusted calls. The verifier still checks every call against the function's preconditions:
// Abstract signature-only proof helpers still require preconditions at call sites
module {
// Abstract signature-only proof helper with precondition
public ghost abstract func safeSqrt(x : Nat) : Nat
requires x <= 1000000;
ensures result * result <= x;
// This lemma violates the precondition in proof code - verification fails
public lemma bad(n : Nat) : () {
let _witness = safeSqrt(n); // Error: cannot prove n <= 1000000
};
}
After a valid call, the verifier may use the abstract function's postconditions. While the declaration remains abstract, those postconditions are assumptions. If the contract is wrong, callers can verify against a false interface until you refine the declaration.
Verification-Driven Development
Abstract functions support a specify-first workflow:
// Verification-Driven Development for proof-only helpers:
// start with specs, add implementations later.
module {
// Stage 1: Abstract ghost helper stubs with contracts only
public ghost abstract func lineTotal(qty : Nat, unit : Nat) : Nat
requires true;
ensures result == qty * unit;
public ghost abstract func discount(subtotal : Nat, rate : Nat) : Nat
requires rate <= 100;
ensures result <= subtotal;
public ghost abstract func shipping(weight : Nat) : Nat
requires true;
ensures result == weight * 2;
// Stage 2: Implement a proof-only helper using stubs
public ghost func invoice(qty : Nat, unit : Nat, rate : Nat, weight : Nat) : Nat
requires rate <= 100;
ensures result == lineTotal(qty, unit) + shipping(weight);
{
let sub = lineTotal(qty, unit);
let ship = shipping(weight);
sub + ship
};
}
The workflow is:
- Write abstract function contracts.
- Verify proof-only callers against those contracts.
- Replace each abstract declaration with an implemented function when the body is ready.
When you refine an abstract function, remove abstract and add the body:
public ghost func distance(a : Int, b : Int) : Nat
requires true;
ensures result >= 0;
{
if (a >= b) { a - b } else { b - a }
};
Abstract vs Trusted
abstract and trusted mean different things:
| Form | Body | Meaning |
|---|---|---|
abstract func | No | VDD placeholder or proof interface to refine later |
trusted func | Optional | Explicit unchecked trusted boundary |
| Implemented non-trusted function | Yes | Body is verified against its contract |
Use abstract for temporary proof interfaces and specification scaffolding. Use trusted only when you intentionally want an unchecked implementation or permanent external assumption.
The compiler rejects abstract trusted because it mixes those two meanings.
Allowed Forms
| Form | Typical Use |
|---|---|
pure abstract func | Specification function usable from contracts |
ghost abstract func | Proof-only helper returning a value |
abstract ghost func | Same as ghost abstract func |
abstract lemma | Bodyless proof obligation |
Most abstract helpers should be ghost abstract func unless they must be usable as pure specification functions from non-ghost contracts. Use abstract lemma when the placeholder is a proof step rather than a value-returning helper.
Restrictions
Abstract functions:
- Must not have a body.
- Must not be
trusted. - Must not be
inline. - Must not use
entry_requires. - Must be refined before compile/run if they appear in deployable code.
- Still create call-site proof obligations from every
requiresclause.
entry_requires is executable runtime-entry checking. An abstract function has no runtime body, so use logical requires clauses instead.
Runtime Restriction
Abstract declarations are verification-only placeholders. They are useful while proving and refactoring, but they are not deployable implementations.
Before compiling or running runtime code, replace abstract declarations with implemented functions. If the implementation itself must remain unchecked, write a trusted function with a body and document why it belongs in the trusted base.
Abstract declarations are allowed in verification-oriented modules and ghost proof surfaces. They are not a way to ship missing runtime behavior.
Summary
abstract funcdeclares a contract-only placeholder with no body.- Callers still have to prove
requires. - Callers may use
ensuresafter a valid call. - Abstract postconditions are assumptions until the declaration is refined.
- Use
ghost abstract funcfor proof-only helpers. - Use
abstract lemmafor bodyless proof obligations. - Use
trusted, notabstract, only for intentional trusted boundaries.
Related Topics
- Trusted base for the soundness cost of assumptions
- Signature-only declarations for
abstractand bodylesstrustedforms - Pure function restrictions for helpers used directly in contracts
- Contract placement when refining abstract declarations with bodies