-
Notifications
You must be signed in to change notification settings - Fork 23
Add B3 Verifier: SMT-based verification for B3 programs #307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
20242c2
40727c6
4414ed7
19a0bf4
466f586
f8af0a1
aaec9a3
c150aba
d73cd24
6153a2c
8c4933f
700c645
b7c5e14
29908bf
f96d23b
6ca219d
95f8c12
b8fd1d6
3a27145
5859f6e
38e53a8
5119550
6e94e24
41119a7
c757d12
ae44cab
48abe84
a03f0cc
fb71f14
8febeb2
b5f6939
d31b140
bf8eed9
b3d6006
309bec8
2905a07
bc57e12
1a6e5f8
54ba932
5029d10
f8bfc43
7848fd3
a822591
0971a6e
9c40158
7f6c312
8517321
c4bca29
053b54c
d7b2b64
b7b2ae9
975d604
3515e53
647058f
56b8d7e
388018f
b6f0f26
f24cd73
8d1b12e
b517ee5
1a245e0
25ac46e
17cee51
8bffd3c
b8747dc
d3d3a9e
c00df60
838a3c6
5ef6fd9
5a55ab8
085078e
a24f3d4
9279956
e217e99
04c0bb9
2f02069
8c231cf
85dcc23
1620932
ee80e24
9c11a05
060244c
bca2f06
fcb2194
e81132a
aff081e
a5d4a33
f57df25
4159e5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| /- | ||
| Copyright Strata Contributors | ||
|
|
||
| SPDX-License-Identifier: Apache-2.0 OR MIT | ||
| -/ | ||
|
|
||
| import Strata.Languages.B3.DDMTransform.DefinitionAST | ||
|
|
||
| /-! | ||
| # Function-to-Axiom Transformation | ||
|
|
||
| Transforms B3 programs by splitting function definitions into declarations and axioms. | ||
|
|
||
| While SMT-LIB 2.6 provides `define-fun-rec` for mutually recursive definitions, | ||
| we use quantified axioms for broader solver compatibility and to maintain consistency | ||
| with our verification approach. By converting function bodies to axioms with quantifiers, | ||
| we enable verification of programs with mutually recursive functions across different | ||
| SMT solvers. | ||
|
|
||
| TODO: Add config option to use `define-fun` for non-recursive functions instead of | ||
| quantified axioms. This could improve solver performance for simple function definitions. | ||
|
|
||
| ## Example: Simple Function | ||
|
|
||
| Input: | ||
| ``` | ||
| function abs(x : int) : int { | ||
| if x >= 0 then x else -x | ||
| } | ||
| ``` | ||
|
|
||
| Output: | ||
| ``` | ||
| function abs(x : int) : int | ||
|
|
||
| axiom forall x : int pattern abs(x) abs(x) == (if x >= 0 then x else -x) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we chatted offline, one day we ought to have a config option that allows the definition of at least non-recursive functions using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that B3 already supports this. |
||
| ``` | ||
|
|
||
| ## Example: Mutually Recursive Functions | ||
|
|
||
| Input: | ||
| ``` | ||
| function isEven(n : int) : int { | ||
| if n == 0 then 1 else isOdd(n - 1) | ||
| } | ||
| function isOdd(n : int) : int { | ||
| if n == 0 then 0 else isEven(n - 1) | ||
| } | ||
| ``` | ||
|
|
||
| Output: | ||
| ``` | ||
| function isEven(n : int) : int | ||
| function isOdd(n : int) : int | ||
|
|
||
| axiom forall n : int pattern isEven(n) isEven(n) == (if n == 0 then 1 else isOdd(n - 1)) | ||
| axiom forall n : int pattern isOdd(n) isOdd(n) == (if n == 0 then 0 else isEven(n - 1)) | ||
| ``` | ||
|
|
||
| ## Example: Function with Precondition | ||
|
|
||
| Input: | ||
| ``` | ||
| function sqrt(x : int) : int | ||
| when x >= 0 | ||
| { | ||
| ... | ||
| } | ||
| ``` | ||
|
|
||
| Output: | ||
| ``` | ||
| function sqrt(x : int) : int | ||
|
|
||
| axiom forall x : int pattern sqrt(x) (x >= 0) ==> (sqrt(x) == ...) | ||
| ``` | ||
| -/ | ||
|
|
||
| namespace Strata.B3.Transform | ||
|
|
||
| open Strata.B3AST | ||
|
|
||
| def transformFunctionDecl (decl : B3AST.Decl α) : Option (B3AST.Decl α × B3AST.Decl α) := | ||
| match decl with | ||
| | .function _m name params resultType tag body => | ||
| match body.val with | ||
| | some bodyAnn => | ||
| match bodyAnn with | ||
| | FunctionBody.functionBody m whens bodyExpr => | ||
| let funcDecl := B3AST.Decl.function m name params resultType tag (Ann.mk body.ann none) | ||
| let paramList := params.val.toList | ||
| let funcCallArgs : Array (Expression α) := | ||
| params.val.mapIdx (fun i _param => Expression.id m i) | ||
| let funcCall := Expression.functionCall m name ⟨m, funcCallArgs⟩ | ||
| let equality := Expression.binaryOp m (BinaryOp.eq m) funcCall bodyExpr | ||
| let axiomBody := | ||
| if whens.val.isEmpty then | ||
| equality | ||
| else | ||
| let whenExprs := whens.val.toList.filterMap (fun w => | ||
| match w with | When.when _m cond => some cond) | ||
| let whenConj := match whenExprs with | ||
| | [] => Expression.literal whens.ann (Literal.boolLit whens.ann true) | ||
| | first :: rest => rest.foldl (fun acc e => | ||
| Expression.binaryOp whens.ann (BinaryOp.and whens.ann) acc e | ||
| ) first | ||
| Expression.binaryOp whens.ann (BinaryOp.implies whens.ann) whenConj equality | ||
| -- Create pattern with function call | ||
| let pattern := Pattern.pattern m ⟨m, #[funcCall]⟩ | ||
| let axiomExpr := paramList.foldr (fun param body => | ||
| match param with | ||
| | FParameter.fParameter _m _inj paramName paramTy => | ||
| let varDecl := VarDecl.quantVarDecl m paramName paramTy | ||
| Expression.quantifierExpr m | ||
| (QuantifierKind.forall m) | ||
| ⟨m, #[varDecl]⟩ ⟨m, #[pattern]⟩ body | ||
| ) axiomBody | ||
| let axiomDecl := Decl.axiom m ⟨m, #[]⟩ axiomExpr | ||
| some (funcDecl, axiomDecl) | ||
| | none => none | ||
| | _ => none | ||
|
|
||
| def functionToAxiom (prog : B3AST.Program α) : B3AST.Program α := | ||
MikaelMayer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| match prog with | ||
| | Program.program m decls => | ||
| Id.run do | ||
| let mut typeDeclsRev : List (B3AST.Decl α) := [] | ||
| let mut funcDeclsRev : List (B3AST.Decl α) := [] | ||
| let mut funcAxiomsRev : List (B3AST.Decl α) := [] | ||
| let mut otherDeclsRev : List (B3AST.Decl α) := [] | ||
|
|
||
| for decl in decls.val.toList do | ||
| match decl with | ||
| | .typeDecl _ _ | .tagger _ _ _ => | ||
| typeDeclsRev := decl :: typeDeclsRev | ||
| | .function _ _ _ _ _ body => | ||
| match body.val with | ||
| | some bodyAnn => | ||
| match bodyAnn with | ||
| | FunctionBody.functionBody _ _ _ => | ||
| match transformFunctionDecl decl with | ||
| | some (funcDecl, axiomDecl) => | ||
| funcDeclsRev := funcDecl :: funcDeclsRev | ||
| funcAxiomsRev := axiomDecl :: funcAxiomsRev | ||
| | none => otherDeclsRev := decl :: otherDeclsRev | ||
| | none => | ||
| funcDeclsRev := decl :: funcDeclsRev | ||
| | .axiom _ _ _ => | ||
| funcAxiomsRev := decl :: funcAxiomsRev | ||
| | _ => | ||
| otherDeclsRev := decl :: otherDeclsRev | ||
|
|
||
| let finalDecls := typeDeclsRev.reverse ++ funcDeclsRev.reverse ++ funcAxiomsRev.reverse ++ otherDeclsRev.reverse | ||
| return Program.program m ⟨decls.ann, finalDecls.toArray⟩ | ||
|
|
||
| end Strata.B3.Transform | ||
Uh oh!
There was an error while loading. Please reload this page.