1 | % (c) 2009-2022 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | ||
5 | :- module(weakest_preconditions, [weakest_precondition/3,set_reference_machine/1,delete_reference_machine/0]). | |
6 | ||
7 | :- use_module(probsrc(module_information),[module_info/2]). | |
8 | :- module_info(group,cbc). | |
9 | :- module_info(description,'This module provides weakest precondition calculation for B and Event-B operations.'). | |
10 | ||
11 | :- use_module(library(lists)). | |
12 | :- use_module(library(sets), [subset/2]). | |
13 | ||
14 | :- use_module(probsrc(bsyntaxtree), [get_texpr_expr/2, | |
15 | get_texpr_id/2, get_texpr_ids/2, | |
16 | get_texpr_type/2, | |
17 | create_texpr/4, safe_create_texpr/3, | |
18 | create_forall/3, | |
19 | create_negation/2, | |
20 | conjunct_predicates/2, | |
21 | replace_id_by_expr/4, | |
22 | replace_ids_by_exprs/4, | |
23 | find_typed_identifier_uses/2, | |
24 | remove_all_infos/2]). | |
25 | :- use_module(probsrc(b_read_write_info), [get_accessed_vars/4]). | |
26 | :- use_module(probsrc(btypechecker), [prime_identifiers/2,prime_identifier/2, is_primed_id/1]). | |
27 | :- use_module(probsrc(error_manager), [add_error_fail/3]). | |
28 | :- use_module(probsrc(tools), [assert_once/1]). | |
29 | :- use_module(probsrc(bmachine), [b_get_machine_operation/4]). | |
30 | :- use_module(probsrc(bmachine_structure), [get_section/3]). | |
31 | ||
32 | :- use_module(probsrc(translate),[translate_substitution/2,print_subst/1]). | |
33 | :- use_module(probsrc(debug),[debug_mode/1]). | |
34 | ||
35 | % dynamic predicate may contain a machine in which operations are looked up | |
36 | % this is nessasary for the wp of operation calls, if the operation does not | |
37 | % belong to the currently loaded machine | |
38 | % (but for example to a refinement/abstraction) | |
39 | :- volatile machine/1. | |
40 | :- dynamic machine/1. | |
41 | ||
42 | set_reference_machine(M) :- | |
43 | delete_reference_machine, | |
44 | assert_once(machine(M)). | |
45 | delete_reference_machine :- | |
46 | retractall(machine(_)). | |
47 | ||
48 | % lookup operations in the reference machine if it exists | |
49 | % otherwise, lookup in loaded machine | |
50 | lookup_operation(Name,Results,Parameters,Body) :- | |
51 | ground(Name), % only for lookup of operations | |
52 | get_texpr_id(Name,op(OperationName)), | |
53 | (machine(M) | |
54 | -> get_section(operation_bodies,M,Ops), | |
55 | get_texpr_expr(Op,operation(TId,Results,Parameters,Body)), | |
56 | get_texpr_id(TId,op(Name)), | |
57 | member(Op,Ops) | |
58 | ; b_get_machine_operation(OperationName,Results,Parameters,Body)). | |
59 | ||
60 | %! weakest_precondition(+Action, +Pred,-WP) | |
61 | weakest_precondition(Action,Pred,WP) :- | |
62 | get_texpr_expr(Action,Expr), | |
63 | weakest_precondition2(Expr,Pred,WP). | |
64 | ||
65 | % any statement. see schneider p. 126 | |
66 | weakest_precondition2(any(Parameters,PreCond,Body),Pred,WP) :- !, | |
67 | weakest_precondition(Body,Pred,InnerWP), | |
68 | safe_create_texpr(implication(PreCond,InnerWP),pred,UnquantifiedWP), | |
69 | create_forall(Parameters,UnquantifiedWP,WP). | |
70 | weakest_precondition2(assign(TIds,Exprs),Pred,WP) :- !, | |
71 | function_assignment_to_overwrite(TIds,Exprs,TIds2,Exprs2), | |
72 | get_texpr_ids(TIds2,StrippedIDs), | |
73 | replace_ids_by_exprs(Pred,StrippedIDs,Exprs2,WP). | |
74 | weakest_precondition2(assign_single_id(TId,Expr),Pred,WP) :- !, | |
75 | function_assignment_to_overwrite([TId],[Expr],[TId2],[Expr2]), | |
76 | get_texpr_id(TId2,Id), | |
77 | replace_id_by_expr(Pred,Id,Expr2,WP). | |
78 | % nondeterministic choice. see schneider p. 126 | |
79 | weakest_precondition2(becomes_element_of(IDs,Set),Pred,WP) :- !, | |
80 | create_members(Set,IDs,MemberOf), | |
81 | safe_create_texpr(implication(MemberOf,Pred),pred,InnerWP), | |
82 | create_forall(IDs,InnerWP,WP). | |
83 | % nondet choice of action. see schneider p. 129 | |
84 | weakest_precondition2(choice(Choices),Pred,WP) :- !, | |
85 | choice_wps(Choices,Pred,WPs), | |
86 | conjunct_predicates(WPs,WP). | |
87 | weakest_precondition2(if(IfList),Pred,WP) :- !, | |
88 | if_wp(IfList,Pred,WP). | |
89 | % let statement: local variable with value. see schneider p. 125 | |
90 | weakest_precondition2(let(Parameters,Defs,Body),Pred,WP) :- !, | |
91 | weakest_precondition(Body,Pred,InnerWP), | |
92 | safe_create_texpr(implication(Defs,InnerWP),pred,UnquantifiedWP), | |
93 | create_forall(Parameters,UnquantifiedWP,WP). | |
94 | weakest_precondition2(operation_call(Operation,ResultsCall,ParametersCall),Pred,WP) :- !, | |
95 | lookup_operation(Operation,ResultsOperation,ParametersOperation,Body), | |
96 | % replace parameter/results identifiers | |
97 | append([ResultsOperation,ParametersOperation],IDs), append([ResultsCall,ParametersCall],Replacements), | |
98 | get_texpr_ids(IDs,StrippedIDs), | |
99 | replace_ids_by_exprs(Body,StrippedIDs,Replacements,NewBody), | |
100 | weakest_precondition(NewBody,Pred,WP). | |
101 | weakest_precondition2(parallel(Actions),Pred,WP) :- !, | |
102 | merge_parallels(Actions,Action), | |
103 | weakest_precondition(Action,Pred,WP). | |
104 | weakest_precondition2(precondition(Condition,Action),Pred,WP) :- !, | |
105 | weakest_precondition(Action,Pred,InnerWP), | |
106 | conjunct_predicates([Condition,InnerWP],WP). | |
107 | % weakest precondition for event-b events | |
108 | weakest_precondition2(rlevent(_Name,_Section,_Stat,Parameters,Guard,_Thm,Act,_VWit,_PWit,_Unmod,_AbsEvents),Pred,WP) :- !, | |
109 | (is_list(Act) | |
110 | -> create_texpr(parallel(Act),subst,[],NewAct) | |
111 | ; NewAct=Act | |
112 | ), | |
113 | weakest_precondition2(any(Parameters,Guard,NewAct),Pred,WP). | |
114 | % there are two kinds of selects: just whens and whens/else | |
115 | % the else case just collects all guards and negates them | |
116 | % see schneider p. 132 | |
117 | weakest_precondition2(select(Whens),Pred,WP) :- !, | |
118 | select_whens_wps(Whens,Pred,WPs), | |
119 | conjunct_predicates(WPs,WP). | |
120 | weakest_precondition2(select(Whens,Else),Pred,WP) :- !, | |
121 | select_whens_wps(Whens,Pred,WPs), | |
122 | conjunct_predicates(WPs,WhensWP), | |
123 | % collect all guards and negate them | |
124 | % => precondition for else-case | |
125 | findall(PC,(member(SW,Whens),get_texpr_expr(SW,select_when(PC,_))), AllPreconds), | |
126 | maplist(create_negation,AllPreconds,NegAllPreconds), | |
127 | conjunct_predicates(NegAllPreconds,ElsePrecond), | |
128 | % imply [T_n]S | |
129 | weakest_precondition(Else,Pred,InnerWP), | |
130 | safe_create_texpr(implication(ElsePrecond,InnerWP),pred,ElseWP), | |
131 | conjunct_predicates([WhensWP,ElseWP],WP). | |
132 | weakest_precondition2(sequence(Actions),Pred,WP) :- !, | |
133 | sequence_wp(Actions,Pred,WP). | |
134 | weakest_precondition2(skip,Pred,WP) :- !,WP=Pred. | |
135 | weakest_precondition2(var(IDs,Subst),Pred,WP) :- !, | |
136 | weakest_precondition(Subst,Pred,InnerWP), | |
137 | create_forall(IDs,InnerWP,WP). | |
138 | weakest_precondition2(while(P,S,I,V),R,WP) :- !, | |
139 | % [while P DO S INVARIANT I VARIANT V END] R | |
140 | % see abrial, b-book, p. 395: | |
141 | % there are five predicates. their conjunction is equal to the weakest precondition | |
142 | % first, create the list of all variables modified by the Stmt (used in forall) | |
143 | find_typed_identifier_uses(S,AllIdentifiersInStmt), | |
144 | get_accessed_vars(S,[],ModifiedVarsInStmt,_Reads), | |
145 | % the identifiers in ModifiedVarsInStmt are not typed. fetch the types from the list of all identifiers | |
146 | % TypedModifiedVarsInStmt = X | |
147 | get_texpr_ids(TypedModifiedVarsInStmt,ModifiedVarsInStmt), | |
148 | subset(TypedModifiedVarsInStmt,AllIdentifiersInStmt), | |
149 | ||
150 | ForAlls = TypedModifiedVarsInStmt, | |
151 | ||
152 | % first obligation: the invariant | |
153 | WP1 = I, | |
154 | ||
155 | % second obligation: forall x in X: (I & P => [S]I) | |
156 | conjunct_predicates([I,P],LHS2), | |
157 | weakest_precondition(S,I,RHS2), | |
158 | safe_create_texpr(implication(LHS2,RHS2),pred,Inner2), | |
159 | create_forall(ForAlls,Inner2,WP2), | |
160 | ||
161 | % third obligation: forall x in X: (I => v in N) | |
162 | safe_create_texpr(member(V,b(integer_set('NATURAL'),set(integer),[])),pred,RHS3), | |
163 | safe_create_texpr(implication(I,RHS3),pred,Inner3), | |
164 | create_forall(ForAlls,Inner3,WP3), | |
165 | ||
166 | % fourth obligation: forall x in X: I & P => [gamma:=V][S](V<gamma) | |
167 | create_texpr(identifier('gamma\''),integer,[],Gamma), | |
168 | create_texpr(assign_single_id(Gamma,V),subst,[],AssignVariantToGamma), | |
169 | safe_create_texpr(less(V,Gamma),pred,VLessGamma), | |
170 | weakest_precondition(S,VLessGamma,RHS4T), | |
171 | weakest_precondition(AssignVariantToGamma,RHS4T,RHS4), | |
172 | LHS4 = LHS2, | |
173 | safe_create_texpr(implication(LHS4,RHS4),pred,Inner4), | |
174 | create_forall(ForAlls,Inner4,WP4), | |
175 | ||
176 | % fifth obligation: forall x in X: (I & not P) => R | |
177 | create_negation(P,NotP), conjunct_predicates([I,NotP],LHS5), | |
178 | safe_create_texpr(implication(LHS5,R),pred,Inner5), | |
179 | create_forall(ForAlls,Inner5,WP5), | |
180 | ||
181 | % conjunct them all together | |
182 | conjunct_predicates([WP1,WP2,WP3,WP4,WP5],WP). | |
183 | weakest_precondition2(Action,Pred,_WeakestPrecondition) :- !, | |
184 | get_texpr_expr(Action,ActionExpr), | |
185 | add_error_fail(weakest_precondition,'weakest-precondition implementation missing: ',[ActionExpr,Pred]). | |
186 | ||
187 | choice_wps([],_Pred,[]). | |
188 | choice_wps([Choice|Choices],Pred,[ChoiceWP|WPs]) :- | |
189 | weakest_precondition(Choice,Pred,ChoiceWP), | |
190 | choice_wps(Choices,Pred,WPs). | |
191 | ||
192 | % see schneider p. 38 | |
193 | % [if E then S else T end] P = (E => [S]P) & (not E => [T]P) | |
194 | if_wp([IF|IFs],Pred,WP) :- | |
195 | get_texpr_expr(IF,if_elsif(Test,Body)), | |
196 | % if-part | |
197 | weakest_precondition(Body,Pred,InnerWP), | |
198 | safe_create_texpr(implication(Test,InnerWP),pred,IfWP), | |
199 | % if there are further else/elseif parts, create pos | |
200 | (if_wp(IFs,Pred,SubWP) | |
201 | -> create_negation(Test,NegTest), | |
202 | safe_create_texpr(implication(NegTest,SubWP),pred,ElseWP), | |
203 | conjunct_predicates([IfWP,ElseWP],WP) | |
204 | ; WP = IfWP). | |
205 | ||
206 | merge_parallels(ParallelAssigns,Result) :- | |
207 | create_texpr(truth,pred,[],True), | |
208 | create_texpr(parallel([]),subst,[],Body), | |
209 | EmptyAny = any([],True,Body), | |
210 | EmptyAssign = assign([],[]), | |
211 | merge_parallels1(ParallelAssigns,EmptyAssign,EmptyAny,Assigns,Anys,Rest), | |
212 | (Rest == [] -> | |
213 | merge_assigns_and_anys(Assigns,Anys,Res), | |
214 | create_texpr(Res,subst,[],Result), | |
215 | (debug_mode(on) -> print_subst(Result),nl; true) | |
216 | ; | |
217 | add_error_fail(merge_parallels, 'merging of parallel executions failed (implementation missing?):', Rest) | |
218 | ). | |
219 | ||
220 | merge_parallels1([],Assigns,Anys,Assigns,Anys,[]). | |
221 | merge_parallels1([Subst|Substs],assign(IDs2,Exprs2),any(Par2,PreCond2,Body2),ResAssigns,ResAnys,Rest) :- | |
222 | get_texpr_expr(Subst,Expr), | |
223 | merge_parallels_normalize(Expr,ExprN), | |
224 | (ExprN = assign(IDs1,Exprs1) -> | |
225 | append(IDs2,IDs1,IDs3), | |
226 | append(Exprs2,Exprs1,Exprs3), | |
227 | NewAssign = assign(IDs3,Exprs3), | |
228 | NewAny = any(Par2,PreCond2,Body2), | |
229 | Rest = Rest1 | |
230 | ; ExprN = any(Par1,PreCond1,Body1) -> | |
231 | append(Par2,Par1,Par3), | |
232 | conjunct_predicates([PreCond2,PreCond1],PreCond3), | |
233 | get_texpr_expr(Body2,parallel(AnySubsts2)), | |
234 | create_texpr(parallel([Body1|AnySubsts2]),subst,[],Body3), | |
235 | NewAny = any(Par3,PreCond3,Body3), | |
236 | NewAssign = assign(IDs2,Exprs2), | |
237 | Rest = Rest1 | |
238 | ; ExprN = skip -> | |
239 | NewAssign = assign(IDs2,Exprs2), | |
240 | NewAny = any(Par2,PreCond2,Body2), | |
241 | Rest = Rest1 | |
242 | ; | |
243 | NewAssign = assign(IDs2,Exprs2), | |
244 | NewAny = any(Par2,PreCond2,Body2), | |
245 | Rest = [Subst|Rest1] | |
246 | ), !, % do not backtrack once the a substitution is merged to the assignment or any generator | |
247 | merge_parallels1(Substs,NewAssign,NewAny,ResAssigns,ResAnys,Rest1). | |
248 | ||
249 | merge_assigns_and_anys(Assign,Any,Result) :- | |
250 | (empty_any_statement(Any),empty_assign_statement(Assign) -> | |
251 | add_error_fail(merge_assigns_and_anys,'merging of parallel executions failed (empty assignment and any statements): ', (Assign,Any)) | |
252 | ; empty_any_statement(Any) -> | |
253 | Result = Assign | |
254 | ; empty_assign_statement(Assign) -> | |
255 | Result = Any | |
256 | ; | |
257 | Any = any(Par,PreCond,Body), | |
258 | get_texpr_expr(Body,parallel(Actions)), | |
259 | create_texpr(Assign,subst,[],BAssign), | |
260 | create_texpr(parallel([BAssign|Actions]),subst,[],NewBody), | |
261 | Result = any(Par,PreCond,NewBody) | |
262 | ). | |
263 | ||
264 | empty_any_statement(any([],_Precond,b(parallel([]),subst,_))). | |
265 | ||
266 | empty_assign_statement(assign([],[])). | |
267 | ||
268 | % The old implementation for merging parallel assignments | |
269 | ||
270 | %% merge_parallels([X],X) :- !. | |
271 | %% merge_parallels([A1,A2|As],X) :- | |
272 | %% merge_parallels2(A1,A2,AMerged), !, | |
273 | %% merge_parallels([AMerged|As],X). | |
274 | %% merge_parallels([A1,A2|As],X) :- | |
275 | %% merge_parallels2(A2,A1,AMerged), !, | |
276 | %% merge_parallels([AMerged|As],X). | |
277 | %% merge_parallels([A1,A2|_As],_X) :- | |
278 | %% get_texpr_expr(A1,A1E), get_texpr_expr(A2,A2E), | |
279 | %% add_error_fail(merge_parallels,'merging of parallel executions failed (implementation missing?): ',(A1E,A2E)). | |
280 | ||
281 | %% merge_parallels2(A1,A2,AMerged) :- | |
282 | %% get_texpr_expr(A1,A1Expr), get_texpr_expr(A2,A2Expr), | |
283 | %% merge_parallels_normalize(A1Expr,A1ExprN), | |
284 | %% merge_parallels_normalize(A2Expr,A2ExprN), | |
285 | %% merge_parallels3(A1ExprN,A2ExprN,AMergedExpr), | |
286 | %% create_texpr(AMergedExpr,subst,[],AMerged). | |
287 | ||
288 | %% merge_parallels3(assign(IDs1,Exprs1),assign(IDs2,Exprs2),assign(IDs3,Exprs3)) :- | |
289 | %% append(IDs1,IDs2,IDs3), append(Exprs1,Exprs2,Exprs3). | |
290 | %% merge_parallels3(any(Parameters,PreCond,Body),SomeSubstitution,any(Parameters,PreCond,NewBody)) :- | |
291 | %% create_texpr(SomeSubstitution,subst,[],SomeSubstitutionTExpr), | |
292 | %% create_texpr(parallel([Body,SomeSubstitutionTExpr]),subst,[],NewBody). | |
293 | % in case we have more than two substitutions in a parallel block (see for example B/Ivo/WeakestPreconditon/Tickets/MergeParallels.mch WP: [Evt1](G(Evt2))) | |
294 | ||
295 | merge_parallels_normalize(assign_single_id(ID,Expr),assign([ID],[Expr])) :- !. | |
296 | merge_parallels_normalize(becomes_element_of(IDs,Set), any(Parameters,PreCond,Body)) :- !, | |
297 | prime_identifiers(IDs,Parameters), create_members(Set,Parameters,PreCond), | |
298 | create_texpr(assign(IDs,Parameters),subst,[],Body). | |
299 | merge_parallels_normalize(becomes_such(Vars,Condition),any(PrimedIDs,Condition,Body)) :- !, | |
300 | find_typed_identifier_uses(Condition,UsedIDs), | |
301 | get_primed_ids(UsedIDs,PrimedIDs), % TO DO: check if we can reuse find_read_vars_for_becomes_such in b_read_write_info.pl | |
302 | get_non_represented_vars(Vars,PrimedIDs,NonRepresented), | |
303 | (NonRepresented\=[] -> | |
304 | get_texpr_ids(NonRepresented,VarsIDs), | |
305 | create_texpr(becomes_such(Vars,Condition),subst,[],TExpr), | |
306 | translate_substitution(TExpr,Subst), | |
307 | add_error_fail(merge_parallels_normalize,'Normalising becomes_such(...) costruct failed because the following variables were not reprsented by prime variables: ', (VarsIDs,Subst)) | |
308 | ; | |
309 | create_texpr(assign(Vars,PrimedIDs),subst,[],Body) | |
310 | ). | |
311 | % Functional overrides in Event-B will be translated to assignments using the override operator (<+) | |
312 | merge_parallels_normalize(X,X). | |
313 | ||
314 | get_non_represented_vars([],_PrimedVars,[]). | |
315 | get_non_represented_vars([Var|Vars],PrimedVars,NonRepresented) :- | |
316 | prime_identifier(Var,VarPrimed), | |
317 | remove_all_infos(VarPrimed,VarPrimedNoInfos), | |
318 | (member(VarPrimedNoInfos,PrimedVars) -> | |
319 | NonRepresented = NonRepresented1 | |
320 | ; | |
321 | NonRepresented = [Var|NonRepresented1] | |
322 | ), | |
323 | get_non_represented_vars(Vars,PrimedVars,NonRepresented1). | |
324 | ||
325 | get_primed_ids([],[]). | |
326 | get_primed_ids([ID|IDs],[ID|Rest]) :- | |
327 | get_texpr_id(ID,I), | |
328 | is_primed_id(I),!, | |
329 | get_primed_ids(IDs,Rest). | |
330 | get_primed_ids([_ID|IDs],Rest) :- | |
331 | get_primed_ids(IDs,Rest). | |
332 | ||
333 | sequence_wp(Actions,Pred,WP) :- | |
334 | reverse(Actions,ReversedActions), | |
335 | sequence_wp2(ReversedActions,Pred,WP). | |
336 | sequence_wp2([],Pred,Pred). | |
337 | sequence_wp2([Action|Actions],PredIn,PredOut) :- | |
338 | weakest_precondition(Action,PredIn,PredT), | |
339 | sequence_wp2(Actions,PredT,PredOut). | |
340 | ||
341 | select_whens_wps([],_Pred,[]). | |
342 | select_whens_wps([When|Whens],Pred,[WP|WPs]) :- | |
343 | get_texpr_expr(When,select_when(PreCond, Body)), | |
344 | weakest_precondition(Body,Pred,InnerWP), | |
345 | safe_create_texpr(implication(PreCond,InnerWP),pred,WP), | |
346 | select_whens_wps(Whens,Pred,WPs). | |
347 | ||
348 | create_members(Set,IDs,MemberOf) :- | |
349 | maplist(create_member(Set),IDs,Preds), | |
350 | conjunct_predicates(Preds,MemberOf). | |
351 | create_member(Set,ID,Res) :- | |
352 | safe_create_texpr(member(ID,Set),pred,Res). | |
353 | ||
354 | %create_renaming(I1,I2,rename(I1,I2)). | |
355 | ||
356 | function_assignment_to_overwrite([],[],[],[]). | |
357 | function_assignment_to_overwrite([b(identifier(ID),Type,Infos)|IdOrFuns],[Expr|Exprs],[b(identifier(ID),Type,Infos)|IdOuts],[Expr|ExprOuts]) :- | |
358 | function_assignment_to_overwrite(IdOrFuns,Exprs,IdOuts,ExprOuts). | |
359 | function_assignment_to_overwrite([b(function(Function,Argument),_Type,_Infos)|IdOrFuns],[Expr|Exprs],[Function|IdOuts],[ExprOut|ExprOuts]) :- | |
360 | get_texpr_type(Argument,ArgumentType), get_texpr_type(Expr,ExprType), | |
361 | get_texpr_type(Function,FunctionType), | |
362 | CoupleType = couple(ArgumentType,ExprType), | |
363 | safe_create_texpr(couple(Argument,Expr),CoupleType,OverwriteCouple), | |
364 | safe_create_texpr(set_extension([OverwriteCouple]),set(CoupleType),CoupleInSet), | |
365 | safe_create_texpr(overwrite(Function,CoupleInSet),FunctionType,ExprOut), | |
366 | function_assignment_to_overwrite(IdOrFuns,Exprs,IdOuts,ExprOuts). |