Standard library / std::formal

std::formal

This module provides the small, stable Formal Silk vocabulary that is broadly reusable across std:: and downstream verified code.

std::formal is intentionally about base facts, not every theory the stdlib happens to use internally. The rule is:

  • keep generic arithmetic, range, pointer, slice, buffer, and vector theories here,
  • move module-owned theories to the module that owns that runtime surface,
  • and keep purely local implementation-detail theories private when they are not useful as downstream vocabulary.

This keeps verification concepts discoverable in the same place readers find the API they describe.

See formal verification for the Formal Silk execution model and theory semantics.

When std::formal is built and packaged as distributable Silk surface, its exported theories participate in the same Formal Silk export-bundle mechanism as any other package: the source declarations remain authoritative for import and verification, while the generated manifest.json / bundle.smt2 payload provides machine-readable solver material for tooling and package distribution.

Importing and using foundational theories#

import { bounds_i64, vector_well_formed } from "std/formal";

struct I64VecView {
  ptr: u64,
  len: i64,
  cap: i64,
}

impl I64VecView {
  #theory vector_well_formed(self.ptr, self.len, self.cap);
  fn len_ok (self: &I64VecView) -> bool {
    return self.len <= self.cap;
  }

  #theory vector_well_formed(self.ptr, self.len, self.cap);
  #theory bounds_i64(index, self.len);
  fn get_raw (self: &I64VecView, index: i64) -> i64 {
    return std::runtime::mem::load_u64(self.ptr, index * 8) as raw i64;
  }
}

What lives outside std::formal#

Module-owned theories now live with their owning modules:

Theory families#

Scalar and arithmetic#

  • nonnegative_i64(x: i64)
  • requires x >= 0
  • use for lengths, counts, capacities, and offsets
  • positive_i64(x: i64)
  • requires x > 0
  • use when zero must be excluded
  • nonnull_u64(ptr: u64)
  • requires ptr != 0
  • use for pointer-like integers that must name storage
  • bounds_i64(index: i64, len: i64)
  • requires 0 <= index < len
  • use for zero-based indexing into initialized sequence elements
  • bounds_inclusive_i64(value: i64, max_inclusive: i64)
  • requires 0 <= value <= max_inclusive
  • use for inclusive upper-bound facts such as nibble or flag ranges
  • range_i64(value: i64, lo: i64, hi: i64)
  • requires lo <= value < hi
  • use for generic half-open interval facts
  • range_inclusive_i64(value: i64, lo: i64, hi: i64)
  • requires lo <= value <= hi
  • use for generic inclusive interval facts
  • add_nonnegative_no_overflow_i64(a: i64, b: i64)
  • requires non-negative operands and proves a + b cannot signed-overflow
  • use before growth arithmetic or len + additional style calculations
  • power_of_two_u64(value: u64)
  • requires value to be a non-zero power of two
  • use for alignment, mask, and table-size reasoning
  • aligned_u64(value: u64, alignment: u64)
  • requires value % alignment == 0
  • use for address or byte-offset alignment facts

Slice, buffer, and vector storage#

  • slice_well_formed(ptr: u64, len: i64)
  • requires len >= 0
  • requires non-null storage when len > 0
  • use for borrowed pointer/length pairs
  • slice_nonempty(ptr: u64, len: i64)
  • requires len > 0 and ptr != 0
  • use when an empty slice is not allowed
  • slice_has_at_least(ptr: u64, len: i64, needed: i64)
  • combines slice_well_formed with len >= needed
  • use before fixed-width reads or multi-byte decoding
  • slice_index_i64(ptr: u64, len: i64, index: i64)
  • combines slice_well_formed with an in-bounds index
  • use before element loads
  • slice_range_i64(start: i64, end: i64, len: i64)
  • requires 0 <= start <= end <= len
  • use before sub-slicing or span copying
  • buffer_well_formed(ptr: u64, cap: i64)
  • requires cap >= 0
  • requires non-null storage when cap > 0
  • use for capacity-only storage handles
  • vector_well_formed(ptr: u64, len: i64, cap: i64)
  • requires 0 <= len <= cap
  • requires non-null storage when cap > 0
  • use for initialized-prefix dynamic storage
  • cap_at_least_len_plus_i64(cap: i64, len: i64, additional: i64)
  • proves that capacity is large enough for len + additional
  • use before append/grow operations
  • vector_has_space_i64(ptr: u64, len: i64, cap: i64, additional: i64)
  • combines vector_well_formed and spare-capacity facts
  • use before writes that extend the initialized prefix
  • vector_index_i64(ptr: u64, len: i64, cap: i64, index: i64)
  • combines vector_well_formed with an in-bounds initialized index
  • use before reads from the live prefix of vector storage

Source repository · Edit this page · View Markdown