Documentation
¶
Overview ¶
Package smt provides SMT-LIB encoding for AILANG contract verification. It translates AILANG Core AST functions with contracts into SMT-LIB format for verification by Z3 or other SMT solvers.
Index ¶
- Variables
- func AllHigherOrderIsInlinable(body core.CoreExpr) bool
- func Assert(expr string) string
- func AssertNot(expr string) string
- func CheckSat() string
- func DeclareConst(name, sort string) string
- func DeclareDatatype(typeName string, variants []ADTVariant) string
- func DeclareEnumDatatype(typeName string, variants []string) string
- func DeclareRecordDatatype(sortName string, fields map[string]string) string
- func EncodeExpr(expr core.CoreExpr) (string, error)
- func FindZ3() (string, error)
- func GenerateListUnrolling(op string, depth int, elemSort string) []string
- func GetModel() string
- func IsRecursiveFunc(body core.CoreExpr, funcName string) bool
- func IsUserDefinedCall(app *core.App, prog *core.Program) (string, bool)
- func MapRecordFields(rec *types.TRecord) (map[string]string, error)
- func MapRecordSortName(rec *types.TRecord) string
- func MapType(t types.Type) (string, error)
- func RecordConstructorName(sortName string) string
- func ReplaceSelfCalls(expr core.CoreExpr, funcName, replacement string) core.CoreExpr
- func ResolveStdlibToBuiltin(module, name string) (string, bool)
- func SortedFieldNamesStr(fields map[string]string) []string
- func SpecializeFilter(funcName string, lambda *core.Lambda, listParam string) (core.CoreExpr, string)
- func SpecializeFoldl(funcName string, lambda *core.Lambda, listParam string) (core.CoreExpr, string)
- func SpecializeMap(funcName string, lambda *core.Lambda, listParam string) (core.CoreExpr, string)
- func SubstituteLambdaVar(body core.CoreExpr, paramName string, replacement core.CoreExpr) core.CoreExpr
- func TopLevelUnrolledName(op string, depth int) string
- func Z3Available() bool
- func Z3Version() string
- type ADTField
- type ADTVariant
- type CalleeDef
- type CalleeInfo
- type EncodeFunctionOpts
- type EncodeResult
- type FunctionParam
- type HOFKind
- type InlinableHOFCall
- type InlineResult
- type ListBuiltinSpec
- type ModelBinding
- type NumericBuiltinSpec
- type RecordTypeInfo
- type RejectionCode
- type SMTContext
- type SMTRejectionReason
- type SolverConfig
- type SolverResult
- type SolverStatus
- type SpecializationResult
- type StringBuiltinSpec
- type UnrollConfig
- type UnrollResult
Constants ¶
This section is empty.
Variables ¶
var BuiltinToSMTOp = map[string]string{
"add_Int": "+",
"add_Float": "+",
"sub_Int": "-",
"sub_Float": "-",
"mul_Int": "*",
"mul_Float": "*",
"div_Int": "div",
"div_Float": "/",
"mod_Int": "mod",
"mod_Float": "mod",
"eq_Int": "=",
"eq_Float": "=",
"eq_Bool": "=",
"ne_Int": "distinct",
"ne_Float": "distinct",
"ne_Bool": "distinct",
"lt_Int": "<",
"lt_Float": "<",
"le_Int": "<=",
"le_Float": "<=",
"gt_Int": ">",
"gt_Float": ">",
"ge_Int": ">=",
"ge_Float": ">=",
"and_Bool": "and",
"or_Bool": "or",
"not_Bool": "not",
"neg_Int": "-",
"neg_Float": "-",
"eq_String": "=",
"ne_String": "distinct",
"lt_String": "str.<",
"le_String": "str.<=",
}
BuiltinToSMTOp maps lowered AILANG builtin names to SMT-LIB operators. After op_lowering, operators become App(VarGlobal($builtin.XXX)) calls. This map reverses that transformation for SMT encoding.
var ErrUnresolvableTypes = errors.New("unresolvable cross-module types")
ErrUnresolvableTypes indicates that the function references types (ADTs, record aliases) that cannot be fully resolved in Z3. Functions with this error should be SKIPPED, not reported as errors.
var ListBuiltinSpecial = map[string]ListBuiltinSpec{ "concat_List": {Op: "seq.++"}, "::": {Op: "seq.++", ConsMode: true}, "_list_length": {Op: "seq.len", Unary: true}, "_list_head": {Op: "seq.nth", Unary: true, AppendZero: true}, "_list_nth": {Op: "seq.nth"}, "_list_contains": {Op: "seq.contains", ContainsMode: true}, "_list_extract": {Op: "seq.extract", TernaryMode: true}, "_list_tail": {Op: "seq.extract", Unary: true, TailMode: true}, }
ListBuiltinSpecial maps AILANG list builtins that need non-standard encoding.
var NumericBuiltinSpecial = map[string]NumericBuiltinSpec{ "intToFloat": {Op: "to_real", Unary: true}, "_int_to_float": {Op: "to_real", Unary: true}, "floatToInt": {Op: "to_int", Unary: true}, "_float_to_int": {Op: "to_int", Unary: true}, }
NumericBuiltinSpecial maps AILANG numeric conversion builtins to SMT-LIB operators. intToFloat → Z3 to_real (exact conversion, no semantic gap). floatToInt → Z3 to_int (rounds toward -inf; Go truncates toward zero — document gap).
var RecursiveListBuiltins = map[string]string{
"_list_reverse": "reverse",
"_list_take": "take",
"_list_drop": "drop",
}
RecursiveListBuiltins maps recursive list operation names to their descriptions. These are encoded via bounded unrolling (GenerateListUnrolling) rather than direct Z3 operator mapping.
var StdlibListToSMT = map[string]string{
"length": "_list_length",
"reverse": "_list_reverse",
"take": "_list_take",
"drop": "_list_drop",
"contains": "_list_contains",
}
StdlibListToSMT maps std/list function names to their builtin equivalents.
var StdlibStringToSMT = map[string]string{
"length": "_str_len",
"substring": "_str_slice",
"find": "_str_find",
"startsWith": "_str_startsWith",
"endsWith": "_str_endsWith",
"contains": "_str_contains",
}
StdlibStringToSMT maps std/string function names to their builtin equivalents. Only trivial wrappers (1:1 forwarding) with Z3 SMT-LIB equivalents are listed.
var StringBuiltinSpecial = map[string]StringBuiltinSpec{ "gt_String": {Op: "str.<", FlipArgs: true}, "ge_String": {Op: "str.<=", FlipArgs: true}, "concat_String": {Op: "str.++"}, "_str_len": {Op: "str.len", Unary: true}, "_str_find": {Op: "str.indexof", AppendZero: true}, "_str_startsWith": {Op: "str.prefixof", FlipArgs: true}, "_str_endsWith": {Op: "str.suffixof", FlipArgs: true}, "_str_slice": {Op: "str.substr", SubstrMode: true}, "_str_contains": {Op: "str.contains"}, }
StringBuiltinSpecial maps AILANG string builtins that need non-standard encoding. These require special handling (operand flipping, extra args, etc.).
Functions ¶
func AllHigherOrderIsInlinable ¶
AllHigherOrderIsInlinable returns true if every lambda-in-arg-position in the body is part of a known HOF call (map_List, filter_List, foldl_List with a literal lambda). Returns true when there is no higher-order at all.
func DeclareConst ¶
DeclareConst generates an SMT-LIB constant declaration.
(declare-const name sort)
func DeclareDatatype ¶
func DeclareDatatype(typeName string, variants []ADTVariant) string
DeclareDatatype generates an SMT-LIB declare-datatype for an ADT. For enums (all variants have zero fields):
(declare-datatype Season ((LOW_SEASON) (HIGH_SEASON)))
For ADTs with fields:
(declare-datatype Shape ((Circle (radius Int)) (Rect (width Int) (height Int))))
func DeclareEnumDatatype ¶
DeclareEnumDatatype is a convenience for enums (all nullary constructors).
func DeclareRecordDatatype ¶
DeclareRecordDatatype generates an SMT-LIB declare-datatype for a record type. Records are modeled as single-constructor datatypes with named field accessors:
(declare-datatype Point ((mk_Point (x Int) (y Int))))
Field order is alphabetical for deterministic output.
func EncodeExpr ¶
EncodeExpr translates a Core AST expression to an SMT-LIB expression string.
func FindZ3 ¶
FindZ3 discovers the Z3 binary location. Search order: AILANG_Z3_PATH env, PATH, common locations.
func GenerateListUnrolling ¶
GenerateListUnrolling generates SMT-LIB declarations for bounded recursive list operations. Returns a slice of SMT-LIB declaration strings, or nil if the operation is not recognized.
For depth N, produces N+1 declarations:
- Level 0: (declare-fun op_0 ...) — uninterpreted function
- Levels 1..N: (define-fun op_k ...) where recursive calls use op_(k-1)
Supported operations: _list_reverse, _list_take, _list_drop
func IsRecursiveFunc ¶
IsRecursiveFunc is the exported version of isRecursive for use by CLI code.
func IsUserDefinedCall ¶
IsUserDefinedCall checks whether an App expression represents a call to a user-defined function (not a builtin or ADT constructor). Handles both VarGlobal (cross-module) and plain Var (same-module) references.
func MapRecordFields ¶
MapRecordFields maps all fields of a record type to their SMT-LIB sorts. Returns the mapping and an error if any field type is not encodable.
func MapRecordSortName ¶
MapRecordSortName returns the SMT-LIB sort name for a record type. Named records use their TypeName directly; anonymous records get a deterministic name based on their fields (sorted alphabetically).
func MapType ¶
MapType maps an AILANG type to its SMT-LIB sort name. Returns the SMT-LIB sort string and an error if the type is not encodable.
func RecordConstructorName ¶
RecordConstructorName returns the SMT-LIB constructor name for a record.
(mk_Point ...) for record type Point
func ReplaceSelfCalls ¶
ReplaceSelfCalls walks the Core AST and replaces references to funcName with replacement. Returns a new AST (does not mutate the original). Respects Lambda/Let/LetRec shadowing.
func ResolveStdlibToBuiltin ¶
ResolveStdlibToBuiltin checks if a module/function pair refers to a stdlib function with a known SMT mapping, and returns the equivalent builtin name.
func SortedFieldNamesStr ¶
SortedFieldNamesStr returns field names from a string map sorted alphabetically.
func SpecializeFilter ¶
func SpecializeFilter(funcName string, lambda *core.Lambda, listParam string) (core.CoreExpr, string)
SpecializeFilter transforms filter(\x -> pred, xs) into a recursive function:
filter_spec(xs) =
if seq.len(xs) == 0 then []
else let h = head(xs) in
let t = tail(xs) in
if pred[x -> h] then h :: filter_spec(t)
else filter_spec(t)
func SpecializeFoldl ¶
func SpecializeFoldl(funcName string, lambda *core.Lambda, listParam string) (core.CoreExpr, string)
SpecializeFoldl transforms foldl(\acc x -> body, init, xs) into a recursive function:
fold_spec(acc, xs) =
if seq.len(xs) == 0 then acc
else let h = head(xs) in
let t = tail(xs) in
fold_spec(body[acc -> acc, x -> h], t)
Note: The specialized function takes (acc, xs) as parameters. The initial value is passed as the acc argument at the call site.
func SpecializeMap ¶
SpecializeMap transforms map(\x -> body, xs) into a recursive function:
map_spec(xs) =
if seq.len(xs) == 0 then (as seq.empty (Seq Int))
else let h = seq.nth(xs, 0) in
let t = seq.extract(xs, 1, seq.len(xs) - 1) in
(seq.++ (seq.unit body[x -> h]) map_spec(t))
Returns the specialized body and the specialized function name.
func SubstituteLambdaVar ¶
func SubstituteLambdaVar(body core.CoreExpr, paramName string, replacement core.CoreExpr) core.CoreExpr
SubstituteLambdaVar replaces all free occurrences of paramName in body with the replacement expression. Respects variable shadowing by Lambda/Let.
func TopLevelUnrolledName ¶
TopLevelUnrolledName returns the name of the top-level (deepest) unrolled function for a given operation and depth.
func Z3Available ¶
func Z3Available() bool
Z3Available returns true if Z3 is installed and accessible.
Types ¶
type ADTField ¶
type ADTField struct {
Name string // Accessor name (auto-generated if not specified)
Sort string // SMT-LIB sort name
}
ADTField describes a field within an ADT variant.
type ADTVariant ¶
ADTVariant describes a single variant of an algebraic data type.
type CalleeDef ¶
type CalleeDef struct {
Name string
SMTLib string // The (define-fun ...) declaration
ADTDecl string // Any ADT declarations needed (may be empty)
}
CalleeDef holds a ready-to-emit SMT-LIB define-fun for a callee.
func ResolveCallees ¶
func ResolveCallees( funcName string, body core.CoreExpr, prog *core.Program, surfaceParams map[string][]FunctionParam, surfaceReturnSorts map[string]string, adtTypes map[string][]ADTVariant, ) ([]CalleeDef, error)
ResolveCallees finds all user-defined function calls in the body, resolves their Core bodies from the program, and returns ordered define-fun declarations for SMT-LIB emission.
The returned definitions are topologically ordered: if A calls B, B's definition appears before A's. Circular calls are detected and produce an error.
type CalleeInfo ¶
type CalleeInfo struct {
Name string
Params []FunctionParam
Body core.CoreExpr
ReturnSort string
ADTTypes map[string][]ADTVariant
}
CalleeInfo describes a resolved callee function for SMT encoding.
type EncodeFunctionOpts ¶
type EncodeFunctionOpts struct {
// Program is the full Core program (needed for resolving cross-function calls).
Program *core.Program
// SurfaceParams maps function names to their parameter info.
SurfaceParams map[string][]FunctionParam
// SurfaceReturnSorts maps function names to their return sort strings.
SurfaceReturnSorts map[string]string
// ReturnType is the function's return type (used to discover record types in return position).
ReturnType types.Type
// Body is the function body expression (used to discover record types constructed in the body).
Body core.CoreExpr
// Contracts are the function's contracts (used to discover record types in ensures clauses).
Contracts []*core.Contract
// RecursiveDepth enables bounded recursion unrolling at the given depth (1-10).
// When > 0 and the function is self-recursive, generates a define-fun chain
// instead of rejecting. 0 means no unrolling (default behavior).
RecursiveDepth int
// HOFInlineDepth controls the unrolling depth for HOF specializations
// (map, filter, foldl with literal lambda arguments). When > 0, inlinable
// HOF calls are specialized into recursive functions and unrolled.
// Default: 3 if not specified and HOF calls are detected.
HOFInlineDepth int
// ExtraDeclarations are additional SMT-LIB declarations to prepend
// (e.g., record types found in ADT constructor fields that need
// to be declared before the ADT itself).
ExtraDeclarations []string
// RecordTypeAliases maps named record type aliases (e.g., "TableCell", "Point")
// to their TRecord types. These are pre-registered during record type collection
// so that named sorts take priority over anonymous Record_field1_field2 sorts.
RecordTypeAliases map[string]*types.TRecord
}
EncodeFunctionOpts holds optional parameters for cross-function call resolution.
type EncodeResult ¶
type EncodeResult struct {
// SMTLib is the complete SMT-LIB program text.
SMTLib string
// Declarations are the type/constant declarations.
Declarations []string
// Assertions are the assert statements.
Assertions []string
// BodyExpr is the encoded function body expression.
BodyExpr string
}
EncodeResult holds the generated SMT-LIB program for a function.
func EncodeFunction ¶
func EncodeFunction( funcName string, params []FunctionParam, body core.CoreExpr, returnSort string, meta *core.DeclMeta, adtTypes map[string][]ADTVariant, opts ...EncodeFunctionOpts, ) (*EncodeResult, error)
EncodeFunction generates a complete SMT-LIB program for verifying a function's contracts.
The encoding follows the standard pattern for bounded verification:
- Declare types (datatypes for ADTs)
- Declare symbolic variables (function parameters)
- Assert preconditions (requires clauses)
- Define function body as "result"
- Assert negation of postconditions (ensures clauses)
- Check satisfiability
If the result is "unsat", all postconditions hold for all inputs satisfying preconditions. If "sat", the model provides a counterexample.
type FunctionParam ¶
FunctionParam describes a function parameter for SMT encoding.
type InlinableHOFCall ¶
type InlinableHOFCall struct {
Kind HOFKind
Lambda *core.Lambda // The literal lambda argument
ListArg core.CoreExpr // The list argument
InitVal core.CoreExpr // Only for foldl: the initial accumulator value
// Original is the original App node for replacement
Original *core.App
}
InlinableHOFCall describes a detected higher-order function call that can be specialized (lambda argument is a literal, not a variable).
func IsInlinableHOF ¶
func IsInlinableHOF(body core.CoreExpr) []InlinableHOFCall
IsInlinableHOF walks the Core AST looking for HOF calls (map, filter, foldl) where the function argument is a literal lambda (not a variable). Returns info about each inlinable call found.
type InlineResult ¶
type InlineResult struct {
// NewBody is the modified function body with HOF calls replaced
// by calls to specialized recursive functions.
NewBody core.CoreExpr
// Specializations are the unrolled recursive function declarations.
Specializations []SpecializationResult
}
InlineResult holds the result of HOF inlining for a function body.
func InlineHOFCalls ¶
func InlineHOFCalls(funcName string, body core.CoreExpr, depth int) *InlineResult
InlineHOFCalls detects inlinable HOF calls in the body, generates specialized recursive functions, replaces the HOF calls with calls to the specialized functions, and returns the unrolled declarations. Returns nil if no HOF calls are found.
type ListBuiltinSpec ¶
type ListBuiltinSpec struct {
Op string // SMT-LIB operator name
Unary bool // Single argument
AppendZero bool // Append literal 0 as extra argument
ConsMode bool // First arg wrapped in (seq.unit ...)
ContainsMode bool // Second arg wrapped in (seq.unit ...) for element containment
TernaryMode bool // Three arguments (e.g., seq.extract xs offset length)
TailMode bool // _list_tail: (seq.extract xs 1 (- (seq.len xs) 1))
}
ListBuiltinSpec describes how to encode a list builtin in SMT-LIB.
type ModelBinding ¶
type ModelBinding struct {
Name string `json:"name"`
Sort string `json:"sort"`
Value string `json:"value"`
}
ModelBinding represents a variable assignment from a counterexample.
type NumericBuiltinSpec ¶
type NumericBuiltinSpec struct {
Op string // SMT-LIB operator name (to_real, to_int)
Unary bool // Always true for conversions
}
NumericBuiltinSpec describes how to encode a numeric conversion builtin in SMT-LIB.
type RecordTypeInfo ¶
type RecordTypeInfo struct {
SortName string // SMT-LIB sort name (e.g., "Point")
CtorName string // Constructor name (e.g., "mk_Point")
FieldNames []string // Sorted field names
FieldSorts map[string]string // Field name → SMT-LIB sort
}
RecordTypeInfo describes a record type for SMT encoding.
type RejectionCode ¶
type RejectionCode string
RejectionCode identifies why a function cannot be SMT-verified.
const ( RejectNoContracts RejectionCode = "NO_CONTRACTS" RejectNotPure RejectionCode = "NOT_PURE" RejectRecursive RejectionCode = "RECURSIVE" RejectHigherOrder RejectionCode = "HIGHER_ORDER" RejectDeepPatterns RejectionCode = "DEEP_PATTERNS" RejectUnencodable RejectionCode = "UNENCODABLE_TYPE" )
type SMTContext ¶
type SMTContext struct {
// Declarations accumulates SMT-LIB type declarations.
Declarations []string
// Variables maps AILANG parameter names to their SMT-LIB types.
Variables map[string]string
// DeclaredTypes tracks which ADT types have been declared.
DeclaredTypes map[string]bool
// ResolvedCallees tracks function names that have been resolved
// as define-fun declarations for cross-function call support.
ResolvedCallees map[string]bool
}
SMTContext holds the state needed for SMT-LIB generation.
func NewSMTContext ¶
func NewSMTContext() *SMTContext
NewSMTContext creates a new SMT encoding context.
type SMTRejectionReason ¶
type SMTRejectionReason struct {
Code RejectionCode
Message string
Location string // Source location if available
Hint string // Suggestion to make it encodable
}
SMTRejectionReason describes why a function cannot be SMT-verified.
func IsSMTEncodable ¶
func IsSMTEncodable(funcName string, meta *core.DeclMeta, body core.CoreExpr) (bool, []SMTRejectionReason)
IsSMTEncodable checks whether a function can be SMT-verified. Returns true with empty reasons if the function is in the decidable fragment. Returns false with rejection reasons otherwise.
func IsSMTEncodableForCallee ¶
func IsSMTEncodableForCallee(funcName string, meta *core.DeclMeta, body core.CoreExpr) (bool, []SMTRejectionReason)
IsSMTEncodableForCallee checks if a function can be used as a callee in cross-function verification. Similar to IsSMTEncodable but doesn't require contracts (callees don't need their own contracts to be inlined).
func (SMTRejectionReason) String ¶
func (r SMTRejectionReason) String() string
type SolverConfig ¶
type SolverConfig struct {
Z3Path string // Path to z3 binary (auto-discovered if empty)
Timeout time.Duration // Per-function timeout (default 5s)
}
SolverConfig holds configuration for the Z3 solver.
func DefaultSolverConfig ¶
func DefaultSolverConfig() SolverConfig
DefaultSolverConfig returns a SolverConfig with sensible defaults.
type SolverResult ¶
type SolverResult struct {
Status SolverStatus
Model []ModelBinding // Populated when Status == StatusCounterexample
Duration time.Duration
SMTLib string // The input SMT-LIB program (for --verbose)
RawOutput string // Raw Z3 stdout
Error string // Error message if Status == StatusError
}
SolverResult holds the result of running Z3 on an SMT-LIB program.
func Solve ¶
func Solve(smtlib string, config SolverConfig) (*SolverResult, error)
Solve runs Z3 on an SMT-LIB program and parses the result.
func SolveFile ¶
func SolveFile(path string, config SolverConfig) (*SolverResult, error)
SolveFile runs Z3 on an SMT-LIB file.
type SolverStatus ¶
type SolverStatus int
SolverStatus represents the result of an SMT solver invocation.
const ( StatusVerified SolverStatus = iota // unsat — postconditions hold for all inputs StatusCounterexample // sat — found inputs that violate postconditions StatusUnknown // unknown — solver could not determine StatusError // error — solver error or not found )
func (SolverStatus) String ¶
func (s SolverStatus) String() string
type SpecializationResult ¶
type SpecializationResult struct {
// Declarations are the SMT-LIB declare-fun/define-fun chain.
Declarations []string
// TopLevelName is the top-level unrolled function name.
TopLevelName string
// Kind is which HOF was specialized.
Kind HOFKind
}
SpecializationResult holds the unrolled declarations for one specialization.
type StringBuiltinSpec ¶
type StringBuiltinSpec struct {
Op string // SMT-LIB operator name
FlipArgs bool // Swap arg order (e.g., gt → lt with flipped args)
Unary bool // Single argument
AppendZero bool // Append literal 0 as extra argument
SubstrMode bool // Convert (s, start, end) → (str.substr s start (- end start))
}
StringBuiltinSpec describes how to encode a string builtin in SMT-LIB.
type UnrollConfig ¶
type UnrollConfig struct {
FuncName string // Name of the recursive function
Params []FunctionParam // Function parameters
Body core.CoreExpr // Original function body
ReturnSort string // SMT-LIB return sort
Depth int // Unrolling depth (1-10)
}
UnrollConfig holds parameters for bounded recursive function unrolling.
type UnrollResult ¶
type UnrollResult struct {
// Declarations contains the declare-fun (level 0) and define-fun (levels 1..N).
Declarations []string
// TopLevelName is the name of the deepest unrolling (funcName_N).
TopLevelName string
}
UnrollResult holds the generated SMT-LIB declarations for a bounded unrolling.
func UnrollRecursiveFunction ¶
func UnrollRecursiveFunction(cfg UnrollConfig) (*UnrollResult, error)
UnrollRecursiveFunction generates a chain of define-fun declarations for bounded recursive function verification (Dafny-style unrolling).
For depth N, it produces N+1 declarations:
- Level 0: (declare-fun funcName_0 ...) — uninterpreted function
- Levels 1..N: (define-fun funcName_k ...) where self-calls are replaced with funcName_(k-1)
The top-level name funcName_N is used in verification conditions.