123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* 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 LICENSE). *)(* *)(**************************************************************************)(** This module presents the implementation of a simple example analyzer for
{{!While_ast}the while language}. It is detailed in
the While tutorial's {!page-chapter3}. *)moduleVar=While_ast.VarmoduleLog=Tracelog.Make(structletcategory="IntvAnalysis"end)(* $MDX part-begin=sva *)moduleSVA_Ival=Single_value_abstraction.IvalmoduleSVA_Bval=Single_value_abstraction.Quadrivalent(* $MDX part-end *)(* $MDX part-begin=sva_state *)moduleState=PatriciaTree.MakeMap(Var)typesva_state=SVA_Ival.integerState.t(* $MDX part-end *)letmap_ppppfmtstore=Format.fprintffmt"{@[<hov>%a@]}"(State.pretty~pp_sep:(funfmt()->Format.fprintffmt";@ ")(funfmtkeyvalue->Format.fprintffmt"@[%a -> %a@]"Var.ppkeyppvalue))storeletstate_pp=map_ppFramac_ival.Ival.pretty(* $MDX part-begin=initial_state *)letinitial_state()=State.empty(* $MDX part-end *)(* $MDX part-begin=expression_sva *)letrecexpression_sva:sva_state->While_ast.aexp->SVA_Ival.integer=funstateexp->letopenWhile_astinmatchexpwith|Varv->State.findvstate|Intc->SVA_Ival.Integer_Forward.iconst(Z.of_intc)|Add(e1,e2)->SVA_Ival.Integer_Forward.iadd(expression_svastatee1)(expression_svastatee2)|Sub(e1,e2)->SVA_Ival.Integer_Forward.isub(expression_svastatee1)(expression_svastatee2)|Mul(e1,e2)->SVA_Ival.Integer_Forward.imul(expression_svastatee1)(expression_svastatee2)(* $MDX part-end *)(* $MDX part-begin=bexpression_sva *)letrecbexpression_sva:sva_state->While_ast.bexp->SVA_Bval.boolean=funstateexp->letopenWhile_astinmatchexpwith|True->SVA_Bval.Boolean_Forward.true_|False->SVA_Bval.Boolean_Forward.false_|Le(e1,e2)->SVA_Ival.Integer_Forward.ile(expression_svastatee1)(expression_svastatee2)|Eq(e1,e2)->SVA_Ival.Integer_Forward.ieq(expression_svastatee1)(expression_svastatee2)|Note1->SVA_Bval.Boolean_Forward.not(bexpression_svastatee1)|And(e1,e2)->SVA_Bval.Boolean_Forward.(&&)(bexpression_svastatee1)(bexpression_svastatee2)|Gt(e1,e2)->SVA_Bval.Boolean_Forward.not@@SVA_Ival.Integer_Forward.ile(expression_svastatee1)(expression_svastatee2)(* $MDX part-end *)(* $MDX part-begin=join *)letjoins1s2=State.idempotent_inter(fun_v1v2->SVA_Ival.Integer_Lattice.joinv1v2)s1s2letwidens1s2=State.idempotent_inter(fun_v1v2->SVA_Ival.Integer_Lattice.widen~previous:v1v2)s1s2(* $MDX part-end *)(* $MDX part-begin=inter *)letinters1s2=State.idempotent_union(fun_v1v2->SVA_Ival.Integer_Lattice.interv1v2)s1s2(* $MDX part-end *)(* $MDX part-begin=includes *)letincludes:sva_state->sva_state->bool=funlr->State.reflexive_subset_domain_for_all2(fun_ab->SVA_Ival.Integer_Lattice.includesab)lr(* $MDX part-end *)(* $MDX part-begin=command_sva *)letrecanalyze_stmt:sva_state->While_ast.stmt->sva_state=funstatestmt->letopenWhile_astinLog.trace(funp->p"Statement: %a"While_ast.pp_stmtstmt)~pp_ret:state_pp@@fun()->matchstmtwith|Skip->state|Assign(var,exp)->letv=expression_svastateexpinState.addvarvstate|Seq(c1,c2)->letstate'=analyze_stmtstatec1inanalyze_stmtstate'c2|If(cond,if_true,if_false)->beginmatchbexpression_svastatecondwith|True->analyze_stmtstateif_true|False->analyze_stmtstateif_false|Bottom->state(* Should be unreachable *)|Top->(* analyze both, then join the results *)lettrue_state=analyze_stmtstateif_trueinletfalse_state=analyze_stmtstateif_falseinjointrue_statefalse_stateend|While(cond,body)->beginmatchbexpression_svastatecondwith|False->state(* no need to execute the body *)|Bottom->state(* Should be unreachable *)|Top|True->letnext=analyze_stmtstatebodyinifincludesstatenextthenstate(* fixpoint reached *)elseanalyze_stmt(widenstate(joinstatenext))stmtend(* $MDX part-end *)(* $MDX part-begin=refine_aexp *)letrecrefine_aexp:sva_state->While_ast.aexp->SVA_Ival.integer->sva_state=funstateexpresult->matchexpwith|Varv->State.addv(SVA_Ival.Integer_Lattice.interresult(State.findvstate))state|Intc->state|Add(e1,e2)->let(v1,v2)=SVA_Ival.Integer_Backward.iadd(expression_svastatee1)(expression_svastatee2)resultinletstate=matchv1withSomev->refine_aexpstatee1v|None->stateinletstate=matchv2withSomev->refine_aexpstatee2v|None->stateinstate|Sub(e1,e2)->let(v1,v2)=SVA_Ival.Integer_Backward.isub(expression_svastatee1)(expression_svastatee2)resultinletstate=matchv1withSomev->refine_aexpstatee1v|None->stateinletstate=matchv2withSomev->refine_aexpstatee2v|None->stateinstate|Mul(e1,e2)->let(v1,v2)=SVA_Ival.Integer_Backward.imul(expression_svastatee1)(expression_svastatee2)resultinletstate=matchv1withSomev->refine_aexpstatee1v|None->stateinletstate=matchv2withSomev->refine_aexpstatee2v|None->stateinstate(* $MDX part-end *)(* $MDX part-begin=refine_bexp *)letrecrefine_bexp:sva_state->While_ast.bexp->SVA_Bval.boolean->sva_state=funstateexpresult->matchexpwith|True|False->state|Note1->letv=SVA_Bval.Boolean_Backward.not(bexpression_svastatee1)resultinbeginmatchvwithSomev->refine_bexpstatee1v|None->stateend|Le(e1,e2)->let(v1,v2)=SVA_Ival.Integer_Backward.ile(expression_svastatee1)(expression_svastatee2)resultinletstate=matchv1withSomev->refine_aexpstatee1v|None->stateinletstate=matchv2withSomev->refine_aexpstatee2v|None->stateinstate|Eq(e1,e2)->let(v1,v2)=SVA_Ival.Integer_Backward.ieq(expression_svastatee1)(expression_svastatee2)resultinletstate=matchv1withSomev->refine_aexpstatee1v|None->stateinletstate=matchv2withSomev->refine_aexpstatee2v|None->stateinstate|And(e1,e2)->let(v1,v2)=SVA_Bval.Boolean_Backward.(&&)(bexpression_svastatee1)(bexpression_svastatee2)resultinletstate=matchv1withSomev->refine_bexpstatee1v|None->stateinletstate=matchv2withSomev->refine_bexpstatee2v|None->stateinstate|Gt(e1,e2)->refine_bexpstate(Not(Le(e1,e2)))result(* $MDX part-end *)letrecanalyze_stmt_refine:sva_state->While_ast.stmt->sva_state=funstatestmt->letopenWhile_astinLog.trace(funp->p"Statement: %a"While_ast.pp_stmtstmt)~pp_ret:state_pp@@fun()->matchstmtwith|Skip->state|Assign(var,exp)->letv=expression_svastateexpinState.addvarvstate|Seq(c1,c2)->letstate'=analyze_stmt_refinestatec1inanalyze_stmt_refinestate'c2(* $MDX part-begin=command_sva_refine *)|If(cond,if_true,if_false)->beginmatchbexpression_svastatecondwith(* no refinement possible when the value is known *)|True->analyze_stmt_refinestateif_true|False->analyze_stmt_refinestateif_false|Bottom->state(* Should be unreachable *)|Top->(* analyze both, then join the results *)lettrue_state=analyze_stmt_refine(refine_bexpstatecondTrue)if_trueinletfalse_state=analyze_stmt_refine(refine_bexpstatecondFalse)if_falseinjointrue_statefalse_stateend|While(cond,body)->letcond_value=bexpression_svastatecondinbeginmatchcond_valuewith|False->state(* no need to execute the body *)|Bottom->state(* Should be unreachable *)|Top|True->letrefined_state=ifcond_value==Topthenrefine_bexpstatecondTrueelsestateinletnext=analyze_stmt_refinerefined_statebodyinifincludesstatenextthenrefine_bexpstatecondFalse(* at the loop exit, the condition is false *)elseanalyze_stmt_refine(widenstate(joinstatenext))stmtend(* $MDX part-end *)