From d838ad2e827c98312aefdbc76b45af4fec8608a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Fri, 16 Oct 2020 17:41:42 +0200 Subject: [PATCH] [wp] induction tactic --- headers/header_spec.txt | 2 + src/plugins/wp/Conditions.mli | 2 +- src/plugins/wp/Makefile.in | 2 +- src/plugins/wp/TacInduction.ml | 115 ++++++++++++++++++ src/plugins/wp/TacInduction.mli | 29 +++++ src/plugins/wp/tests/wp_tip/induction.i | 28 +++++ .../oracle_qualif/induction.0.res.oracle | 12 ++ .../script/lemma_ByInd.json | 14 +++ .../oracle_qualif/induction.1.res.oracle | 10 ++ .../script/lemma_ByInd.json | 11 ++ .../oracle_qualif/induction.2.res.oracle | 10 ++ .../script/lemma_ByInd.json | 11 ++ .../wp_tip/oracle_qualif/induction.res.oracle | 10 ++ .../script/lemma_ByInd.json | 13 ++ 14 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 src/plugins/wp/TacInduction.ml create mode 100644 src/plugins/wp/TacInduction.mli create mode 100644 src/plugins/wp/tests/wp_tip/induction.i create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.res.oracle create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.session/script/lemma_ByInd.json create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.res.oracle create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.session/script/lemma_ByInd.json create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.res.oracle create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.session/script/lemma_ByInd.json create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction.res.oracle create mode 100644 src/plugins/wp/tests/wp_tip/oracle_qualif/induction_prove.0.session/script/lemma_ByInd.json diff --git a/headers/header_spec.txt b/headers/header_spec.txt index c77fc0d433b..a86fbfb6d25 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1629,6 +1629,8 @@ src/plugins/wp/TacFilter.ml: CEA_WP src/plugins/wp/TacFilter.mli: CEA_WP src/plugins/wp/TacHavoc.ml: CEA_WP src/plugins/wp/TacHavoc.mli: CEA_WP +src/plugins/wp/TacInduction.ml: CEA_WP +src/plugins/wp/TacInduction.mli: CEA_WP src/plugins/wp/TacInstance.ml: CEA_WP src/plugins/wp/TacInstance.mli: CEA_WP src/plugins/wp/TacLemma.ml: CEA_WP diff --git a/src/plugins/wp/Conditions.mli b/src/plugins/wp/Conditions.mli index 878a73afc76..3e6aa0e00a2 100644 --- a/src/plugins/wp/Conditions.mli +++ b/src/plugins/wp/Conditions.mli @@ -142,7 +142,7 @@ val map_sequence : (pred -> pred) -> sequence -> sequence (** Rewrite all root predicates in sequence *) val map_sequent : (pred -> pred) -> sequent -> sequent -(** Rewrite all root predocates in hypotheses and goal *) +(** Rewrite all root predicates in hypotheses and goal *) val insert : ?at:int -> step -> sequent -> sequent (** Insert a step in the sequent immediately [at] the specified position. diff --git a/src/plugins/wp/Makefile.in b/src/plugins/wp/Makefile.in index 5264a1b2cfd..2d81efcad32 100644 --- a/src/plugins/wp/Makefile.in +++ b/src/plugins/wp/Makefile.in @@ -85,7 +85,7 @@ PLUGIN_CMO:= \ CfgCompiler StmtSemantics \ VCS script proof wpo wpReport \ Footprint Tactical Strategy \ - TacSplit TacChoice TacRange \ + TacSplit TacChoice TacRange TacInduction \ TacArray TacCompound TacUnfold \ TacHavoc TacInstance TacLemma \ TacFilter TacCut WpTac TacNormalForm \ diff --git a/src/plugins/wp/TacInduction.ml b/src/plugins/wp/TacInduction.ml new file mode 100644 index 00000000000..7a5a4185418 --- /dev/null +++ b/src/plugins/wp/TacInduction.ml @@ -0,0 +1,115 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Lang + +type env = { + n : F.var ; + sigma : F.sigma ; + mutable hind : F.pred list ; +} + +let rec strip env p = + match F.p_expr p with + | And ps -> F.p_all (strip env) ps + | _ -> + let p = F.p_subst env.sigma p in + if F.occursp env.n p then + ( env.hind <- p :: env.hind ; F.p_true ) + else p + +let process value n0 ~inf ~sup seq = + + (* Transfrom seq into: hyps => (forall n, goal) *) + let n = Lang.freshvar ~basename:"n" Qed.Logic.Int in + let i = Lang.freshvar ~basename:"i" Qed.Logic.Int in + let vn = F.e_var n in + let vi = F.e_var i in + let v0 = F.e_int n0 in + let sigma = Lang.sigma () in + F.Subst.add sigma value vn ; + let env = { n ; sigma ; hind = [] } in + let hyps = Conditions.map_sequence (strip env) (fst seq) in + let goal_n = F.p_hyps env.hind @@ F.p_subst sigma (snd seq) in + let goal_i = F.p_subst_var n vi goal_n in + + (* Base: n = n0 *) + let goal_base = F.p_imply (F.p_equal vn v0) goal_n in + + (* Hind: n0 <= i < n *) + let goal_sup = + if sup + then + let hsup = [ F.p_leq v0 vi ; F.p_lt vi vn ] in + let hind = F.p_forall [i] (F.p_hyps hsup goal_i) in + F.p_hyps [F.p_lt v0 vn; hind] goal_n + else F.p_leq value v0 in + + (* Hind: n < i <= n0 *) + let goal_inf = + if inf + then + let hinf = [ F.p_lt vn vi ; F.p_leq vi v0 ] in + let hind = F.p_forall [i] (F.p_hyps hinf goal_i) in + F.p_hyps [F.p_lt vn v0; hind] goal_n + else F.p_leq v0 value in + + (* All Cases *) + List.map (fun (name,goal) -> name , (hyps,goal)) [ + "Base" , goal_base ; + "Induction (sup)" , goal_sup ; + "Induction (inf)" , goal_inf ; + ] + +(* -------------------------------------------------------------------------- *) +(* --- Induction Tactical --- *) +(* -------------------------------------------------------------------------- *) + +let vbase,pbase = Tactical.spinner ~id:"base" + ~title:"Base" ~descr:"Value of base case" () +let vsup,psup = Tactical.checkbox ~id:"hsup" + ~title:"Sup" ~descr:"Induction over base <= n" ~default:true () +let vinf,pinf = Tactical.checkbox ~id:"hinf" + ~title:"Inf" ~descr:"Induction over n <= base" ~default:true () + +class induction = + object(self) + inherit Tactical.make + ~id:"Wp.induction" + ~title:"Induction" + ~descr:"Proof by integer induction" + ~params:[pbase;psup;pinf] + + method select _feedback (s : Tactical.selection) = + let value = Tactical.selected s in + if F.is_int value then + let base = self#get_field vbase in + let inf = self#get_field vinf in + let sup = self#get_field vsup in + Applicable(process value base ~inf ~sup) + else Not_applicable + + end + +let tactical = Tactical.export (new induction) + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/TacInduction.mli b/src/plugins/wp/TacInduction.mli new file mode 100644 index 00000000000..21c817a0b33 --- /dev/null +++ b/src/plugins/wp/TacInduction.mli @@ -0,0 +1,29 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(** Built-in Range Tactical (auto-registered) *) + +open Tactical + +val tactical : tactical + +(**************************************************************************) diff --git a/src/plugins/wp/tests/wp_tip/induction.i b/src/plugins/wp/tests/wp_tip/induction.i new file mode 100644 index 00000000000..5ff948c0f86 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/induction.i @@ -0,0 +1,28 @@ +/* run.config + DONTRUN: +*/ + +/* run.config_qualif + OPT: -wp-prover script,alt-ergo -wp-timeout 1 + OPT: -wp-prover script,alt-ergo -wp-timeout 1 + OPT: -wp-prover script,alt-ergo -wp-timeout 1 +*/ + +// Script 0: induction on f(x) => success +// Script 1: induction on x => unsuccess +// Script 2: induction on y => unsuccess + +/*@ + axiomatic Inductive { + + logic integer f(integer x); + predicate P(integer x, integer y); + + axiom Hbse: \forall integer y; P(0,y); + axiom Hsup: \forall integer i,y; 0 <= i ==> P(i,y) ==> P(i+1,y); + axiom Hinf: \forall integer i,y; i <= 0 ==> P(i,y) ==> P(i-1,y); + + lemma ByInd: \forall integer x,y; P(f(x),y); + + } +*/ diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.res.oracle b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.res.oracle new file mode 100644 index 00000000000..6a84303d1b4 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.res.oracle @@ -0,0 +1,12 @@ +# frama-c -wp -wp-timeout 1 [...] +[kernel] Parsing tests/wp_tip/induction.i (no preprocessing) +[wp] Running WP plugin... +[wp] 1 goal scheduled +[wp] [Script] Goal typed_lemma_ByInd : Valid +[wp] Proved goals: 1 / 1 + Qed: 0 + Script: 1 +------------------------------------------------------------ + Axiomatics WP Alt-Ergo Total Success + Axiomatic Inductive - - 1 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.session/script/lemma_ByInd.json b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.session/script/lemma_ByInd.json new file mode 100644 index 00000000000..c70ff7e2d9c --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.0.session/script/lemma_ByInd.json @@ -0,0 +1,14 @@ +[ { "prover": "Alt-Ergo:2.2.0", "verdict": "unknown" }, + { "prover": "script", "verdict": "valid" }, + { "header": "Induction", "tactic": "Wp.induction", + "params": { "base": 0, "hsup": true, "hinf": true }, + "select": { "select": "inside-goal", "occur": 0, "target": "(L_f x_0)", + "pattern": "L_f$x" }, + "children": { "Base": [ { "prover": "Alt-Ergo:2.2.0", "verdict": "valid", + "time": 0.0075, "steps": 6 } ], + "Induction (sup)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "valid", "time": 0.0113, + "steps": 21 } ], + "Induction (inf)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "valid", "time": 0.009, + "steps": 20 } ] } } ] diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.res.oracle b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.res.oracle new file mode 100644 index 00000000000..68fc7293ba5 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.res.oracle @@ -0,0 +1,10 @@ +# frama-c -wp -wp-timeout 1 [...] +[kernel] Parsing tests/wp_tip/induction.i (no preprocessing) +[wp] Running WP plugin... +[wp] 1 goal scheduled +[wp] [Script] Goal typed_lemma_ByInd : Unsuccess +[wp] Proved goals: 0 / 1 +------------------------------------------------------------ + Axiomatics WP Alt-Ergo Total Success + Axiomatic Inductive - - 1 0.0% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.session/script/lemma_ByInd.json b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.session/script/lemma_ByInd.json new file mode 100644 index 00000000000..5a250b7fa27 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.1.session/script/lemma_ByInd.json @@ -0,0 +1,11 @@ +[ { "prover": "Alt-Ergo:2.2.0", "verdict": "unknown" }, + { "header": "Induction", "tactic": "Wp.induction", + "params": { "base": 0, "hsup": true, "hinf": true }, + "select": { "select": "inside-goal", "occur": 0, "target": "x_0", + "pattern": "$x" }, + "children": { "Base": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "unknown" } ], + "Induction (sup)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "unknown" } ], + "Induction (inf)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "timeout", "time": 1. } ] } } ] diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.res.oracle b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.res.oracle new file mode 100644 index 00000000000..68fc7293ba5 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.res.oracle @@ -0,0 +1,10 @@ +# frama-c -wp -wp-timeout 1 [...] +[kernel] Parsing tests/wp_tip/induction.i (no preprocessing) +[wp] Running WP plugin... +[wp] 1 goal scheduled +[wp] [Script] Goal typed_lemma_ByInd : Unsuccess +[wp] Proved goals: 0 / 1 +------------------------------------------------------------ + Axiomatics WP Alt-Ergo Total Success + Axiomatic Inductive - - 1 0.0% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.session/script/lemma_ByInd.json b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.session/script/lemma_ByInd.json new file mode 100644 index 00000000000..c3224ca1dac --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.2.session/script/lemma_ByInd.json @@ -0,0 +1,11 @@ +[ { "prover": "Alt-Ergo:2.2.0", "verdict": "unknown" }, + { "header": "Induction", "tactic": "Wp.induction", + "params": { "base": 0, "hsup": true, "hinf": true }, + "select": { "select": "inside-goal", "occur": 0, "target": "y_0", + "pattern": "$y" }, + "children": { "Base": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "unknown" } ], + "Induction (sup)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "unknown" } ], + "Induction (inf)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "timeout", "time": 1. } ] } } ] diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.res.oracle b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.res.oracle new file mode 100644 index 00000000000..1524fcd67af --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction.res.oracle @@ -0,0 +1,10 @@ +# frama-c -wp [...] +[kernel] Parsing tests/wp_tip/induction.i (no preprocessing) +[wp] Running WP plugin... +[wp] 1 goal scheduled +[wp] [Script] Goal typed_lemma_Simple : Unsuccess +[wp] Proved goals: 0 / 1 +------------------------------------------------------------ + Axiomatics WP Alt-Ergo Total Success + Axiomatic Inductive - - 1 0.0% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_tip/oracle_qualif/induction_prove.0.session/script/lemma_ByInd.json b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction_prove.0.session/script/lemma_ByInd.json new file mode 100644 index 00000000000..5a434573223 --- /dev/null +++ b/src/plugins/wp/tests/wp_tip/oracle_qualif/induction_prove.0.session/script/lemma_ByInd.json @@ -0,0 +1,13 @@ +[ { "prover": "script", "verdict": "valid", "time": 0.0549, "steps": 117 }, + { "header": "Induction", "tactic": "Wp.induction", + "params": { "base": 0, "hsup": true, "hinf": true }, + "select": { "select": "inside-goal", "occur": 0, "target": "(L_f x_0)", + "pattern": "L_f$x" }, + "children": { "Base": [ { "prover": "Alt-Ergo:2.2.0", "verdict": "valid", + "time": 0.0083, "steps": 6 } ], + "Induction (sup)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "valid", "time": 0.0549, + "steps": 117 } ], + "Induction (inf)": [ { "prover": "Alt-Ergo:2.2.0", + "verdict": "valid", "time": 0.0274, + "steps": 86 } ] } } ] -- GitLab