diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c0c7073a..2e0122b95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,12 +60,21 @@ jobs: # for the session so that it does not have to rechecked every time - name: Set up Isabelle Boogie language session run: | - echo "$PWD/foundational_boogie/BoogieLang" >> isabelle_dir/ROOTS - isabelle_dir/bin/isabelle build -b -j4 -d $PWD/foundational_boogie/BoogieLang Boogie_Lang + echo "$PWD/foundational-boogie/BoogieLang" >> isabelle_dir/ROOTS + isabelle_dir/bin/isabelle build -b -j4 -d $PWD/foundational-boogie/BoogieLang Boogie_Lang - name: Check external benchmark proofs run: | python3 etc/scripts/check_proofs.py --inputdir table_benchmarks_proofs --reps 1 + + - name: Generate CFG optimization benchmarks + run: | + BOOGIE_EXE=$(find $PWD -type f -name "BoogieDriver") + python3 etc/scripts/generate_proofs.py --inputdir ProofGenerationBenchmarks/cfg_optimizations_tests --outputdir cfg_optimizations_proofs --boogieproofExe $BOOGIE_EXE + + #- name: Check CFG optimization proofs + # run: | + # python3 etc/scripts/check_proofs.py --inputdir cfg_optimizations_proofs --reps 1 - name: Generate Boogie benchmark proofs run: | @@ -74,4 +83,4 @@ jobs: - name: Check Boogie benchmark proofs run: | - python3 etc/scripts/check_proofs.py --inputdir boogie_benchmarks_proofs --reps 1 \ No newline at end of file + python3 etc/scripts/check_proofs.py --inputdir boogie_benchmarks_proofs --reps 1 diff --git a/.gitmodules b/.gitmodules index c3f8cfee6..60e687d2c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ -[submodule "foundational_boogie"] - path = foundational_boogie - url = https://github.com/gauravpartha/foundational_boogie +[submodule "foundational-boogie"] + path = foundational-boogie + url = https://github.com/viperproject/foundational-boogie + branch = cfg_optimizations diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/expansion4.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/expansion4.bpl index 0b5342cc3..cfd0672d5 100644 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/expansion4.bpl +++ b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/expansion4.bpl @@ -1,11 +1,7 @@ // RUN: %boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" -/* MANUAL REWRITE: function foo(x:int) : int { if x <= 0 then 1 else foo(x - 1) + 2 } -*/ -function foo(x:int) : int; -axiom( forall x:int :: ((x <= 0 ==> foo(x) == 1) && (x > 0 ==> foo(x) == foo(x - 1) + 2))); procedure bar() { diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/test0.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/test0.bpl deleted file mode 100644 index 999b9e128..000000000 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/inline/test0.bpl +++ /dev/null @@ -1,50 +0,0 @@ -// RUN: %boogie "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -// inlined functions - -function Twice(x: int) returns (int) -{ - x + x -} - -function Double(x: int) returns (int) -{ - 3 * x - x -} - -function f(int) returns (int); -function g(int) returns (int); -function h(int) returns (int); -function k(int) returns (int); -axiom (forall x: int :: Twice(x) == f(x)); // here, Twice(x) and f(x) are both triggers -axiom (forall x: int :: Double(x) == g(x)); // since Double is inlined, the trigger here is just g(x) -axiom (forall x: int :: { f(x) } f(x) < h(x) ); -axiom (forall x: int :: { g(x) } g(x) < k(x) ); - -procedure P(a: int, b: int, c: int) -{ - // The following is provable, because Twice triggers its definition and the resulting f(a) - // triggers the relation to h(a). - assert Twice(a) < h(a); - if (*) { - // The following is NOT provable, because Double is inlined and thus no g(b) term is ever - // created - assert Double(b) < k(b); // error - } else { - // The following IS provable, because the explicit g(c) will cause both of the necessary - // quantifiers to trigger - assert g(c) == 2*c; - assert Double(c) < k(c); - } -} - -// nullary functions - -function Five() returns (int) { 5 } - -function Eight() returns (e: int) { 8 } - -procedure Q() -{ - assert 8 * Five() == 5 * Eight(); -} diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/secure/simple.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/secure/simple.bpl index 59a092ae7..cf2442fac 100644 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/secure/simple.bpl +++ b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/secure/simple.bpl @@ -1,11 +1,12 @@ -//:: ProofGen(IgnoreFile) // Z3 4.1: /trace /proverOpt:O:smt.mbqi=true /proverOpt:O:smt.relevancy=0 -function xor(a: bool, b: bool) returns (bool) { (!a && b) || (a && !b) } + +/** MANUAL REWRITE: Replaced all xor with xorCustom (since xor is already defined in Isabelle) */ +function xorCustom(a: bool, b: bool) returns (bool) { (!a && b) || (a && !b) } procedure Incorrect_A( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { ap := a; bp := b; @@ -14,7 +15,7 @@ returns ( ap: bool, bp: bool) procedure Incorrect_B( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { ap := a; bp := b; @@ -23,7 +24,7 @@ returns ( ap: bool, bp: bool) procedure Incorrect_X( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { ap := a; bp := b; @@ -32,27 +33,27 @@ returns ( ap: bool, bp: bool) procedure Correct_A( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { havoc ap; - bp := xor(xor(ap, a), b); + bp := xorCustom(xorCustom(ap, a), b); } procedure Correct_B( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { havoc ap; - bp := xor(xor(ap, a), b); + bp := xorCustom(xorCustom(ap, a), b); } procedure Correct_X( a: bool, b: bool) returns ( ap: bool, bp: bool) - ensures xor(ap, bp) == xor(a, b); + ensures xorCustom(ap, bp) == xorCustom(a, b); { havoc ap; - bp := xor(xor(ap, a), b); + bp := xorCustom(xorCustom(ap, a), b); } diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/stratifiedinline/bar4.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/stratifiedinline/bar4.bpl index efeac9586..14583e3b4 100644 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/stratifiedinline/bar4.bpl +++ b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/stratifiedinline/bar4.bpl @@ -27,20 +27,13 @@ modifies x, y; } -procedure main() returns (b: bool) +procedure main() returns (b: bool) modifies x, y; { assume x == y; call foo(); if (x == y) { call b := bar(); - /** MANUAL REWRITE: assume (if b then x+1 != y else x != y+1); - */ - if(b) { - assume x+1 != y; - } else { - assume x != y+1; - } } } diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/test20/issue-32.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/test20/issue-32.bpl index f980a2c78..b3d4c45d8 100644 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/test20/issue-32.bpl +++ b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/test20/issue-32.bpl @@ -1,10 +1,12 @@ //:: ProofGen(IgnoreFile) // RUN: %boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" -function Lit(x: T) : T; -axiom Lit(true); + +/** MANUEL REWRITE renamed Lit to LitA to avoid Isabelle Lit clash **/ +function LitA(x: T) : T; +axiom LitA(true); procedure test() { - assert Lit(true); + assert LitA(true); } diff --git a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/textbook/DivMod.bpl b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/textbook/DivMod.bpl index fc359d47c..fad75f353 100644 --- a/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/textbook/DivMod.bpl +++ b/ProofGenerationBenchmarks/boogie_testsuite_benchmarks/textbook/DivMod.bpl @@ -7,11 +7,7 @@ // // Rustan Leino, 23 Sep 2010 -/** MANUAL REWRITE: function abs(x: int): int { if 0 <= x then x else -x } -**/ -function abs(x: int): int; -axiom (forall x: int :: ( 0<= x ==> abs(x) == x) && ( (!(0 <= x)) ==> abs(x) == -x)); function divt(int, int): int; function modt(int, int): int; @@ -43,29 +39,9 @@ procedure T_from_E(a,b: int) returns (q,r: int) qq := dive(a,b); rr := mode(a,b); - /** MANUAL REWRITE q := if 0 <= a || rr == 0 then qq else if 0 <= b then qq+1 else qq-1; r := if 0 <= a || rr == 0 then rr else if 0 <= b then rr-b else rr+b; - **/ - if(0 <= a || rr == 0) { - q := qq; - } else { - if (0<=b) { - q := qq+1; - } else { - q := qq-1; - } - } - if (0 <= a || rr == 0) { - r := rr; - } else { - if(0 <= b) { - r := rr-b; - } else { - r := rr+b; - } - } - assume true; + assume true; } procedure E_from_T(a,b: int) returns (q,r: int) @@ -84,26 +60,6 @@ procedure E_from_T(a,b: int) returns (q,r: int) qq := divt(a,b); rr := modt(a,b); - /* MANUAL REWRITE q := if 0 <= rr then qq else if 0 < b then qq-1 else qq+1; r := if 0 <= rr then rr else if 0 < b then rr+b else rr-b; - */ - if(0 <= rr) { - q := qq; - } else { - if (0< b) { - q := qq-1; - } else { - q := qq+1; - } - } - if (0 <= rr) { - r := rr; - } else { - if(0 < b) { - r := rr+b; - } else { - r := rr-b; - } - } } diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_1.bpl new file mode 100644 index 000000000..2e1140fd3 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_1.bpl @@ -0,0 +1,424 @@ +procedure p() { + var x: int; + var y: int; + var i: int; + var z: int; + var a: int; + var b: int; + var c: int; + x := 0; + y := 0; + i := 0; + z := 0; + a := 0; + b := 0; + c := 0; + loop: + while (i <= 100){ + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto b1; + + b1: + y := y + 1; + goto b2; + + b2: + x := x + 1; + goto b3; + + b3: + z := z + 1; + goto b4; + + b4: + a := a + 1; + goto b5; + + b5: + b := b + 1; + goto b6; + + b6: + c := c + 1; + + + } + + + b7: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto b8; + + b8: + y := y + 1; + goto b9; + + b9: + x := x + 1; + goto b10; + + b10: + z := z + 1; + goto b11; + + b11: + a := a + 1; + goto b12; + + b12: + b := b + 1; + goto b13; + + b13: + c := c + 1; + + x := 0; + y := 0; + i := 0; + z := 0; + a := 0; + b := 0; + c := 0; + + loop2: + while (i <= 100){ + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c1; + + c1: + y := y + 1; + goto c2; + + c2: + x := x + 1; + goto c3; + + c3: + z := z + 1; + goto c4; + + c4: + a := a + 1; + goto c5; + + c5: + b := b + 1; + goto c6; + + c6: + c := c + 1; + + + } + + + c7: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c8; + + c8: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c9; + + c9: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c10; + + c10: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c11; + + c11: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c12; + + c12: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto c13; + + c13: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d7; + + + d7: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d8; + + d8: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d9; + + d9: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d10; + + d10: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d11; + + d11: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d12; + + d12: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto d13; + + d13: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e7; + + e7: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e8; + + e8: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e9; + + e9: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e10; + + e10: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e11; + + e11: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e12; + + e12: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto e13; + + e13: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f7; + + + f7: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f8; + + f8: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f9; + + f9: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f10; + + f10: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f11; + + f11: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f12; + + f12: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + goto f13; + + f13: + x := x + 1; + y := y + 1; + i := i + 1; + z := z + 1; + a := a + 1; + b := b + 1; + c := c + 1; + + + + + + + + + + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_2.bpl new file mode 100644 index 000000000..56885e12c --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/big_program_2.bpl @@ -0,0 +1,310 @@ +procedure p() { + var x: int; + var y: int; + var i: int; + var z: int; + var a: int; + var b: int; + var c: int; + x := 0; + z := 0; + + b1: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto l1; + + l1: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b1; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + b2: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto l2; + + l2: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b2; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + b3: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto l3; + + l3: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b3; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + + + i := 0; + y := 0; + b4: + + while ( i <= 10 ){ + y := y + i; + goto b5; + + b5: + i := i + 1; + } + + i := i + 1; + if (y <= 1000){ + goto b4; + } + else{ + goto b6; + } + + b6: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b7; + + b7: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b8; + + b8: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b9; + + b9: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + + b10: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto b11; + + b11: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b10; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + b12: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto b13; + + b13: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b12; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + b14: + x := x + 1; + z := z + 1; + i := 0; + y := 0; + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + x := x + 1; + z := z + 1; + y := y + i; + goto b15; + + b15: + x := x + 1; + z := z + 1; + i := i + 1; + } + } + i := i + 1; + if (y <= 1000){ + goto b14; + } + + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + + + + i := 0; + y := 0; + b16: + + while ( i <= 10 ){ + y := y + i; + goto b17; + + b17: + i := i + 1; + } + + i := i + 1; + if (y <= 1000){ + goto b16; + } + else{ + goto b18; + } + + b18: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b19; + + b19: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b20; + + b20: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + goto b21; + + b21: + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + x := x + 1; + z := z + 1; + i := i + 1; + + + + + + + + + + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_1.bpl new file mode 100644 index 000000000..a604fb3a4 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_1.bpl @@ -0,0 +1,22 @@ +procedure p() { + var y: int; + + y := 1; + + y := y+1; + goto l1; + + l1: + y := y+2; + goto l2; + + l2: + y := y+3; + goto l3,l4; + + l3: + assert y >= 0; + + l4: + assert y >= 0; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_2.bpl new file mode 100644 index 000000000..321bf38e6 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_2.bpl @@ -0,0 +1,40 @@ +procedure procedure_1() { + var y: int; + + y := 1; + + y := y+1; + goto l1; + + l1: + y := y+2; + goto l2, l7; + + l2: + y := y+3; + goto l3,l4; + + l3: + assert y >= 0; + goto l4, l5; + + l4: + assert y >= 0; + goto l5; + + l5: + y:= y+5; + goto l6; + + l6: + assume false; + goto l7; + + l7: + y := y + 7; + assume false; + goto l8; + + l8: + y := y + 8; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_3.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_3.bpl new file mode 100644 index 000000000..ae7a142c2 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/block_coalescing_3.bpl @@ -0,0 +1,36 @@ +procedure procedure_1() { + var y: int; + var i: int; + + i := 0; + y := 0; + + goto l1, l2; + + l1: + i := i + 5; + goto l3; + + l3: + i := i + 1; + y := y + i; + goto l4; + + l4: + y := y + 1; + goto l2; + + + l2: + if (i>y){ + y := i; + goto l5; + + l5: + y := i + 1; + } + assume false; + i := i + 1; + y := i + 1; + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/coalesced_loop_head.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/coalesced_loop_head.bpl new file mode 100644 index 000000000..4f10149c6 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/coalesced_loop_head.bpl @@ -0,0 +1,36 @@ +procedure procedure_1() { + var y: int; + var i: int; + + i := 0; + y := 0; + + goto l1, l2; + + l1: + i := i + 5; + goto l3; + + l3: + i := i + 1; + y := y + i; + goto l4; + + l4: + y := y + 1; + goto l1, l2; + + + l2: + if (i>y){ + y := i; + goto l5; + + l5: + y := i + 1; + } + assume false; + i := i + 1; + y := i + 1; + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/dead_var_elim.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/dead_var_elim.bpl new file mode 100644 index 000000000..8387f0deb --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/dead_var_elim.bpl @@ -0,0 +1,14 @@ + +procedure p2(){ + var k: int; + var j: int; + var i: bool; + + i := true; + + goto T1; + + T1: + k := 0; + k := k + 1; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_pruning_coalescing_combined_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_pruning_coalescing_combined_1.bpl new file mode 100644 index 000000000..11546c2b4 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_pruning_coalescing_combined_1.bpl @@ -0,0 +1,39 @@ +procedure procedure_test() { + var y: int; + var i: int; + + l1: + i := 0; + y := 0; + goto l2; + + + l2: + i := i+1; + + + l3: + while ( i <= 10 ){ + i := i + 1; + goto l4, l2; + + l4: + y := y + i; + } + + assert true; + + + l5: + i := i + 10; + goto l6; + + + l6: + i := i + 10; + assume false; + goto l7; + + l7: + i := i + 1; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_1.bpl new file mode 100644 index 000000000..667d5e4c8 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_1.bpl @@ -0,0 +1,40 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + y := 0; + l10: + + while ( i <= 10 ){ + y := y + i; + goto l1; + + l1: + i := i + 1; + } + + i := i + 1; + if (y <= 1000){ + goto l10; + } + else{ + goto l2; + } + + l2: + i := i + 1; + goto l3; + + l3: + i := i + 1; + goto l4; + + l4: + i := i + 1; + goto l5; + + l5: + i := i + 1; + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_2.bpl new file mode 100644 index 000000000..726795b2e --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_with_coalesced_body_2.bpl @@ -0,0 +1,39 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + y := 0; + l10: + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + y := y + i; + goto l1; + + l1: + i := i + 1; + } + } + + + i := i + 1; + if (y <= 1000){ + goto l10; + } + else{ + goto l2, l3; + } + + + l2: + while (i <= 1000){ + i := i + 1; + assume false; + i := i + 1; + } + + l3: + y := 1000; + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_1.bpl new file mode 100644 index 000000000..03f6313a3 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_1.bpl @@ -0,0 +1,22 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + goto l1; + + l1: + y := 0; + goto l2; + + + l2: + while ( i <= 10 ){ + y := y + i; + i := i + 1; + } + + + i := i + 1; + assume true; + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_2.bpl new file mode 100644 index 000000000..041012eed --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_2.bpl @@ -0,0 +1,28 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + goto l1; + + l1: + y := 0; + goto l2; + + + l2: + while (y <= 1000){ + i := 0; + while ( i <= 10 ){ + y := y + i; + i := i + 1; + goto l1, l3; + } + goto l3; + } + + + l3: + i := i + 1; + assume true; + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_3.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_3.bpl new file mode 100644 index 000000000..c8eccd22c --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/loop_without_coalesced_body_3.bpl @@ -0,0 +1,23 @@ +procedure p() { + var y: int; + var i: int; + + l1: + i := 0; + y := 0; + + + l2: + i := i+1; + + + l3: + while ( i <= 10 ){ + goto l2,l4; + l4: + i := i + 1; + y := y + i; + } + + assert true; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/no_optimization.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/no_optimization.bpl new file mode 100644 index 000000000..2e024b268 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/no_optimization.bpl @@ -0,0 +1,8 @@ +procedure procedure_1() { + var i: int; + i := 0; + + while(i <= 10){ + i := i + 1; + } +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_1.bpl new file mode 100644 index 000000000..c03f86b67 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_1.bpl @@ -0,0 +1,30 @@ +procedure p() { + var y: int; + var i: int; + + l1: + i := 0; + y := 0; + goto l2,l5; + + + l2: + i := i+1; + goto l3; + + l3: + assume false; + goto l4; + + l4: + i := i + 1; + goto l6; + + l5: + assume false; + goto l6; + + + l6: + assert true; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_2.bpl new file mode 100644 index 000000000..0157aa05b --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_2.bpl @@ -0,0 +1,23 @@ +procedure p() { + var y: int; + var i: int; + + l1: + i := 0; + y := 0; + + + l2: + i := i+1; + + + l3: + while ( i <= 10 ){ + goto l2,l4; + l4: + assume false; + y := y + i; + } + + assert y >= 0; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_3.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_3.bpl new file mode 100644 index 000000000..ef7795ebd --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_3.bpl @@ -0,0 +1,36 @@ +procedure procedure_1() { + var y: int; + var i: int; + + i := 0; + y := 0; + + goto l1, l2; + + l1: + i := i + 5; + goto l3; + + l3: + i := i + 1; + y := y + i; + goto l4; + + l4: + y := y + 1; + goto l2; + + + l2: + if (i>y){ + y := i; + goto l5; + + l5: + assume false; + } + assume false; + i := i + 1; + y := i + 1; + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_4.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_4.bpl new file mode 100644 index 000000000..46d7f3296 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/pruning_4.bpl @@ -0,0 +1,40 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + y := 0; + l10: + + while ( i <= 10 ){ + y := y + i; + goto l1; + + l1: + i := i + 1; + } + + i := i + 1; + if (y <= 1000){ + goto l10; + } + else{ + goto l2; + } + + l2: + i := i + 1; + goto l3; + + l3: + i := i + 1; + goto l4; + + l4: + assume false; + goto l5; + + l5: + i := i + 1; + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_1.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_1.bpl new file mode 100644 index 000000000..66727cd3b --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_1.bpl @@ -0,0 +1,62 @@ +procedure p() { + var y: int; + + y := 1; + + y := y+1; + goto l1; + + l1: + y := y+2; + goto l2; + + l2: + y := y+3; + goto l3,l4; + + l3: + assert y >= 0; + + l4: + assert y >= 0; +} + +procedure procedure_test() { + var y: int; + var i: int; + + l1: + i := 0; + y := 0; + goto l2; + + + l2: + i := i+1; + + + l3: + while ( i <= 10 ){ + i := i + 1; + goto l4, l2; + + l4: + y := y + i; + } + + assert true; + + + l5: + i := i + 10; + goto l6; + + + l6: + i := i + 10; + assume false; + goto l7; + + l7: + i := i + 1; +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_2.bpl b/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_2.bpl new file mode 100644 index 000000000..a98aef8e4 --- /dev/null +++ b/ProofGenerationBenchmarks/cfg_optimizations_tests/two_procedures_2.bpl @@ -0,0 +1,80 @@ +procedure p() { + var y: int; + var i: int; + i := 0; + y := 0; + l10: + + while ( i <= 10 ){ + y := y + i; + goto l1; + + l1: + i := i + 1; + } + + i := i + 1; + if (y <= 1000){ + goto l10; + } + else{ + goto l2; + } + + l2: + i := i + 1; + goto l3; + + l3: + i := i + 1; + goto l4; + + l4: + assume false; + goto l5; + + l5: + i := i + 1; + + +} + +procedure p_new() { + var y: int; + var i: int; + i := 0; + y := 0; + l10: + while (y<=1000){ + i := 0; + while ( i <= 10 ){ + y := y + i; + goto l1; + + l1: + i := i + 1; + } + } + + + i := i + 1; + if (y <= 1000){ + goto l10; + } + else{ + goto l2, l3; + } + + + l2: + while (i <= 1000){ + i := i + 1; + assume false; + i := i + 1; + } + + l3: + y := 1000; + + +} \ No newline at end of file diff --git a/ProofGenerationBenchmarks/external_benchmarks/modified/DivMod.bpl b/ProofGenerationBenchmarks/external_benchmarks/modified/DivMod.bpl index bfefa3942..aec587030 100644 --- a/ProofGenerationBenchmarks/external_benchmarks/modified/DivMod.bpl +++ b/ProofGenerationBenchmarks/external_benchmarks/modified/DivMod.bpl @@ -1,5 +1,4 @@ -function abs(x: int): int; -axiom (forall x: int :: ( 0<= x ==> abs(x) == x) && ( (!(0 <= x)) ==> abs(x) == -x)); +function abs(x: int): int { if 0 <= x then x else -x } function divt(int, int): int; function modt(int, int): int; axiom (forall a,b: int :: divt(a,b)*b + modt(a,b) == a); @@ -19,25 +18,9 @@ procedure T_from_E(a,b: int) returns (q,r: int) var qq,rr: int; qq := dive(a,b); rr := mode(a,b); - if(0 <= a || rr == 0) { - q := qq; - } else { - if (0<=b) { - q := qq+1; - } else { - q := qq-1; - } - } - if (0 <= a || rr == 0) { - r := rr; - } else { - if(0 <= b) { - r := rr-b; - } else { - r := rr+b; - } - } - assume true; + q := if 0 <= a || rr == 0 then qq else if 0 <= b then qq+1 else qq-1; + r := if 0 <= a || rr == 0 then rr else if 0 <= b then rr-b else rr+b; + assume true; } procedure E_from_T(a,b: int) returns (q,r: int) requires b != 0; @@ -48,22 +31,6 @@ procedure E_from_T(a,b: int) returns (q,r: int) var qq,rr: int; qq := divt(a,b); rr := modt(a,b); - if(0 <= rr) { - q := qq; - } else { - if (0< b) { - q := qq-1; - } else { - q := qq+1; - } - } - if (0 <= rr) { - r := rr; - } else { - if(0 < b) { - r := rr+b; - } else { - r := rr-b; - } - } + q := if 0 <= rr then qq else if 0 < b then qq-1 else qq+1; + r := if 0 <= rr then rr else if 0 < b then rr+b else rr-b; } diff --git a/README.md b/README.md index cf56b4c62..adabfafe0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ supported subset). The goal of this project is to increase the reliability of the Boogie verifier. Whenever the tool is run on a program, the proof generation extension generates -an Isabelle proof that shows a transformed version of the input program is +an Isabelle proof that shows that the input program is correct under the assumption of the VC that Boogie generates. Most of the source code in the `Source` folder is directly forked from the original @@ -22,7 +22,8 @@ Boogie performs various transformations on the input program before finally generating an AST. For the default Boogie options, the steps performed by Boogie are roughly as follows (and also in this order): -1. Parse source AST program to an internal CFG representation (from this point onwards +1. Parse input program into AST representation +1. Transform AST program to an internal CFG representation (from this point onwards only CFG representations are used) 2. Basic transformations 1: eliminate dead variables, coalesce blocks, prune unreachable blocks 3. CFG-to-DAG phase: eliminate cycles via loop invariants @@ -34,9 +35,12 @@ constant propagation 6. Basic transformations 3: Remove empty blocks, prune unreachable blocks 7. VC phase: generate VC using weakest precondition -Our certificate shows for each procedure that the CFG right before the CFG-to-DAG -phase is correct under the assumption of the VC. That is, we support all the -transformation listed above except those listed in points 1 and 2. +For a large part of our supported subset, our generated certificate shows that the input Boogie program represented +as an AST program (i.e., the representation after parsing) is correct under the assumption of the VC. +For some parts of our subset, we support only the validation of a subset of the transformations: +In particular, we currently do not support gotos and breaks in the AST-to-CFG transformation, and we also do not support the elimination of dead variables. +If the input program has gotos or breaks, and dead variables, then our generated certificate shows that the CFG representation *after* dead variable elimination (but before block coalescing) +is correct under the assumption of the VC (so, we can handle representations of gotos and breaks in CFGs). ## Modifications to the VC We change the VC that Boogie generates in the following ways: @@ -50,35 +54,34 @@ with a "null" argument where a counterexample-related argument is expected. ## Supported subset -We currently support only the default Boogie options and we do not -support any attributes (the subsumption attribute is one exception). In terms of -language features, we currently support: +We currently support only the default Boogie options and we do not support any attributes (the subsumption attribute is one exception). Moreover, we currently do not support files that contain type constructors without any polymorphism. The reason is that Boogie currently monomorphizes such programs, which leads to a different VC. If you want to try such programs, just add some polymorphic function to the program such as `function test(x:T):T` (that does not have to be used anywhere), which forces Boogie to apply the `/typeEncoding:p` command-line option (which specifies the type encoding that we do support, but this command-line option is overriden if the program is monomorphic). + +In terms of language features, we currently support: + * Integers and booleans * Type constructors * (Polymorphic) functions * Most operations on integers/booleans * Type and value quantification * Old expressions -* Any gotos/labels/while loops that Boogie accepts +* Any gotos/labels/while loops that Boogie accepts for the CFG transformations (but not yet for the AST-to-CFG transformation) * Commands: assertions, assumptions, assignments, havocs -Moreover, we currently do not support files that contain type constructors without -any polymorphism. The reason is that Boogie currently monomorphizes such programs, -which leads to a different VC. If you want to try such programs, just add some -polymorphic function to the program such as `function test(x:T):T` (that -does not have to be used anywhere). - ## Dependencies Our tool has the same dependencies as Boogie for the generation of Isabelle proofs: * [.NET Core](https://dotnet.microsoft.com) * a supported SMT solver (see the [original Boogie repository](https://github.com/boogie-org/boogie) for details) -To check Isabelle proofs, one additionally requires Isabelle 2021, as well as +To check Isabelle proofs, one additionally requires Isabelle 2022, as well as an installation of the Isabelle session that provides the [formalization of -Boogie](https://github.com/gauravpartha/foundational_boogie/). Installation -of this session can be done by adding the path to `foundational_boogie/BoogieLang` -to the `ROOTS` file in the Isabelle home directory. +Boogie](https://github.com/gauravpartha/foundational-boogie/). Installation +of this session can be done by adding the path to `foundational-boogie/BoogieLang` +to the `ROOTS` file in the Isabelle home directory, or by running + +``` +isabelle components add -u foundational-boogie/BoogieLang +``` ## Building @@ -108,10 +111,11 @@ directory already exists) in which the proofs are stored. In the proof generation output folder, a separate folder is created for each procedure. There are multiple Isabelle theory files in each folder. The main theorem for the procedure is the last Isabelle lemma in the file with the suffix -`cfg_to_dag_proof.thy`. This final lemma shows that the validity of the VC -implies correctness of the input CFG of the CFG-to-DAG phase. +`asttocfg_proof.thy` in general. If the AST-to-CFG or dead variable elimination is not supported, +then the relevant file ends with `cfgoptimizations_proof.thy` in general, but if we detect that the initial +CFG optimizations ("basic transformations 1" above) had no effect, then the relevant file ends with `cfgtodag_proof.thy`. When using the tool, one currently needs to make sure that no special characters are used that are reserved in Isabelle (such as `#` or `'`). Moreover, for files that do not verify, the tool cannot provide detailed information, since -the counterexample information is not available. \ No newline at end of file +the counterexample information is not available. diff --git a/Source/Boogie.sln b/Source/Boogie.sln index 865070aca..b6465d89a 100644 --- a/Source/Boogie.sln +++ b/Source/Boogie.sln @@ -42,10 +42,13 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreTests", "UnitTests\CoreTests\CoreTests.csproj", "{D2D77420-CFDB-4DA1-B7E7-844C8E8CC686}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProofGeneration", "ProofGeneration\ProofGeneration.csproj", "{3059B8EB-DCE0-4468-9801-5D0A2A226472}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecutionEngineTests", "UnitTests\ExecutionEngineTests\ExecutionEngineTests.csproj", "{473CF455-4306-46E3-9A44-FB7DBA42CA38}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Isabelle", "Isabelle\Isabelle.csproj", "{89B2CE6C-5BE5-49F2-9C88-8EADFDA9C549}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProofGenUtil", "ProofGenUtil\ProofGenUtil.csproj", "{729804D8-E3CE-4C06-AE15-F1D833AC3E06}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -203,18 +206,6 @@ Global {7A4B3721-D902-4265-94DB-CA8B0BB295B7}.Release|x64.Build.0 = Release|Any CPU {7A4B3721-D902-4265-94DB-CA8B0BB295B7}.Release|x86.ActiveCfg = Release|Any CPU {7A4B3721-D902-4265-94DB-CA8B0BB295B7}.Release|x86.Build.0 = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|x64.ActiveCfg = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|x64.Build.0 = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|x86.ActiveCfg = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Debug|x86.Build.0 = Debug|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|Any CPU.Build.0 = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|x64.ActiveCfg = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|x64.Build.0 = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|x86.ActiveCfg = Release|Any CPU - {CE190976-D727-4038-8D8A-8C5579E48CCF}.Release|x86.Build.0 = Release|Any CPU {51768B2E-FFF9-49F0-96D7-022250A4BEB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {51768B2E-FFF9-49F0-96D7-022250A4BEB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {51768B2E-FFF9-49F0-96D7-022250A4BEB4}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -299,6 +290,20 @@ Global {89B2CE6C-5BE5-49F2-9C88-8EADFDA9C549}.Release|x64.Build.0 = Release|Any CPU {89B2CE6C-5BE5-49F2-9C88-8EADFDA9C549}.Release|x86.ActiveCfg = Release|Any CPU {89B2CE6C-5BE5-49F2-9C88-8EADFDA9C549}.Release|x86.Build.0 = Release|Any CPU + {3059B8EB-DCE0-4468-9801-5D0A2A226472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3059B8EB-DCE0-4468-9801-5D0A2A226472}.Debug|Any CPU.Build.0 = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|x64.ActiveCfg = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|x64.Build.0 = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|x86.ActiveCfg = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Debug|x86.Build.0 = Debug|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|Any CPU.Build.0 = Release|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|x64.ActiveCfg = Release|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|x64.Build.0 = Release|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|x86.ActiveCfg = Release|Any CPU + {729804D8-E3CE-4C06-AE15-F1D833AC3E06}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {FDF25D38-01A2-4EAA-8A3E-6F8F7CD254D2} = {EC2B5ECD-B97D-43D6-86F8-28163710B717} diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 539a145b3..6aecce831 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -4108,13 +4108,24 @@ public List /*!*/ [Rep] public StmtList StructuredStmts; [Rep] public List /*!*/ Blocks; public Procedure Proc; - + + #region proofgen + public bool unreachableBlocksPruned; + #endregion + // Blocks before applying passification etc. // Both are used only when /inline is set. public List OriginalBlocks; + public List OriginalLocVars; + public readonly ISet AssertionChecksums = new HashSet(ChecksumComparer.Default); + + #region ProofGen + public IDictionary CoalescedBlocksToTarget; + public IDictionary ListCoalescedBlocks; + #endregion public sealed class ChecksumComparer : IEqualityComparer { @@ -4469,6 +4480,13 @@ public Implementation(IToken /*!*/ tok, StructuredStmts = structuredStmts; BigBlocksResolutionContext ctx = new BigBlocksResolutionContext(structuredStmts, errorHandler); Blocks = ctx.Blocks; + + #region proofgen + IDictionary mapFromImplementationToProofGenInfo = AstToCfgProofGenInfoManager.GetImplToProofGenInfo(); + AstToCfgProofGenInfo correspondingProofGenInfo = AstToCfgProofGenInfoManager.GetCurrentProofGenInfo(); + mapFromImplementationToProofGenInfo.Add(this, correspondingProofGenInfo); + #endregion + BlockPredecessorsComputed = false; scc = null; Attributes = kv; @@ -4986,6 +5004,11 @@ public void PruneUnreachableBlocks() { // This statement sequence will never reach the end, because of this "assume false" or "assert false". // Hence, it does not reach its successors. + + #region proofgen + unreachableBlocksPruned = true; + #endregion + b.TransferCmd = new ReturnCmd(b.TransferCmd.tok); goto NEXT_BLOCK; } diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs index 14534af60..37c1a54c1 100644 --- a/Source/Core/AbsyCmd.cs +++ b/Source/Core/AbsyCmd.cs @@ -436,6 +436,13 @@ public List /*!*/ Blocks { get { + /** Gaurav: TODO move this part to unique caller, too brittle here */ + #region proofgen + AstToCfgProofGenInfo proofGenInfo = new AstToCfgProofGenInfo(); + proofGenInfo.SetStmtList(stmtList); + AstToCfgProofGenInfoManager.SetCurrentProofGenInfo(proofGenInfo); + #endregion + Contract.Ensures(cce.NonNullElements(Contract.Result>())); if (blocks == null) { @@ -446,6 +453,16 @@ public List /*!*/ Blocks // Also, determine a good value for "prefix". CheckLegalLabels(stmtList, null, null); + #region proofgen + proofGenInfo.InstantiateVars(); + + foreach (BigBlock bb in stmtList.BigBlocks) + { + proofGenInfo.AddBigBlockBeforeNamingAnonymous(bb, true, null, null, BranchIndicator.NoGuard); + } + proofGenInfo.MakeOriginalAst(stmtList); + #endregion + // fill in names of anonymous blocks NameAnonymousBlocks(stmtList); @@ -455,7 +472,7 @@ public List /*!*/ Blocks if (this.errorHandler.count == startErrorCount) { // generate blocks from the big blocks - CreateBlocks(stmtList, null); + CreateBlocks(stmtList, null, proofGenInfo); } } @@ -694,8 +711,9 @@ void RecordSuccessors(StmtList stmtList, BigBlock successor) // If the enclosing context is a loop, then "runOffTheEndLabel" is the loop head label; // otherwise, it is null. - void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) + void CreateBlocks(StmtList stmtList, string runOffTheEndLabel, AstToCfgProofGenInfo proofGenInfo) { + Contract.Requires(stmtList != null); Contract.Requires(blocks != null); List cmdPrefixToApply = stmtList.PrefixCommands; @@ -723,6 +741,11 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) // this BigBlock has the very same components as a Block Contract.Assert(b.ec == null); Block block = new Block(b.tok, b.LabelName, theSimpleCmds, b.tc); + + #region proofgen + proofGenInfo.CreateBlockPairingWithHints(b, block, BranchIndicator.NoGuard, null); + #endregion + blocks.Add(block); } else if (b.ec == null) @@ -740,6 +763,10 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) Block block = new Block(b.tok, b.LabelName, theSimpleCmds, trCmd); blocks.Add(block); + + #region proofgen + proofGenInfo.CreateBlockPairingWithHints(b, block, BranchIndicator.NoGuard, null); + #endregion } else if (b.ec is BreakCmd) { @@ -747,6 +774,10 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) Contract.Assert(bcmd.BreakEnclosure != null); Block block = new Block(b.tok, b.LabelName, theSimpleCmds, GotoSuccessor(b.ec.tok, bcmd.BreakEnclosure)); blocks.Add(block); + + #region proofgen + proofGenInfo.CreateBlockPairingWithHints(b, block, BranchIndicator.NoGuard, null); + #endregion } else if (b.ec is WhileCmd) { @@ -777,6 +808,10 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) Block block = new Block(b.tok, b.LabelName, theSimpleCmds, new GotoCmd(wcmd.tok, new List {loopHeadLabel})); blocks.Add(block); + + #region proofgen + proofGenInfo.CreateBlockPairingWithHints(b, block, BranchIndicator.NoGuard, null); + #endregion // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; List ssHead = new List(); @@ -789,16 +824,42 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) new GotoCmd(wcmd.tok, new List {loopDoneLabel, loopBodyLabel})); blocks.Add(block); + #region proofgen + foreach (var tuple in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (tuple.Value == b) + { + BigBlock simpleCmdsRemoved = tuple.Key; + proofGenInfo.AddBigBlockToBlockPair(simpleCmdsRemoved, block); + proofGenInfo.AddBigBlockToHintsPair(simpleCmdsRemoved, (null, BranchIndicator.NoGuard)); + break; + } + } + #endregion + if (!bodyGuardTakenCareOf) { // LoopBody: assume guard; goto firstLoopBlock; block = new Block(wcmd.tok, loopBodyLabel, ssBody, new GotoCmd(wcmd.tok, new List {wcmd.Body.BigBlocks[0].LabelName})); blocks.Add(block); + + #region proofgen + BigBlock loopBodyBeginning = wcmd.Body.BigBlocks[0]; + proofGenInfo.CreateBlockPairingWithHints(loopBodyBeginning, block, BranchIndicator.GuardHolds, wcmd.Guard); + #endregion } + #region proofgen + foreach (BigBlock bb in wcmd.Body.BigBlocks) + { + proofGenInfo.AddBigBlockToLoopPair(bb, b); + } + proofGenInfo.AddBigBlockToHintsPair(wcmd.Body.BigBlocks.First(), (wcmd.Guard, BranchIndicator.GuardHolds)); + #endregion + // recurse to create the blocks for the loop body - CreateBlocks(wcmd.Body, loopHeadLabel); + CreateBlocks(wcmd.Body, loopHeadLabel, proofGenInfo); // LoopDone: assume !guard; goto loopSuccessor; TransferCmd trCmd; @@ -814,6 +875,38 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) block = new Block(wcmd.tok, loopDoneLabel, ssDone, trCmd); blocks.Add(block); + + #region proofgen + BigBlock loopDone = b.successorBigBlock; + BigBlock parent = stmtList.ParentBigBlock; + BigBlock parentCopy = null; + if (parent != null) + { + parentCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[parent]; + } + if (loopDone == null) + { + loopDone = new BigBlock(Token.NoToken, null, new List(), null, null);; + + //Set new loopDone Big block as a successor to 'b' in the original AST + b.successorBigBlock = loopDone; + + //Record loop done as a successor big block in the AST to 'b'. + proofGenInfo.RecordLoopDoneBlock(loopDone); + + //Add it to to all necessary mappings, in which it will be used when generating a proof. + proofGenInfo.AddBigBlockBeforeNamingAnonymous(loopDone, true, parent, parentCopy, BranchIndicator.NoGuard); + proofGenInfo.AddBigBlockToBlockPair(loopDone, block); + + if (!proofGenInfo.GetMappingCopyBigBlockToHints().ContainsKey(loopDone)) + { + proofGenInfo.AddBigBlockToHintsPair(loopDone, (wcmd.Guard, BranchIndicator.GuardFails)); + } + + BigBlock loopDoneCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[loopDone]; + proofGenInfo.AddToOriginalAst(loopDoneCopy); + } + #endregion } else { @@ -854,6 +947,17 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) Block block = new Block(b.tok, predLabel, predCmds, new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); blocks.Add(block); + + #region proofgen + proofGenInfo.CreateBlockPairingWithHints(b, block, BranchIndicator.NoGuard, null); + + proofGenInfo.AddBigBlockToHintsPair(ifcmd.thn.BigBlocks.First(), (ifcmd.Guard, BranchIndicator.GuardHolds)); + + if (ifcmd.elseBlock != null) + { + proofGenInfo.AddBigBlockToHintsPair(ifcmd.elseBlock.BigBlocks.First(), (ifcmd.Guard, BranchIndicator.GuardFails)); + } + #endregion if (!thenGuardTakenCareOf) { @@ -861,10 +965,15 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) block = new Block(ifcmd.tok, thenLabel, ssThen, new GotoCmd(ifcmd.tok, new List {ifcmd.thn.BigBlocks[0].LabelName})); blocks.Add(block); + + #region proofgen + BigBlock thnBranchBeginning = ifcmd.thn.BigBlocks[0]; + proofGenInfo.CreateBlockPairingWithHints(thnBranchBeginning, block, BranchIndicator.GuardHolds, ifcmd.Guard); + #endregion } // recurse to create the blocks for the then branch - CreateBlocks(ifcmd.thn, n == 0 ? runOffTheEndLabel : null); + CreateBlocks(ifcmd.thn, n == 0 ? runOffTheEndLabel : null, proofGenInfo); if (ifcmd.elseBlock != null) { @@ -875,10 +984,14 @@ void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) block = new Block(ifcmd.tok, elseLabel, ssElse, new GotoCmd(ifcmd.tok, new List {ifcmd.elseBlock.BigBlocks[0].LabelName})); blocks.Add(block); + #region proofgen + BigBlock elseBranchBeginning = ifcmd.elseBlock.BigBlocks[0]; + proofGenInfo.CreateBlockPairingWithHints(elseBranchBeginning, block, BranchIndicator.GuardFails, ifcmd.Guard); + #endregion } // recurse to create the blocks for the else branch - CreateBlocks(ifcmd.elseBlock, n == 0 ? runOffTheEndLabel : null); + CreateBlocks(ifcmd.elseBlock, n == 0 ? runOffTheEndLabel : null, proofGenInfo); } else if (ifcmd.elseIf != null) { diff --git a/Source/Core/AstToCfgProofGenInfo.cs b/Source/Core/AstToCfgProofGenInfo.cs new file mode 100644 index 000000000..ba714cc95 --- /dev/null +++ b/Source/Core/AstToCfgProofGenInfo.cs @@ -0,0 +1,655 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices.ComTypes; +using ProofGenUtil; + +using Core; + +namespace Microsoft.Boogie +{ + public enum BranchIndicator + { + NoGuard, + GuardHolds, + GuardFails, + LoopBody + } + + /// + /// A class used to collect information needed for the generation of the AST-to-CFG part of the validation certificate for a Boogie procedure. + /// + public class AstToCfgProofGenInfo + { + /// The original object, which contains the original list of objects, which is Boogie's internal AST representation. + private StmtList stmtList; + + private List _unoptimizedLocalVars; + + public List UnoptimizedLocalVars + { + get => _unoptimizedLocalVars; + set => _unoptimizedLocalVars = value; + } + + private bool _eliminatedDeadVars = false; + + public bool EliminatedDeadVars + { + get => _eliminatedDeadVars; + set => _eliminatedDeadVars = value; + } + + private IList unoptimizedBlocks; + + private List newVarsAfterDesugaringinCFGBlocks; + + /// A list of copies of the objects in . + private IList originalAst; + + /// A list containing the same objects, which are in with all nested BigBlocks explicitly added. + private IList bigblocksBeforeNamingAnonymous; + + private IDictionary mappingOrigBlocktoUnoptimizedCopy; + + // Dictionaries used to keep track of relations between different kinds of blocks. + private IDictionary mappingOrigBigBlockToOrigBlock; + private IDictionary mappingCopyBigblockToOrigBigblock; + private IDictionary mappingOrigBigblockToCopyBigblock; + + /** A dictionary mapping a 'LoopHead' BigBlock to a tuple of two BigBlocks. + * A LoopHead is a special BigBlock that is a copy, has an empty list of simpleCmds and a as a . + * The key is the LoopHead. + * The value is the original BigBlock the LoopHead Big Block was created from. + */ + private IDictionary mappingLoopHeadBigBlocktoOrigLoopBigBlock; + + // A marker set to false indicates that a BigBlock is nested in another BigBlock. + private IDictionary mappingCopyBigBlockToMarker; + + /** The first hint is a guard (possibly null) that needs to be taken care of in the global lemma that's to be generated. + * The second hint is a BranchIndicator that indicates how that needs to be done. */ + private IDictionary mappingBigBlockToHints; + + // Map a BigBlock (copy) to the unique integer label (index) of its corresponding Term object (Term object definition is in InnerAst.cs). + private IDictionary mappingCopyBigBlockToIndex; + + // Map a BigBlock (original) to the Loop BigBlock (original) it's nested into. + private IDictionary mappingBigBlockToOrigLoopBigBlock; + + // List of BigBlocks added after a loop if that loop is the end of the procedure or if that loop has no successors. + // I've never encountered an instance where this list contains more than one element, i.e, this doesn't need to be a list in our setting. + // However, this depends on when successor Big Blocks are computed. + // If successor BigBlocks were to be computed after the check on line 886 in AbsyCmd.cs is done, + // then this list will contain a BigBlock corresponding to every loop. + private IList loopEndingBlocks; + + private List newVarsFromDesugaring; + + private bool optimizationsDone; + + public void InstantiateVars() + { + originalAst = new List(); + bigblocksBeforeNamingAnonymous = new List(); + + mappingCopyBigBlockToMarker = new Dictionary(); + + mappingOrigBigBlockToOrigBlock = new Dictionary(); + mappingCopyBigblockToOrigBigblock = new Dictionary(); + mappingOrigBigblockToCopyBigblock = new Dictionary(); + mappingLoopHeadBigBlocktoOrigLoopBigBlock = new Dictionary(); + + mappingBigBlockToHints = new Dictionary(); + mappingCopyBigBlockToIndex = new Dictionary(); + + mappingBigBlockToOrigLoopBigBlock = new Dictionary(); + newVarsFromDesugaring = new List(); + + loopEndingBlocks = new List(); + } + + public void SetStmtList(StmtList stmtList) + { + this.stmtList = stmtList; + } + + public StmtList GetStmtList() + { + return stmtList; + } + + public void SetUnoptimizedBlocks(IList blocks) + { + unoptimizedBlocks = blocks; + } + + public void SetOptimizationsFlag() + { + optimizationsDone = true; + } + + public bool GetOptimizationsFlag() + { + return optimizationsDone; + } + + public IList GetUnoptimizedBlocks() + { + return unoptimizedBlocks; + } + + public void SetNewVarsCFG(List newVars) + { + newVarsAfterDesugaringinCFGBlocks = newVars; + } + + public List GetNewVarsCFG() + { + return newVarsAfterDesugaringinCFGBlocks; + } + + public IList GetBigBlocks() + { + return bigblocksBeforeNamingAnonymous; + } + + public IList GetOriginalAst() + { + return originalAst; + } + + public IDictionary GetMappingOrigBlockToUnoptimizedCopy() + { + return mappingOrigBlocktoUnoptimizedCopy; + } + + public IDictionary GetMappingCopyBigBlockToMarker() + { + return mappingCopyBigBlockToMarker; + } + + public IDictionary GetMappingOrigBigBlockToOrigBlock() + { + return mappingOrigBigBlockToOrigBlock; + } + + public IDictionary GetMappingCopyBigblockToOrigBigblock() + { + return mappingCopyBigblockToOrigBigblock; + } + + public IDictionary GetMappingOrigBigblockToCopyBigblock() + { + return mappingOrigBigblockToCopyBigblock; + } + + public IDictionary GetMappingLoopHeadBigBlocktoOrigLoopBigBlock() + { + return mappingLoopHeadBigBlocktoOrigLoopBigBlock; + } + + public IDictionary GetMappingCopyBigBlockToHints() + { + return mappingBigBlockToHints; + } + + public IDictionary GetMappingBigBlockToLoopBigBlock() + { + return mappingBigBlockToOrigLoopBigBlock; + } + + public IDictionary GetMappingCopyBigBlockToIndex() + { + return mappingCopyBigBlockToIndex; + } + + public IList GetloopEndingBlocks() + { + return loopEndingBlocks; + } + + public List GetNewVarsFromDesugaring() + { + return newVarsFromDesugaring; + } + + public void MakeOriginalAst(StmtList stmtList) + { + foreach (var bb in stmtList.BigBlocks) + { + BigBlock copy = mappingOrigBigblockToCopyBigblock[bb]; + originalAst.Add(copy); + } + } + + public void AddToOriginalAst(BigBlock b) + { + originalAst.Add(b); + } + + /// + /// Add a BigBlock to the list and recursively add its nested BigBlocks. + /// + public void AddBigBlockBeforeNamingAnonymous(BigBlock b, bool marker, BigBlock parentBigBlockOrig, + BigBlock parentBigBlockCopy, BranchIndicator branchIndicator) + { + BigBlock copy = null; + switch (branchIndicator) + { + case BranchIndicator.NoGuard: + + //If the elseBlock field of an is null, initialize it to a with one 'empty' BigBlock. + FillEmptyElseBranches(b); + + copy = CopyBigBlock(b); + mappingCopyBigblockToOrigBigblock.Add(copy, b); + mappingOrigBigblockToCopyBigblock.Add(b, copy); + mappingCopyBigBlockToMarker.Add(copy, marker); + bigblocksBeforeNamingAnonymous.Add(copy); + break; + + case BranchIndicator.GuardHolds: + { + IfCmd ifcmd = (IfCmd) parentBigBlockOrig.ec; + int position = ifcmd.thn.BigBlocks.IndexOf(b); + + IfCmd ifcmdCopy = (IfCmd) parentBigBlockCopy.ec; + BigBlock currentBigBlockCopy = ifcmdCopy.thn.BigBlocks[position]; + + mappingCopyBigblockToOrigBigblock.Add(currentBigBlockCopy, b); + mappingOrigBigblockToCopyBigblock.Add(b, currentBigBlockCopy); + mappingCopyBigBlockToMarker.Add(currentBigBlockCopy, marker); + bigblocksBeforeNamingAnonymous.Add(currentBigBlockCopy); + + copy = currentBigBlockCopy; + break; + } + case BranchIndicator.GuardFails: + { + IfCmd ifcmd = (IfCmd) parentBigBlockOrig.ec; + int position = ifcmd.elseBlock.BigBlocks.IndexOf(b); + + IfCmd ifcmdCopy = (IfCmd) parentBigBlockCopy.ec; + BigBlock currentBigBlockCopy = ifcmdCopy.elseBlock.BigBlocks[position]; + + mappingCopyBigblockToOrigBigblock.Add(currentBigBlockCopy, b); + mappingOrigBigblockToCopyBigblock.Add(b, currentBigBlockCopy); + mappingCopyBigBlockToMarker.Add(currentBigBlockCopy, marker); + bigblocksBeforeNamingAnonymous.Add(currentBigBlockCopy); + + copy = currentBigBlockCopy; + break; + } + case BranchIndicator.LoopBody: + { + WhileCmd wcmd = (WhileCmd) parentBigBlockOrig.ec; + int position = wcmd.Body.BigBlocks.IndexOf(b); + + WhileCmd wcmdCopy = (WhileCmd) parentBigBlockCopy.ec; + BigBlock currentBigBlockCopy = wcmdCopy.Body.BigBlocks[position]; + + mappingCopyBigblockToOrigBigblock.Add(currentBigBlockCopy, b); + mappingOrigBigblockToCopyBigblock.Add(b, currentBigBlockCopy); + mappingCopyBigBlockToMarker.Add(currentBigBlockCopy, marker); + bigblocksBeforeNamingAnonymous.Add(currentBigBlockCopy); + + copy = currentBigBlockCopy; + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(branchIndicator), branchIndicator, + "Unknown or Uninitialized Branch Indicator"); + } + + if (b.ec is IfCmd) + { + IfCmd ifcmd = (IfCmd) b.ec; + foreach (BigBlock bb in ifcmd.thn.BigBlocks) + { + AddBigBlockBeforeNamingAnonymous(bb, false, b, copy, BranchIndicator.GuardHolds); + } + + if (ifcmd.elseBlock != null) + { + foreach (BigBlock bb in ifcmd.elseBlock.BigBlocks) + { + AddBigBlockBeforeNamingAnonymous(bb, false, b, copy, BranchIndicator.GuardFails); + } + } + } + else if (b.ec is WhileCmd) + { + BigBlock simpleCmdsRemoved = new BigBlock(copy.tok, copy.LabelName, new List(), copy.ec, copy.tc); + + /* + * This is a special case where the newly created LoopHead BigBlock is mapped to itself. + * One could alternatively store the 'simpleCmdsRemoved' BigBlocks in a separate list but + * then one would need an additional if-statement or a for-loop to access that list during the creation of continuations + * and later on proofs. + */ + mappingCopyBigblockToOrigBigblock.Add(simpleCmdsRemoved, simpleCmdsRemoved); + mappingOrigBigblockToCopyBigblock.Add(simpleCmdsRemoved, simpleCmdsRemoved); + + mappingLoopHeadBigBlocktoOrigLoopBigBlock.Add(simpleCmdsRemoved, b); + bigblocksBeforeNamingAnonymous.Add(simpleCmdsRemoved); + mappingCopyBigBlockToMarker.Add(simpleCmdsRemoved, false); + + WhileCmd whilecmd = (WhileCmd) b.ec; + foreach (BigBlock bb in whilecmd.Body.BigBlocks) + { + AddBigBlockBeforeNamingAnonymous(bb, false, b, copy, BranchIndicator.LoopBody); + } + } + } + + public void AddBigBlockToLoopPair(BigBlock b0, BigBlock b1) + { + mappingBigBlockToOrigLoopBigBlock.Add(b0, b1); + + if (b0.ec is IfCmd ifcmd) + { + foreach (var thenBb in ifcmd.thn.BigBlocks) + { + AddBigBlockToLoopPair(thenBb, b1); + } + + if (ifcmd.elseBlock != null) + { + foreach (var elseBb in ifcmd.elseBlock.BigBlocks) + { + AddBigBlockToLoopPair(elseBb, b1); + } + } + } + } + + public void AddBigBlockToBlockPair(BigBlock b0, Block b1) + { + mappingOrigBigBlockToOrigBlock.Add(b0, b1); + } + + public void AddBigBlockToHintsPair(BigBlock b, (Expr, BranchIndicator) tuple) + { + mappingBigBlockToHints.Add(b, (tuple.Item1, tuple.Item2)); + } + + public void AddBigBlockToIndexPair(BigBlock b, int index) + { + mappingCopyBigBlockToIndex.Add(b, index); + } + + /// + /// Make a copy of a BigBlock. The objects in its simpleCmds field and in the simpleCmds fields of its nested BigBlocks are not copied. + /// + public BigBlock CopyBigBlock(BigBlock b) + { + StructuredCmd ecCopy = null; + if (b.ec is IfCmd) + { + IfCmd @if = (IfCmd) b.ec; + + IList thenCopy = new List(); + foreach (BigBlock bb in @if.thn.BigBlocks) + { + thenCopy.Add(CopyBigBlock(bb)); + } + + StmtList thenCopyStmts = new StmtList(thenCopy, @if.thn.EndCurly); + + IList elseCopy = new List(); + StmtList elseCopyStmts; + if (@if.elseBlock == null) + { + elseCopyStmts = new StmtList(elseCopy, new Token()); + } + else + { + foreach (BigBlock bb in @if.elseBlock.BigBlocks) + { + elseCopy.Add(CopyBigBlock(bb)); + } + + elseCopyStmts = new StmtList(elseCopy, @if.elseBlock.EndCurly); + } + + ecCopy = new IfCmd(@if.tok, @if.Guard, thenCopyStmts, @if.elseIf, elseCopyStmts); + } + else if (b.ec is WhileCmd) + { + WhileCmd @while = (WhileCmd) b.ec; + + IList bodyCopy = new List(); + foreach (BigBlock bb in @while.Body.BigBlocks) + { + bodyCopy.Add(CopyBigBlock(bb)); + } + + StmtList bodyCopyStmts = new StmtList(bodyCopy, @while.Body.EndCurly); + ecCopy = new WhileCmd(@while.tok, @while.Guard, @while.Invariants, bodyCopyStmts); + } + else + { + ecCopy = b.ec; + } + + var copyCmds = new List(); + var newVarsFromDesugaring = new List(); + foreach (var cmd in b.simpleCmds) + { + copyCmds.Add(cmd); + } + + GetNewVarsFromDesugaring().AddRange(newVarsFromDesugaring); + String nameCopy = b.LabelName != null ? "''" + b.LabelName + "''" : null; + return new BigBlock(b.tok, nameCopy, copyCmds, ecCopy, b.tc); + } + + /// + /// If the elseBlock field of an is null, initialize it to a with one 'empty' BigBlock. + /// This is done, so that the AST-to-CFG part of the proof generation can properly generate a proof. + /// + private void FillEmptyElseBranches(BigBlock b) + { + if (b.ec is IfCmd ifcmd) + { + foreach (var thenBb in ifcmd.thn.BigBlocks) + { + FillEmptyElseBranches(thenBb); + } + + if (ifcmd.elseIf == null && ifcmd.elseBlock == null) + { + IList emptyElseBranch = new List(); + BigBlock emptyElseBranchBigBlock = new BigBlock(Token.NoToken, null, new List(), null, null); + emptyElseBranch.Add(emptyElseBranchBigBlock); + + emptyElseBranchBigBlock.successorBigBlock = b.successorBigBlock; + + ifcmd.elseBlock = new StmtList(emptyElseBranch, Token.NoToken); + } + + if (ifcmd.elseBlock != null) + { + foreach (var elseBb in ifcmd.elseBlock.BigBlocks) + { + FillEmptyElseBranches(elseBb); + } + } + } + else if (b.ec is WhileCmd wcmd) + { + foreach (var bodyBb in wcmd.Body.BigBlocks) + { + FillEmptyElseBranches(bodyBb); + } + } + } + + public void RecordLoopDoneBlock(BigBlock loopDoneBigBlock) + { + loopEndingBlocks.Add(loopDoneBigBlock); + } + + private bool InBigBlock(BigBlock bigBlockToBeChecked, BigBlock possibleContainerBigBlock) + { + if (possibleContainerBigBlock.ec is WhileCmd wcmd) + { + if (wcmd.Body.BigBlocks.Contains(bigBlockToBeChecked)) + { + return true; + } + + foreach (var bodyBb in wcmd.Body.BigBlocks) + { + if (InBigBlock(bigBlockToBeChecked, bodyBb)) + { + return true; + } + } + } + else if (possibleContainerBigBlock.ec is IfCmd ifcmd) + { + if (ifcmd.thn.BigBlocks.Contains(bigBlockToBeChecked) || + ifcmd.elseBlock.BigBlocks.Contains(bigBlockToBeChecked)) + { + return true; + } + + foreach (var thenBb in ifcmd.thn.BigBlocks) + { + if (InBigBlock(bigBlockToBeChecked, thenBb)) + { + return true; + } + } + + if (ifcmd.elseBlock != null) + { + foreach (var elseBb in ifcmd.elseBlock.BigBlocks) + { + if (InBigBlock(bigBlockToBeChecked, elseBb)) + { + return true; + } + } + } + } + + return false; + } + + private static readonly MethodInfo CloneMethod = + typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); + + public IList CopyBlocks( + IList blocks, + Dictionary> predecessorMap, + bool desugarCalls, + Predicate deepCopyCmdPred, + out List newVarsFromDesugaring) + { + //shallow copy of each block + update edges to copied blocks + deep copy of cmds if specified + //TODO: need to make sure this is sufficient + IDictionary oldToNewBlock = new Dictionary(); + + IList copyBlocks = new List(); + + newVarsFromDesugaring = new List(); + + //don't copy variables, since proof generation assumes sharing of variable identities + Func copyCmd = cmd => + deepCopyCmdPred(cmd) + ? ProofGenUtil.ObjectExtensions.Copy(cmd, t => t != typeof(IdentifierExpr) && t != typeof(TypeVariable) && t != typeof(QKeyValue)) + : (Cmd) CloneMethod.Invoke(cmd, null); + + foreach (var b in blocks) + { + var copyCmds = new List(); + foreach (var cmd in b.Cmds) + if (cmd is SugaredCmd sugaredCmd && desugarCalls) + { + var stateCmd = sugaredCmd.Desugaring as StateCmd; + newVarsFromDesugaring.AddRange(stateCmd.Locals); + foreach (var desugaredCmd in stateCmd.Cmds) copyCmds.Add(copyCmd(desugaredCmd)); + } + else + { + copyCmds.Add(copyCmd(cmd)); + } + + var copyBlock = (Block) CloneMethod.Invoke(b, null); + copyBlock.Cmds = copyCmds; + copyBlock.Predecessors = predecessorMap[b]; + + copyBlocks.Add(copyBlock); + oldToNewBlock.Add(b, copyBlock); + } + + //make sure block references are updated accordingly + foreach (var copyBlock in copyBlocks) + { + if (copyBlock.TransferCmd is GotoCmd gtc) + { + var newSuccessors = gtc.labelTargets.Select(succ => oldToNewBlock[succ]).ToList(); + var gotoCmdCopy = (GotoCmd) CloneMethod.Invoke(gtc, null); + gotoCmdCopy.labelTargets = newSuccessors; + copyBlock.TransferCmd = gotoCmdCopy; + } + else + { + copyBlock.TransferCmd = (TransferCmd) CloneMethod.Invoke(copyBlock.TransferCmd, null); + } + + if (copyBlock.Predecessors != null) + copyBlock.Predecessors = copyBlock.Predecessors.Select(succ => oldToNewBlock[succ]).ToList(); + } + + mappingOrigBlocktoUnoptimizedCopy = oldToNewBlock; + return copyBlocks; + } + + /// + /// Copy from . We compute predecessors ourselves, since at certain points the + /// predecessors property for blocks is not in-sync with the CFG (and we do not want to adjust the Boogie + /// objects). + /// + public Dictionary> ComputePredecessors(IEnumerable blocks) + { + var predecessors = new Dictionary>(); + foreach (var b in blocks) predecessors.Add(b, new List()); + + foreach (var b in blocks) + { + var gtc = b.TransferCmd as GotoCmd; + if (gtc != null) + { + Contract.Assert(gtc.labelTargets != null); + foreach (var /*!*/ dest in gtc.labelTargets) + { + Contract.Assert(dest != null); + predecessors[dest].Add(b); + } + } + } + + return predecessors; + } + + public void CreateBlockPairingWithHints(BigBlock bigblock, Block block, BranchIndicator branchIndicator, Expr guard) + { + if (!GetMappingOrigBigBlockToOrigBlock().ContainsKey(bigblock)) + { + AddBigBlockToBlockPair(bigblock, block); + + if (!GetMappingCopyBigBlockToHints().ContainsKey(bigblock)) + { + AddBigBlockToHintsPair(bigblock, (guard, branchIndicator)); + } + } + } + + } +} \ No newline at end of file diff --git a/Source/Core/AstToCfgProofGenInfoManager.cs b/Source/Core/AstToCfgProofGenInfoManager.cs new file mode 100644 index 000000000..3bfe6618f --- /dev/null +++ b/Source/Core/AstToCfgProofGenInfoManager.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; + +namespace Microsoft.Boogie +{ + public static class AstToCfgProofGenInfoManager + { + private static IDictionary implToProofGenInfo; + private static AstToCfgProofGenInfo currentProofGenInfo; + + public static IDictionary GetImplToProofGenInfo() + { + return implToProofGenInfo ??= new Dictionary(); + } + + public static AstToCfgProofGenInfo GetCurrentProofGenInfo() + { + return currentProofGenInfo ??= new AstToCfgProofGenInfo(); + } + + public static void SetCurrentProofGenInfo(AstToCfgProofGenInfo proofGenInfo) + { + currentProofGenInfo = proofGenInfo; + } + } +} \ No newline at end of file diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index 4ee03d1da..d4d5c435e 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -1044,7 +1044,24 @@ public enum TypeEncoding public string ProofOutputDir = null; public bool OnlyCheckProofGenSupport = false; public bool DontStoreProofGenFiles = false; - public bool GenerateIsaProgNoProofs = false; + + /* If use id-based lemma naming, then whenever lemmas are specific to an entity that is represented using an id (i.e., a natural number), + then the lemma name is uniquely determined by that id. For example, if variables are represented using natural numbers, + then the membership lemmas for those variables are uniquely determined by the corresponding natural number. */ + public bool UseIdBasedLemmaNaming = false; + + /* + * 0: disabled -> proofs are generated + * 1: partially enabled -> only AST program is generated but with membership lemmas + * 2: enabled -> only AST program is generated + */ + public int GenerateIsaProgNoProofs = 0; + + public bool OnlyGenerateInitialProgramIsa() + { + return GenerateIsaProgNoProofs != 0; + } + public bool DesugarMaps = false; #endregion @@ -1820,14 +1837,18 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command return true; - case "isaProgNoProofs": + case "useIdBasedLemmaNaming": if (ps.ConfirmArgumentCount(0)) { - GenerateIsaProgNoProofs = true; + UseIdBasedLemmaNaming = true; } - + return true; + case "isaProgNoProofs": + ps.GetNumericArgument(ref GenerateIsaProgNoProofs, 3); + return true; + case "desugarMaps": if (ps.ConfirmArgumentCount(0)) { diff --git a/Source/Core/Core.csproj b/Source/Core/Core.csproj index 74241e141..13b6de5c1 100644 --- a/Source/Core/Core.csproj +++ b/Source/Core/Core.csproj @@ -9,6 +9,7 @@ + diff --git a/Source/Core/DeadVarElim.cs b/Source/Core/DeadVarElim.cs index 11a542ef6..a79d3703d 100644 --- a/Source/Core/DeadVarElim.cs +++ b/Source/Core/DeadVarElim.cs @@ -4,6 +4,7 @@ using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; + namespace Microsoft.Boogie { public class UnusedVarEliminator : VariableCollector @@ -478,14 +479,21 @@ public static void CoalesceBlocks(Program program) return multiPredBlocks; } + + public override Implementation VisitImplementation(Implementation impl) { + + #region proofgen + IDictionary CoalescedBlocksToTarget = new Dictionary(); + IDictionary ListCoalescedBlocks = new Dictionary(); + #endregion + //Contract.Requires(impl != null); Contract.Ensures(Contract.Result() != null); //Console.WriteLine("Procedure {0}", impl.Name); //Console.WriteLine("Initial number of blocks = {0}", impl.Blocks.Count); - HashSet multiPredBlocks = ComputeMultiPredecessorBlocks(impl); Contract.Assert(cce.NonNullElements(multiPredBlocks)); HashSet visitedBlocks = new HashSet(); @@ -524,8 +532,32 @@ public override Implementation VisitImplementation(Implementation impl) { Block /*!*/ succ = cce.NonNull(gotoCmd.labelTargets[0]); + if (!multiPredBlocks.Contains(succ)) { + #region proofgen + if (!ListCoalescedBlocks.ContainsKey(b)) + { + List newList = new List(); + newList.Add(b); + BlockCoalescingInfo coalescedHead = new BlockCoalescingInfo(newList, 0); + ListCoalescedBlocks.Add(b, coalescedHead); + } + ListCoalescedBlocks[b].coalescedBlocks.Add(succ); + + BlockCoalescingInfo curr = new BlockCoalescingInfo(ListCoalescedBlocks[b].coalescedBlocks, ListCoalescedBlocks[b].coalescedBlocks.Count - 1); + ListCoalescedBlocks.Add(succ, curr); + + if (!CoalescedBlocksToTarget.ContainsKey(succ)) + { + CoalescedBlocksToTarget.Add(succ, b); + } + if (!CoalescedBlocksToTarget.ContainsKey(b)) + { + CoalescedBlocksToTarget.Add(b, b); + } + #endregion + foreach (Cmd /*!*/ cmd in succ.Cmds) { Contract.Assert(cmd != null); @@ -579,6 +611,11 @@ public override Implementation VisitImplementation(Implementation impl) } } + #region proofgen + impl.CoalescedBlocksToTarget = CoalescedBlocksToTarget; + impl.ListCoalescedBlocks = ListCoalescedBlocks; + #endregion + // Console.WriteLine("Final number of blocks = {0}", impl.Blocks.Count); return impl; } @@ -2316,4 +2353,18 @@ public override Block VisitBlock(Block node) return base.VisitBlock(node); } } + #region proofgen + public class BlockCoalescingInfo + { + + public List coalescedBlocks; + public int idx; + + public BlockCoalescingInfo(List coalescedBlocks, int idx) + { + this.coalescedBlocks = coalescedBlocks; + this.idx = idx; + } + } + #endregion } \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 9aec74302..d39e80c4c 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -480,7 +480,7 @@ public static bool ProcessProgram(Program program, string bplFileName, string pr if (CommandLineOptions.Clo.PrintFile != null) { PrintBplFile(CommandLineOptions.Clo.PrintFile, program, false, true, CommandLineOptions.Clo.PrettyPrint); } - + PipelineOutcome oc = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); if (oc != PipelineOutcome.ResolvedAndTypeChecked) { return true; @@ -507,6 +507,41 @@ public static bool ProcessProgram(Program program, string bplFileName, string pr CoalesceBlocks(program); Inline(program); + + #region proofgen + foreach (var tuple in AstToCfgProofGenInfoManager.GetImplToProofGenInfo()) + { + Implementation impl = tuple.Key; + AstToCfgProofGenInfo proofGenInfo = tuple.Value; + + IList unoptimizedBlocksCopies = proofGenInfo.GetUnoptimizedBlocks(); + IList optimizedBlocksOriginal = impl.Blocks; + // FIXME: optimizedBlocksOriginal does not contain desugared commands while optimized does --> too many false positives + + int totalNumberOfCommandsA = 0; + int totalNumberOfCommandsB = 0; + + foreach (var block in unoptimizedBlocksCopies) + { + totalNumberOfCommandsA += block.cmds.Count; + } + + foreach (var block in optimizedBlocksOriginal) + { + totalNumberOfCommandsB += block.cmds.Count; + } + + bool eliminatedDeadVars = impl.LocVars.Count != proofGenInfo.UnoptimizedLocalVars.Count; + proofGenInfo.EliminatedDeadVars = eliminatedDeadVars; + if (impl.unreachableBlocksPruned || + totalNumberOfCommandsA != totalNumberOfCommandsB || + unoptimizedBlocksCopies.Count != optimizedBlocksOriginal.Count || + eliminatedDeadVars) + { + proofGenInfo.SetOptimizationsFlag(); + } + } + #endregion #region check if proof gen potentially supports input program @@ -814,6 +849,21 @@ public static PipelineOutcome ResolveAndTypecheck(Program program, string bplFil CollectModSets(program); + #region proofgen + foreach (var tuple in AstToCfgProofGenInfoManager.GetImplToProofGenInfo()) + { + Implementation impl = tuple.Key; + AstToCfgProofGenInfo proofGenInfo = tuple.Value; + + proofGenInfo.UnoptimizedLocalVars = new List(impl.LocVars); + + var predecessorMap = proofGenInfo.ComputePredecessors(impl.Blocks); + var unoptimizedBlockCopies = proofGenInfo.CopyBlocks(impl.Blocks , predecessorMap, true, cmd => false, out var newVarsAfterDesugaring); + proofGenInfo.SetUnoptimizedBlocks(unoptimizedBlockCopies); + proofGenInfo.SetNewVarsCFG(newVarsAfterDesugaring); + } + #endregion + civlTypeChecker = new CivlTypeChecker(program); civlTypeChecker.TypeCheck(); if (civlTypeChecker.checkingContext.ErrorCount != 0) diff --git a/Source/ProofGenUtil/ObjectExtensions.cs b/Source/ProofGenUtil/ObjectExtensions.cs new file mode 100644 index 000000000..84281091f --- /dev/null +++ b/Source/ProofGenUtil/ObjectExtensions.cs @@ -0,0 +1,158 @@ +/** + * +Deep copy utility method taken from https://github.com/Burtsev-Alexey/net-object-deep-copy and adjusted + +Source code is released under the MIT license. + +The MIT License (MIT) +Copyright (c) 2014 Burtsev Alexey + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using ProofGenUtil.ArrayExtensions; + +namespace ProofGenUtil +{ + public static class ObjectExtensions + { + private static readonly MethodInfo CloneMethod = + typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); + + public static bool IsPrimitive(this Type type) + { + if (type == typeof(string)) return true; + return type.IsValueType & type.IsPrimitive; + } + + /// + /// Do not deeply copy objects for which evaluates to false + /// + public static object Copy(this object originalObject, Predicate deepCopyPred) + { + return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer()), + deepCopyPred); + } + + private static object InternalCopy(object originalObject, IDictionary visited, + Predicate deepCopyPred) + { + if (originalObject == null) return null; + var typeToReflect = originalObject.GetType(); + if (IsPrimitive(typeToReflect) || !deepCopyPred(typeToReflect)) return originalObject; + if (visited.ContainsKey(originalObject)) return visited[originalObject]; + if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null; + var cloneObject = CloneMethod.Invoke(originalObject, null); + if (typeToReflect.IsArray) + { + var arrayType = typeToReflect.GetElementType(); + if (IsPrimitive(arrayType) == false && deepCopyPred(arrayType)) + { + var clonedArray = (Array) cloneObject; + clonedArray.ForEach((array, indices) => + array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited, deepCopyPred), indices)); + } + } + + visited.Add(originalObject, cloneObject); + CopyFields(originalObject, visited, cloneObject, typeToReflect, deepCopyPred); + RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect, deepCopyPred); + return cloneObject; + } + + private static void RecursiveCopyBaseTypePrivateFields(object originalObject, + IDictionary visited, object cloneObject, Type typeToReflect, Predicate deepCopyPred) + { + if (typeToReflect.BaseType != null) + { + RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType, + deepCopyPred); + CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, deepCopyPred, + BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate); + } + } + + private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, + Type typeToReflect, Predicate deepCopyPred, + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.FlattenHierarchy, Func filter = null) + { + foreach (var fieldInfo in typeToReflect.GetFields(bindingFlags)) + { + if (filter != null && filter(fieldInfo) == false) continue; + if (IsPrimitive(fieldInfo.FieldType) || !deepCopyPred(fieldInfo.FieldType)) continue; + var originalFieldValue = fieldInfo.GetValue(originalObject); + var clonedFieldValue = InternalCopy(originalFieldValue, visited, deepCopyPred); + fieldInfo.SetValue(cloneObject, clonedFieldValue); + } + } + + public static T Copy(this T original, Predicate deepCopyPred) + { + return (T) Copy((object) original, deepCopyPred); + } + } + + public class ReferenceEqualityComparer : EqualityComparer + { + public override bool Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + public override int GetHashCode(object obj) + { + if (obj == null) return 0; + return obj.GetHashCode(); + } + } + + namespace ArrayExtensions + { + public static class ArrayExtensions + { + public static void ForEach(this Array array, Action action) + { + if (array.LongLength == 0) return; + var walker = new ArrayTraverse(array); + do + { + action(array, walker.Position); + } while (walker.Step()); + } + } + + internal class ArrayTraverse + { + private readonly int[] maxLengths; + public int[] Position; + + public ArrayTraverse(Array array) + { + maxLengths = new int[array.Rank]; + for (var i = 0; i < array.Rank; ++i) maxLengths[i] = array.GetLength(i) - 1; + Position = new int[array.Rank]; + } + + public bool Step() + { + for (var i = 0; i < Position.Length; ++i) + if (Position[i] < maxLengths[i]) + { + Position[i]++; + for (var j = 0; j < i; j++) Position[j] = 0; + return true; + } + + return false; + } + } + } +} \ No newline at end of file diff --git a/Source/ProofGenUtil/ProofGenUtil.csproj b/Source/ProofGenUtil/ProofGenUtil.csproj new file mode 100644 index 000000000..eb2460e91 --- /dev/null +++ b/Source/ProofGenUtil/ProofGenUtil.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Source/ProofGeneration/ASTRepr/ASTRepr.cs b/Source/ProofGeneration/ASTRepr/ASTRepr.cs new file mode 100644 index 000000000..1f195bfca --- /dev/null +++ b/Source/ProofGeneration/ASTRepr/ASTRepr.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.Boogie; + +namespace ProofGeneration.ASTRepresentation +{ + public class ASTRepr + { + private readonly IList bigblocks; + + public ASTRepr(IList bigblocks) + { + this.bigblocks = bigblocks; + } + + public int NumOfBlocks() + { + return bigblocks.Count; + } + + public bool ContainsBlock(BigBlock b) + { + return bigblocks.Contains(b); + } + + public int GetUniqueIntLabel(BigBlock b) + { + for (var i = 0; i < bigblocks.Count; i++) + { + if (bigblocks[i] == b) + { + return i; + } + } + + return -1; + } + + public TermList GetAstAsTermList(AstToCfgProofGenInfo proofGenInfo) + { + IList terms = new List(); + for (var i = 0; i < bigblocks.Count; i++) + { + if (proofGenInfo.GetMappingCopyBigBlockToMarker()[bigblocks[i]]) + { + Term bb = IsaCommonTerms.TermIdentFromName("bigblock_" + i); + terms.Add(bb); + } + } + + return new TermList(terms); + } + + public List GetMainContinuations(AstToCfgProofGenInfo proofGenInfo) + { + List strings = new List(); + for (var i = 0; i < bigblocks.Count; i++) + { + if (proofGenInfo.GetMappingCopyBigBlockToMarker()[bigblocks[i]]) + { + string cont = "cont_" + i + "_def"; + strings.Add(cont); + } + } + + return strings; + } + + public IEnumerable GetBlocksForwards() + { + for (var i = 0; i < bigblocks.Count; i++) + yield return bigblocks[i]; + } + + public IEnumerable GetBlocksBackwards() + { + for (var i = bigblocks.Count - 1; i >= 0; i--) + yield return bigblocks[i]; + } + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/AstToCfg/AstToCfgEndToEnd.cs b/Source/ProofGeneration/AstToCfg/AstToCfgEndToEnd.cs new file mode 100644 index 000000000..a7d874afb --- /dev/null +++ b/Source/ProofGeneration/AstToCfg/AstToCfgEndToEnd.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.ASTRepresentation; +using ProofGeneration.PhasesUtil; +using ProofGeneration.Util; +using ProofGeneration.ASTRepresentation; + +namespace ProofGeneration.AstToCfg +{ + public class AstToCfgEndToEnd + { + private readonly string axiomAssmName = "Axioms"; + private readonly string binderEmptyAssmName = "BinderNs"; + private readonly string closedAssmName = "Closed"; + private readonly string constsGlobalsAssmName = "ConstsGlobal"; + //private readonly TermIdent finalNodeOrReturn = IsaCommonTerms.TermIdentFromName("m'"); + //private readonly TermIdent finalState = IsaCommonTerms.TermIdentFromName("s'"); + private readonly string finterpAssmName = "FInterp"; + private readonly string nonEmptyTypesAssmName = "NonEmptyTypes"; + + private readonly TermIdent normalInitState = IsaCommonTerms.TermIdentFromName("ns"); + private readonly string oldGlobalAssmName = "OldGlobal"; + private readonly string paramsLocalsAssmName = "ParamsLocal"; + private readonly string preconditionsAssmName = "Precondition"; + + private readonly string redAssmName = "Red"; + private readonly string vcAssmName = "VC"; + private BoogieContextIsa boogieContext; + + private IProgramAccessor beforeCfgProgramAccessor; + + private readonly string varContextName = "\\0"; + + //private readonly LemmaDecl entryLemma; + + public IEnumerable EndToEndProof( + string entryBlockAstLemma, + EndToEndLemmaConfig endToEndLemmaConfig, + string cfgToDagEndToEndLemma, + Term vcAssm, + IProgramAccessor beforeCfgProgramAccessor, + IProgramAccessor afterCfgProgramAccessor, + ASTRepr ast, + AstToCfgProofGenInfo proofGenInfo) + { + if (endToEndLemmaConfig == EndToEndLemmaConfig.DoNotGenerate) + { + throw new ArgumentException("end-to-end lemma invoked even though disabled"); + } + + this.beforeCfgProgramAccessor = beforeCfgProgramAccessor; + + boogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("(M::mbodyCFG proc_context)"), + IsaCommonTerms.TermIdentFromName(varContextName), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.EmptyList + ); + + var abbrev = new AbbreviationDecl( + varContextName, + new Tuple, Term>(new List(), + new TermTuple(beforeCfgProgramAccessor.ConstsAndGlobalsDecl(), beforeCfgProgramAccessor.ParamsAndLocalsDecl())) + ); + var result = new List {abbrev}; + + BigBlock bigblock0 = ast.GetBlocksForwards().First(); + string cont0 = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[bigblock0]; + Term cont0Id = IsaCommonTerms.TermIdentFromName(cont0); + Term stateName = IsaCommonTerms.TermIdentFromName("ns"); + Term initState1 = IsaBoogieTerm.Normal(stateName); + + var kStepBigBlockRed = IsaBoogieTerm.RedBigBlockKStep( + BoogieContextIsa.CreateWithNewVarContext( + boogieContext, + new TermTuple(beforeCfgProgramAccessor.ConstsAndGlobalsDecl(), beforeCfgProgramAccessor.ParamsAndLocalsDecl()) + ), + IsaBoogieTerm.StartConfigTerm(bigblock0, cont0Id, beforeCfgProgramAccessor, initState1), + IsaBoogieTerm.EndConfigTerm(), + IsaBoogieTerm.astId(), + IsaCommonTerms.TermIdentFromName("j") + ); + + var proofSb = new StringBuilder(); + proofSb.AppendLine("proof -"); + proofSb.AppendLine("from " + redAssmName + " obtain j where Aux:" + "\"" + kStepBigBlockRed + "\""); + proofSb.AppendLine("by (meson rtranclp_imp_relpowp)"); + proofSb.AppendLine("show ?thesis"); + proofSb.AppendLine(ProofUtil.Apply("rule ast_to_cfg_lemmas." + entryBlockAstLemma)); + proofSb.AppendLine("unfolding ast_to_cfg_lemmas_def"); + proofSb.AppendLine("apply (rule FInterp)"); + proofSb.AppendLine("apply (rule Aux)"); + proofSb.AppendLine("apply (rule valid_config_implies_not_failure)"); + proofSb.AppendLine(ProofUtil.Apply("rule " + cfgToDagEndToEndLemma)); + proofSb.AppendLine("apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + proofSb.AppendLine("using " + vcAssmName + " apply simp"); + proofSb.AppendLine("using " + closedAssmName + " apply simp"); + proofSb.AppendLine("using " + nonEmptyTypesAssmName + " apply simp"); + proofSb.AppendLine(ProofUtil.Apply("rule " + finterpAssmName)); + proofSb.AppendLine("using " + axiomAssmName + " apply simp"); + + proofSb.AppendLine("using " + preconditionsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def " + + beforeCfgProgramAccessor.PreconditionsDecl() + "_def " + afterCfgProgramAccessor.PreconditionsDecl() + "_def)"); + proofSb.AppendLine("using " + paramsLocalsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + proofSb.AppendLine("using " + constsGlobalsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + + proofSb.AppendLine("using " + oldGlobalAssmName + " apply simp"); + proofSb.AppendLine("using " + binderEmptyAssmName + " apply simp"); + + proofSb.AppendLine("apply (rule valid_config_implies_satisfied_posts)"); + proofSb.AppendLine(ProofUtil.Apply("rule " + cfgToDagEndToEndLemma)); + proofSb.AppendLine("apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + //TODO: don't hardcode this + proofSb.AppendLine("using " + vcAssmName + " apply simp"); + proofSb.AppendLine("using " + closedAssmName + " apply simp"); + proofSb.AppendLine("using " + nonEmptyTypesAssmName + " apply simp"); + proofSb.AppendLine(ProofUtil.Apply("rule " + finterpAssmName)); + proofSb.AppendLine("using " + axiomAssmName + " apply simp"); + + proofSb.AppendLine("using " + preconditionsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def " + + beforeCfgProgramAccessor.PreconditionsDecl() + "_def " + afterCfgProgramAccessor.PreconditionsDecl() + "_def)"); + proofSb.AppendLine("using " + paramsLocalsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + proofSb.AppendLine("using " + constsGlobalsAssmName + " apply (simp add: " + + beforeCfgProgramAccessor.ParamsDecl() + "_def " + beforeCfgProgramAccessor.LocalsDecl() + "_def " + + afterCfgProgramAccessor.ParamsDecl() + "_def " + afterCfgProgramAccessor.LocalsDecl() + "_def)"); + + proofSb.AppendLine("using " + oldGlobalAssmName + " apply simp"); + proofSb.AppendLine("using " + binderEmptyAssmName + " apply simp+"); + + proofSb.AppendLine("done"); + proofSb.AppendLine("qed"); + + var helperLemmaName = "end_to_end_theorem_aux_ast"; + + var helperLemma = + new LemmaDecl( + helperLemmaName, + LemmaContext(ast, vcAssm, proofGenInfo), + IsaBoogieTerm.AstValidConfiguration(boogieContext, beforeCfgProgramAccessor.PostconditionsDecl()), + new Proof(new List {proofSb.ToString()}) + ); + result.Add(helperLemma); + + if (endToEndLemmaConfig == EndToEndLemmaConfig.GenerateForProcedure) + { + var assumptions = new List(); + + string contName = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[bigblock0]; + Term contId = IsaCommonTerms.TermIdentFromName(contName); + Term initStateId = IsaBoogieTerm.Normal(IsaCommonTerms.TermIdentFromName("ns")); + + Term astDecl = ast.GetAstAsTermList(proofGenInfo); + List continuations = ast.GetMainContinuations(proofGenInfo); + string continuationsAsString = null; + foreach (var str in continuations) + { + continuationsAsString += str + " "; + } + + var initAstTerm = new TermApp + ( + IsaCommonTerms.TermIdentFromName("init_ast"), + astDecl, + IsaCommonTerms.TermIdentFromName("ns") + ); + + BoogieContextIsa dummyBoogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("M"), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.TermIdentFromName("\\") + ); + + var bigBlockMultiRedInitAst = IsaBoogieTerm.RedBigBlockMulti( + dummyBoogieContext, + initAstTerm, + IsaBoogieTerm.EndConfigTerm(), + IsaBoogieTerm.astId() + ); + + assumptions.Add(bigBlockMultiRedInitAst); + + var bigBlockMultiRed = IsaBoogieTerm.RedBigBlockMulti( + dummyBoogieContext, + IsaBoogieTerm.StartConfigTerm(bigblock0, contId, beforeCfgProgramAccessor, initStateId), + IsaBoogieTerm.EndConfigTerm(), + IsaBoogieTerm.astId() + ); + + var proofMethods = new List + { + "using assms", + "by (simp add: " + continuationsAsString + ")" + }; + + var initializationLemmaName = "initialization"; + var initializationLemma = + new LemmaDecl( + initializationLemmaName, + ContextElem.CreateWithAssumptions(assumptions), + bigBlockMultiRed, + new Proof(proofMethods) + ); + result.Add(initializationLemma); + + //transform end to end theorem to a compact representation + var endToEndLemma = + new LemmaDecl( + "end_to_end_theorem_ast", + ContextElem.CreateWithAssumptions(new List { vcAssm }, new List { "VC" }), + ProcedureIsCorrect( + beforeCfgProgramAccessor.FunctionsDecl(), + IsaCommonTerms.TermIdentFromName(beforeCfgProgramAccessor.ConstsDecl()), + IsaCommonTerms.TermIdentFromName(beforeCfgProgramAccessor.UniqueConstsDecl()), + IsaCommonTerms.TermIdentFromName(beforeCfgProgramAccessor.GlobalsDecl()), + beforeCfgProgramAccessor.AxiomsDecl(), + beforeCfgProgramAccessor.ProcDecl()), + new Proof( + new List + { + ProofUtil.Apply(ProofUtil.Rule(ProofUtil.OF("end_to_end_util2", helperLemmaName))), + "apply (rule initialization)", + "unfolding " + beforeCfgProgramAccessor.ProcDeclName() + "_def", + "apply assumption " + "using VC apply simp " + " apply assumption+", + ProofUtil.By("simp_all add: exprs_to_only_checked_spec_1 exprs_to_only_checked_spec_2 " + + beforeCfgProgramAccessor.ProcDeclName() + "_def " + + beforeCfgProgramAccessor.CfgDeclName() + "_def") + } + )); + + result.Add(endToEndLemma); + } + + return result; + } + + private ContextElem LemmaContext( + ASTRepr ast, + Term vcAssm, + AstToCfgProofGenInfo proofGenInfo + ) + { + BigBlock bigblock0 = ast.GetBlocksForwards().First(); + string contName = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[bigblock0]; + Term contId = IsaCommonTerms.TermIdentFromName(contName); + Term initStateId = IsaBoogieTerm.Normal(IsaCommonTerms.TermIdentFromName("ns")); + + var bigBlockMultiRed = IsaBoogieTerm.RedBigBlockMulti( + BoogieContextIsa.CreateWithNewVarContext( + boogieContext, + new TermTuple(beforeCfgProgramAccessor.ConstsAndGlobalsDecl(), beforeCfgProgramAccessor.ParamsAndLocalsDecl()) + ), + IsaBoogieTerm.StartConfigTerm(bigblock0, contId, beforeCfgProgramAccessor, initStateId), + IsaBoogieTerm.EndConfigTerm(), + IsaBoogieTerm.astId() + ); + var closedAssm = EndToEndAssumptions.ClosednessAssumption(boogieContext.absValTyMap); + var nonEmptyTypesAssm = EndToEndAssumptions.NonEmptyTypesAssumption(boogieContext.absValTyMap); + var finterpAssm = IsaBoogieTerm.FunInterpWf(boogieContext.absValTyMap, beforeCfgProgramAccessor.FunctionsDecl(), + boogieContext.funContext); + var absValType = new VarType("a"); + //need to explicitly give type for normal state, otherwise Isabelle won't know that the abstract value type is the same as used in the VC + var axiomAssm = EndToEndAssumptions.AxiomAssumption(boogieContext, beforeCfgProgramAccessor, + new TermWithExplicitType(normalInitState, IsaBoogieType.NormalStateType(absValType))); + var presAssm = + IsaBoogieTerm.ExprAllSat(boogieContext, normalInitState, beforeCfgProgramAccessor.PreconditionsDecl()); + var localsAssm = EndToEndAssumptions.LocalStateAssumption(boogieContext, + IsaCommonTerms.Snd(boogieContext.varContext), normalInitState); + var globalsAssm = EndToEndAssumptions.GlobalStateAssumption(boogieContext, + IsaCommonTerms.Fst(boogieContext.varContext), normalInitState); + var oldGlobalStateAssm = EndToEndAssumptions.OldGlobalStateAssumption(normalInitState); + var binderEmptyAssm = EndToEndAssumptions.BinderStateEmpty(normalInitState); + + return + ContextElem.CreateWithAssumptions( + new List + { + bigBlockMultiRed, vcAssm, closedAssm, nonEmptyTypesAssm, finterpAssm, axiomAssm, + presAssm, localsAssm, globalsAssm, oldGlobalStateAssm, binderEmptyAssm + }, + new List + { + redAssmName, vcAssmName, closedAssmName, nonEmptyTypesAssmName, finterpAssmName, axiomAssmName, + preconditionsAssmName, paramsLocalsAssmName, constsGlobalsAssmName, oldGlobalAssmName, + binderEmptyAssmName + } + ); + } + + private static Term ProcedureIsCorrect(Term funDecls, Term constantDecls, Term uniqueConstantDecls, Term globalDecls, Term axioms, + Term procedure) + { + var typeInterpId = new SimpleIdentifier("A"); + return + TermQuantifier.MetaAll( + new List{typeInterpId}, + null, + new TermApp( + IsaCommonTerms.TermIdentFromName("proc_is_correct"), + new TermWithExplicitType(new TermIdent(typeInterpId), IsaBoogieType.AbstractValueTyFunType(new VarType("a"))), + funDecls, + constantDecls, + uniqueConstantDecls, + globalDecls, + axioms, + procedure, + new TermWithExplicitType( + IsaCommonTerms.TermIdentFromName("Ast.proc_body_satisfies_spec"), + new DataType("satisfies_spec_func_type", new VarType("a")) + ) + ) + ); + } + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/AstToCfg/AstToCfgLemmaManager.cs b/Source/ProofGeneration/AstToCfg/AstToCfgLemmaManager.cs new file mode 100644 index 000000000..33fd24170 --- /dev/null +++ b/Source/ProofGeneration/AstToCfg/AstToCfgLemmaManager.cs @@ -0,0 +1,1876 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.Util; + +namespace ProofGeneration.AstToCfg +{ + public class AstToCfgLemmaManager + { + private readonly CFGRepr afterCfg; + private readonly IProgramAccessor afterCfgProgAccess; + + private readonly BasicCmdIsaVisitor basicCmdIsaVisitor; + + private readonly IProgramAccessor beforeCfgProgAccess; + private readonly IDictionary beforeToAfterBlock; + + private readonly BoogieContextIsa astBoogieContext; + private readonly BoogieContextIsa cfgBoogieContext; + + private readonly TermIdent finalNode = IsaCommonTerms.TermIdentFromName("m'"); + private readonly Term finalState = IsaCommonTerms.TermIdentFromName("s'"); + private readonly string funContextWfName; + + private readonly IDictionary mappingBigBlockToGlobalLemmaDecl; + private readonly IDictionary mappingBigBlockToLocalLemmaDecl; + + //private readonly CfgToDagHintManager hintManager; + + private readonly IsaUniqueNamer namer = new IsaUniqueNamer(); + + private readonly Term initState1; + private readonly Term normalInitState1 = IsaCommonTerms.TermIdentFromName("ns1"); + + private readonly string redCfgName = "Red"; + + //private readonly TypingTacticGenerator typingTacticGenerator; + private readonly IVariableTranslation variableTranslation; + + private readonly IDictionary beforeDagOrigBlock; + + public AstToCfgLemmaManager( + IProgramAccessor beforeCfgProgAccess, + IProgramAccessor afterCfgProgAccess, + BoogieContextIsa astBoogieContext, + BoogieContextIsa cfgBoogieContext, + CFGRepr afterCfg, + string funContextWfName, + IDictionary beforeDagOrigBlock, + IDictionary beforeToAfterBlock, + IVariableTranslationFactory varFactory + ) + { + this.beforeCfgProgAccess = beforeCfgProgAccess; + this.afterCfgProgAccess = afterCfgProgAccess; + this.afterCfg = afterCfg; + this.funContextWfName = funContextWfName; + variableTranslation = varFactory.CreateTranslation().VarTranslation; + this.beforeToAfterBlock = beforeToAfterBlock; + + this.astBoogieContext = astBoogieContext; + this.cfgBoogieContext = cfgBoogieContext; + + initState1 = IsaBoogieTerm.Normal(normalInitState1); + basicCmdIsaVisitor = new BasicCmdIsaVisitor(varFactory); + mappingBigBlockToGlobalLemmaDecl = new Dictionary(); + mappingBigBlockToLocalLemmaDecl = new Dictionary(); + + this.beforeDagOrigBlock = beforeDagOrigBlock; + } + + private TermList HavocedVarsTerm(IEnumerable vars) + { + return new TermList( + vars.Select(x => + { + if (variableTranslation.TryTranslateVariableId(x, out var varId, out _)) + { + return varId; + } + + throw new ProofGenUnexpectedStateException("Could not extract variable id"); + }).ToList()); + } + private Term InvariantsTerm(IEnumerable invs) + { + return new TermList(invs.Select(e => basicCmdIsaVisitor.Translate(e)).ToList()); + } + private Term ExprTerm(Expr expr) + { + return basicCmdIsaVisitor.Translate(expr); + } + + public LemmaDecl LocalLemma( + BigBlock beforeBlock, + Block afterBlock, + Expr guardHint, + Func blockLemmaName, + BranchIndicator indicator) + { + var beforeBigblockDefName = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(beforeBlock).First(); + Term startConfig = IsaBoogieTerm.StartConfigTerm(beforeBlock, IsaCommonTerms.TermIdentFromName("cont0"), beforeCfgProgAccess, initState1); + Term endConfig = IsaBoogieTerm.EndConfigTerm(); + + var afterCmdsDefName = afterCfgProgAccess.BlockInfo().CmdsQualifiedName(afterBlock); + Term afterCmds = IsaCommonTerms.TermIdentFromName(afterCmdsDefName); + + var quatifiedstateId = new SimpleIdentifier("ns1'"); + + var finalStateId2 = new SimpleIdentifier("s2'"); + Term finalState2 = new TermIdent(finalStateId2); + Term astId = IsaBoogieTerm.astId(); + + var localLemmas = new List(); + + #region local block lemma + + var assumptions = new List + { + IsaBoogieTerm.RedBigBlock(astBoogieContext, startConfig, endConfig, astId) + }; + + Term cfgVerifiesAssm = + TermQuantifier.MetaAll( + new List {finalStateId2}, + null, + TermBinary.MetaImplies( + IsaBoogieTerm.RedCmdList(cfgBoogieContext, afterCmds, initState1, finalState2), + TermBinary.Neq(finalState2, IsaBoogieTerm.Failure())) + ); + assumptions.Add(cfgVerifiesAssm); + + bool hasGuardHint = false; + if (indicator == BranchIndicator.GuardHolds && guardHint != null) + { + Term traceIsPossible = IsaBoogieTerm.RedExpr(astBoogieContext, + ExprTerm(guardHint), + normalInitState1, + new TermApp(IsaBoogieTerm.BoolValConstr(), IsaCommonTerms.TermIdentFromName("True"))); + hasGuardHint = true; + assumptions.Add(traceIsPossible); + } + else if (indicator == BranchIndicator.GuardFails && guardHint != null) + { + Term traceIsPossible = IsaBoogieTerm.RedExpr(astBoogieContext, + ExprTerm(guardHint), + normalInitState1, + new TermApp(IsaBoogieTerm.BoolValConstr(), IsaCommonTerms.TermIdentFromName("False"))); + hasGuardHint = true; + assumptions.Add(traceIsPossible); + } + + Term conclusion = + TermBinary.And( + TermBinary.Neq(IsaCommonTerms.TermIdentFromName("reached_state"), IsaBoogieTerm.Failure()), + TermQuantifier.ForAll( + new List {quatifiedstateId}, + null, + TermBinary.Implies( + TermBinary.Eq(IsaCommonTerms.TermIdentFromName("reached_state"), IsaBoogieTerm.Normal(new TermIdent(quatifiedstateId))), + IsaBoogieTerm.RedCmdList(cfgBoogieContext, afterCmds, initState1, IsaBoogieTerm.Normal(new TermIdent(quatifiedstateId))) ) + )); + + var proofMethods = new List(); + var proofSb = new StringBuilder(); + + if (indicator == 0 || guardHint == null) + { + proofSb.AppendLine(ProofUtil.Apply("rule block_local_rel_generic")); + proofSb.AppendLine(ProofUtil.Apply("rule Rel_Main_test[of " + beforeBigblockDefName + "]")); + proofSb.AppendLine(ProofUtil.Apply("simp add: " + beforeBigblockDefName + "_def " + afterCmdsDefName + "_def")); + proofSb.AppendLine(ProofUtil.Apply(ProofUtil.Repeat("simp add: " + afterCmdsDefName + "_def"))); + proofSb.AppendLine(ProofUtil.Apply("rule astStep")); + proofSb.AppendLine(ProofUtil.Apply("rule cfgBlockDoesntFail")); + proofSb.AppendLine(ProofUtil.Apply( ProofUtil.Repeat("simp add: " + afterCmdsDefName + "_def " + beforeBigblockDefName + "_def"))); + proofSb.AppendLine("done"); + } + else if (indicator == BranchIndicator.GuardHolds) + { + proofSb.AppendLine("unfolding " + afterCmdsDefName + "_def"); + proofSb.AppendLine(ProofUtil.Apply("rule guard_holds_push_through_assumption")); + proofSb.AppendLine(ProofUtil.Apply("rule block_local_rel_generic")); + proofSb.AppendLine(ProofUtil.Apply("rule Rel_Main_test[of " + beforeBigblockDefName + "]")); + proofSb.AppendLine(ProofUtil.Apply("simp add: " + beforeBigblockDefName + "_def")); + proofSb.AppendLine(ProofUtil.Apply("simp+")); + proofSb.AppendLine(ProofUtil.Apply("rule astStep")); + proofSb.AppendLine(ProofUtil.Apply("rule push_through_assumption_test1, rule cfgBlockDoesntFail")); + proofSb.AppendLine(ProofUtil.Apply("simp add: " + afterCmdsDefName + "_def")); + proofSb.AppendLine("using guardHint "); + proofSb.AppendLine(ProofUtil.Apply(ProofUtil.Repeat("simp add: " + beforeBigblockDefName + "_def"))); + proofSb.AppendLine("done"); + } + else if (indicator == BranchIndicator.GuardFails) + { + proofSb.AppendLine("unfolding " + afterCmdsDefName + "_def"); + proofSb.AppendLine(ProofUtil.Apply("rule guard_fails_push_through_assumption")); + proofSb.AppendLine(ProofUtil.Apply("rule block_local_rel_generic")); + proofSb.AppendLine(ProofUtil.Apply("rule Rel_Main_test[of " + beforeBigblockDefName + "]")); + proofSb.AppendLine(ProofUtil.Apply("simp add: " + beforeBigblockDefName + "_def")); + proofSb.AppendLine(ProofUtil.Apply("simp+")); + proofSb.AppendLine(ProofUtil.Apply("rule astStep")); + proofSb.AppendLine(ProofUtil.Apply("rule cfgBlockDoesntFail")); + proofSb.AppendLine(ProofUtil.Apply("simp add: " + afterCmdsDefName + "_def")); + proofSb.AppendLine(ProofUtil.Apply("rule push_through_assumption1")); + proofSb.AppendLine(ProofUtil.Apply("simp")); + proofSb.AppendLine(ProofUtil.Apply(NegationRule(guardHint))); + proofSb.AppendLine("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + proofSb.AppendLine(ProofUtil.Apply( ProofUtil.Repeat("simp add: " + beforeBigblockDefName + "_def"))); + proofSb.AppendLine(ProofUtil.Apply(NegationRule(guardHint))); + proofSb.AppendLine("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + proofSb.AppendLine("done"); + } + + List assmsLabels = new List {"astStep", "cfgBlockDoesntFail"}; + if (hasGuardHint) + { + assmsLabels.Add("guardHint"); + } + var localLemma = new LemmaDecl( + blockLemmaName(beforeBlock), + ContextElem.CreateWithAssumptions(assumptions, assmsLabels), + conclusion, + new Proof(new List {proofSb.ToString()}) + ); + + #endregion + + mappingBigBlockToLocalLemmaDecl.Add(beforeBlock, localLemma); + localLemmas.Add(localLemma); + + return localLemma; + } + + #region generate global lemma + public LemmaDecl GenerateGlobalLemma( + BigBlock startingBigBlock, + Term continuation, + Block relatedBlock, + Term posts, + (Expr, BranchIndicator) hints, + Func globalBlockLemmaName, + AstToCfgProofGenInfo proofGenInfo) + { + var assumptions = new List(); + + #region assumption 1: trace through the ast + Term startConfig = IsaBoogieTerm.StartConfigTerm(startingBigBlock, continuation, beforeCfgProgAccess, initState1); + Term endConfig = IsaBoogieTerm.EndConfigTerm(); + Term astId = IsaBoogieTerm.astId(); + Term numStepsId = IsaCommonTerms.TermIdentFromName("j"); + + Term astTrace = IsaBoogieTerm.RedBigBlockKStep(astBoogieContext, startConfig, endConfig, astId, numStepsId); + assumptions.Add(astTrace); + #endregion + + var finalCfgNodeId = new SimpleIdentifier("m'"); + var finalStateId = new SimpleIdentifier("s'"); + Term initialStateNode = new NatConst(afterCfg.GetUniqueIntLabel(relatedBlock)); + var boundStateId = new SimpleIdentifier("ns_end"); + + #region assumption 2: all cfg traces end in a non-failing state + + Term cfgVerifiesAssm = + TermQuantifier.MetaAll( + new List {finalCfgNodeId, finalStateId}, + null, + TermBinary.MetaImplies( + IsaBoogieTerm.RedCFGMulti(cfgBoogieContext, + afterCfgProgAccess.CfgDecl(), + IsaBoogieTerm.CFGConfigNode( + initialStateNode, IsaBoogieTerm.Normal(normalInitState1) + ), + IsaBoogieTerm.CFGConfig(finalNode, finalState)), + TermBinary.Neq(finalState, IsaBoogieTerm.Failure())) + ); + assumptions.Add(cfgVerifiesAssm); + + #endregion + + #region assumption 3: all cfg traces end in a state that satisfies the post-conditions + Term cfgSatisfiesPostsAssm = + TermQuantifier.MetaAll( + new List {finalCfgNodeId, finalStateId}, + null, + TermBinary.MetaImplies( + IsaBoogieTerm.RedCFGMulti(cfgBoogieContext, + afterCfgProgAccess.CfgDecl(), + IsaBoogieTerm.CFGConfigNode( + initialStateNode, IsaBoogieTerm.Normal(normalInitState1) + ), + IsaBoogieTerm.CFGConfig(finalNode, finalState)), + TermBinary.MetaImplies( + new TermApp(IsaCommonTerms.TermIdentFromName("is_final_config"), IsaBoogieTerm.CFGConfig(finalNode, finalState)), + TermQuantifier.ForAll( + new List {boundStateId}, + null, + TermBinary.Implies( + TermBinary.Eq(finalState, IsaBoogieTerm.Normal(new TermIdent(boundStateId))), + IsaBoogieTerm.ExprAllSat(astBoogieContext, new TermIdent(boundStateId), posts)) + ) + )) + ); + assumptions.Add(cfgSatisfiesPostsAssm); + + #endregion + + #region conditional assumption 4: the given trace through the ast is actually possible + + bool guardSemantics = false; + BigBlock correspondingOrigBigBlock = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + if (hints.Item2 == BranchIndicator.GuardHolds && hints.Item1 != null) + { + Term traceIsPossible = IsaBoogieTerm.RedExpr(astBoogieContext, + ExprTerm(hints.Item1), + normalInitState1, + new TermApp(IsaBoogieTerm.BoolValConstr(), IsaCommonTerms.TermIdentFromName("True"))); + guardSemantics = true; + assumptions.Add(traceIsPossible); + } + else if (hints.Item2 == BranchIndicator.GuardFails && hints.Item1 != null) + { + Term traceIsPossible = IsaBoogieTerm.RedExpr(astBoogieContext, + ExprTerm(hints.Item1), + normalInitState1, + new TermApp(IsaBoogieTerm.BoolValConstr(), IsaCommonTerms.TermIdentFromName("False"))); + guardSemantics = true; + assumptions.Add(traceIsPossible); + } + else if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().ContainsKey(startingBigBlock)) + { + WhileCmd wcmd = (WhileCmd) startingBigBlock.ec; + if (wcmd.Guard == null) + { + Term personalGuard = Isabelle.Util.IsaCommonTerms.TermIdentFromName("personal_guard"); + Term equalitySign = Isabelle.Util.IsaCommonTerms.TermIdentFromName("="); + + Term personalGuardIsNone = + new TermApp(personalGuard, equalitySign, IsaCommonTerms.NoneOption()); + guardSemantics = true; + assumptions.Add(personalGuardIsNone); + } + } + else if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingOrigBigBlock)) + { + BigBlock loopBigBlockOrig = proofGenInfo.GetMappingBigBlockToLoopBigBlock()[correspondingOrigBigBlock]; + BigBlock loopBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[loopBigBlockOrig]; + + WhileCmd wcmd = (WhileCmd) loopBigBlockCopy.ec; + if (wcmd.Guard == null) + { + Term enclosingLoopGuard = Isabelle.Util.IsaCommonTerms.TermIdentFromName("guard_of_enclosing_loop"); + Term equalitySign = Isabelle.Util.IsaCommonTerms.TermIdentFromName("="); + + Term enclosingLoopGuardIsNone = + new TermApp(enclosingLoopGuard, equalitySign, IsaCommonTerms.NoneOption()); + guardSemantics = true; + assumptions.Add(enclosingLoopGuardIsNone); + } + } + + #endregion + + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().ContainsKey(startingBigBlock)) + { + correspondingBigBlockOrig = proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()[startingBigBlock]; + } + BigBlock successorBigBlockOrig = correspondingBigBlockOrig.successorBigBlock; + + #region conditional assumption 5: loop induction hypothesis + + bool hasLoopIH = false; + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + BigBlock enclosingLoop = proofGenInfo.GetMappingBigBlockToLoopBigBlock()[correspondingBigBlockOrig]; + BigBlock enclosingLoopCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[enclosingLoop]; + + IDictionary origBlockBeforeDag = beforeDagOrigBlock.InverseDict(); + Block enclosingLoopCfgBlock = proofGenInfo.GetMappingOrigBigBlockToOrigBlock()[enclosingLoop]; + Block enclosingLoopCfgBlockCopy = origBlockBeforeDag[enclosingLoopCfgBlock]; + + Term blockIndex; + Block successorCfgBlock = afterCfg.GetSuccessorBlocks(enclosingLoopCfgBlockCopy).First(); + blockIndex = new NatConst(afterCfg.GetUniqueIntLabel(successorCfgBlock)); + + int unwrappedEnclosingLoopBigBlockIndex = proofGenInfo.GetMappingCopyBigBlockToIndex()[enclosingLoopCopy] + 1; + + Term succBigBlockTermId = IsaCommonTerms.TermIdentFromName(beforeCfgProgAccess.BigBlockInfo().TheoryName + ".bigblock_" + unwrappedEnclosingLoopBigBlockIndex); + Term succContId = IsaCommonTerms.TermIdentFromName("cont_" + unwrappedEnclosingLoopBigBlockIndex); + Term cfgBodyId = IsaCommonTerms.TermIdentFromName(afterCfgProgAccess.BlockInfo().getTheoryName() + ".proc_body"); + //Term blockIndex = new NatConst(unwrappedEnclosingLoopBigBlockIndex); + + Term loop_ih_assm = IsaBoogieTerm.AstToCfgLoopIndHypothesis(astBoogieContext, cfgBoogieContext, astId, succBigBlockTermId, succContId, cfgBodyId, blockIndex, posts); + hasLoopIH = true; + assumptions.Add(loop_ih_assm); + } + + #endregion + + Term conclusion = IsaBoogieTerm.AstValidConfiguration(astBoogieContext, posts); + + #region proof + var proofMethods = new List(); + + //A BigBlock is final if it EITHER has no successor and it has no structure to branch into OR it contains a return command. + if (successorBigBlockOrig == null && startingBigBlock.ec == null || startingBigBlock.tc is ReturnCmd) + { + proofMethods = GenerateFinalBlockProof(startingBigBlock, relatedBlock, hints.Item1, hints.Item2, proofGenInfo); + } + //A BigBlock is a LoopHead if it is a key in this dictionary. + else if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().ContainsKey(startingBigBlock)) + { + WhileCmd wcmd = (WhileCmd) startingBigBlock.ec; + Expr guard = wcmd.Guard; + proofMethods = GenerateLoopHeadProof(startingBigBlock, relatedBlock, guard, proofGenInfo); + } + //A BigBlock is generic if it has no structure to branch into and it doesn't contain a return command. + else if (startingBigBlock.ec == null) + { + proofMethods = GenerateProofGeneric(startingBigBlock, relatedBlock, hints.Item1, hints.Item2, proofGenInfo); + } + //A 'WhileSuccessor' proof is generated for a BigBlock that contains a non-null WhileCmd object and a non-empty simpleCmds list. + //A BigBlock that contains a non-null WhileCmd object and an empty simpleCmds list is treated as a special case. + else if (startingBigBlock.ec is WhileCmd && mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + WhileCmd wcmd = (WhileCmd) startingBigBlock.ec; + Expr personalGuard = wcmd.Guard; + proofMethods = GenerateWhileSuccessorProof(startingBigBlock, relatedBlock, hints.Item1, personalGuard, hints.Item2, proofGenInfo); + } + //An 'IfSuccessor' proof is generated for a BigBlock that contains a non-null IfCmd object. + else if (startingBigBlock.ec is IfCmd) + { + IfCmd ifcmd = (IfCmd) startingBigBlock.ec; + Expr guard = ifcmd.Guard; + proofMethods = GenerateIfSuccessorProof(startingBigBlock, relatedBlock, guard, hints.Item1, hints.Item2, proofGenInfo); + } + //TODO: Test consecutive loops in a loop. + /* TODO: All BigBlocks in a loop body except the very first one are assigned a NoGuard BranchIndicator. + TODO: This works for ProofGen only if Block Coalescing is turned off. */ + //These last three checks all address the special case of a BigBlock that contains a non-null WhileCmd object and an empty simpleCmds. + //1. The BigBlock contains a non-null WhileCmd object and an empty simpleCmds and is right in the beginning of an Else Branch. + else if (hints.Item2 == BranchIndicator.GuardFails && + correspondingBigBlockOrig.ec is WhileCmd && + !startingBigBlock.simpleCmds.Any()) + { + proofMethods = GenerateEndingAfterUnwrappingProof(startingBigBlock, relatedBlock, hints.Item1, BranchIndicator.GuardFails, proofGenInfo); + } + //2. The BigBlock contains a non-null WhileCmd object and an empty simpleCmds and is in another loop. + else if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofMethods = GenerateEndingAfterUnwrappingProof(startingBigBlock, relatedBlock, hints.Item1, hints.Item2, proofGenInfo); + } + //3. The BigBlock contains a non-null WhileCmd object and an empty simpleCmds. + else if (!startingBigBlock.simpleCmds.Any() && startingBigBlock.ec is WhileCmd) + { + proofMethods = GenerateEndingAfterUnwrappingProof(startingBigBlock, relatedBlock, hints.Item1, BranchIndicator.NoGuard, proofGenInfo); + } + #endregion + + List assmsLabels = new List {"astTrace", "cfgDoesntFail", "cfgSatisfiesPosts"}; + if (guardSemantics) + { + assmsLabels.Add("guardHint"); + } + + if (hasLoopIH) + { + assmsLabels.Add("inductionHypothesis"); + } + + var globalLemma = new LemmaDecl( + globalBlockLemmaName(startingBigBlock), + ContextElem.CreateWithAssumptions(assumptions, assmsLabels), + conclusion, + new Proof(proofMethods) + ); + + mappingBigBlockToGlobalLemmaDecl.Add(startingBigBlock, globalLemma); + return globalLemma; + } + #endregion + + private string NegationRule(Expr guard) + { + NAryExpr nary = guard as NAryExpr; + + if (ReferenceEquals(guard, Expr.Literal(true))) + { + return ProofUtil.Rule("neg_equiv1"); + } + if (ReferenceEquals(guard, Expr.Literal(false))) + { + return ProofUtil.Rule("neg_equiv2"); + } + if (nary != null) + { + if (nary.Fun is UnaryOperator unOp) + { + if (unOp.Op == UnaryOperator.Opcode.Not) + { + return ProofUtil.Rule("double_neg"); + } + } + else if (nary.Fun is BinaryOperator binOp) + { + if (binOp.Op == BinaryOperator.Opcode.Eq) + { + return ProofUtil.Rule("neg_eq"); + } + if (binOp.Op == BinaryOperator.Opcode.Neq) + { + return ProofUtil.Rule("neg_neq"); + } + if (binOp.Op == BinaryOperator.Opcode.Lt) + { + return ProofUtil.Rule("neg_lt"); + } + if (binOp.Op == BinaryOperator.Opcode.Le) + { + return ProofUtil.Rule("neg_le"); + } + if (binOp.Op == BinaryOperator.Opcode.Ge) + { + return ProofUtil.Rule("neg_ge"); + } + if (binOp.Op == BinaryOperator.Opcode.Gt) + { + return ProofUtil.Rule("neg_gt"); + } + } + } + + return ProofUtil.Rule("neg_refl"); + } + + private string ExpandDefinitions(string contId, BigBlock startingBigBlock, AstToCfgProofGenInfo proofGenInfo, BranchIndicator branchIndicator) + { + IfCmd @if = (IfCmd) startingBigBlock.ec; + + string expansion = "apply (simp add: " + contId + "_def "; + if (branchIndicator == BranchIndicator.GuardHolds) + { + foreach (var thenBb in @if.thn.BigBlocks) + { + BigBlock thenBranchCopy = thenBb; + string thenBranchId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(thenBranchCopy).First(); + string thenBranchContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[thenBranchCopy]); + expansion += thenBranchId + "_def " + thenBranchContId + "_def "; + } + } + else if (@if.elseBlock != null) + { + foreach (var elseBb in @if.elseBlock.BigBlocks) + { + BigBlock elseBranchCopy = elseBb; + string elseBranchId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(elseBranchCopy).First(); + string elseBranchContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[elseBranchCopy]); + expansion += elseBranchId + "_def " + elseBranchContId + "_def "; + } + } + + expansion += ")"; + return expansion; + } + + private List GenerateFinalBlockProof( + BigBlock startingBigBlock, + Block relatedBlock, + Expr entryGuard, + BranchIndicator indicator, + AstToCfgProofGenInfo proofGenInfo) + { + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + + string syntacticRel; + string traceIsPossible = "using guardHint " + ProofUtil.Apply(ProofUtil.Simp()); + List finalPartofProof; + List middlePartofProof; + List beginningOfProof; + + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + string nameLemmaLocal = "placeholder"; + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + nameLemmaLocal = mappingBigBlockToLocalLemmaDecl[startingBigBlock].Name; + } + + syntacticRel = ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + "]"); + finalPartofProof = new List + { + ProofUtil.Apply("rule " + nameLemmaLocal), + "apply assumption+" + }; + + if (indicator != 0 && entryGuard != null) { finalPartofProof.Add(traceIsPossible); } + } + else if (startingBigBlock.tc is ReturnCmd) + { + syntacticRel = ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + "]"); + finalPartofProof = new List + { + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("simp add: end_return") + }; + } + else + { + syntacticRel = ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + "]"); + finalPartofProof = new List + { + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply("simp add: end_static") + }; + } + + finalPartofProof.Add("done"); + finalPartofProof.Add("qed"); + + if (indicator == 0 || entryGuard == null) + { + middlePartofProof = new List + { + ProofUtil.Apply("rule disjI1"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule " + outEdgesId), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule cfgSatisfiesPosts"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("simp add: " + nodeId) + }; + } + else if (indicator == BranchIndicator.GuardFails) + { + middlePartofProof = new List + { + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(" " + NegationRule(entryGuard)), + "using guardHint " + ProofUtil.Apply((ProofUtil.Simp())), + ProofUtil.Apply("rule " + outEdgesId), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule cfgSatisfiesPosts"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("simp add: " + nodeId) + }; + } + else if (indicator == BranchIndicator.GuardHolds) + { + middlePartofProof = new List + { + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule disjI1"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()), + "using guardHint " + ProofUtil.Apply((ProofUtil.Simp())), + ProofUtil.Apply("rule " + outEdgesId), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule cfgSatisfiesPosts"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("simp add: " + nodeId) + }; + } + else + { + middlePartofProof = new List + { + ProofUtil.Apply("rule disjI1"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule " + outEdgesId), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule cfgSatisfiesPosts"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("simp add: " + nodeId), + }; + } + + List beginningOfProofA = new List + { + "proof -", + "show ?thesis", + ProofUtil.Apply("rule generic_ending_block_global_rel"), + syntacticRel, + ProofUtil.Apply("simp add: " + bigblockId + "_def") + }; + + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + beginningOfProofA.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + + List beginningOfProofB = new List + { + ProofUtil.Apply("rule astTrace"), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply(ProofUtil.Simp()), + startingBigBlock.tc is ReturnCmd ? "" : ProofUtil.Apply("rule " + contId + "_def"), + ProofUtil.Apply("rule " + nodeId) + }; + + beginningOfProof = new List(); + beginningOfProof.AddRange(beginningOfProofA); + beginningOfProof.AddRange(beginningOfProofB); + + List proofMethods = new List(); + proofMethods.AddRange(beginningOfProof); + proofMethods.AddRange(middlePartofProof); + proofMethods.AddRange(finalPartofProof); + + return proofMethods; + } + + private List GenerateLoopHeadProof( + BigBlock startingBigBlock, + Block relatedBlock, + Expr personalGuard, + AstToCfgProofGenInfo proofGenInfo) + { + List proofMethods = new List(); + + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().Keys.Contains(startingBigBlock)) + { + correspondingBigBlockOrig = proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()[startingBigBlock]; + } + + //Get the BigBlock that comes after the loop. + BigBlock afterLoopBigBlockOrig = correspondingBigBlockOrig.successorBigBlock; + BigBlock afterLoopBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[afterLoopBigBlockOrig]; + + BigBlock unwrappedAfterLoopBigBlockCopy = null; + foreach (var tuple in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (tuple.Value == afterLoopBigBlockOrig) + { + unwrappedAfterLoopBigBlockCopy = tuple.Key; + break; + } + } + + //Get the BigBlock that's the first one in the loop body. + WhileCmd wcmd = (WhileCmd) correspondingBigBlockOrig.ec; + BigBlock bodyBigBlockOrig = wcmd.Body.BigBlocks.First(); + BigBlock bodyBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[bodyBigBlockOrig]; + + //Get the successor CFG blocks to the CFG block that's related to the starting BigBlock. + IEnumerable successors = afterCfg.GetSuccessorBlocks(relatedBlock); + if (successors != null && successors.Any()) + { + Block succ1 = successors.First(); + int succ1UniqueIntLabel = afterCfg.GetUniqueIntLabel(succ1); + Block succ2 = successors.Last(); + int succ2UniqueIntLabel = afterCfg.GetUniqueIntLabel(succ2); + + //Get the names of all of the terms that are to be used in the proof. + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(relatedBlock); + + //string bigblockBodyId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(bodyBigBlockCopy).First(); + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + string bodyBigBlockContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[bodyBigBlockCopy]); + + #region loop body definitions in simplifier + + string unfoldedLoopBodyDefinitions = "apply (simp add: " + contId + "_def "; + WhileCmd _while = (WhileCmd) correspondingBigBlockOrig.ec; + foreach (BigBlock bodyBb in _while.Body.BigBlocks) + { + BigBlock bodyBbCopy = null; + if (proofGenInfo.GetMappingOrigBigblockToCopyBigblock().ContainsKey(bodyBb)) + { + bodyBbCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[bodyBb]; + } + else + { + foreach (var tuple in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (tuple.Value == bodyBb) + { + bodyBbCopy = tuple.Key; + break; + } + } + } + + if (bodyBbCopy != null) + { + string currBigBlockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(bodyBbCopy).First(); + string currContId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[bodyBbCopy]; + + unfoldedLoopBodyDefinitions = + unfoldedLoopBodyDefinitions + currBigBlockId + "_def " + currContId + "_def "; + } + } + + unfoldedLoopBodyDefinitions += ")"; + #endregion + + string afterLoopBigBlockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(afterLoopBigBlockCopy).First(); + + string succBlockId1 = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(succ1); + string succNodeId1 = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(succ1); + string succOutEdgesId1 = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(succ1); + + string afterLoopContId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[afterLoopBigBlockCopy]; + + string nameLemmaSucc1 = mappingBigBlockToGlobalLemmaDecl[bodyBigBlockCopy].Name; + string nameLemmaSucc2 = mappingBigBlockToGlobalLemmaDecl[afterLoopBigBlockCopy].Name; + + string unwrappedAfterLoopBigBlockId = ""; + string unwrappedAfterLoopContId = ""; + string nameLemmaSucc3 = ""; + if (unwrappedAfterLoopBigBlockCopy != null) + { + unwrappedAfterLoopBigBlockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(unwrappedAfterLoopBigBlockCopy).First(); + unwrappedAfterLoopContId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[unwrappedAfterLoopBigBlockCopy]; + nameLemmaSucc3 = mappingBigBlockToGlobalLemmaDecl[unwrappedAfterLoopBigBlockCopy].Name; + } + + #region construct proof + + var beginningOfProof = new List + { + "using assms", + "proof (induction j arbitrary: ns1 rule: less_induct)", + "case (less j)", + "then show ?case", + "proof (cases j)", + "case 0", + "then show ?thesis", + "using valid_configuration_def less.prems(1) is_final.elims(2) " + contId + "_def" + " by fastforce", + "next", + "case (Suc j')", + "show ?thesis", + ProofUtil.Apply("rule block_global_rel_loop_head "), + ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + " _ _ _ " + blockId + "]"), + ProofUtil.Apply("simp add:" + bigblockId + "_def " + blockId + "_def"), + ProofUtil.Apply("rule less(2)"), + ProofUtil.Apply("rule less(3), simp"), + ProofUtil.Apply("rule less(4), simp"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("simp add:" + bigblockId + "_def"), + "apply simp ", + ProofUtil.Apply("rule block_local_rel_loop_head"), + ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + "]"), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + bigblockId + "_def")), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + blockId + "_def " + nodeId)), + ProofUtil.Apply("rule " + contId + "_def"), + ProofUtil.Apply("erule disjE") + }; + if (personalGuard == null) + { + beginningOfProof.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + "apply (erule disjE)", + //TODO: get rid of this + "defer" + }); + } + beginningOfProof.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succ2UniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + outEdgesId)), + ProofUtil.Apply("simp add:member_rec(1)") + }); + if (personalGuard != null) { beginningOfProof.Add(ProofUtil.Apply("erule conjE")); } + beginningOfProof.AddRange(new List + { + ProofUtil.Apply("rule " + nameLemmaSucc1 + ""), + unfoldedLoopBodyDefinitions, + ProofUtil.Apply(ProofUtil.Repeat("blast")), + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule less.IH"), + ProofUtil.Apply("erule strictly_smaller_helper2"), + ProofUtil.Apply(ProofUtil.Simp()), + "unfolding " + contId + "_def " + bodyBigBlockContId + "_def", + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("blast"), + ProofUtil.Apply("blast") + }); + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) && + personalGuard == null) + { + beginningOfProof.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + + var insideOfLoopAddition1 = new List + { + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + + proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) && + personalGuard == null + ? ProofUtil.Apply("rule less(6)") + : ProofUtil.Apply("rule less(5)"), + + ProofUtil.Apply("rule strictly_smaller_helper3"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + }; + + var insideOfLoopAddition2 = new List + { + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + + proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) && + personalGuard == null + ? ProofUtil.Apply("rule less(6)") + : ProofUtil.Apply("rule less(5)"), + + ProofUtil.Apply("rule strictly_smaller_helper4"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + }; + + var whileTrueEndingAfterSkippingEndBlock = new List(); + if (personalGuard == null && !proofGenInfo.GetMappingBigBlockToLoopBigBlock() + .ContainsKey(correspondingBigBlockOrig)) + { + whileTrueEndingAfterSkippingEndBlock.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + whileTrueEndingAfterSkippingEndBlock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succ1UniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + outEdgesId)), + ProofUtil.Apply("simp add:member_rec(1)") + }); + if (personalGuard != null) { whileTrueEndingAfterSkippingEndBlock.Add(ProofUtil.Apply("erule conjE")); } + whileTrueEndingAfterSkippingEndBlock.AddRange(new List + { + ProofUtil.Apply("rule ending_after_skipping_endblock2"), + //TODO: repeat is possibly redundant here! + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("blast") + }); + whileTrueEndingAfterSkippingEndBlock.Add(personalGuard != null + ? ProofUtil.Apply("blast") + : ProofUtil.Apply(ProofUtil.Repeat("blast"))); + if (personalGuard != null) { whileTrueEndingAfterSkippingEndBlock.Add(ProofUtil.Apply(ProofUtil.Simp())); } + if (personalGuard != null && !personalGuard.Equals(Expr.True)) + { + whileTrueEndingAfterSkippingEndBlock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule " + nameLemmaSucc2 + ""), + ProofUtil.Apply("blast") + }); + } + if (personalGuard == null) + { + whileTrueEndingAfterSkippingEndBlock.AddRange(new List + { + ProofUtil.Apply("rule " + nameLemmaSucc2), + ProofUtil.Apply(ProofUtil.Simp()) + }); + } + whileTrueEndingAfterSkippingEndBlock.Add(ProofUtil.Apply("blast")); + + var endingAfterSkippingEndblock = new List(); + if (personalGuard == null && + !proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + endingAfterSkippingEndblock.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succ1UniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + outEdgesId)), + ProofUtil.Apply("simp add:member_rec(1)") + }); + if (personalGuard != null) { endingAfterSkippingEndblock.Add(ProofUtil.Apply("erule conjE")); } + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply("rule ending_after_skipping_endblock2"), + //TODO: repeat is possibly redundant here! + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("blast") + }); + endingAfterSkippingEndblock.Add(personalGuard != null + ? ProofUtil.Apply("blast") + : ProofUtil.Apply(ProofUtil.Repeat("blast"))); + if (personalGuard != null) { endingAfterSkippingEndblock.Add(ProofUtil.Apply(ProofUtil.Simp())); } + if (personalGuard != null && !personalGuard.Equals(Expr.True)) + { + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule " + nameLemmaSucc2 + ""), + ProofUtil.Apply("blast") + }); + } + if (personalGuard == null) + { + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply("rule " + nameLemmaSucc2), + ProofUtil.Apply(ProofUtil.Simp()) + }); + } + if (proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.Add(ProofUtil.Apply(ProofUtil.Repeat("blast"))); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.Add(personalGuard != null + ? ProofUtil.Apply("rule correctness_propagates_through_assumption") + : ProofUtil.Apply("rule correctness_propagates_through_empty")); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.AddRange(new List + { + "apply blast", + ProofUtil.Apply("simp add: " + succNodeId1 + ""), + ProofUtil.Apply("simp add: " + succBlockId1 + "_def") + }); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig) && (personalGuard != null)) + { + endingAfterSkippingEndblock.Add(ProofUtil.Apply(NegationRule(personalGuard))); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("simp add: " + succOutEdgesId1 + ""), + ProofUtil.Apply(ProofUtil.Repeat("simp add: member_rec")) + }); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.Add(personalGuard != null + ? ProofUtil.Apply("rule correctness_propagates_through_assumption3") + : ProofUtil.Apply("rule correctness_propagates_through_empty2")); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.AddRange(new List + { + "apply blast", + ProofUtil.Apply("simp add: " + succNodeId1 + ""), + ProofUtil.Apply("simp add: " + succBlockId1 + "_def") + }); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig) && (personalGuard != null)) + { + endingAfterSkippingEndblock.Add(ProofUtil.Apply(NegationRule(personalGuard))); + } + if (!proofGenInfo.GetloopEndingBlocks().Contains(afterLoopBigBlockOrig)) + { + endingAfterSkippingEndblock.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("simp add: " + succOutEdgesId1 + ""), + ProofUtil.Apply(ProofUtil.Repeat("simp add: member_rec")) + }); + } + + var endingAfterSkippingEndBlockAndUnwrapping = new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succ1UniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + outEdgesId)), + ProofUtil.Apply("simp add:member_rec(1)"), + ProofUtil.Apply("erule conjE"), + + ProofUtil.Apply("rule ending_after_skipping_endblock_and_unwrapping"), + "apply assumption", + ProofUtil.Apply("simp add: " + afterLoopBigBlockId + "_def"), + ProofUtil.Apply(ProofUtil.Simp()), + "apply assumption", + "apply blast", + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("simp add:" + succNodeId1 + ""), + ProofUtil.Apply("simp add:" + succBlockId1 + "_def"), + ProofUtil.Apply(NegationRule(personalGuard)), + ProofUtil.Apply("simp add:" + succOutEdgesId1), + ProofUtil.Apply("simp add: member_rec"), + ProofUtil.Apply("simp add:" + succNodeId1), + ProofUtil.Apply("simp add:" + succOutEdgesId1), + ProofUtil.Apply("simp add: member_rec"), + ProofUtil.Apply("rule " + nameLemmaSucc3), + ProofUtil.Apply("simp add: " + unwrappedAfterLoopBigBlockId + "_def " + afterLoopContId + "_def " + unwrappedAfterLoopContId + "_def"), + ProofUtil.Apply("rule correctness_propagates_through_assumption"), + "apply blast", + ProofUtil.Apply("simp add:" + succNodeId1 + ""), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("simp add:" + succOutEdgesId1 + ""), + ProofUtil.Apply("simp add: member_rec"), + "apply blast", + ProofUtil.Apply("rule correctness_propagates_through_assumption3"), + ProofUtil.Apply(ProofUtil.Repeat("simp add:" + succNodeId1)), + ProofUtil.Apply("simp add:" + succOutEdgesId1), + ProofUtil.Apply(ProofUtil.Repeat("simp add: member_rec")) + }; + + proofMethods.AddRange(beginningOfProof); + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofMethods.AddRange(insideOfLoopAddition1); + } + + //TODO: do the second check if BlockCoalescing is activated, otherwise keep as is. + if (personalGuard != null && personalGuard.Equals(Expr.True)) + { + proofMethods.AddRange(whileTrueEndingAfterSkippingEndBlock); + } + else + { + proofMethods.AddRange(endingAfterSkippingEndblock); + } + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofMethods.AddRange(insideOfLoopAddition2); + } + + proofMethods.AddRange( + new List + { + "done", + "qed", + "qed" + }); + + #endregion + + } + return proofMethods; + } + + private List GenerateProofGeneric( + BigBlock startingBigBlock, + Block relatedBlock, + Expr entryGuard, + BranchIndicator indicator, + AstToCfgProofGenInfo proofGenInfo) + { + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + BigBlock successorBigBlockOrig = correspondingBigBlockOrig.successorBigBlock; + BigBlock successorBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[successorBigBlockOrig]; + + foreach (var kvPair in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (kvPair.Value == successorBigBlockOrig) + { + successorBigBlockCopy = kvPair.Key; + break; + } + } + + int succUniqueLoopLabel = -1; + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + BigBlock loop = proofGenInfo.GetMappingBigBlockToLoopBigBlock()[correspondingBigBlockOrig]; + BigBlock loopCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[loop]; + + IDictionary origBlockBeforeDag = beforeDagOrigBlock.InverseDict(); + + Block enclosingLoopCfgBlock = proofGenInfo.GetMappingOrigBigBlockToOrigBlock()[loop]; + Block enclosingLoopCfgBlockCopy = origBlockBeforeDag[enclosingLoopCfgBlock]; + + Block successorCfgBlock = afterCfg.GetSuccessorBlocks(enclosingLoopCfgBlockCopy).First(); + succUniqueLoopLabel = afterCfg.GetUniqueIntLabel(successorCfgBlock); + } + + var proofMethods = new List(); + IEnumerable successors = afterCfg.GetSuccessorBlocks(relatedBlock); + if (successors != null && successors.Any()) + { + Block succ1 = successors.First(); + int succUniqueIntLabel = afterCfg.GetUniqueIntLabel(succ1); + + string nameLemmaLocal = "prelimNameTest"; + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + nameLemmaLocal = mappingBigBlockToLocalLemmaDecl[startingBigBlock].Name; + } + + string nameLemmaSucc = "nameLemmaSuccTest"; + if (!proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) || + correspondingBigBlockOrig.successorBigBlock != proofGenInfo.GetMappingBigBlockToLoopBigBlock()[correspondingBigBlockOrig]) + { + nameLemmaSucc = mappingBigBlockToGlobalLemmaDecl[successorBigBlockCopy].Name; + } + + var beginningOfProofA = new List + { + "proof -", + "show ?thesis ", + ProofUtil.Apply("rule block_global_rel_generic"), + startingBigBlock.simpleCmds.Any() ? ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + "]") : ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + "]") , + ProofUtil.Apply("simp add: " + bigblockId + "_def") + }; + if (startingBigBlock.simpleCmds.Any()) + { + beginningOfProofA.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + var beginningOfProofB = new List + { + ProofUtil.Apply("rule astTrace"), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply("rule " + nodeId + "") + }; + + var beginningOfProof = new List(); + beginningOfProof.AddRange(beginningOfProofA); + beginningOfProof.AddRange(beginningOfProofB); + + var middlePartOfProof = new List(); + if (indicator == 0 || entryGuard == null) + { + middlePartOfProof = new List + { + ProofUtil.Apply("rule disjI1"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule " + contId + "_def"), + ProofUtil.Apply("simp add: " + nodeId + "") + }; + + if (startingBigBlock.simpleCmds.Any()) + { + middlePartOfProof.Add(ProofUtil.Apply("rule " + nameLemmaLocal + "")); + middlePartOfProof.Add("apply assumption"); + middlePartOfProof.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + } + else if (indicator == BranchIndicator.GuardFails) + { + middlePartOfProof = new List + { + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("" + NegationRule(entryGuard) + ""), + "using guardHint " + ProofUtil.Apply((ProofUtil.Simp())), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule " + contId + "_def"), + ProofUtil.Apply("simp add: " + nodeId + "") + }; + + if (startingBigBlock.simpleCmds.Any()) + { + middlePartOfProof.Add(ProofUtil.Apply("rule " + nameLemmaLocal + "")); + middlePartOfProof.Add("apply assumption"); + middlePartOfProof.Add(ProofUtil.Apply(ProofUtil.Simp())); + middlePartOfProof.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + } + //TODO: this check needs revision, I don't understand why this condition works. It might fail for complex procedures. + else if (indicator == BranchIndicator.GuardHolds || proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + middlePartOfProof = new List + { + ProofUtil.Apply("rule disjI2"), + ProofUtil.Apply("rule disjI1"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()), + "using guardHint " + ProofUtil.Apply((ProofUtil.Simp())), + ProofUtil.Apply("rule cfgDoesntFail"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule " + contId + "_def"), + ProofUtil.Apply("simp add: " + nodeId + "") + }; + + if (startingBigBlock.simpleCmds.Any()) + { + middlePartOfProof.Add(ProofUtil.Apply("rule " + nameLemmaLocal + "")); + middlePartOfProof.Add("apply assumption"); + middlePartOfProof.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + + if (startingBigBlock.simpleCmds.Any() && indicator == BranchIndicator.GuardHolds) + { + middlePartOfProof.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + + } + + var proofEnd = new List(); + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) && + correspondingBigBlockOrig.successorBigBlock.ec is WhileCmd) + { + proofEnd = new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x=" + succUniqueLoopLabel + "]")), + ProofUtil.Apply("simp add: " + outEdgesId + ""), + ProofUtil.Apply("simp add: member_rec(1)"), + ProofUtil.Apply("rule loop_IH_apply"), + ProofUtil.Apply("rule inductionHypothesis"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }; + } + else + { + proofEnd = new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succUniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add: " + outEdgesId)), + ProofUtil.Apply("simp add: member_rec(1)"), + ProofUtil.Apply("rule " + nameLemmaSucc + "") + }; + + if (!proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofEnd.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + + proofEnd.Add(proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) + ? "apply blast+" + : "apply auto"); + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofEnd.Add(ProofUtil.Apply("rule loop_IH_prove")); + proofEnd.Add(ProofUtil.Apply("rule loop_IH_apply")); + proofEnd.Add(ProofUtil.Apply("rule inductionHypothesis")); + proofEnd.Add(ProofUtil.Apply("rule less_trans_inv")); + proofEnd.Add("apply auto"); + } + + } + + proofMethods.AddRange(beginningOfProof); + proofMethods.AddRange(middlePartOfProof); + proofMethods.AddRange(proofEnd); + proofMethods.AddRange( + new List + { + "done", + "qed" + }); + } + return proofMethods; + } + + + private List GenerateEndingAfterUnwrappingProof( + BigBlock startingBigBlock, + Block relatedBlock, + Expr entryGuard, + BranchIndicator indicator, + AstToCfgProofGenInfo proofGenInfo) + { + BigBlock correspondingOrigBigBlock = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + List loopExtension = new List(); + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingOrigBigBlock)) + { + loopExtension = new List + { + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + ProofUtil.Apply("rule inductionHypothesis"), + ProofUtil.Apply("rule strictly_smaller_helper2"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + }; + } + + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(relatedBlock); + + BigBlock correspondingOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + BigBlock unwrappedBigBlockCopy = null; + foreach (var tuple in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (tuple.Value == correspondingOrig) + { + unwrappedBigBlockCopy = tuple.Key; + break; + } + } + + string succBigBlockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(unwrappedBigBlockCopy).First(); + + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + string succContId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[unwrappedBigBlockCopy]; + string nameLemmaSuccGlobal = mappingBigBlockToGlobalLemmaDecl[unwrappedBigBlockCopy].Name; + + string correctnessPropagates = null; + string correctnessPropagatesPosts = null; + if (entryGuard == null) + { correctnessPropagates = ProofUtil.Apply("rule correctness_propagates_through_empty"); + correctnessPropagatesPosts = ProofUtil.Apply("rule correctness_propagates_through_empty2"); } + else if (indicator == BranchIndicator.GuardFails) + { correctnessPropagates = ProofUtil.Apply("rule correctness_propagates_through_assumption"); + correctnessPropagatesPosts = ProofUtil.Apply("rule correctness_propagates_through_assumption3"); } + else if (indicator == BranchIndicator.GuardHolds) + { correctnessPropagates = ProofUtil.Apply("rule correctness_propagates_through_assumption2"); + correctnessPropagatesPosts = ProofUtil.Apply("rule correctness_propagates_through_assumption4"); } + else if (indicator == BranchIndicator.NoGuard) + { correctnessPropagates = ProofUtil.Apply("rule correctness_propagates_through_empty"); + correctnessPropagatesPosts = ProofUtil.Apply("rule correctness_propagates_through_empty2"); } + + string rule = "apply( " + NegationRule(entryGuard) + ")"; + if (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.NoGuard || entryGuard == null) + { + rule = ""; + } + + List proofMethods = new List + { + ProofUtil.Apply("rule ending_after_unwrapping"), + ProofUtil.Apply("rule astTrace"), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply("rule cfgDoesntFail, simp"), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule " + nameLemmaSuccGlobal + ""), + ProofUtil.Apply("simp add: " + succBigBlockId + "_def " + contId + "_def " + succContId + "_def") + }; + + if (correctnessPropagates != null) { proofMethods.Add(correctnessPropagates); } + + proofMethods.AddRange( new List + { + "using assms(2)", + "apply blast", + ProofUtil.Apply("simp add: " + nodeId + ""), + ProofUtil.Apply("simp add: " + blockId + "_def") + }); + + if (indicator != BranchIndicator.NoGuard) { proofMethods.Add(rule); } + if (indicator != BranchIndicator.NoGuard && entryGuard != null) { proofMethods.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); } + + proofMethods.AddRange( new List + { + ProofUtil.Apply("simp add: " + outEdgesId + ""), + ProofUtil.Apply("simp add: member_rec"), + ProofUtil.Apply(ProofUtil.Simp()) + }); + + if (correctnessPropagatesPosts != null) { proofMethods.Add(correctnessPropagatesPosts); } + + proofMethods.AddRange( new List + { + "using assms(3)", + "apply blast", + ProofUtil.Apply("simp add: " + nodeId + ""), + ProofUtil.Apply("simp add: " + blockId + "_def") + }); + + if (indicator != BranchIndicator.NoGuard) { proofMethods.Add(rule); } + if (indicator != BranchIndicator.NoGuard && entryGuard != null) { proofMethods.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); } + + proofMethods.AddRange( new List + { + ProofUtil.Apply("simp add: " + outEdgesId + ""), + ProofUtil.Apply("simp add: member_rec"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + + if (loopExtension.Any()) + { + proofMethods.AddRange(loopExtension); + } + proofMethods.Add("done"); + + return proofMethods; + } + + private List GenerateWhileSuccessorProof( + BigBlock startingBigBlock, + Block relatedBlock, + Expr entryGuard, + Expr personalGuard, + BranchIndicator indicator, + AstToCfgProofGenInfo proofGenInfo) + { + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + afterCfg.GetUniqueIntLabel(relatedBlock); + + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + BigBlock successorBigBlockCopy = null; + foreach (var tuple in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (correspondingBigBlockOrig == tuple.Value) + { + successorBigBlockCopy = tuple.Key; + break; + } + } + + string succBigBlockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(successorBigBlockCopy).First(); + + WhileCmd wcmd = (WhileCmd) correspondingBigBlockOrig.ec; + BigBlock bodyBigBlockOrig = wcmd.Body.BigBlocks.First(); + BigBlock bodyBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[bodyBigBlockOrig]; + + var proofMethods = new List(); + IEnumerable successors = afterCfg.GetSuccessorBlocks(relatedBlock); + if (successors != null && successors.Any()) + { + Block succ = successors.First(); + int succUniqueIntLabel = afterCfg.GetUniqueIntLabel(succ); + + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + string successorContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[successorBigBlockCopy]); + + string nameLemmaSucc = mappingBigBlockToGlobalLemmaDecl[successorBigBlockCopy].Name; + string nameLemmaLocal = null; + + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + nameLemmaLocal = mappingBigBlockToLocalLemmaDecl[startingBigBlock].Name; + } + + proofMethods = new List + { + "proof -", + "show ?thesis", + ProofUtil.Apply("rule block_global_rel_while_successor"), + ProofUtil.Apply("rule astTrace"), + indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails ? + ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + "]") : ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + " _ " + blockId + "]"), + ProofUtil.Apply("simp add: " + bigblockId + "_def " + blockId + "_def"), + ProofUtil.Apply("simp add: " + bigblockId + "_def " + blockId + "_def"), + ProofUtil.Apply("simp add: " + bigblockId + "_def " + blockId + "_def"), + ProofUtil.Apply("simp add: " + blockId + "_def"), + ProofUtil.Apply("rule " + nodeId + "") + }; + + if (entryGuard == null) { proofMethods.Add(ProofUtil.Apply("rule disjI1")); } + if (entryGuard != null) + { + if (indicator == BranchIndicator.NoGuard) + { + proofMethods.Add(ProofUtil.Apply("rule disjI1")); + } + else + { + proofMethods.Add(ProofUtil.Apply("rule disjI2")); + } + } + if (entryGuard != null && indicator == BranchIndicator.GuardHolds) { proofMethods.Add(ProofUtil.Apply("rule disjI1")); } + if (entryGuard != null && indicator == BranchIndicator.GuardFails) { proofMethods.Add(ProofUtil.Apply("rule disjI2")); } + + proofMethods.Add(ProofUtil.Apply("simp add: " + blockId + "_def")); + + if (entryGuard != null && + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails)) + { + proofMethods.Add(ProofUtil.Apply("rule conjI")); + proofMethods.Add(ProofUtil.Apply(ProofUtil.Simp())); + proofMethods.Add(ProofUtil.Apply("rule conjI")); + proofMethods.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + + if (entryGuard != null && (indicator == BranchIndicator.GuardFails)) + { + proofMethods.Add(ProofUtil.Apply("rule conjI")); + proofMethods.Add(ProofUtil.Apply("" + NegationRule(entryGuard) + "")); + } + + if (entryGuard != null && + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails)) + { + proofMethods.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + + proofMethods.AddRange(new List + { + ProofUtil.Apply("rule cfgDoesntFail, simp"), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + proofMethods.AddRange(new List + { + ProofUtil.Apply("simp add: " + nodeId + ""), + ProofUtil.Apply("rule " + nameLemmaLocal + ""), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + } + + if (entryGuard != null && + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails)) + { + proofMethods.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + + proofMethods.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succUniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add: " + outEdgesId)), + ProofUtil.Apply("simp add: member_rec(1)"), + ProofUtil.Apply("rule " + nameLemmaSucc + ""), + ProofUtil.Apply("simp add: " + succBigBlockId + "_def " + contId + "_def "+ successorContId + "_def") + }); + + proofMethods.Add(proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) + ? "apply blast+" + : "apply auto"); + + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + proofMethods.AddRange(new List + { + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + ProofUtil.Apply("rule inductionHypothesis"), + ProofUtil.Apply("rule less_trans_inv"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + } + + proofMethods.AddRange(new List + { + "done", + "qed" + }); + } + return proofMethods; + } + + private List GenerateIfSuccessorProof( + BigBlock startingBigBlock, + Block relatedBlock, + Expr personalGuard, + Expr entryGuard, + BranchIndicator indicator, + AstToCfgProofGenInfo proofGenInfo) + { + string bigblockId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(startingBigBlock).First(); + string blockId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".block_" + + afterCfg.GetUniqueIntLabel(relatedBlock); + string nodeId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".node_" + + afterCfg.GetUniqueIntLabel(relatedBlock); + string outEdgesId = afterCfgProgAccess.BlockInfo().getTheoryName() + ".outEdges_" + + afterCfg.GetUniqueIntLabel(relatedBlock); + + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[startingBigBlock]; + + IfCmd _if = (IfCmd) correspondingBigBlockOrig.ec; + BigBlock thenBranchOrig = _if.thn.BigBlocks.First(); + BigBlock thenBranchCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[thenBranchOrig]; + string thenBranchId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(thenBranchCopy).First(); + string thenBranchContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[thenBranchCopy]); + string nameLemmaThen = mappingBigBlockToGlobalLemmaDecl[thenBranchCopy].Name; + + string nameLemmaElse = "noLemmaElse"; + if (_if.elseBlock != null) + { + BigBlock elseBranchOrig = _if.elseBlock.BigBlocks.First(); + BigBlock elseBranchCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[elseBranchOrig]; + string elseBranchId = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(elseBranchCopy).First(); + string elseBranchContId = "cont_" + (proofGenInfo.GetMappingCopyBigBlockToIndex()[elseBranchCopy]); + nameLemmaElse = mappingBigBlockToGlobalLemmaDecl[elseBranchCopy].Name; + } + + var proofMethods = new List(); + IEnumerable successors = afterCfg.GetSuccessorBlocks(relatedBlock); + if (successors != null && successors.Any()) + { + Block succ = successors.First(); + Block succ2 = successors.Last(); + int succUniqueIntLabel = afterCfg.GetUniqueIntLabel(succ); + int succUniqueIntLabel2 = afterCfg.GetUniqueIntLabel(succ2); + + string contId = "cont_" + proofGenInfo.GetMappingCopyBigBlockToIndex()[startingBigBlock]; + string nameLemmaLocal = null; + + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + nameLemmaLocal = mappingBigBlockToLocalLemmaDecl[startingBigBlock].Name; + } + + var beginningOfProof = new List + { + "proof -", + "show ?thesis", + ProofUtil.Apply("rule block_global_rel_if_successor"), + startingBigBlock.simpleCmds.Any() ? + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails ? + ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + "]") : ProofUtil.Apply("rule Rel_Main_test[of " + bigblockId + " _ " + blockId + "]")) : + ProofUtil.Apply("rule Rel_Invs[of " + bigblockId + "]"), + ProofUtil.Apply("simp add: " + blockId + "_def " + bigblockId + "_def") + }; + if (startingBigBlock.simpleCmds.Any()) { beginningOfProof.Add(ProofUtil.Apply("simp add: " + blockId + "_def")); } + beginningOfProof.AddRange(new List + { + ProofUtil.Apply("rule astTrace"), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply("rule " + nodeId + "") + }); + + var middlePartOfProof = new List(); + if (entryGuard == null) { middlePartOfProof.Add(ProofUtil.Apply("rule disjI1")); } + if (entryGuard != null) + { + middlePartOfProof.Add(indicator == BranchIndicator.NoGuard + ? ProofUtil.Apply("rule disjI1") + : ProofUtil.Apply("rule disjI2")); + } + if (entryGuard != null && indicator == BranchIndicator.GuardHolds) { middlePartOfProof.Add(ProofUtil.Apply("rule disjI1")); } + if (entryGuard != null && indicator == BranchIndicator.GuardFails) { middlePartOfProof.Add(ProofUtil.Apply("rule disjI2")); } + middlePartOfProof.Add(ProofUtil.Apply("simp add: " + blockId + "_def")); + if (entryGuard != null && + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails)) + { + middlePartOfProof.AddRange(new List + { + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply(ProofUtil.Simp()) + }); + } + if (entryGuard != null && (indicator == BranchIndicator.GuardFails)) + { + middlePartOfProof.AddRange(new List + { + ProofUtil.Apply("rule conjI"), + ProofUtil.Apply("" + NegationRule(entryGuard) + "") + }); + } + if (entryGuard != null && + (indicator == BranchIndicator.GuardHolds || indicator == BranchIndicator.GuardFails)) + { + middlePartOfProof.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + middlePartOfProof.AddRange(new List + { + ProofUtil.Apply("rule cfgDoesntFail, simp"), + ProofUtil.Apply("rule cfgSatisfiesPosts, blast"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + if (mappingBigBlockToLocalLemmaDecl.ContainsKey(startingBigBlock)) + { + middlePartOfProof.AddRange(new List + { + ProofUtil.Apply("simp add: " + nodeId + ""), + ProofUtil.Apply("rule " + nameLemmaLocal + ""), + ProofUtil.Apply("simp add: " + bigblockId + "_def"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + } + if (entryGuard != null && startingBigBlock.simpleCmds.Any() && (indicator == BranchIndicator.GuardHolds || + indicator == BranchIndicator.GuardFails)) + { + middlePartOfProof.Add("using guardHint " + ProofUtil.Apply(ProofUtil.Simp())); + } + if (personalGuard != null) { middlePartOfProof.Add(ProofUtil.Apply("erule disjE")); } + if (personalGuard == null) { middlePartOfProof.Add(ProofUtil.Apply("rule disjE, simp")); } + + + var finalPartOfProof = new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succUniqueIntLabel + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add: " + outEdgesId)), + ProofUtil.Apply("simp add: member_rec(1)"), + ProofUtil.Apply("rule conjE"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule " + nameLemmaThen + ""), + ExpandDefinitions(contId, startingBigBlock, proofGenInfo, BranchIndicator.GuardHolds), + "apply blast", + "apply blast" + }; + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + finalPartOfProof.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + ProofUtil.Apply("rule inductionHypothesis"), + ProofUtil.Apply("rule less_trans_inv"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + } + + if (personalGuard != null && + !proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + finalPartOfProof.Add(ProofUtil.Apply(ProofUtil.Simp())); + } + + finalPartOfProof.AddRange(new List + { + ProofUtil.Apply(ProofUtil.Repeat("erule allE[where x = " + succUniqueIntLabel2 + "]")), + ProofUtil.Apply(ProofUtil.Repeat("simp add: " + outEdgesId)), + ProofUtil.Apply("simp add: member_rec(1)"), + ProofUtil.Apply("rule conjE"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())), + ProofUtil.Apply("rule " + nameLemmaElse + ""), + ExpandDefinitions(contId, startingBigBlock, proofGenInfo, BranchIndicator.GuardFails) + }); + + finalPartOfProof.Add(proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig) + ? "apply blast+" + : "apply auto"); + + if (proofGenInfo.GetMappingBigBlockToLoopBigBlock().ContainsKey(correspondingBigBlockOrig)) + { + finalPartOfProof.AddRange(new List + { + ProofUtil.Apply("rule loop_IH_prove"), + ProofUtil.Apply("rule loop_IH_apply"), + ProofUtil.Apply("rule inductionHypothesis"), + ProofUtil.Apply("rule less_trans_inv"), + ProofUtil.Apply(ProofUtil.Repeat(ProofUtil.Simp())) + }); + } + + finalPartOfProof.AddRange(new List + { + "done", + "qed" + }); + + proofMethods.AddRange(beginningOfProof); + proofMethods.AddRange(middlePartOfProof); + proofMethods.AddRange(finalPartOfProof); + } + return proofMethods; + } + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/AstToCfg/AstToCfgManager.cs b/Source/ProofGeneration/AstToCfg/AstToCfgManager.cs new file mode 100644 index 000000000..dc6211bcf --- /dev/null +++ b/Source/ProofGeneration/AstToCfg/AstToCfgManager.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.Boogie; +using ProofGeneration.ASTRepresentation; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.PhasesUtil; +using ProofGeneration.Util; + +namespace ProofGeneration.AstToCfg +{ + internal class AstToCfgManager + { + public static Theory AstToCfgProof( + string uniqueTheoryName, + PhasesTheories phasesTheories, + EndToEndLemmaConfig endToEndLemmaConfig, + ProofGenConfig config, + Term vcAssm, + AstToCfgProofGenInfo proofGenInfo, + ASTRepr beforeCfgAst, + CFGRepr afterCfg, + IDictionary beforeDagOrigBlock, + IDictionary mappingWithHints, + IDictionary beforeToAfter, + IProgramAccessor beforeCfgProgAccess, + IProgramAccessor afterCfgProgAccess, + IVariableTranslationFactory varFactory, + MultiCmdIsaVisitor multiCmdIsaVisitor) + { + LemmaDecl entryLemma = null; + + var varContextName = "\\1"; + var varContextAbbrev = new AbbreviationDecl( + varContextName, + new Tuple, Term>(new List(), beforeCfgProgAccess.VarContext()) + ); + + var funContextWfName = "Wf_Fun"; + var astBoogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("M"), + IsaCommonTerms.TermIdentFromName(varContextName), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.EmptyList); + var cfgBoogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("M"), + IsaCommonTerms.TermIdentFromName(varContextName), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.EmptyList); + + var lemmaManager = new AstToCfgLemmaManager( + beforeCfgProgAccess, + afterCfgProgAccess, + astBoogieContext, + cfgBoogieContext, + afterCfg, + funContextWfName, + beforeDagOrigBlock, + beforeToAfter, + varFactory); + + var lemmaNamer = new IsaUniqueNamer(); + IList outerDecls = new List(); + + outerDecls.Add(varContextAbbrev); + outerDecls.Add(new DeclareDecl("Nat.One_nat_def[simp del]")); + + foreach (BigBlock beforeBlock in beforeCfgAst.GetBlocksBackwards()) + { + Block afterBlock = beforeToAfter[beforeBlock]; + + int bigblockIndex = proofGenInfo.GetMappingCopyBigBlockToIndex()[beforeBlock]; + + BigBlock successorBigBlockOrig; + BigBlock successorBigBlockCopy; + int succBigBlockIndex = -1; + Block successorBlock; + int succBlockIndex = -1; + if (proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[beforeBlock].successorBigBlock != null) + { + successorBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[beforeBlock].successorBigBlock; + successorBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[successorBigBlockOrig]; + succBigBlockIndex = proofGenInfo.GetMappingCopyBigBlockToIndex()[successorBigBlockCopy]; + successorBlock = beforeToAfter[successorBigBlockCopy]; + succBlockIndex = afterCfg.GetUniqueIntLabel(successorBlock); + } + + (Block, Expr, BranchIndicator) hints = mappingWithHints[beforeBlock]; + + if (beforeBlock.simpleCmds.Any() && multiCmdIsaVisitor.Translate(beforeBlock.simpleCmds).Any()) + { + LemmaDecl localLemmaDecl = lemmaManager.LocalLemma( + beforeBlock, + afterBlock, + hints.Item2, + bigblock => GetLemmaName(bigblock, lemmaNamer, beforeCfgProgAccess.BigBlockInfo()), + hints.Item3 + ); + outerDecls.Add(localLemmaDecl); + } + + LemmaDecl globalLemmaDecl = + lemmaManager.GenerateGlobalLemma( + beforeBlock, + IsaCommonTerms.TermIdentFromName("cont_" + bigblockIndex), + afterBlock, + IsaCommonTerms.TermIdentFromName(beforeCfgProgAccess.BigBlockInfo().TheoryName + ".post"), + (hints.Item2, hints.Item3), + bigblock => GetGlobalLemmaName(bigblock, lemmaNamer, beforeCfgProgAccess.BigBlockInfo()), + proofGenInfo + ); + outerDecls.Add(globalLemmaDecl); + + if (proofGenInfo.GetMappingCopyBigBlockToIndex()[beforeBlock] == 0) + { + entryLemma = globalLemmaDecl; + } + } + + if (entryLemma == null) + { + throw new ProofGenUnexpectedStateException("AST-to-CFG phase: entry lemma not assigned"); + } + + var absValType = new VarType("a"); + var cfgToDagLemmasLocale = new LocaleDecl( + "ast_to_cfg_lemmas", + new ContextElem( + new List> + { + Tuple.Create((TermIdent) astBoogieContext.absValTyMap, + IsaBoogieType.AbstractValueTyFunType(absValType)), + Tuple.Create((TermIdent) astBoogieContext.funContext, IsaBoogieType.FunInterpType(absValType)) + }, + new List + { + IsaBoogieTerm.FunInterpWf(astBoogieContext.absValTyMap, beforeCfgProgAccess.FunctionsDecl(), + astBoogieContext.funContext) + }, + new List {funContextWfName} + ), + outerDecls + ); + + var theoryOuterDecls = new List(); + theoryOuterDecls.Add(cfgToDagLemmasLocale); + + if (endToEndLemmaConfig != EndToEndLemmaConfig.DoNotGenerate) + { + var phaseToConnectTo = config.GenerateCfgOptProof(proofGenInfo.GetOptimizationsFlag()) + ? PhasesTheories.Phase.CfgOptimizations + : PhasesTheories.Phase.CfgToDag; + + var endToEndManager = new AstToCfgEndToEnd(); + var endToEndDecls = endToEndManager.EndToEndProof( + entryLemma.Name, + endToEndLemmaConfig, + phasesTheories.EndToEndLemmaName(phaseToConnectTo, true) + "_theorem_aux", + vcAssm, + beforeCfgProgAccess, + afterCfgProgAccess, + beforeCfgAst, + proofGenInfo + ); + theoryOuterDecls.AddRange(endToEndDecls); + } + + List importTheories = new List + { + "Boogie_Lang.Ast", "Boogie_Lang.Ast_Cfg_Transformation", "Boogie_Lang.Semantics", "Boogie_Lang.Util", + "Boogie_Lang.BackedgeElim", "Boogie_Lang.TypingML", + beforeCfgProgAccess.TheoryName(), + afterCfgProgAccess.TheoryName() + }; + + if (config.GenerateCfgOptProof(proofGenInfo.GetOptimizationsFlag())) + { + importTheories.Add(phasesTheories.TheoryName(PhasesTheories.Phase.CfgOptimizations)); + } + else if(config.GenerateCfgDagProof) + { + importTheories.Add(phasesTheories.TheoryName(PhasesTheories.Phase.CfgToDag)); + } + + return new Theory( + uniqueTheoryName, + importTheories, + theoryOuterDecls + ); + } + private static string GetLemmaName(BigBlock b, IsaUniqueNamer namer, IsaBigBlockInfo bbInfo) + { + return namer.GetName(b, "rel_" + bbInfo.CmdsQualifiedName(b).First()); + } + + private static string GetGlobalLemmaName(BigBlock b, IsaUniqueNamer namer, IsaBigBlockInfo bbInfo) + { + return "global_" + namer.GetName(b, "rel_" + bbInfo.CmdsQualifiedName(b).First()); + } + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/ApplicableIsaVisitor.cs b/Source/ProofGeneration/BoogieIsaInterface/ApplicableIsaVisitor.cs index 53aa8c342..72578dbaa 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/ApplicableIsaVisitor.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/ApplicableIsaVisitor.cs @@ -3,6 +3,7 @@ using Isabelle.Ast; using Microsoft.Boogie; using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.Util; namespace ProofGeneration { @@ -67,10 +68,22 @@ public Term Visit(MapStore mapStore) { throw new NotImplementedException(); } - + public Term Visit(ArithmeticCoercion arithCoercion) { + if (_args.Count != 1) + { + throw new ExprArgException(); + } + + if(arithCoercion.Coercion == ArithmeticCoercion.CoercionType.ToReal) + { + return IsaBoogieTerm.IntToReal(_args[0]); + } + else + { throw new NotImplementedException(); + } } public Term Visit(IfThenElse ifThenElse) diff --git a/Source/ProofGeneration/BoogieIsaInterface/BoogieGlobalData.cs b/Source/ProofGeneration/BoogieIsaInterface/BoogieGlobalData.cs index 26cd9dffc..078634033 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/BoogieGlobalData.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/BoogieGlobalData.cs @@ -6,7 +6,7 @@ namespace ProofGeneration.BoogieIsaInterface public class BoogieGlobalData { public BoogieGlobalData(IEnumerable functions, IEnumerable axioms, - IEnumerable globalVars, IEnumerable constants) + IEnumerable globalVars, IEnumerable constants) { Functions = functions; Axioms = axioms; @@ -17,12 +17,12 @@ public BoogieGlobalData(IEnumerable functions, IEnumerable axio public IEnumerable Functions { get; } public IEnumerable Axioms { get; } public IEnumerable GlobalVars { get; } - public IEnumerable Constants { get; } + public IEnumerable Constants { get; } public static BoogieGlobalData CreateEmpty() { return new BoogieGlobalData(new List(), new List(), new List(), - new List()); + new List()); } } } \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/BoogieMethodData.cs b/Source/ProofGeneration/BoogieIsaInterface/BoogieMethodData.cs index c628b8dc2..1ce4d5f32 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/BoogieMethodData.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/BoogieMethodData.cs @@ -32,7 +32,7 @@ public BoogieMethodData( public IEnumerable Functions => globalData.Functions; public IEnumerable Axioms => globalData.Axioms; public IEnumerable GlobalVars => globalData.GlobalVars; - public IEnumerable Constants => globalData.Constants; + public IEnumerable Constants => globalData.Constants; public IEnumerable TypeParams { get; } public IEnumerable InParams { get; } diff --git a/Source/ProofGeneration/BoogieIsaInterface/IProgramAccessor.cs b/Source/ProofGeneration/BoogieIsaInterface/IProgramAccessor.cs index 3d540ab25..c6935d3cf 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IProgramAccessor.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IProgramAccessor.cs @@ -11,6 +11,7 @@ public interface IGlobalProgramAccessor public Term AxiomsDecl(); public Term ConstsAndGlobalsDecl(); public string ConstsDecl(); + public string UniqueConstsDecl(); public string GlobalsDecl(); /// @@ -43,6 +44,8 @@ public interface IProgramAccessor : IGlobalProgramAccessor public Term VarContext(); public IsaBlockInfo BlockInfo(); + + public IsaBigBlockInfo BigBlockInfo(); public string MembershipLemma(Declaration d); diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaBigBlockInfo.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaBigBlockInfo.cs new file mode 100644 index 000000000..58854eef6 --- /dev/null +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaBigBlockInfo.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Isabelle.Ast; +using Microsoft.Boogie; + +namespace ProofGeneration.BoogieIsaInterface +{ + public class IsaBigBlockInfo + { + public IsaBigBlockInfo( + string theoryName, + IDictionary bigblockIds, + IDictionary> bigblockDefs + ) + { + TheoryName = theoryName; + BigBlockIds = bigblockIds; + BigBlockDefs = bigblockDefs; + } + + public IDictionary BigBlockIds { get; } + public IDictionary> BigBlockDefs { get; } + public string TheoryName { get; } + + public IList CmdsQualifiedName(BigBlock b) + { + return QualifiedName(BigBlockDefs[b]); + } + + private IList QualifiedName(IList decls) + { + IList names = new List(); + foreach (var decl in decls) + { + names.Add(TheoryName + "." + decl.Name); + } + + return names; + } + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaBlockInfo.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaBlockInfo.cs index 7edf5bb75..1a46ccf90 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IsaBlockInfo.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaBlockInfo.cs @@ -25,26 +25,21 @@ IDictionary blockCmdsLemmas public IDictionary BlockIds { get; } public IDictionary BlockCmdsDefs { get; } - public IDictionary BlockOutEdgesLemmas { get; } - public IDictionary BlockCmdsLemmas { get; } - + public string getTheoryName() { return theoryName; } public string CmdsQualifiedName(Block b) { return QualifiedName(BlockCmdsDefs[b]); } - public string OutEdgesMembershipLemma(Block b) { return QualifiedName(BlockOutEdgesLemmas[b]); } - public string BlockCmdsMembershipLemma(Block b) { return QualifiedName(BlockCmdsLemmas[b]); } - private string QualifiedName(OuterDecl decl) { return theoryName + "." + decl.Name; diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieTerm.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieTerm.cs index 40c2bae7e..e1968d6ad 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieTerm.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieTerm.cs @@ -23,6 +23,8 @@ public static class IsaBoogieTerm private static readonly TermIdent boolLitId = IsaCommonTerms.TermIdentFromName("LBool"); private static readonly TermIdent realLitId = IsaCommonTerms.TermIdentFromName("LReal"); + private static readonly TermIdent intToRealId = IsaCommonTerms.TermIdentFromName("IntToReal"); + private static readonly TermIdent varId = IsaCommonTerms.TermIdentFromName("Var"); private static readonly TermIdent bvarId = IsaCommonTerms.TermIdentFromName("BVar"); private static readonly TermIdent oldVarId = IsaCommonTerms.TermIdentFromName("Old"); @@ -40,6 +42,10 @@ public static class IsaBoogieTerm private static readonly TermIdent redCfgMultiId = IsaCommonTerms.TermIdentFromName("red_cfg_multi"); private static readonly TermIdent redCfgKStepId = IsaCommonTerms.TermIdentFromName("red_cfg_k_step"); private static readonly TermIdent redCmdListId = IsaCommonTerms.TermIdentFromName("red_cmd_list"); + private static readonly TermIdent redBigBlockId = IsaCommonTerms.TermIdentFromName("red_bigblock"); + private static readonly TermIdent redBigBlockKStepId = IsaCommonTerms.TermIdentFromName("red_bigblock_k_step"); + private static readonly TermIdent redBigBlockMultiId = IsaCommonTerms.TermIdentFromName("rtranclp"); + private static readonly TermIdent astValidConfigId = IsaCommonTerms.TermIdentFromName("Ast.valid_configuration"); private static readonly TermIdent redExprId = IsaCommonTerms.TermIdentFromName("red_expr"); private static readonly TermIdent normalStateId = IsaCommonTerms.TermIdentFromName("Normal"); private static readonly TermIdent magicStateId = IsaCommonTerms.TermIdentFromName("Magic"); @@ -48,6 +54,8 @@ public static class IsaBoogieTerm private static readonly TermIdent outEdgesId = IsaCommonTerms.TermIdentFromName("out_edges"); private static readonly TermIdent nodeToBlockId = IsaCommonTerms.TermIdentFromName("node_to_block"); private static readonly TermIdent funInterpWfId = IsaCommonTerms.TermIdentFromName("fun_interp_wf"); + + private static readonly TermIdent astLoopIhId = IsaCommonTerms.TermIdentFromName("loop_IH"); private static readonly TermIdent funInterpSingleWfId = IsaCommonTerms.TermIdentFromName("fun_interp_single_wf"); @@ -77,6 +85,7 @@ private static readonly TermIdent public static TermIdent ConvertValToBoolId { get; } = IsaCommonTerms.TermIdentFromName("convert_val_to_bool"); public static TermIdent ConvertValToIntId { get; } = IsaCommonTerms.TermIdentFromName("convert_val_to_int"); + public static TermIdent SematicsProcSpecSatisfied { get; } = IsaCommonTerms.TermIdentFromName("Semantics.proc_body_satisfies_spec"); public static TermIdent ConvertValToRealId { get; } = IsaCommonTerms.TermIdentFromName("convert_val_to_real"); //TODO initialize all the default constructors, so that they only need to be allocated once (Val, Var, etc...) @@ -108,7 +117,7 @@ public static Term BVar(int i) Term natConst = new NatConst(i); return new TermApp(bvarId, natConst); } - + public static Term Old(Term body) { return new TermApp(oldVarId, body); @@ -322,27 +331,37 @@ public static Term Binop(BinaryOperator.Opcode opcode, Term arg1, Term arg2) var list = new List {arg1, IsaCommonTerms.TermIdentFromName(bopIsa), arg2}; return new TermApp(IsaCommonTerms.TermIdentFromName("BinOp"), list); } + + private static Term Unop(Term unop, Term arg) + { + var list = new List {unop, arg}; + return new TermApp(IsaCommonTerms.TermIdentFromName("UnOp"), list); + } public static Term Unop(UnaryOperator.Opcode opcode, Term arg) { - string uopIsa; + string uopIsa; - switch (opcode) - { - case UnaryOperator.Opcode.Not: - uopIsa = "Not"; - break; - case UnaryOperator.Opcode.Neg: - uopIsa = "UMinus"; - break; - default: - throw new NotImplementedException(); - } + switch (opcode) + { + case UnaryOperator.Opcode.Not: + uopIsa = "Not"; + break; + case UnaryOperator.Opcode.Neg: + uopIsa = "UMinus"; + break; + default: + throw new NotImplementedException(); + } - var list = new List {IsaCommonTerms.TermIdentFromName(uopIsa), arg}; - return new TermApp(IsaCommonTerms.TermIdentFromName("UnOp"), list); + return Unop(IsaCommonTerms.TermIdentFromName(uopIsa), arg); } - + + public static Term IntToReal(Term intTerm) + { + return Unop(intToRealId, intTerm); + } + //value quantification /* @@ -393,6 +412,11 @@ public static Term ProcCall(string procname, IList args, IList retur { return new TermApp(procCallId, new StringConst(procname), new TermList(args), new TermList(returnVars)); } + + public static Term MethodASTBody(IList bigblocks) + { + return new TermList(bigblocks); + } public static Term MethodCFGBody(Term entryNode, Term outEdges, Term nodeToBlock) { @@ -482,6 +506,112 @@ public static Term RedCmdList(BoogieContextIsa boogieContext, Term cmdList, Term } ); } + + public static Term RedBigBlock(BoogieContextIsa boogieContext, Term startConfig, Term endConfig, Term ast) + { + return + new TermApp(redBigBlockId, + new List + { + boogieContext.absValTyMap, + boogieContext.methodContext, + boogieContext.varContext, + boogieContext.funContext, + boogieContext.rtypeEnv, + ast, + startConfig, + endConfig + } + ); + } + + public static Term RedBigBlockKStep(BoogieContextIsa boogieContext, Term startConfig, Term endConfig, Term ast, Term numSteps) + { + return + new TermApp(redBigBlockKStepId, + new List + { + boogieContext.absValTyMap, + boogieContext.methodContext, + boogieContext.varContext, + boogieContext.funContext, + boogieContext.rtypeEnv, + ast, + startConfig, + numSteps, + endConfig + } + ); + } + + public static Term RedBigBlockMulti(BoogieContextIsa boogieContext, Term startConfig, Term endConfig, Term ast) + { + IList bigblockContextList = new List + { + boogieContext.absValTyMap, + boogieContext.methodContext, + boogieContext.varContext, + boogieContext.funContext, + boogieContext.rtypeEnv, + ast + }; + Term redbigBlockTerm = new TermApp(redBigBlockId, bigblockContextList); + + return + new TermApp(redBigBlockMultiId, + new List + { + redbigBlockTerm, + startConfig, + endConfig + } + ); + } + + public static Term StartConfigTerm(BigBlock b, Term cont0, IProgramAccessor beforeCfgProgAccess, Term initState1) + { + var beforeBigblockDefName = beforeCfgProgAccess.BigBlockInfo().CmdsQualifiedName(b).First(); + Term beforeBigblock = IsaCommonTerms.TermIdentFromName(beforeBigblockDefName); + + IList startConfigArgs = new List(); + startConfigArgs.Add(beforeBigblock); + startConfigArgs.Add(cont0); + startConfigArgs.Add(initState1); + + return new TermTuple(startConfigArgs); + } + + public static Term EndConfigTerm() + { + IList endConfigArgs = new List(); + endConfigArgs.Add(IsaCommonTerms.TermIdentFromName("reached_bb")); + endConfigArgs.Add(IsaCommonTerms.TermIdentFromName("reached_cont")); + endConfigArgs.Add(IsaCommonTerms.TermIdentFromName("reached_state")); + + return new TermTuple(endConfigArgs); + } + public static Term astId() + { + return IsaCommonTerms.TermIdentFromName("T"); + } + + public static Term AstValidConfiguration(BoogieContextIsa boogieContext, Term posts) + { + return + new TermApp(astValidConfigId, + new List + { + boogieContext.absValTyMap, + boogieContext.varContext, + boogieContext.funContext, + boogieContext.rtypeEnv, + posts, + IsaCommonTerms.TermIdentFromName("reached_bb"), + IsaCommonTerms.TermIdentFromName("reached_cont"), + IsaCommonTerms.TermIdentFromName("reached_state") + } + ); + } public static Term RedCFGMulti(BoogieContextIsa boogieContext, Term cfg, Term initCFGConfig, Term finalCFGConfig) @@ -520,6 +650,31 @@ public static Term RedCFGKStep(BoogieContextIsa boogieContext, Term cfg, Term in finalCFGConfig }); } + + public static Term AstToCfgLoopIndHypothesis(BoogieContextIsa astBoogieContext, BoogieContextIsa cfgBoogieContext, Term ast, Term bigblock, Term cont, Term cfgBody, Term blockIndex, Term posts) + { + return + new TermApp(astLoopIhId, + new List + { + IsaCommonTerms.TermIdentFromName("j"), + astBoogieContext.absValTyMap, + astBoogieContext.methodContext, + //cfgBoogieContext.methodContext, + astBoogieContext.varContext, + astBoogieContext.funContext, + astBoogieContext.rtypeEnv, + ast, + bigblock, + cont, + cfgBody, + blockIndex, + posts, + IsaCommonTerms.TermIdentFromName("reached_bb"), + IsaCommonTerms.TermIdentFromName("reached_cont"), + IsaCommonTerms.TermIdentFromName("reached_state") + }); + } public static Term CFGConfigNode(Term node, Term state) { @@ -552,12 +707,12 @@ public static Term RedExpr(BoogieContextIsa boogieContext, Term expr, Term state }); } - public static Term FunDecl(Function f, IVariableTranslationFactory varTranslationFactory, + public static Term FunDecl(Function f, IVariableTranslationFactory varTranslationFactory, IsaUniqueNamer uniqueNamer, bool includeName = true) { var typeIsaVisitor = LemmaHelper.FunTypeIsaVisitor(f, varTranslationFactory); - Term fname = new StringConst(f.Name); + Term fname = new StringConst(uniqueNamer.RemoveApostropheInFunc(f.Name)); Term numTypeParams = new NatConst(f.TypeParameters.Count); var argTypes = new TermList(f.InParams.Select(v => typeIsaVisitor.Translate(v.TypedIdent.Type)).ToList()); var retType = typeIsaVisitor.Translate(f.OutParams.First().TypedIdent.Type); @@ -630,9 +785,9 @@ public static Term FunInterpWf(Term absValTyMap, Term fdecls, Term finterp) } public static Term FunInterpSingleWf(Function f, Term absValTyMap, Term fTerm, - IVariableTranslationFactory factory) + IVariableTranslationFactory factory, IsaUniqueNamer uniqueNamer) { - return FunInterpSingleWf(absValTyMap, FunDecl(f, factory), fTerm); + return FunInterpSingleWf(absValTyMap, FunDecl(f, factory, uniqueNamer), fTerm); } public static Term FunInterpSingleWf(Term absValTyMap, Term fdecl, Term fun) @@ -695,5 +850,28 @@ public static Term InstantiateType(Term rtypeEnv, Term ty) { return new TermApp(instTypeId, rtypeEnv, ty); } + + public static Term ProcedureIsCorrectCfg(Term funDecls, Term constantDecls, Term uniqueConstants, Term globalDecls, Term axioms, + Term procedure) + { + var typeInterpId = new SimpleIdentifier("A"); + return + TermQuantifier.MetaAll( + new List {typeInterpId}, + null, + new TermApp( + IsaCommonTerms.TermIdentFromName("Semantics.proc_is_correct"), + //TODO: here assuming that we use "'a" for the abstract value type carrier t --> make t a parameter somewhere + new TermWithExplicitType(new TermIdent(typeInterpId), + IsaBoogieType.AbstractValueTyFunType(new VarType("a"))), + funDecls, + constantDecls, + uniqueConstants, + globalDecls, + axioms, + procedure, + IsaBoogieTerm.SematicsProcSpecSatisfied)); + } + } } \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieType.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieType.cs index c70eb4089..77faa45be 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieType.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaBoogieType.cs @@ -92,7 +92,12 @@ public static TypeIsa CFGNodeOrReturnType() public static TypeIsa ProcedureType() { - return new DataType("procedure"); + return new DataType("mbodyCFG procedure"); + } + + public static TypeIsa AstProcedureType() + { + return new DataType("ast procedure"); } public static TypeIsa GetBlockType() @@ -125,7 +130,7 @@ public static TypeIsa VarContextType() public static TypeIsa ProcContextType() { - return new DataType("proc_context", new List()); + return new DataType("mbodyCFG proc_context", new List()); } public static TypeIsa FunInterpType(TypeIsa absValType) diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGenerator.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGenerator.cs index e15cc494e..d9816d796 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGenerator.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGenerator.cs @@ -63,8 +63,9 @@ public IProgramAccessor GetIsaProgram( IsaProgramGeneratorConfig config, IVariableTranslationFactory varTranslationFactory, CFGRepr cfg, + IsaUniqueNamer uniqueNamer, out IList decls, - bool generateMembershipLemmas = true, + MembershipLemmaConfig membershipLemmaConfig, bool onlyGlobalData = false ) { @@ -91,8 +92,9 @@ public IProgramAccessor GetIsaProgram( var isaGlobalProgramRepr = new IsaGlobalProgramRepr( FunctionDeclarationsName(), AxiomDeclarationsName(), - VariableDeclarationsName("globals"), - VariableDeclarationsName("constants") + VariableDeclarationsName(globalsName), + VariableDeclarationsName(constantsName), + uniqueConstantsName ); var globalsMax = methodData.Constants.Count() + methodData.GlobalVars.Count() - 1; // assume single versioning and order on constants, globals, params, locals @@ -118,7 +120,7 @@ public IProgramAccessor GetIsaProgram( VariableDeclarationsName("locals"), cfgName, procDefName); - membershipLemmaManager = new MembershipLemmaManager(config, isaProgramRepr, blockInfo, + membershipLemmaManager = new MembershipLemmaManager(config, isaProgramRepr, blockInfo, null, Tuple.Create(globalsMax, localsMin), varTranslationFactory, theoryName); var nodesToBlocks = GetNodeToBlocksIsa(cfg, blockInfo.BlockCmdsDefs); @@ -170,7 +172,7 @@ public IProgramAccessor GetIsaProgram( /* membership lemmas might still be added even if the parameter and local variable definitions are not generated * at this point (since the variable context may still be different, which requires other lookup lemmas) */ - if (generateMembershipLemmas) + if (membershipLemmaConfig.GenerateVariableMembershipLemmas) { membershipLemmaManager.AddVariableMembershipLemmas(methodData.InParams, VarKind.ParamOrLocal); membershipLemmaManager.AddVariableMembershipLemmas(methodData.Locals, VarKind.ParamOrLocal); @@ -180,30 +182,40 @@ public IProgramAccessor GetIsaProgram( if (config.generateAxioms) { decls.Add(GetAxioms(methodData.Axioms)); - if(generateMembershipLemmas) membershipLemmaManager.AddAxiomMembershipLemmas(methodData.Axioms); + if (membershipLemmaConfig.GenerateAxiomMembershipLemmas) + { + membershipLemmaManager.AddAxiomMembershipLemmas(methodData.Axioms); + } } if (config.generateFunctions) { - decls.Add(GetFunctionDeclarationsIsa(methodData.Functions)); - if(generateMembershipLemmas) membershipLemmaManager.AddFunctionMembershipLemmas(methodData.Functions); + decls.Add(GetFunctionDeclarationsIsa(methodData.Functions, uniqueNamer)); + if (membershipLemmaConfig.GenerateFunctionMembershipLemmas) + { + membershipLemmaManager.AddFunctionMembershipLemmas(methodData.Functions, uniqueNamer); + } } if (config.generateGlobalsAndConstants) { decls.Add(GetVariableDeclarationsIsa("globals", methodData.GlobalVars)); - decls.Add(GetVariableDeclarationsIsa("constants", methodData.Constants)); + decls.Add(GetVariableDeclarationsIsa(constantsName, methodData.Constants)); + decls.Add(GetUniqueConstants(uniqueConstantsName, methodData.Constants)); } - if (generateMembershipLemmas) + if (membershipLemmaConfig.GenerateVariableMembershipLemmas) { membershipLemmaManager.AddVariableMembershipLemmas(methodData.GlobalVars, VarKind.Global); membershipLemmaManager.AddVariableMembershipLemmas(methodData.Constants, VarKind.Constant); decls.AddRange(membershipLemmaManager.OuterDecls()); } - if (config.specsConfig != SpecsConfig.None) - { + /* always add procedure definition even if it is potentially not used + (easier than having conditions here, especially since cost is small) + */ + if (!onlyGlobalData) + { DefDecl methodDef = MethodDefinition(membershipLemmaManager, methodData, config.specsConfig); decls.Add(methodDef); } @@ -217,8 +229,10 @@ private DefDecl GetAxioms(IEnumerable axioms) foreach (var ax in axioms) { var axTerms = cmdIsaVisitor.Translate(ax.Expr); + if (axTerms.Count != 1) throw new ProofGenUnexpectedStateException(GetType(), "axiom not translated into single term"); + axiomsExpr.Add(axTerms.First()); } @@ -255,8 +269,10 @@ private DefDecl MethodDefinition(IProgramAccessor programAccessor, BoogieMethodD //TODO: incorporate return values and modified variables Tuple.Create("proc_rets", (Term) IsaCommonTerms.EmptyList), Tuple.Create("proc_modifs", (Term) modifiedVarsTerm), - Tuple.Create("proc_pres", specConfig == SpecsConfig.All ? programAccessor.PreconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PreconditionsDecl())), - Tuple.Create("proc_posts", specConfig == SpecsConfig.All ? programAccessor.PostconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PostconditionsDecl())), + Tuple.Create("proc_pres", specConfig == SpecsConfig.All ? + programAccessor.PreconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PreconditionsDecl())), + Tuple.Create("proc_posts", specConfig == SpecsConfig.All ? + programAccessor.PostconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PostconditionsDecl())), //TODO: support abstract procedures Tuple.Create("proc_body", IsaCommonTerms.SomeOption(new TermTuple(IsaCommonTerms.TermIdentFromName(programAccessor.LocalsDecl()), programAccessor.CfgDecl()))) @@ -362,13 +378,13 @@ private OuterDecl GetNodeToBlocksIsa(CFGRepr cfg, IDictionary return DefDecl.CreateWithoutArg(nodeToBlocksName, new TermList(nodeList)); } - private DefDecl GetFunctionDeclarationsIsa(IEnumerable functions) + private DefDecl GetFunctionDeclarationsIsa(IEnumerable functions, IsaUniqueNamer uniqueNamer) { //var equations = new List, Term>>(); var fdecls = new List(); - foreach (var f in functions) fdecls.Add(IsaBoogieTerm.FunDecl(f, varTranslationFactory)); + foreach (var f in functions) fdecls.Add(IsaBoogieTerm.FunDecl(f, varTranslationFactory, uniqueNamer)); return DefDecl.CreateWithoutArg(FunctionDeclarationsName(), new TermList(fdecls)); } @@ -388,6 +404,10 @@ private string PostconditionDeclarationName() return "post"; } + private string globalsName = "globals"; + private string constantsName = "constants"; + private string uniqueConstantsName = "unique_consts"; + private DefDecl GetVariableDeclarationsIsa(string varKind, IEnumerable variables) { var typeIsaVisitor = new TypeIsaVisitor(varTranslation.TypeVarTranslation); @@ -411,6 +431,31 @@ private DefDecl GetVariableDeclarationsIsa(string varKind, IEnumerable equation); } + private DefDecl GetUniqueConstants(String defName, IEnumerable constants) + { + var uniqueConstNames = new List(); + + foreach (var c in constants) + { + if (!c.Unique) + { + continue; + } + + if (varTranslation.VarTranslation.TryTranslateVariableId(c, out var resId, out _)) + { + uniqueConstNames.Add(resId); + } + else + { + throw new ProofGenUnexpectedStateException(GetType(), "Cannot translate constant " + c.Name); + } + } + + var equation = new Tuple, Term>(new List(), new TermList(uniqueConstNames)); + return new DefDecl(defName, IsaCommonTypes.GetListType(IsaBoogieType.VnameType()), equation); + } + private string VariableDeclarationsName(string varKind) { return varKind + "_vdecls"; diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGeneratorForAst.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGeneratorForAst.cs new file mode 100644 index 000000000..fd103fcc0 --- /dev/null +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramGeneratorForAst.cs @@ -0,0 +1,438 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.BaseTypes; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.ASTRepresentation; +using ProofGeneration.Util; + +namespace ProofGeneration +{ + internal class IsaProgramGeneratorForAst + { + private MultiCmdIsaVisitor cmdIsaVisitor; + private BoogieVariableTranslation varTranslation; + private IVariableTranslationFactory varTranslationFactory; + + private readonly string astName = "proc_body"; + private readonly string procDefName = "ast_proc"; + + public IProgramAccessor GetIsaProgram( + string theoryName, + string procName, + BoogieMethodData methodData, + IsaProgramGeneratorConfig config, + IVariableTranslationFactory varTranslationFactory, + ASTRepr ast, + ASTRepr originalAst, + AstToCfgProofGenInfo proofGenInfo, + IsaUniqueNamer uniqueNamer, + out IList decls, + MembershipLemmaConfig membershipLemmaConfig, + bool onlyGlobalData = false + ) + { + this.varTranslationFactory = varTranslationFactory; + varTranslation = varTranslationFactory.CreateTranslation(); + cmdIsaVisitor = new MultiCmdIsaVisitor(varTranslationFactory); + + decls = new List(); + var isaGlobalProgramRepr = new IsaGlobalProgramRepr( + FunctionDeclarationsName(), + AxiomDeclarationsName(), + VariableDeclarationsName("globals"), + VariableDeclarationsName("constants"), + "unique_consts" + ); + var globalsMax = methodData.Constants.Count() + methodData.GlobalVars.Count() - 1; + // assume single versioning and order on constants, globals, params, locals + var localsMin = globalsMax + 1; + if (globalsMax < 0) + globalsMax = 0; + + MembershipLemmaManager membershipLemmaManager; + if (onlyGlobalData) + { + membershipLemmaManager = new MembershipLemmaManager( + isaGlobalProgramRepr, globalsMax, varTranslationFactory, theoryName ); + } + else + { + var bigblockInfo = BigBlockToInfo(theoryName, ast, proofGenInfo); + var isaProgramRepr = new IsaProgramRepr( + isaGlobalProgramRepr, + PreconditionDeclarationName(), + PostconditionDeclarationName(), + VariableDeclarationsName("params"), + VariableDeclarationsName("locals"), + astName, + procDefName); + membershipLemmaManager = new MembershipLemmaManager(config, isaProgramRepr, null, bigblockInfo, + Tuple.Create(globalsMax, localsMin), varTranslationFactory, theoryName); + + foreach (var decl_list in bigblockInfo.BigBlockDefs.Values) + { + decls.AddRange(decl_list); + } + + IList continuations = getContinuations(originalAst, proofGenInfo); + decls.AddRange(continuations); + + IList bigblock_terms = new List(); + IEnumerable bigblocks = ast.GetBlocksBackwards(); + foreach (BigBlock b in bigblocks) + { + Term b_term = IsaCommonTerms.TermIdentFromName("bigblock_" + ast.GetUniqueIntLabel(b)); + if (proofGenInfo.GetMappingCopyBigBlockToMarker()[b]) + { + bigblock_terms.Add(b_term); + } + } + + bigblock_terms = Enumerable.Reverse(bigblock_terms).ToList(); + var methodBodyAST = IsaBoogieTerm.MethodASTBody(bigblock_terms); + + var methodBodyDecl = GetMethodBodyASTDecl(procName, methodBodyAST); + decls.AddRange( + new List + { + methodBodyDecl + }); + + if (config.specsConfig != SpecsConfig.None) + { + OuterDecl preconditions; + OuterDecl postconditions; + + if (config.specsConfig == SpecsConfig.AllPreCheckedPost) + { + preconditions = GetExprListIsa(PreconditionDeclarationName(), methodData.Preconditions.Select(pre => pre.Item1)); + postconditions = GetExprListIsa(PostconditionDeclarationName(), methodData.Postconditions.Where(post => !post.Item2).Select(post => post.Item1)); + } + else + { + preconditions = GetExprListIsa(PreconditionDeclarationName(), methodData.Preconditions); + postconditions = GetExprListIsa(PostconditionDeclarationName(), methodData.Postconditions); + } + + decls.Add(preconditions); + decls.Add(postconditions); + } + + if(config.generateParamsAndLocals) { + decls.Add(GetVariableDeclarationsIsa("params", methodData.InParams)); + decls.Add(GetVariableDeclarationsIsa("locals", methodData.Locals)); + } + + /* membership lemmas might still be added even if the parameter and local variable definitions are not generated + * at this point (since the variable context may still be different, which requires other lookup lemmas) + */ + if (membershipLemmaConfig.GenerateVariableMembershipLemmas) + { + membershipLemmaManager.AddVariableMembershipLemmas(methodData.InParams, VarKind.ParamOrLocal); + membershipLemmaManager.AddVariableMembershipLemmas(methodData.Locals, VarKind.ParamOrLocal); + } + } + + if (config.generateAxioms) + { + decls.Add(GetAxioms(methodData.Axioms)); + if (membershipLemmaConfig.GenerateAxiomMembershipLemmas) + { + membershipLemmaManager.AddAxiomMembershipLemmas(methodData.Axioms); + } + } + + if (config.generateFunctions) + { + decls.Add(GetFunctionDeclarationsIsa(methodData.Functions, uniqueNamer)); + if (membershipLemmaConfig.GenerateFunctionMembershipLemmas) + { + membershipLemmaManager.AddFunctionMembershipLemmas(methodData.Functions, uniqueNamer); + } + } + + if (config.generateGlobalsAndConstants) + { + decls.Add(GetVariableDeclarationsIsa("globals", methodData.GlobalVars)); + decls.Add(GetVariableDeclarationsIsa("constants", methodData.Constants)); + } + + if (membershipLemmaConfig.GenerateVariableMembershipLemmas) + { + membershipLemmaManager.AddVariableMembershipLemmas(methodData.GlobalVars, VarKind.Global); + membershipLemmaManager.AddVariableMembershipLemmas(methodData.Constants, VarKind.Constant); + decls.AddRange(membershipLemmaManager.OuterDecls()); + } + + if (config.specsConfig != SpecsConfig.None) + { + DefDecl methodDef = MethodDefinition(membershipLemmaManager, methodData, config.specsConfig); + decls.Add(methodDef); + } + + return membershipLemmaManager; + } + + private DefDecl GetAxioms(IEnumerable axioms) + { + var axiomsExpr = new List(); + foreach (var ax in axioms) + { + var axTerms = cmdIsaVisitor.Translate(ax.Expr); + if (axTerms.Count != 1) + throw new ProofGenUnexpectedStateException(GetType(), "axiom not translated into single term"); + + axiomsExpr.Add(axTerms.First()); + } + + var equation = new Tuple, Term>(new List(), new TermList(axiomsExpr)); + + return new DefDecl(AxiomDeclarationsName(), equation); + } + + private string AxiomDeclarationsName() + { + return "axioms"; + } + + private DefDecl MethodDefinition(IProgramAccessor programAccessor, BoogieMethodData methodData, SpecsConfig specConfig) + { + var modifiedVarsTerm = new TermList( + methodData.ModifiedVars.Select(id => + { + if (varTranslation.VarTranslation.TryTranslateVariableId(id.Decl, out Term idTerm, out _)) + { + return idTerm; + } + else + { + throw new ProofGenUnexpectedStateException("Could not get variable id"); + } + }).ToList()); + + var mapping = + new List> + { + Tuple.Create("proc_ty_args", (Term) new NatConst(methodData.TypeParams.Count())), + Tuple.Create("proc_args", (Term) IsaCommonTerms.TermIdentFromName(programAccessor.ParamsDecl())), + //TODO: incorporate return values and modified variables + Tuple.Create("proc_rets", (Term) IsaCommonTerms.EmptyList), + Tuple.Create("proc_modifs", (Term) modifiedVarsTerm), + Tuple.Create("proc_pres", specConfig == SpecsConfig.All ? programAccessor.PreconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PreconditionsDecl())), + Tuple.Create("proc_posts", specConfig == SpecsConfig.All ? programAccessor.PostconditionsDecl() : IsaBoogieTerm.LiftExprsToCheckedSpecs(programAccessor.PostconditionsDecl())), + //TODO: support abstract procedures + Tuple.Create("proc_body", + IsaCommonTerms.SomeOption(new TermTuple(IsaCommonTerms.TermIdentFromName(programAccessor.LocalsDecl()), programAccessor.CfgDecl()))) + }; + + Term method = new TermRecord(mapping); + + DefDecl methodDef = DefDecl.CreateWithoutArg(procDefName, IsaBoogieType.AstProcedureType(), method); + return methodDef; + } + + private IsaBigBlockInfo BigBlockToInfo(string theoryName, ASTRepr ast, AstToCfgProofGenInfo proofGenInfo) + { + var blockToDecl = new Dictionary>(); + var blockToCounter = new Dictionary(); + + BigBlockTermBuilder builder = new BigBlockTermBuilder(); + + foreach (BigBlock b in ast.GetBlocksBackwards()) + { + int flag = 0; + if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().Keys.Contains(b)) + { + flag = 1; + } + + int uniqueIntLabel = ast.GetUniqueIntLabel(b); + string nameToUse = "bigblock_" + uniqueIntLabel; + var translatedBigBlock = builder.makeBigBlockTerm(b, proofGenInfo, cmdIsaVisitor, flag, 0, nameToUse, out int updatedNestedBlockTracker); + + proofGenInfo.AddBigBlockToIndexPair(b, uniqueIntLabel); + + IDictionary> bb_defs = builder.getBigblockDefDecls(); + foreach(KeyValuePair> bb_def in bb_defs) + { + if (!blockToDecl.ContainsKey(bb_def.Key)) + { + blockToDecl.Add(bb_def.Key, bb_def.Value); + } + } + + blockToCounter[b] = uniqueIntLabel; + } + + return new IsaBigBlockInfo(theoryName, blockToCounter, blockToDecl); + } + + private IList getContinuations(ASTRepr originalAst, AstToCfgProofGenInfo proofGenInfo) + { + IList declsToReturn = new List(); + BigBlockTermBuilder builder = new BigBlockTermBuilder(); + + //Loop through the big blocks in 'original AST' backwards. + //The name 'original AST' refers to the fact that the AST is in its original form, as constructed, i.e, it's not 'unrolled'. + //However, the big blocks inside it are copies. + foreach (BigBlock b in originalAst.GetBlocksBackwards()) + { + BigBlock correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[b]; + BigBlock successorBigBlockOrig = correspondingBigBlockOrig.successorBigBlock; + + int successorIndex = -1; + if (successorBigBlockOrig != null) + { + BigBlock successorBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[successorBigBlockOrig]; + successorIndex = proofGenInfo.GetMappingCopyBigBlockToIndex()[successorBigBlockCopy]; + } + + //This should be the same as big block 'b'? + BigBlock correspondingBigBlockCopy = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[correspondingBigBlockOrig]; + + //Construct the continuation term, which corresponds to 'b' and which is to be used in the Isabelle proofs later. + Term continuation = builder.makeContinuationTerm(correspondingBigBlockCopy, proofGenInfo, successorIndex); + int bigblockIndex = proofGenInfo.GetMappingCopyBigBlockToIndex()[correspondingBigBlockCopy]; + DefDecl continuationDecl = DefDecl.CreateWithoutArg("cont_" + bigblockIndex, continuation); + declsToReturn.Add(continuationDecl); + + //Special continuation term, which is used if 'b' contains a WhileCmd. + DefDecl continuationDeclForUnwrappedBigBlock = null; + + if (b.ec is WhileCmd) + { + foreach (var pair in proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()) + { + if (pair.Value == correspondingBigBlockOrig) + { + BigBlock unwrapped = pair.Key; + + Term continuationUnwrapped = builder.makeContinuationTerm(unwrapped, proofGenInfo, successorIndex); + int bigblockIndexUnwrapped = proofGenInfo.GetMappingCopyBigBlockToIndex()[unwrapped]; + continuationDeclForUnwrappedBigBlock = DefDecl.CreateWithoutArg("cont_" + bigblockIndexUnwrapped, continuationUnwrapped); + } + } + + if (continuationDeclForUnwrappedBigBlock != null) + { + declsToReturn.Add(continuationDeclForUnwrappedBigBlock); + } + + WhileCmd wcmd = (WhileCmd) b.ec; + ASTRepr body = new ASTRepr(wcmd.Body.BigBlocks); + + //recursively construct continuation terms for the body of the while-loop (again from the end to the beginning). + IList bodyConts = getContinuations(body, proofGenInfo); + declsToReturn.AddRange(bodyConts); + } + else if (b.ec is IfCmd) + { + IfCmd ifcmd = (IfCmd) b.ec; + ASTRepr thn = new ASTRepr(ifcmd.thn.BigBlocks); + + //recursively construct continuation terms for the body of the thn-branch of the if-statement. + declsToReturn.AddRange(getContinuations(thn, proofGenInfo)); + + if (ifcmd.elseBlock != null) + { + ASTRepr elseBlock = new ASTRepr(ifcmd.elseBlock.BigBlocks); + + //recursively construct continuation terms for the body of the else-branch of the if-statement. + declsToReturn.AddRange(getContinuations(elseBlock, proofGenInfo)); + } + } + } + + return declsToReturn; + } + + private DefDecl GetFunctionDeclarationsIsa(IEnumerable functions, IsaUniqueNamer uniqueNamer) + { + var fdecls = new List(); + foreach (var f in functions) fdecls.Add(IsaBoogieTerm.FunDecl(f, varTranslationFactory, uniqueNamer)); + + return DefDecl.CreateWithoutArg(FunctionDeclarationsName(), new TermList(fdecls)); + } + + private string FunctionDeclarationsName() + { + return "fdecls"; + } + + private string PreconditionDeclarationName() + { + return "pres"; + } + + private string PostconditionDeclarationName() + { + return "post"; + } + + private DefDecl GetVariableDeclarationsIsa(string varKind, IEnumerable variables) + { + var typeIsaVisitor = new TypeIsaVisitor(varTranslation.TypeVarTranslation); + + var vdecls = new List(); + + foreach (var v in variables) + { + if (varTranslation.VarTranslation.TryTranslateVariableId(v, out var resId, out _)) + { + vdecls.Add(IsaBoogieTerm.VarDecl(v, resId, typeIsaVisitor, cmdIsaVisitor.TranslateSingle)); + } + else + { + throw new ProofGenUnexpectedStateException(GetType(), "Cannot translate variable " + v.Name); + } + } + + var equation = new Tuple, Term>(new List(), new TermList(vdecls)); + return new DefDecl(VariableDeclarationsName(varKind), IsaBoogieType.VariableDeclsType, + equation); + } + + private string VariableDeclarationsName(string varKind) + { + return varKind + "_vdecls"; + } + + private DefDecl GetExprListIsa(string declName, IEnumerable exprs) + { + var result = new List(); + foreach (var expr in exprs) + { + var termTuple = cmdIsaVisitor.TranslateSingle(expr); + result.Add(termTuple); + } + + return DefDecl.CreateWithoutArg(declName, new TermList(result)); + } + + private DefDecl GetExprListIsa(string declName, IEnumerable> exprs) + { + var result = new List(); + foreach (var expr in exprs) + { + var termTuple = new TermTuple(cmdIsaVisitor.TranslateSingle(expr.Item1), new BoolConst(expr.Item2)); + result.Add(termTuple); + } + + return DefDecl.CreateWithoutArg(declName, new TermList(result)); + } + + private DefDecl GetMethodBodyASTDecl(string methodName, Term methodBodyAST) + { + return DefDecl.CreateWithoutArg(astName, methodBodyAST); + } + + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/IsaProgramRepr.cs b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramRepr.cs index 36a88721d..e1781da79 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/IsaProgramRepr.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/IsaProgramRepr.cs @@ -4,6 +4,7 @@ public class IsaGlobalProgramRepr { public readonly string axiomsDeclDef; public readonly string constantsDeclDef; + public readonly string uniqueConstantsDeclDef; public readonly string funcsDeclDef; public readonly string globalsDeclDef; @@ -11,12 +12,14 @@ public IsaGlobalProgramRepr( string funcsDeclDef, string axiomsDeclDef, string globalsDeclDef, - string constantsDeclDef) + string constantsDeclDef, + string uniqueConstsDeclDef) { this.funcsDeclDef = funcsDeclDef; this.axiomsDeclDef = axiomsDeclDef; this.globalsDeclDef = globalsDeclDef; this.constantsDeclDef = constantsDeclDef; + this.uniqueConstantsDeclDef = uniqueConstsDeclDef; } } diff --git a/Source/ProofGeneration/BoogieIsaInterface/MembershipLemmaManager.cs b/Source/ProofGeneration/BoogieIsaInterface/MembershipLemmaManager.cs index 807f5f253..e22a68263 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/MembershipLemmaManager.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/MembershipLemmaManager.cs @@ -28,6 +28,7 @@ public class MembershipLemmaManager : IProgramAccessor new Dictionary(); private readonly string consts; + private readonly string uniqueConsts; private readonly string[] constsAndGlobalsDefs; private readonly Term constsAndGlobalsList; private readonly string constsWfName = "consts_wf"; @@ -41,18 +42,19 @@ public class MembershipLemmaManager : IProgramAccessor private readonly string globalsWfName = "globals_wf"; private readonly List helperLemmas = new List(); private readonly IsaBlockInfo isaBlockInfo; + private readonly IsaBigBlockInfo isaBigBlockInfo; private readonly IsaProgramRepr isaProgramRepr; private readonly string locals; private readonly string localsMinName = "locals_min"; private readonly string localsWfName = "locals_wf"; private readonly IDictionary lookupVarTyLemmas = new Dictionary(); - private readonly IsaUniqueNamer lookupVarTyNamer = new IsaUniqueNamer(); + private readonly IsaUniqueNamer lookupVarTyNamer = new(); private readonly IDictionary membershipLemmas = new Dictionary(); - private readonly IsaUniqueNamer membershipNamer = new IsaUniqueNamer(); + private readonly IsaUniqueNamer membershipNamer = new(); private readonly string parameters; private readonly string[] paramsAndLocalsDefs; @@ -71,6 +73,12 @@ private readonly IDictionary //indicates whether this instance or any of its ancestors contains local method information (i.e., parameters, etc...) private readonly bool containsLocalInformation = true; + /* There are two membership lemmas for each constant. So we need two different prefixes for the corresponding names. + The following prefix is for the constant membership lemma that is declared first and used by the second + membership lemma. + */ + private readonly string separateConstantMembershipLemmaPrefix = "mconst"; + /// /// Initializes a new instance of the class. /// Generated instance only has information about global data. @@ -91,6 +99,7 @@ public MembershipLemmaManager ( config = new IsaProgramGeneratorConfig(null, true, true, true, false, SpecsConfig.None, false); consts = QualifyAccessName(isaProgramRepr.GlobalProgramRepr.constantsDeclDef); + uniqueConsts = QualifyAccessName(isaProgramRepr.GlobalProgramRepr.uniqueConstantsDeclDef); globals = QualifyAccessName(isaProgramRepr.GlobalProgramRepr.globalsDeclDef); constsAndGlobalsDefs = @@ -107,6 +116,7 @@ public MembershipLemmaManager( IsaProgramGeneratorConfig config, IsaProgramRepr isaProgramRepr, IsaBlockInfo isaBlockInfo, + IsaBigBlockInfo isaBigBlockInfo, Tuple GlobalsMaxLocalsMin, IVariableTranslationFactory factory, string theoryName @@ -118,6 +128,7 @@ string theoryName this.theoryName = theoryName; this.config = config; this.isaBlockInfo = isaBlockInfo; + this.isaBigBlockInfo = isaBigBlockInfo; typeIsaVisitor = new TypeIsaVisitor(factory.CreateTranslation().TypeVarTranslation); basicCmdIsaVisitor = new BasicCmdIsaVisitor(factory); paramsAndLocalsDefs = @@ -136,6 +147,9 @@ string theoryName consts = config.generateGlobalsAndConstants ? QualifyAccessName(isaProgramRepr.GlobalProgramRepr.constantsDeclDef) : parent.ConstsDecl(); + uniqueConsts = config.generateGlobalsAndConstants + ? QualifyAccessName(isaProgramRepr.GlobalProgramRepr.uniqueConstantsDeclDef) + : parent.UniqueConstsDecl(); globals = config.generateGlobalsAndConstants ? QualifyAccessName(isaProgramRepr.GlobalProgramRepr.globalsDeclDef) : parent.GlobalsDecl(); @@ -247,6 +261,11 @@ public string ConstsDecl() return consts; } + public string UniqueConstsDecl() + { + return uniqueConsts; + } + public string GlobalsDecl() { return globals; @@ -283,6 +302,11 @@ public IsaBlockInfo BlockInfo() { return isaBlockInfo; } + + public IsaBigBlockInfo BigBlockInfo() + { + return isaBigBlockInfo; + } public string LookupVarDeclLemma(Variable v) { @@ -290,7 +314,7 @@ public string LookupVarDeclLemma(Variable v) if (lookupVarTyLemmas.TryGetValue(v, out var result)) return QualifyAccessName(result.Name+"(1)"); - return parent.LookupVarTyLemma(v); + return parent.LookupVarDeclLemma(v); } public string LookupVarTyLemma(Variable v) @@ -367,13 +391,13 @@ public IEnumerable OuterDecls() return result; } - public void AddFunctionMembershipLemmas(IEnumerable functions) + public void AddFunctionMembershipLemmas(IEnumerable functions, IsaUniqueNamer uniqueNamer) { AddNamedDeclsMembershipLemmas(functions, IsaCommonTerms.TermIdentFromName(isaProgramRepr.GlobalProgramRepr.funcsDeclDef), new[] {isaProgramRepr.GlobalProgramRepr.funcsDeclDef+ "_def"}, - d => new StringConst(d.Name), - d => IsaBoogieTerm.FunDecl((Function) d, factory, false), + d => new StringConst(uniqueNamer.RemoveApostropheInFunc(d.Name)), + d => IsaBoogieTerm.FunDecl((Function) d, factory, uniqueNamer, false), false ); } @@ -435,6 +459,18 @@ public void AddVariableMembershipLemmas(IEnumerable variables, VarKind } } + private string MembershipLemmaPrefix(NamedDeclaration d) + { + if (d is Function) + { + return "mfun"; + } + else + { + return "mvar"; + } + } + private void AddNamedDeclsMembershipLemmas( IEnumerable decls, Term sourceList, @@ -445,7 +481,8 @@ private void AddNamedDeclsMembershipLemmas( { foreach (var d in decls) { - Term lhs = new TermApp(IsaCommonTerms.TermIdentFromName("map_of"), new List {sourceList, nameOf(d)}); + var nameOfDecl = nameOf(d); + Term lhs = new TermApp(IsaCommonTerms.TermIdentFromName("map_of"), new List {sourceList, nameOfDecl}); var rhs = IsaCommonTerms.SomeOption(declOf(d)); Term statement = TermBinary.Eq(lhs, rhs); @@ -455,12 +492,12 @@ private void AddNamedDeclsMembershipLemmas( proof = new Proof(new List {"by " + ProofUtil.Simp(definitions)}); else proof = new Proof(new List - {"by " + "(simp add: " + ConstantMembershipName(d) + " del: Nat.One_nat_def)"}); + {"by " + "(simp add: " + ConstantMembershipName(d, nameOfDecl, separateConstantMembershipLemmaPrefix) + " del: Nat.One_nat_def)"}); if (!separateConstantLemmas) - membershipLemmas.Add(d, new LemmaDecl(MembershipName(d), statement, proof)); + membershipLemmas.Add(d, new LemmaDecl(MembershipName(d, nameOfDecl, MembershipLemmaPrefix(d)), statement, proof)); else - constantMembershipLemmas.Add(d, new LemmaDecl(ConstantMembershipName(d), statement, proof)); + constantMembershipLemmas.Add(d, new LemmaDecl(ConstantMembershipName(d, nameOfDecl, separateConstantMembershipLemmaPrefix), statement, proof)); } } @@ -472,12 +509,13 @@ private void AddLookupVarDeclTyLemmas( { foreach (var v in vars) { + var nameOfDecl = nameOf(v); var (ty, whereClause) = declOf(v); - var lookupDeclLhs = IsaBoogieTerm.LookupVarDecl(VarContext(), nameOf(v)); + var lookupDeclLhs = IsaBoogieTerm.LookupVarDecl(VarContext(), nameOfDecl); var lookupDeclRhs = IsaCommonTerms.SomeOption(new TermTuple(ty, whereClause)); Term lookupDeclStmt = TermBinary.Eq(lookupDeclLhs, lookupDeclRhs); - var lookupTyLhs = IsaBoogieTerm.LookupVarTy(VarContext(), nameOf(v)); + var lookupTyLhs = IsaBoogieTerm.LookupVarTy(VarContext(), nameOfDecl); var lookupTyRhs = IsaCommonTerms.SomeOption(ty); Term lookupTyStmt = TermBinary.Eq(lookupTyLhs, lookupTyRhs); @@ -488,7 +526,7 @@ private void AddLookupVarDeclTyLemmas( "by (simp_all add: lookup_var_decl_global_2 lookup_var_decl_local lookup_var_decl_ty_Some)" }); lookupVarTyLemmas.Add(v, new LemmaDecl( - lookupVarTyNamer.GetName(v, "l_" + v.Name), + MembershipName(() => lookupVarTyNamer.GetName(v, "l_" + v.Name), "lvar", nameOfDecl), new List {lookupDeclStmt, lookupTyStmt}, proof) ); @@ -509,19 +547,28 @@ public void AddAxiomMembershipLemmas(IEnumerable axioms) id++; } } + + private string MembershipName(Func normalNameFunc, string membershipPrefix, Term memberTerm) + { + if (CommandLineOptions.Clo.UseIdBasedLemmaNaming && memberTerm is NatConst natConst) + { + return membershipPrefix + natConst; + } - private string MembershipName(NamedDeclaration d) + return membershipPrefix + normalNameFunc(); + } + + private string MembershipName(NamedDeclaration d, Term memberTerm, String membershipPrefix) { - var name = membershipNamer.GetName(d, d.Name); - if (d is Function) - return "mfun_" + name; - return "m_" + name; + var normalNameFunc = () => membershipNamer.GetName(d, d.Name); + + return MembershipName(normalNameFunc, membershipPrefix, memberTerm); } - private string ConstantMembershipName(NamedDeclaration d) + private string ConstantMembershipName(NamedDeclaration d, Term constId, String membershipPrefix) { - var name = membershipNamer.GetName(d, d.Name); - return "mconst_" + name; + var normalNameFunc = () => membershipNamer.GetName(d, d.Name); + return MembershipName(normalNameFunc, membershipPrefix, constId); } private string MembershipName(Axiom a, int id) @@ -597,8 +644,8 @@ private void AddDisjointnessLemmas(int globalsMax, int localsMin) else proofMethods = new List { - "using " + LocalsAtLeastMin() + " " + GlobalsAtMostMax(), - "by fastforce" + "using " + ProofUtil.OF("max_min_disjoint_2", GlobalsAtMostMax(), LocalsAtLeastMin()), + "by linarith" }; helperLemmas.Add( new LemmaDecl(globalsLocalsDisjName, statement, new Proof(proofMethods)) diff --git a/Source/ProofGeneration/BoogieIsaInterface/MultiCmdIsaVisitor.cs b/Source/ProofGeneration/BoogieIsaInterface/MultiCmdIsaVisitor.cs index deb266f99..81823426f 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/MultiCmdIsaVisitor.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/MultiCmdIsaVisitor.cs @@ -50,7 +50,7 @@ public IList Translate(IList cmds) { if (!(cmd is CommentCmd)) { - cmdsIsa.AddRange(Translate(cmd)); + cmdsIsa.AddRange(Translate(cmd)); } } diff --git a/Source/ProofGeneration/BoogieIsaInterface/ProgramGeneratorHelperForAst.cs b/Source/ProofGeneration/BoogieIsaInterface/ProgramGeneratorHelperForAst.cs new file mode 100644 index 000000000..0ac11c6f1 --- /dev/null +++ b/Source/ProofGeneration/BoogieIsaInterface/ProgramGeneratorHelperForAst.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; +using System.Xml; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.BaseTypes; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; + +namespace ProofGeneration +{ + internal class BigBlockTermBuilder + { + private IDictionary> bigBlocksDefDecls; + + public BigBlockTermBuilder() + { + bigBlocksDefDecls = new Dictionary>(); + } + + public IDictionary> getBigblockDefDecls() + { + return bigBlocksDefDecls; + } + + public Term makeBigBlockTerm(BigBlock b, AstToCfgProofGenInfo proofGenInfo, MultiCmdIsaVisitor cmdIsaVisitor, int flag, int nestedBlockTracker, string nameToUse, + out int updatedNestedBlockTracker) + { + var bigblock_term = IsaCommonTerms.TermIdentFromName("BigBlock"); + IList bigblock_args = new List(); + + Term name_option; + name_option = b.LabelName == null ? IsaCommonTerms.NoneOption() : IsaCommonTerms.SomeOption(IsaCommonTerms.TermIdentFromName(b.LabelName)); + + bigblock_args.Add(name_option); + + var translatedCmds = cmdIsaVisitor.Translate(b.simpleCmds); + TermList cmdsList = new TermList(translatedCmds); + + bigblock_args.Add(cmdsList); + + Term structure_option = makeStructuredCmdTerm(b, proofGenInfo, cmdIsaVisitor, nestedBlockTracker + 1, flag, nameToUse, + out int updatedTrackerAfterStr); + bigblock_args.Add(structure_option); + + Term transfer_option = null; + if (b.tc is null) + { + transfer_option = IsaCommonTerms.NoneOption(); + } + else if (b.tc is GotoCmd) + { + GotoCmd _goto = (GotoCmd) b.tc; + List target_names = _goto.labelNames; + IList goto_arg_terms = new List(); + foreach (String target in target_names) + { + TermIdent target_term = IsaCommonTerms.TermIdentFromName(target); + goto_arg_terms.Add(target_term); + } + TermIdent goto_ident = IsaCommonTerms.TermIdentFromName("Goto"); + Term arg_for_option = new TermApp(goto_ident, goto_arg_terms); + transfer_option = IsaCommonTerms.SomeOption(arg_for_option); + } + else if (b.tc is ReturnCmd) + { + Term arg_for_option = IsaCommonTerms.TermIdentFromName("Return"); + transfer_option = IsaCommonTerms.SomeOption(arg_for_option); + } + bigblock_args.Add(transfer_option); + + IList bb_decls = new List(); + Term bigblock = new TermApp(bigblock_term, bigblock_args); + + if (nestedBlockTracker == 0) + { + DefDecl bigblock_def = DefDecl.CreateWithoutArg(nameToUse, bigblock); + bb_decls.Add(bigblock_def); + bigBlocksDefDecls.Add(b, bb_decls); + } + + updatedNestedBlockTracker = updatedTrackerAfterStr; + return bigblock; + } + + public Term makeStructuredCmdTerm(BigBlock b, AstToCfgProofGenInfo proofGenInfo, MultiCmdIsaVisitor cmdIsaVisitor, int nestedBlockTracker, int flag, string nameToUse, + out int updatedNestedBlockTracker) + { + Term strCmdTermOption = null; + if (b.ec == null) + { + strCmdTermOption = IsaCommonTerms.NoneOption(); + } + else if (b.ec is IfCmd) + { + IfCmd _if = (IfCmd) b.ec; + Term if_term = IsaCommonTerms.TermIdentFromName("ParsedIf"); + + Expr guard = _if.Guard; + Term guard_term = null; + if (guard is null) + { + guard_term = IsaCommonTerms.NoneOption(); + } + else + { + guard_term = IsaCommonTerms.SomeOption(cmdIsaVisitor.TranslateSingle(guard)); + } + + IList then_branch = _if.thn.BigBlocks; + IList then_branch_bigblock_terms = new List(); + foreach (BigBlock bb in then_branch) + { + Term translated_bb = makeBigBlockTerm(bb, proofGenInfo, cmdIsaVisitor, 0, nestedBlockTracker, nameToUse, + out int updatedTrackerAfterBlock); + + foreach (var pair in bigBlocksDefDecls) + { + if (pair.Value.First().Equation.Item2.ToString() == translated_bb.ToString() && + proofGenInfo.GetMappingCopyBigBlockToIndex()[pair.Key] == proofGenInfo.GetMappingCopyBigBlockToIndex()[bb]) + { + translated_bb = IsaCommonTerms.TermIdentFromName(pair.Value.First().Name); + break; + } + } + + then_branch_bigblock_terms.Add(translated_bb); + nestedBlockTracker = updatedTrackerAfterBlock + 1; + } + Term then_branch_term = new TermList(then_branch_bigblock_terms); + + IList else_branch = _if.elseBlock.BigBlocks; + IList else_branch_bigblock_terms = new List(); + foreach (BigBlock bb in else_branch) + { + Term translated_bb = makeBigBlockTerm(bb, proofGenInfo, cmdIsaVisitor, 0, nestedBlockTracker, nameToUse, + out int updatedTrackerAfterBlock); + + foreach (var pair in bigBlocksDefDecls) + { + if (pair.Value.First().Equation.Item2.ToString() == translated_bb.ToString() && + proofGenInfo.GetMappingCopyBigBlockToIndex()[pair.Key] == proofGenInfo.GetMappingCopyBigBlockToIndex()[bb]) + { + translated_bb = IsaCommonTerms.TermIdentFromName(pair.Value.First().Name); + break; + } + } + + else_branch_bigblock_terms.Add(translated_bb); + nestedBlockTracker = updatedTrackerAfterBlock + 1; + } + Term else_branch_term = new TermList(else_branch_bigblock_terms); + + IList if_args_list = new List(); + if_args_list.Add(guard_term); + if_args_list.Add(then_branch_term); + if_args_list.Add(else_branch_term); + + strCmdTermOption = IsaCommonTerms.SomeOption(new TermApp(if_term, if_args_list)); + } + else if (b.ec is WhileCmd) + { + WhileCmd _while = (WhileCmd) b.ec; + Term wrapper = IsaCommonTerms.TermIdentFromName("WhileWrapper"); + Term while_term = IsaCommonTerms.TermIdentFromName("ParsedWhile"); + + Expr guard = _while.Guard; + Term guard_term = null; + if (guard is null) + { + guard_term = IsaCommonTerms.NoneOption(); + } + else + { + guard_term = IsaCommonTerms.SomeOption(cmdIsaVisitor.TranslateSingle(guard)); + } + + IList invariants = _while.Invariants; + IList inv_terms = new List(); + foreach (PredicateCmd inv in invariants) + { + Term translated_inv = cmdIsaVisitor.TranslateSingle(inv.Expr); + inv_terms.Add(translated_inv); + } + Term invs_as_term = new TermList(inv_terms); + + IList bodyBigBlocks = _while.Body.BigBlocks; + IList body_bigblock_terms = new List(); + foreach (BigBlock bb in bodyBigBlocks) + { + Term translated_bb = makeBigBlockTerm(bb, proofGenInfo, cmdIsaVisitor, 0, nestedBlockTracker, nameToUse, + out int updatedTrackerAfterBlock); + + foreach (var pair in bigBlocksDefDecls) + { + if (pair.Value.First().Equation.Item2.ToString() == translated_bb.ToString() && + proofGenInfo.GetMappingCopyBigBlockToIndex()[pair.Key] == proofGenInfo.GetMappingCopyBigBlockToIndex()[bb]) + { + translated_bb = IsaCommonTerms.TermIdentFromName(pair.Value.First().Name); + break; + } + } + + body_bigblock_terms.Add(translated_bb); + nestedBlockTracker = updatedTrackerAfterBlock + 1; + } + Term body_term = new TermList(body_bigblock_terms); + + IList while_args_list = new List(); + while_args_list.Add(guard_term); + while_args_list.Add(invs_as_term); + while_args_list.Add(body_term); + + Term unwrapped_str_loc = new TermApp(while_term, while_args_list); + strCmdTermOption = IsaCommonTerms.SomeOption(flag == 1 ? unwrapped_str_loc : new TermApp(wrapper, unwrapped_str_loc)); + } + else if (b.ec is BreakCmd) + { + BreakCmd _break = (BreakCmd) b.ec; + Term label = IsaCommonTerms.TermIdentFromName(_break.Label); + Term break_term = IsaCommonTerms.TermIdentFromName("ParsedBreak"); + Term strCmdTerm = new TermApp(break_term, label); + + strCmdTermOption = IsaCommonTerms.SomeOption(strCmdTerm); + } + + updatedNestedBlockTracker = nestedBlockTracker; + return strCmdTermOption; + } + + public Term makeContinuationTerm(BigBlock b, AstToCfgProofGenInfo proofGenInfo, int successorIndex) + { + Term continuationTerm; + Term continuationStart; + Term continuationEnd; + + BigBlock correspondingBigBlockOrig; + if (proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().Keys.Contains(b)) + { + correspondingBigBlockOrig = proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock()[b]; + } + else + { + correspondingBigBlockOrig = proofGenInfo.GetMappingCopyBigblockToOrigBigblock()[b]; + } + + BigBlock successorBigBlockOrig = correspondingBigBlockOrig.successorBigBlock; + + //if the big block has no successors of any kind, make a 'KStop' continuation. + if (successorBigBlockOrig == null) + { + continuationTerm = IsaCommonTerms.TermIdentFromName("KStop"); + return continuationTerm; + } + + //if the big block is the last one in the body of a loop, + //update the successor index needed for the continuation and make a 'KSeq' continuation. + if (successorBigBlockOrig.ec is WhileCmd) + { + WhileCmd wcmd = (WhileCmd) successorBigBlockOrig.ec; + if (InLoop(correspondingBigBlockOrig, wcmd.Body.BigBlocks)) + { + successorIndex++; + + continuationStart = + IsaCommonTerms.TermIdentFromName("KSeq bigblock_" + successorIndex); + continuationTerm = new TermApp(continuationStart, IsaCommonTerms.TermIdentFromName("cont_" + successorIndex)); + return continuationTerm; + } + } + + //if the big block is the special, artificially made, 'unwrapped' loop head big block, make a 'KEndBlock' continuation. + if (b.ec is WhileCmd && proofGenInfo.GetMappingLoopHeadBigBlocktoOrigLoopBigBlock().Keys.Contains(b)) + { + continuationStart = IsaCommonTerms.TermIdentFromName("KEndBlock (KSeq bigblock_" + successorIndex); + continuationEnd = IsaCommonTerms.TermIdentFromName(")"); + continuationTerm = new TermApp(continuationStart, IsaCommonTerms.TermIdentFromName("cont_" + successorIndex), continuationEnd); + return continuationTerm; + } + + //in any other case, make a standard 'KSeq' continuation. + continuationStart = IsaCommonTerms.TermIdentFromName("KSeq bigblock_" + successorIndex); + continuationTerm = new TermApp(continuationStart, IsaCommonTerms.TermIdentFromName("cont_" + successorIndex)); + + return continuationTerm; + } + + private bool InLoop(BigBlock b, IList bbs) + { + foreach (var curr in bbs) + { + if (curr == b) + { + return true; + } + + if (curr.ec is IfCmd ifcmd) + { + if (InLoop(b, ifcmd.thn.BigBlocks) || (ifcmd.elseBlock != null) && InLoop(b, ifcmd.elseBlock.BigBlocks)) + { + return true; + } + } + } + + return false; + } + + + } +} \ No newline at end of file diff --git a/Source/ProofGeneration/BoogieIsaInterface/VariableTranslation/DeBruijnFixedVarTranslation.cs b/Source/ProofGeneration/BoogieIsaInterface/VariableTranslation/DeBruijnFixedVarTranslation.cs index d0489bd66..4b51a519c 100644 --- a/Source/ProofGeneration/BoogieIsaInterface/VariableTranslation/DeBruijnFixedVarTranslation.cs +++ b/Source/ProofGeneration/BoogieIsaInterface/VariableTranslation/DeBruijnFixedVarTranslation.cs @@ -47,7 +47,6 @@ void AddVarsToMapping(IEnumerable vars, IDictionary dic public int VariableId(Variable variable) { if (paramsAndLocalMapping.TryGetValue(variable, out var localResult)) return localResult; - if (globalsMapping.TryGetValue(variable, out var globalResult)) return globalResult; throw new ProofGenUnexpectedStateException(GetType(), "cannot find variable " + variable); diff --git a/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsEndToEnd.cs b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsEndToEnd.cs new file mode 100644 index 000000000..de8d9e28e --- /dev/null +++ b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsEndToEnd.cs @@ -0,0 +1,214 @@ +namespace ProofGeneration.CFGOptimizations; +using System; +using System.Collections.Generic; +using System.Text; +using Isabelle.Ast; +using Isabelle.Util; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.PhasesUtil; +using ProofGeneration.Util; + +public class CFGOptimizationsEndToEnd +{ + private readonly string axiomAssmName = "Axioms"; + private readonly string binderEmptyAssmName = "BinderNs"; + private readonly string closedAssmName = "Closed"; + private readonly string constsGlobalsAssmName = "ConstsGlobal"; + private readonly TermIdent finalNodeOrReturn = IsaCommonTerms.TermIdentFromName("m'"); + private readonly TermIdent finalState = IsaCommonTerms.TermIdentFromName("s'"); + private readonly string finterpAssmName = "FInterp"; + private readonly string nonEmptyTypesAssmName = "NonEmptyTypes"; + + private readonly TermIdent normalInitState = IsaCommonTerms.TermIdentFromName("ns"); + private readonly string oldGlobalAssmName = "OldGlobal"; + private readonly string paramsLocalsAssmName = "ParamsLocal"; + private readonly string preconditionsAssmName = "Precondition"; + + private readonly string redAssmName = "Red"; + private readonly string vcAssmName = "VC"; + private BoogieContextIsa boogieContext; + + private IProgramAccessor programAccessor; + + + private readonly string varContextName = "\\0"; + + + public IEnumerable EndToEndProof( + string entryBlockCfgOptLemma, + EndToEndLemmaConfig endToEndLemmaConfig, + Term vcAssm, + IProgramAccessor beforeOptProgAccess, + IProgramAccessor afterOptProgAccess, + IProgramAccessor programAccessor, + CFGRepr afterOptCFG, + PhasesTheories phasesTheories, + string procedureName) + { + if (endToEndLemmaConfig == EndToEndLemmaConfig.DoNotGenerate) + { + throw new ArgumentException("CFG Optimizations Phase: end-to-end lemma invoked even though disabled"); + } + + this.programAccessor = programAccessor; + boogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("M"), + IsaCommonTerms.TermIdentFromName(varContextName), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.EmptyList + ); + + var abbrev = new AbbreviationDecl( + varContextName, + new Tuple, Term>(new List(), + new TermTuple(afterOptProgAccess.ConstsAndGlobalsDecl(), afterOptProgAccess.ParamsAndLocalsDecl())) + ); + + var result = new List {abbrev}; + + var kStepRed = IsaBoogieTerm.RedCFGKStep( + BoogieContextIsa.CreateWithNewVarContext( + boogieContext, + new TermTuple(afterOptProgAccess.ConstsAndGlobalsDecl(), afterOptProgAccess.ParamsAndLocalsDecl()) + ), + beforeOptProgAccess.CfgDecl(), + IsaBoogieTerm.CFGConfigNode(new NatConst(afterOptCFG.GetUniqueIntLabel(afterOptCFG.entry)), + IsaBoogieTerm.Normal(normalInitState)), + IsaCommonTerms.TermIdentFromName("j"), + IsaBoogieTerm.CFGConfig(finalNodeOrReturn, finalState) + ); + + var proofSb = new StringBuilder(); + proofSb.AppendLine("proof -"); + proofSb.AppendLine("from " + redAssmName + " obtain j where Aux:" + "\"" + kStepRed + "\""); + proofSb.AppendLine("by (meson rtranclp_imp_relpowp)"); + proofSb.AppendLine("show ?thesis"); + proofSb.AppendLine(ProofUtil.Apply("rule " + entryBlockCfgOptLemma)); + proofSb.AppendLine("apply (rule Aux)"); + proofSb.AppendLine("apply (rule allI | rule impI)+"); + proofSb.AppendLine("apply (rule " + phasesTheories.TheoryName(PhasesTheories.Phase.CfgToDag) + ".end_to_end_theorem_aux)"); + proofSb.AppendLine("using assms"); + proofSb.AppendLine("by auto"); + proofSb.AppendLine("qed"); + + var helperLemmaName = "end_to_end_theorem_aux"; + + var helperLemma = + new LemmaDecl( + helperLemmaName, + LemmaContext(afterOptCFG, vcAssm, afterOptProgAccess), + CfgOptLemmaConclusion(boogieContext, afterOptProgAccess.PostconditionsDecl(), + finalNodeOrReturn, finalState), + new Proof(new List {proofSb.ToString()}) + ); + result.Add(helperLemma); + + if (endToEndLemmaConfig == EndToEndLemmaConfig.GenerateForProcedure) + { + var endToEndLemma = + new LemmaDecl( + "end_to_end_theorem", + ContextElem.CreateWithAssumptions(new List {vcAssm}, new List {"VC"}), + IsaBoogieTerm.ProcedureIsCorrectCfg( + programAccessor.FunctionsDecl(), + IsaCommonTerms.TermIdentFromName(programAccessor.ConstsDecl()), + IsaCommonTerms.TermIdentFromName(programAccessor.UniqueConstsDecl()), + IsaCommonTerms.TermIdentFromName(programAccessor.GlobalsDecl()), + programAccessor.AxiomsDecl(), + IsaCommonTerms.TermIdentFromName(programAccessor.ProcDeclName())), + new Proof( + new List + { + ProofUtil.Apply(ProofUtil.Rule(ProofUtil.OF("end_to_end_util",helperLemmaName))), + ProofUtil.Apply("assumption"), + "using VC apply simp", + ProofUtil.Apply("assumption+"), + ProofUtil.Apply($"unfold {programAccessor.ProcDeclName()}_def"), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.PreconditionsDeclName()+"_def", + beforeOptProgAccess.ProcDeclName()+"_def", + "exprs_to_only_checked_spec_1")), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.PostconditionsDeclName()+"_def ", + beforeOptProgAccess.ProcDeclName()+"_def", + "exprs_to_only_checked_spec_2")), + ProofUtil.Apply(ProofUtil.Simp()), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.ProcDeclName() + "_def")), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.ProcDeclName() + "_def")), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.ProcDeclName() + "_def")), + ProofUtil.Apply(ProofUtil.Simp(beforeOptProgAccess.CfgDeclName() + "_def")), + "done" + } + ) ); + + result.Add(endToEndLemma); + } + + return result; + } + + + private ContextElem LemmaContext( + CFGRepr cfg, + Term vcAssm, + IProgramAccessor afterOptProgAccess + ) + { + var multiRed = IsaBoogieTerm.RedCFGMulti( + BoogieContextIsa.CreateWithNewVarContext( + boogieContext, + new TermTuple(afterOptProgAccess.ConstsAndGlobalsDecl(), afterOptProgAccess.ParamsAndLocalsDecl()) + ), + programAccessor.CfgDecl(), + IsaBoogieTerm.CFGConfigNode(new NatConst(cfg.GetUniqueIntLabel(cfg.entry)), + IsaBoogieTerm.Normal(normalInitState)), + IsaBoogieTerm.CFGConfig(finalNodeOrReturn, finalState) + ); + var closedAssm = EndToEndAssumptions.ClosednessAssumption(boogieContext.absValTyMap); + var nonEmptyTypesAssm = EndToEndAssumptions.NonEmptyTypesAssumption(boogieContext.absValTyMap); + var finterpAssm = IsaBoogieTerm.FunInterpWf(boogieContext.absValTyMap, programAccessor.FunctionsDecl(), + boogieContext.funContext); + var absValType = new VarType("a"); + //need to explicitly give type for normal state, otherwise Isabelle won't know that the abstract value type is the same as used in the VC + var axiomAssm = EndToEndAssumptions.AxiomAssumption(boogieContext, programAccessor, + new TermWithExplicitType(normalInitState, IsaBoogieType.NormalStateType(absValType))); + var presAssm = + IsaBoogieTerm.ExprAllSat(boogieContext, normalInitState, afterOptProgAccess.PreconditionsDecl()); + var localsAssm = EndToEndAssumptions.LocalStateAssumption(boogieContext, + IsaCommonTerms.Snd(boogieContext.varContext), normalInitState); + var globalsAssm = EndToEndAssumptions.GlobalStateAssumption(boogieContext, + IsaCommonTerms.Fst(boogieContext.varContext), normalInitState); + var oldGlobalStateAssm = EndToEndAssumptions.OldGlobalStateAssumption(normalInitState); + var binderEmptyAssm = EndToEndAssumptions.BinderStateEmpty(normalInitState); + + return + ContextElem.CreateWithAssumptions( + new List + { + multiRed, vcAssm, closedAssm, nonEmptyTypesAssm, finterpAssm, axiomAssm, + presAssm, localsAssm, globalsAssm, oldGlobalStateAssm, binderEmptyAssm + }, + new List + { + redAssmName, vcAssmName, closedAssmName, nonEmptyTypesAssmName, finterpAssmName, axiomAssmName, + preconditionsAssmName, paramsLocalsAssmName, constsGlobalsAssmName, oldGlobalAssmName, + binderEmptyAssmName + } + ); + } + + public static Term CfgOptLemmaConclusion(BoogieContextIsa boogieContext, Term post, Term finalNode, + Term finalState) + { + return new TermApp( + IsaCommonTerms.TermIdentFromName("Semantics.valid_configuration"), + boogieContext.absValTyMap, + boogieContext.varContext, + boogieContext.funContext, + boogieContext.rtypeEnv, + post, + finalNode, + finalState); + } + +} \ No newline at end of file diff --git a/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsLemmaManager.cs b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsLemmaManager.cs new file mode 100644 index 000000000..9af80be42 --- /dev/null +++ b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsLemmaManager.cs @@ -0,0 +1,661 @@ +using Isabelle.Ast; +using ProofGeneration.BoogieIsaInterface; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Isabelle.Util; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.Util; + + +namespace ProofGeneration.CFGOptimizations; + +public class CFGOptimizationsLemmaManager +{ + private readonly IProgramAccessor beforeOptProgAccess; + private readonly IProgramAccessor afterOptProgAccess; + private readonly CFGRepr beforeOptimizations; + private readonly CFGRepr afterOptimizations; + private readonly string funContextWfName; + private readonly IDictionary beforeToAfterBlock; + private readonly BoogieContextIsa boogieContext; + public CFGOptimizationsLemmaManager( + IProgramAccessor beforeOptProgAccess, + IProgramAccessor afterOptProgAccess, + CFGRepr beforeOptimizations, + CFGRepr afterOptimizations, + string funContextWfName, + BoogieContextIsa boogieContext, + IDictionary beforeToAfterBlock + ) + { + this.beforeOptProgAccess = beforeOptProgAccess; + this.afterOptProgAccess = afterOptProgAccess; + this.beforeOptimizations = beforeOptimizations; + this.afterOptimizations = afterOptimizations; + this.funContextWfName = funContextWfName; + this.boogieContext = boogieContext; + this.beforeToAfterBlock = beforeToAfterBlock; + } + + public LemmaDecl GlobalBlockLemmaPruningNotCoalesced( //Pruning of unreachable blocks where no coalescing happened + Block beforeBlock, + Block afterBlock, + string blockLemmaName, + IList Loops) + { + + var proofMethods = new List + { + "apply (rule pruning_not_coalesced_loop)", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "apply (rule " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + ")", + "apply (rule " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")", + "apply (unfold " + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock) + "_def)", + "apply simp", + "apply (unfold " + afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) + "_def)", + "apply simp" + }; + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() == 0) + { + proofMethods.Add("by (rule " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + else + { + proofMethods.Add("by simp"); + } + + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + Term conclusion = getConclusion(loopHeads, "global_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, false, null); + + + var blockLemma = new LemmaDecl( + blockLemmaName, + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl GlobalBlockLemma( //normal global block lemma where the global block lemma needs to hold for all successors + Block beforeBlock, + Block afterBlock, + Func blockLemmaName, + IDictionary> beforeOptBlockToLoops, + IList Loops, + ISet loopHeadsSet) + { + + + + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + var function = new List(); + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (beforeToAfterBlock.Keys.Contains(succ)) + { + Block succAfter = beforeToAfterBlock[succ]; + function.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[succ] + "," + afterOptProgAccess.BlockInfo().BlockIds[succAfter] + ")"); + } + } + var proofMethods = new List + { + "apply (rule loopBlock_global_block[where ?f = \"the \\ map_of [" + string.Join(",", function) + "]\"])", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "apply simp" + }; + int countCases = 0; + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (loopHeadsSet.Contains(succ) && Loops.Contains(succ)) + { + countCases = countCases + 1; + } + } + + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 1 && beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() - countCases > 1) + { + proofMethods.Add("apply (intro conjI)"); + } + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (Loops.Count == 0) + { + proofMethods.Add("apply (rule " + blockLemmaName(succ) + ")"); + } + else if (!(loopHeadsSet.Contains(succ) && Loops.Contains(succ))) + { + var loopHeadsSucc = new List(); + foreach (Block loop in beforeOptBlockToLoops[succ]) + { + loopHeadsSucc.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + proofMethods.Add("apply(rule exI[where ?x = \"{" + string.Join(",", loopHeadsSucc) + "}\"])"); + proofMethods.Add("apply simp"); + proofMethods.Add("apply (rule " +blockLemmaName(succ) + ")"); + } + + + } + proofMethods.Add("apply simp"); + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 0) + { + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + + + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 1) + { + proofMethods.Add("apply (intro conjI)"); + } + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + proofMethods.Add("apply simp"); + } + + proofMethods.Add("apply (rule " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + ")"); + proofMethods.Add("apply (rule " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")"); + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) + "_def " + + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock) + "_def)"); + proofMethods.Add("apply simp"); + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() == 0) + { + proofMethods.Add("by (rule " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + else + { + proofMethods.Add("by simp"); + } + + Term conclusion = getConclusion(loopHeads, "global_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, false, null); + + var blockLemma = new LemmaDecl( + blockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + public LemmaDecl HybridBlockLemmaTail( //show the hybrid block lemma for the tail of coalesced blocks + Block beforeBlock, + Block afterBlock, + Func GlobalblockLemmaName, + Func HybridblockLemmaName, + IDictionary> beforeOptBlockToLoops, + IList Loops, + ISet loopHeadsSet) + { + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + var function = new List(); + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (beforeToAfterBlock.Keys.Contains(succ)) + { + Block succAfter = beforeToAfterBlock[succ]; + function.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[succ] + "," + afterOptProgAccess.BlockInfo().BlockIds[succAfter] + ")"); + } + } + + var proofMethods = new List + { + + "apply (rule loopBlock_global_block_hybrid[where ?f = \"the \\ map_of [" + string.Join(",", function) + "]\"])", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "apply simp" + }; + int countCases = 0; + + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (loopHeadsSet.Contains(succ) && Loops.Contains(succ)) + { + countCases++; + } + } + + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 1 && beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() - countCases > 1) + { + proofMethods.Add("apply (intro conjI)"); + } + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (Loops.Count == 0) + { + proofMethods.Add("apply (rule " + GlobalblockLemmaName(succ) + ")"); + } + else if (!(loopHeadsSet.Contains(succ) && Loops.Contains(succ))) + { + var loopHeadsSucc = new List(); + foreach (Block loop in beforeOptBlockToLoops[succ]) + { + loopHeadsSucc.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + proofMethods.Add("apply(rule exI[where ?x = \"{" + string.Join(",", loopHeadsSucc) + "}\"])"); + proofMethods.Add("apply simp"); + proofMethods.Add("apply (rule " + GlobalblockLemmaName(succ) + ")"); + } + } + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 0) + { + proofMethods.Add("apply simp"); + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + + proofMethods.Add("apply simp"); + + proofMethods.Add("apply (unfold " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + " " + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock) + "_def)"); + proofMethods.Add("apply simp"); + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() == 0) + { + proofMethods.Add("by (rule " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + else + { + proofMethods.Add("by simp"); + } + List listCoalescedBlocks = new List(); + listCoalescedBlocks.Add(beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock)); + + + Term conclusion = getConclusion(loopHeads, "hybrid_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, true, listCoalescedBlocks); + + + var blockLemma = new LemmaDecl( + HybridblockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl HybridBlockLemma( //extend hybrid block lemma + Block beforeBlock, + Block afterBlock, + Block succ, + Func HybridblockLemmaName, + IList Loops, + IDictionary ListCoalescedBlocks) + { + var proofMethods = new List + { + "apply (rule extend_hybrid_global_block_lemma_loop)", + "apply (rule " + HybridblockLemmaName(succ) + ")", + "apply (rule " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "by simp" + }; + + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + int i = ListCoalescedBlocks[beforeBlock].idx; + List coalescedBlocks = ListCoalescedBlocks[beforeBlock].coalescedBlocks; + + List listCoalescedBlocks = new List(); + foreach (Block current in coalescedBlocks.GetRange(i, coalescedBlocks.Count - i)) + { + listCoalescedBlocks.Add(beforeOptProgAccess.BlockInfo().CmdsQualifiedName(current)); + } + + + + Term conclusion = getConclusion(loopHeads, "hybrid_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, true, listCoalescedBlocks); + + var blockLemma = new LemmaDecl( + HybridblockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl ConvertHybridToGlobal( //show the hybrid block lemma for the head of coalesced blocks + Block beforeBlock, + Block afterBlock, + Func GlobalblockLemmaName, + Func HybridblockLemmaName, + IList Loops, + IDictionary ListCoalescedBlocks) + { + var proofMethods = new List + { + "apply (rule convert_hybrid_global_block_lemma_loop)", + "apply (rule " + HybridblockLemmaName(beforeBlock) + ")" + }; + + int i = ListCoalescedBlocks[beforeBlock].idx; + List coalescedBlocks = ListCoalescedBlocks[beforeBlock].coalescedBlocks; + List listCoalescedBlocks = new List(); + foreach (Block current in coalescedBlocks.GetRange(i, coalescedBlocks.Count - i)) + { + listCoalescedBlocks.Add(beforeOptProgAccess.BlockInfo().CmdsQualifiedName(current)); + } + + + foreach (string b in listCoalescedBlocks) + { + proofMethods.Add("apply (unfold " + b + "_def)"); + } + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + " " + afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) + "_def)"); + proofMethods.Add("by simp"); + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + + Term conclusion = getConclusion(loopHeads, "global_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, false, null); + + var blockLemma = new LemmaDecl( + GlobalblockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl HybridBlockLemmaPruning( //Pruning of unreachable blocks with block coalescing + Block beforeBlock, + Block afterBlock, + Func blockLemmaName, + IList Loops) + { + var proofMethods = new List + { + "apply (rule pruning_coalesced_loop)", + "apply (rule " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + ")", + "apply (unfold " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")", + "apply (unfold " + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock) + "_def)", + "apply simp", + "apply simp", + "apply (unfold " + afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) + "_def)", + "apply simp" + }; + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + proofMethods.Add("apply (unfold " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")"); + proofMethods.Add("by simp"); + + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + + List listCoalescedBlocks = new List(); + listCoalescedBlocks.Add(beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock)); + + Term conclusion = getConclusion(loopHeads, "hybrid_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, true, listCoalescedBlocks); + + var blockLemma = new LemmaDecl( + blockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl LoopHeadNotCoalesced( //Loop Head uncoalesced + Block beforeBlock, + Block afterBlock, + Func blockLemmaName, + IDictionary> beforeOptBlockToLoops, + IList Loops, + ISet loopHeadsSet) + { + + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + var function = new List(); + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (beforeToAfterBlock.Keys.Contains(succ)) + { + Block succAfter = beforeToAfterBlock[succ]; + function.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[succ] + "," + afterOptProgAccess.BlockInfo().BlockIds[succAfter] + ")"); + } + } + var proofMethods = new List + { + "apply (rule loopHead_global_block[where ?f = \"the \\ map_of [" + string.Join(",", function) + "]\"])", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "apply simp" + }; + + int countCases = 0; + + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (loopHeadsSet.Contains(succ) && Loops.Contains(succ)) + { + countCases++; + } + } + + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 1 && beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() - countCases > 1) + { + proofMethods.Add("apply (intro conjI)"); + } + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + if (!(loopHeadsSet.Contains(succ) && Loops.Contains(succ))) + { + var loopHeadsSucc = new List(); + foreach (Block loop in beforeOptBlockToLoops[succ]) + { + loopHeadsSucc.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + proofMethods.Add("apply(rule exI[where ?x = \"{" + string.Join(",", loopHeadsSucc) + "}\"])"); + proofMethods.Add("apply simp"); + proofMethods.Add("apply (rule " + blockLemmaName(succ) + ")"); + } + + } + proofMethods.Add("apply simp"); + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 0) + { + proofMethods.Add("apply (unfold " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + + + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() > 1) + { + proofMethods.Add("apply (intro conjI)"); + } + foreach (Block succ in beforeOptimizations.GetSuccessorBlocks(beforeBlock)) + { + proofMethods.Add("apply simp"); + } + proofMethods.Add("apply (rule " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + ")"); + proofMethods.Add("apply (rule " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")"); + proofMethods.Add("apply (unfold "+ afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) + "_def " + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(beforeBlock) + "_def)"); + proofMethods.Add("apply simp"); + if (beforeOptimizations.GetSuccessorBlocks(beforeBlock).Count() == 0) + { + proofMethods.Add("by (rule " + afterOptProgAccess.BlockInfo().OutEdgesMembershipLemma(afterBlock) + ")"); + } + else + { + proofMethods.Add("by simp"); + } + + Term conclusion = getConclusion(loopHeads, "global_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, false, null); + + var blockLemma = new LemmaDecl( + blockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl LoopHeadCoalesced( //Loop Head coalesced + Block beforeBlock, + Block afterBlock, + Func GlobalblockLemmaName, + Func HybridblockLemmaName, + IList Loops, + IDictionary ListCoalescedBlocks) + { + var loopHeads = new List(); + foreach (Block loop in Loops) + { + loopHeads.Add("(" + beforeOptProgAccess.BlockInfo().BlockIds[loop] + "," + afterOptProgAccess.BlockInfo().BlockIds[beforeToAfterBlock[loop]] + ")"); + } + + var proofMethods = new List + { + "apply (rule loopHead_global_block_hybrid)", + "apply (rule " + beforeOptProgAccess.BlockInfo().OutEdgesMembershipLemma(beforeBlock) + ")", + "apply (rule " + ProofUtil.OF("hybrid_block_lemma_loop_eq_loop_heads", + HybridblockLemmaName(beforeOptimizations.GetSuccessorBlocks(beforeBlock).FirstOrDefault())) + ")", + "apply blast", + "apply (rule " + beforeOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(beforeBlock) + ")", + "apply (rule " + afterOptProgAccess.BlockInfo().BlockCmdsMembershipLemma(afterBlock) + ")", + "apply (unfold " + afterOptProgAccess.BlockInfo().CmdsQualifiedName(afterBlock) +"_def)" + }; + + int i = ListCoalescedBlocks[beforeBlock].idx; + List coalescedBlocks = ListCoalescedBlocks[beforeBlock].coalescedBlocks; + foreach (Block current in coalescedBlocks.GetRange(i, coalescedBlocks.Count - i)) + { + proofMethods.Add("apply (unfold " + beforeOptProgAccess.BlockInfo().CmdsQualifiedName(current) + "_def)"); + } + proofMethods.Add("by simp"); + + + + Term conclusion = getConclusion(loopHeads, "global_block_lemma_loop", beforeBlock, afterBlock, beforeOptProgAccess, + afterOptProgAccess, false, null); + + var blockLemma = new LemmaDecl( + GlobalblockLemmaName(beforeBlock), + conclusion, + new Proof(proofMethods)); + return blockLemma; + } + + public LemmaDecl EntryLemma(string entryLemmaName, string globalBlockLemmaEntryBlockName, Block beforeEntryBlock, + Block afterEntryBlock) + { + Term numSteps = IsaCommonTerms.TermIdentFromName("j"); + Term normalInitState1 = IsaCommonTerms.TermIdentFromName("ns1"); + TermIdent finalNode = IsaCommonTerms.TermIdentFromName("m'"); + Term finalState = IsaCommonTerms.TermIdentFromName("s'"); + var redCfg = IsaBoogieTerm.RedCFGKStep( + boogieContext, + beforeOptProgAccess.CfgDecl(), + IsaBoogieTerm.CFGConfigNode(new NatConst(beforeOptProgAccess.BlockInfo().BlockIds[beforeEntryBlock]), + IsaBoogieTerm.Normal(normalInitState1)), + numSteps, + IsaBoogieTerm.CFGConfig(finalNode, finalState)); + + var finalNodeId2 = new SimpleIdentifier("m2'"); + var finalStateId2 = new SimpleIdentifier("s2'"); + var tarVer = cfgOptTargetVerifies(new NatConst(afterOptProgAccess.BlockInfo().BlockIds[afterEntryBlock]), + normalInitState1, finalNodeId2, finalStateId2); + + + var assumptions = new List {redCfg}; + assumptions.Add(tarVer); + + return new LemmaDecl( + entryLemmaName, + ContextElem.CreateWithAssumptions(assumptions), + CFGOptimizationsEndToEnd.CfgOptLemmaConclusion(boogieContext, afterOptProgAccess.PostconditionsDecl(), finalNode, finalState), + new Proof(new List + { + "using " + globalBlockLemmaEntryBlockName, + "unfolding global_block_lemma_loop_def", + "using assms(1) assms(2) by blast" + }) + ); + + } + + private Term cfgOptTargetVerifies( + Term initialStateNode, + Term initialNormalState, + Identifier finalNodeId2, + Identifier finalStateId2 + ) + { + Term finalNode2 = new TermIdent(finalNodeId2); + Term finalState2 = new TermIdent(finalStateId2); + + Func, IList, Term, TermQuantifier> forallConstructor; + Func impliesConstructor; + forallConstructor = TermQuantifier.ForAll; + impliesConstructor = TermBinary.Implies; + + return + forallConstructor( + new List {finalNodeId2, finalStateId2}, + null, + impliesConstructor( + IsaBoogieTerm.RedCFGMulti(boogieContext, + afterOptProgAccess.CfgDecl(), + IsaBoogieTerm.CFGConfigNode( + initialStateNode, IsaBoogieTerm.Normal(initialNormalState) + ), + IsaBoogieTerm.CFGConfig(finalNode2, finalState2) + ), + CFGOptimizationsEndToEnd.CfgOptLemmaConclusion(boogieContext, afterOptProgAccess.PostconditionsDecl(), finalNode2, finalState2)) + ); + } + + public static Term getConclusion(List loopHeads, string name, Block beforeBlock, Block afterBlock, IProgramAccessor beforeOptProgAccess, IProgramAccessor afterOptProgAccess, bool isHybridBlock, List listCoalescedBlocks) + { + var loopHeadsSet = new TermSet(loopHeads.Select(lh => IsaCommonTerms.TermIdentFromName(lh))); + + var varContextName = "\\"; + IList terms = new List(); + terms.Add(IsaCommonTerms.TermIdentFromName("A")); + terms.Add(IsaCommonTerms.TermIdentFromName("M")); + terms.Add(IsaCommonTerms.TermIdentFromName(varContextName)); + terms.Add(IsaCommonTerms.TermIdentFromName("\\")); + terms.Add(IsaCommonTerms.TermIdentFromName("\\")); + terms.Add(beforeOptProgAccess.CfgDecl()); + terms.Add(afterOptProgAccess.CfgDecl()); + terms.Add(new NatConst(beforeOptProgAccess.BlockInfo().BlockIds[beforeBlock])); + terms.Add(new NatConst(afterOptProgAccess.BlockInfo().BlockIds[afterBlock])); + if (isHybridBlock) + { + var listCoalescedBlocksTerm = listCoalescedBlocks.Aggregate(IsaCommonTerms.EmptyList, + (x, y) => IsaCommonTerms.AppendList(x, IsaCommonTerms.TermIdentFromName(y))); + terms.Add(listCoalescedBlocksTerm); + } + terms.Add(loopHeadsSet); + terms.Add(afterOptProgAccess.PostconditionsDecl()); + Term conclusion = new TermApp(IsaCommonTerms.TermIdentFromName(name), terms); + return conclusion; + } + +} + diff --git a/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsManager.cs b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsManager.cs new file mode 100644 index 000000000..126f6738f --- /dev/null +++ b/Source/ProofGeneration/CFGOptimizations/CFGOptimizationsManager.cs @@ -0,0 +1,300 @@ +using Isabelle.Ast; +using ProofGeneration.BoogieIsaInterface; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Isabelle.Ast; +using Isabelle.Util; +using Microsoft.Boogie; +using ProofGeneration.BoogieIsaInterface; +using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGRepresentation; +using ProofGeneration.PhasesUtil; +using ProofGeneration.Util; +using ProofGenUtil; + + +namespace ProofGeneration.CFGOptimizations; + +public class CfgOptimizationsManager +{ + + public static Theory CfgOptProof( + PhasesTheories phasesTheories, + EndToEndLemmaConfig endToEndLemmaConfig, + CFGRepr beforeOptimizations, + CFGRepr afterOptimizations, + IDictionary beforeToAfter, // mapping from current block to target block + IProgramAccessor beforeOptCfgProgAccess, //before CFG optimizations + IProgramAccessor afterOptCfgProgAccess, //after CFG optimizationsList + IDictionary ListCoalescedBlocks, + IDictionary CoalescedBlocksToTarget, + IDictionary> beforeDagBlockToLoops, + Term vcAssm, + string procedureName, + bool generateCfgDagProof, + IDictionary selfLoops) + { + IDictionary afterToBefore = beforeToAfter.ToDictionary(x => x.Value, x => x.Key); + IDictionary> beforeOptBlockToLoops = new Dictionary>(); + Block coalescedAfterBlock = new Block(); + ISet loopHeadsSet = new HashSet(); + IDictionary selfLoopsNew = new Dictionary(); + + + foreach (Block beforeBlock in beforeOptimizations.GetBlocksForwards()) + { + if (beforeToAfter.ContainsKey(beforeBlock)) + { + IList temp = new List(); + foreach (Block loopHeader in beforeDagBlockToLoops[beforeToAfter[beforeBlock]]) + { + temp.Add(afterToBefore[loopHeader]); + loopHeadsSet.Add(afterToBefore[loopHeader]); + } + beforeOptBlockToLoops.Add(beforeBlock, temp); + if (selfLoops.ContainsKey(beforeToAfter[beforeBlock])) + { + selfLoopsNew.Add(beforeBlock, beforeBlock); + loopHeadsSet.Add(beforeBlock); + } + } + else if (CoalescedBlocksToTarget.ContainsKey(beforeBlock)) { + coalescedAfterBlock = CoalescedBlocksToTarget[beforeBlock]; + List temp = new List(); + foreach (Block loopHeader in beforeDagBlockToLoops[coalescedAfterBlock]) + { + temp.Add(afterToBefore[loopHeader]); + loopHeadsSet.Add(afterToBefore[loopHeader]); + } + + foreach (Block afterSucc in afterOptimizations.GetSuccessorBlocks( + CoalescedBlocksToTarget[beforeBlock])) + { + if (beforeDagBlockToLoops[coalescedAfterBlock].Count < beforeDagBlockToLoops[afterSucc].Count) + { + temp.Add(afterToBefore[CoalescedBlocksToTarget[beforeBlock]]); + loopHeadsSet.Add(afterToBefore[CoalescedBlocksToTarget[beforeBlock]]); + break; + } + } + if (selfLoops.ContainsKey(coalescedAfterBlock)) + { + temp.Add(afterToBefore[coalescedAfterBlock]); + loopHeadsSet.Add(afterToBefore[coalescedAfterBlock]); + } + beforeOptBlockToLoops.Add(beforeBlock, temp); + + } + } + + + var varContextName = "\\"; + + var funContextWfName = "Wf_Fun"; + + var boogieContext = new BoogieContextIsa( + IsaCommonTerms.TermIdentFromName("A"), + IsaCommonTerms.TermIdentFromName("M"), + IsaCommonTerms.TermIdentFromName(varContextName), + IsaCommonTerms.TermIdentFromName("\\"), + IsaCommonTerms.TermIdentFromName("\\")); + + + var lemmaManager = new CFGOptimizationsLemmaManager( + beforeOptCfgProgAccess, + afterOptCfgProgAccess, + beforeOptimizations, + afterOptimizations, + funContextWfName, + boogieContext, + beforeToAfter); + + var lemmaNamer = new IsaUniqueNamer(); + var outerDecls = new List(); + + + outerDecls.Add(new DeclareDecl("Nat.One_nat_def[simp del]")); + + + CFGRepr beforeOptimizationsCopy = beforeOptimizations.Copy(); + beforeOptimizationsCopy.DeleteBackedges(beforeOptBlockToLoops, selfLoopsNew); + + List topoOrder = new List(); + TopologicalOrder(topoOrder, beforeOptimizationsCopy); + topoOrder.Reverse(); + + foreach (var beforeBlock in topoOrder) + { + + if (loopHeadsSet.Contains(beforeBlock) && CoalescedBlocksToTarget.ContainsKey(beforeBlock)) //In this case we have a coalesced loop head + { + coalescedAfterBlock = CoalescedBlocksToTarget[beforeBlock]; + var globalBlock = lemmaManager.LoopHeadCoalesced(beforeBlock, coalescedAfterBlock, + bigblock => GetGlobalBlockLemmaName(bigblock, lemmaNamer), + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), + beforeOptBlockToLoops[beforeBlock], ListCoalescedBlocks); + outerDecls.Add(globalBlock); + } + else if (loopHeadsSet.Contains(beforeBlock)) //normal Loop Head + { + var globalBlock = lemmaManager.LoopHeadNotCoalesced(beforeBlock, beforeToAfter[beforeBlock], bigblock => GetGlobalBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops, beforeOptBlockToLoops[beforeBlock], loopHeadsSet); + outerDecls.Add(globalBlock); + } + else if (CoalescedBlocksToTarget.ContainsKey(beforeBlock)) //in this case we have block coalescing + { + if (ProgramToVCProof.LemmaHelper.FinalStateIsMagic(beforeBlock)) //Pruning of Unreachable Blocks Coalesced + { + coalescedAfterBlock = CoalescedBlocksToTarget[beforeBlock]; + var pruningCoalesced = lemmaManager.HybridBlockLemmaPruning(beforeBlock, coalescedAfterBlock, + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops[beforeBlock]); + outerDecls.Add(pruningCoalesced); + } + else if (beforeToAfter.ContainsKey(beforeBlock)) //Head of coalesced blocks + { + Block afterBlock = beforeToAfter[beforeBlock]; + var head = lemmaManager.HybridBlockLemma(beforeBlock, afterBlock, + beforeOptimizations.GetSuccessorBlocks(beforeBlock).FirstOrDefault(), + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops[beforeBlock], + ListCoalescedBlocks); + outerDecls.Add(head); + var convertGlobalBlock = lemmaManager.ConvertHybridToGlobal(beforeBlock, afterBlock, + bigblock => GetGlobalBlockLemmaName(bigblock, lemmaNamer), + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops[beforeBlock], + ListCoalescedBlocks); + outerDecls.Add(convertGlobalBlock); + } + else if (ListCoalescedBlocks[beforeBlock].coalescedBlocks.Count == ListCoalescedBlocks[beforeBlock].idx + 1) //tail of coalesced blocks + { + coalescedAfterBlock = CoalescedBlocksToTarget[beforeBlock]; + + var tail = lemmaManager.HybridBlockLemmaTail(beforeBlock, coalescedAfterBlock, + bigblock => GetGlobalBlockLemmaName(bigblock, lemmaNamer), + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), + beforeOptBlockToLoops, beforeOptBlockToLoops[beforeBlock], loopHeadsSet); + outerDecls.Add(tail); + } + else //in Between Block + { + coalescedAfterBlock = CoalescedBlocksToTarget[beforeBlock]; + var inBetweenBlock = lemmaManager.HybridBlockLemma(beforeBlock, coalescedAfterBlock, + beforeOptimizations.GetSuccessorBlocks(beforeBlock).FirstOrDefault(), + bigblock => GetHybridBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops[beforeBlock], + ListCoalescedBlocks); + outerDecls.Add(inBetweenBlock); + } + } + else //no block coalescing + { + if (beforeToAfter.Keys.Contains(beforeBlock)) + { + Block afterBlock = beforeToAfter[beforeBlock]; + if (ProgramToVCProof.LemmaHelper.FinalStateIsMagic(beforeBlock)) //If there is an assume or assert false statement in the block + { + var pruning = lemmaManager.GlobalBlockLemmaPruningNotCoalesced(beforeBlock, afterBlock, GetGlobalBlockLemmaName(beforeBlock, lemmaNamer), beforeOptBlockToLoops[beforeBlock]); + outerDecls.Add(pruning); + } + else //otherwhise we just need to apply the normal global block lemma. Assumption: Global Block Lemma holds for all successors + { + var globalBlock = lemmaManager.GlobalBlockLemma(beforeBlock, afterBlock, bigblock => GetGlobalBlockLemmaName(bigblock, lemmaNamer), beforeOptBlockToLoops, beforeOptBlockToLoops[beforeBlock], loopHeadsSet); + outerDecls.Add(globalBlock); + } + } + } + } + + var entryLemma = lemmaManager.EntryLemma("entry_lemma", + GetGlobalBlockLemmaName(beforeOptimizations.entry, lemmaNamer), beforeOptimizations.entry, + afterOptimizations.entry); + outerDecls.Add(entryLemma); + if (endToEndLemmaConfig != EndToEndLemmaConfig.DoNotGenerate) + { + var endToEndManager = new CFGOptimizationsEndToEnd(); + var endToEndDecls = endToEndManager.EndToEndProof( + "entry_lemma", + endToEndLemmaConfig, + vcAssm, + beforeOptCfgProgAccess, + afterOptCfgProgAccess, + beforeOptCfgProgAccess, + afterOptimizations, + phasesTheories, + procedureName + ); + outerDecls.AddRange(endToEndDecls); + } + + + List importTheories = new List + { + "Boogie_Lang.Semantics", "Boogie_Lang.Util", "Boogie_Lang.CFGOptimizationsLoop", + afterOptCfgProgAccess.TheoryName(), + beforeOptCfgProgAccess.TheoryName() + }; + if (generateCfgDagProof) + { + importTheories.Add(phasesTheories.TheoryName(PhasesTheories.Phase.CfgToDag)); + } + + + return new Theory( + phasesTheories.TheoryName(PhasesTheories.Phase.CfgOptimizations), + importTheories, + outerDecls + ); + } + + private static string GetGlobalBlockLemmaName(Block b, IsaUniqueNamer namer) + { + return "global_block_lemma_" + namer.GetName(b, "block_" + b.Label); + } + + private static string GetHybridBlockLemmaName(Block b, IsaUniqueNamer namer) + { + return "hybrid_block_lemma_" + namer.GetName(b, "block_" + b.Label); + } + + + + + + + + + private static void TopologicalOrder(List topoOrder, CFGRepr beforeOptimizationsCopy) + { + Dictionary inDegree = new Dictionary(); + foreach (var b in beforeOptimizationsCopy.GetBlocksForwards()) + { + inDegree.Add(b, b.Predecessors.Count()); + } + + Queue q = new Queue(); + foreach (var b in beforeOptimizationsCopy.GetBlocksForwards()) + { + if (inDegree[b] == 0) + { + q.Enqueue(b); + } + } + + while (q.Count > 0) + { + Block u = q.Dequeue(); + topoOrder.Add(u); + foreach (var b in beforeOptimizationsCopy.GetSuccessorBlocks(u)) + { + if (--inDegree[b] == 0) + { + q.Enqueue(b); + } + } + + } + + + } + + +} \ No newline at end of file diff --git a/Source/ProofGeneration/CFGRepr/CFGRepr.cs b/Source/ProofGeneration/CFGRepr/CFGRepr.cs index 4199ab844..efaf91155 100644 --- a/Source/ProofGeneration/CFGRepr/CFGRepr.cs +++ b/Source/ProofGeneration/CFGRepr/CFGRepr.cs @@ -1,13 +1,15 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; +using ProofGenUtil; namespace ProofGeneration.CFGRepresentation { public class CFGRepr { - private readonly Block[] blocks; + private Block[] blocks; //make sure that I can change that from readonly public readonly Block entry; private readonly IDictionary labeling; private readonly IDictionary> outgoingBlocks; @@ -75,5 +77,89 @@ public IEnumerable GetBlocksForwards() for (var i = blocks.Length - 1; i >= 0; i--) yield return blocks[i]; } + + + //TODO: Check if this is correct + public CFGRepr(CFGRepr other) + { + // Copy blocks array + blocks = new Block[other.blocks.Length]; + Array.Copy(other.blocks, blocks, other.blocks.Length); + + // Copy outgoingBlocks dictionary + outgoingBlocks = new Dictionary>(); + foreach (var kvp in other.outgoingBlocks) + { + outgoingBlocks.Add(kvp.Key, new List(kvp.Value)); + } + + + // Copy labeling dictionary + labeling = new Dictionary(); + foreach (var kvp in other.labeling) + { + labeling.Add(kvp.Key, kvp.Value); + } + + + // Copy entry block + entry = other.entry; + } + + public CFGRepr Copy() + { + return new CFGRepr(this); + } + + public void DeleteBackedges(IDictionary> BlockToLoops, IDictionary selfLoops) + { + foreach (Block b in blocks) + { + List Backedges = new List(); + if (BlockToLoops.ContainsKey(b)) + { + if (ProgramToVCProof.LemmaHelper.FinalStateIsMagic(b)) + { + foreach (Block succ in GetSuccessorBlocks(b)) + { + Backedges.Add(succ); + } + foreach (Block toRemove in Backedges) + { + toRemove.Predecessors.Remove(b); + outgoingBlocks[b].Remove(toRemove); + } + } + else + { + foreach (Block succ in GetSuccessorBlocks(b)) + { + if (BlockToLoops[b].Contains(succ)) + { + Backedges.Add(succ); + } + } + foreach (Block toRemove in Backedges) + { + toRemove.Predecessors.Remove(b); + outgoingBlocks[b].Remove(toRemove); + } + + } + + } + + if (selfLoops.ContainsKey(b)) + { + outgoingBlocks[b].Remove(b); + b.Predecessors.Remove(b); + } + } + } + + + + + } } \ No newline at end of file diff --git a/Source/ProofGeneration/CFGRepr/CFGReprTransformer.cs b/Source/ProofGeneration/CFGRepr/CFGReprTransformer.cs index ee5f5204c..8519ec57d 100644 --- a/Source/ProofGeneration/CFGRepr/CFGReprTransformer.cs +++ b/Source/ProofGeneration/CFGRepr/CFGReprTransformer.cs @@ -46,7 +46,7 @@ public static CFGRepr GetCfgRepresentation( if (config.GenerateBlockCopy) { - blocksToConvert = CopyBlocks(impl.Blocks, predecessorMap, config.GenerateBlockCopy, + blocksToConvert = CopyBlocks(impl.Blocks, predecessorMap, config.DesugarCalls, config.DeepCopyCmdPred, out newVarsFromDesugaring); var newToOldInternal = new Dictionary(); @@ -139,7 +139,7 @@ private static Dictionary> ComputePredecessors(IEnumerable GetTopologicalLabeling(IList blocks) + public static IDictionary GetTopologicalLabeling(IList blocks) { Contract.Requires(blocks != null); Contract.Ensures(blocks.Count == Contract.Result>().Count); diff --git a/Source/ProofGeneration/CfgToDag/CfgToDagEndToEnd.cs b/Source/ProofGeneration/CfgToDag/CfgToDagEndToEnd.cs index 668842cc0..d799045fb 100644 --- a/Source/ProofGeneration/CfgToDag/CfgToDagEndToEnd.cs +++ b/Source/ProofGeneration/CfgToDag/CfgToDagEndToEnd.cs @@ -29,11 +29,13 @@ public class CfgToDagEndToEnd private readonly string redAssmName = "Red"; private readonly string vcAssmName = "VC"; private BoogieContextIsa boogieContext; + private IProgramAccessor programAccessor; private readonly string varContextName = "\\0"; public IEnumerable EndToEndProof( + bool lemmaForProcedure, string entryCfgLemma, string passificationEndToEndLemma, Term vcAssm, @@ -41,6 +43,7 @@ public IEnumerable EndToEndProof( CFGRepr cfg) { this.programAccessor = programAccessor; + boogieContext = new BoogieContextIsa( IsaCommonTerms.TermIdentFromName("A"), IsaCommonTerms.TermIdentFromName("M"), @@ -116,28 +119,46 @@ public IEnumerable EndToEndProof( result.Add(helperLemma); //transform end to end theorem to a compact representation - var endToEndLemma = + if (lemmaForProcedure) + { + var endToEndLemma = new LemmaDecl( - "end_to_end_theorem", - ContextElem.CreateWithAssumptions(new List {vcAssm}, new List {"VC"}), - ProcedureIsCorrect( - programAccessor.FunctionsDecl(), - IsaCommonTerms.TermIdentFromName(programAccessor.ConstsDecl()), - IsaCommonTerms.TermIdentFromName(programAccessor.GlobalsDecl()), - programAccessor.AxiomsDecl(), - programAccessor.ProcDecl()), - new Proof( - new List - { - ProofUtil.Apply(ProofUtil.Rule(ProofUtil.OF("end_to_end_util",helperLemmaName))), - "apply assumption " + "using VC apply simp " + " apply assumption+", - ProofUtil.By("simp_all add: exprs_to_only_checked_spec_1 exprs_to_only_checked_spec_2 " + - programAccessor.ProcDeclName() + "_def " + programAccessor.CfgDeclName() + "_def") - } - ) ); - - result.Add(endToEndLemma); - return result; + "end_to_end_theorem", + ContextElem.CreateWithAssumptions(new List { vcAssm }, new List { "VC" }), + IsaBoogieTerm.ProcedureIsCorrectCfg( + programAccessor.FunctionsDecl(), + IsaCommonTerms.TermIdentFromName(programAccessor.ConstsDecl()), + IsaCommonTerms.TermIdentFromName(programAccessor.UniqueConstsDecl()), + IsaCommonTerms.TermIdentFromName(programAccessor.GlobalsDecl()), + programAccessor.AxiomsDecl(), + programAccessor.ProcDecl()), + new Proof( + new List + { + ProofUtil.Apply(ProofUtil.Rule(ProofUtil.OF("end_to_end_util", helperLemmaName))), + "apply assumption " + "using VC apply simp " + " apply assumption+", + ProofUtil.By("simp_all add: exprs_to_only_checked_spec_1 exprs_to_only_checked_spec_2 " + + programAccessor.ProcDeclName() + "_def " + programAccessor.CfgDeclName() + "_def " + + programAccessor.PreconditionsDeclName() + "_def " + + programAccessor.PostconditionsDeclName() + "_def " + + programAccessor.ParamsDecl() + "_def " + programAccessor.LocalsDecl() + "_def " + + programAccessor.PreconditionsDeclName() + "_def " + + programAccessor.PostconditionsDeclName() + "_def " + + programAccessor.ParamsDecl() + "_def " + programAccessor.LocalsDecl() + "_def " + + + programAccessor.FunctionsDecl() + "_def " + programAccessor.FunctionsDecl() + + "_def " + + programAccessor.AxiomsDecl() + "_def " + programAccessor.AxiomsDecl() + "_def " + + programAccessor.ConstsDecl() + "_def " + programAccessor.ConstsDecl() + "_def " + + programAccessor.GlobalsDecl() + "_def " + programAccessor.GlobalsDecl() + "_def " + + "exprs_to_only_checked_spec_def") + } + )); + + result.Add(endToEndLemma); + } + + return result; } private ContextElem LemmaContext( @@ -188,23 +209,5 @@ Term vcAssm ); } - public static Term ProcedureIsCorrect(Term funDecls, Term constantDecls, Term globalDecls, Term axioms, - Term procedure) - { - var typeInterpId = new SimpleIdentifier("A"); - return - TermQuantifier.MetaAll( - new List{ typeInterpId}, - null, - new TermApp( - IsaCommonTerms.TermIdentFromName("proc_is_correct"), - //TODO: here assuming that we use "'a" for the abstract value type carrier t --> make t a parameter somewhere - new TermWithExplicitType(new TermIdent(typeInterpId), IsaBoogieType.AbstractValueTyFunType(new VarType("a"))), - funDecls, - constantDecls, - globalDecls, - axioms, - procedure)); - } } } \ No newline at end of file diff --git a/Source/ProofGeneration/CfgToDag/CfgToDagHintManager.cs b/Source/ProofGeneration/CfgToDag/CfgToDagHintManager.cs index 807a079cb..33d799919 100644 --- a/Source/ProofGeneration/CfgToDag/CfgToDagHintManager.cs +++ b/Source/ProofGeneration/CfgToDag/CfgToDagHintManager.cs @@ -15,6 +15,10 @@ public class CfgToDagHintManager //maps backedge nodes which are not present before the CFG-to-DAG to the corresponding loop head private readonly IDictionary newBackedgeNodesToLoopHead = new Dictionary(); + + //maps nodes before entry to loop which are not present before CFG-to-DAG to the corresponding loop head + private readonly IDictionary newPreLoopNodesToLoopHead = new Dictionary(); + private readonly IDictionary origToBeforeDag; private IDictionary _afterDagToOrig; @@ -53,7 +57,13 @@ public void AddNewBackedgeBlock(Block newBackedgeBlock, Block loopHead) { newBackedgeNodesToLoopHead.Add(newBackedgeBlock, loopHead); } - + + /// pre loop entry block that is newly added in the CFG-to-DAG phase. + /// corresponding loop head + public void AddNewPreLoopEntryBlock(Block newPreLoopEntryBlock, Block loopHead) + { + newPreLoopNodesToLoopHead.Add(newPreLoopEntryBlock, loopHead); + } public bool IsNewBackedgeBlock(Block afterDagBlock, out LoopHeadHint loopHeadHint) { @@ -78,15 +88,20 @@ public bool IsNewBackedgeBlock(Block afterDagBlock, out Block beforeDagLoopHead, beforeDagLoopHead = null; return false; } + + public bool IsNewPreLoopEntryBlock(Block afterDagBlock) + { + return newPreLoopNodesToLoopHead.ContainsKey(_afterDagToOrig[afterDagBlock]); + } - public bool IsLoopHead(Block block, out LoopHeadHint hint) + public bool IsLoopHead(Block beforeDagBlock, out LoopHeadHint hint) { - return loopHeadHints.TryGetValue(beforeDagToOrig[block], out hint); + return loopHeadHints.TryGetValue(beforeDagToOrig[beforeDagBlock], out hint); } - public LoopHeadHint GetLoopHead(Block block) + public LoopHeadHint GetLoopHead(Block beforeDagBlock) { - return loopHeadHints[beforeDagToOrig[block]]; + return loopHeadHints[beforeDagToOrig[beforeDagBlock]]; } } } \ No newline at end of file diff --git a/Source/ProofGeneration/CfgToDag/CfgToDagLemmaManager.cs b/Source/ProofGeneration/CfgToDag/CfgToDagLemmaManager.cs index ba46fdfdb..9a9b1402a 100644 --- a/Source/ProofGeneration/CfgToDag/CfgToDagLemmaManager.cs +++ b/Source/ProofGeneration/CfgToDag/CfgToDagLemmaManager.cs @@ -634,7 +634,7 @@ public static Term CfgLemmaConclusion(BoogieContextIsa boogieContext, Term post, Term finalState) { return new TermApp( - IsaCommonTerms.TermIdentFromName("valid_configuration"), + IsaCommonTerms.TermIdentFromName("Semantics.valid_configuration"), boogieContext.absValTyMap, boogieContext.varContext, boogieContext.funContext, @@ -729,22 +729,24 @@ private void GenerateProofBody( If so, then we need to get the invariant satisfiability from that block. */ foreach (var afterSuc in afterDagCfg.GetSuccessorBlocks(afterBlock)) + { if (hintManager.IsNewBackedgeBlock(afterSuc, out var loopHead, out var _) && loopHead == bSuc) { - //TODO separate function for this and share code with case below - var afterSucId = afterDagProgAccess.BlockInfo().BlockIds[afterSuc]; - sb.AppendLine("(* proof strategy for new backedge block *)"); - sb.AppendLine("apply (erule allE[where x=" + afterSucId + "])"); - sb.AppendLine(ProofUtil.Apply( - ProofUtil.Simp(afterDagProgAccess.BlockInfo() - .OutEdgesMembershipLemma(afterBlock)))); - sb.AppendLine(ProofUtil.Apply(ProofUtil.Simp("member_rec(1)"))); - sb.AppendLine(ProofUtil.Apply("erule " + cfgLemmaName(afterSuc))); - sb.AppendLine(ProofUtil.Apply("assumption, assumption, simp")); - sb.AppendLine("(* finish proof strategy for new backedge block *)"); - break; + //TODO separate function for this and share code with case below + var afterSucId = afterDagProgAccess.BlockInfo().BlockIds[afterSuc]; + sb.AppendLine("(* proof strategy for new backedge block *)"); + sb.AppendLine("apply (erule allE[where x=" + afterSucId + "])"); + sb.AppendLine(ProofUtil.Apply( + ProofUtil.Simp(afterDagProgAccess.BlockInfo() + .OutEdgesMembershipLemma(afterBlock)))); + sb.AppendLine(ProofUtil.Apply(ProofUtil.Simp("member_rec(1)"))); + sb.AppendLine(ProofUtil.Apply("erule " + cfgLemmaName(afterSuc))); + sb.AppendLine(ProofUtil.Apply("assumption, assumption, simp")); + sb.AppendLine("(* finish proof strategy for new backedge block *)"); + break; } + } if (beforeBlock.Equals(bSuc)) { @@ -795,22 +797,36 @@ to propagate the execution in the DAG } else { - /* need to find the empty block that was added in between (does not matter which one, if there - * are multiple such empty blocks) */ + // need to find the empty block that was added in between + var bSucAfterIsLoopHead = hintManager.IsLoopHead(bSuc, out _); + foreach (var afterSuc in afterSuccessors) { var afterSucSuccessors = afterDagCfg.GetSuccessorBlocks(afterSuc); - if (!afterSuc.Cmds.Any() && afterSucSuccessors.Count() == 1 && - afterSucSuccessors.First().Equals(bSucAfter)) + + // block in between must have a single expected successor + if (afterSucSuccessors.Count() != 1 || !afterSucSuccessors.First().Equals(bSucAfter)) { - addedBlock = afterSuc; - break; + continue; } + + /** if expected successor is not a loop head, then the block in between must be empty, + * otherwise the block in between must be a new pre entry loop block (which asserts the loop invariant) + */ + if ( (!bSucAfterIsLoopHead && !afterSuc.Cmds.Any()) + || + (bSucAfterIsLoopHead && hintManager.IsNewPreLoopEntryBlock(afterSuc)) ) + { + addedBlock = afterSuc; + break; //if there are multiple blocks that satisfy the criteria, then we can pick any, so break here + } } if (addedBlock == null) - throw new ProofGenUnexpectedStateException("Could not find block"); - + { + throw new ProofGenUnexpectedStateException("Could not find new target block added in between two source blocks"); + } + bSucAfterId = afterDagProgAccess.BlockInfo().BlockIds[addedBlock]; } @@ -908,8 +924,7 @@ private string TypingTactics(IEnumerable typingTactics, string stateWtTh "type_safety_top_level_inv", funContextWfName, beforeDagProgAccess.FuncsWfTyLemma(), - beforeDagProgAccess - .VarContextWfTyLemma()))); + beforeDagProgAccess.VarContextWfTyLemma()))); if (stateWtThm != null) sb.AppendLine(ProofUtil.Apply("rule " + stateWtThm)); @@ -975,8 +990,7 @@ Func cfgLemmaName .BlockCmdsMembershipLemma(afterBlock))); sb.AppendLine(ProofUtil.Apply("erule " + dagVerifiesName)); sb.AppendLine(ProofUtil.Apply("rule " + dagAssmsName)); - sb.AppendLine("unfolding " + beforeDagProgAccess.PostconditionsDeclName() + - "_def"); + sb.AppendLine("unfolding " + beforeDagProgAccess.PostconditionsDeclName() + "_def"); sb.AppendLine(ProofUtil.Apply("rule " + blockLemmaName(beforeBlock))); sb.AppendLine("apply assumption+"); sb.AppendLine( @@ -1030,7 +1044,7 @@ private Proof GenerateProofLoopHead( sb.AppendLine("show ?case"); sb.AppendLine("proof (cases j)"); sb.AppendLine( - "case 0 with less.prems(1) show ?thesis unfolding valid_configuration_def by auto"); + "case 0 with less.prems(1) show ?thesis unfolding Semantics.valid_configuration_def by auto"); sb.AppendLine("next"); sb.AppendLine("case (Suc j')"); sb.Append("from less(3) have " + stateRelLoopHeadName + ":"); @@ -1152,7 +1166,7 @@ Term numSteps cfg, modVars, invs, - beforeDagProgAccess.PostconditionsDecl(), + beforeDagProgAccess.PostconditionsDecl(), normalState, finalState, loopHeadNode, diff --git a/Source/ProofGeneration/CfgToDag/CfgToDagManager.cs b/Source/ProofGeneration/CfgToDag/CfgToDagManager.cs index f99fff19a..e8c464577 100644 --- a/Source/ProofGeneration/CfgToDag/CfgToDagManager.cs +++ b/Source/ProofGeneration/CfgToDag/CfgToDagManager.cs @@ -7,11 +7,12 @@ using ProofGeneration.BoogieIsaInterface; using ProofGeneration.BoogieIsaInterface.VariableTranslation; using ProofGeneration.CFGRepresentation; +using ProofGeneration.PhasesUtil; using ProofGeneration.Util; namespace ProofGeneration.CfgToDag { - public class CfgToDagManager + public class CfgToDagManager { /** * cases: @@ -23,7 +24,9 @@ public class CfgToDagManager */ public static Theory CfgToDagProof( PhasesTheories phasesTheories, - bool generateEndToEndLemma, + EndToEndLemmaConfig endToEndLemmaConfig, + bool generatePassificationProof, + bool generateVcProof, Term vcAssm, CFGRepr beforeDagCfg, CFGRepr afterDagCfg, @@ -33,35 +36,14 @@ public static Theory CfgToDagProof( IDictionary beforeToAfter, IProgramAccessor beforeDagProgAccess, IProgramAccessor afterDagProgAccess, - IVariableTranslationFactory varFactory) + IVariableTranslationFactory varFactory, + IDictionary> blocksToLoops) { var afterToBefore = beforeToAfter.InverseDict(); //track mapping from blocks to loops that the block is contained in and for which it is not the loop head - IDictionary> blocksToLoops = new Dictionary>(); - - foreach (var afterBlock in afterDagCfg.GetBlocksBackwards()) - if (afterToBefore.TryGetValue(afterBlock, out var beforeBlock)) - { - var loops = new HashSet(); - foreach (var bSuc in beforeDagCfg.GetSuccessorBlocks(beforeBlock)) - if (blocksToLoops.TryGetValue(bSuc, out var loopsSuc)) - //if successor inside of a loop L and the block is not the loop head of L, then the block is also inside L - foreach (var loopSuc in loopsSuc) - if (!loopSuc.Equals(beforeBlock)) - loops.Add(loopSuc); - /* a node is inside all loops for which it has an out-going backedge - if a node has a backedge to itself (i.e., it is also a loop head), then we do not add this loop - */ - if (hintManager.TryIsBackedgeNode(beforeBlock, out var backedgeLoops)) - foreach (var backedgeLoop in backedgeLoops) - if (beforeBlock != backedgeLoop) - loops.Add(backedgeLoop); - - var loopsList = loops.ToList(); - blocksToLoops.Add(beforeBlock, loopsList); - } - + + var varContextName = "\\1"; var varContextAbbrev = new AbbreviationDecl( varContextName, @@ -200,10 +182,11 @@ public static Theory CfgToDagProof( var theoryOuterDecls = new List(); theoryOuterDecls.Add(cfgToDagLemmasLocale); - if (generateEndToEndLemma) + if (endToEndLemmaConfig != EndToEndLemmaConfig.DoNotGenerate) { var endToEndManager = new CfgToDagEndToEnd(); var endToEndDecls = endToEndManager.EndToEndProof( + endToEndLemmaConfig == EndToEndLemmaConfig.GenerateForProcedure, cfgToDagLemmasLocale.Name + "." + entryLemma.Name, phasesTheories.EndToEndLemmaName(PhasesTheories.Phase.Passification, true), vcAssm, @@ -219,8 +202,9 @@ public static Theory CfgToDagProof( { "Boogie_Lang.Semantics", "Boogie_Lang.Util", "Boogie_Lang.BackedgeElim", "Boogie_Lang.TypingML", beforeDagProgAccess.TheoryName(), - afterDagProgAccess.TheoryName(), phasesTheories.TheoryName(PhasesTheories.Phase.Passification), - phasesTheories.TheoryName(PhasesTheories.Phase.Vc) + afterDagProgAccess.TheoryName(), + generatePassificationProof ? phasesTheories.TheoryName(PhasesTheories.Phase.Passification) : "", + generateVcProof ? phasesTheories.TheoryName(PhasesTheories.Phase.Vc) : "" }, theoryOuterDecls ); diff --git a/Source/ProofGeneration/MembershipLemmaConfig.cs b/Source/ProofGeneration/MembershipLemmaConfig.cs new file mode 100644 index 000000000..b59a878bc --- /dev/null +++ b/Source/ProofGeneration/MembershipLemmaConfig.cs @@ -0,0 +1,28 @@ +namespace ProofGeneration; + +public class MembershipLemmaConfig +{ + + public bool GenerateAxiomMembershipLemmas + { + get; + } + + public bool GenerateFunctionMembershipLemmas + { + get; + } + + public bool GenerateVariableMembershipLemmas + { + get; + } + + public MembershipLemmaConfig(bool generateAxiomMembershipLemmas, bool generateFunctionMembershipLemmas, bool generateVariableMembershipLemmas) + { + GenerateAxiomMembershipLemmas = generateAxiomMembershipLemmas; + GenerateFunctionMembershipLemmas = generateFunctionMembershipLemmas; + GenerateVariableMembershipLemmas = generateVariableMembershipLemmas; + } + +} \ No newline at end of file diff --git a/Source/ProofGeneration/Passification/PassificationEndToEnd.cs b/Source/ProofGeneration/Passification/PassificationEndToEnd.cs index 05e940b63..630d9556c 100644 --- a/Source/ProofGeneration/Passification/PassificationEndToEnd.cs +++ b/Source/ProofGeneration/Passification/PassificationEndToEnd.cs @@ -47,7 +47,7 @@ public class PassificationEndToEnd private IProgramAccessor programAccessor; private IVariableTranslation varTranslation; private Term vcAssm; - + public PassificationEndToEnd() { stateRelList = IsaCommonTerms.TermIdentFromName(stateRelListDefName); diff --git a/Source/ProofGeneration/Passification/PassificationLemmaManager.cs b/Source/ProofGeneration/Passification/PassificationLemmaManager.cs index ee9c0ae80..defcf34d5 100644 --- a/Source/ProofGeneration/Passification/PassificationLemmaManager.cs +++ b/Source/ProofGeneration/Passification/PassificationLemmaManager.cs @@ -29,7 +29,7 @@ internal class PassificationLemmaManager private readonly TermIdent normalInitState = IsaCommonTerms.TermIdentFromName("n_s"); - private readonly OldVarFinder oldVarFinder = new OldVarFinder(); + private readonly OldVarFinder oldVarFinder = new(); private readonly IDictionary origToPassiveBlock; private readonly IProgramAccessor passiveProgramAccessor; diff --git a/Source/ProofGeneration/Passification/PassificationManager.cs b/Source/ProofGeneration/Passification/PassificationManager.cs index 02a8679c7..2c782bd08 100644 --- a/Source/ProofGeneration/Passification/PassificationManager.cs +++ b/Source/ProofGeneration/Passification/PassificationManager.cs @@ -36,10 +36,10 @@ public static Theory PassificationProof( PassiveRelationGen relationGen, IProgramAccessor beforePassiveProgAccess, IProgramAccessor passiveProgAccess, - BoogieMethodData beforePassiveData, IVariableTranslationFactory beforePassiveFactory, IVariableTranslationFactory passiveFactory) { + var varContextName = "\\1"; var passiveVarContextName = "\\2"; var varContextNonPassivePassive = Tuple.Create(varContextName, passiveVarContextName); diff --git a/Source/ProofGeneration/PhasesUtil/EndToEndLemmaConfig.cs b/Source/ProofGeneration/PhasesUtil/EndToEndLemmaConfig.cs new file mode 100644 index 000000000..a2b0aad8d --- /dev/null +++ b/Source/ProofGeneration/PhasesUtil/EndToEndLemmaConfig.cs @@ -0,0 +1,8 @@ +namespace ProofGeneration.PhasesUtil; + +public enum EndToEndLemmaConfig +{ + DoNotGenerate, + GenerateForProcedure, + GenerateForEntryBlock +} \ No newline at end of file diff --git a/Source/ProofGeneration/PhasesTheories.cs b/Source/ProofGeneration/PhasesUtil/PhasesTheories.cs similarity index 61% rename from Source/ProofGeneration/PhasesTheories.cs rename to Source/ProofGeneration/PhasesUtil/PhasesTheories.cs index d35f810bd..af121a4f0 100644 --- a/Source/ProofGeneration/PhasesTheories.cs +++ b/Source/ProofGeneration/PhasesUtil/PhasesTheories.cs @@ -1,4 +1,4 @@ -namespace ProofGeneration +namespace ProofGeneration.PhasesUtil { public class PhasesTheories { @@ -6,11 +6,15 @@ public enum Phase { Vc, Passification, - CfgToDag + CfgToDag, + CfgOptimizations, + AstToCfg } + private readonly string astToCfgTheoryName; private readonly string cfgToDagTheoryName; private readonly string passificationTheoryName; + private readonly string CfgOptimizationsTheoryName; private readonly string vcPhaseTheoryName; @@ -20,6 +24,8 @@ public PhasesTheories(string prefix) vcPhaseTheoryName = prefix + "_vcphase_proof"; passificationTheoryName = prefix + "_passification_proof"; cfgToDagTheoryName = prefix + "_cfgtodag_proof"; + CfgOptimizationsTheoryName = prefix + "_cfgoptimizations_proof"; + astToCfgTheoryName = prefix + "_asttocfg_proof"; } public string TheoryName(Phase phase) @@ -32,6 +38,10 @@ public string TheoryName(Phase phase) return passificationTheoryName; case Phase.CfgToDag: return cfgToDagTheoryName; + case Phase.CfgOptimizations: + return CfgOptimizationsTheoryName; + case Phase.AstToCfg: + return astToCfgTheoryName; default: throw new ProofGenUnexpectedStateException("unknown phase"); } @@ -53,5 +63,21 @@ private string MaybeQualifiedName(string theory, string name, bool qualify) { return qualify ? theory + "." + name : name; } + + public static EndToEndLemmaConfig EndToEndConfig(bool generateEndToEndTheorem, Phase currentPhase, Phase phaseWithProcEndToEnd) + { + if (!generateEndToEndTheorem) + { + return EndToEndLemmaConfig.DoNotGenerate; + } + + if (currentPhase == phaseWithProcEndToEnd) + { + return EndToEndLemmaConfig.GenerateForProcedure; + } + + return EndToEndLemmaConfig.GenerateForEntryBlock; + } + } } \ No newline at end of file diff --git a/Source/ProofGeneration/ProgramToVCProof/EndToEndVCProof.cs b/Source/ProofGeneration/ProgramToVCProof/EndToEndVCProof.cs index a18200f15..023121183 100644 --- a/Source/ProofGeneration/ProgramToVCProof/EndToEndVCProof.cs +++ b/Source/ProofGeneration/ProgramToVCProof/EndToEndVCProof.cs @@ -56,6 +56,8 @@ internal class EndToEndVCProof private readonly IsaUniqueNamer stateCorresNamer = new IsaUniqueNamer(); private readonly TypeIsaVisitor typeIsaVisitor; + private readonly IsaUniqueNamer namerForFunctions; + private readonly IVariableTranslationFactory varTranslationFactory; private readonly string vcAssmName = "VC"; @@ -83,6 +85,7 @@ public EndToEndVCProof( IVariableTranslationFactory varTranslationFactory, IVCVarFunTranslator vcVarFunTranslator, TypePremiseEraserFactory eraserFactory, + IsaUniqueNamer namerForFunctions, VCExpressionGenerator vcExprGen) { this.methodData = methodData; @@ -97,6 +100,7 @@ public EndToEndVCProof( this.varTranslationFactory = varTranslationFactory; this.vcVarFunTranslator = vcVarFunTranslator; this.eraserFactory = eraserFactory; + this.namerForFunctions = namerForFunctions; this.vcExprGen = vcExprGen; basicCmdIsaVisitor = new BasicCmdIsaVisitor(varTranslationFactory); typeIsaVisitor = new TypeIsaVisitor(varTranslationFactory.CreateTranslation().TypeVarTranslation); @@ -326,7 +330,7 @@ string TypeOfArgAssmLabel(int i) //funtion interpretation well-formed assumption assms.Add(IsaBoogieTerm.FunInterpSingleWf(boogieContext.absValTyMap, - IsaBoogieTerm.FunDecl(f, varTranslationFactory, false), funTerm)); + IsaBoogieTerm.FunDecl(f, varTranslationFactory, namerForFunctions, false), funTerm)); assmLabels.Add(finterpAssmName); //type variables closed assumption diff --git a/Source/ProofGeneration/ProgramToVCProof/VcPhaseManager.cs b/Source/ProofGeneration/ProgramToVCProof/VcPhaseManager.cs index 2d006ba33..6ba41bd43 100644 --- a/Source/ProofGeneration/ProgramToVCProof/VcPhaseManager.cs +++ b/Source/ProofGeneration/ProgramToVCProof/VcPhaseManager.cs @@ -54,6 +54,7 @@ public static Theory ProgramToVcProof( IVariableTranslationFactory varFactory, TypePremiseEraserFactory eraserFactory, VCExpressionGenerator gen, + IsaUniqueNamer namerForFunctions, out Term vcAssm, out LemmaDecl endToEndLemma) { @@ -133,6 +134,7 @@ public static Theory ProgramToVcProof( varFactory, vcProofData.VcTranslator, eraserFactory, + namerForFunctions, gen); passiveOuterDecls.AddRange(endToEnd.GenerateProof(out vcAssm, out endToEndLemma)); } @@ -147,7 +149,8 @@ public static Theory ProgramToVcProof( new List { "Boogie_Lang.Semantics", "Boogie_Lang.Util", "Boogie_Lang.VCHints", "Boogie_Lang.VCPhaseML", - passiveProgAccess.TheoryName(), beforePassiveProgAccess.TheoryName() + passiveProgAccess.TheoryName(), + beforePassiveProgAccess != null ? beforePassiveProgAccess.TheoryName() : "" }, passiveOuterDecls); } diff --git a/Source/ProofGeneration/ProofGenConfig.cs b/Source/ProofGeneration/ProofGenConfig.cs index af7ae0003..c6673f7c0 100644 --- a/Source/ProofGeneration/ProofGenConfig.cs +++ b/Source/ProofGeneration/ProofGenConfig.cs @@ -1,23 +1,134 @@ -namespace ProofGeneration +using System; +using System.Collections.Generic; +using ProofGeneration.PhasesUtil; + +namespace ProofGeneration { /// /// Used to indicate which proofs should be generated. /// public class ProofGenConfig { - public ProofGenConfig( - bool generateCfgDagE2E, - bool generatePassifE2E, - bool generateVcE2E - ) + + private IDictionary generatePhaseProof; + + private List phases = new List + { + PhasesTheories.Phase.AstToCfg, + PhasesTheories.Phase.CfgOptimizations, + PhasesTheories.Phase.CfgToDag, + PhasesTheories.Phase.Passification, + PhasesTheories.Phase.Vc + }; + + private void UpdatePhaseDict(IDictionary dict, PhasesTheories.Phase key, V value) + { + if (!phases.Contains(key)) { - GenerateCfgDagE2E = generateCfgDagE2E; - GeneratePassifE2E = generatePassifE2E; - GenerateVcE2E = generateVcE2E; + throw new ArgumentException("Unexpected phase " + key); } - public bool GenerateCfgDagE2E { get; } - public bool GeneratePassifE2E { get; } - public bool GenerateVcE2E { get; } + dict[key] = value; + } + + private void UpdatePhaseProofFlag(PhasesTheories.Phase phase, bool flag) + { + UpdatePhaseDict(generatePhaseProof, phase, flag); + } + + private bool GetPhaseProofFlag(PhasesTheories.Phase phase) + { + return generatePhaseProof[phase]; + } + + private ProofGenConfig AllOptions(bool enabled) + { + generatePhaseProof = new Dictionary(); + + foreach (var phase in phases) + { + generatePhaseProof.Add(phase, enabled); + } + + return this; + } + + public ProofGenConfig AllOptionsEnabled() + { + return AllOptions(true); + } + + public ProofGenConfig AllOptionsDisabled() + { + return AllOptions(false); + } + + public bool GenerateAstCfgE2E(bool deadVarsElim) => + GenerateAstCfgProof && (!deadVarsElim && GenerateCfgDagE2E); //currently do not produce E2E if dead variables are eliminated + + public bool GenerateCfgDagE2E => GenerateCfgDagProof && GeneratePassifE2E; + + public bool GeneratePassifE2E => GeneratePassifProof && GenerateVcE2E; + + public bool GenerateVcE2E => GenerateVcProof; + + public bool GenerateAstCfgProof => GetPhaseProofFlag(PhasesTheories.Phase.AstToCfg); + public ProofGenConfig SetAstCfgProof(bool flag) + { + UpdatePhaseProofFlag(PhasesTheories.Phase.AstToCfg, flag); + return this; + } + + public bool GenerateCfgOptProof(bool optimizationsHaveAnEffect) => + optimizationsHaveAnEffect && GetPhaseProofFlag(PhasesTheories.Phase.CfgOptimizations); + + public ProofGenConfig SetCfgOptProof(bool flag) + { + UpdatePhaseProofFlag(PhasesTheories.Phase.CfgOptimizations, flag); + return this; + } + + public bool GenerateCfgDagProof => GetPhaseProofFlag(PhasesTheories.Phase.CfgToDag); + public ProofGenConfig SetCfgDagProof(bool flag) + { + UpdatePhaseProofFlag(PhasesTheories.Phase.CfgToDag, flag); + return this; + } + + public bool GeneratePassifProof => GetPhaseProofFlag(PhasesTheories.Phase.Passification); + public ProofGenConfig SetPassifProof(bool flag) + { + UpdatePhaseProofFlag(PhasesTheories.Phase.Passification, flag); + return this; + } + + public bool GenerateVcProof => GetPhaseProofFlag(PhasesTheories.Phase.Vc); + + public ProofGenConfig SetVcProof(bool flag) + { + UpdatePhaseProofFlag(PhasesTheories.Phase.Vc, flag); + return this; + } + + /** Program Generation Getters */ + + public bool GenerateBeforeAstCfgProg => GenerateAstCfgProof; + + public bool GenerateUnoptimizedCfgProg(bool optimizationsHaveAnEffect) + { + return optimizationsHaveAnEffect && GenerateAstCfgProof || + GenerateCfgOptProof(optimizationsHaveAnEffect); + } + + public bool GenerateBeforeCfgDagProg(bool optimizationsHaveAnEffect) + { + return (!optimizationsHaveAnEffect && GenerateAstCfgProof) || //directly connect AST to CFG before CfgToDag if there are no optimizations + GenerateCfgOptProof(optimizationsHaveAnEffect) || + GenerateCfgDagProof; + } + + public bool GenerateBeforePassiveProg => GenerateCfgDagProof || GeneratePassifProof; + public bool GeneratePassifiedProg => GeneratePassifProof || GenerateVcProof; + } } \ No newline at end of file diff --git a/Source/ProofGeneration/ProofGenerationLayer.cs b/Source/ProofGeneration/ProofGenerationLayer.cs index 8af9a9883..166f649a1 100644 --- a/Source/ProofGeneration/ProofGenerationLayer.cs +++ b/Source/ProofGeneration/ProofGenerationLayer.cs @@ -8,11 +8,15 @@ using Microsoft.Boogie.ProofGen; using Microsoft.Boogie.TypeErasure; using Microsoft.Boogie.VCExprAST; +using ProofGeneration.ASTRepresentation; +using ProofGeneration.AstToCfg; using ProofGeneration.BoogieIsaInterface; using ProofGeneration.BoogieIsaInterface.VariableTranslation; +using ProofGeneration.CFGOptimizations; using ProofGeneration.CFGRepresentation; using ProofGeneration.CfgToDag; using ProofGeneration.Passification; +using ProofGeneration.PhasesUtil; using ProofGeneration.ProgramToVCProof; using ProofGeneration.Util; using ProofGeneration.VCProofGen; @@ -22,9 +26,20 @@ namespace ProofGeneration public class ProofGenerationLayer { private static Implementation afterPassificationImpl; + private static Implementation beforeCFGtoDagImpl; + //private static Implementation beforeDagImpl; + private static AstToCfgProofGenInfo proofGenInfo; + + private static ASTRepr beforeCfgAst; + private static IDictionary beforeCfgAfterCfgBlock; + + private static IDictionary beforeOptimizationsOrigBlock; private static IDictionary beforeDagOrigBlock; private static IDictionary beforeDagAfterDagBlock; + + private static CFGRepr beforeOptimizationsCFG; + private static CFGRepr beforeDagCfg; private static BoogieMethodData beforeDagData; @@ -60,38 +75,65 @@ public class ProofGenerationLayer private static CfgToDagHintManager cfgToDagHintManager; + //private static Block uniqueExitBlockOrigBeforeOptimizations; private static Block uniqueExitBlockOrig; - private static readonly ProofGenConfig _proofGenConfig = - new ProofGenConfig(true, true, true); + //new ProofGenConfig().AllOptionsDisabled().SetCfgOptProof(true); + // + //private static ProofGenConfig _proofGenConfig = new ProofGenConfig().AllOptionsDisabled().SetCfgOptProof(true); private static IProgramAccessor globalDataProgAccess; + private static IsaUniqueNamer uniqueNamer; + private static IDictionary procNameToTopLevelPrograms = new Dictionary(); + + private static MembershipLemmaConfig MembershipLemmaConfig() + { + switch (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + { + case 0: return new MembershipLemmaConfig(true, true, true); + // axiom membership lemmas are required only if proofs are generated for the Boogie program + case 1: return new MembershipLemmaConfig(false, true, true); + default: return new MembershipLemmaConfig(false, false, false); + } + } /// /// Provide program for the next procedure (for the global declarations). /// public static void Program(Program p) { + if (uniqueNamer == null) + { + uniqueNamer = new IsaUniqueNamer(); + } + if (boogieGlobalData == null) { boogieGlobalData = new BoogieGlobalData(p.Functions, p.Axioms, p.GlobalVariables, p.Constants); - + var methodData = BoogieMethodData.CreateOnlyGlobal(boogieGlobalData); var fixedVarTranslation = new DeBruijnFixedVarTranslation(methodData); var fixedTyVarTranslation = new DeBruijnFixedTVarTranslation(methodData); var factory = new DeBruijnVarFactory(fixedVarTranslation, fixedTyVarTranslation, boogieGlobalData); var globalDataTheoryName = "global_data"; - var globalDataConfig = new IsaProgramGeneratorConfig(null, true, true, true, false, SpecsConfig.None, false); + var globalDataConfig = new IsaProgramGeneratorConfig(null, + true, + true, + true, + false, + SpecsConfig.None, + false); globalDataProgAccess = new IsaProgramGenerator().GetIsaProgram( globalDataTheoryName, "proc", methodData, globalDataConfig, factory, null, + uniqueNamer, out var declsGlobalData, - !CommandLineOptions.Clo.GenerateIsaProgNoProofs, + MembershipLemmaConfig(), true ); @@ -102,17 +144,69 @@ public static void Program(Program p) } } + private static bool AstContainsGotoOrBreakOrElseIf(AstToCfgProofGenInfo proofGenInfo) + { + IList ast = proofGenInfo.GetBigBlocks(); + foreach (var b in ast) + { + if (b.ec is BreakCmd || b.tc is GotoCmd || (b.ec is IfCmd bIf && bIf.elseIf != null)) + { + return true; + } + } + + return false; + } + + private static bool checkForGotos(StmtList stmtList) + { + foreach (var bb in stmtList.BigBlocks) + { + if (bb.tc is GotoCmd) + { + return true; + } + + if (bb.ec is IfCmd ifcmd) + { + checkForGotos(ifcmd.thn); + checkForGotos(ifcmd.elseBlock); + } + else if (bb.ec is WhileCmd whilecmd) + { + checkForGotos(whilecmd.Body); + } + } + + return false; + } + /// /// Provide source CFG for CFG-to-DAG phase /// public static void BeforeCFGToDAG(Implementation impl) { + beforeCFGtoDagImpl = impl; + var config = new CFGReprConfigBuilder().SetIsAcyclic(false).SetBlockCopy(true).SetDesugarCalls(true) .Build(); beforeDagCfg = CFGReprTransformer.GetCfgRepresentation(impl, config, out beforeDagOrigBlock, out var newVarsFromDesugaring); + + foreach (var block in impl.Blocks) + { + if (beforeDagOrigBlock.Values.Contains(block)) + { + continue; + } + else + { + throw new Exception(); + } + } + beforeDagData = MethodDataFromImpl(impl, boogieGlobalData, newVarsFromDesugaring); uniqueExitBlockOrig = null; } @@ -140,7 +234,7 @@ public static void CreateUnifiedExitBlock(Block generatedExitBlock) /// public static void BeforePassification(Implementation impl) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; var config = new CFGReprConfigBuilder().SetIsAcyclic(true).SetBlockCopy(true).SetDesugarCalls(true) @@ -158,6 +252,7 @@ out var newVarsFromDesugaring // compute mapping between copied blocks (before dag -> after dag) var origToAfterDag = beforePassiveOrigBlock.InverseDict(); + beforeDagAfterDagBlock = DictionaryComposition(beforeDagOrigBlock, origToAfterDag); } @@ -165,11 +260,11 @@ out var newVarsFromDesugaring private static BoogieMethodData MethodDataFromImpl( Implementation impl, BoogieGlobalData globalData, - List extraLocalVariables = null + List extraLocalVariables = null, + List overrideLocals = null ) { - //add out params to local variables for now - var locals = new List(impl.LocVars).Union(impl.OutParams); + IEnumerable locals = (overrideLocals ?? impl.LocVars); if (extraLocalVariables != null) locals = locals.Union(extraLocalVariables); @@ -196,9 +291,10 @@ private static BoogieMethodData MethodDataFromImpl( return new BoogieMethodData( globalData, new List(impl.TypeParameters), - new List(impl.InParams), + new List(impl.InParams).Union(impl.OutParams), //add out parameters to in parameters for now locals, - null, + new List(), + //new List(impl.OutParams), new List(impl.Proc.Modifies), preconditions, postconditions); @@ -212,7 +308,7 @@ public static void RecordInitialVariableMapping(Block b, IDictionary(variableToExpr)); @@ -242,8 +338,7 @@ private static bool TypeCheckBeforeVcMaybeRewritesCmd(Cmd cmd) public static void AfterPassificationCheckGlobalMap(Implementation impl) { afterPassificationImpl = impl; - - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; finalProgData = MethodDataFromImpl(impl, boogieGlobalData); @@ -310,7 +405,7 @@ private static IDictionary DictionaryComposition(IDictionary public static void AfterUnreachablePruning(Implementation impl) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; var config = new CFGReprConfigBuilder().SetIsAcyclic(true).SetBlockCopy(true).SetDesugarCalls(false) @@ -339,7 +434,7 @@ public static void NextVcHintForBlock( CommandLineOptions.SubsumptionOption subsumptionOption ) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; vcHintManager.NextHintForBlock(cmd, block, exprVC, postVC, resultVC, subsumptionOption); } @@ -350,6 +445,8 @@ CommandLineOptions.SubsumptionOption subsumptionOption /// public static void VcIsTrivial() { + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) + return; vcHintManager.TransformHintsToTrivialVc(); } @@ -360,7 +457,7 @@ public static void VcIsTrivial() /// public static void NextPassificationHint(Block block, Cmd cmd, Variable origVar, Expr passiveExpr) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; passificationHintManager.AddHint(block, cmd, origVar, passiveExpr); } @@ -371,7 +468,7 @@ public static void NextPassificationHint(Block block, Cmd cmd, Variable origVar, /// public static void LoopHeadHint(Block block, IEnumerable varsToHavoc, IEnumerable invariants) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; cfgToDagHintManager.AddHint(block, new LoopHeadHint(varsToHavoc, invariants)); } @@ -382,14 +479,25 @@ public static void LoopHeadHint(Block block, IEnumerable varsToHavoc, /// public static void NewBackedgeBlock(Block oldBackedgeBlock, Block newBackedgeBlock, Block loopHead) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; cfgToDagHintManager.AddNewBackedgeBlock(newBackedgeBlock, loopHead); } + + /// + /// Provide hint that the pre loop entry block is replaced by a new + /// pre entry loop block for a loop with loop head . + /// + public static void NewPreLoopEntryBlock(Block oldPreLoopEntryBlock, Block newPreLoopEntryBlock, Block loopHead) + { + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) + return; + cfgToDagHintManager.AddNewPreLoopEntryBlock(newPreLoopEntryBlock, loopHead); + } public static void SetTypeEraserFactory(TypePremiseEraserFactory factory) { - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) return; typePremiseEraserFactory = factory; var uniqueNamer = new IsaUniqueNamer(); @@ -408,6 +516,55 @@ public static void SetTypeEraserFactory(TypePremiseEraserFactory factory) vcHintManager = new VCHintManager(new VcRewriteLemmaGen(factory, translator)); } + private static void DesugarCmdsInBigBlock(BigBlock b) + { + List copyCmds = new List(); + List newVarsFromDesugaring = new List(); + foreach (Cmd cmd in b.simpleCmds) + { + + if (cmd is SugaredCmd sugaredCmd) + { + var stateCmd = sugaredCmd.Desugaring as StateCmd; + if (stateCmd != null) + { + newVarsFromDesugaring.AddRange(stateCmd.Locals); + foreach (var desugaredCmd in stateCmd.Cmds) + { + copyCmds.Add(desugaredCmd); + } + } + } + else + { + copyCmds.Add(cmd); + } + } + + proofGenInfo.GetNewVarsFromDesugaring().AddRange(newVarsFromDesugaring); + b.simpleCmds = copyCmds; + + if (b.ec is IfCmd ifCmd) + { + foreach (var then_bb in ifCmd.thn.BigBlocks) + { + DesugarCmdsInBigBlock(then_bb); + } + + foreach (var else_bb in ifCmd.elseBlock.BigBlocks) + { + DesugarCmdsInBigBlock(else_bb); + } + } + else if (b.ec is WhileCmd wcmd) + { + foreach (var body_bb in wcmd.Body.BigBlocks) + { + DesugarCmdsInBigBlock(body_bb); + } + } + } + /// /// Generate all proofs for the current procedure. /// @@ -431,61 +588,364 @@ public static void VCGenerateAllProofs( Boogie2VCExprTranslator translator, TypeAxiomBuilderPremisses axiomBuilder) { - var uniqueNamer = new IsaUniqueNamer(); + var map = AstToCfgProofGenInfoManager.GetImplToProofGenInfo(); + proofGenInfo = map[afterPassificationImpl]; + ProofGenConfig _proofGenConfig = new ProofGenConfig().AllOptionsEnabled(); + + if (AstContainsGotoOrBreakOrElseIf(proofGenInfo)) + { + _proofGenConfig.SetAstCfgProof(false); + } + + IList bigBlocks = proofGenInfo.GetBigBlocks(); + foreach (BigBlock b in bigBlocks) + { + DesugarCmdsInBigBlock(b); + } + + IList unoptimizedCfgBlocks = proofGenInfo.GetUnoptimizedBlocks(); + IDictionary mappingOrigBlockToUnoptimizedCopy = + proofGenInfo.GetMappingOrigBlockToUnoptimizedCopy(); + IDictionary mappingUnoptimizedCopyToOrigBlock = + mappingOrigBlockToUnoptimizedCopy.InverseDict(); + beforeOptimizationsOrigBlock = mappingUnoptimizedCopyToOrigBlock; + + BoogieMethodData beforeOptimizationsData; + + if (CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) + { + beforeOptimizationsData = MethodDataFromImpl( + afterPassificationImpl, + boogieGlobalData, + /* Since in the proofs calls are desugared, there can be more variables after desugarging. If only + the program should be generated, then these variables should be ignored. */ + null, + proofGenInfo.UnoptimizedLocalVars + ); + } else if(proofGenInfo.EliminatedDeadVars) { + beforeOptimizationsData = MethodDataFromImpl( + afterPassificationImpl, + boogieGlobalData, + proofGenInfo.GetNewVarsCFG(), + proofGenInfo.UnoptimizedLocalVars + ); + } else { + /* If no dead variables were eliminated then we pick the same data as for the rest of the CFGs before the passification. + Note that picking `beforeDagData` would not work as intended because it does not contain the desugared variables + from calls. + */ + beforeOptimizationsData = beforePassiveData; + } + + var afterOptimizationsData = beforePassiveData; + + beforeOptimizationsCFG = GetCfgBeforeOptimizations(unoptimizedCfgBlocks); + + if (uniqueNamer == null) + { + uniqueNamer = new IsaUniqueNamer(); + } + var theories = new List(); if (axiomBuilder == null && typeAxioms != null) throw new ArgumentException("type axioms can only be null if axiom builder is null"); + + var beforeOptimizationsVarTranslationFactory = + new DeBruijnVarFactory( + new DeBruijnFixedVarTranslation(beforeOptimizationsData), + new DeBruijnFixedTVarTranslation(beforeOptimizationsData), + boogieGlobalData); + + IProgramAccessor beforeAstToCfgProgAccess = null; + IProgramAccessor unoptimizedCfgProgAccess = null; //unoptimized CFG with original variable declarations + IProgramAccessor unoptimizedCfgOptVarProgAccess = null; //unoptimized CFG where dead variables are eliminated + IProgramAccessor beforeCfgToDagProgAccess = null; + IProgramAccessor beforePassiveProgAccess = null; + IProgramAccessor passiveProgAccess = null; + + //Hack: specs config used to distinguish between all (free + checks) (--> expression tuples) or just checked (no tuples) + var specsConfigDefault = + CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa() + ? SpecsConfig.All + : SpecsConfig.AllPreCheckedPost; + + var membershipLemmaConfig = MembershipLemmaConfig(); - /* Since in the proofs calls are desugared, there can be more variables in "beforePassiveData". If only - the progam should be generaed, then these variables should be ignored. */ - var mainData = CommandLineOptions.Clo.GenerateIsaProgNoProofs ? beforeDagData : beforePassiveData; + if (_proofGenConfig.GenerateBeforeAstCfgProg || CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) + { + #region before ast to cfg program + + beforeCfgAst = new ASTRepr(proofGenInfo.GetBigBlocks()); + ASTRepr originalAst = new ASTRepr(proofGenInfo.GetOriginalAst()); + + var beforeAstToCfgTheoryName = + uniqueNamer.GetName(afterPassificationImpl.Name + "_before_ast_to_cfg_prog"); + var beforeAstToCfgConfig = new IsaProgramGeneratorConfig(globalDataProgAccess, + false, + false, + false, + true, + specsConfigDefault, + true); + + beforeAstToCfgProgAccess = new IsaProgramGeneratorForAst().GetIsaProgram( + beforeAstToCfgTheoryName, + afterPassificationImpl.Name, + beforeOptimizationsData, beforeAstToCfgConfig, beforeOptimizationsVarTranslationFactory, + beforeCfgAst, + originalAst, + proofGenInfo, + uniqueNamer, + out var programDeclsBeforeAstToCfg, + membershipLemmaConfig); + procNameToTopLevelPrograms.Add(afterPassificationImpl.Proc.Name + "ast", beforeAstToCfgProgAccess); + + var beforeAstToCfgProgTheory = new Theory(beforeAstToCfgTheoryName, + new List + { + "Boogie_Lang.Ast", "Boogie_Lang.Semantics", "Boogie_Lang.TypeSafety", "Boogie_Lang.Util", + "\"../" + globalDataProgAccess.TheoryName() + "\"" + }, + programDeclsBeforeAstToCfg); + theories.Add(beforeAstToCfgProgTheory); + + if(CommandLineOptions.Clo.OnlyGenerateInitialProgramIsa()) + { + StoreResult(uniqueNamer.GetName(afterPassificationImpl.Proc.Name), theories); + return; + } + + #endregion + } - var fixedVarTranslation2 = new DeBruijnFixedVarTranslation(mainData); - var fixedTyVarTranslation2 = new DeBruijnFixedTVarTranslation(mainData); - var varTranslationFactory2 = - new DeBruijnVarFactory(fixedVarTranslation2, fixedTyVarTranslation2, boogieGlobalData); + var afterOptimizationsVarTranslationFactory = + new DeBruijnVarFactory( + new DeBruijnFixedVarTranslation(afterOptimizationsData), + new DeBruijnFixedTVarTranslation(afterOptimizationsData), + boogieGlobalData); - #region before cfg to dag program - var beforeCfgToDagTheoryName = uniqueNamer.GetName(afterPassificationImpl.Name + "_before_cfg_to_dag_prog"); - //Hack: specs config used to distinguish between all (free + checks) (--> expression tuples) or just checked (no tuples) - var specsConfig = CommandLineOptions.Clo.GenerateIsaProgNoProofs ? SpecsConfig.All : SpecsConfig.AllPreCheckedPost; - var beforeCfgToDagConfig = new IsaProgramGeneratorConfig(globalDataProgAccess, false, false, false, true, specsConfig, true); - var beforeCfgToDagProgAccess = new IsaProgramGenerator().GetIsaProgram( + var cmdIsaVisitor = new MultiCmdIsaVisitor(afterOptimizationsVarTranslationFactory); + + if (_proofGenConfig.GenerateUnoptimizedCfgProg(proofGenInfo.GetOptimizationsFlag())) + { + #region unoptimized cfg program + + var parentAccessorForUnoptimizedCfg = beforeAstToCfgProgAccess; + + Func getUnoptimizedCfgConfig = generateLocalsAndParams => + new IsaProgramGeneratorConfig( + parentAccessorForUnoptimizedCfg ?? globalDataProgAccess, + false, + false, + false, + generateLocalsAndParams, + /* if generate locals and params, then encoding of output variables may change, thus should generate + pre- and postconditions in such a case*/ + parentAccessorForUnoptimizedCfg != null && !generateLocalsAndParams ? SpecsConfig.None : specsConfigDefault, + !_proofGenConfig.GenerateBeforeAstCfgProg); + + Func>> getUnoptimizedCfgAccess = + (theoryName, config, methodData, factory) => + { var accessor = + new IsaProgramGenerator().GetIsaProgram( + theoryName, + afterPassificationImpl.Name, + /* pick data after optimizations (allows connecting to CFG-to-DAG proof directly) --> + dead variable elimination step must be handled separately + */ + methodData, config, factory, + beforeOptimizationsCFG, + uniqueNamer, + out var programDeclsUnoptimizedCfg, + membershipLemmaConfig); + return Tuple.Create(accessor, programDeclsUnoptimizedCfg); + }; + + { + // unoptimized CFG with original variables + + var unoptimizedCfgTheoryName = + uniqueNamer.GetName(afterPassificationImpl.Name + "_unoptimized_cfg_prog"); + + var config = getUnoptimizedCfgConfig(parentAccessorForUnoptimizedCfg == null); + (unoptimizedCfgProgAccess, var programDeclsUnoptimizedCfg) = + getUnoptimizedCfgAccess( + unoptimizedCfgTheoryName, + config, + beforeOptimizationsData, + beforeOptimizationsVarTranslationFactory + ); + + procNameToTopLevelPrograms.Add(afterPassificationImpl.Proc.Name + "unoptimized_orig_var", + unoptimizedCfgProgAccess); + + var unoptimizedCfgProgTheory = new Theory(unoptimizedCfgTheoryName, + new List + { + "Boogie_Lang.Semantics", "Boogie_Lang.TypeSafety", "Boogie_Lang.Util", + _proofGenConfig.GenerateBeforeAstCfgProg ? beforeAstToCfgProgAccess.TheoryName() : "\"../"+globalDataProgAccess.TheoryName()+"\"" + }, + programDeclsUnoptimizedCfg); + theories.Add(unoptimizedCfgProgTheory); + } + + if (proofGenInfo.EliminatedDeadVars) + { + // unoptimized CFG without dead variables --> different program representation due to DeBruijn indexing + + var unoptimizedCfgOptVarTheoryName = + uniqueNamer.GetName(afterPassificationImpl.Name + "_unoptimized_cfg_opt_var_prog"); + + (unoptimizedCfgOptVarProgAccess, var programDeclsUnoptimizedOptVarCfg) = + getUnoptimizedCfgAccess( + unoptimizedCfgOptVarTheoryName, + //since dead variables were eliminated, need to generate the local variable declarations without the dead variables + getUnoptimizedCfgConfig(true), + afterOptimizationsData, + afterOptimizationsVarTranslationFactory + ); + + procNameToTopLevelPrograms.Add(afterPassificationImpl.Proc.Name + "unoptimized_opt_var", unoptimizedCfgOptVarProgAccess); + + var unoptimizedCfgProgTheory = new Theory(unoptimizedCfgOptVarTheoryName, + new List + { + "Boogie_Lang.Semantics", "Boogie_Lang.TypeSafety", "Boogie_Lang.Util", + _proofGenConfig.GenerateBeforeAstCfgProg ? beforeAstToCfgProgAccess.TheoryName() : "\"../"+globalDataProgAccess.TheoryName()+"\"" + }, + programDeclsUnoptimizedOptVarCfg); + theories.Add(unoptimizedCfgProgTheory); + } + else + { + // no need for two different representations of unoptimized CFG + unoptimizedCfgOptVarProgAccess = unoptimizedCfgProgAccess; + } + + #endregion + } + + if (_proofGenConfig.GenerateBeforeCfgDagProg(proofGenInfo.GetOptimizationsFlag())) + { + #region before cfg to dag program + var beforeCfgToDagTheoryName = uniqueNamer.GetName(afterPassificationImpl.Name + "_before_cfg_to_dag_prog"); + + var parentAccessorForBeforeCfgToDag = unoptimizedCfgOptVarProgAccess ?? beforeAstToCfgProgAccess; + var beforeCfgToDagConfig = new IsaProgramGeneratorConfig( + parentAccessorForBeforeCfgToDag ?? globalDataProgAccess, + false, + false, + false, + parentAccessorForBeforeCfgToDag == null, + parentAccessorForBeforeCfgToDag == null || (proofGenInfo.EliminatedDeadVars && parentAccessorForBeforeCfgToDag != unoptimizedCfgOptVarProgAccess) ? + /* make sure to generate separate pre- and post if no parent accessor or dead variables were eliminated + and the parent accessor has generated the pre- and post without dead variables (which impacts output variable representation) + */ + specsConfigDefault : + SpecsConfig.None, + parentAccessorForBeforeCfgToDag == null || proofGenInfo.GetOptimizationsFlag()); + + beforeCfgToDagProgAccess = new IsaProgramGenerator().GetIsaProgram( beforeCfgToDagTheoryName, afterPassificationImpl.Name, - mainData, beforeCfgToDagConfig, varTranslationFactory2, + afterOptimizationsData, beforeCfgToDagConfig, afterOptimizationsVarTranslationFactory, beforeDagCfg, + uniqueNamer, out var programDeclsBeforeCfgToDag, - !CommandLineOptions.Clo.GenerateIsaProgNoProofs); - procNameToTopLevelPrograms.Add(afterPassificationImpl.Proc.Name, beforeCfgToDagProgAccess); + membershipLemmaConfig); + procNameToTopLevelPrograms.Add(afterPassificationImpl.Proc.Name, beforeCfgToDagProgAccess); - var beforeCfgToDagProgTheory = new Theory(beforeCfgToDagTheoryName, - new List {"Boogie_Lang.Semantics", "Boogie_Lang.TypeSafety", "Boogie_Lang.Util", "\"../"+globalDataProgAccess.TheoryName()+"\""}, + var beforeCfgToDagProgTheory = new Theory(beforeCfgToDagTheoryName, + new List {"Boogie_Lang.Semantics", "Boogie_Lang.TypeSafety", "Boogie_Lang.Util", + parentAccessorForBeforeCfgToDag != null ? parentAccessorForBeforeCfgToDag.TheoryName() : "\"../"+globalDataProgAccess.TheoryName()+"\""}, programDeclsBeforeCfgToDag); - theories.Add(beforeCfgToDagProgTheory); - #endregion + theories.Add(beforeCfgToDagProgTheory); + #endregion + } - if (CommandLineOptions.Clo.GenerateIsaProgNoProofs) + if (_proofGenConfig.GenerateBeforePassiveProg) { - StoreResult("program_" + afterPassificationImpl.Proc.Name, theories); - return; + #region before passive program + + IProgramAccessor parentProgramAccessorForPassification = beforeCfgToDagProgAccess; + + var beforePassiveProgTheoryName = + uniqueNamer.GetName(afterPassificationImpl.Name + "_before_passive_prog"); + var beforePassiveConfig = + new IsaProgramGeneratorConfig( + parentProgramAccessorForPassification ?? globalDataProgAccess, + false, + false, + false, + parentProgramAccessorForPassification == null, + parentProgramAccessorForPassification != null ? SpecsConfig.None : specsConfigDefault, + parentProgramAccessorForPassification == null); + beforePassiveProgAccess = new IsaProgramGenerator().GetIsaProgram(beforePassiveProgTheoryName, + afterPassificationImpl.Name, + afterOptimizationsData, beforePassiveConfig, afterOptimizationsVarTranslationFactory, + beforePassificationCfg, + uniqueNamer, + out var programDeclsBeforePassive, + membershipLemmaConfig); + + var beforePassificationProgTheory = new Theory(beforePassiveProgTheoryName, + new List {"Boogie_Lang.Semantics", "Boogie_Lang.Util", + parentProgramAccessorForPassification != null ? parentProgramAccessorForPassification.TheoryName() : globalDataProgAccess.TheoryName()}, + programDeclsBeforePassive); + theories.Add(beforePassificationProgTheory); + + #endregion } + + if (_proofGenConfig.GeneratePassifiedProg) + { + #region after passification program - #region before passive program + //use global version map for translation + var fixedVarTranslation = new SimpleFixedVarTranslation(globalVersionMap); + var fixedTyVarTranslation = new DeBruijnFixedTVarTranslation(finalProgData); + varTranslationFactory = + new DeBruijnVarFactory(fixedVarTranslation, fixedTyVarTranslation, boogieGlobalData); - var beforePassiveProgTheoryName = uniqueNamer.GetName(afterPassificationImpl.Name + "_before_passive_prog"); - var beforePassiveConfig = - new IsaProgramGeneratorConfig(beforeCfgToDagProgAccess, false, false, false, false, SpecsConfig.None, false); - var beforePassiveProgAccess = new IsaProgramGenerator().GetIsaProgram(beforePassiveProgTheoryName, + var finalProgTheoryName = uniqueNamer.GetName(afterPassificationImpl.Name + "_passive_prog"); + + IProgramAccessor parentProgramAccessorFoPassiveProg = beforePassiveProgAccess; + + var passiveProgConfig = + new IsaProgramGeneratorConfig( + parentProgramAccessorFoPassiveProg ?? globalDataProgAccess, + false, + false, + false, + true, + parentProgramAccessorFoPassiveProg != null ? SpecsConfig.None : specsConfigDefault, + false); + passiveProgAccess = new IsaProgramGenerator().GetIsaProgram(finalProgTheoryName, afterPassificationImpl.Name, - mainData, beforePassiveConfig, varTranslationFactory2, - beforePassificationCfg, - out var programDeclsBeforePassive, - !CommandLineOptions.Clo.GenerateIsaProgNoProofs); + finalProgData, passiveProgConfig, varTranslationFactory, + //we use the CFG before the peep-hole transformations, so that we can directly use the VC to program proof in the passification phase + afterPassificationCfg, + uniqueNamer, + out var programDecls, + membershipLemmaConfig); - #endregion + var afterPassificationProgTheory = + new Theory(finalProgTheoryName, + new List + {"Boogie_Lang.Semantics", "Boogie_Lang.Util", parentProgramAccessorFoPassiveProg != null ? parentProgramAccessorFoPassiveProg.TheoryName() : ""}, + programDecls); + theories.Add(afterPassificationProgTheory); - var vcAllAxioms = AxiomHandler.AxiomInfo( + #endregion + } + + var phasesTheories = new PhasesTheories(uniqueNamer.GetName(afterPassificationImpl.Name)); + Term vcAssm = null; + LemmaDecl endToEndLemma = null; + + if (_proofGenConfig.GenerateVcProof) + { + var vcAllAxioms = AxiomHandler.AxiomInfo( axiomBuilder != null, boogieGlobalData.Axioms, vcAxioms, @@ -493,7 +953,7 @@ public static void VCGenerateAllProofs( typeAxiomInfo, out var allAxiomsInfo); - var vcLocale = VCToIsaInterface.ConvertVC( + var vcLocale = VCToIsaInterface.ConvertVC( "vc", vc, vcAllAxioms, @@ -507,43 +967,19 @@ public static void VCGenerateAllProofs( out var vcTranslator, out var vcFunctions); - //use global version map for translation - var fixedVarTranslation = new SimpleFixedVarTranslation(globalVersionMap); - var fixedTyVarTranslation = new DeBruijnFixedTVarTranslation(finalProgData); - varTranslationFactory = - new DeBruijnVarFactory(fixedVarTranslation, fixedTyVarTranslation, boogieGlobalData); - - var finalProgTheoryName = uniqueNamer.GetName(afterPassificationImpl.Name + "_passive_prog"); - var passiveProgConfig = - new IsaProgramGeneratorConfig(beforePassiveProgAccess, false, false, false, true, SpecsConfig.None, false); - var passiveProgAccess = new IsaProgramGenerator().GetIsaProgram(finalProgTheoryName, - afterPassificationImpl.Name, - finalProgData, passiveProgConfig, varTranslationFactory, - //we use the CFG before the peep-hole transformations, so that we can directly use the VC to program proof in the passification phase - afterPassificationCfg, - out var programDecls, - !CommandLineOptions.Clo.GenerateIsaProgNoProofs); + var vcBoogieInfo = new VcBoogieInfo(vcinst, vcinstAxiom, vcAllAxioms, allAxiomsInfo); - var finalProgTheory = - new Theory(finalProgTheoryName, - new List - {"Boogie_Lang.Semantics", "Boogie_Lang.Util", beforePassiveProgAccess.TheoryName()}, - programDecls); - theories.Add(finalProgTheory); - - var vcBoogieInfo = new VcBoogieInfo(vcinst, vcinstAxiom, vcAllAxioms, allAxiomsInfo); - - var vcProofData = new ProgramVcProofData( + var vcProofData = new ProgramVcProofData( vcFunctions, vcBoogieInfo, vcHintManager, vcLocale, vcTranslator - ); + ); - var phasesTheories = new PhasesTheories(afterPassificationImpl.Name); + #region VC phase proof - var theoryPassive = VcPhaseManager.ProgramToVcProof( + var vcPhaseProofTheory = VcPhaseManager.ProgramToVcProof( phasesTheories.TheoryName(PhasesTheories.Phase.Vc), _proofGenConfig.GenerateVcE2E, afterUnreachablePruningCfg, @@ -557,26 +993,26 @@ public static void VCGenerateAllProofs( varTranslationFactory, typePremiseEraserFactory, gen, - out var vcAssm, - out var endToEndLemma - ); - theories.Add(theoryPassive); + uniqueNamer, + out var vcAssmPrelim, + out var endToEndLemmaPrelim + ); - #region before passive + vcAssm = vcAssmPrelim; + endToEndLemma = endToEndLemmaPrelim; + + theories.Add(vcPhaseProofTheory); - var passificationProgTheory = new Theory(beforePassiveProgTheoryName, - new List {"Boogie_Lang.Semantics", "Boogie_Lang.Util", beforeCfgToDagTheoryName}, - programDeclsBeforePassive); - theories.Add(passificationProgTheory); + #endregion + } - /* - Console.WriteLine("Passive prog mapping: " + fixedVarTranslation.OutputMapping()); - Console.WriteLine("Before passive prog mapping: " + fixedVarTranslation2.OutputMapping()); - */ + if (_proofGenConfig.GeneratePassifProof) + { + #region passification proof - var passificationProofTheory = PassificationManager.PassificationProof( + var passificationProofTheory = PassificationManager.PassificationProof( phasesTheories.TheoryName(PhasesTheories.Phase.Passification), - theoryPassive.TheoryName, + _proofGenConfig.GenerateVcProof ? phasesTheories.TheoryName(PhasesTheories.Phase.Vc) : "", _proofGenConfig.GeneratePassifE2E, endToEndLemma, vcAssm, @@ -585,25 +1021,32 @@ out var endToEndLemma passiveRelationGen, beforePassiveProgAccess, passiveProgAccess, - mainData, - varTranslationFactory2, + afterOptimizationsVarTranslationFactory, varTranslationFactory - ); - theories.Add(passificationProofTheory); + ); + theories.Add(passificationProofTheory); + + #endregion + } - #endregion - - #region cfg to dag - - var uniqueExitBlock = - uniqueExitBlockOrig != null - ? beforePassiveOrigBlock.First(kv => kv.Value == uniqueExitBlockOrig).Key - : null; + var phaseWithEndToEndTheorem = GetPhaseWithEndToEndTheorem(_proofGenConfig, proofGenInfo.GetOptimizationsFlag(), proofGenInfo.EliminatedDeadVars); + IDictionary> beforeDagBlocktoLoops = null; + IDictionary selfLoops = null; + if (_proofGenConfig.GenerateCfgDagProof) + { + #region cfg to dag - var cfgToDagProofTheory = CfgToDagManager.CfgToDagProof( + var uniqueExitBlock = + uniqueExitBlockOrig != null + ? beforePassiveOrigBlock.First(kv => kv.Value == uniqueExitBlockOrig).Key + : null; + beforeDagBlocktoLoops = getBeforeDagBlockToLoops(beforeDagAfterDagBlock, beforePassificationCfg, cfgToDagHintManager, out selfLoops); + var cfgToDagProofTheory = CfgToDagManager.CfgToDagProof( phasesTheories, - _proofGenConfig.GenerateCfgDagE2E, + PhasesTheories.EndToEndConfig(_proofGenConfig.GenerateCfgDagE2E, PhasesTheories.Phase.CfgToDag, phaseWithEndToEndTheorem), + _proofGenConfig.GeneratePassifProof, + _proofGenConfig.GenerateVcProof, vcAssm, beforeDagCfg, beforePassificationCfg, @@ -613,11 +1056,213 @@ out var endToEndLemma beforeDagAfterDagBlock, beforeCfgToDagProgAccess, beforePassiveProgAccess, - varTranslationFactory2); - theories.Add(cfgToDagProofTheory); - #endregion + afterOptimizationsVarTranslationFactory, + beforeDagBlocktoLoops); + theories.Add(cfgToDagProofTheory); + + #endregion + } + + if (_proofGenConfig.GenerateCfgOptProof(proofGenInfo.GetOptimizationsFlag())) + { + #region cfg optimizations + + // compute mapping between copied blocks (before opt -> after opt) + var origToAfterOpt = beforeDagOrigBlock.InverseDict(); + IDictionary beforeOptAfterOptBlock = DictionaryComposition(beforeOptimizationsOrigBlock, origToAfterOpt); + if (beforeDagBlocktoLoops == null) + { + beforeDagBlocktoLoops = getBeforeDagBlockToLoops(beforeDagAfterDagBlock, beforePassificationCfg, cfgToDagHintManager, out selfLoops); + } + + IDictionary CoalescedBlocksToTarget = DictionaryComposition(afterPassificationImpl.CoalescedBlocksToTarget, origToAfterOpt); + CoalescedBlocksToTarget = DictionaryComposition(beforeOptimizationsOrigBlock, CoalescedBlocksToTarget); + + IDictionary ListCoalescedBlocks = new Dictionary(); + foreach (var curr in afterPassificationImpl.ListCoalescedBlocks.Keys) + { + List temp = new List(); + foreach (var next in afterPassificationImpl.ListCoalescedBlocks[curr].coalescedBlocks) + { + temp.Add(beforeOptimizationsOrigBlock.InverseDict()[next]); + } + + BlockCoalescingInfo currInfo = + new BlockCoalescingInfo(temp, afterPassificationImpl.ListCoalescedBlocks[curr].idx); + ListCoalescedBlocks.Add(beforeOptimizationsOrigBlock.InverseDict()[curr], currInfo); + } + + + var cfgOptimizationsProofTheory = CfgOptimizationsManager.CfgOptProof( + phasesTheories, + PhasesTheories.EndToEndConfig(_proofGenConfig.GenerateCfgDagE2E, PhasesTheories.Phase.CfgOptimizations, phaseWithEndToEndTheorem), + beforeOptimizationsCFG, + beforeDagCfg, + beforeOptAfterOptBlock, + unoptimizedCfgOptVarProgAccess, + beforeCfgToDagProgAccess, + ListCoalescedBlocks, + CoalescedBlocksToTarget, + beforeDagBlocktoLoops, + vcAssm, + afterPassificationImpl.Name, + _proofGenConfig.GenerateCfgDagProof, + selfLoops); + theories.Add(cfgOptimizationsProofTheory); + + #endregion + } + + if (_proofGenConfig.GenerateAstCfgProof) + { + #region ast to cfg + + beforeCfgAfterCfgBlock = new Dictionary(); + IDictionary mappingWithHints = + new Dictionary(); + + IDictionary mappingOrigBigBlockToOrigBlock = + proofGenInfo.GetMappingOrigBigBlockToOrigBlock(); + + IDictionary mappingBigBlockToHints = + proofGenInfo.GetMappingCopyBigBlockToHints(); + foreach (var pair in mappingOrigBigBlockToOrigBlock) + { + var origBigBlock = pair.Key; + var copyBigBlock = proofGenInfo.GetMappingOrigBigblockToCopyBigblock()[origBigBlock]; + var origBlock = pair.Value; + + var blockToBlockMapToReferTo = proofGenInfo.GetOptimizationsFlag() + ? mappingOrigBlockToUnoptimizedCopy + : beforeDagOrigBlock.InverseDict(); + + var copyBlock = blockToBlockMapToReferTo[origBlock]; + var hints = mappingBigBlockToHints[origBigBlock]; + + beforeCfgAfterCfgBlock.Add(copyBigBlock, copyBlock); + mappingWithHints.Add(copyBigBlock, (copyBlock, hints.Item1, hints.Item2)); + } + + CFGRepr astCfgReprInput; + IDictionary blockToBlockMapInput; + IProgramAccessor beforeCfgToDagProgAccessInput; + + if (!proofGenInfo.GetOptimizationsFlag()) + { + //if no optimizations were applied, then directly connect to CFG before CFG-to-DAG + astCfgReprInput = beforeDagCfg; + blockToBlockMapInput = beforeDagOrigBlock; + beforeCfgToDagProgAccessInput = beforeCfgToDagProgAccess; + } + else + { + //optimizations were applied, then connect to CFG before optimizations + astCfgReprInput = beforeOptimizationsCFG; + blockToBlockMapInput = mappingUnoptimizedCopyToOrigBlock; + beforeCfgToDagProgAccessInput = unoptimizedCfgProgAccess; + } + + var astToCfgProofTheory = AstToCfgManager.AstToCfgProof( + phasesTheories.TheoryName(PhasesTheories.Phase.AstToCfg), + phasesTheories, + PhasesTheories.EndToEndConfig(_proofGenConfig.GenerateAstCfgE2E(proofGenInfo.EliminatedDeadVars), + PhasesTheories.Phase.AstToCfg, phaseWithEndToEndTheorem), + _proofGenConfig, + vcAssm, + proofGenInfo, + beforeCfgAst, + astCfgReprInput, + blockToBlockMapInput, + mappingWithHints, + beforeCfgAfterCfgBlock, + beforeAstToCfgProgAccess, + beforeCfgToDagProgAccessInput, + afterOptimizationsVarTranslationFactory, + cmdIsaVisitor); + theories.Add(astToCfgProofTheory); + + #endregion + } + + StoreResult(uniqueNamer.GetName(afterPassificationImpl.Proc.Name), theories); + } + + /// + /// Return phase that must contain the end-to-end theorem with the corresponding source procedure if + /// the generation of end-to-end proofs is enabled. + /// + /// + /// Are there CFG optimizations? + /// Were dead variables eliminated? + /// + /// + private static PhasesTheories.Phase GetPhaseWithEndToEndTheorem(ProofGenConfig proofGenConfig, bool cfgOptimzations, bool deadVarsElim) + { + if (!cfgOptimzations && deadVarsElim) + { + throw new ArgumentException("dead variables eliminated but cfg optimizations not marked"); + } + + if (proofGenConfig.GenerateAstCfgE2E(deadVarsElim)) + { + return PhasesTheories.Phase.AstToCfg; + } + + if (proofGenConfig.GenerateCfgDagE2E && cfgOptimzations) + { + return PhasesTheories.Phase.CfgOptimizations; + } + + /* + Note that we could be more fine-grained here and instead return the passification or VC phases, if + the CFG-to-DAG phase end-to-end is disabled, but for now we only support the procedure version of the + end-to-end theorem until the CFG-to-DAG phase (the CFG-to-DAG end-to-end is usually disabled only for + debugging purposes). + */ + return PhasesTheories.Phase.CfgToDag; + } + + private static CFGRepr GetCfgBeforeOptimizations(IList unoptimizedCFGBlocks) + { + var config = new CFGReprConfigBuilder().SetIsAcyclic(false).SetBlockCopy(true).SetDesugarCalls(true) + .Build(); - StoreResult(afterPassificationImpl.Proc.Name, theories); + IDictionary labeling; + if (config.IsAcyclic) + { + labeling = CFGReprTransformer.GetTopologicalLabeling(unoptimizedCFGBlocks); + } + else + { + labeling = new Dictionary(); + var idx = 0; + foreach (var b in unoptimizedCFGBlocks) + { + labeling.Add(b, idx); + idx++; + } + } + + IDictionary> outgoingBlocks = new Dictionary>(); + foreach (var block in unoptimizedCFGBlocks) + { + var curOutgoing = new List(); + if (block.TransferCmd is GotoCmd gotoCmd) curOutgoing.AddRange(gotoCmd.labelTargets); + outgoingBlocks.Add(block, curOutgoing); + } + + Block entryBlock = null; + foreach (var block in unoptimizedCFGBlocks) + { + if (block.Predecessors.Count == 0) + { + if (entryBlock != null) + throw new ProofGenUnexpectedStateException(typeof(CFGReprTransformer), "no unique CFG entry"); + entryBlock = block; + } + } + + return new CFGRepr(outgoingBlocks, labeling, entryBlock); } private static void StoreResult(string preferredDirName, IEnumerable theories) @@ -629,5 +1274,53 @@ public static BoogieIsaProgInterface BoogieIsaProgInterface() { return new BoogieIsaProgInterface(new Dictionary(procNameToTopLevelPrograms), globalDataProgAccess); } + + public static IDictionary> getBeforeDagBlockToLoops(IDictionary beforeToAfter, CFGRepr afterDagCfg, CfgToDagHintManager hintManager, out IDictionary selfLoops) + { + selfLoops = new Dictionary(); + var afterToBefore = beforeToAfter.InverseDict(); + IDictionary> blocksToLoops = new Dictionary>(); + foreach (var afterBlock in afterDagCfg.GetBlocksBackwards()) + { + if (afterToBefore.TryGetValue(afterBlock, out var beforeBlock)) + { + var loops = new HashSet(); + foreach (var bSuc in beforeDagCfg.GetSuccessorBlocks(beforeBlock)) + { + if (blocksToLoops.TryGetValue(bSuc, out var loopsSuc)) + { + //if successor inside of a loop L and the block is not the loop head of L, then the block is also inside L + foreach (var loopSuc in loopsSuc) + { + if (!loopSuc.Equals(beforeBlock)) + { + loops.Add(loopSuc); + } + } + } + } + /* a node is inside all loops for which it has an out-going backedge + if a node has a backedge to itself (i.e., it is also a loop head), then we do not add this loop + */ + if (hintManager.TryIsBackedgeNode(beforeBlock, out var backedgeLoops)) + { + foreach (var backedgeLoop in backedgeLoops) + { + if (beforeBlock != backedgeLoop) + { + loops.Add(backedgeLoop); + } + else + { + selfLoops.Add(beforeBlock, backedgeLoop); + } + } + } + var loopsList = loops.ToList(); + blocksToLoops.Add(beforeBlock, loopsList); + } + } + return blocksToLoops; + } } } \ No newline at end of file diff --git a/Source/ProofGeneration/ProofGenerationOutput.cs b/Source/ProofGeneration/ProofGenerationOutput.cs index 2ca8136c4..141116b24 100644 --- a/Source/ProofGeneration/ProofGenerationOutput.cs +++ b/Source/ProofGeneration/ProofGenerationOutput.cs @@ -6,6 +6,7 @@ using Isabelle.Ast; using Isabelle.IsaPrettyPrint; using Microsoft.Boogie; +using ProofGeneration.Util; namespace ProofGeneration { @@ -119,7 +120,10 @@ public static void FinishStoring() throw new ProofGenUnexpectedStateException("main directory not yet set"); using var sw = new StreamWriter(Path.Combine(_mainDir, "ROOT")); - sw.WriteLine("session " + SessionName(Path.GetFileName(_mainDir)) + " = " + "Boogie_Lang + "); + + var sessionName = new IsaUniqueNamer().GetName(Path.GetFileName(_mainDir)); + + sw.WriteLine("session " + SessionName(sessionName) + " = " + "Boogie_Lang + "); var subDirs = Directory.EnumerateDirectories(_mainDir).ToList(); if (subDirs.Any()) diff --git a/Source/ProofGeneration/Util/IsaUniqueNamer.cs b/Source/ProofGeneration/Util/IsaUniqueNamer.cs index 140be41e3..8fb1b50e8 100644 --- a/Source/ProofGeneration/Util/IsaUniqueNamer.cs +++ b/Source/ProofGeneration/Util/IsaUniqueNamer.cs @@ -43,6 +43,11 @@ public string GetName(string preferredName) { return GetName(preferredName, preferredName); } + + public string RemoveApostropheInFunc(string preferredName) + { + return RemoveApostropheInFunc(preferredName, preferredName); + } /// /// Returns a unique, legal Isabelle name resembling . @@ -54,11 +59,47 @@ public string GetName(object obj, string preferredName) var preferredNameMod = preferredName; foreach (var illegalChar in illegalChars) preferredNameMod = preferredNameMod.Replace(illegalChar, '_'); + /* TODO: might need to replace numbers in some cases for ROOT file + if (preferredNameMod.Contains('0')) preferredNameMod = preferredNameMod.Replace('0', 'a'); + if (preferredNameMod.Contains('1')) preferredNameMod = preferredNameMod.Replace('1', 'b'); + if (preferredNameMod.Contains('2')) preferredNameMod = preferredNameMod.Replace('2', 'c'); + if (preferredNameMod.Contains('3')) preferredNameMod = preferredNameMod.Replace('3', 'd'); + if (preferredNameMod.Contains('4')) preferredNameMod = preferredNameMod.Replace('4', 'e'); + if (preferredNameMod.Contains('5')) preferredNameMod = preferredNameMod.Replace('5', 'f'); + if (preferredNameMod.Contains('6')) preferredNameMod = preferredNameMod.Replace('6', 'g'); + if (preferredNameMod.Contains('7')) preferredNameMod = preferredNameMod.Replace('7', 'h'); + if (preferredNameMod.Contains('8')) preferredNameMod = preferredNameMod.Replace('8', 'i'); + if (preferredNameMod.Contains('9')) preferredNameMod = preferredNameMod.Replace('9', 'j'); + */ + if (reservedNames.Contains(preferredNameMod)) preferredNameMod = preferredNameMod + "ZZ"; if (preferredName.Length > 0 && preferredName.Last() == '_') preferredNameMod = preferredNameMod + "n"; + return uniqueNamer.GetName(obj, GetValidIsaString(preferredNameMod)); } + public string RemoveApostrophe(object obj, string preferredName) + { + var preferredNameMod = preferredName; + if (preferredNameMod.Contains("\'" + "\'" + "\'")) + { + preferredNameMod = preferredNameMod.Replace("\'" + "\'" + "\'", "AA" + "\'" + "\'"); + } + + return uniqueNamer.GetName(obj, preferredNameMod); + } + + public string RemoveApostropheInFunc(object obj, string preferredName) + { + var preferredNameMod = preferredName; + if (preferredNameMod.Contains("\'")) + { + preferredNameMod = preferredNameMod.Replace("\'", "AA"); + } + + return uniqueNamer.GetName(obj, preferredNameMod); + } + public string GetLocalName(object obj, string preferredName) { return uniqueNamer.GetLocalName(obj, GetValidIsaString(preferredName)); diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index aaf80527e..b87eb8c26 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -1064,7 +1064,7 @@ private void RecordCutEdge(Dictionary> edgesCut, Block from, public void ConvertCFG2DAG(Implementation impl, Dictionary> edgesCut = null, int taskID = -1) { - Contract.Requires(impl != null); + Contract.Requires(impl != null); impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization #region proofgen @@ -1263,6 +1263,10 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary bool: + return file in os.listdir(path) + +def check_string_exists_in_file(search_string, path) -> bool: + if not(os.path.isfile(path)): + print("ERROR: {} is not an existing file.".format(path)) + exit(1) + + matches = [line for line in open(path,'r') if search_string in line] + return matches != [] + + +def ast_to_cfg_proof(dir, procedure_name): + return os.path.join(dir, procedure_name+"_asttocfg_proof.thy") + +def cfg_opt_proof(dir, procedure_name): + return os.path.join(dir, procedure_name+"_cfgoptimizations_proof.thy") + +def cfg_to_dag_proof(dir, procedure_name): + return os.path.join(dir, procedure_name+"_cfgtodag_proof.thy") + +def get_e2e_kind(procedure_name, dir) -> EndToEndKind: + ast_exists : bool = os.path.isfile(ast_to_cfg_proof(dir, procedure_name)) + if not(os.path.isfile(cfg_to_dag_proof(dir, procedure_name))): + print("ERROR: CFG-to-DAG proof {} does not exist in {}".format(cfg_to_dag_proof(dir, procedure_name), dir)) + exit(1) + + # important to query cfg_to_dag first, since cfg_opt proof may not exist (relies on lazy evaluation of `or`) + # this is a method because if ast e2e exists then we do not need to evaluate this expression (+ want reuse) + def cfg_end_to_end(): + return (check_string_exists_in_file("lemma end_to_end_theorem:", cfg_to_dag_proof(dir, procedure_name)) or + check_string_exists_in_file("lemma end_to_end_theorem:", cfg_opt_proof(dir, procedure_name))) + + if ast_exists: + if check_string_exists_in_file("lemma end_to_end_theorem_ast:", ast_to_cfg_proof(dir, procedure_name)): + return EndToEndKind.full + + if cfg_end_to_end(): + return EndToEndKind.ast_and_full_cfg + + print("ERROR: no end-to-end theorem found in {}".format(dir)) + exit(1) + + if cfg_end_to_end(): + return EndToEndKind.only_full_cfg + + print("ERROR: no end-to-end theorem found in {}".format(dir)) + exit(1) + +def count_non_empty_lines(file_path): + file_content = open(file_path, "r") + nonempty_lines = [line for line in file_content if line.strip()] + return len(nonempty_lines) + +def certificate_size(proof_dir_path) -> int: + length_certificate = 0 + for root, dirs, files in os.walk(proof_dir_path): + for file in files: + if file.endswith('.thy'): + file_path = os.path.join(os.path.join(proof_dir_path, root), file) + length_certificate += count_non_empty_lines(file_path) + + return length_certificate + +def count_num_abstract_procs(lines) -> int: + count = 0 + regex_pattern = r'procedure \w*\((\w|:)*\);' + for line in lines: + matches = re.findall(regex_pattern, line) + count += len(matches) + return count + +def collect_data_single_boogie_file(proof_dir_path, boogie_loc): + e2e_proof_kinds = [] + num_procedure_proofs = 0 + for procedure_proof in os.listdir(proof_dir_path): + procedure_proof_path = os.path.join(proof_dir_path, procedure_proof) + if(os.path.isdir(procedure_proof_path)): + if(not(procedure_proof.endswith("_proofs"))): + print("ERROR: procedure proof directory {} does not end with '_proofs'".format(procedure_proof_path)) + exit(1) + + procedure_name = procedure_proof.split("_proofs")[0] + e2e_proof_kind = get_e2e_kind(procedure_name, procedure_proof_path) + e2e_proof_kinds.append(e2e_proof_kind) + num_procedure_proofs += 1 + + """ + if len(e2e_proof_kinds) != num_procedures: + print("ERROR: mismatch number of procedures and e2e proofs in {}".format(proof_dir_path)) + exit(1) + """ + + return TestData(file=os.path.basename(proof_dir_path), num_procedure_proofs=num_procedure_proofs, boogie_loc = boogie_loc, isabelle_loc=certificate_size(proof_dir_path),end_to_end_proof=e2e_proof_kinds) + +def collect_complete_data(boogie_files_dir, boogie_proofs_dir) -> List[TestData]: + data = [] + boogie_proofs_dir_abs = os.path.abspath(boogie_proofs_dir) + for proof_dir in os.listdir(boogie_proofs_dir_abs): + proof_dir_path = os.path.join(boogie_proofs_dir_abs, proof_dir) + if not(os.path.isfile(os.path.join(proof_dir_path, "ROOT"))): + print("Skipping {}".format(proof_dir_path)) + continue + + if not(proof_dir.endswith("_proofs")): + print("ERROR: {} does not end with '_proofs'".format(proof_dir)) + exit(1) + + boogie_file_name = proof_dir.split("_proofs")[0]+".bpl" + boogie_file_path = os.path.join(boogie_files_dir, boogie_file_name) + if not(os.path.isfile(boogie_file_path)): + # try finding any file with the same name + + candidates = [os.path.join(root,f) for root, dirs, files in os.walk(boogie_files_dir) for f in files if f == boogie_file_name] + if len(candidates) == 0: + print("ERROR: Boogie file {} does not exist".format(boogie_file_name)) + exit(1) + elif len(candidates) >= 2: + print("ERROR: Multiple candidates for Boogie file {}".format(boogie_file_name)) + exit(1) + + boogie_file_path = os.path.join(boogie_files_dir, candidates[0]) + if(not(os.path.exists(boogie_file_path))): + print("CODE ERROR: file {} does not exist".format(boogie_file_path)) + exit(1) + + boogie_file_content = [line for line in open(boogie_file_path,'r')] + num_procedures = len([line for line in boogie_file_content if line.strip(" ").startswith("procedure ")]) + + #if num_abstract_procedures > 0: + #print("{} has {} abstract procedures".format(boogie_file_path, num_abstract_procedures)) + + """ + if num_concrete_procedures != len([y for y in os.listdir(proof_dir_path) if os.path.isdir(os.path.join(proof_dir_path, y))]): + print("ERROR: Number of non-abstract procedures in {} do not match number of proofs in {}".format(boogie_file_path, proof_dir_path)) + exit(1) + """ + + data.append(collect_data_single_boogie_file(proof_dir_path, count_non_empty_lines(boogie_file_path))) + + return data + +def write_data_into_csv(data : List[TestData], output_file): + with open(output_file, 'w', newline='') as output: + writer = csv.writer(output, delimiter=',') + + writer.writerow(["File", "# Procs", "Boogie LOC", "Isabelle LOC", "E2E Kind"]) + for d in data: + e2e_repr = " ".join(e.name for e in d.end_to_end_proof) + writer.writerow([d.file, d.num_procedure_proofs, d.boogie_loc, d.isabelle_loc, e2e_repr]) + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument( + "-t", "--testsdir", + help="Directory where the Boogie files are located, for which the proofs were generated.", + required=True) + + parser.add_argument( + "-p", "--proofsdir", + help="Directory where the Boogie proofs are located.", + required=True) + + + args = parser.parse_args() + + if (not(os.path.isdir(args.testsdir))): + print("The input directory " + args.testsdir+ " does not point to an existing directory.") + exit(1) + + if (not(os.path.isdir(args.proofsdir))): + print("The input directory " + args.proofsdir+ " does not point to an existing directory.") + exit(1) + + data : List[TestData] = collect_complete_data(boogie_files_dir=args.testsdir, boogie_proofs_dir=args.proofsdir) + + num_boogie_files = len([f for root, dirs, files in os.walk(args.testsdir) for f in files if f.endswith(".bpl")]) + if(len(data) != num_boogie_files): + print("ERROR: length of data does not match number of Boogie files") + exit(1) + + print("Numer of E2E proofs {}".format(len(data))) + + def count_e2e_kind(kind): + return sum([len([k for k in d.end_to_end_proof if k == kind]) for d in data]) + + num_full_proofs = count_e2e_kind(EndToEndKind.full) + num_ast_and_full_cfgproofs = count_e2e_kind(EndToEndKind.ast_and_full_cfg) + num_only_cfg_proofs = count_e2e_kind(EndToEndKind.only_full_cfg) + + print("Full proofs: {}, AST and full CFG proofs: {}, Only CFG proofs: {}".format( + num_full_proofs, + num_ast_and_full_cfgproofs, + num_only_cfg_proofs + )) + + total_num_procedure_proofs = sum([d.num_procedure_proofs for d in data]) + + if((num_full_proofs + num_ast_and_full_cfgproofs + num_only_cfg_proofs) != total_num_procedure_proofs): + print("ERROR: sum of E2E kinds does not match with length of data") + exit(1) + + output_file_name = time.strftime("%Y%m%d_%H%M")+os.path.basename(args.proofsdir)+"_analysis.csv" + write_data_into_csv(data, output_file_name) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/etc/scripts/count_lines.py b/etc/scripts/count_lines.py index b15ca215d..666c31780 100644 --- a/etc/scripts/count_lines.py +++ b/etc/scripts/count_lines.py @@ -13,7 +13,7 @@ def print_theory_line_info(input_dir): has_theories = True file_path = os.path.join(os.path.join(dir_path, root), file) file_content = open(file_path, "r") - nonempty_lines = [line.strip("\n") for line in file_content if line != "\n"] + nonempty_lines = [line for line in file_content if line.strip()] length_certificate += len(nonempty_lines) if has_theories: diff --git a/etc/scripts/find_supported_boogie_benchmarks.py b/etc/scripts/find_supported_boogie_benchmarks.py index 96f90df70..4e0920f3c 100644 --- a/etc/scripts/find_supported_boogie_benchmarks.py +++ b/etc/scripts/find_supported_boogie_benchmarks.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess import time import sys @@ -8,11 +9,28 @@ boogie_proofgen_bin = 'boogieproof' boogie_orig_bin = 'boogieorig' -def adjust_and_filter(filename, output_filename): +# copies file to output directory such that the directory structure of the source destination is kept (relative to test_dir) +def store_file_in_output_dir(file_path, output_dir, test_dir): + relpath = os.path.relpath(file_path, test_dir) + print("Test dir: {}, File path: {}, Relative path: {}".format(test_dir, file_path, relpath)) + target_path = os.path.join(output_dir, relpath) + + os.makedirs(os.path.dirname(target_path), exist_ok=True) + + shutil.copyfile(file_path, target_path) + +def adjust_and_filter(filename, output_dir, test_dir): n_supported_success = 0 n_supported_error = 0 n_empty_files = 0 + if(os.path.exists(output_dir)): + print("Error: Output dir {} exists".format(output_dir)) + exit(1) + + os.mkdir(output_dir) + output_filename = os.path.join(output_dir, "origin_list.txt") + with open(output_filename, 'w') as output_file: with open(filename) as f: print("Starting next selection: Remove all attributes of files in first selection and check if they verify.") @@ -48,11 +66,15 @@ def adjust_and_filter(filename, output_filename): # space before 0, otherwise "10 errors" would succeed the test as well if " 0 errors" in str(output) and (not ("inconclusive" in str(output))): n_supported_success += 1 - print("Selected " + boogie_file) + + print("Selected " + boogie_file) + output_file.write(boogie_file+"\n") + + store_file_in_output_dir(file_path=boogie_file, output_dir=output_dir, test_dir=test_dir) else: n_supported_error += 1 - # print("Failure:" + boogie_file) + print("Failure:" + boogie_file) print("Found " + str(n_supported_success) + " potentially supported tests that are verified after removing all attributes.") @@ -68,8 +90,8 @@ def main(): required=True) parser.add_argument( - "-o", "--outputfile", - help="Specify output file containing the list of test that are verified by Boogie and that are (potentially) supported by proof generation.", + "-o", "--outputdir", + help="Specify output directory containing the tests are verified by Boogie and that are (potentially) supported by proof generation.", required=True) args = parser.parse_args() @@ -78,37 +100,49 @@ def main(): print(args.testdir + " is not a directory.") exit(1) - if(os.path.exists(args.outputfile)): - print(args.outputfile + " already exists.") + if(os.path.exists(args.outputdir)): + print(args.outputdir + " already exists.") exit(1) + # write all Boogie test file paths that are potentially supported by proof generation into a temporary file filename = time.strftime("%Y%m%d-%H%M%S")+"_potentially_supported.log" with open(filename, 'w') as potentially_supported_file: print("Starting coarse-grained selection of tests") + + def file_potentially_supported_from_output(output): + output_split = output.splitlines() + return len(output_split) > 0 and output_split[0].startswith("Success:") + n_candidate_files = 0 + for root, dirs, files in os.walk(args.testdir): for file in files: if file.endswith('.bpl'): test_file_path = os.path.join(root, file) + n_candidate_files += 1 + + print(test_file_path) + + # the option "/onlyCheckProofGenSupport" only performs a coarse-grained check whether + # proof generation supports a file and does not generate any proofs + process = subprocess.Popen([boogie_proofgen_bin, "/onlyCheckProofGenSupport",test_file_path], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = process.communicate() + return_code = process.returncode + + if return_code == 0 and file_potentially_supported_from_output(output.decode('utf-8')): + print("success") + potentially_supported_file.write(test_file_path+"\n") + else: + print("error (return code: {}, error: {}, output: {})".format(return_code, error.decode('utf-8'), output.decode('utf-8'))) - with open(test_file_path) as f: - n_candidate_files += 1 - # the option "/onlyCheckProofGenSupport" only performs a coarse-grained check whether - # proof generation supports a file and does not generate any proofs - output = subprocess.check_output([boogie_proofgen_bin, "/onlyCheckProofGenSupport",test_file_path]).decode(sys.stdout.encoding) - - output_split = output.splitlines() - if len(output_split) > 0 and output_split[0].startswith("Success:"): - # print(output_split[0][8:]) - potentially_supported_file.write(output_split[0][8:]+"\n") print("Finished coarse-grained selection of tests") """ From the identified files, select those tests that verify after removing attributes (this has a side effect on the files --> attributes removed). """ - adjust_and_filter(filename, args.outputfile) + adjust_and_filter(filename, args.outputdir, args.testdir) # remove the temporary file os.remove(filename) diff --git a/foundational-boogie b/foundational-boogie new file mode 160000 index 000000000..90411340a --- /dev/null +++ b/foundational-boogie @@ -0,0 +1 @@ +Subproject commit 90411340ac568c7870e85dd9ec627b84f01e79a3 diff --git a/foundational_boogie b/foundational_boogie deleted file mode 160000 index 4667c5387..000000000 --- a/foundational_boogie +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4667c538759128ad88588ff2c1ae821d7a78b16b