1 % (c) 2009-2026 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(bmachine_construction,[reset_bmachine_construction/0,
6 check_machine/4,
7 type_in_machine_l/7,
8 type_open_predicate_with_quantifier/6,
9 get_constructed_machine_name/2, get_constructed_machine_name_and_filenumber/3,
10 type_open_formula/8,
11 create_scope/6, % create type-checking scope
12 filter_linking_invariant/3,
13 machine_promotes_operations/2,
14 machine_hides_unpromoted_operation/4,
15 used_machine_not_included_anywhere/3,
16 external_procedure_used/1,
17 abstract_variable_removed_in/3,
18 dummy_machine_name/2,
19 extract_only_definitions/4]).
20
21 :- use_module(module_information,[module_info/2]).
22 :- module_info(group,typechecker).
23 :- module_info(description,'This module contains the rules for loading, including, seeing, etc. B machines, scope of constants, variables, parameters, etc.').
24
25 :- use_module(library(lists)).
26 :- use_module(library(avl)).
27
28 :- use_module(self_check).
29 :- use_module(tools).
30 :- use_module(error_manager).
31 :- use_module(debug).
32 :- use_module(preferences).
33
34 :- use_module(btypechecker).
35 :- use_module(b_ast_cleanup).
36 :- use_module(bsyntaxtree).
37 :- use_module(bmachine_structure).
38 :- use_module(pragmas).
39 :- use_module(b_global_sets,[register_enumerated_sets/2]).
40
41 :- use_module(bmachine_static_checks).
42 :- use_module(tools_lists,[ord_member_nonvar_chk/2, remove_dups_keep_order/2]).
43
44 :- use_module(translate,[print_machine/1]).
45
46
47 :- set_prolog_flag(double_quotes, codes).
48
49 %maximum_type_errors(100).
50
51 :- dynamic debug_machine/0.
52 :- dynamic abstract_variable_removed_in/3.
53
54 :- use_module(pref_definitions,[b_get_important_preferences_from_raw_machine/2]).
55 set_important_prefs_from_machine(Main,Machines) :-
56 find_machine(Main,Machines,_MType,_Header,RawMachine),
57 ? get_raw_model_type(Main,Machines,RawModelType),!,
58 b_get_important_preferences_from_raw_machine(RawMachine,RawModelType),
59 check_important_annotions_from_machine(RawMachine).
60
61 check_important_annotions_from_machine(RawMachine) :-
62 ? member(definitions(_Pos,Defs),RawMachine),
63 member(expression_definition(DPOS,'PROB_REQUIRED_VERSION',[],RawValue),Defs),!,
64 (RawValue = string(VPos,Version)
65 -> add_message(bmachine_construction,'Checking PROB_REQUIRED_VERSION: ',Version,VPos),
66 (check_version(Version,VPos) -> true ; true)
67 ; add_warning(bmachine_construction,'PROB_REQUIRED_VERSION must provide a version number string.','',DPOS)
68 ).
69 check_important_annotions_from_machine(_).
70
71 :- use_module(version,[compare_against_current_version/2,full_version_str/1]).
72 check_version(VersAtom,DPOS) :- atom_codes(VersAtom,Codes),
73 split_chars(Codes,".",VCNrs),
74 maplist(codes_to_number(DPOS),VCNrs,VNrs),
75 compare_against_current_version(VNrs,Result),
76 (Result = current_older
77 -> full_version_str(Cur),
78 ajoin(['This model requires at newer version of ProB than ',Cur,'. Download at least version: '],Msg),
79 add_warning(prob_too_old,Msg,VersAtom,DPOS)
80 ; true).
81
82 codes_to_number(DPOS,C,A) :-
83 catch(number_codes(A,C), error(syntax_error(_N),_),
84 (atom_codes(AA,C),
85 add_warning(bmachine_construction,'Illegal part of version number (use only numbers separated by dots): ',AA,DPOS),fail)).
86
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88
89 :- use_module(preferences,[temporary_set_preference/3,reset_temporary_preference/2]).
90
91 % typecheck complete machines (incl. includes)
92 check_machine(MainName,MachinesMayContainGenerated,Result,FinalErrors) :-
93 clear_warnings,
94 debug_println(9,checking_machine(MainName)),
95 temporary_set_preference(perform_stricter_static_checks,true,CHNG),
96 strip_global_pragmas(MachinesMayContainGenerated,Machines,_WasGenerated), % TODO: is generated still used?
97 set_important_prefs_from_machine(MainName,Machines), % set important prefs before type-checking,...
98 check_main_machine_file_origin(MainName,Machines),
99 % we need to find all SEES/USES declarations, because all references
100 % refer to the same machine and that has to be included exactly once.
101 find_uses(Machines,GlobalUses,NotIncluded,Errors,E1),
102 % if a seen/used machine was not included, we include it by constructing
103 % a dummy machine that extends the seen/used machine and the origional main machine
104 extend_not_included_uses(NotIncluded,MainName,NewMainName,Machines,IMachines),
105 % figure out the right order of machines, so that each machine is
106 % type-checked before a dependend machine is loaded
107 machine_order(IMachines,Order),
108 assert_machine_order(Order),
109 % expand (= type-check + includes) machines in the given order
110 expand_machines(Order,IMachines,GlobalUses,[],Results1,E1,E2),
111 % until now, the initialisation section consists of a list of init_stmt(MachineName,Desc,Substitution)
112 % fold_initialisation merges those to one substitution, respecting the dependencies
113 % between the machines
114 maplist(fold_initialisation(Order),Results1,Results),
115 % find main machine
116 memberchk(machine(NewMainName,MainMachine),Results), %nl,print('MAIN:'),nl,print_machine(MainMachine),
117 % if the main machine has parameters, we convert them to global sets resp. constants
118 convert_parameters_to_global_sets(E2,[],MainMachine,Result1),
119 % add some additional informations about the machine
120 add_machine_infos(MainName,Machines,Results,Result1,Result),
121 ( debug_machine -> print_machine(Result); true),
122 %% comment in to pretty print all machines:
123 %( member(machine(Name,Mchx),Results), format('~n*** Machine ~w~n',[Name]), print_machine(Mchx),nl,fail ; true),
124 % finalize the list of errors, remove duplicates
125 sort(Errors,FinalErrors),
126 % output warnings
127 show_warnings,
128 % run static checks on the resulting machine
129 static_check_main_machine(Result),
130 reset_temporary_preference(perform_stricter_static_checks,CHNG).
131 check_machine(Main,_,Result,FinalErrors) :-
132 add_internal_error('Internal error: Checking the machine failed: ',
133 check_machine(Main,_,Result,FinalErrors)),
134 fail.
135
136 % remove global pragrams at top-level and assert them
137 strip_global_pragmas([],[],false).
138 strip_global_pragmas([generated(POS,M)|Ms],MachinesOut,true) :- !, % @generated pragma used at top of file
139 assertz(pragmas:global_pragma(generated,POS)),
140 strip_global_pragmas([M|Ms],MachinesOut,_).
141 strip_global_pragmas([unit_alias(_,Alias,Content,M)|Ms],MachinesOut,true) :- !,
142 assertz(pragmas:global_pragma(unit_alias,[Alias|Content])),
143 strip_global_pragmas([M|Ms],MachinesOut,_).
144 strip_global_pragmas([M|Ms],[M|SMs],WasGenerated) :-
145 strip_global_pragmas(Ms,SMs,WasGenerated).
146
147
148 fold_initialisation(Order,machine(Name,Sections),machine(Name,NewSections)) :-
149 select_section(initialisation,List,Subst,Sections,NewSections),
150 maplist(extract_init_substitutions(List),Order,LSubstitutions),
151 append(LSubstitutions,Substitutions),
152 create_init_sequence(Substitutions,Subst).
153 extract_init_substitutions(Unsorted,Name,Substitutions) :-
154 convlist(unzip_init(Name),Unsorted,Substitutions). % keep inits for Name
155 unzip_init(Name,init_stmt(Name,Subst),Subst).
156 create_init_sequence([],Subst) :- !, create_texpr(skip,subst,[generated],Subst).
157 create_init_sequence([I],I) :- !.
158 create_init_sequence(L,Sequence) :-
159 (L = [First|_], get_texpr_info(First,AllInfos), extract_pos_infos(AllInfos,Pos) -> true ; Pos=[]),
160 % TODO: try and merge first and last position info
161 (get_preference(allow_overriding_initialisation,true),
162 override_initialisation_sequence(L,NL)
163 -> NewList=NL ; NewList=L),
164 create_texpr(sequence(NewList),subst,[initial_sequence|Pos],Sequence).
165
166 :- use_module(b_read_write_info,[get_accessed_vars/4]).
167 % override initialisation sequence, remove unnecessary earlier assignments (which may time-out, ...)
168 % in future we may inline equalities into becomes_such that, e.g., x :: S ; x := 1 --> x :: X /\ {1}
169 % currently x :: S ; x := 1 gets simplified to skip ; x := 1
170 override_initialisation_sequence(List,NewList) :-
171 append(Prefix,[Last],List),
172 Prefix = [_|_], % there are some earlier initialisation statements to simplify
173 get_accessed_vars(Last,[],LIds,Read),
174 ord_intersection(LIds,Read,RWIds),
175 (RWIds = []
176 -> true
177 ; add_warning(b_machine_construction,'Initialisation (override) statement reads written variables:',RWIds,Last)
178 ),
179 process_override(Last,Prefix, NewLast, SPrefix),
180 append(SPrefix,[NewLast],NewList).
181
182
183 process_override(b(parallel(List),subst,Info), Prefix,
184 b(parallel(NewList),subst,Info), NewPrefix) :- !,
185 l_process_override(List,Prefix,NewList,NewPrefix).
186 process_override(Subst, Prefix, NewSubst, NewPrefix) :-
187 create_after_pred(Subst,AfterPred),
188 get_accessed_vars(Subst,[],Ids,Read),
189 % write(after_pred(Ids, read(Read))),nl, translate:print_bexpr(AfterPred),nl,
190 ord_intersection(Read,Ids,RWIds),
191 (RWIds=[]
192 -> maplist(try_simplify_init_stmt(Ids,AfterPred,Keep),Prefix,NewPrefix),
193 !,
194 (Keep==merged_override_stmt
195 -> NewSubst = b(skip,subst,[was(Subst)])
196 ; NewSubst = Subst
197 )
198 ; add_warning(b_machine_construction,'Initialisation statement reads written variables:',RWIds,Subst),
199 fail % we actually have a before after predicate which may modify RWIds
200 ).
201 process_override(Subst, Prefix , Subst, Prefix) :- add_message(b_machine_construction,'Keeping: ',Subst,Subst).
202
203 l_process_override([],Prefix,[],Prefix).
204 l_process_override([H|List],Prefix,[H1|NewList],NewPrefix) :-
205 process_override(H,Prefix,H1,Prefix1),
206 l_process_override(List,Prefix1,NewList,NewPrefix).
207
208 :- use_module(library(ordsets)).
209 simplify_init_stmt(b(parallel(List),subst,I),OverrideIds,AfterPred,Keep,b(parallel(SList),subst,I)) :- !,
210 maplist(try_simplify_init_stmt(OverrideIds,AfterPred,Keep),List,SList).
211 simplify_init_stmt(Assign,OverrideIds,AfterPred,Keep,NewSubst) :-
212 get_accessed_vars(Assign,[],AssignIds,_Read),
213 (ord_subset(OverrideIds,AssignIds),
214 merge_statement(Assign,AssignIds,AfterPred,NewSubst),
215 Keep = merged_override_stmt
216 -> % we need to remove the override assign, if it is non-det.
217 add_message(b_machine_construction,'Adapting initialisation due to override: ',NewSubst,Assign)
218 ; ord_subset(AssignIds,OverrideIds)
219 % The assignment is useless, will be completely overriden
220 -> NewSubst = b(skip,subst,[was(Assign)]),
221 add_message(b_machine_construction,'Removing initialisation due to override: ',Assign,Assign),
222 Keep = keep_override_stmt
223 ).
224 % TODO: we could simplify IF-THEN-ELSE, ... and other constructs
225
226 % Note: the merging assumes the initialisation before the override assigns each overriden variable only once
227 try_simplify_init_stmt(OverrideIds,AfterPred,Keep,Stmt,NewStmt) :-
228 (simplify_init_stmt(Stmt,OverrideIds,AfterPred,Keep,N)
229 -> NewStmt=N ; NewStmt=Stmt).
230
231 merge_statement(b(Subst,subst,Info),AssignIds,AfterPred,b(NewSubst,subst,Info)) :-
232 merge_stmt_aux(Subst,AssignIds,AfterPred,NewSubst).
233
234 merge_stmt_aux(becomes_such(Ids,Pred),_AssignIds,AfterPred,becomes_such(Ids,NewPred)) :-
235 conjunct_predicates([AfterPred,Pred],NewPred).
236 merge_stmt_aux(becomes_element_of(Ids,Set),_,AfterPred2,becomes_such(Ids,NewPred)) :-
237 create_couple(Ids,Couple),
238 safe_create_texpr(member(Couple,Set),pred,AfterPred1),
239 conjunct_predicates([AfterPred1,AfterPred2],NewPred).
240
241 create_after_pred(b(Subst,subst,Info),Pred) :- create_after_pred_aux(Subst,Info,Pred).
242
243 create_after_pred_aux(assign_single_id(Id,RHS),Info,b(equal(Id,RHS),pred,Info)).
244 create_after_pred_aux(assign(LHS,RHS),_Info,Conj) :- % TODO: split assignments so that we can individually apply preds
245 maplist(create_equality,LHS,RHS,Eqs),
246 conjunct_predicates(Eqs,Conj).
247 % the following two are non-deterministic; hence we need to remove the substitutions
248 % in case we have managed to merge them into an earlier becomes_such,... (otherwise we may do a second non-det incompatible choice)
249 create_after_pred_aux(becomes_element_of(Id,Set),Info,b(member(Couple,Set),pred,Info)) :-
250 create_couple(Id,Couple).
251 create_after_pred_aux(becomes_such(_,Pred),_Info,Pred).
252
253
254 % -------------------------
255
256 add_machine_infos(MainName,Machines,CheckedMachines,Old,New) :-
257 ? get_raw_model_type(MainName,Machines,RawModelType), functor(RawModelType,ModelType,_), % argument is position
258 % model type could be machine or system (or model ?)
259 !,
260 append_to_section(meta,[model_type/ModelType,hierarchy/Hierarchy,header_pos/HeaderPosList],Old,NewT),
261 get_refinement_hierarchy(MainName,Machines,Hierarchy),
262 find_machine_header_positions(Machines,HeaderPosList),
263 add_refined_machine(Hierarchy,CheckedMachines,NewT,New).
264 get_refinement_hierarchy(Main,Machines,[Main|Abstractions]) :-
265 ( find_refinement(Machines,Main,Abstract) ->
266 get_refinement_hierarchy(Abstract,Machines,Abstractions)
267 ;
268 Abstractions = []).
269 find_refinement([M|Rest],Name,Abstract) :-
270 ( get_constructed_machine_name(M,Name) ->
271 refines(M,Abstract)
272 ;
273 find_refinement(Rest,Name,Abstract)).
274
275 find_machine_header_positions(Machines,SRes) :-
276 Header = machine_header(Pos,_Nm,_Paras),
277 findall(Name/Pos,find_machine(Name,Machines,_Type,Header,_Sections),Res),
278 sort(Res,SRes).
279
280 :- use_module(specfile,[animation_minor_mode/1]).
281 % check whether the main machine has filenumber 1; if not something strange is going on.
282 % an example can be found in prob_examples/public_examples/B/ErrorMachines/IllegalSeesIncludes/WrongNameM1.mch
283 check_main_machine_file_origin(MainName,Machines) :-
284 ? member(M,Machines), get_machine_parameters(M,MainName,_,Position),
285 !,
286 (Position = none -> true
287 ; get_nr_name(Position,Number,Name)
288 -> (Number=1 -> true
289 ; ajoin(['Main machine name ',MainName,' overriden by machine in file ',Number,' :'],Msg),
290 add_error(bmachine_construction,Msg,Name,Position))
291 ; add_error(bmachine_construction,'Could not extract file number and name:',Position)
292 ).
293 check_main_machine_file_origin(MainName,_) :-
294 add_error(bmachine_construction,'Could not extract file number and name for main machine:',MainName).
295 get_nr_name(none,Nr,Name) :- !, Nr=1,Name=none.
296 get_nr_name(1,Nr,Name) :- !, Nr=1,Name=none. % TLA mode, animation_minor_mode(tla) not yet set and positions are numbers
297 get_nr_name(Position,Number,Name) :- extract_file_number_and_name(Position,Number,Name).
298
299 add_refined_machine([_Main,Refined|_],Machines,Old,New) :-
300 member(machine(Refined,Body),Machines),!,
301 append_to_section(meta,[refined_machine/Body],Old,New).
302 add_refined_machine(_,_,M,M). % not refining
303
304 convert_parameters_to_global_sets(Ein,Eout) -->
305 % extract and remove parameters and constraints
306 select_section(parameters,PP,[]),
307 select_section(internal_parameters,IP,[]),
308 {create_texpr(truth,pred,[],Truth)},
309 select_section(constraints,C,Truth),
310 % split parameters into sets and scalars
311 { split_list(is_set_parameter,PP,Sets,Scalars),
312 foldl(type_set_parameter,Sets,Ein,Eout) },
313 % put the sets to the deferred sets
314 append_to_section(deferred_sets,Sets),
315 % and the scalars to the constants
316 append_to_section(concrete_constants,Scalars),
317 append_to_section(concrete_constants,IP),
318 % the scalars should be typed by constraints,
319 % so move the constraints to the properties
320 select_section(properties,OldProps,NewProps),
321 {conjunct_predicates([C,OldProps],NewProps)}.
322 is_set_parameter(TExpr) :-
323 % upper case identifiers denote set parameters, otherwise scalars
324 get_texpr_id(TExpr,Name),is_upper_case(Name).
325 type_set_parameter(TExpr,Ein,Eout) :-
326 get_texpr_id(TExpr,Name),
327 get_texpr_type(TExpr,Type),
328 get_texpr_pos(TExpr,Pos),
329 % we directly type the deferred set
330 unify_types_werrors(set(global(Name)),Type,Pos,'PARAMETER',Ein,Eout).
331
332
333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334 % included machines
335
336 expand_machines([],_,_,M,M,Errors,Errors).
337 expand_machines([M|Rest],Machines,GlobalUses,Typed,Result,Ein,Eout) :-
338 ? ( expand_machine(M,Machines,GlobalUses,Typed,New,Ein,E1) -> true
339 ; add_error(bmachine_construction,'Expansion of machine failed:',M),fail),
340 expand_machines(Rest,Machines,GlobalUses,[machine(M,New)|Typed],Result,E1,Eout).
341
342 % resolve all includes and typecheck the machine
343 % expand_machine(Name,Machines,TypeChecked,Expanded) :
344 % Name of the Machine to expand
345 % Machines contains the list of all loaded machines
346 % TypeChecked contains the list of all expanded machines so far
347 % Expanded is the resulting machine
348 expand_machine(Name,Machines,GlobalUses,TypeChecked,Expanded,Ein,Eout) :-
349 debug_format(9,'~nExpanding machine ~w~n',[Name]),
350 % find the machine in the list
351 find_machine(Name,Machines,MType,Header,RawMachineWithPragmas),
352 % remove pragmas for later reattachment
353 strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,_Pragmas),
354 % look for abstract machine
355 get_abstractions(Name,Machines,TypeChecked,Abstractions),
356 % merge all used and seen machines
357 use_and_see_machines(RawMachine,TypeChecked,SeenRefs),
358 % merge all included machines into one machine
359 include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes),
360 % Parameters contains now a list of parameters for each included machine
361 % Included contains now a machine that represents all included machines
362 % their parameters are renamed to internal_parameters
363 append([Abstractions,Included,SeenRefs],RefMachines),
364 % typecheck this machine
365 debug_stats(type_machine(Name)),
366 ? type_machine(Header,Name,MType,RawMachine,RefMachines,TypedLocal,RefMachines2,Ein,Err1),
367 % merge included and including machine
368 debug_stats(merge_included_machines(Name)),
369 merge_included_machines(Name,TypedLocal,Included,Promotes,Expanded2,Err1,Err2),
370 % add some predicates that state the equivalence between arguments
371 % in the include statement and the parameters of the included machine
372 add_link_constraints(Includes,MType,Parameters,RefMachines2,Expanded2,Expanded3,Err2,Err3),
373 % put together refinement and abstraction.
374 merge_refinement_and_abstraction(Name,Expanded3,[ref(local,TypedLocal)|RefMachines2],Expanded4,Err3,Eout),
375 % merge used machines, will also prefix
376 debug_stats(merge_used_machines(Name)),
377 merge_used_machines(Included,Expanded4,Expanded5),
378 % clean up the syntax tree
379 debug_stats(clean_up_machine(Name)),
380 clean_up_machine(Expanded5,RefMachines2,Expanded),
381 debug_stats(finished_clean_up_and_expand_machine(Name)).
382
383 strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,Pragmas) :-
384 selectchk(units(_Pos,RealVariables,Pragmas),RawMachineWithPragmas,TRawMachine), !,
385 RawMachine = [RealVariables|TRawMachine].
386 strip_machine_section_pragmas(Machine,Machine,[]).
387
388 merge_included_machines(Name, TypedLocal, RefMachines, Promotes, Merged, Ein, Eout) :-
389 (Promotes=[] -> true ; assertz(machine_promotes_operations(Name,Promotes))),
390 include(is_included_ref,RefMachines,Included),
391 create_machine(Name,Empty),
392 % move the included operations into the promoted or unpromoted section
393 move_operations(Included,Promotes,Included2,Ein,Eout),
394 LocalAndIncluded = [ref(local,TypedLocal)|Included2],
395 % TODO: make sure that the constants of two instances of the same machine do not repeat (check what should be done for distinct machines with same identifiers)
396 concat_sections_of_refs([identifiers,initialisation,operation_bodies,
397 assertions,used,values],LocalAndIncluded,Empty,Merged1),
398 conjunct_sections_of_refs([constraints,properties,invariant],LocalAndIncluded,Merged1,Merged2),
399 concat_section_of_simple_lists(freetypes,LocalAndIncluded,Merged2,Merged3),
400 get_section(definitions,TypedLocal,Defs),
401 write_section(definitions,Defs,Merged3,Merged).
402 is_included_ref(ref(included,_)).
403
404 concat_section_of_simple_lists(Sec,References,In,Out) :-
405 maplist(extract_machine_from_ref,References,Machines),
406 sort_machines_by_global_order(Machines,OMachines),
407 foldl(concat_section_of_simple_lists2(Sec),OMachines,In,Out).
408 concat_section_of_simple_lists2(Sec,Machine,In,Out) :-
409 get_section(Sec,In,Orig),
410 get_section(Sec,Machine,List),
411 append(Orig,List,NewList),
412 write_section(Sec,NewList,In,Out).
413
414 merge_used_machines(RefMachines,Old,New) :-
415 % get all included machines from the references
416 convlist(ref_to_included_machine,RefMachines,Included),
417 foldl(merge_used_machines2,Included,Old,New).
418 merge_used_machines2(Included,Old,New) :-
419 get_section(used,Included,IncludedUse),
420 map_split_list(useful_renaming,IncludedUse,RenamingList,_),
421 (RenamingList = [] -> New = Old ; rename_used(RenamingList,Old,New)).
422 ref_to_included_machine(ref(included,Inc),Inc).
423
424 % the renaming does not add a prefix; happens for constants in used/seen machines
425 %useless_renaming(includeduse(_Mach,ID,TID)) :- get_texpr_id(TID,ID).
426 useful_renaming(includeduse(_Mach,ID,TID),rename(ID,NewID)) :- get_texpr_id(TID,NewID), NewID \= ID.
427
428
429 % will add prefixes to identifiers
430 % TODO: unclear to me why this cannot be done by rename_bt? Is it for treating SEEN machines not included?
431 rename_used(RenamingList,Old,New) :-
432 expand_shortcuts([properties,invariant, assertions,
433 initialisation,operation_bodies],Sections), % TO DO: also traverse GOAL ? should we later apply this at the REPL level as well ? should we apply it to other DEFINITIONS ?
434 foldl(rename_used_sec(RenamingList),Sections,Old,New).
435 rename_used_sec(RenamingList,Sec,Old,New) :-
436 select_section_texprs(Sec,TExprs,NewTExprs,Old,New),
437 rename_used2_l(TExprs,RenamingList,NewTExprs).
438
439 rename_used2(TExpr,RenamingList,NewTExpr) :-
440 get_texpr_expr(TExpr,operation(Id,Params,Results,Subst)),!,
441 get_texpr_exprs(Params,Ps),
442 remove_renamings(Ps,RenamingList,NRenamings0),
443 get_texpr_exprs(Results,Rs),
444 remove_renamings(Rs,NRenamings0,NRenamings),
445 rename_used2_l(Params,NRenamings,NParams), % should normally do nothing; unless variable capture occurs
446 rename_used2_l(Results,NRenamings,NResults), % ditto
447 rename_used2(Subst,NRenamings,NSubst),
448 get_texpr_type(TExpr,Type),
449 get_texpr_info(TExpr,Info),
450 l_rename_info(Info,NRenamings,NewInfo),
451 create_texpr(operation(Id,NParams,NResults,NSubst),Type,NewInfo,NewTExpr).
452 rename_used2(TExpr,RenamingList,NewTExpr) :-
453 % rename identifier by adding machine prefix M.id
454 get_texpr_id(TExpr,_), % result is also Id (always ??)
455 TExpr = b(_,Type,Info),
456 member(usesee(_MachName,Id,_UseOrSeeMode),Info), % TODO: do we need to check MachName matches includeduse/3 above?
457 member(rename(Id,NewId),RenamingList),
458 % we now rename Id to Name.Id
459 !, NewTExpr = b(identifier(NewId),Type,Info).
460 %add_message(rename,'Rename ID: ',TExpr,Info),add_message(rename,'Rename ID: ',NewTExpr,NewTExpr).
461 rename_used2(TExpr,RenamingList,NTExpr1) :-
462 remove_bt(TExpr,Expr,NExpr,NTExpr),
463 syntaxtransformation_for_renaming(Expr,Subs,TNames,NSubs,NExpr),!,
464 get_texpr_exprs(TNames,Names),
465 remove_renamings(Names,RenamingList,NRenamings),
466 rename_used2_l(Subs,NRenamings,NSubs),
467 rename_infos(NTExpr,NRenamings,NTExpr1).
468 rename_used2(TExpr,RenamingList,NTExpr) :-
469 add_internal_error('Rename failed: ',rename_used2(TExpr,RenamingList,NTExpr)),
470 NTExpr = TExpr.
471
472 % update infos, e.g., read/modifies for while loops
473 rename_infos(b(E,T,I),RenamingList,b(E,T,NI)) :-
474 l_rename_info(I,RenamingList,NI).
475
476 l_rename_info([],_,[]).
477 l_rename_info([Info|T],RenamingList,[NewInfo|NT]) :-
478 rename_info(RenamingList,Info,NewInfo),
479 l_rename_info(T,RenamingList,NT).
480
481 rename_info(RenamingList,Info,NewInfo) :- info_field_contains_ids(Info,I,NewInfo,SNI),!,
482 maplist(rename_usage_info(RenamingList),I,NI), sort(NI,SNI).
483 rename_info(_,I,I).
484
485 info_field_contains_ids(reads(I),I,reads(SNI),SNI).
486 info_field_contains_ids(modifies(I),I,modifies(SNI),SNI).
487 info_field_contains_ids(non_det_modifies(I),I,non_det_modifies(SNI),SNI).
488 info_field_contains_ids(modifies_locals(I),I,modifies_locals(SNI),SNI).
489 info_field_contains_ids(reads_locals(I),I,reads_locals(SNI),SNI).
490 info_field_contains_ids(used_ids(I),I,used_ids(SNI),SNI).
491
492 rename_usage_info(RenamingList,ID,NewID) :-
493 (member(rename(ID,NewID),RenamingList) -> true ; NewID = ID).
494
495
496 rename_used2_l([],_,R) :- !, R=[].
497 rename_used2_l([T|Rest],RenamingList,[NT|NRest]) :-
498 rename_used2(T,RenamingList,NT), !,
499 rename_used2_l(Rest,RenamingList,NRest).
500 rename_used2_l(X,Y,Z) :- add_internal_error('Rename failed: ', rename_used2_l(X,Y,Z)),Z=X.
501
502 %rename_used_ids(InIds,RenamingList,OutIds) :-
503 % maplist(rename_used_ids2(RenamingList),InIds,OutIds).
504 %rename_used_ids2(RenamingList,InId,OutId) :-
505 % memberchk(rename(InId,I2),RenamingList),!, OutId=I2.
506 %rename_used_ids2(_RenamingList,Id,Id).
507
508 get_abstractions(CName,Machines,TypedMachines,[ref(abstraction,Abstraction)]) :-
509 ? refines(M,AName),
510 get_constructed_machine_name(M,CName),
511 memberchk(M,Machines),!,
512 memberchk(machine(AName,Abstraction),TypedMachines).
513 get_abstractions(_,_,_,[]).
514
515 get_includes_and_promotes(Sections,M,Includes,Promotes) :-
516 optional_rawmachine_section(includes,Sections,[],Includes1),
517 optional_rawmachine_section(imports,Sections,[],Imports),
518 optional_rawmachine_section(extends,Sections,[],Extends),
519 optional_rawmachine_section(promotes,Sections,[],Promotes1),
520 append([Includes1,Extends,Imports],Includes),
521 maplist(expand_extends(M),Extends,Promotes2),
522 append([Promotes1|Promotes2],Promotes).
523 expand_extends(Machines,machine_reference(_,Ref,_),Promotes) :-
524 split_prefix(Ref,Prefix,Name),
525 memberchk(machine(Name,Body),Machines),
526 get_section(promoted,Body,Promoted),
527 prefix_identifiers(Promoted,Prefix,Renamings),
528 rename_bt_l(Promoted,Renamings,TPromotes),
529 maplist(add_nonpos,TPromotes,Promotes).
530 add_nonpos(TId,identifier(none,Id)) :-
531 get_texpr_id(TId,op(Id)).
532
533 move_operations([],Promotes,[],Ein,Eout) :-
534 foldl(add_promotes_not_found_error,Promotes,Ein,Eout).
535 move_operations([ref(_,IncMachine)|IncRest],Promotes,[ref(included,NewIncMachine)|RefRest],Ein,Eout) :-
536 move_operations2(IncMachine,Promotes,NewIncMachine,RestPromotes),
537 move_operations(IncRest,RestPromotes,RefRest,Ein,Eout).
538 move_operations2(Included,Promotes,Result,RestPromotes) :-
539 select_section(promoted,IncOperations,Promoted,Included,Included1),
540 select_section(unpromoted,OldUnpromoted,Unpromoted,Included1,Result),
541 filter_promoted(IncOperations,Promotes,Promoted,NewUnpromoted,RestPromotes),
542 append(OldUnpromoted,NewUnpromoted,Unpromoted).
543 filter_promoted([],Promotes,[],[],Promotes).
544 filter_promoted([TExpr|OpsRest],Promotes,Promoted,Unpromoted,RestPromotes) :-
545 get_texpr_id(TExpr,op(P)),
546 ( select(identifier(_,P),Promotes,RestPromotes1) ->
547 !,Promoted = [TExpr|RestPromoted],
548 Unpromoted = RestUnpromoted
549 ;
550 RestPromotes1 = Promotes,
551 Promoted = RestPromoted,
552 Unpromoted = [TExpr|RestUnpromoted]),
553 filter_promoted(OpsRest,RestPromotes1,RestPromoted,RestUnpromoted,RestPromotes).
554 add_promotes_not_found_error(identifier(Pos,Id),[error(Msg,Pos)|E],E) :-
555 ajoin(['Promoted operation ',Id,' not found'],Msg).
556
557 find_machine(Name,Machines,Type,Header,Sections) :-
558 Header = machine_header(_,Name,_),
559 ? ( (member(abstract_machine(_,_ModelType,Header,Sections),Machines),
560 Type=machine)
561 ; (member(refinement_machine(_,Header,_Abstract,Sections),Machines),
562 Type=refinement)
563 ; (member(implementation_machine(_,Header,_Abstract,Sections),Machines),
564 Type=implementation)),
565 !.
566
567 include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes) :-
568 get_includes_and_promotes(RawMachine,TypeChecked,Includes,Promotes),
569 maplist(include_machine(TypeChecked, GlobalUses), Includes, Parameters, Singles),
570 remove_duplicate_set_inclusions(Singles,Singles1),
571 % duplicate constants inclusion is handled currently in concat_section_contents via section_can_have_duplicates
572 % create refs
573 maplist(create_ref,Singles1,Included).
574 create_ref(IncMachine,ref(included,IncMachine)).
575
576 % it is possible that a deferred or enumerated set is declared in an included machine
577 % that is included more than once. We remove all but one occurrences.
578 remove_duplicate_set_inclusions([],[]).
579 remove_duplicate_set_inclusions([M],[M]) :- !.
580 remove_duplicate_set_inclusions([M|Rest],[M|CleanedRest]) :-
581 get_section(deferred_sets,M,DSets),
582 get_section(enumerated_sets,M,ESets),
583 get_section(enumerated_elements,M,EElements),
584 append([DSets,ESets,EElements],Identifiers),
585 sort(Identifiers,SortedIds),
586 maplist(remove_duplicate_sets2(SortedIds),Rest,Rest2),
587 remove_duplicate_set_inclusions(Rest2,CleanedRest).
588 remove_duplicate_sets2(Identifiers,M,Cleaned) :-
589 remove_duplicate_sets_section(deferred_sets,Identifiers,M,M1),
590 remove_duplicate_sets_section(enumerated_sets,Identifiers,M1,M2),
591 remove_duplicate_sets_section(enumerated_elements,Identifiers,M2,Cleaned).
592 remove_duplicate_sets_section(Section,Identifiers,In,Out) :-
593 select_section(Section,Old,New,In,Out),
594 %get_texpr_ids(Identifiers,II),format('Removing duplicates ~w = ~w~n',[Section,II]),
595 exclude(element_is_duplicate(Identifiers),Old,New).
596
597 element_is_duplicate(Identifiers,TId) :-
598 get_texpr_id(TId,Name),
599 get_texpr_type(TId,Type),
600 get_texpr_id(ToRemove,Name),
601 get_texpr_type(ToRemove,Type),
602 ord_member_nonvar_chk(ToRemove,Identifiers),
603 get_texpr_info(TId,InfosA),
604 get_texpr_info(ToRemove,InfosB),
605 member(def(Sec,File),InfosA),
606 member(def(Sec,File),InfosB),
607 debug_format(5,'Removed duplicate included identifier: ~w~n',[Name]),
608 !.
609
610 include_machine(TypeChecked,GlobalUses,machine_reference(_Pos,FullRef,_Args),
611 parameters(FullRef,Parameters), TM) :-
612 split_prefix(FullRef,Prefix,Name),
613 % TM1 is the already typechecked included machine:
614 ? member(machine(Name,TM1),TypeChecked),!,
615 debug_println(9,including_machine(Name)),
616 % TM2 is a fresh copy, so we prevent unification of the parameter types:
617 (get_section(parameters,TM1,[]) -> TM2=TM1
618 ; copy_term(TM1,TM2) % this can be expensive; only copy if there are parameters
619 ),
620 % If the included machine is used somewhere, we store the identifiers
621 % to enable joining the different references later:
622 include_usings(Name,GlobalUses,TM2,TM3),
623 % TM3 is typechecked and all internal variables are renamed with a prefix
624 hide_private_information(Name,FullRef,TM3,TM4),
625 % TM4 is typechecked, and if it was referenced with a prefix (e.g. INCLUDES p.M2)
626 % all variables are prefixed
627 %print(prefixing(Prefix)),nl,
628 prefix_machine(Prefix,TM4,TM5),
629 % We need the parameters later to state their equivalence to the arguments
630 get_section(parameters,TM5,Parameters),
631 % We move the parameters to the internal parameters, because the resulting
632 % machine has only the parameters of the including machine.
633 parameters_to_internal(TM5,TM).
634
635 include_usings(Name,GlobalUses,Old,New) :-
636 ? ( member(usemch(Name,Prefix,_Kind,_FromMch,_Pos),GlobalUses) ->
637 add_machine_prefix(Name,Prefix,FullName),
638 store_usage_info(Old,FullName,UsedInfo),
639 append_to_section(used,UsedInfo,Old,New)
640 ;
641 Old = New).
642 % returns a list of which identifiers are used in the machine
643 % for each identifier, we have a trible includeuse(Name,Id,TExpr)
644 % where Name is the name of the used machine, Id is the
645 % original ID and TExpr is the currently used reference to this
646 % identifier
647 store_usage_info(Machine,Name,UsedInfo) :-
648 expand_shortcuts([identifiers],IdSections),
649 foldl(store_usage_info2(Machine,Name),IdSections,UsedInfo,[]).
650 store_usage_info2(Machine,Name,Sec,I,O) :-
651 get_section(Sec,Machine,Content),
652 foldl(store_usage_info3(Name),Content,I,O).
653 store_usage_info3(Name,TId,[includeduse(Name,Id,TId)|L],L) :-
654 get_texpr_id(TId,Id).
655
656 % conjunct sections that contain predicates (CONSTRAINTS, PROPERTIES, INVARIANT)
657 conjunct_sections_of_refs(Sections1,References,Old,New) :-
658 expand_shortcuts(Sections1,Sections),
659 maplist(extract_machine_from_ref,References,Machines),
660 sort_machines_by_global_order(Machines,OMachines),
661 foldl(conjunct_sections2(OMachines),Sections,Old,New).
662 conjunct_sections2(Machines,Section,Old,New) :-
663 % Section is constraints,properties,invariant, ...
664 write_section(Section,NewContent,Old,New), % prepare new section
665 get_section_of_machines(Machines,Section,Preds),
666 %maplist(get_machine_name,Machines,Ns),print(got_sections(Section,Ns)),nl,
667 %translate:l_print_bexpr_or_subst(Preds),nl,
668 conjunct_predicates(Preds,NewContent).
669
670
671 % merge sections that contain a list of expressions/formulas like identifiers, assertions, ...
672 concat_sections_of_refs(Sections1,References,Old,New) :-
673 maplist(extract_machine_from_ref,References,Machines),
674 maplist(create_tag_by_reference,References,Tags),
675 sort_machines_by_global_order(Machines,Tags,OMachines,STags),
676 % should we only sort for some sections, e.g., assertions
677 % for each machine, create a tag where the expression comes from
678 concat_sections(Sections1,OMachines,STags,Old,New).
679
680 extract_machine_from_ref(ref(_,M),M).
681
682 create_tag_by_reference(ref(local,_Machine),[]) :- !.
683 create_tag_by_reference(ref(RefType,Machine),[RefType/Name]) :-
684 get_machine_name(Machine,Name).
685
686 concat_sections(Sections1,Machines,Tags,Old,New) :-
687 expand_shortcuts(Sections1,Sections),
688 foldl(concat_section2(Machines,Tags),Sections,Old,New).
689 concat_section2(Machines,Tags,Section,Old,New) :-
690 write_section(Section,NewContent,Old,New),
691 maplist(get_tagged_lsection_of_machine(Section),Machines,Tags,Contents),
692 concat_section_contents(Section,Contents,NewContent).
693
694 concat_section_contents(_,[SingleContent],Res) :- !, Res=SingleContent. % no need to remove_dups
695 concat_section_contents(Section,Contents,NewContent) :-
696 append(Contents,ConcContents),
697 (section_can_have_duplicates(Section)
698 -> remove_dup_tids_keep_order(ConcContents,NewContent)
699 ; NewContent=ConcContents).
700
701 % remove_dups version which keeps order of original elements
702 % and works with typed identifiers
703 % used if a machine is seen multiple times with different prefixes for e.g. constants
704 % see Section 7.26 of Atelier-B handbook:
705 % nom des éléments énumérés et des constantes de MSees, sans le préfixe de
706 % renommage, si plusieurs instances de machines sont vues, les noms des
707 % données ne doivent pas être répétés,
708 remove_dup_tids_keep_order([],[]).
709 remove_dup_tids_keep_order([H|T],[H|Res]) :- empty_avl(E),
710 get_texpr_id(H,ID),avl_store(ID,E,true,A1),
711 rem_dups3(T,A1,Res).
712
713 rem_dups3([],_,[]).
714 rem_dups3([H|T],AVL,Res) :-
715 get_texpr_id(H,ID),
716 (avl_fetch(ID,AVL) -> rem_dups3(T,AVL,Res)
717 ; Res=[H|TRes],
718 avl_store(ID,AVL,true,AVL1),
719 rem_dups3(T,AVL1,TRes)).
720
721 % see issue PROB-403
722 section_can_have_duplicates(X) :- section_can_be_included_multiple_times_nonprefixed(X).
723 % should we also remove duplicates in PROPERTIES section ? cf. machines used in test 1857
724
725
726 get_tagged_lsection_of_machine(Section,Machine,Tags,TaggedContent) :-
727 get_section(Section,Machine,Content),
728 (Tags=[] -> TaggedContent=Content ; maplist(tag_with_origin(Tags),Content,TaggedContent)).
729
730 tag_with_origin(Origins,TExpr,TaggedExpr) :-
731 change_info_of_expression_or_init(TExpr,Info,TaggedInfo,TaggedExpr),
732 % add a new origin to the old tag or if not existent, add a new info field
733 ( Origins = [] -> TaggedInfo = Info
734 ; selectchk(origin(Orest),Info,origin(Onew),TaggedInfo) -> append(Origins,Orest,Onew)
735 ; TaggedInfo = [origin(Origins)|Info]).
736 % the substitutions in the initialisation are additionally wrapped by an init_stmt/2 term
737 % a small hack to work with those too.
738 % TODO: this became a very ugly hack -- redo!
739 change_info_of_expression_or_init(init_stmt(A,OExpr),Old,New,init_stmt(A,NExpr)) :- % ...
740 !,change_info_of_expression_or_init(OExpr,Old,New,NExpr).
741 % ignore the info for includeduse/3 completely
742 change_info_of_expression_or_init(includeduse(A,B,C),[],_,includeduse(A,B,C)) :- !.
743 change_info_of_expression_or_init(freetype(FTypeId,Cases),[],_,freetype(FTypeId,Cases)) :- !.
744 change_info_of_expression_or_init(OExpr,Old,New,NExpr) :-
745 create_texpr(Expr,Type,Old,OExpr),!,
746 create_texpr(Expr,Type,New,NExpr).
747 change_info_of_expression_or_init(OExpr,Old,New,NExpr) :-
748 add_internal_error('Illegal typed expression:',change_info_of_expression_or_init(OExpr,Old,New,NExpr)),
749 Old=[], NExpr=OExpr.
750
751 % adds a prefix to all variables and promoted operations
752 prefix_machine('',TM,TM) :- !.
753 prefix_machine(Prefix,Old,New) :-
754 debug_println(5,prefixing_machine(Prefix)),
755 expand_shortcuts([variables,promoted], RenamedIdentiferSections),
756 get_all_identifiers(RenamedIdentiferSections,Old,Identifiers),
757 prefix_identifiers(Identifiers,Prefix,Renamings),
758 find_relevant_sections(RenamedIdentiferSections,[machine],Sections1),
759 append(RenamedIdentiferSections,Sections1,Sections),
760 rename_in_sections(Sections,Renamings,Old,M),
761 rename_includeduse(M,Renamings,New).
762 rename_includeduse(Old,Renamings,New) :-
763 select_section(used,OldContent,NewContent,Old,New),
764 maplist(rename_includeduse2(Renamings),OldContent,NewContent).
765 rename_includeduse2(Renamings,includeduse(M,N,TExpr),includeduse(M,N,NExpr)) :-
766 rename_bt(TExpr,Renamings,NExpr).
767
768 get_all_identifiers(Sections1,M,Identifiers) :-
769 expand_shortcuts(Sections1,Sections),
770 maplist(get_all_identifiers2(M),Sections,LIdentifiers),
771 append(LIdentifiers,Identifiers).
772 get_all_identifiers2(M,Sec,Identifiers) :-
773 get_section(Sec,M,Identifiers).
774
775 % hide all parameters and unpromoted operations
776 hide_private_information(MachName,Prefix,Machine,NewMachine) :-
777 get_section(parameters,Machine,Params),
778 get_section(unpromoted,Machine,UnPromoted),
779 append(Params,UnPromoted,ToHide),
780 %debug_println(9,hide_private(Prefix,ToHide)),
781 ( ToHide = [] -> NewMachine=Machine
782 ;
783 debug_format(9,'Hiding private parameters ~w and unpromoted operations ~w of machine ~w~n',
784 [Params,UnPromoted,MachName]),
785 maplist(assert_hides_operation_info(MachName,Prefix),UnPromoted),
786 prefix_identifiers(ToHide,Prefix,Renamings),
787 % we have to do the renaming in promoted operations, too, because
788 % those operations might use the renamed parameters and their reads/modifies
789 % info must be updated
790 rename_in_sections([parameters,promoted,unpromoted],Renamings,Machine,Machine1),
791 rename_includeduse(Machine1,Renamings,Machine2),
792 % now find sections that can see parameters,operations:
793 rename_relevant_sections([parameters,operations],Renamings,Machine2,NewMachine)
794 ).
795
796 % store information about which operations got renamed:
797 assert_hides_operation_info(MachName,Prefix,TExpr) :- Prefix \= '',
798 prefix_identifier(Prefix,TExpr,rename(op(Old),op(New))),
799 get_texpr_pos(TExpr,Pos),
800 !,
801 debug_format(19,'Hiding unpromoted operation ~w in machine ~w, new name: ~w~n',[Old,MachName,New]),
802 assertz(machine_hides_unpromoted_operation(Old,MachName,New,Pos)).
803 assert_hides_operation_info(_,_,_).
804
805 prefix_identifiers(Identifiers,'',Identifiers) :- !.
806 prefix_identifiers(Old,Prefix,New) :-
807 maplist(prefix_identifier(Prefix),Old,New).
808 prefix_identifier(Prefix,TExpr,rename(Old,New)) :-
809 get_texpr_expr(TExpr,identifier(Old)),
810 (Old=op(OI) -> New=op(NI) ; OI=Old,NI=New),
811 ajoin([Prefix,'.',OI],NI). % , print(rename(Old,New)),nl.
812
813 parameters_to_internal(M1,M2) :-
814 select_section(internal_parameters,OldParams,Params,M1,M3),
815 select_section(parameters,NewParams,[],M3,M2),
816 append(OldParams,NewParams,Params).
817
818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
819 % uses and sees relations
820 find_uses(Machines,GlobalUses,NotIncludedWoDups,Ein,Eout) :-
821 findall(usemch(UsedMch,UPrefix,Kind,FromMchName,Pos),
822 use_usage(Machines,Pos,Kind,UsedMch,UPrefix,FromMchName), UnsortedUses),
823 sort(UnsortedUses,Uses),
824 check_include_use(Uses,Machines,NotIncluded,Ein,Eout),
825 remove_duplicate_uses(NotIncluded,NotIncludedWoDups),
826 GlobalUses = Uses. %maplist(remove_prefix,Uses,GlobalUses).
827
828 %remove_prefix(usemch(U,_,_,_,_),U).
829 same_usemch(usemch(U,Prefix,_,_,_),usemch(U,Prefix,_,_,_)). % use same machine with same prefix
830 same_usemch(usemch_not_inc(U,Prefix,_,_,_,_),usemch_not_inc(U,Prefix,_,_,_,_)).
831
832 % remove duplicate uses: a non-included/imported machine can be seen in many places;
833 % we only need to add it later once to the dummy machine
834 remove_duplicate_uses([],R) :- !, R=[].
835 remove_duplicate_uses([USE|T],Res) :- remove_dup_aux(T,USE,Res).
836 remove_dup_aux([],USE,[USE]).
837 remove_dup_aux([USE|T],PREVUSE,Res) :-
838 (same_usemch(USE,PREVUSE) -> remove_dup_aux(T,PREVUSE,Res)
839 ; Res = [PREVUSE|TR], remove_dup_aux(T,USE,TR)).
840
841
842
843 % check_include_use/5 checks if the used machines are included in any machine
844 % check_include_use(+Uses,+Machines,-NotIncluded,Ein,Eout)
845 % Uses: The list of machine names that are used
846 % Machines: The list of machines
847 % NotIncluded: The list of used machines (their names) that are not included
848 % Ein/Eout: The errors (as difference-list)
849 check_include_use([],_,[],Errors,Errors).
850 check_include_use([USEMCH|Rest],Machines,NotIncluded,Ein,Eout) :-
851 USEMCH = usemch(UsedMach,UsePrefix,Kind,FromMchName,Pos),
852 findall(i(MachName,UsePrefix,IPos),include_usage(Machines,UsedMach,UsePrefix,MachName,IPos),Inc),
853 ( Inc=[] -> Ein=E1,
854 % find all places where the machine is included with another prefix
855 findall(OtherPrefix,include_usage(Machines,UsedMach,OtherPrefix,_,_IPos),OtherInclusions),
856 NotIncluded = [usemch_not_inc(UsedMach,UsePrefix,Kind,FromMchName,Pos,OtherInclusions)|RestNotIncluded],
857 (used_machine_not_included_anywhere(UsedMach,UsePrefix,Kind) -> true % we already produced a message
858 ; assertz(used_machine_not_included_anywhere(UsedMach,UsePrefix,Kind)),
859 (debug_mode(off),OtherInclusions=[],UsePrefix='' -> true
860 ; add_machine_prefix(UsedMach,UsePrefix,FullName),
861 (OtherInclusions=[]
862 -> ajoin(['machine ',Kind, ' in ', FromMchName,
863 ' not included/imported anywhere else (will create dummy top-level machine to include it): '],Msg)
864 ; ajoin_with_sep(OtherInclusions,',',I2),
865 (UsePrefix=''
866 -> ajoin(['machine ',Kind, ' in ', FromMchName,
867 ' without prefix only included/imported with prefixes [',I2,
868 '] (will create dummy top-level machine to include it): '],Msg)
869 ; I2=''
870 -> ajoin(['machine ',Kind, ' in ', FromMchName,
871 ' only included/imported without prefix (will create dummy top-level machine to include it): '],Msg)
872 ; ajoin(['machine ',Kind, ' in ', FromMchName, ' only included/imported with other prefixes [',I2,
873 '] (will create dummy top-level machine to include it): '],Msg)
874 )
875 ),
876 add_message(bmachine_construction,Msg,FullName,Pos)
877 )
878 )
879 ; Inc=[_] -> NotIncluded = RestNotIncluded, Ein=E1
880 ; Inc=[i(_M1,_,Pos1),i(_M2,_,Pos2)|_],
881 NotIncluded = RestNotIncluded,
882 translate_span(Pos1,PS1),
883 translate_span(Pos2,PS2),
884 (UsePrefix = ''
885 -> ajoin([Kind,' machine ',UsedMach,' is included more than once: ',PS1,' and ',PS2],Msg)
886 ; ajoin([Kind,' machine ',UsedMach,' is included more than once with prefix ',UsePrefix,
887 ': ',PS1,' and ',PS2],Msg)
888 ),
889 Ein = [error(Msg,Pos)|E1]
890 ),
891 check_include_use(Rest,Machines,RestNotIncluded,E1,Eout).
892
893
894
895 % extend_not_included_uses(+Uses,+Main,-Name,+Machines,-AllMachines):
896 % Create a dummy machine that extends the main machine and extends or includes
897 % all seen/used machines that are not included if such machines exist.
898 % In case of refinement this is done for the whole refinement chain.
899 % Uses: List of machine names and prefix usemch/2 of the machines that are used/seen but not included
900 % Main: The name of the main machine
901 % Name: The name of the generated dummy machine (or Main if no dummy machine is generated)
902 % Machines: List of all specified Machines
903 % AllMachines: List of all Machines + the new dummy machine(s)
904 extend_not_included_uses([],Main,Main,Machines,Machines) :- !.
905 extend_not_included_uses(Uses,Main,NewMainName,Machines,AllMachines) :-
906 get_refinement_hierarchy(Main,Machines,RefChain),
907 maplist(extend_not_included_uses2(Uses,Machines),RefChain,NewMachines),
908 append(NewMachines,Machines,AllMachines),
909 dummy_machine_name(Main,NewMainName),
910 debug_format(19,'Creating dummy machine called ~w for main machine~n',[NewMainName]).
911 extend_not_included_uses2(Uses,Machines,Name,DummyMachine) :-
912 debug_format(19,'Creating dummy subsidiary machine for refined machine ~w~n',[Name]),
913 create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine),
914 IncludeMain = machine_reference(none,Name,Parameters),
915 ( get_preference(seen_machines_included,true) ->
916 % extend just the main machine, include the used/seen machines
917 References = [extends(none,[IncludeMain]),includes(none,UReferences)]
918 ;
919 % extends the main machine and all used/seen machines
920 References = [extends(none,[IncludeMain|UReferences])]),
921 maplist(find_using(Machines),Uses,UReferences,LUParameters),
922 append([Parameters|LUParameters],DummyParameters),
923 copy_raw_definitions(Name,Machines,OptDefs),
924 % we store a flag "is_dummy" in the machine because we have a special case
925 % later, see type_constraints/7.
926 append([References,[is_dummy],OptDefs],Sections).
927 create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine) :-
928 dummy_machine_name(Name,DummyName),
929 ? get_raw_model_type(Name,Machines,ModelType),
930 !,
931 Header = machine_header(_,Name,Parameters),
932 DummyHeader = machine_header(none,DummyName,DummyParameters),
933 ? member(Machine,Machines),
934 ? generate_raw_machine(Header,DummyHeader,ModelType,Sections,Machine,DummyMachine),!.
935
936 generate_raw_machine(OldHeader,NewHeader,_,NewSections,
937 abstract_machine(_, ModelType,OldHeader,_),
938 abstract_machine(none,ModelType,NewHeader,NewSections)).
939 generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections,
940 refinement_machine(_, OldHeader,_Abstract, _),
941 abstract_machine(none,ModelType,NewHeader,NewSections)).
942 generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections,
943 implementation_machine(_, OldHeader,_Abstract, _),
944 abstract_machine(none,ModelType,NewHeader,NewSections)).
945
946 dummy_machine_name(Name,DummyName) :-
947 atom_concat('MAIN_MACHINE_FOR_',Name,DummyName).
948
949 add_machine_prefix(UsedMch,'',Name) :- !, UsedMch=Name.
950 add_machine_prefix(UsedMch,UsePrefix,Name) :- ajoin([UsePrefix,'.',UsedMch],Name).
951
952 find_using(Machines,usemch_not_inc(UsedMch,UsePrefix,_,_,_,OtherPrefixes),
953 machine_reference(none,Name,Arguments),Arguments) :-
954 % invent a dummy prefix for the SEEN/USED machine which is not included
955 % to avoid name-clashes with main machine
956 % was using(U).U, but awkward to type in REPL
957 % try machine name itself; unless somewhere else it is included this way
958 (member(UsedMch,OtherPrefixes) % somewhere we already have an INCLUDES UsedMach.UsedMach
959 -> DummyPrefix = using(UsedMch)
960 ; DummyPrefix = UsedMch),
961 ajoin([DummyPrefix,'.',UsedMch],Name0),
962 add_machine_prefix(Name0,UsePrefix,Name),
963 ? member(M,Machines), get_machine_parameters(M,UsedMch,Params,_),!,
964 maplist(add_use_param,Params,Arguments).
965 add_use_param(identifier(_,Param),identifier(none,Name)) :-
966 ( is_upper_case(Param) ->
967 ajoin(['Useparam(',Param,')'],Name)
968 ;
969 ajoin(['useparam(',Param,')'],Name)).
970
971 % copy_raw_definitions(+Name,+Machines,-OptDefs):
972 % Get the definitions section from a raw (untyped) machine
973 % Name: Name of the machine
974 % Machines: List of Machines
975 % OptDefs: [] if no definitions are present or [Def] if a definition section Def is present
976 copy_raw_definitions(Name,Machines,OptDefs) :-
977 ? get_constructed_machine_name_and_body(M,Name,_,Sections),
978 memberchk(M,Machines),!,
979 Def = definitions(_,_),
980 ( memberchk(Def,Sections) ->
981 OptDefs = [Def]
982 ;
983 OptDefs = []).
984
985 add_def_dependency_information(DefsIn,DefsOut,Ein,Eout) :-
986 extract_def_name_set(DefsIn,DefNames,DN),
987 maplist(add_def_dep(DN),DefsIn,Defs1),
988 check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,Ein,Eout).
989
990 extract_def_name_set(Defs,DefNames,DN) :-
991 maplist(get_def_name,Defs,DefNames),
992 maplist(to_mapset_entry,DefNames,DN1),
993 list_to_avl(DN1,DN).
994
995 get_def_name(Def,Name) :- arg(1,Def,Name).
996 get_def_pos(Def,Pos) :- arg(3,Def,Pos).
997 get_def_dependencies(Def,Dependencies) :- arg(6,Def,Dependencies).
998 to_mapset_entry(Name,Name-true).
999
1000 add_def_dep(DN,In,Out) :-
1001 analyse_definition_dependencies(In,DN,Deps),
1002 In = definition_decl(Name,DefType,DefInfos,Pos,Args,RawExpr),
1003 Out = definition_decl(Name,DefType,DefInfos,Pos,Args,RawExpr,Deps).
1004
1005 check_for_cyclic_def_dependency(Defs,DefNames,DefsOut,Ein,Eout) :-
1006 % check if we have a cyclic definition:
1007 create_definitions_avl(Defs,DefsAvl),
1008 search_for_cyclic_definition(DefNames,DefsAvl,[],Pos,RCycle),!,
1009 % if we have found a cyclic definition, generate an error message, ...
1010 reverse(RCycle,Cycle),add_arrows(Cycle,Msg0),
1011 ajoin(['Found cyclic definitions: '|Msg0],Msg),
1012 Ein = [error(Msg,Pos)|E1],
1013 % ... remove the definitions in the cycle (to prevent later infinite loops) ...
1014 exclude(definition_in_list(Cycle),Defs,Defs1),
1015 % ... and check the remaining definitions.
1016 check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,E1,Eout).
1017 check_for_cyclic_def_dependency(Defs,_DefNames,Defs,E,E).
1018 add_arrows([E],[E]) :- !.
1019 add_arrows([E|Erest],[E,'->'|Arest]) :- add_arrows(Erest,Arest).
1020 definition_in_list(List,Def) :-
1021 get_def_name(Def,Name),memberchk(Name,List).
1022
1023 create_definitions_avl(Defs,DefsAvl) :-
1024 maplist(split_def_name,Defs,Entries),
1025 list_to_avl(Entries,DefsAvl).
1026 split_def_name(Def,Name-Def) :- get_def_name(Def,Name).
1027
1028
1029 % just a depth-first search to find a cycle
1030 search_for_cyclic_definition(DefNames,Definitions,Visited,Pos,Cycle) :-
1031 ? member(Name,DefNames),avl_fetch(Name,Definitions,Definition),
1032 get_def_pos(Definition,Pos),
1033 ( memberchk(Name,Visited) ->
1034 Cycle = [Name|Visited]
1035 ;
1036 get_def_dependencies(Definition,Dependencies),
1037 search_for_cyclic_definition(Dependencies,Definitions,[Name|Visited],_,Cycle)
1038 ).
1039
1040 :- assert_must_succeed((
1041 list_to_avl([def1-true,def2-true,def3-true,def4-true],DefNames),
1042 analyse_definition_dependencies(
1043 conjunct(none,
1044 equals(none,
1045 identifier(none,x),
1046 identifier(none,def1)),
1047 equals(none,
1048 definition(none,def4,
1049 [function(none,
1050 identifier(none,def3),
1051 integer(none,5))]),
1052 identifier(y))),DefNames,Defs),
1053 Defs==[def1,def3,def4]
1054 )).
1055 % analyse_definition_dependencies(+Expr,+DefinitionNames,Deps):
1056 % Expr: raw (i.e. untyped) expression to analyse
1057 % DN: AVL set (i.e. mapping from elements to 'true') of the names of the definitions
1058 % This is needed to decide if an identifier is a reference to a definition
1059 % Deps: A list of used definitions (a list of their names)
1060 analyse_definition_dependencies(Expr,DN,Deps) :-
1061 analyse_definition_dependencies2(Expr,DN,Unsorted,[]),
1062 sort(Unsorted,Deps).
1063 analyse_definition_dependencies2(VAR,_DN) --> {var(VAR)},!,
1064 {add_internal_error('Variable DEFINITION expression in',analyse_definition_dependencies)}.
1065 analyse_definition_dependencies2([Expr|Rest],DN) -->
1066 !, analyse_definition_dependencies2(Expr,DN),
1067 analyse_definition_dependencies2(Rest,DN).
1068 analyse_definition_dependencies2(definition(_Pos,Name,Args),DN) -->
1069 !,[Name],analyse_definition_dependencies2(Args,DN).
1070 analyse_definition_dependencies2(identifier(_Pos,Name),DN) -->
1071 {avl_fetch(Name,DN),!},[Name].
1072 analyse_definition_dependencies2(Expr,DN) -->
1073 { compound(Expr),functor(Expr,_Functor,Arity),!},
1074 analyse_definition_dependencies_arg(2,Arity,Expr,DN).
1075 analyse_definition_dependencies2(_Expr,_DN) --> [].
1076
1077 analyse_definition_dependencies_arg(I,Arity,Expr,DN) -->
1078 { I =< Arity,!,arg(I,Expr,Arg),I2 is I+1 },
1079 analyse_definition_dependencies2(Arg,DN),
1080 analyse_definition_dependencies_arg(I2,Arity,Expr,DN).
1081 analyse_definition_dependencies_arg(_I,_Arity,_Expr,_DN) --> [].
1082
1083
1084 :- use_module(tools_positions, [get_position_filenumber/2]).
1085
1086 get_constructed_machine_name(MachineTerm,Name) :- get_constructed_machine_name_and_body(MachineTerm,Name,_Pos,_).
1087 % name and pos; pos can be used for filenumber
1088 get_constructed_machine_name_and_filenumber(MachineTerm,Name,Filenumber) :-
1089 get_constructed_machine_name_and_body(MachineTerm,Name,Pos,_),
1090 (get_position_filenumber(Pos,FN) -> Filenumber=FN ; Filenumber=unknown).
1091 get_constructed_machine_name_and_body(abstract_machine(_,_ModelType,machine_header(Pos,Name,_),Body),Name,Pos,Body).
1092 get_constructed_machine_name_and_body(refinement_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body).
1093 get_constructed_machine_name_and_body(implementation_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body).
1094
1095 refines(refinement_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract).
1096 refines(implementation_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract).
1097
1098 get_machine_parameters(abstract_machine(Pos,_ModelType,machine_header(_,Name,Parameters),_),Name,Parameters,Pos).
1099 get_machine_parameters(refinement_machine(Pos,machine_header(_,Name,Parameters),_,_),Name,Parameters,Pos).
1100 get_machine_parameters(implementation_machine(Pos,machine_header(_,Name,Parameters),_,_),Name,Parameters,Pos).
1101
1102 get_raw_model_type(Main,Machines,ModelType) :-
1103 ? get_constructed_machine_name_and_body(M,Main,_,_),
1104 memberchk(M,Machines),
1105 ( refines(M,RefName) ->
1106 get_raw_model_type(RefName,Machines,ModelType)
1107 ;
1108 M = abstract_machine(_,ModelType,_,_)).
1109
1110 some_machine_name_body(Machines,M,Name,Body) :-
1111 ? member(M,Machines),
1112 get_constructed_machine_name_and_body(M,Name,_,Body).
1113
1114 use_usage(Machines,Pos,Kind,UsedMch,UsePrefix,FromMchName) :-
1115 ? some_machine_name_body(Machines,_,FromMchName,Body),
1116 ? ( member(sees(Pos,R),Body),Kind=seen
1117 ; member(uses(Pos,R),Body),Kind=used),
1118 ? member(identifier(_,PrefixUse),R),
1119 split_prefix(PrefixUse,UsePrefix,UsedMch).
1120 % include_usage/4 checks if there is any machine in Machines that
1121 % includes/extends/imports the used machine
1122 % include_usage(+Machines,+Use,-Name):
1123 % Machines: The list of machines
1124 % UsedMch: The name of the used machine
1125 % UsePrefix: the prefix with which the machine is used
1126 % Name: The name of the machine that imports the used machine
1127 % include_usage/4 fails if there is not such an import
1128 include_usage(Machines,UsedMch,UsePrefix,MachName,Pos) :-
1129 ? some_machine_name_body(Machines,_,MachName,Body),
1130 ( member(includes(_,R),Body)
1131 ; member(extends(_,R), Body)
1132 ; member(imports(_,R), Body)),
1133 member(machine_reference(Pos,PrefixRef,_),R),
1134 % The name could be prefixed, we need it without prefix:
1135 split_prefix(PrefixRef,UsePrefix,UsedMch).
1136
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 % uses and sees clauses
1139
1140 % returns a list of references to used and seen machines
1141 % the machines contain only identifier sections and
1142 % the identifier are prefixed accordingly
1143 % all identifier are marked as coming from a seen/used machine
1144 use_and_see_machines(Sections,Machines,References) :-
1145 get_uses_and_sees(Sections,Uses,Sees),
1146 maplist(use_or_see_machine(used,Machines),Uses,Used),
1147 maplist(use_or_see_machine(seen,Machines),Sees,Seen),
1148 append(Used,Seen,References).
1149
1150 % get uses and sees sections from the untyped machines
1151 get_uses_and_sees(Sections,Uses,Sees) :-
1152 get_uses_or_sees2(sees,Sections,Sees),
1153 get_uses_or_sees2(uses,Sections,Uses).
1154 get_uses_or_sees2(Mode,Sections,US) :-
1155 optional_rawmachine_section(Mode,Sections,[],US1),
1156 findall(I,member(identifier(_,I),US1),US).
1157
1158 % Mode is used or seen
1159 use_or_see_machine(Mode,TypedMachines,Ref,ref(Mode,Result)) :-
1160 split_prefix(Ref,Prefix,MachName),
1161 memberchk(machine(MachName,Typed),TypedMachines),
1162 create_machine(MachName,Empty),
1163 use_sections([sets],Mode,'',MachName,Typed,Empty,M1),
1164 use_sections([constants,variables,promoted],Mode,Prefix,MachName,Typed,M1,Result).
1165 use_sections(Sections,Mode,Prefix,MachName,Typed,Old,New) :-
1166 expand_shortcuts(Sections,AllSections),
1167 foldl(use_section(Mode,Prefix,MachName,Typed),AllSections,Old,New).
1168 use_section(Mode,Prefix,MachName,Machine,Section,OldM,NewM) :-
1169 get_section_texprs(Section,Machine,Identifiers),
1170 write_section(Section,NewIds,OldM,NewM),
1171 ( Prefix='' ->
1172 Ids1=Identifiers
1173 ;
1174 prefix_identifiers(Identifiers,Prefix,Renamings),
1175 rename_bt_l(Identifiers,Renamings,Ids1)),
1176 maplist(add_use_info_to_identifier(Mode,MachName),Ids1,NewIds).
1177 add_use_info_to_identifier(Mode,MachName,TExpr,New) :-
1178 get_texpr_id(TExpr,PId), get_texpr_type(TExpr,Type),
1179 get_texpr_id(New,PId), get_texpr_type(New,Type),
1180 split_prefix(PId,_Prefix,Id),
1181 get_texpr_info(TExpr,Info),
1182 get_texpr_info(New,[usesee(MachName,Id,Mode)|Info]).
1183
1184
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1186 % add a section to the machine that describes the linking of
1187 % parameters and arguments
1188 add_link_constraints(Includes,MType,Parameters,RefMachines,Old,New,Ein,Eout) :-
1189 AllRefMachines = [ref(local,Old)|RefMachines],
1190 extract_parameter_types(RefMachines,NonGroundExceptions),
1191 foldl(add_link_section(MType,Parameters,AllRefMachines,NonGroundExceptions),
1192 Includes,Links/Ein,[]/Eout), % TO DO: Daniel check if [] is correct here
1193 %print(conjunct_predicates(Links,Link)),nl,
1194 conjunct_predicates(Links,Link),
1195 select_section(constraints,OConstraints,NConstraints,Old,New),
1196 conjunct_predicates([Link,OConstraints],NConstraints).
1197 add_link_section(MType,Parameters,RefMachines,NonGroundExceptions,
1198 machine_reference(Pos,Ref,Args),Links/Ein,RLinks/Eout) :-
1199 visible_env(MType,includes,RefMachines,Env,Ein,E1),
1200 memberchk(parameters(Ref,TParameters),Parameters),
1201 ( same_length(TParameters, Args) ->
1202 get_texpr_types(TParameters,Types),
1203 btype_ground_dl(Args,Env,NonGroundExceptions,Types,TArgs,E1,Eout),
1204 maplist(create_plink_equality,TParameters,TArgs,LLinks)
1205 ;
1206 E1 = [error('wrong number of machine arguments',Pos)|Eout],
1207 LLinks = []),
1208 append(LLinks,RLinks,Links).
1209 create_plink_equality(P,A,E) :-
1210 create_texpr(equal(P,A),pred,[parameterlink],E).
1211
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 % type machines
1214
1215 type_machine(Header,Name,MType,RawMachine,RefMachines,TypedMachine,NewRefMachines,Ein,Eout) :-
1216 Header = machine_header(_,Name,_),
1217 create_machine(Name,Empty),
1218 % (optional) definitions
1219 extract_definitions(RawMachine,Empty,DefsOnly,Ein,E0),
1220 debug_stats(extracted_definitions(Name)),
1221 % create the identifiers that will be typed,
1222 % Local will contain the identifier sections
1223 create_id_sections(Header,RawMachine,Name,DefsOnly,Local),
1224 debug_stats(created_identifier_sections(Name)),
1225 ? create_freetypes(RawMachine,MType,RefMachines,Local,Local1,E0,E1),
1226 % in case of a refinement, check if all newly defined operations are refinements
1227 link_to_refinement(MType,Name,RawMachine,RefMachines,Local1,Local2,E1,E2),
1228 % extract types that can be variables
1229 debug_stats(created_link_to_refinement(Name)),
1230 extract_parameter_types([ref(local,Local2)|RefMachines],NonGroundExceptions),
1231 % check for a VALUES clause. They are a little bit tricky because they can replace
1232 % already defined deferred sets by integers or other deferred sets
1233 process_values_section(MType,RawMachine,NonGroundExceptions,
1234 Local2/E2/RefMachines,Local3/E3/NewRefMachines),
1235 % type-check the other sections (properties, invariant, operation_bodies, etc)
1236 type_sections(RawMachine,MType,[ref(local,Local3)|NewRefMachines],NonGroundExceptions,
1237 Name,E3,Eout,Local3,TypedMachine).
1238
1239 % extract definitions from a definition file
1240 extract_only_definitions(MainName,RawMachine,DefsOnlyMachine,FinalErrors) :-
1241 create_machine(MainName,Empty),
1242 extract_definitions(RawMachine,Empty,DefsOnlyMachine,Errors,[]),
1243 sort(Errors,FinalErrors),
1244 add_all_perrors(FinalErrors).
1245
1246 extract_definitions(RawMachine,In,Out,Ein,Eout) :-
1247 optional_definition_sections([definitions,substitutions,predicates,expressions],RawMachine,[],Definitions1),
1248 write_section(definitions,Definitions,In,Out),
1249 % analyse dependencies
1250 add_def_dependency_information(Definitions1,Definitions2,Ein,Eout),
1251 % replace external function definitions by calls to the external function
1252 replace_external_declarations(Definitions2,Definitions).
1253
1254 is_file_definition(file_definition(_Pos,_Filename)).
1255
1256
1257 % get result from multiple sections and merge
1258 optional_definition_sections([],_RawMachineSecList,Acc,Acc).
1259 optional_definition_sections([Elem|T],RawMachineSecList,Acc,Result) :-
1260 ( rawmachine_section(Elem,RawMachineSecList,Result1)
1261 -> % remove all references to definition files from definitions
1262 exclude(is_file_definition,Result1,Result2),
1263 % replace expression_definition(...) by defintion(expression,...), etc.
1264 change_definition_style(Result2,Elem,Result3),
1265 clever_append(Result3,Acc,Acc2)
1266 ; Acc2=Acc),
1267 optional_definition_sections(T,RawMachineSecList,Acc2,Result).
1268
1269 change_definition_style(DefsIn,Elem,DefsOut) :-
1270 maplist(change_definition_style2(Elem),DefsIn,DefsOut).
1271 change_definition_style2(Elem,Def,definition_decl(Name,DefType,DefInfos,Pos,Args,RawExpr)) :-
1272 Def =.. [Functor,Pos,Name,Args,RawExpr],
1273 !,
1274 maplist(check_def_argument(Name,Pos),Args),
1275 map_def_functor(Functor,DefType),
1276 (Elem=definitions -> DefInfos = []
1277 ; DefInfos = [hygienic_def]). % EXPRESSIONS, PREDICATES or SUBSTITUTIONS section
1278 change_definition_style2(_,Def,_) :-
1279 add_internal_error('Unrecognised DEFINITION declaration:',Def),fail.
1280
1281 map_def_functor(expression_definition,X) :- !, X=expression.
1282 map_def_functor(predicate_definition,X) :- !, X=predicate.
1283 map_def_functor(substitution_definition,X) :- !, X=substitution.
1284 map_def_functor(X,expression) :- add_internal_error('Illegal definition AST node:',X).
1285
1286 % check formal arguments of definitions , e.g., here xx in square2 is not an identifier: xx == 20; square2(xx) == xx*xx
1287 check_def_argument(_,_,identifier(_,_)) :- !.
1288 check_def_argument(DefName,DefPos,definition(_,ID,_)) :- !,
1289 tools:ajoin(['Formal parameter ', ID, ' is a definition call in Definition: '],Msg),
1290 add_error(bmachine_construction,Msg,DefName,DefPos).
1291 check_def_argument(DefName,DefPos,FP) :- !,
1292 tools:ajoin(['Formal parameter ', FP, ' is not an identifier in Definition: '],Msg),
1293 add_error(bmachine_construction,Msg,DefName,DefPos).
1294
1295 replace_external_declarations(Definitions,NewDefs) :-
1296 split_list(is_external_declaration,Definitions,ExtFunctionDecls,RestDefs),
1297 foldl(replace_external_declaration,ExtFunctionDecls,RestDefs,NewDefs).
1298 is_external_declaration(definition_decl(DefName,expression,_Infos,_Pos,_Params,_Def,_Dependencies)) :-
1299 ? external_name(DefName,ExtType,_ExpectedDefType,_ExtCall,FunName),
1300 debug_format(4,'external ~w declared: ~w~n', [ExtType,FunName]).
1301 external_name(DefName,_,_,_,_) :-
1302 \+ atom(DefName),!,
1303 add_internal_error('Non-atomic DEFINITION id:',DefName),fail.
1304 external_name(DefName,function,expression,external_function_call,FunName) :-
1305 atom_concat('EXTERNAL_FUNCTION_',FunName,DefName).
1306 external_name(DefName,predicate,predicate,external_pred_call,FunName) :-
1307 atom_concat('EXTERNAL_PREDICATE_',FunName,DefName).
1308 external_name(DefName,substitution,substitution,external_subst_call,FunName) :-
1309 atom_concat('EXTERNAL_SUBSTITUTION_',FunName,DefName).
1310
1311 replace_external_declaration(definition_decl(DefName,expression,DefPos,_,TypeParams,Decl,_Deps),In,Out) :-
1312 OldDefinition = definition_decl(FunName,ExpectedDefType,DefInfos,Pos,FunParams,FunDef,Deps),
1313 NewDefinition = definition_decl(FunName,ExpectedDefType,DefInfos,Pos,FunParams,ExtCall,Deps),
1314 ? ( external_name(DefName,_ExtType,ExpectedDefType,ExtCallFunctor,FunName),
1315 ExtCall =.. [ExtCallFunctor,Pos,FunName,FunParams,FunDef,
1316 rewrite_protected(TypeParams),rewrite_protected(Decl)],
1317 selectchk(OldDefinition,In,NewDefinition,Out) ->
1318 assert_external_procedure_used(FunName)
1319 ; external_name(DefName,ExpectedKind,_,_,FunName),
1320 selectchk(definition_decl(FunName,OtherDefType,_,_OtherPos,_,_,_),In,_,_),!,
1321 % definition found for different type of external function
1322 ajoin(['No DEFINITION associated with external ',ExpectedKind,
1323 ' (but definition as ',OtherDefType,' exists):'],Msg),
1324 add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In
1325 ; external_name(DefName,ExpectedKind,_,_,FunName) ->
1326 % no definition found for external function
1327 ajoin(['No DEFINITION associated with external ',ExpectedKind,':'],Msg),
1328 add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In
1329 ; % no definition found for external function
1330 ajoin(['No DEFINITION associated with:'],Msg),
1331 add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In
1332 ).
1333
1334 :- use_module(external_function_declarations,
1335 [external_function_library/2, safe_external_function_library/2,
1336 get_external_function_definition/3]).
1337 % store external definitions of a given library in the type environment, e.g. for debugging in Repl or VisB
1338 % Library could be "LibraryStrings.def"
1339
1340 store_ext_defs(Library,In,Out) :-
1341 (Library = all_available_libraries
1342 -> findall(extfun(Id,Lib),external_function_library(Id,Lib),Ids)
1343 ; Library = safe_available_libraries
1344 -> findall(extfun(Id,Lib),safe_external_function_library(Id,Lib),Ids)
1345 ; findall(extfun(Id,Library),external_function_library(Id,Library),Ids)
1346 ),
1347 (Ids=[] -> add_warning(store_ext_defs,'No external functions found for library: ',Library) ; true),
1348 foldl(store_ext_def,Ids,In,Out).
1349
1350 store_ext_def(extfun(Id,Library),In,Out) :- env_lookup_type(Id,In,_),!,
1351 debug_println(4,not_storing_ext_def(Id,Library)), % already imported from stdlib or user has other definition
1352 Out=In.
1353 store_ext_def(extfun(FunName,Library),In,Out) :-
1354 ? get_external_function_definition(FunName,Library,DEFINITION),!,
1355 debug_println(4,storing_ext_def(FunName,Library)),
1356 env_store(FunName,DEFINITION,[loc('automatically included',Library,definitions)],In,Out).
1357 store_ext_def(extfun(Id,_),In,Out) :-
1358 add_internal_error('Cannot add external definition: ',Id),
1359 Out=In.
1360
1361
1362 % store which machine promotes which operations
1363 :- dynamic machine_promotes_operations/2, machine_hides_unpromoted_operation/4, machine_global_order/1.
1364 :- dynamic used_machine_not_included_anywhere/3.
1365
1366 reset_bmachine_construction :-
1367 retractall(machine_promotes_operations(_,_)),
1368 retractall(machine_hides_unpromoted_operation(_,_,_,_)),
1369 retractall(abstract_variable_removed_in(_,_,_)),
1370 retractall(machine_global_order(_)),
1371 retractall(used_machine_not_included_anywhere(_,_,_)),
1372 reset_external_procedure_used.
1373
1374
1375 % maybe this information should be stored somewhere else ??
1376 :- dynamic external_procedure_used/1.
1377 reset_external_procedure_used :- retractall(external_procedure_used(_)).
1378 assert_external_procedure_used(FunName) :-
1379 (external_procedure_used(FunName) -> true ; assertz(external_procedure_used(FunName))).
1380
1381 link_to_refinement(machine,_,_RawMachine,_RefMachines,Local,Local,Errors,Errors).
1382 link_to_refinement(refinement,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :-
1383 link_to_refinement(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout).
1384 link_to_refinement(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :-
1385 link_to_refinement2(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout).
1386 link_to_refinement2(_MType,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :-
1387 memberchk(ref(abstraction,AbstractMachine),RefMachines),
1388 copy_constraints(Local,AbstractMachine,NewLocal,Ein,E1),
1389 type_vars_in_refinement(AbstractMachine,NewLocal),
1390 type_refinement_operations(Name,RawMachine,AbstractMachine,NewLocal,E1,Eout).
1391
1392 copy_constraints(Local,AbstractMachine,NewLocal,Ein,Eout) :-
1393 get_section(parameters,Local,LocalParameters),
1394 get_section(parameters,AbstractMachine,AbstractParameters),
1395 check_if_equal_identifiers(LocalParameters,AbstractParameters,Ein,Eout,Local),
1396 get_section(constraints,AbstractMachine,Constraints),
1397 write_section(constraints,Constraints,Local,NewLocal).
1398 check_if_equal_identifiers(Local,Abstract,Ein,Eout,LocalMachine) :-
1399 ( same_length(Local,Abstract) ->
1400 foldl(check_if_equal_identifiers2,Local,Abstract,Ein,Eout)
1401 ;
1402 get_texpr_ids(Abstract,AIDs),
1403 get_machine_name(LocalMachine,MachName),
1404 ajoin(['Refinement ',MachName,' must have same Parameters ', AIDs,' as abstract Machine'],Msg),
1405 Ein = [error(Msg,none)|Eout]
1406 ).
1407 check_if_equal_identifiers2(LParam,AParam,Ein,Eout) :-
1408 get_texpr_id(LParam,LName),
1409 get_texpr_id(AParam,AName),
1410 ( LName = AName ->
1411 % type in refinement is the same as in the abstraction
1412 get_texpr_type(LParam,Type),
1413 get_texpr_type(AParam,Type),
1414 Ein=Eout
1415 ;
1416 get_texpr_pos(LParam,Pos),
1417 ajoin(['Parameter ',LName,' of refinement machine must be ',
1418 AName,' like in the abstract machine'],Msg),
1419 Ein = [error(Msg,Pos)|Eout]
1420 ).
1421
1422 % in case of a refinement, give variables the same type as in the abstract machine
1423 % the same for constants
1424 type_vars_in_refinement(AbstractMachine,ConcreteMachine) :-
1425 pass_type(AbstractMachine,[abstract_variables,concrete_variables],
1426 ConcreteMachine,[abstract_variables,concrete_variables]),
1427 pass_type(AbstractMachine,[abstract_constants,concrete_constants],
1428 ConcreteMachine,[abstract_constants,concrete_constants]).
1429
1430 % pass the type from variables in Sections1 of Machine1 to
1431 % the variables of the same name in Sections2 of Machine2
1432 % Machine1,Machine2: a machine
1433 % Sections1, Sections2: a list of section names
1434 pass_type(Machine1,Sections1,Machine2,Sections2) :-
1435 get_sections_and_append(Sections1,Machine1,Vars1),
1436 get_sections_and_append(Sections2,Machine2,Vars2),
1437 maplist(pass_type2(Vars2),Vars1).
1438 get_sections_and_append([],_M,[]).
1439 get_sections_and_append([Sec|RestSections],M,R) :-
1440 get_section(Sec,M,L), append(L,Rest,R),
1441 get_sections_and_append(RestSections,M,Rest).
1442 pass_type2(DstVariables,SrcVariable) :-
1443 get_texpr_id(SrcVariable,Id),
1444 get_texpr_id(DstVariable,Id),
1445 ( memberchk(DstVariable,DstVariables) ->
1446 get_texpr_type(DstVariable,Type),
1447 get_texpr_type(SrcVariable,Type)
1448 ;
1449 true).
1450
1451
1452 % in case of a refinement, check if the defined operations are already defined
1453 % in the abstract machine and copy that type.
1454 type_refinement_operations(MName,RawMachine,AbstractMachine,Local,Ein,Eout) :-
1455 get_operation_identifiers(RawMachine,Operations),
1456 type_refinement_operations2(Operations,MName,Local,AbstractMachine,Ein,Eout).
1457 type_refinement_operations2([],_,_,_AbstractMachine,Errors,Errors).
1458 type_refinement_operations2([Op|Rest],MName,M,AbstractMachine,Ein,Eout) :-
1459 get_abstract_operation_name_wo_infos(Op,AOp),
1460 %print(treating_refines_operation(Op,AOp)),nl,
1461 get_texpr_pos(Op,Pos),
1462 % look up the abstract definition
1463 get_abstract_op(AOp,MName,AbstractMachine,Pos,Ein,E1),
1464 % store the type in the identifier section
1465 copy_texpr_wo_info(Op,SOp),
1466 ( get_section(promoted,M,Operations),
1467 memberchk(SOp, Operations) -> true
1468 ; get_section(unpromoted,M,Operations), % this is probably a LOCAL_OPERATION
1469 memberchk(SOp, Operations) -> true
1470 ; add_error(btypechecker,'Could not find operation for type checking:',Op),fail
1471 ),
1472 % do the rest
1473 type_refinement_operations2(Rest,MName,M,AbstractMachine,E1,Eout).
1474 % looks up the type of the operator in an abstract machine
1475 get_abstract_op(Op,_,Abstraction,_,Errors,Errors) :-
1476 % look for the operation in promoted and unpromoted
1477 ( get_section(promoted,Abstraction,AbstractOps), member(Op,AbstractOps)
1478 ; get_section(unpromoted,Abstraction,AbstractOps), member(Op,AbstractOps) ),
1479 % forget alternatives
1480 !.
1481 get_abstract_op(Op,_,_Abstraction,_Pos,Errors,Errors) :-
1482 % we might allow new operations
1483 get_preference(allow_new_ops_in_refinement,true),!, % ALLOW_NEW_OPERATIONS_IN_REFINEMENT
1484 get_texpr_type(Op,op(_,_)).
1485 get_abstract_op(Op,MName,AbstractMachine,Pos,[warning(Msg,Pos)|Eout],Eout) :-
1486 % in case we do not find the operation, store an error
1487 get_texpr_id(Op,op(Id)),
1488 get_machine_name(AbstractMachine,Name),
1489 ajoin(['operation ', Id, ' from ', MName, ' is not defined in the abstract machine ',Name,
1490 ' (set ALLOW_NEW_OPERATIONS_IN_REFINEMENT to TRUE to allow this)'], Msg).
1491 % copy a typed expression without the additional information (just expression and type)
1492 copy_texpr_wo_info(A,B) :-
1493 % copy the expression and type, the additional information may be different
1494 get_texpr_expr(A,Expr),get_texpr_expr(B,Expr),
1495 get_texpr_type(A,Type),get_texpr_type(B,Type).
1496
1497 get_abstract_operation_name_wo_infos(b(_,Type,Infos),Res) :-
1498 memberchk(refines_operation(RefID),Infos),!, % renaming occurs:
1499 Res = b(identifier(op(RefID)),Type,_).
1500 get_abstract_operation_name_wo_infos(ID,Copy) :- copy_texpr_wo_info(ID,Copy).
1501
1502 create_id_sections(Header,RawMachine,Name) -->
1503 create_id_sections_header(Header),
1504 %{print(created_header(Name)),nl},
1505 create_set_sections(RawMachine,Name),
1506 %{print(created_set(Name)),nl},
1507 create_constants_sections(RawMachine),
1508 %{print(created_constants(Name)),nl},
1509 create_variables_sections(RawMachine),
1510 %{print(created_variables(Name)),nl},
1511 create_operations_sections(RawMachine,Name).
1512
1513 extract_parameter_types(MachineRefs,ParameterTypes) :-
1514 maplist(extract_parameter_types2,MachineRefs,ParameterTypesL),
1515 append(ParameterTypesL,ParameterTypes).
1516 extract_parameter_types2(ref(_,Machine),ParameterTypes) :-
1517 get_section(parameters,Machine,VisibleParams),
1518 get_section(internal_parameters,Machine,InternalParams),
1519 append(VisibleParams,InternalParams,Params),
1520 include(is_a_parameter_set,Params,ParameterSets),
1521 maplist(get_texpr_set_type,ParameterSets,ParameterTypes).
1522 is_a_parameter_set(TExpr) :-
1523 get_texpr_info(TExpr,Info),
1524 memberchk(parameter_set,Info).
1525
1526 type_sections(RawMachine,MachineType,RefMachines,NonGroundExceptions,Name,Ein,Eout) -->
1527 {debug_stats('TYPING CONSTRAINTS'(Name))},
1528 type_constraints(MachineType,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,E1),
1529 % Maybe the VALUES section must be moved up later because it may be used to
1530 % substitute types (e.g. deferred sets to integers) for later use
1531 {debug_stats('TYPING PROPERTIES'(Name))},
1532 type_section_with_single_predicate(properties,Name,[constants],MachineType,RawMachine,RefMachines,NonGroundExceptions,E1,E2),
1533 {debug_stats('TYPING INVARIANT'(Name))},
1534 type_section_with_single_predicate(invariant,Name,[variables],MachineType,RawMachine,RefMachines,NonGroundExceptions,E2,E3),
1535 {debug_stats('TYPING ASSERTIONS'(Name))},
1536 type_section_with_predicate_list(assertions,[],MachineType,RawMachine,RefMachines,NonGroundExceptions,E3,E4),
1537 {debug_stats('TYPING INITIALISATION'(Name))},
1538 type_initialisation_section(RawMachine,Name,MachineType,RefMachines,NonGroundExceptions,E4,E5),
1539 {debug_stats('TYPING OPERATIONS'(Name))},
1540 type_operations_section(RawMachine,MachineType,RefMachines,NonGroundExceptions,E5,Eout),
1541 {debug_stats('FINISHED TYPING SECTIONS'(Name))}.
1542
1543 % skip this section, it is copied from the abstract machine and
1544 % does not need to be typed again
1545 type_constraints(refinement,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !.
1546 type_constraints(implementation,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !.
1547 type_constraints(machine,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,Eout) -->
1548 % if the machine is a dummy machine (in presence of a not included seen or used
1549 % machine), we must omit the check that the (lower case) parameters are all typed.
1550 % We can assume that they are properly typed by the included machine.
1551 {(is_dummy_machine(RawMachine) -> IdsToType = [] ; IdsToType = [parameters])},
1552 type_section_with_single_predicate(constraints,Name,IdsToType,machine,
1553 RawMachine,RefMachines,NonGroundExceptions,Ein,Eout).
1554
1555 ?is_dummy_machine(RawMachine) :- member(is_dummy,RawMachine),!.
1556
1557 % Header: Parameters
1558 create_id_sections_header(machine_header(_,_,Parameters),Old,New) :-
1559 write_section(parameters,TParams,Old,New),
1560 maplist(create_id_section_parameter,Parameters,TParams).
1561 create_id_section_parameter(Param,TParam) :-
1562 Expr=identifier(Name),
1563 ext2int_with_pragma(Param,Expr,_,Type,Expr,[ParameterType],TParam),
1564 ( is_upper_case(Name) ->
1565 ParameterType = parameter_set,
1566 Type = set(_)
1567 ;
1568 ParameterType = parameter_scalar).
1569 is_upper_case(Name) :- \+ atom(Name),!, add_internal_error('Illegal call:', is_upper_case(Name)),fail.
1570 is_upper_case(Name) :- atom_codes(Name,[C|_]),
1571 memberchk(C,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").
1572
1573 % Body: Sets
1574 create_set_sections(Sections,MachineName) -->
1575 write_section(deferred_sets,DeferredSets),
1576 write_section(enumerated_sets,EnumeratedSets),
1577 write_section(enumerated_elements,EnumeratedElements),
1578 {optional_rawmachine_section(sets,Sections,[],Sets),
1579 split_list(is_deferred_set_element,Sets,RawDeferredSets,RawEnumeratedSets),
1580 maplist(create_deferred_set_section(MachineName),RawDeferredSets,DeferredSets),
1581 maplist(create_enumerated_set_section(Sections,MachineName),
1582 RawEnumeratedSets,EnumeratedSets,LEnumeratedElements),
1583 append(LEnumeratedElements,EnumeratedElements)}.
1584 is_deferred_set_element(AstElem) :- has_functor(AstElem,deferred_set,_).
1585 create_deferred_set_section(MachineName,DS,TExpr) :-
1586 unwrap_opt_description(DS,deferred_set(Pos,I),TInfos),
1587 Infos = [given_set,def(deferred_set,MachineName)|TInfos],
1588 create_global_id(I,Pos,Infos,TExpr).
1589 create_enumerated_set_section(Sections,MachineName,ES,TExpr,Elements) :-
1590 unwrap_opt_description(ES,EnumSetList,TInfos),
1591 create_enum_set_aux(EnumSetList,Sections,MachineName,TInfos,TExpr,Elements).
1592
1593 create_global_id(identifier(_,Id),Pos,Infos,TExpr) :- !,
1594 add_warning(bmachine_construction,'Identifier unexpectedly not atomic: ',Id,Pos), % happened e.g. in ANTLR parser
1595 create_identifier(Id,Pos,set(global(Id)),Infos,TExpr).
1596 create_global_id(Id,Pos,Infos,TExpr) :- !,
1597 create_identifier(Id,Pos,set(global(Id)),Infos,TExpr).
1598
1599 create_enum_set_aux(enumerated_set(Pos,I,Elems),_,MachineName,TInfos,TExpr,Elements) :- !,
1600 % regular case I = {Elems1, ...}
1601 Infos = [given_set,def(enumerated_set,MachineName)|TInfos],
1602 create_global_id(I,Pos,Infos,TExpr),
1603 maplist(create_enum_set_element(I,MachineName),Elems,Elements).
1604 create_enum_set_aux(enumerated_set_via_def(Pos,I,ElemsDEF),Sections,MachineName,TInfos,TExpr,Elements) :- !,
1605 % we have the case I = ElemsDEF and DEFINITIONS ElemsDEF == {Elems1, ...}
1606 Infos = [given_set,def(enumerated_set,MachineName)|TInfos],
1607 create_global_id(I,Pos,Infos,TExpr),
1608 (optional_rawmachine_section(definitions,Sections,[],Defs),
1609 member(expression_definition(DPos,ElemsDEF,Paras,DefBody),Defs)
1610 -> (Paras \= []
1611 -> add_error(bmachine_construction,
1612 'DEFINITION for enumerated set elements must not have parameters:',ElemsDEF,DPos),
1613 Elements=[]
1614 ; DefBody = set_extension(_,Elems),
1615 maplist(create_enum_set_element(I,MachineName),Elems,Elements)
1616 -> true
1617 ; add_error(bmachine_construction,
1618 'DEFINITION for enumerated set elements must be of the form {El1,El2,...}:',ElemsDEF,DPos),
1619 Elements=[]
1620 )
1621 ; add_error(bmachine_construction,'No DEFINITION for enumerated set elements found:',ElemsDEF,Pos),
1622 Elements=[]
1623 ).
1624 create_enum_set_aux(E,_,_,_,_,_) :- add_internal_error('Illegal enumerated set:',E),fail.
1625
1626
1627 % deal with optional description(Pos,Desc,A) wrapper
1628 has_functor(description(_,_Desc,A),Functor,Arity) :- !, functor(A,Functor,Arity).
1629 has_functor(A,Functor,Arity) :- functor(A,Functor,Arity).
1630
1631 % remove description wrapper and generate info field
1632 unwrap_opt_description(Pragma,Res,Infos) :-
1633 unwrap_pragma(Pragma,Expr,I),!, Res=Expr,Infos=I.
1634 unwrap_opt_description(A,A,[]).
1635
1636
1637 create_enum_set_element(Id,MachineName,Ext,Elem) :-
1638 (Id=identifier(none,RID)
1639 -> Type=global(RID), add_warning(bmachine_construction,'Unexpected non-atomic global set id: ',RID)
1640 ; Type=global(Id)),
1641 ext2int_with_pragma(Ext,Expr,_Pos,Type,Expr,[enumerated_set_element,def(enumerated_element,MachineName)],Elem).
1642
1643 create_identifier(Id,Pos,Type,Infos,TExpr) :-
1644 create_texpr(identifier(Id),Type,[nodeid(Pos)|Infos],TExpr).
1645
1646 % Constants
1647 create_constants_sections(RawMachine) -->
1648 create_section_identifiers(constants,concrete_constants,RawMachine),
1649 create_section_identifiers(abstract_constants,abstract_constants,RawMachine).
1650 % Variables
1651 create_variables_sections(RawMachine) -->
1652 create_section_identifiers(concrete_variables,concrete_variables,RawMachine),
1653 create_section_identifiers(variables,abstract_variables,RawMachine).
1654
1655 % Freetypes: Treat them as additional constants, plus add entries in the "freetypes"
1656 % section of the resulting machine
1657 create_freetypes(RawMachine,MType,RefMachines,Old,New,Ein,Eout) :-
1658 optional_rawmachine_section(freetypes,RawMachine,[],RawFreetypes),
1659 ? create_freetypes2(RawFreetypes,MType,[ref(local,Old)|RefMachines],Old,New,Ein,Eout).
1660 create_freetypes2([],_MType,_RefMachines,M,M,E,E) :- !.
1661 create_freetypes2(RawFreetypes,MType,RefMachines,Old,New,Ein,Eout) :-
1662 % we need the NonGroundExceptions for type checking
1663 extract_parameter_types(RefMachines,NonGroundExceptions),
1664 % create identifiers in the constants section
1665 ? maplist(create_ids_for_freetype,RawFreetypes,IdsFreetypes),
1666 % we just combined the results to keep the numbers of arguments low (too much for maplist)
1667 maplist(split_ft,IdsFreetypes,IdentifiersL,Freetypes),
1668 append(IdentifiersL,Identifiers),
1669 % create properties for each freetype
1670 foldl(create_properties_for_freetype(MType,RefMachines,NonGroundExceptions,Identifiers),
1671 RawFreetypes,IdsFreetypes,PropertiesL,Ein,Eout),
1672 conjunct_predicates(PropertiesL,Properties),
1673 (debug_mode(off) -> true
1674 ; format('Created PROPERTIES for FREETYPES:~n',[]), translate:nested_print_bexpr(Properties),nl),
1675 append_to_section(abstract_constants,Identifiers,Old,Inter),
1676 conjunct_to_section(properties,Properties,Inter,Inter2),
1677 write_section(freetypes,Freetypes,Inter2,New).
1678
1679 split_ft(ft(Ids,Freetype),Ids,Freetype).
1680
1681
1682 create_ids_for_freetype(FT,ft([TId|TCons],freetype(Id,Cases))) :-
1683 is_freetype_declaration(FT,_Pos,Id,_TypeParams,Constructors),
1684 !,
1685 create_typed_id_with_given_set_info(Id,set(freetype(Id)),TId),
1686 ? maplist(create_ids_for_constructor(Id),Constructors,TCons,Cases).
1687 create_ids_for_freetype(FT,_) :-
1688 add_internal_error('Illegal freetype term:',create_ids_for_freetype(FT,_)),fail.
1689
1690 % add given_set info so that is_just_type3 can detect this as a type
1691 create_typed_id_with_given_set_info(IDName,Type,b(identifier(IDName),Type,[given_set])).
1692
1693 % deconstruct a .prob Prolog encoding of a freetype declaration
1694 % new versions of parser generate freetype parameters
1695 is_freetype_declaration(freetype(Pos,Id,Constructors),Pos,Id,[],Constructors).
1696 is_freetype_declaration(freetype(Pos,Id,TypeParams,Constructors),Pos,Id,TypeParams,Constructors) :-
1697 (TypeParams=[] -> true ; add_warning(bmachine_construction,'Not yet supporting freetype parameters:',Id,Pos)).
1698
1699 create_ids_for_constructor(Id,constructor(_Pos,Name,_Arg),TCons,case(Name,Type)) :-
1700 create_typed_id(Name,set(couple(Type,freetype(Id))),TCons).
1701 create_ids_for_constructor(Id,element(_Pos,Name),TCons,case(Name,constant([Name]))) :-
1702 create_typed_id(Name,freetype(Id),TCons).
1703
1704 create_properties_for_freetype(MType,RefMachines,NonGroundExceptions, AllFTIdentifiers,
1705 FREETYPE,ft(Ids,_Freetypes),Properties,Ein,Eout) :-
1706 is_freetype_declaration(FREETYPE,_Pos,Id,_TypeParams,Constructors),
1707 debug_format(9,'Processing freetype ~w (one of ~w)~n',[Id,AllFTIdentifiers]),
1708 % The freetype type
1709 FType = freetype(Id),
1710 % We use the standard type environment of properties...
1711 visible_env(MType,properties,RefMachines,CEnv,Ein,E1),
1712 % ...plus the identifiers of the free type (type name and constructor names)
1713 add_identifiers_to_environment(Ids,CEnv,FEnv0),
1714 % ...plus the identifiers of all free type names (will overwrite Id, but not a problem)
1715 % Note: instead of adding all freetype ids, we could just add the ones preceding Id
1716 add_identifiers_to_environment(AllFTIdentifiers,FEnv0,FEnv),
1717 % We generate a comprehension set for all elements
1718 create_typed_id(Id,set(FType),TId),
1719 create_texpr(equal(TId,TComp),pred,[],FDef),
1720 unique_typed_id("_freetype_arg",FType,Element),
1721 create_recursive_compset([Element],ECond,set(FType),[],RecId,TComp),
1722 create_typed_id(RecId,set(FType),TRecId),
1723 % For each constructor, we generate a definition and a condition for the
1724 % comprehension set above
1725 foldl(create_properties_for_constructor(Id,FEnv,Element,TRecId,NonGroundExceptions),
1726 Constructors,Defs,Conds,E1,Eout),
1727 conjunct_predicates(Conds,ECond),
1728 conjunct_predicates([FDef|Defs],Properties).
1729
1730 /* create_properties_for_constructor(+Env,+Element,+RecId,+NGE,+Constructor,-Def,-Cond,Ein,Eout)
1731 Env: Type environment
1732 Element: A typed identifier "e" that is used in the definition of the freetype set:
1733 ft = {e | e has_freetype_constructor x => e:...}
1734 RecId: The typed identifier that can be used to refer to the freetype set (ft in the
1735 example above
1736 NGE: "Non ground exceptions", needed for type checking when having a parametrized machine
1737 Constructor: The constructor expression as it comes from the parser
1738 (constructor(Pos,Name,ArgSet) or element(Pos,Name))
1739 Def: The predicate that defines the constant for the constructor,
1740 e.g. "cons = {i,o | i:NAT & o = freetype(cons,i)}"
1741 Cond: The predicate that checks the argument of a freetype in the freetype set
1742 (That would be the part "e:..." in the example above.
1743 Ein,Eout: Used for type checker errors
1744 */
1745 create_properties_for_constructor(FID,Env,Element,RecId,NonGroundExceptions,
1746 Constructor,Def,Cond,Ein,Eout) :-
1747 constructor_name(Constructor,Name),
1748 env_lookup_type(Name,Env,CType),
1749 create_properties_for_constructor2(Constructor,Env,NonGroundExceptions,FID,
1750 Element,RecId,CDef,Cond,Ein,Eout),
1751 get_texpr_type(CDef,CType),
1752 create_typed_id(Name,CType,CId),
1753 create_texpr(equal(CId,CDef),pred,[],Def).
1754 constructor_name(element(_Pos,Name),Name).
1755 constructor_name(constructor(_Pos,Name,_Domain),Name).
1756 create_properties_for_constructor2(element(_Pos,Name),_Env,_NonGroundExceptions,FID,
1757 _Element,_RecId,CDef,Cond,Ein,Ein) :-
1758 create_texpr(value(freeval(FID,Name,term(Name))),freetype(FID),[],CDef),
1759 create_texpr(truth,pred,[],Cond).
1760 create_properties_for_constructor2(constructor(_Pos,Name,RArg),Env,NonGroundExceptions,
1761 FID,Element,RecId,CDef,Cond,Ein,Eout) :-
1762 % First, type check the given set of the domain:
1763 btype_ground_dl([RArg],Env,NonGroundExceptions,[set(DType)],[TDomain],Ein,Eout),
1764 % then create the RHS of "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}"
1765 create_definition_for_constructor(Name,TDomain,FID,CDef),
1766 % The check in the freetype comprehension set is of the form
1767 % e "of_freetype_case" Name => "content_of"(e) : Domain
1768 create_texpr(implication(IsCase,DomainTest),pred,[],Cond),
1769 create_texpr(freetype_case(FID,Name,Element),pred,[],IsCase),
1770 create_texpr(freetype_destructor(FID,Name,Element),DType,[],Content),
1771 % all references to the freetype itself are replaced by the recursive reference
1772 replace_id_by_expr(TDomain,FID,RecId,TDomain2),
1773 create_texpr(member(Content,TDomain2),pred,[],DomainTest).
1774
1775 :- use_module(bsyntaxtree,[get_texpr_set_type/2]).
1776
1777 /* The constructor is a function to the freetype, defined with a comprehension set:
1778 The general form is "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}"
1779 create_definition_for_constructor(+Name,+DType,+FID,-CType,-CDef) :-
1780 Name: Name of the constructor
1781 TDomain: The user-specified domain
1782 FID: The name of the free type
1783 CDef: The RHS of the definition "Name = CDef"
1784 */
1785 create_definition_for_constructor(Name,TDomain,FID,CDef) :-
1786 % get the type of the domain:
1787 get_texpr_set_type(TDomain,DType),
1788 % create argument and result identifiers:
1789 unique_typed_id("_constr_arg",DType,TArgId), % was constructor_arg and constructor_res
1790 unique_typed_id("_constr_res",freetype(FID),TResId),
1791 % The comprehension set as a whole
1792 CType = set(couple(DType,freetype(FID))),
1793 create_texpr(comprehension_set([TArgId,TResId],Pred),CType,
1794 [prob_annotation('SYMBOLIC')],CDef),
1795 create_texpr(conjunct(DomainCheck,Construction),pred,[],Pred),
1796 % "i:Domain"
1797 create_texpr(member(TArgId,TDomain),pred,[],DomainCheck),
1798 % "o=freetype_constructor(i)
1799 create_texpr(freetype_constructor(FID,Name,TArgId),freetype(FID),[],FreetypeCons),
1800 create_texpr(equal(TResId,FreetypeCons),pred,[],Construction).
1801
1802 % Operations
1803 create_operations_sections(RawMachine,Name,Old,New) :-
1804 write_section(promoted,PromotedOperationIds,Old,New0),
1805 get_operation_identifiers(RawMachine,operations,OperationIdentifiers),
1806 (get_operation_identifiers(RawMachine,local_operations,LocOpIDs),
1807 LocOpIDs \= [] % if there is a LOCAL_OPERATIONS section we assume user wants to call them
1808 -> maplist(add_infos_to_identifier([is_local_operation]),LocOpIDs,LocOpIds2),
1809 append_to_section(unpromoted,LocOpIds2,New0,New),
1810 % Now remove all LOCAL_OPERATIONS from the promoted ones
1811 % see for example prob_examples/examples/B/ClearSy/RoboSim/SRanger_case_Jul25/logic_i.imp
1812 exclude(duplicate_local_operation_id(Name,LocOpIDs),OperationIdentifiers,PromotedOperationIds)
1813 ; New = New0, PromotedOperationIds = OperationIdentifiers
1814 ).
1815
1816
1817
1818 % check if a LOCAL_OPERATION is already declared in the OPERATIONS section:
1819 % in this case: we use the operation body defined in the OPERATIONS section, but remove it from the promoted list
1820 duplicate_local_operation_id(Name,OpIds,TID) :-
1821 get_texpr_id(TID,op(OpID)),
1822 member(TID2,OpIds),
1823 get_texpr_id(TID2,op(OpID)),!,
1824 debug_println(9,duplicate_local_operation_id(OpID,Name)).
1825
1826 get_operation_identifiers(RawMachine,OperationIdentifiers) :-
1827 get_operation_identifiers(RawMachine,operations,OperationIdentifiers).
1828 get_operation_identifiers(RawMachine,SECTION,OperationIdentifiers) :-
1829 optional_rawmachine_section(SECTION,RawMachine,[],Ops),
1830 maplist(extract_operation_identifier,Ops,OperationIdentifiers).
1831 extract_operation_identifier(Ext,TId) :-
1832 remove_pos(Ext, operation(ExtId,_,_,_)),!,
1833 ext2int_with_pragma(ExtId,identifier(I),_,op(_,_),identifier(op(I)),Infos,TId),
1834 operation_infos(Infos).
1835 extract_operation_identifier(Ext,TId) :-
1836 remove_pos(Ext, refined_operation(ExtId,_,_,RefinesOp,_)),!,
1837 ext2int_with_pragma(ExtId,identifier(I),_,op(_,_),identifier(op(I)),[refines_operation(RefinesOp)|Infos],TId),
1838 operation_infos(Infos).
1839 extract_operation_identifier(Ext,TId) :-
1840 remove_pos(Ext, description_operation(_Desc,RealOp)),!,
1841 extract_operation_identifier(RealOp,TId).
1842 extract_operation_identifier(Ext,_) :- add_internal_error('Unknown operation node:',Ext),fail.
1843
1844 % VALUES section:
1845 % process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut):
1846 % Type-check the VALUES section and change the type of valuated deferred sets, if necessary
1847 % MachineType, RawMachine, NonGroundExceptions: as usual, see other comments
1848 % Min/Mout: The currently constructed machine
1849 % Ein/Eout: The difference list of errors
1850 % RefMin/RefMout: The list of already typechecked machines. These typechecked machines can be
1851 % altered by this predicate because if a deferred set is valuated by an integer set or
1852 % other deferred set, all occurrences of the original type are replaced by the new type,
1853 % even for the already typed machines.
1854 process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut) :-
1855 optional_rawmachine_section(values,RawMachine,[],RawValues),
1856 process_values_section_aux(RawValues,MachineType,NonGroundExceptions,
1857 Min/Ein/RefMIn,Mout/Eout/RefMOut).
1858 process_values_section_aux([],_MachineType,_NonGroundExceptions,In,Out) :- !,In=Out.
1859 process_values_section_aux(RawValues,MachineType,NonGroundExceptions,
1860 Min/Ein/RefMin,Mout/Eout/RefMout) :-
1861 debug_stats('TYPING VALUES'),
1862 type_values_section(MachineType,RawValues,RefMin,NonGroundExceptions,Min/Ein,Mout/Eout),
1863 % will be added as additional_property in bmachine
1864 RefMin=RefMout.
1865
1866 type_values_section(MachineType,RawValues,RefMachines,NonGroundExceptions,Min/Ein,Mout/Eout) :-
1867 write_section(values,Values,Min,Mout),
1868 visible_env(MachineType,values_expression,RefMachines,Env,Ein,E1),
1869 % We have to pass an environment that can be modified because in each
1870 % valuation the previously valuated constants can be used.
1871 foldl(extract_values_entry(NonGroundExceptions),RawValues,Values,Env/E1,_ResultingEnv/Eout).
1872
1873 extract_values_entry(NonGroundExceptions, values_entry(POS,ValueID,ValueExpr), Entry,
1874 EnvIn/Ein,EnvOut/Eout) :-
1875 % TODO: There seem to be a lot of additional constraints for valuations in VALUES that are not
1876 % yet handled here
1877 btype_ground_dl([ValueExpr],EnvIn,NonGroundExceptions,[Type],[TExpr],Ein,Eout),
1878 clean_up(TExpr,[],CTExpr), % ensure we remove things like mult_or_cart/2
1879 create_identifier(ValueID,POS,Type,[valuated_constant],TypedID),
1880 create_texpr(values_entry(TypedID,CTExpr),values_entry,[nodeid(POS)],Entry),
1881 debug_println(9,valued_constant(ValueID)),
1882 EnvOut=EnvIn.
1883 %add_identifiers_to_environment([TypedID],EnvIn,EnvOut). % ideally we should register the new type of TypedID
1884 % However: ProB can currently only process VALUES clauses where the type does not change
1885
1886 % type_section_with_single_predicate(+Sec,+Name,+SectionsToType,+MachineType,+Sections,
1887 % +RefMachines,+NonGroundExceptions,+Ein,-Eout,+Old,-New):
1888 % Type a section such as INVARIANT, PROPERTIES, CONSTRAINTS with a single predicate
1889 % Sec: section name in the raw (untyped) machine (e.g. invariant)
1890 % Name: name of machine from which this section comes
1891 % SectionsToType: list of section names that contain identifiers that must be typed
1892 % by this predicate (e.g. all variables must be typed by the invariant)
1893 % MachineType: machine type (machine, refinement, ...)
1894 % Sections: list of sections representing the raw (untyped) machine
1895 % RefMachines: list of already typed machines
1896 % NonGroundExceptions: list of types that may be not ground because the concrete type
1897 % is determinded by machine parameter
1898 % Ein/Eout: difference list of errors
1899 % Old/New: the new typed section is added (by conjunction) to the machine
1900 type_section_with_single_predicate(Sec,Name,SectionsToType,MachineType,Sections,
1901 RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :-
1902 optional_rawmachine_section(Sec,Sections,truth(none),Predicate),
1903 ( Predicate = truth(_) ->
1904 % even if there is no content, we must check if all identifiers are typed
1905 check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout),
1906 Old=New
1907 ;
1908 get_machine_infos(Sections,Infos),
1909 toplevel_raw_predicate_sanity_check(Sec,Name,Predicate,Infos),
1910 type_predicates(Sec,SectionsToType,MachineType,[Predicate],RefMachines,NonGroundExceptions,
1911 [Typed],Ein,Eout),
1912 conjunct_to_section(Sec,Typed,Old,New)
1913 ),
1914 !.
1915 type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_) :-
1916 add_internal_error('type_section_with_single_predicate failed',
1917 type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_)),
1918 fail.
1919
1920 % get some infos relevant for sanity check:
1921 get_machine_infos(Sections,Infos) :-
1922 ((rawmachine_section_exists(concrete_variables,Sections) ; rawmachine_section_exists(abstract_variables,Sections))
1923 -> Infos = [has_variables|I1] ; Infos = I1),
1924 ((rawmachine_section_exists(concrete_constants,Sections) ; rawmachine_section_exists(abstract_constants,Sections))
1925 -> I1 = [has_constants] ; I1 = []).
1926
1927 % Type a section with multiple predicates, such as ASSERTIONS
1928 type_section_with_predicate_list(Sec,SectionsToType,MachineType,Sections,
1929 RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :-
1930 write_section(Sec,Typed,Old,New),
1931 optional_rawmachine_section(Sec,Sections,[],Predicates),
1932 type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout),
1933 !.
1934 type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New) :-
1935 add_internal_error('type_section_with_predicate_list failed',
1936 type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New)),
1937 fail.
1938
1939
1940 type_predicates(_Sec,SectionsToType,_MachineType,[],RefMachines,NonGroundExceptions,Typed,Ein,Eout) :-
1941 !,Typed=[],
1942 check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout).
1943 type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout) :-
1944 visible_env(MachineType, Sec, RefMachines, Env, Ein, E1),
1945 same_length(Predicates,Types),maplist(is_pred_type,Types),
1946 btype_ground_dl_in_section(Sec,Predicates,Env,NonGroundExceptions,Types,Typed1,E1,E2),
1947 mark_with_section(Sec,Typed1,Typed),
1948 check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,E2,Eout),
1949 (no_perrors_occured(Ein,Eout)
1950 -> perform_post_static_check(Typed) % only run if no type errors, it makes use of find_typed_identifier_uses
1951 ; true
1952 ).
1953
1954 no_perrors_occured(Ein,Eout) :- Ein==Eout,!.
1955 no_perrors_occured([H|T],Eout) :- nonvar(H),not_a_perror(H),no_perrors_occured(T,Eout).
1956
1957 not_a_perror(warning(_,_)).
1958 % other possible values: error(Msg,Pos), internal_error(Msg,Pos); see get_perror/2
1959
1960 % check if the identifiers that should be typed by this section are completly typed
1961 check_if_all_ids_are_typed([],_RefMachines,_NonGroundExceptions,Ein,Eout) :- !,Ein=Eout.
1962 check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout) :-
1963 memberchk(ref(local,Local),RefMachines),
1964 get_all_identifiers(SectionsToType,Local,IdentifiersToType),
1965 check_ground_types_dl(IdentifiersToType, NonGroundExceptions, Ein, Eout).
1966
1967
1968 mark_with_section(Sec,In,Out) :-
1969 maplist(mark_with_section2(Sec),In,Out).
1970 mark_with_section2(Sec,In,Out) :-
1971 remove_bt(In,conjunct(A,B),conjunct(NA,NB),Out),!,
1972 mark_with_section2(Sec,A,NA), mark_with_section2(Sec,B,NB).
1973 mark_with_section2(Sec,In,Out) :-
1974 add_texpr_infos(In,[section(Sec)],Out).
1975
1976 type_initialisation_section(Sections,Name,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :-
1977 write_section(initialisation,Initialisation,Old,New),
1978 ( rawmachine_section_with_opt_desc(initialisation,Sections,Init,Desc,DescPos) ->
1979 visible_env(MType, initialisation, RefMachines, InitEnv, Ein, E1),
1980 btype_ground_dl([Init],InitEnv,NonGroundExceptions,[subst],[TypedInit],E1,Eout),
1981 (Desc='' -> TypedInit2 = TypedInit
1982 ; add_texpr_infos(TypedInit,[description(Desc),description_position(DescPos)],TypedInit2)
1983 ),
1984 Initialisation=[init_stmt(Name,TypedInit2)]
1985 ;
1986 Ein=Eout,
1987 Initialisation=[]).
1988
1989 :- use_module(library(ugraphs)).
1990
1991 type_operations_section(Sections,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :-
1992 write_section(operation_bodies,Operations,Old,New),
1993 visible_env(MType, operation_bodies, RefMachines, Env, Ein, E1),
1994 optional_rawmachine_section(operations,Sections,[],Ops1),
1995 optional_rawmachine_section(local_operations,Sections,[], LocalOps2),
1996 exclude(duplicate_local_operation(Ops1),LocalOps2,FOps2),
1997 append(FOps2,Ops1,Ops),
1998 topological_sort_operations(Ops,LocalOps2,Env,SOps),
1999 (debug_mode(off) -> true ; length(SOps,NrOps),debug_stats(finished_topological_sorting(NrOps))),
2000 same_length(SOps,Types), maplist(is_op_type,Types),
2001 ? btype_ground_dl(SOps,Env,NonGroundExceptions,Types,Operations,E1,Eout),!.
2002
2003 % check if a LOCAL_OPERATION also exists as a regular operation; if so we can ignore it and
2004 % just use the full operation; see also duplicate_local_operation_id above
2005 duplicate_local_operation(NonLocalOps,LocalOperation) :-
2006 raw_op_id(LocalOperation,OpName),
2007 member(NonLocalOp,NonLocalOps),
2008 raw_op_id(NonLocalOp,OpName), % we found a non-local operation with the same name
2009 !,
2010 raw_op_pos(LocalOperation,LPos),
2011 raw_op_pos(NonLocalOp,NPos),
2012 translate_span(NPos,PS2),
2013 ajoin(['Ignoring LOCAL_OPERATION already defined in OPERATIONS section ',PS2,': '],Msg),
2014 add_message(bmachine_construction,Msg,OpName,LPos).
2015
2016
2017 allow_local_or_expr_op_calls :-
2018 (get_preference(allow_local_operation_calls,true) -> true
2019 ; get_preference(allow_operation_calls_in_expr,true)).
2020 % at the moment ProB allows local calls inside expressions, independently of the allow_local_operation_calls preference
2021
2022 % perform a topological sort of the operations: treat called operation before calling operation
2023 % only relevant when allow_local_operation_calls is set to true
2024 topological_sort_operations(Ops,LocalOps,Env,SortedOps) :-
2025 (LocalOps = [] % there are no LOCAL_OPERATIONS
2026 -> allow_local_or_expr_op_calls % unless call operations in machine itself, no need to perform a topological sort
2027 ; true % we have local_operations, we need topological sort to ensure they are processed before other operations
2028 ),
2029 findall(OtherID-ID, (member(Op,Ops),op_calls_op(Op,Env,ID,OtherID)),Edges),
2030 % print(edges(Edges)),nl,
2031 % TO DO: maybe only store edges where OtherID also appears in Ops (i.e., call within same machine)
2032 Edges \= [],
2033 !,
2034 findall(ID,(member(operation(_,RawID,_,_,_),Ops),raw_id(RawID,ID)),Vertices), %print(vertices(Vertices)),nl,
2035 vertices_edges_to_ugraph(Vertices,Edges,Graph),
2036 (top_sort(Graph,Sorted)
2037 -> sort_ops(Sorted,Ops,SortedOps)
2038 ; get_preference(allow_operation_calls_in_expr,true) ->
2039 add_warning(topological_sort,'Mutual recursion or cycle in the (local) operation call graph, this may cause problems computing reads information: ',Edges),
2040 SortedOps=Ops
2041 % not necessarily a problem, because operations called in expressions are not allowed to modify the state
2042 % TODO: however, we may have an issue with computing reads information correctly for mutual recursion !?
2043 % direct recursion should be ok
2044 ; add_error(topological_sort,'Cycle in the (local) operation call graph: ',Edges),
2045 SortedOps=Ops).
2046 topological_sort_operations(Ops,_,_,Ops).
2047
2048 sort_ops([],Ops,Ops). % Ops should be []
2049 sort_ops([OpID|T],Ops,Res) :-
2050 raw_op_id(Op1,OpID),
2051 (select(Op1,Ops,TOps)
2052 -> Res = [Op1|TSOps], sort_ops(T,TOps,TSOps)
2053 ; % operation from another machine
2054 % print(could_not_find(OpID,Ops)),nl,
2055 sort_ops(T,Ops,Res)
2056 ).
2057
2058 is_op_type(op(_,_)).
2059 is_pred_type(pred).
2060
2061 % compute which other operations are directly called
2062 op_calls_op(operation(_,RawID,_,_,RawBody),Env,ID,OtherID) :- raw_id(RawID,ID),
2063 raw_body_calls_operation(RawBody,ID,Env,OtherID).
2064
2065 raw_body_calls_operation(RawBody,ID,Env,OtherID) :-
2066 raw_member(OpCall,RawBody),
2067 raw_op_call(OpCall,ID,Env,RawOtherID), raw_id(RawOtherID,OtherID).
2068
2069 raw_op_call(operation_call(_,RawOtherID,_,_),_,_, RawOtherID).
2070 raw_op_call(operation_call_in_expr(_,RawOtherID,_),ID,_, RawOtherID) :-
2071 \+ raw_id(RawOtherID,ID). % we do not look at direct recursion: it poses no problem for computing reads/writes info
2072 raw_op_call(function(_,RawOtherID,_), ID, Env, RawOtherID) :- % function calls possibly not yet translated to operation_call_in_expr
2073 get_preference(allow_operation_calls_in_expr,true),
2074 \+ raw_id(RawOtherID,ID), % direct recursion ok
2075 btypechecker:is_operation_call(RawOtherID,Env).
2076 raw_op_call(identifier(Pos,OtherID), ID, Env, RawOtherID) :- % possible operation call in expr without arguments
2077 OtherID \= ID, % direct recursion ok
2078 get_preference(allow_operation_calls_in_expr,true),
2079 RawOtherID = identifier(Pos,OtherID),
2080 btypechecker:is_operation_call(RawOtherID,Env).
2081
2082
2083 raw_op_id(operation(_,RawID,_,_,_RawBody),ID) :- raw_id(RawID,ID).
2084 raw_id(identifier(_,ID),ID).
2085 raw_op_pos(operation(Pos,_RawID,_,_,_RawBody),Pos).
2086
2087 % a utility function to work on the raw AST Functor(POS,Arg1,...,Argn)
2088 % this will not be able to look inside DEFINITIONS !
2089 % TO DO: deal with more raw substitutions which have list arguments
2090 raw_member(X,X).
2091 raw_member(X,parallel(_,List)) :- !, member(Term,List), raw_member(X,Term).
2092 raw_member(X,sequence(_,List)) :- !, member(Term,List), raw_member(X,Term).
2093 raw_member(X,[H|T]) :- !, (raw_member(X,H) ; raw_member(X,T)).
2094 raw_member(X,Term) :- compound(Term), Term =.. [_Functor,_Pos|Args],
2095 member(A,Args), raw_member(X,A).
2096
2097
2098 create_section_identifiers(Section,DestSection,RawMachine,Old,New) :-
2099 write_section(DestSection,Vars,Old,New),
2100 optional_rawmachine_section(Section,RawMachine,[],Identifiers),
2101 create_section_ids2(Identifiers,[],Vars,DestSection,New).
2102
2103 create_section_ids2([],_,[],_,_).
2104 create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :-
2105 expand_definition_to_variable_list(Ext,MachSections,List),!,
2106 append(List,Rest,NewList),
2107 create_section_ids2(NewList,Infos,Res,DestSection,MachSections).
2108 create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :-
2109 create_section_id(Ext,Infos,DestSection,TId),
2110 ( TId = error(Msg,Term,Pos) ->
2111 Res = TRest, add_error(bmachine_construction,Msg,Term,Pos)
2112 ;
2113 Res = [TId|TRest]),
2114 create_section_ids2(Rest,Infos,TRest,DestSection,MachSections).
2115 create_section_id(Ext,Infos,DestSection,TId) :-
2116 (unwrap_pragma(Ext,Ext2,PragmaInfos) -> append(PragmaInfos,Infos,FullInfos)
2117 ; Ext2=Ext, FullInfos=Infos),
2118 I=identifier(_),
2119 ( ext2int(Ext2,I,_Pos,_Type,I,FullInfos,TId) ->
2120 true
2121 ; Ext2 = definition(POSINFO,ID,_) ->
2122 TId = error('Trying to use DEFINITION name as identifier: ',
2123 (ID,within(DestSection)), POSINFO)
2124 ;
2125 TId = error('Illegal identifier: ',
2126 (Ext2,within(DestSection)), Ext2)
2127 ).
2128
2129 % support using DEFINITIONS which are variable lists;
2130 % currently for ProB parser you need to write VARS == (x,y,..) for Atelier-B: VARS == x,y,..
2131 expand_definition_to_variable_list(definition(_POSINFO,ID,_),MachSections,List) :-
2132 get_section(definitions,MachSections,Defs),
2133 member(definition_decl(ID,expression,_DefInfos,_InnerPos,[],RawExpr,_Deps),Defs),
2134 % we could check if DefInfos contains hygienic_def
2135 extract_identifier_list(RawExpr,List,[]).
2136
2137 % convert a raw tuple into a raw identifier list:
2138 extract_identifier_list(identifier(Pos,ID)) --> [identifier(Pos,ID)].
2139 extract_identifier_list(couple(_,List)) -->
2140 extract_identifier_list(List).
2141 extract_identifier_list([]) --> [].
2142 extract_identifier_list([H|T]) --> extract_identifier_list(H), extract_identifier_list(T).
2143
2144
2145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2146 % sort the machines topologically
2147
2148 % can be used to store the result:
2149 assert_machine_order(Order) :-
2150 retractall(machine_global_order(_)),
2151 debug_print(9,machine_global_order(Order)),
2152 assertz(machine_global_order(Order)).
2153
2154 get_mach_position(Order,M,Ref,Pos-mch(M,Ref)) :-
2155 get_machine_name(M,Name),
2156 ? (nth1(Pos,Order,Name) -> true
2157 ; add_internal_error('Unknown machine:',Name:Order),
2158 Pos=0).
2159 get_machine(_-mch(M,_),M). % re-extract info after sorting
2160 get_reference(_-mch(_,R),R).
2161
2162 :- use_module(library(samsort),[samkeysort/2]).
2163 sort_machines_by_global_order(Machines,SortedMachines) :-
2164 sort_machines_by_global_order(Machines,_,SortedMachines,_).
2165 sort_machines_by_global_order(Machines,Refs,SortedMachines,SortedRefs) :-
2166 machine_global_order(Order),
2167 maplist(get_mach_position(Order),Machines,Refs,KM), % add position so that sorting works
2168 samkeysort(KM,SKM), % for test 925 it is important to keep duplicates and not use sort/2
2169 maplist(get_machine,SKM,SortedMachines),
2170 maplist(get_reference,SKM,SortedRefs),!.
2171 %maplist(get_machine_name,Sorted,SNs),print(sorted(SNs)),nl.
2172 sort_machines_by_global_order(M,R,M,R) :-
2173 add_internal_error('Sorting machines failed:',M).
2174
2175 % perform the actual computation:
2176 machine_order(Machines,Order) :-
2177 machine_dependencies(Machines,Dependencies),
2178 topsort(Dependencies,Order).
2179
2180 % sort the machines topologically
2181 topsort(Deps,Sorted) :-
2182 topsort2(Deps,[],Sorted).
2183 topsort2([],_,[]) :- !.
2184 topsort2(Deps,Known,Sorted) :-
2185 split_list(all_deps_known(Known),Deps,DAvailable,DNotAvailable),
2186 DAvailable = [_|_], % we have new machines available whose dependencies are all known
2187 !,
2188 maplist(dep_name,DAvailable,Available),
2189 append(Available,Known,NewKnown),
2190 append(Available,Rest,Sorted),
2191 topsort2(DNotAvailable,NewKnown,Rest).
2192 topsort2(Deps,_Known,_) :-
2193 member(dep(Name,NameDeps),Deps),
2194 add_error(bmachine_construction,'Could not resolve machine dependencies for: ',Name:depends_on(NameDeps)),
2195 fail.
2196
2197 ?all_deps_known(K,dep(_Name,Deps)) :- sort(Deps,DS),sort(K,KS),subseq0(KS,DS),!.
2198 dep_name(dep(Name,_Deps),Name).
2199
2200 % find dependencies between machines
2201 machine_dependencies(Machines,Dependencies) :-
2202 maplist(machine_dependencies2,Machines,Deps),
2203 sort(Deps,Dependencies).
2204 machine_dependencies2(M,dep(Name,Deps)) :-
2205 get_constructed_machine_name_and_body(M,Name,_,Body),
2206 findall(Ref,
2207 (refines(M,Ref);machine_reference(Body,Ref)),
2208 Deps).
2209
2210 machine_reference(MachineBody,Ref) :-
2211 ? ( member(sees(_,R),MachineBody)
2212 ; member(uses(_,R),MachineBody) ),
2213 ? member(identifier(_,PrefixRef),R),
2214 split_prefix(PrefixRef,_,Ref).
2215 machine_reference(MachineBody,Ref) :-
2216 ( member(includes(_,R),MachineBody)
2217 ? ; member(extends(_,R),MachineBody)
2218 ; member(imports(_,R),MachineBody) ),
2219 ? member(machine_reference(_,PrefixRef,_),R),
2220 split_prefix(PrefixRef,_,Ref).
2221
2222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2223 % refinements
2224 merge_refinement_and_abstraction(Name,Concrete,RefMachines,Result,Ein,Eout) :-
2225 memberchk(ref(abstraction,Abstraction),RefMachines),
2226 append_sections([sets,concrete_constants,concrete_variables],Abstraction,Concrete,M1),
2227 append_if_new(abstract_constants,Abstraction,M1,M2),
2228 get_section(properties,Abstraction,AbstractProperties),
2229 conjunct_to_section(properties,AbstractProperties,M2,M3),
2230 % now get current invariant Invariant and filter out gluing/linking invariant
2231 % (the linking invariant talks about variables which no longer exist; hence we cannot check it anymore)
2232 select_section(invariant,Invariant,FullConcreteInvariant,M3,M4),
2233 write_section(linking_invariant,LinkingInvariant,M4,Merged),
2234 % we now also copy from the abstraction those invariants which are still valid
2235 get_machine_sorted_variables(Abstraction,SortedAbsVars),
2236 get_machine_sorted_variables(Concrete,SortedConcrVars),
2237 assert_removed_abs_vars(SortedAbsVars,SortedConcrVars,Name),
2238 get_section(invariant,Abstraction,AbstractInvariant),
2239 filter_abstract_invariant(AbstractInvariant,SortedConcrVars,AbsInvariantStillValid),
2240 filter_linking_invariant(Invariant,LinkingInvariant,ConcreteInvariant),
2241 conjunct_predicates([AbsInvariantStillValid,ConcreteInvariant],FullConcreteInvariant),
2242 propagate_abstract_operations(Abstraction,Merged,RefMachines,Result,Ein,Eout).
2243 merge_refinement_and_abstraction(_,Machine,_,Machine,Errors,Errors).
2244
2245 :- use_module(probsrc(bsyntaxtree), [same_id/3]).
2246 assert_removed_abs_vars([],_,_).
2247 assert_removed_abs_vars([TID1|H],[],Name) :- !,
2248 assert_abstract_variable_removed_in(Name,TID1),
2249 assert_removed_abs_vars(H,[],Name).
2250 assert_removed_abs_vars([TID1|T1],[TID2|T2],Name) :- same_id(TID1,TID2,_),!,
2251 assert_removed_abs_vars(T1,T2,Name).
2252 assert_removed_abs_vars([TID1|T1],[TID2|T2],Name) :- TID1 @< TID2,!,
2253 assert_abstract_variable_removed_in(Name,TID1),
2254 assert_removed_abs_vars(T1,[TID2|T2],Name).
2255 assert_removed_abs_vars([TID1|T1],[_|T2],Name) :- % TID2 added in Name
2256 assert_removed_abs_vars([TID1|T1],T2,Name).
2257
2258
2259 assert_abstract_variable_removed_in(Name,TID) :-
2260 get_texpr_id(TID,ID),
2261 ajoin(['Variable ',ID,' removed in refinement machine: '],Msg),
2262 add_debug_message(bmachine_construction,Msg,Name,TID),
2263 assertz(abstract_variable_removed_in(ID,Name,TID)).
2264
2265
2266 % append sections from abstract machine to concrete machine:
2267 append_sections(Sections,AbsMachine,Old,New) :-
2268 expand_shortcuts(Sections,AllSections),
2269 append_sections2(AllSections,AbsMachine,Old,New).
2270 append_sections2([],_,M,M).
2271 append_sections2([Section|Rest],AbsMachine,Old,New) :-
2272 get_section(Section,AbsMachine,Content),
2273 append_to_section3(Section,Content,Old,Inter),
2274 append_sections2(Rest,AbsMachine,Inter,New).
2275
2276 append_to_section3(Section,Content,Old,Inter) :- section_can_have_duplicates(Section),!,
2277 append_to_section_and_remove_dups(Section,Content,Old,Inter).
2278 append_to_section3(Section,Content,Old,Inter) :- append_to_section(Section,Content,Old,Inter).
2279
2280 :- assert_must_succeed((create_machine(abs,EA), create_machine(conc,EB),
2281 write_section(abstract_constants,[b(identifier(x),integer,[some_info])],EA,A),
2282 write_section(abstract_constants,[b(identifier(x),integer,[other_info]),
2283 b(identifier(y),integer,[info])],EB,B),
2284 append_if_new(abstract_constants,A,B,ResultM),
2285 get_section(abstract_constants,ResultM,ResultConst),
2286 ResultConst==[b(identifier(x),integer,[other_info]),
2287 b(identifier(y),integer,[info])]
2288 )).
2289
2290 append_if_new(Section,Machine,In,Out) :-
2291 get_section(Section,Machine,Content),
2292 select_section(Section,Old,New,In,Out),
2293 get_texpr_ids(Old,ExistingIds),
2294 exclude(is_in_existing_ids(ExistingIds),Content,NewElements),
2295 append(Old,NewElements,New).
2296 is_in_existing_ids(ExistingIds,TId) :-
2297 get_texpr_id(TId,Id),
2298 memberchk(Id,ExistingIds).
2299
2300 % filter linking and concrete invariant
2301 filter_linking_invariant(Invariant,Linking,Concrete) :-
2302 split_conjuncts(Invariant,Predicates),
2303 split_list(contains_abstraction_refs,Predicates,Linkings,Concretes),
2304 conjunct_predicates(Linkings,Linking),
2305 conjunct_predicates(Concretes,Concrete).
2306
2307
2308 % contains_abstraction_refs can be used on predicates of the current machine: the abstraction info field has been computed for this machine
2309 contains_abstraction_refs(TExpr) :-
2310 syntaxtraversion(TExpr,_,_,Infos,Subs,_),
2311 ( memberchk(abstraction,Infos) % This info field comes from the last argument of visibility/6
2312 -> true
2313 ; member(Sub,Subs),
2314 contains_abstraction_refs(Sub)).
2315
2316 % Determine which part of the abstract invariant can be imported into the refinement machine
2317 % TODO: should this be applied to WHILE loop INVARIANTS?
2318 filter_abstract_invariant(AbsInvariant,SortedConcrVars,ConcreteInv) :-
2319 split_conjuncts(AbsInvariant,Predicates),
2320 filter_abs_invs(Predicates,SortedConcrVars,Concretes),
2321 conjunct_predicates(Concretes,ConcreteInv). %, print('COPY: '), translate:print_bexpr(Concrete),nl.
2322 :- use_module(translate,[translate_bexpression/2]).
2323
2324 filter_abs_invs([],_,[]).
2325 filter_abs_invs([TExpr|TInv],SortedConcrVars,Res) :-
2326 contains_abstract_variables2(SortedConcrVars,TExpr,Cause),!,
2327 (silent_mode(on) -> true
2328 ; translate_bexpression(Cause,ID),
2329 ajoin(['Discarding abstract INVARIANT (requires variable `',ID,'`): '],Msg),
2330 add_message(bmachine_construction,Msg,TExpr,Cause)
2331 ),
2332 (adapt_invariant(SortedConcrVars,TExpr,NewTExpr)
2333 -> Res = [NewTExpr|TRes],
2334 add_message(bmachine_construction,'Replaced abstract INVARIANT by: ',NewTExpr,Cause)
2335 ; Res=TRes
2336 ),
2337 filter_abs_invs(TInv,SortedConcrVars,TRes).
2338 filter_abs_invs([TExpr|TInv],SortedConcrVars,[TExpr|TRes]) :-
2339 filter_abs_invs(TInv,SortedConcrVars,TRes).
2340
2341 contains_abstract_variables2(SortedConcrVars,TExpr,Cause) :-
2342 syntaxtraversion(TExpr,Expr,Type,Infos,Subs,_),
2343 ( memberchk(loc(_,_Mch,abstract_variables),Infos) % are there other things that pose problems: abstract_constants ?
2344 -> %print('Abs: '),translate:print_bexpr(TExpr),nl, print(SortedConcrVars),nl,
2345 Cause=TExpr,
2346 \+ ord_member_nonvar_chk(b(Expr,Type,_),SortedConcrVars) % otherwise variable is re-introduced with same type
2347 % in some Event-B models: VARIABLES keyword is used and in refinement VARIABLES are re-listed
2348 % TO DO: check what happens when variable not immediately re-introduced
2349 ; member(Sub,Subs),
2350 contains_abstract_variables2(SortedConcrVars,Sub,Cause)
2351 ).
2352
2353 % try and keep part of invariant, e.g., if f:ABS-->Ran translate to f: TYPE +-> Ran
2354 adapt_invariant(SortedConcrVars,b(member(LHS,RHS),pred,I),b(member(LHS,NewRHS),pred,I)) :-
2355 \+ contains_abstract_variables2(SortedConcrVars,LHS,_),
2356 adapt_to_concrete_superset(RHS,SortedConcrVars,NewRHS),
2357 \+ get_texpr_expr(NewRHS,typeset). % not really useful
2358
2359 adapt_to_concrete_superset(b(E,Type,Info),SortedConcrVars,b(NewE,Type,Info)) :-
2360 adapt_super2(E,SortedConcrVars,NewE). % TODO: adapt info fields?
2361 adapt_to_concrete_superset(b(_E,Type,_Info),_SortedConcrVars,b(typeset,Type,[])).
2362 % Range of function remains concrete, Domain is abstract and no longer available:
2363 adapt_super2(PFUN,SortedConcrVars,partial_function(NewDom,RAN)) :-
2364 is_fun(PFUN,partial,DOM,RAN),
2365 \+ contains_abstract_variables2(SortedConcrVars,RAN,_),
2366 adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom).
2367 adapt_super2(TFUN,SortedConcrVars,partial_function(NewDom,RAN)) :-
2368 is_fun(TFUN,total,DOM,RAN),
2369 \+ contains_abstract_variables2(SortedConcrVars,RAN,_),
2370 adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom).
2371 % Domain of function remains concrete, Range is abstract and no longer available:
2372 adapt_super2(PFUN,SortedConcrVars,partial_function(DOM,NewRan)) :-
2373 is_fun(PFUN,partial,DOM,RAN),
2374 \+ contains_abstract_variables2(SortedConcrVars,DOM,_),
2375 adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan).
2376 adapt_super2(TFUN,SortedConcrVars,total_function(DOM,NewRan)) :-
2377 is_fun(TFUN,total,DOM,RAN),
2378 \+ contains_abstract_variables2(SortedConcrVars,DOM,_),
2379 adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan).
2380 adapt_super2(FUN,SortedConcrVars,partial_function(NewDom,NewRan)) :-
2381 is_fun(FUN,_,DOM,RAN),
2382 adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom),
2383 adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan).
2384 % TODO: more cases, intersection(ABS,CONCR) -> CONCR, cartesian_product
2385
2386 is_fun(partial_function(DOM,RAN),partial,DOM,RAN).
2387 is_fun(partial_injection(DOM,RAN),partial,DOM,RAN).
2388 is_fun(partial_surjection(DOM,RAN),partial,DOM,RAN).
2389 is_fun(partial_bijection(DOM,RAN),partial,DOM,RAN).
2390 is_fun(total_function(DOM,RAN),total,DOM,RAN).
2391 is_fun(total_injection(DOM,RAN),total,DOM,RAN).
2392 is_fun(total_surjection(DOM,RAN),total,DOM,RAN).
2393 is_fun(total_bijection(DOM,RAN),total,DOM,RAN).
2394
2395 % ---------------------
2396
2397 get_machine_sorted_variables(Machine,SortedAllVars) :-
2398 get_section(abstract_variables,Machine,AbsVars),
2399 get_section(concrete_variables,Machine,ConcVars),
2400 append(ConcVars,AbsVars,AllVars),
2401 sort(AllVars,SortedAllVars).
2402
2403 split_conjuncts(Expr,List) :-
2404 split_conjuncts2(Expr,List,[]).
2405 split_conjuncts2(Expr) -->
2406 {get_texpr_expr(Expr,conjunct(A,B)),!},
2407 split_conjuncts2(A),
2408 split_conjuncts2(B).
2409 split_conjuncts2(Expr) --> [Expr].
2410
2411 % copy the abstract operations or re-use their preconditions
2412 % TODO: think about copying Initialisation?
2413 propagate_abstract_operations(Abstract,Concrete,RefMachines,Result,Ein,Eout) :-
2414 get_section(promoted,Abstract,APromoted),
2415 get_section(operation_bodies,Abstract,ABodies),
2416 % signature: select_section(SecName,OldContent,NewContent,OldMachine,NewMachine)
2417 select_section(promoted,CPromotedIn,CPromotedOut,Concrete,Concrete2),
2418 select_section(operation_bodies,CBodiesIn,CBodiesOut,Concrete2,Result),
2419 propagate_aops(APromoted,ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout).
2420 propagate_aops([],_ABodies,_RefMachines,CPromoted,CBodies,CPromoted,CBodies,Errors,Errors).
2421 propagate_aops([APromoted|ApRest],ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout) :-
2422 get_operation(APromoted,ABodies,AbstractOp),
2423 def_get_texpr_id(APromoted,op(APromotedOpName)),
2424 copy_texpr_wo_info(APromoted,CProm),
2425 ( member(CProm,CPromotedIn) ->
2426 debug_format(19,'Refining promoted abstract operation ~w to refinement machine.~n',[APromotedOpName]),
2427 extract_concrete_preconditions(AbstractOp,RefMachines,Pre),
2428 change_operation(APromoted,ConcreteOpOld,ConcreteOpNew,CBodiesIn,CBodiesRest),
2429 add_precondition(Pre,ConcreteOpOld,ConcreteOpNew), % propagate PRE down to concrete operation
2430 CPromotedIn = CPromotedRest,
2431 Ein = E1
2432 % TO DO: do not copy if event is refined at least once with renaming !
2433 ; is_refined_by_some_event(APromotedOpName,CPromotedIn,ConcreteOpName) ->
2434 debug_format(19,'Not copying abstract operation ~w to refinement machine, as it is refined by ~w.~n',[APromotedOpName,ConcreteOpName]),
2435 CPromotedRest=CPromotedIn, CBodiesRest=CBodiesIn,
2436 E1=Ein
2437 ;
2438 debug_format(19,'Copying abstract operation ~w to refinement machine, as it is not refined.~n',[APromotedOpName]),
2439 % TO DO: check that this is also the right thing to do for Atelier-B Event-B
2440 % TO DO: check that the variables are also still there
2441 check_copied_operation(APromoted,AbstractOp,RefMachines,Ein,E1),
2442 append(CPromotedIn,[APromoted],CPromotedRest),
2443 append(CBodiesIn,[AbstractOp],CBodiesRest)
2444 ),
2445 propagate_aops(ApRest,ABodies,RefMachines,CPromotedRest,CBodiesRest,CPromotedOut,CBodiesOut,E1,Eout).
2446
2447 is_refined_by_some_event(AbstractName,CPromotedList,ConcreteName) :-
2448 member(TID,CPromotedList),
2449 get_texpr_info(TID,Infos),
2450 memberchk(refines_operation(AbstractName),Infos),
2451 def_get_texpr_id(TID,ConcreteName).
2452
2453 get_operation(TId,Bodies,Operation) :-
2454 select_operation(TId,Bodies,Operation,_BodiesRest).
2455 change_operation(TId,OldOp,NewOp,OldBodies,[NewOp|NewBodies]) :-
2456 select_operation(TId,OldBodies,OldOp,NewBodies).
2457 select_operation(TId,Bodies,Operation,BodiesRest) :-
2458 copy_texpr_wo_info(TId,OpId),
2459 get_texpr_expr(Operation,operation(OpId,_,_,_)),
2460 select(Operation,Bodies,BodiesRest),!.
2461
2462 extract_concrete_preconditions(Op,RefMachines,FPre) :-
2463 extract_preconditions_op(Op,Pre),
2464 extract_op_arguments(Op,Args),
2465 conjunction_to_list(Pre,Pres),
2466 % todo: check the "machine" parameter
2467 % TODO: check if we are currently in an implementation machine and use implementation instead of refinement?
2468 % see also test leftpad_i in test 2242 with ss:seq(Chars) as pre-condition
2469 visible_env(refinement,operation_preconditions,RefMachines,Env1,_Errors,[]),
2470 store_variables(Args,Env1,Env),
2471 filter_predicates_with_unknown_identifiers(Pres,Env,FPres),
2472 conjunct_predicates(FPres,FPre).
2473
2474 extract_op_arguments(Op,Params) :-
2475 get_texpr_expr(Op,operation(_,_,Params,_)).
2476
2477 extract_preconditions_op(OpExpr,Pre) :-
2478 get_texpr_expr(OpExpr,operation(_,_,_,Subst)),
2479 extract_preconditions(Subst,Pres,_),
2480 conjunct_predicates(Pres,Pre).
2481 extract_preconditions(TExpr,Pres,Inner) :-
2482 get_texpr_expr(TExpr,Expr),
2483 extract_preconditions2(Expr,TExpr,Pres,Inner).
2484 extract_preconditions2(precondition(Pre,Subst),_,[Pre|Pres],Inner) :- !,
2485 extract_preconditions(Subst,Pres,Inner).
2486 extract_preconditions2(block(Subst),_,Pres,Inner) :- !,
2487 extract_preconditions(Subst,Pres,Inner).
2488 extract_preconditions2(_,Inner,[],Inner).
2489
2490 :- use_module(btypechecker,[prime_atom0/2]).
2491 filter_predicates_with_unknown_identifiers([],_Env,[]).
2492 filter_predicates_with_unknown_identifiers([Pred|Prest],Env,Result) :-
2493 ( find_unknown_identifier(Pred,Env,_Id) ->
2494 !,Result = Rrest
2495 ;
2496 Result = [Pred|Rrest]),
2497 filter_predicates_with_unknown_identifiers(Prest,Env,Rrest).
2498 find_unknown_identifier(Pred,Env,Id) :-
2499 get_texpr_id(Pred,Id),!,
2500 \+ env_lookup_type(Id,Env,_),
2501 (atom(Id),prime_atom0(UnprimedId,Id)
2502 -> % we have an identifier with $0 at end
2503 \+ env_lookup_type(UnprimedId,Env,_) % check unprimed identifier also unknown
2504 ; true).
2505 find_unknown_identifier(Pred,Env,Id) :-
2506 syntaxtraversion(Pred,_,_,_,Subs,Names),
2507 store_variables(Names,Env,Subenv),
2508 find_unknown_identifier_l(Subs,Subenv,Id).
2509 find_unknown_identifier_l([S|_],Env,Id) :-
2510 find_unknown_identifier(S,Env,Id),!.
2511 find_unknown_identifier_l([_|Rest],Env,Id) :-
2512 find_unknown_identifier_l(Rest,Env,Id).
2513
2514 :- use_module(library(ordsets),[ord_union/3]).
2515 % we add a precondition to an existing operation
2516 % Note: we need to update the reads info computed by the type checker (compute_accessed_vars_infos_for_operation)
2517 add_precondition(b(truth,_,_),Old,New) :- !, Old=New.
2518 add_precondition(Pre,b(Old,T,I),b(New,T,I2)) :-
2519 Old=operation(Id,Res,Params,Subst),
2520 New=operation(Id,Res,Params,NewSubst),
2521 extract_preconditions(Subst,OldPres,Inner),
2522 conjunct_predicates([Pre|OldPres],NewPre),
2523 create_texpr(precondition(NewPre,Inner),subst,[],NewSubst),
2524 (select(reads(OldReads),I,I1) % we do not have to update modifies(.), non_det_modifies(.), reads_locals(.),...
2525 -> I2=[reads(NewReads)|I1],
2526 get_texpr_ids(Params,Ignore),
2527 find_identifier_uses(Pre,Ignore,PreUsedIds),
2528 ord_union(PreUsedIds,OldReads,NewReads)
2529 ; add_internal_error('No reads info for operation: ',add_precondition(Pre,b(Old,T,I),b(New,T,I2))),
2530 I2=I).
2531
2532
2533
2534 check_copied_operation(OpRef,Op,RefMachines,Ein,Eout) :-
2535 % todo: check the "refinement" parameter
2536 visible_env(refinement,operation_bodies,RefMachines,Env1,_Errors,[]),
2537 get_texpr_id(OpRef,OpId),get_texpr_type(OpRef,OpType),
2538 env_store(OpId,OpType,[],Env1,Env),
2539 findall(U, find_unknown_identifier(Op,Env,U), Unknown1),
2540 ( Unknown1=[] -> Ein=Eout
2541 ;
2542 sort(Unknown1,Unknown),
2543 op(OpName) = OpId,
2544 join_ids(Unknown,IdList),
2545 (Unknown = [_] -> Plural=[]; Plural=['s']),
2546 append([['Operation ',OpName,
2547 ' was copied from abstract machine but the identifier'],
2548 Plural,
2549 [' '],
2550 IdList,
2551 [' cannot be seen anymore']],Msgs),
2552 ajoin(Msgs,Msg), Ein = [error(Msg,none)|Eout]
2553 ).
2554 join_ids([I],[Msg]) :- !,opname(I,Msg).
2555 join_ids([A|Rest],[Msg,','|Mrest]) :- opname(A,Msg),join_ids(Rest,Mrest).
2556 opname(op(Id),Id) :- !.
2557 opname(Id,Id).
2558
2559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2560 % split an identifier into a (possible empty) prefix and the name itself
2561 % e.g. split_prefix('path.to.machine', 'path.to', 'machine').
2562 split_prefix(Term,Prefix,Main) :-
2563 one_arg_term(Functor,Arg,Term),!,
2564 one_arg_term(Functor,MArg,Main),
2565 split_prefix(Arg,Prefix,MArg).
2566 split_prefix(PR,Prefix,Main) :-
2567 safe_atom_chars(PR,Chars,split_prefix1),
2568 split_prefix2(Chars,Chars,[],CPrefix,CMain),
2569 safe_atom_chars(Main,CMain,split_prefix2),
2570 safe_atom_chars(Prefix,CPrefix,split_prefix3).
2571 split_prefix2([],Main,_,[],Main).
2572 split_prefix2([C|Rest],Previous,PrefBefore,Prefix,Main) :-
2573 ( C='.' ->
2574 append(PrefBefore,RestPrefix,Prefix),
2575 split_prefix2(Rest,Rest,[C],RestPrefix,Main)
2576 ;
2577 append(PrefBefore,[C],NextPref),
2578 split_prefix2(Rest,Previous,NextPref,Prefix,Main)).
2579
2580 rawmachine_section(Elem,List,Result) :-
2581 rawmachine_section_with_opt_desc(Elem,List,Result,_,_).
2582
2583
2584 rawmachine_section_with_opt_desc(Elem,List,Result,Desc,Pos) :- section_can_have_description_pragma(Elem),
2585 functor(Pattern,Elem,2),
2586 arg(2,Pattern,Result),
2587 select(description_machine_clause(Pos,DescText,Pattern),List,_),!,
2588 (DescText = description_text(_Pos,Desc) -> true ; Desc='?illegal_desc?').
2589 rawmachine_section_with_opt_desc(Elem,List,Result,'',unknown) :-
2590 functor(Pattern,Elem,2),
2591 arg(2,Pattern,Result),
2592 ? select(Pattern,List,Rest),!,
2593 (functor(Pattern2,Elem,2),member(Pattern2,Rest)
2594 -> arg(1,Pattern2,Pos),
2595 add_error(bmachine_construction,'Multiple sections for: ',Elem,Pos)
2596 ; true).
2597
2598
2599 section_can_have_description_pragma(initialisation).
2600
2601 optional_rawmachine_section(Elem,RawMachineSecList,Default,Result) :-
2602 ( rawmachine_section(Elem,RawMachineSecList,Result1) -> Result=Result1
2603 ; Result=Default).
2604
2605
2606 clever_append([],Y,R) :- !, R=Y.
2607 clever_append(X,Y,R) :- X \= [_|_],!, % can happen for old parser for EXPRESSIONS/PREDICATES
2608 add_error(bmachine_construction,'Illegal section format (update parser?): ',X), R=Y.
2609 clever_append(X,[],R) :- !, R=X.
2610 clever_append(X,Y,R) :- append(X,Y,R).
2611
2612 one_arg_term(Functor,Arg,Term) :- %print(one_arg_term(Functor,Arg,Term)),nl,
2613 functor(Term,Functor,1),arg(1,Term,Arg).
2614
2615 % check if a rawmachine section list has a given section
2616 rawmachine_section_exists(Elem,List) :- %
2617 functor(Pattern,Elem,2),
2618 ? (member(Pattern,List) -> true).
2619
2620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2621 % visibility rules
2622
2623 % the visibility/5 predicate declares what a part of a machine can see
2624 % visibility(MType, Scope, Section, Access, Info) means:
2625 % In a machine of type MType (machine, refinement, implementation),
2626 % an expression in a Section (like invariant) can see
2627 % the identifiers in the Access sections. Access is a list of
2628 % sections, where shortcuts are allowed (see shortcut/2, e.g., shortcut(operations,[unpromoted,promoted]).).
2629 % Scope defines where (in relation to the Section)
2630 % the section in Access are (local: same machine, included: in an
2631 % included machine, etc.)
2632 % Info is a list of additional information that is added to each
2633 % identifier in the environment to build up. E.g. in an operation
2634 % definition, constants are marked readonly.
2635 visibility(machine, local, constraints, [parameters],[]).
2636 visibility(machine, local, includes, [parameters,sets,constants],[]).
2637 visibility(machine, local, properties, [sets,constants],[]).
2638 visibility(machine, local, invariant, [parameters,sets,constants,variables],[]).
2639 visibility(machine, local, operation_bodies, [parameters,sets,constants],[readonly]).
2640 visibility(machine, local, operation_bodies, [operations],Info) :- Info =[readonly,dontcall].
2641 % an operation to be readonly: means we cannot assign to it; but the operation itself can change variables
2642 % for allow_operation_calls_in_expr=true we check that operation is inquiry in type checker
2643 % (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry]
2644 % ; Info =[readonly,dontcall] ). % we check in btype(operation_call_in_expr)
2645 visibility(machine, local, operation_bodies, [variables],[]).
2646 visibility(machine, local, initialisation, Access,[not_initialised|Info]) :-
2647 ? visibility(machine,local,operation_bodies,Access,Info).
2648
2649 visibility(machine, Scope, assertions, [Allow],[inquiry|DC]) :-
2650 (Scope = local -> Allow=operations, DC=[dontcall] ; Allow=promoted, DC=[]),
2651 get_preference(allow_operation_calls_in_expr,true). % if we allow calling operations in expressions
2652 visibility(machine, Scope, invariant, [promoted],[inquiry]) :-
2653 Scope \= local, % do not allow calling local operations in invariant; their correctness relies on the invariant
2654 get_preference(allow_operation_calls_in_expr,true).
2655
2656 visibility(machine, included, properties, [sets,constants],[]).
2657 visibility(machine, included, invariant, [sets,constants,variables],[]).
2658 visibility(machine, included, operation_bodies, [sets,constants,variables],[readonly]).
2659 visibility(machine, included, operation_bodies, [promoted],Info) :- Info = [readonly].
2660 % for allow_operation_calls_in_expr=true we check that operation is inquiry in type checker
2661 % (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry] ; Info=[readonly]).
2662 %visibility(machine, included, initialisation, [sets,constants,variables,promoted],[readonly]).
2663 visibility(machine, included, initialisation, [sets,constants,promoted],[readonly]).
2664 visibility(machine, included, initialisation, [variables],Info) :-
2665 (get_preference(allow_overriding_initialisation,true)
2666 -> Info = [] % allow overriding INITIALISATION / making it more concrete
2667 ; Info = [readonly]). % default Atelier-B semantics
2668
2669 visibility(machine, used, properties, [sets,constants],[]).
2670 visibility(machine, used, invariant, [parameters,sets,constants,variables],[]).
2671 visibility(machine, used, operation_bodies, [parameters,sets,constants,variables],[readonly]).
2672 visibility(machine, used, initialisation, [parameters,sets,constants,variables],[readonly]).
2673 visibility(machine, used, operation_bodies, [operations],[inquiry]) :-
2674 get_preference(allow_operation_calls_for_uses,true). %% added by leuschel, allowed in Schneider Book
2675 % but not allowed by Atelier-B; see test 2135
2676
2677 visibility(machine, seen, includes, [sets,constants],[]).
2678 visibility(machine, seen, properties, [sets,constants],[]).
2679 visibility(machine, seen, invariant, [sets,constants],[]).
2680 visibility(machine, seen, operation_bodies, [sets,constants,variables],[readonly]).
2681 visibility(machine, seen, initialisation, [sets,constants,variables],[readonly]).
2682 visibility(machine, seen, operation_bodies, [operations],[inquiry]). %% added by leuschel, allow query operation
2683
2684 visibility(refinement, local, Section, Access, Info) :-
2685 Section \= assertions, % assertions are handled below
2686 visibility(machine, local, Section, Access, Info).
2687
2688 visibility(refinement, abstraction, includes, [sets,concrete_constants],[]).
2689 visibility(refinement, abstraction, properties, [sets,constants],[]).
2690 visibility(refinement, abstraction, invariant, [sets,constants,concrete_variables],[]).
2691 visibility(refinement, abstraction, invariant, [abstract_variables],[abstraction]).
2692 visibility(refinement, abstraction, operation_bodies, [sets,concrete_constants],[readonly]).
2693 visibility(refinement, abstraction, operation_bodies, [concrete_variables],[]).
2694
2695 % a special operation_precondition section, for propagating pre-conditions down to concrete machines
2696 % disallowing access to concrete_variables of abstract machine (see tests 614, 1018, 1644, 1679, 2385)
2697 visibility(refinement, abstraction, operation_preconditions, [sets,concrete_constants],[readonly]).
2698 visibility(refinement, Kind, operation_preconditions, Part,Access) :- Kind \= abstraction,
2699 visibility(refinement, Kind, operation_bodies, Part,Access).
2700
2701 visibility(refinement, included, properties, [sets,constants],[]).
2702 visibility(refinement, included, invariant, [sets,constants,variables],[]).
2703 visibility(refinement, included, operation_bodies, [sets,constants,variables,promoted],[re]). % What is re ??? TO DO: investigate
2704
2705 visibility(refinement, seen, includes, [sets,constants],[]).
2706 visibility(refinement, seen, properties, [sets,constants],[]).
2707 visibility(refinement, seen, invariant, [sets,constants],[]).
2708 visibility(refinement, seen, operation_bodies, [sets,constants,variables],[readonly]).
2709 visibility(refinement, seen, operation_bodies, [operations],[inquiry]).
2710
2711 visibility(refinement, Ref, initialisation, Access, [not_initialised|Info]) :-
2712 visibility(refinement,Ref,operation_bodies,Access,Info).
2713
2714 % assertions have same visibility as invariant
2715 visibility(MType, Ref, assertions, Part, Access) :-
2716 ? visibility(MType,Ref,invariant,Part,Access).
2717
2718 visibility(implementation, Ref, Section, Part, Access) :-
2719 visibility(refinement, Ref, Section, Part, Access).
2720 visibility(implementation, local, values_expression, [concrete_constants,sets],[]). % seems to have no effect (yet); see ArrayValuationAImp
2721 visibility(implementation, included, values_expression, [concrete_constants,sets],[]).
2722 visibility(implementation, seen, values_expression, [concrete_constants,sets],[]).
2723 visibility(implementation, abstraction,values_expression, [concrete_constants,sets],[]).
2724
2725 % For predicates over pre- and post-states
2726 visibility(MType, Rev, prepost, Access, Info) :-
2727 ? visibility(MType, Rev, invariant, Access, Info).
2728 visibility(MType, Rev, prepost, Access, [primed,poststate|Info]) :-
2729 ? visibility(MType, Rev, invariant, Access, Info).
2730
2731 % add error messages for some common mistakes (access to parameters in the properties)
2732 visibility(machine, local, properties, [parameters], [error('a parameter cannot be accessed in the PROPERTIES section')]).
2733 % the following rule should be helpful for the user, but additionally it also
2734 % provides a mechanism to re-introduce abstract variables in the INVARIANT
2735 % section of a while loop (to enable this, an identifier is also marked with "abstraction")
2736 % in the predicate allow_access_to_abstract_var
2737 visibility(refinement, abstraction, operation_bodies, [abstract_variables],
2738 [error('illegal access to an abstract variable in an operation'),abstraction]).
2739 visibility(refinement, abstraction, operation_bodies, [abstract_constants],
2740 [error('illegal access to an abstract constant in an operation (only allowed in WHILE INVARIANT or ASSERT)'),abstraction]).
2741
2742
2743 % lookups up all identifier sections that are accessible from
2744 % the given section, removes all shortcuts and removes
2745 % duplicate entries
2746 %
2747 % Returns a list of vis(Section,Info) where Section is the
2748 % identifier section that can be seen with Info as a list
2749 % of additional information
2750 expanded_visibility(MType, Ref, Part, Access) :-
2751 findall(vis(Section,Infos),
2752 ( visibility(MType,Ref,Part,Sections1,Infos),
2753 expand_shortcuts(Sections1,Sections),
2754 member(Section,Sections)),
2755 Access1),
2756 sort(Access1,Access).
2757 %format('MType=~w, Ref=~w, Part=~w~n Vis=~w~n',[MType,Ref,Part,Access]).
2758
2759 % visible_env/6 creates a type environment for a certain
2760 % part of a machine by looking up which identifier should
2761 % be visible from there and what additional information should
2762 % be added (e.g. to restrict access to read-only)
2763 %
2764 % The visibility/6 facts are used to define which identifier are visible
2765 %
2766 % MType: machine type (machine, refinement, ...)
2767 % Part: part of the machine for which the environment should be created
2768 % RefMachines: referred machines that are already typed
2769 % Env: The created environment
2770 % Errors: errors might be added if multiple variables with the same identifier
2771 % are declared
2772 visible_env(MType, Part, RefMachines, Env, Err_in, Err_out) :-
2773 env_empty(Init),
2774 visible_env(MType, Part, RefMachines, Init, Env, Err_in, Err_out).
2775 % visible_env/7 is like visible_env/6, but an initial environment
2776 % can be given in "In"
2777 visible_env(MType, Part, RefMachines, In, Out, Err_in, Err_out) :-
2778 foldl(visible_env2(MType,Part),RefMachines,In/Err_in,Out/Err_out).
2779 visible_env2(MType,Part,extended_local_ref(Machine),InEnvErr,OutEnvErr) :- !,
2780 Scope=local,
2781 get_machine_name(Machine,MName),
2782 %format('adding identifiers from machine ~w for scope ~w~n',[MName,Scope]),
2783 get_section(definitions,Machine,Defs),
2784 foldl(env_add_def(MName),Defs,InEnvErr,InEnvErr2),
2785 (bmachine:additional_configuration_machine(_MchName,AddMachine),
2786 get_section(definitions,AddMachine,AdditionalDefs)
2787 -> foldl(env_add_def(MName),AdditionalDefs,InEnvErr2,InterEnvErr)
2788 ; InterEnvErr = InEnvErr2
2789 ),
2790 expanded_visibility(MType, Scope, Part, Access),
2791 foldl(create_vis_env(Scope,MName,Machine),Access,InterEnvErr,OutEnvErr).
2792 visible_env2(MType,Part,ref(Scope,Machine),InEnvErr,OutEnvErr) :-
2793 get_machine_name(Machine,MName),
2794 %format('adding identifiers from machine ~w for scope ~w~n',[MName,Scope]),
2795 ( Scope == local ->
2796 get_section(definitions,Machine,Defs),
2797 %nl,print(adding_defs(Defs)),nl,nl,
2798 foldl(env_add_def(MName),Defs,InEnvErr,InterEnvErr)
2799 ;
2800 InEnvErr=InterEnvErr),
2801 expanded_visibility(MType, Scope, Part, Access),
2802 foldl(create_vis_env(Scope,MName,Machine),Access,InterEnvErr,OutEnvErr).
2803 env_add_def(MName,definition_decl(Name,PType,DefInfos,Pos,Params,Body,_Deps),InEnvErr,OutEnvErr) :-
2804 Type = definition(PType,Params,Body),
2805 Info = [nodeid(Pos),loc(local,MName,definitions)|DefInfos],
2806 create_texpr(identifier(Name),Type,Info,TExpr),
2807 add_unique_variable(TExpr,InEnvErr,OutEnvErr),!.
2808 env_add_def(MName,Def,EnvErr,EnvErr) :-
2809 add_internal_error('Cannot deal with DEFINITION: ',env_add_def(MName,Def)).
2810
2811 create_vis_env(Scope,MName,IDS,vis(Section,Infos),InEnvErr,OutEnvErr) :-
2812 get_section(Section,IDS,Identifiers1),
2813 % get_texpr_ids(Identifiers1,II),format('Got identifiers for ~w:~w = ~w~n',[MName,Section,II]),
2814 (Section=freetypes
2815 -> generate_free_type_ids(Identifiers1,Identifiers2)
2816 ; Identifiers2=Identifiers1),
2817 maplist(add_infos_to_identifier([loc(Scope,MName,Section)|Infos]),Identifiers2,Identifiers),
2818 l_add_unique_variables(Identifiers,InEnvErr,OutEnvErr).
2819
2820 % optimized version of foldl(add_unique_variable,Identifiers,InEnvErr,OutEnvErr).
2821 l_add_unique_variables([],Env,Env).
2822 l_add_unique_variables([ID|T],InEnvErr,OutEnvErr) :-
2823 add_unique_variable(ID,InEnvErr,IntEnv),
2824 l_add_unique_variables(T,IntEnv,OutEnvErr).
2825
2826 % freetypes: generate IDs for Z freetype section
2827 % The classical B FREETYPE section is dealt with in another place (TODO: unify this)
2828 generate_free_type_ids([freetype(FTypeId,Cases)|T],[b(identifier(FTypeId),set(freetype(FTypeId)),[])|FT]) :- !,
2829 findall(b(identifier(CaseID),CaseType,[]),
2830 (member(case(CaseID,ArgType),Cases), gen_freecase_type(ArgType,FTypeId,CaseType)),
2831 FT, FT2),
2832 %write(generated_freetype_cases(FT)),nl,
2833 generate_free_type_ids(T,FT2).
2834 generate_free_type_ids(T,T).
2835
2836 gen_freecase_type(ArgType,FTypeId,Type) :- nonvar(ArgType), ArgType=constant(_),!, Type = freetype(FTypeId).
2837 gen_freecase_type(ArgType,FTypeId,set(couple(ArgType,freetype(FTypeId)))).
2838
2839 add_unique_variable(Var1,Old/Err_in,New/Err_out) :-
2840 optionally_rewrite_id(Var1,Var),
2841 get_texpr_id(Var,Id),
2842 get_texpr_type(Var,Type),
2843 get_texpr_info(Var,InfosOfNew),!,
2844 ( env_lookup_type(Id,Old,_) ->
2845 % we have a collision of two identifiers
2846 handle_collision(Var,Id,Type,Old,InfosOfNew,New,Err_in,Err_out)
2847 ;
2848 % no collision, just introduce the new identifier
2849 env_store(Id,Type,InfosOfNew,Old,New),
2850 %btypechecker:portray_env(New),nl,
2851 Err_in=Err_out
2852 ).
2853 add_unique_variable(Var,Env/Err,Env/Err) :- print(Var),nl,
2854 ( Var = b(definition(DEFNAME,_),_,INFO) ->
2855 add_error(add_unique_variable,'DEFINITION used in place of Identifier: ',DEFNAME,INFO)
2856 ; Var = b(description(Txt,_),_,INFO) ->
2857 add_error(add_unique_variable,'Unsupported @desc pragma: ',Txt,INFO)
2858 ;
2859 add_error(add_unique_variable,'Expected Identifier, but got: ',Var,Var)
2860 ).
2861
2862 add_infos_to_identifier(NewInfos,In,Out) :-
2863 add_texpr_infos(In,NewInfos,Out).
2864
2865 % get the ID of the variable, prime it if the infos contain "primed"
2866 optionally_rewrite_id(Var,NewVar) :-
2867 get_texpr_info(Var,InfosIn),
2868 ( selectchk(primed,InfosIn,InfosOut) ->
2869 get_texpr_id(Var,Id1),
2870 get_texpr_type(Var,Type),
2871 atom_concat(Id1,'\'',Id),
2872 create_texpr(identifier(Id),Type,InfosOut,NewVar)
2873 ;
2874 Var = NewVar).
2875
2876 :- use_module(translate,[translate_span/2]).
2877 % in case of a collision, we have three options:
2878 % - overwrite the old identifier,
2879 % - ignore the new identifier or
2880 % - generate an error message
2881 handle_collision(Var,Name,Type,OldEnv,InfosOfNew,NewEnv,Ein,Eout) :-
2882 env_lookup_infos(Name,OldEnv,InfosOfExisting),
2883 %btypechecker:portray_env(OldEnv),nl,
2884 ( collision_precedence(Name,InfosOfExisting,InfosOfNew) ->
2885 % this identifier should be ignored
2886 OldEnv = NewEnv,
2887 Ein = Eout
2888 ; collision_precedence(Name,InfosOfNew,InfosOfExisting) ->
2889 % the existing should be overwritten
2890 env_store(Name,Type,InfosOfNew,OldEnv,NewEnv),
2891 Ein = Eout
2892 ;
2893 % generate error and let the environment unchanged
2894 (Name = op(IName) -> Kind='Operation identifier'; Name=IName, Kind='Identifier'),
2895 get_texpr_pos(Var,Pos1),
2896 safe_get_info_pos(InfosOfExisting,Pos2),
2897 ( double_inclusion_allowed(Name,Pos1,Pos2,InfosOfExisting)
2898 -> %print(double_inclusion_of_id_allowed(Name,Type,Pos1,OldEnv,InfosOfExisting)),nl,
2899 OldEnv=NewEnv,
2900 Ein=Eout
2901 ; (better_pos(Pos2,Pos1), \+ better_pos(Pos1,Pos2)
2902 -> Pos = Pos2, translate_span(Pos1,PS1)
2903 ; Pos = Pos1,
2904 PS1='' % Pos1 is already part of error span
2905 ),
2906 translate_inclusion_path(InfosOfExisting,Path2),
2907 get_texpr_info(Var,Info1), translate_inclusion_path(Info1,Path1),
2908 (Pos1=Pos2, Path1=Path2
2909 -> ajoin([Kind,' \'', IName, '\'', PS1, Path1, ' included twice '],Msg)
2910 % This should probably not happen; internal error in machine construction?
2911 ; translate_span(Pos2,PS2),
2912 %ajoin(['Identifier \'', IName, '\' declared twice at (Line:Col[:File]) ', PS1, Path1, ' and ', PS2, Path2],Msg),
2913 ajoin([Kind,' \'', IName, '\'', PS1, Path1, ' already declared at ', PS2, Path2],Msg)
2914 ),
2915 %format(user_error,'*** ~w~n',[Msg]),trace,
2916 Ein = [error(Msg,Pos)|Eout],
2917 OldEnv = NewEnv
2918 )
2919 ).
2920
2921
2922 % example identifier: %b(identifier(aa),integer,[loc(seen,M2,concrete_constants),readonly,usesee(M2,aa,seen),origin([included/M1]),nodeid(pos(18,2,2,11,2,13))])
2923
2924 % try and infer inclusion path of identifier from Infos
2925 translate_inclusion_path(Infos,Str) :- member(loc(How,Machine,Section),Infos),!,
2926 (member(origin(Path),Infos) -> get_origin_path(Path,T) ; T=[')']),
2927 ajoin([' (', How,' from ', Section, ' section of ', Machine |T],Str).
2928 translate_inclusion_path(_,'').
2929
2930 get_origin_path([How/Machine|T],[', ', How,' from ',Machine|TRes]) :- !,
2931 get_origin_path(T,TRes).
2932 get_origin_path(_,[')']).
2933
2934
2935 % SEE ISSUE PROB-403, test 1857
2936 % Correct behaviour related to multiple instantiation is specified in
2937 % B Reference Manual (AtelierB 4.2.1), 8.3 B Project/Instantiating and renaming.
2938 % => Constants and Sets defined in machines instantiated multiple times CAN be used in the including machine with their original (non-prefixed names).
2939 % Note: this code did lead to the constants being added multiple times; this has been fixed in concat_section_contents
2940 double_inclusion_allowed(Name,Pos1,Pos2,InfosOfExisting) :-
2941 %print(check_double_inclusion(Name,Pos1,Pos2,InfosOfExisting)),nl,
2942 Pos1==Pos2,
2943 %print(chk(InfosOfExisting)),nl,
2944 member(loc(LOC,_,Section),InfosOfExisting),
2945 %print(try(LOC,Section)),nl,
2946 section_can_be_included_multiple_times_nonprefixed(Section),
2947 % check that we are in a context of an included machine identifier:
2948 (inclusion_directive(LOC,Name,Pos2)
2949 -> true
2950 ; %LOC is probably local
2951 member(origin([INCL/_MACHINE|_]),InfosOfExisting),
2952 inclusion_directive(INCL,Name,Pos2)
2953 ).
2954
2955 inclusion_directive(included,_,_).
2956 inclusion_directive(used,_,_).
2957 inclusion_directive(seen,_,_). % imports ??
2958 inclusion_directive(abstraction,Name,Pos) :- % probably not allowed by Atelier-B
2959 (debug_mode(off) -> true
2960 ; add_message(bmachine_construction,'Allowing double inclusion from abstraction of: ',Name,Pos)).
2961
2962 section_can_be_included_multiple_times_nonprefixed(abstract_constants).
2963 section_can_be_included_multiple_times_nonprefixed(concrete_constants).
2964 section_can_be_included_multiple_times_nonprefixed(sets).
2965 section_can_be_included_multiple_times_nonprefixed(enumerated_sets).
2966 section_can_be_included_multiple_times_nonprefixed(enumerated_elements).
2967 section_can_be_included_multiple_times_nonprefixed(deferred_sets). % added 2.12.2022
2968 section_can_be_included_multiple_times_nonprefixed(constants). % shortcut
2969
2970
2971 % decide which position info is better: prefer info in main file (highlighting)
2972 :- use_module(bmachine,[b_get_main_filenumber/1]).
2973 better_pos(Pos,_) :- get_position_filenumber(Pos,Filenumber),
2974 b_get_main_filenumber(Filenumber).
2975 better_pos(_,none).
2976
2977 safe_get_info_pos(Info,Pos) :- (get_info_pos(Info,Pos) -> true ; Pos=none).
2978
2979 % collision_precedence/3 decides if the first variable takes
2980 % precedence over the second in case of a collision
2981 % the decision is made by the additional information of both
2982 % variables
2983 collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :-
2984 % in case of a re-introduced variable from the abstraction,
2985 % we prefer the concrete variable to the abstract one.
2986 is_abstraction(DroppedVarInfos,PreferredVarInfos),!.
2987 collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :-
2988 % We are checking an Event-B model with multi-level support
2989 % and have the same variable in two different refinement levels.
2990 % Then the one in the more refined module takes precedence
2991 member(level(L1),PreferredVarInfos),
2992 member(level(L2),DroppedVarInfos),
2993 % Level 0 is the abstract level, level 1 the first refinement, etc.
2994 L1 > L2,!.
2995 collision_precedence(Name,PreferredVarInfos,DroppedVarInfos) :-
2996 % A local definition takes precedence over a non-local identifier
2997 % TODO:
2998 member(loc(local,_DefMachine,definitions),PreferredVarInfos),
2999 member(loc(_,_VarMachine,Section),DroppedVarInfos),
3000 Section \= definitions,!,
3001 (preferences:get_preference(warn_if_definition_hides_variable,true)
3002 % default is true; we could also check clash_strict_checks
3003 -> get_id_kind(Section,HiddenIdKind),
3004 (get_info_pos(PreferredVarInfos,Pos1), Pos1 \= none,
3005 get_info_pos(DroppedVarInfos,Pos2), Pos2 \= none
3006 -> translate:translate_span(Pos1,Pos1Str), translate:translate_span(Pos2,Pos2Str),
3007 ajoin(['Warning: DEFINITION of ', Name, ' at ', Pos1Str,
3008 ' hides ',HiddenIdKind,' with same name at ', Pos2Str, '.'], Msg)
3009 ; ajoin(['Warning: DEFINITION of ', Name, ' hides ',HiddenIdKind,' with same name.'], Msg),
3010 Pos1 = unknown
3011 ),
3012 store_warning(Msg,Pos1) % TO DO: add position info
3013 ; true
3014 ).
3015 % TO DO: allow identical DEFINITIONS ?
3016
3017 % translate section name into a name for the identifier
3018 get_id_kind(abstract_constants,'constant').
3019 get_id_kind(concrete_constants,'constant').
3020 get_id_kind(parameters,'parameter').
3021 get_id_kind(deferred_sets,'deferred set').
3022 get_id_kind(enumerated_sets,'enumerated set').
3023 get_id_kind(enumerated_elements,'enumerated set element').
3024 get_id_kind(freetypes,'freetype').
3025 get_id_kind(promoted,'operation').
3026 % TODO: complete and use in other places
3027 get_id_kind(_SectionName,'variable').
3028
3029
3030 % is_abstraction/2 tries to find out if (in case of a name clash of
3031 % two variables) the second variable is just the re-introduced abstract
3032 % variable in a refinement.
3033 % InfosAbs is the list of information about the abstract variable
3034 % InfosConc is the list of information about the concrete variable
3035 is_abstraction(InfosAbs,InfosConc) :-
3036 % one variable is an abstract variable, introduced in the abstraction
3037 member(loc(abstraction,_,abstract_variables),InfosAbs),
3038 % the other is either an abstract or concrete variable,
3039 member(VarType,[abstract_variables,concrete_variables]),
3040 % introduced either locally or in an included machine
3041 member(Scope,[local,included]),
3042 member(loc(Scope,_,VarType),InfosConc).
3043 % and the same for constants:
3044 is_abstraction(InfosAbs,InfosConc) :-
3045 % one variable is an abstract variable, introduced in the abstraction
3046 member(loc(abstraction,_,abstract_constants),InfosAbs),
3047 % the other is either an abstract or concrete variable,
3048 member(VarType,[abstract_constants,concrete_constants]),
3049 % introduced either locally or in an included machine
3050 member(Scope,[local,included]),
3051 member(loc(Scope,_,VarType),InfosConc).
3052
3053 % shortcuts for sections, to ease the use of typical combinations of
3054 % sections
3055 shortcut(all_parameters,[parameters,internal_parameters]).
3056 shortcut(sets,[deferred_sets,enumerated_sets,enumerated_elements|T]) :-
3057 (animation_minor_mode(M), (M=z ; M=eventb)
3058 -> T = [freetypes]
3059 ; T = []). % The FREETYPES section in classical B is unfortunately dealt with differently and
3060 % we currently have errors if add the freetypes section here
3061 shortcut(constants,[abstract_constants,concrete_constants]).
3062 shortcut(variables,[abstract_variables,concrete_variables]).
3063 shortcut(operations,[unpromoted,promoted]).
3064 shortcut(identifiers,[all_parameters,sets,constants,variables,operations]).
3065
3066 expand_shortcuts(Sections,Expanded) :-
3067 foldl(expand_shortcut,Sections,Expanded,[]).
3068 expand_shortcut(Section,Sections,RSections) :-
3069 ( shortcut(Section,Expanded) ->
3070 foldl(expand_shortcut,Expanded,Sections,RSections)
3071 ; valid_section(Section) ->
3072 Sections = [Section|RSections]
3073 ;
3074 add_internal_error('invalid section',expand_shortcut(Section,Sections,RSections)),fail).
3075
3076 % find sections that can see given sections
3077 find_relevant_sections(RSecs,MTypes,Result) :-
3078 expand_shortcuts(RSecs,Sections),
3079 findall(R,
3080 ( member(MType,MTypes),
3081 visibility(MType,local,R,SAccess,_),
3082 expand_shortcuts(SAccess,Access),
3083 member(S,Sections),
3084 member(S,Access),
3085 valid_section(R)),
3086 Result1),
3087 sort(Result1,Result).
3088
3089
3090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3091 % renaming
3092
3093 rename_relevant_sections(DefSecs,Renamings,Machine,New) :-
3094 find_relevant_sections(DefSecs,[machine],Relevant),
3095 rename_in_sections(Relevant,Renamings,Machine,New).
3096 rename_in_sections([],_,M,M).
3097 rename_in_sections([Section|Rest],Renamings,Old,New) :-
3098 select_section_texprs(Section,OTExprs,NTExprs,Old,Inter),
3099 rename_bt_l(OTExprs,Renamings,NTExprs),
3100 rename_in_sections(Rest,Renamings,Inter,New).
3101
3102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3103 :- use_module(kernel_freetypes,[register_freetypes/1]).
3104 % cleaning up machine
3105 clean_up_machine(In,RefMachines,Out) :-
3106 extract_parameter_types([ref(local,In)|RefMachines],NonGroundExceptions),
3107 clean_up_machine2(NonGroundExceptions,In,Out).
3108 clean_up_machine2(NonGroundExceptions) -->
3109 get_section_content(enumerated_sets,Enum),
3110 get_section_content(enumerated_elements,Elems),
3111 {register_enumerated_sets(Enum,Elems)}, % register name of enumerated sets, e.g., to detect finite in ast_cleanup
3112 get_section_content(freetypes,Freetypes),
3113 {register_freetypes(Freetypes)}, % so that info is available in ast_cleanup for eval_set_extension
3114 clean_up_section(constraints,NonGroundExceptions),
3115 clean_up_section(properties,NonGroundExceptions),
3116 clean_up_section(invariant,NonGroundExceptions),
3117 clean_up_section(initialisation,NonGroundExceptions),
3118 clean_up_section(assertions,NonGroundExceptions),
3119 clean_up_section(operation_bodies,NonGroundExceptions).
3120 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
3121 :- if(environ(prob_safe_mode,true)).
3122 clean_up_section(Section,NonGroundExceptions,In,Out) :-
3123 select_section_texprs(Section,Old,New,In,Out),
3124 %format('Cleaning up and optimizing section ~w~n',[Section]), %maplist(check_ast,Old), % this will raise errors
3125 clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section),
3126 %format('Checking result of clean_up section ~w~n',[Section]),
3127 maplist(check_ast(true),New),
3128 formatsilent('Finished checking section ~w~n',[Section]).
3129 :- else.
3130 clean_up_section(Section,NonGroundExceptions,In,Out) :-
3131 % debug_stats(cleaning_up(Section)),
3132 select_section_texprs(Section,Old,New,In,Out),
3133 clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section).
3134 :- endif.
3135
3136 get_section_content(SecName,SectionContent,Mch,Mch) :- get_section(SecName,Mch,SectionContent).
3137
3138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3139 % type expressions in context of an already type-checked machine
3140 % TO DO: maybe do some performance optimisations for identifiers, values, ... to avoid creating scope, see formula_typecheck2_for_eval optimisation
3141 % b_type_literal does it in bmachine before calling type_in_machine_l
3142 type_in_machine_l(Exprs,Scope,NonGroundExceptions,Machine,Types,TExprs,Errors) :-
3143 MType = machine,
3144 create_scope_if_necessary(Exprs,Scope,MType,Machine,Env,Errors,E1), % this can be expensive for big B machines
3145 %runtime_profiler:profile_single_call(create_scope,unknown,
3146 % bmachine_construction:create_scope(Scope,MType,Machine,Env,Errors,E1)),
3147 btype_ground_dl(Exprs,Env,NonGroundExceptions,Types,TExprUncleans,E1,[]),
3148 perform_post_static_check(TExprUncleans),
3149 ? maplist(clean_up_pred_or_expr(NonGroundExceptions),TExprUncleans,TExprs).
3150
3151 % detect a few common expressions which do not require creating a scope of all the identifiers
3152 create_scope_if_necessary([E],_Scope,MType,Machine,Env,Ein,Eout) :- raw_expr_wo_ids(E),!,
3153 create_scope([],MType,Machine,Env,Ein,Eout).
3154 create_scope_if_necessary(_Exprs,Scope,MType,Machine,Env,Ein,Eout) :-
3155 create_scope(Scope,MType,Machine,Env,Ein,Eout).
3156
3157 % a few common raw ASTs which do not refer to identifiers
3158 raw_expr_wo_ids(falsity(_)).
3159 raw_expr_wo_ids(truth(_)).
3160 raw_expr_wo_ids(empty_set(_)).
3161 raw_expr_wo_ids(equal(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3162 raw_expr_wo_ids(not_equal(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3163 raw_expr_wo_ids(interval(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3164 raw_expr_wo_ids(add(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3165 raw_expr_wo_ids(minus_or_set_subtract(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3166 raw_expr_wo_ids(mult_or_cart(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B).
3167 raw_expr_wo_ids(unary_minus(_,A)) :- raw_expr_wo_ids(A).
3168 raw_expr_wo_ids(boolean_true(_)).
3169 raw_expr_wo_ids(boolean_false(_)).
3170 raw_expr_wo_ids(bool_set(_)).
3171 raw_expr_wo_ids(integer(_,_)).
3172 raw_expr_wo_ids(real(_,_)).
3173 raw_expr_wo_ids(string(_,_)).
3174
3175
3176 % note prob_ids(visible), external_library(all_available_libraries) scope is expanded somewhere else
3177 create_scope(pre_expanded_scope(PEnv,Errors),_MType,_Machine,Env,Ein,Eout) :-
3178 !, % already pre-expanded
3179 Env=PEnv, append(Errors,Eout, Ein).
3180 create_scope(Scope,MType,Machine,Env,Ein,Eout) :-
3181 env_empty(Init),
3182 add_theory_operators(Machine,Init,WithOperators),
3183 foldl(create_scope2(MType,Machine),Scope,WithOperators/Ein,Env/Eout).
3184 add_theory_operators(Machine,Ein,Eout) :-
3185 get_section(operators,Machine,Operators),
3186 keys_and_values(Operators,Ids,Ops),
3187 foldl(env_store_operator,Ids,Ops,Ein,Eout).
3188 create_scope2(MType,Machine,Scope,In/Ein,Out/Eout) :-
3189 ( Scope = constants ->
3190 visible_env(MType,properties,[ref(local,Machine)],In,Out,Ein,Eout)
3191 ; Scope = variables ->
3192 visible_env(MType,invariant,[ref(local,Machine)],In,Out,Ein,Eout)
3193 ; Scope = variables_and_additional_defs ->
3194 visible_env(MType,invariant,[extended_local_ref(Machine)],In,Out,Ein,Eout)
3195 ; Scope = assertions_scope_and_additional_defs ->
3196 visible_env(MType,assertions,[extended_local_ref(Machine)],In,Out,Ein,Eout)
3197 ; Scope = prepost ->
3198 visible_env(MType,prepost,[ref(local,Machine)],In,Out,Ein,Eout)
3199 ; Scope = operation_bodies ->
3200 visible_env(MType,operation_bodies,[ref(local,Machine)],In,Out,Ein,Eout)
3201 ; Scope = operation(Op) ->
3202 create_operation_scope(Op,Machine,In,Out), Ein=Eout
3203 ; Scope = env(ExplicitEnv) ->
3204 ExplicitEnv = Out, Ein=Eout
3205 ; Scope = identifier(Ids) ->
3206 store_variables(Ids,In,Out), Ein=Eout
3207 ; Scope = external_library(LibName) % be sure to make these last to avoid duplication errors
3208 -> store_ext_defs(LibName,In,Out), Ein=Eout
3209 ;
3210 add_error(bmachine_construction, 'invalid scope', Scope),fail).
3211 create_operation_scope(Op,Machine,In,Out) :-
3212 get_section(operation_bodies,Machine,OpBodies),
3213 get_texpr_id(OpId,op(Op)),
3214 get_texpr_expr(TOp,operation(OpId,Results,Params,_)),
3215 ( member(TOp,OpBodies) ->
3216 append(Results,Params,LocalVariables),
3217 store_variables(LocalVariables,In,Out)
3218 ;
3219 ajoin(['operation \'',Op,'\' not found for building scope'], Msg),
3220 add_error(bmachine_construction,Msg),fail).
3221 store_variables(Ids,In,Out) :- foldl(store_variable,Ids,In,Out).
3222 store_variable(Id,In,Out) :-
3223 get_texpr_id(Id,Name),get_texpr_type(Id,Type),
3224 env_store(Name,Type,[],In,Out).
3225
3226 type_open_predicate_with_quantifier(OptionalOuterQuantifier,Predicate,Scope,Machine,TResult,Errors) :-
3227 type_open_formula(Predicate,Scope,false,Machine,pred,Identifiers,TPred,Errors),
3228 ( Identifiers = [] ->
3229 TResult = TPred
3230 ; OptionalOuterQuantifier=forall ->
3231 create_forall(Identifiers,TPred,TResult)
3232 ; OptionalOuterQuantifier=no_quantifier ->
3233 TResult = TPred
3234 ; % OptionalOuterQuantifier=exists
3235 %perform_do_not_enumerate_analysis(Identifiers,TPred,'EXISTS',Span,TPred2), % now done by apply_kodkod_or_other_optimisations
3236 infer_seq_types_for_tids(Identifiers,TPred,Identifiers2), % relevant for test 1187
3237 create_exists(Identifiers2,TPred,TResult)
3238 ).
3239
3240 type_open_formula(Raw,Scope,AllowOpenIdsinExpressions,Machine,Type,Identifiers,Result,Errors) :-
3241 create_scope_if_necessary([Raw],Scope,machine,Machine,Env1,Errors,E1),
3242 ( Identifiers==[] -> Mode=closed, Env1=Env
3243 ; Mode=open, openenv(Env1,Env)),
3244 btype_ground_dl([Raw],Env,[],[Type],[TExprUnclean],E1,E2),
3245 ( Mode=closed -> true
3246 ;
3247 openenv_identifiers(Env,Identifiers), % TODO: treat theory operators ?
3248 check_ground_types_dl(Identifiers,[],E2,E3)
3249 ),
3250 %print(type_open_formula(Identifiers,TExprUnclean)),nl,
3251 mark_outer_quantifier_ids(TExprUnclean,TExprUnclean2),
3252 perform_post_static_check([TExprUnclean2]),
3253 clean_up_pred_or_expr([],TExprUnclean2,TResult), % TODO: only run if requested
3254 ( Identifiers = [] -> % no newly introduced identifiers, no problem
3255 E3 = []
3256 ; Type = pred -> % newly introduced identifiers in a predicate - ok
3257 E3 = []
3258 ; AllowOpenIdsinExpressions=true -> % we explicitly allow open ids in expressions
3259 E3 = []
3260 ; % newly introduced identifiers in expression make no sense
3261 % (is that so?)
3262 foldl(add_unknown_identifier_error,Identifiers,E3,[])
3263 ),
3264 Result = TResult.
3265 add_unknown_identifier_error(TId,[error(Msg,Pos)|E],E) :-
3266 get_texpr_id(TId,Id),
3267 ajoin(['Unknown identifier ',Id,'.'],Msg),
3268 % TO DO: generate fuzzy match and possible completions message
3269 get_texpr_pos(TId,Pos).
3270
3271 % mark outermost identfiers so that they don't get optimized away
3272 % e.g., ensure that we print the solution for something like #(y2,x2).(x2 : 0..10 & y2 : 0..10 & x2 = 10 & y2 = 10)
3273 mark_outer_quantifier_ids(b(exists(IDS,Pred),pred,Info),Res) :-
3274 maplist(mark_id,IDS,MIDS),!,
3275 Res = b(exists(MIDS,Pred),pred,Info).
3276 mark_outer_quantifier_ids(b(let_predicate(IDS,Exprs,Pred),pred,Info),Res) :-
3277 maplist(mark_id,IDS,MIDS),!,
3278 Res = b(let_predicate(MIDS,Exprs,Pred),pred,Info).
3279 mark_outer_quantifier_ids(b(forall(IDS,LHS,RHS),pred,Info),Res) :-
3280 maplist(mark_id,IDS,MIDS),!,
3281 Res = b(forall(MIDS,LHS,RHS),pred,Info).
3282 mark_outer_quantifier_ids(X,X).
3283
3284 mark_id(b(identifier(ID),TYPE,INFO),b(identifier(ID),TYPE,[do_not_optimize_away|INFO])).
3285
3286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3287
3288 % some additional static checks; they should be run only once before ast_cleanup runs
3289 % TO DO: move maybe some of the exists/forall checks here; would be simpler and not require removed_typing inspection
3290
3291 :- use_module(bsyntaxtree,[map_over_typed_bexpr/2]).
3292 perform_post_static_check([Typed|_]) :- %print(t(Typed)),nl,
3293 preferences:get_preference(disprover_mode,false),
3294 map_over_typed_bexpr(post_static_check,Typed),
3295 fail.
3296 perform_post_static_check([_|T]) :- !, perform_post_static_check(T).
3297 perform_post_static_check(_).
3298
3299 post_static_check(b(E,T,I)) :-
3300 %bsyntaxtree:check_infos(I,post_static_check),
3301 post_static_check_aux(E,T,I).
3302
3303 :- use_module(bsyntaxtree,[find_identifier_uses/3, is_a_disjunct_or_implication/4]).
3304 :- use_module(library(ordsets),[ord_subtract/3]).
3305 % should detect patterns like { zz | # r__1, q__1, zz . ( ...)}
3306 % {x,z|z=1}[{TRUE}] = {1}
3307 % %x.(1=1|1)(TRUE) = 1
3308 post_static_check_aux(lambda(Ids,Body,Expr),_T,Info) :-
3309 get_texpr_ids(Ids,AtomicIds), sort(AtomicIds,SortedIds),
3310 find_identifier_uses(Body,[],UsedIds1),
3311 find_identifier_uses(Expr,[],UsedIds2), % relevant for tests 1106, 1264, 1372, 1622 to also look at Expr
3312 ord_subtract(SortedIds,UsedIds1,S2),
3313 ord_subtract(S2,UsedIds2,UnusedIds), UnusedIds \= [],
3314 add_warning_with_info('Condition of lambda does not use these identifiers: ',UnusedIds,Body,Info).
3315 post_static_check_aux(comprehension_set(Ids,Body),_T,Info) :-
3316 get_texpr_ids(Ids,AtomicIds), sort(AtomicIds,SortedIds),
3317 find_identifier_uses(Body,[],UsedIds),
3318 ord_subtract(SortedIds,UsedIds,UnusedIds), UnusedIds \= [],
3319 add_warning_with_info('Body of comprehension set does not use these identifiers: ',UnusedIds,Body,Info).
3320 post_static_check_aux(exists(Ids,P),_T,Info) :-
3321 is_a_disjunct_or_implication(P,Type,_Q,_R),
3322 exists_body_warning(Ids,P,Info,Type).
3323
3324 % see also check_implication_inside_exists ast cleanup rule
3325 exists_body_warning(_,_,_,_) :- animation_minor_mode(X),(X=eventb ; X=tla),!.
3326 exists_body_warning(Ids,P,I,Type) :-
3327 ajoin(['Body of existential quantifier is a(n) ',Type,
3328 ' (not allowed by Atelier-B): '],Msg),
3329 add_warning_with_info(Msg,b(exists(Ids,P),pred,I),P,I).
3330
3331
3332
3333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3334 % Warnings
3335
3336 :- use_module(bsyntaxtree,[contains_info_pos/1]).
3337 add_warning_with_info(Msg1,Msg2,P,Info) :-
3338 (contains_info_pos(Info) -> Pos=Info ; Pos=P),
3339 add_warning(bmachine_construction,Msg1,Msg2,Pos).
3340
3341 :- dynamic warnings/2.
3342 clear_warnings :-
3343 retractall( warnings(_,_) ).
3344 show_warnings :-
3345 warnings(Warning,Span),
3346 add_warning(bmachine_construction, Warning,'',Span),
3347 fail.
3348 show_warnings.
3349
3350
3351 store_warning(Warning,Span) :-
3352 (warnings(Warning,Span) -> true ; assertz(warnings(Warning,Span))).