(***************************************************************************** * Featherweight-OCL --- A Formal Semantics for UML-OCL Version OCL 2.5 * for the OMG Standard. * http://www.brucker.ch/projects/hol-testgen/ * * Analysis_OCL.thy --- OCL Contracts and an Example. * This file is part of HOL-TestGen. * * Copyright (c) 2012-2015 Université Paris-Saclay, Univ. Paris-Sud, France * 2013-2015 IRT SystemX, France * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * * Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************) theory Analysis_OCL imports Analysis_UML begin text \\label{ex:employee-analysis:ocl}\ section\OCL Part: Invariant\ text\These recursive predicates can be defined conservatively by greatest fix-point constructions---automatically. See~\cite{brucker.ea:hol-ocl-book:2006,brucker:interactive:2007} for details. For the purpose of this example, we state them as axioms here. \begin{ocl} context Person inv label : self .boss <> null implies (self .salary \ ((self .boss) .salary)) \end{ocl} \ definition Person_label\<^sub>i\<^sub>n\<^sub>v :: "Person \ Boolean" where "Person_label\<^sub>i\<^sub>n\<^sub>v (self) \ (self .boss <> null implies (self .salary \\<^sub>i\<^sub>n\<^sub>t ((self .boss) .salary)))" definition Person_label\<^sub>i\<^sub>n\<^sub>v\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e :: "Person \ Boolean" where "Person_label\<^sub>i\<^sub>n\<^sub>v\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e (self) \ (self .boss@pre <> null implies (self .salary@pre \\<^sub>i\<^sub>n\<^sub>t ((self .boss@pre) .salary@pre)))" definition Person_label\<^sub>g\<^sub>l\<^sub>o\<^sub>b\<^sub>a\<^sub>l\<^sub>i\<^sub>n\<^sub>v :: "Boolean" where "Person_label\<^sub>g\<^sub>l\<^sub>o\<^sub>b\<^sub>a\<^sub>l\<^sub>i\<^sub>n\<^sub>v \ (Person .allInstances()->forAll\<^sub>S\<^sub>e\<^sub>t(x | Person_label\<^sub>i\<^sub>n\<^sub>v (x)) and (Person .allInstances@pre()->forAll\<^sub>S\<^sub>e\<^sub>t(x | Person_label\<^sub>i\<^sub>n\<^sub>v\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e (x))))" lemma "\ \ \ (X .boss) \ \ \ Person .allInstances()->includes\<^sub>S\<^sub>e\<^sub>t(X .boss) \ \ \ Person .allInstances()->includes\<^sub>S\<^sub>e\<^sub>t(X) " oops (* To be generated generically ... hard, but crucial lemma that should hold. It means that X and it successor are object representation that actually occur in the state. *) lemma REC_pre : "\ \ Person_label\<^sub>g\<^sub>l\<^sub>o\<^sub>b\<^sub>a\<^sub>l\<^sub>i\<^sub>n\<^sub>v \ \ \ Person .allInstances()->includes\<^sub>S\<^sub>e\<^sub>t(X) \ \\X\ represented object in state\ \ \ REC. \ \ REC(X) \ (Person_label\<^sub>i\<^sub>n\<^sub>v (X) and (X .boss <> null implies REC(X .boss)))" oops (* Attempt to allegiate the burden of he following axiomatizations: could be a witness for a constant specification ...*) text\This allows to state a predicate:\ axiomatization inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l :: "Person \ Boolean" where inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l_def: "(\ \ Person .allInstances()->includes\<^sub>S\<^sub>e\<^sub>t(self)) \ (\ \ (inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l(self) \ (self .boss <> null implies (self .salary \\<^sub>i\<^sub>n\<^sub>t ((self .boss) .salary)) and inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l(self .boss))))" axiomatization inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e :: "Person \ Boolean" where inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e_def: "(\ \ Person .allInstances@pre()->includes\<^sub>S\<^sub>e\<^sub>t(self)) \ (\ \ (inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e(self) \ (self .boss@pre <> null implies (self .salary@pre \\<^sub>i\<^sub>n\<^sub>t ((self .boss@pre) .salary@pre)) and inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e(self .boss@pre))))" lemma inv_1 : "(\ \ Person .allInstances()->includes\<^sub>S\<^sub>e\<^sub>t(self)) \ (\ \ inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l(self) = ((\ \ (self .boss \ null)) \ ( \ \ (self .boss <> null) \ \ \ ((self .salary) \\<^sub>i\<^sub>n\<^sub>t (self .boss .salary)) \ \ \ (inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l(self .boss))))) " oops (* Let's hope that this holds ... *) lemma inv_2 : "(\ \ Person .allInstances@pre()->includes\<^sub>S\<^sub>e\<^sub>t(self)) \ (\ \ inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e(self)) = ((\ \ (self .boss@pre \ null)) \ (\ \ (self .boss@pre <> null) \ (\ \ (self .boss@pre .salary@pre \\<^sub>i\<^sub>n\<^sub>t self .salary@pre)) \ (\ \ (inv\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\<^sub>_\<^sub>l\<^sub>a\<^sub>b\<^sub>e\<^sub>l\<^sub>A\<^sub>T\<^sub>p\<^sub>r\<^sub>e(self .boss@pre)))))" oops (* Let's hope that this holds ... *) text\A very first attempt to characterize the axiomatization by an inductive definition - this can not be the last word since too weak (should be equality!)\ coinductive inv :: "Person \ (\)st \ bool" where "(\ \ (\ self)) \ ((\ \ (self .boss \ null)) \ (\ \ (self .boss <> null) \ (\ \ (self .boss .salary \\<^sub>i\<^sub>n\<^sub>t self .salary)) \ ( (inv(self .boss))\ ))) \ ( inv self \)" section\OCL Part: The Contract of a Recursive Query\ text\The original specification of a recursive query : \begin{ocl} context Person::contents():Set(Integer) pre: true post: result = if self.boss = null then Set{i} else self.boss.contents()->including(i) endif \end{ocl}\ text\For the case of recursive queries, we use at present just axiomatizations:\ axiomatization contents :: "Person \ Set_Integer" ("(1(_).contents'('))" 50) where contents_def: "(self .contents()) = (\ \. SOME res. let res = \ _. res in if \ \ (\ self) then ((\ \ true) \ (\ \ res \ if (self .boss \ null) then (Set{self .salary}) else (self .boss .contents() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary)) endif)) else \ \ res \ invalid)" and cp0_contents:"(X .contents()) \ = ((\_. X \) .contents()) \" interpretation contents : contract0 "contents" "\ self. true" "\ self res. res \ if (self .boss \ null) then (Set{self .salary}) else (self .boss .contents() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary)) endif" proof (unfold_locales) show "\self \. true \ = true \" by auto next show "\self. \\ \' \''. ((\, \') \ true) = ((\, \'') \ true)" by auto next show "\self. self .contents() \ \ \. SOME res. let res = \ _. res in if \ \ (\ self) then ((\ \ true) \ (\ \ res \ if (self .boss \ null) then (Set{self .salary}) else (self .boss .contents() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary)) endif)) else \ \ res \ invalid" by(auto simp: contents_def ) next have A:"\self \. ((\_. self \) .boss \ null) \ = (\_. (self .boss \ null) \) \" by (metis (no_types) StrictRefEq\<^sub>O\<^sub>b\<^sub>j\<^sub>e\<^sub>c\<^sub>t_\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n cp_StrictRefEq\<^sub>O\<^sub>b\<^sub>j\<^sub>e\<^sub>c\<^sub>t cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\) have B:"\self \. (\_. Set{(\_. self \) .salary} \) = (\_. Set{self .salary} \)" apply(subst UML_Set.OclIncluding.cp0) apply(subst (2) UML_Set.OclIncluding.cp0) apply(subst (2) Analysis_UML.cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\\\) by simp have C:"\self \. ((\_. self \).boss .contents()->including\<^sub>S\<^sub>e\<^sub>t((\_. self \).salary) \) = (self .boss .contents() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary) \)" apply(subst UML_Set.OclIncluding.cp0) apply(subst (2) UML_Set.OclIncluding.cp0) apply(subst (2) Analysis_UML.cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\\\) apply(subst cp0_contents) apply(subst (2) cp0_contents) apply(subst (2) cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\) by simp show "\self res \. (res \ if (self .boss) \ null then Set{self .salary} else self .boss .contents()->including\<^sub>S\<^sub>e\<^sub>t(self .salary) endif) \ = ((\_. res \) \ if (\_. self \) .boss \ null then Set{(\_. self \) .salary} else(\_. self \) .boss .contents()->including\<^sub>S\<^sub>e\<^sub>t((\_. self \) .salary) endif) \" apply(subst cp_StrongEq) apply(subst (2) cp_StrongEq) apply(subst cp_OclIf) apply(subst (2)cp_OclIf) by(simp add: A B C) qed text\Specializing @{thm contents.unfold2}, one gets the following more practical rewrite rule that is amenable to symbolic evaluation:\ theorem unfold_contents : assumes "cp E" and "\ \ \ self" shows "(\ \ E (self .contents())) = (\ \ E (if self .boss \ null then Set{self .salary} else self .boss .contents()->including\<^sub>S\<^sub>e\<^sub>t(self .salary) endif))" by(rule contents.unfold2[of _ _ _ "\ X. true"], simp_all add: assms) text\Since we have only one interpretation function, we need the corresponding operation on the pre-state:\ consts contentsATpre :: "Person \ Set_Integer" ("(1(_).contents@pre'('))" 50) axiomatization where contentsATpre_def: " (self).contents@pre() = (\ \. SOME res. let res = \ _. res in if \ \ (\ self) then ((\ \ true) \ \ \pre\ (\ \ (res \ if (self).boss@pre \ null \ \post\ then Set{(self).salary@pre} else (self).boss@pre .contents@pre() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre) endif))) else \ \ res \ invalid)" and cp0_contents_at_pre:"(X .contents@pre()) \ = ((\_. X \) .contents@pre()) \" interpretation contentsATpre : contract0 "contentsATpre" "\ self. true" "\ self res. res \ if (self .boss@pre \ null) then (Set{self .salary@pre}) else (self .boss@pre .contents@pre() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre)) endif" proof (unfold_locales) show "\self \. true \ = true \" by auto next show "\self. \\ \' \''. ((\, \') \ true) = ((\, \'') \ true)" by auto next show "\self. self .contents@pre() \ \\. SOME res. let res = \ _. res in if \ \ \ self then \ \ true \ \ \ res \ (if self .boss@pre \ null then Set{self .salary@pre} else self .boss@pre .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre) endif) else \ \ res \ invalid" by(auto simp: contentsATpre_def) next have A:"\self \. ((\_. self \) .boss@pre \ null) \ = (\_. (self .boss@pre \ null) \) \" by (metis StrictRefEq\<^sub>O\<^sub>b\<^sub>j\<^sub>e\<^sub>c\<^sub>t_\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n cp_StrictRefEq\<^sub>O\<^sub>b\<^sub>j\<^sub>e\<^sub>c\<^sub>t cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\_at_pre) have B:"\self \. (\_. Set{(\_. self \) .salary@pre} \) = (\_. Set{self .salary@pre} \)" apply(subst UML_Set.OclIncluding.cp0) apply(subst (2) UML_Set.OclIncluding.cp0) apply(subst (2) Analysis_UML.cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\\\_at_pre) by simp have C:"\self \. ((\_. self \).boss@pre .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t((\_. self \).salary@pre) \) = (self .boss@pre .contents@pre() ->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre) \)" apply(subst UML_Set.OclIncluding.cp0) apply(subst (2) UML_Set.OclIncluding.cp0) apply(subst (2) Analysis_UML.cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\\\_at_pre) apply(subst cp0_contents_at_pre) apply(subst (2) cp0_contents_at_pre) apply(subst (2) cp_dot\<^sub>P\<^sub>e\<^sub>r\<^sub>s\<^sub>o\<^sub>n\\\\_at_pre) by simp show "\self res \. (res \ if (self .boss@pre) \ null then Set{self .salary@pre} else self .boss@pre .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre) endif) \ = ((\_. res \) \ if (\_. self \) .boss@pre \ null then Set{(\_. self \) .salary@pre} else(\_. self \) .boss@pre .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t((\_. self \) .salary@pre) endif) \" apply(subst cp_StrongEq) apply(subst (2) cp_StrongEq) apply(subst cp_OclIf) apply(subst (2)cp_OclIf) by(simp add: A B C) qed text\Again, we derive via @{thm [source] contents.unfold2} a Knaster-Tarski like Fixpoint rule that is amenable to symbolic evaluation:\ theorem unfold_contentsATpre : assumes "cp E" and "\ \ \ self" shows "(\ \ E (self .contents@pre())) = (\ \ E (if self .boss@pre \ null then Set{self .salary@pre} else self .boss@pre .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t(self .salary@pre) endif))" by(rule contentsATpre.unfold2[of _ _ _ "\ X. true"], simp_all add: assms) text\Note that these \inlineocl{@pre} variants on methods are only available on queries, \ie, operations without side-effect.\ section\OCL Part: The Contract of a User-defined Method\ text\ The example specification in high-level OCL input syntax reads as follows: \begin{ocl} context Person::insert(x:Integer) pre: true post: contents():Set(Integer) contents() = contents@pre()->including(x) \end{ocl} This boils down to: \ definition insert :: "Person \Integer \ Void" ("(1(_).insert'(_'))" 50) where "self .insert(x) \ (\ \. SOME res. let res = \ _. res in if (\ \ (\ self)) \ (\ \ \ x) then (\ \ true \ (\ \ ((self).contents() \ (self).contents@pre()->including\<^sub>S\<^sub>e\<^sub>t(x)))) else \ \ res \ invalid)" text\The semantic consequences of this definition were computed inside this locale interpretation:\ interpretation insert : contract1 "insert" "\ self x. true" "\ self x res. ((self .contents()) \ (self .contents@pre()->including\<^sub>S\<^sub>e\<^sub>t(x)))" apply unfold_locales apply(auto simp:insert_def) apply(subst cp_StrongEq) apply(subst (2) cp_StrongEq) apply(subst contents.cp0) apply(subst UML_Set.OclIncluding.cp0) apply(subst (2) UML_Set.OclIncluding.cp0) apply(subst contentsATpre.cp0) by(simp) (* an extremely hacky proof that cries for reformulation and automation - bu *) text\The result of this locale interpretation for our @{term insert} contract is the following set of properties, which serves as basis for automated deduction on them: \begin{table}[htbp] \centering \begin{tabu}{lX[,c,]} \toprule Name & Theorem \\ \midrule @{thm [source] insert.strict0} & @{thm [display=false] insert.strict0} \\ @{thm [source] insert.nullstrict0} & @{thm [display=false] insert.nullstrict0} \\ @{thm [source] insert.strict1} & @{thm [display=false] insert.strict1} \\ @{thm [source] insert.cp\<^sub>P\<^sub>R\<^sub>E} & @{thm [display=false] insert.cp\<^sub>P\<^sub>R\<^sub>E} \\ @{thm [source] insert.cp\<^sub>P\<^sub>O\<^sub>S\<^sub>T} & @{thm [display=false] insert.cp\<^sub>P\<^sub>O\<^sub>S\<^sub>T} \\ @{thm [source] insert.cp_pre} & @{thm [display=false] insert.cp_pre} \\ @{thm [source] insert.cp_post} & @{thm [display=false] insert.cp_post} \\ @{thm [source] insert.cp} & @{thm [display=false] insert.cp} \\ @{thm [source] insert.cp0} & @{thm [display=false] insert.cp0} \\ @{thm [source] insert.def_scheme} & @{thm [display=false] insert.def_scheme} \\ @{thm [source] insert.unfold} & @{thm [display=false] insert.unfold} \\ @{thm [source] insert.unfold2} & @{thm [display=false] insert.unfold2} \\ \bottomrule \end{tabu} \caption{Semantic properties resulting from a user-defined operation contract.} \label{tab:sem_operation_contract} \end{table} \ end