1 | :- module(welldef, [ensure_wd/2, | |
2 | po_on_lhs/1, | |
3 | po_on_lhs/2, | |
4 | strip_pos/2, | |
5 | wd_pos/2]). | |
6 | ||
7 | :- use_module(library(lists), [maplist/3, maplist/4, append/2]). | |
8 | ||
9 | :- use_module(wdsrc(well_def_hyps), [empty_hyps/1, push_hyp/4, negate_hyp/2]). | |
10 | :- use_module(wdsrc(well_def_analyser), [transform_wd/4, compute_wd/4, | |
11 | toplevel_wd_pos/3]). | |
12 | :- use_module(probsrc(bsyntaxtree), [repair_used_ids/3]). | |
13 | ||
14 | ||
15 | :- use_module(fuzztypes). | |
16 | ||
17 | ||
18 | %% wd_pos(Predicate, ProofObligations). | |
19 | % | |
20 | % Returns a list of proof obligations for the given predicate. | |
21 | wd_pos(Pred, WDPOs) :- | |
22 | empty_hyps(Hyps), | |
23 | findall(WDPO, compute_wd(Pred, Hyps, [discharge_po], WDPO), WDPOs). | |
24 | ||
25 | ||
26 | %% ensure_wd(+Predicate, -PredicateWithProofObligations). | |
27 | % | |
28 | % Ensures that the given predicate is well-defined by adding open | |
29 | % proof obligations for all well-definedness conditions as additional conjuncts. | |
30 | ensure_wd(Pred, WDPred) :- | |
31 | empty_hyps(Hyps), | |
32 | % push_hyp(Hyps, Pred, [create_full_po], FullHyps), | |
33 | % transform_bexpr(b_ast_cleanup:compute_wd_info, Pred, InterPred), | |
34 | transform_wd(Pred, Hyps, [discharge_po], AnnotatedPred), | |
35 | ensure_wd_node(AnnotatedPred, Hyps, _, PrelimWD), | |
36 | repair_used_ids('Welldef Enforcer', PrelimWD, WDPred). | |
37 | ||
38 | %% ensure_wd_node(+Ast, +Hyps, -RemainingGoals, -WdAst). | |
39 | % | |
40 | % Implements any WD conditions into the closest predicate nodes. | |
41 | % When not possible the remaining conditions are returned via `RemainingGoals`. | |
42 | ensure_wd_node([N | Ns], H, RemainingPOs, [Wd|Wds]) :- | |
43 | !, % Special case: List. | |
44 | ensure_wd_node_list([N | Ns], H, NestedRemainingPOs, [Wd|Wds]), | |
45 | append(NestedRemainingPOs, RemainingPOs). | |
46 | ensure_wd_node(b(Node, Type, Info), H, RemainingPOs, NewAst) :- | |
47 | % Special cases: POs to LHS (middle child) | |
48 | Cases = [general_sum, general_product, quantified_intersection, quantified_union, lambda, forall], | |
49 | node_split(Node, Name, [Ids, Pred, Expr]), | |
50 | member(Name, Cases), | |
51 | !, | |
52 | wd_to_lhs(Pred, Expr, H, WdPred, WdExpr), | |
53 | node_split(WdNode, Name, [Ids, WdPred, WdExpr]), | |
54 | WdAst = b(WdNode, Type, Info), | |
55 | toplevel_wd_pos(WdAst, H, RootPOs), | |
56 | conjunct_or_propagate_pos(WdAst, RootPOs, NewAst, RemainingPOs). | |
57 | ensure_wd_node(b(Node, Type, Info), H, RemainingPOs, NewAst) :- | |
58 | % Special cases: POs to RHS (last child) | |
59 | Cases = [event_b_comprehension_set], | |
60 | node_split(Node, Name, [Ids, Expr, Pred]), | |
61 | member(Name, Cases), | |
62 | !, | |
63 | wd_to_rhs(Expr, Pred, H, WdExpr, WdPred), | |
64 | node_split(WdNode, Name, [Ids, WdExpr, WdPred]), | |
65 | WdAst = b(WdNode, Type, Info), | |
66 | toplevel_wd_pos(WdAst, H, RootPOs), | |
67 | conjunct_or_propagate_pos(WdAst, RootPOs, NewAst, RemainingPOs). | |
68 | ensure_wd_node(b(assertion_expression(Test, ErrText, Value), Type, Info), H, POs, Result) :- | |
69 | Result = b(assertion_expression(Test, ErrText, WdValue), Type, Info), | |
70 | ensure_wd_node(Value, H, POs, WdValue). | |
71 | ensure_wd_node(b(if_then_else(If, Then, Else), Type, Info), H, POs, WdIfThenElse) :- | |
72 | Type \= pred, !, % Special case for If-Expressions. | |
73 | ensure_wd_node(If, H, [], WdIf), | |
74 | push_hyp(H, If, [create_full_po], ThenH), | |
75 | transform_wd(Then, H, [discharge_po], TThen), | |
76 | ensure_wd_node(TThen, ThenH, ThenPOs, WdThen), | |
77 | negate_hyp(If, NotIf), | |
78 | push_hyp(H, NotIf, [create_full_po], ElseH), | |
79 | ensure_wd_node(Else, ElseH, ElsePOs, WdElse), | |
80 | if_then_else_po(WdIf, ThenPOs, IfThen), | |
81 | ensure_wd_node(NotIf, H, [], WdNotIf), | |
82 | if_then_else_po(WdNotIf, ElsePOs, NotIfElse), | |
83 | WdIfThenElse = b(if_then_else(WdIf, WdThen, WdElse), Type, Info), | |
84 | append(IfThen, NotIfElse, POs). | |
85 | ensure_wd_node(b(Node, Type, Info), H, RemainingPOs, NewAst) :- | |
86 | % Special cases: WD constraints from LHS chain into RHS | |
87 | Cases = [conjunct,implication], | |
88 | node_split(Node, Name, [Lhs, Rhs]), | |
89 | member(Name, Cases), | |
90 | !, | |
91 | ensure_wd_node(Lhs, H, [], WdLhs), | |
92 | push_hyp(H, WdLhs, [create_full_po], RhsH), | |
93 | ensure_wd_node(Rhs, RhsH, [], WdRhs), | |
94 | node_split(WdNode, Name, [WdLhs, WdRhs]), | |
95 | WdAst = b(WdNode, Type, Info), | |
96 | toplevel_wd_pos(WdAst, H, RootPOs), | |
97 | conjunct_or_propagate_pos(WdAst, RootPOs, NewAst, RemainingPOs). | |
98 | ensure_wd_node(Node, H, RemainderPOs, WdPred) :- | |
99 | wd_children_and_pos(Node, H, POs, InterNode), | |
100 | conjunct_or_propagate_pos(InterNode, POs, WdPred, RemainderPOs). | |
101 | ||
102 | ||
103 | ensure_wd_node_list([], _, [], []) :- !. | |
104 | ensure_wd_node_list([N | Ns], H, [R | Rs], [Wd | Wds]) :- | |
105 | ensure_wd_node(N, H, R, Wd), | |
106 | ensure_wd_node_list(Ns, H, Rs, Wds). | |
107 | ||
108 | ||
109 | ||
110 | % Helper for repetitive code. | |
111 | wd_children_and_pos(b(Node, Type, Info), H, POs, UpdatedNode) :- | |
112 | node_split(Node, Name, Children), | |
113 | ensure_wd_node_list(Children, H, Remainders, WdChildren), | |
114 | append(Remainders, ChildPOs), | |
115 | node_split(NewNode, Name, WdChildren), | |
116 | UpdatedNode = b(NewNode, Type, Info), | |
117 | toplevel_wd_pos(UpdatedNode, H, RootPOs), | |
118 | append(ChildPOs, RootPOs, POs). | |
119 | ||
120 | ||
121 | % Helps dispatching whether the POs need to be included into a predicate or | |
122 | % an expression needs to propagate them upwards. | |
123 | conjunct_or_propagate_pos(b(Node, pred, Info), POs, WdPred, []) :- | |
124 | !, % predicates conjunct the POs into themselves. | |
125 | filter_discharged_pos(POs, UndischargedPOs), | |
126 | include_pos(UndischargedPOs, b(Node, pred, Info), WdPred). | |
127 | conjunct_or_propagate_pos(Node, POs, Node, POs). % Expressions remain unchanged. | |
128 | ||
129 | ||
130 | ||
131 | ||
132 | ||
133 | % Assumes Lhs is predicate and Rhs is expression. | |
134 | wd_to_lhs(Lhs, Rhs, H, WdLhs, Rhs2) :- | |
135 | ensure_wd_node(Lhs, H, [], Lhs2), | |
136 | push_hyp(H, Lhs2, [create_full_po], NewH), | |
137 | wd_children_and_pos(Rhs, NewH, POs, Rhs2), | |
138 | filter_discharged_pos(POs, UndischargedPOs), | |
139 | include_pos(UndischargedPOs, Lhs2, WdLhs). | |
140 | % Assumes Lhs is expression and Rhs is predicate. | |
141 | wd_to_rhs(Lhs, Rhs, H, WdLhs, WdRhs) :- | |
142 | wd_to_lhs(Rhs, Lhs, H, WdRhs, WdLhs). | |
143 | ||
144 | ||
145 | if_then_else_po(Condition, BranchPOs, Res) :- | |
146 | filter_discharged_pos(BranchPOs, Filtered), | |
147 | if_then_else_implication(Filtered, Condition, Res). | |
148 | ||
149 | if_then_else_implication([], _, []) :- !. | |
150 | if_then_else_implication(POs, Condition, [Res]) :- | |
151 | po_conjunct(POs, BranchConjunct), | |
152 | Res = b(implication(Condition, BranchConjunct), pred, [contains_wd_condition]). | |
153 | ||
154 | ||
155 | ||
156 | ||
157 | %% include_pos(ProofObligations, Predicate, PredicateWithPOs). | |
158 | % | |
159 | % Returns a predicate with the given proof obligations included via `conjunct` nodes. | |
160 | include_pos([], Pred, Pred) :- !. | |
161 | include_pos(POs, Pred, b(conjunct(POsConj, Pred), pred, [contains_wd_condition,fuzzed_with_wd_po_included])) :- | |
162 | po_conjunct(POs, POsConj). | |
163 | ||
164 | filter_discharged_pos([], []). | |
165 | filter_discharged_pos([PO | POs], [PO | NonDischarged]) :- | |
166 | PO = b(_, _, Info), | |
167 | \+ member(discharged(true, _), Info), !, | |
168 | filter_discharged_pos(POs, NonDischarged). | |
169 | filter_discharged_pos([_|POs], NonDischarged) :- | |
170 | filter_discharged_pos(POs, NonDischarged). | |
171 | ||
172 | po_conjunct([PO], PO) :- !. | |
173 | po_conjunct([PO | POs], b(conjunct(PO, POsConj), pred, [contains_wd_condition])) :- | |
174 | po_conjunct(POs, POsConj). | |
175 | ||
176 | ||
177 | %% po_on_lhs(+AST). | |
178 | % | |
179 | % Succeeds if the left side of the AST is marked as POs. | |
180 | % This will be the case, if the b/3 structure contains the | |
181 | % `fuzzed_with_wd_po_included` flag. | |
182 | % | |
183 | % AST: The AST to check. | |
184 | po_on_lhs(b(conjunct(_, _), _, Info)) :- | |
185 | member(fuzzed_with_wd_po_included, Info). | |
186 | ||
187 | %% po_on_lhs(+AST, -RHS). | |
188 | % | |
189 | % Succeeds if the left side of the AST is marked as POs. | |
190 | % This will be the case, if the b/3 structure contains the | |
191 | % `fuzzed_with_wd_po_included` flag. | |
192 | % | |
193 | % AST: The AST to check. | |
194 | % RHS: The non-PO part of the AST. | |
195 | po_on_lhs(b(conjunct(_, RHS), _, Info), RHS) :- | |
196 | member(fuzzed_with_wd_po_included, Info). | |
197 | ||
198 | ||
199 | %% strip_pos(+Ast, -AstWithoutPOs). | |
200 | % | |
201 | % Removes any subtrees which are generated by `ensure_wd` from the predicate. | |
202 | strip_pos([A|As], Raws) :- | |
203 | !, | |
204 | maplist(strip_pos, [A|As], Raws). | |
205 | strip_pos(Ast, RawAst) :- | |
206 | po_on_lhs(Ast, InterAst), | |
207 | !, | |
208 | strip_pos(InterAst, RawAst). | |
209 | strip_pos(b(assertion_expression(_, _, SecuredPred), _, _), Result) :- | |
210 | !, % assertion expressions are discarded. | |
211 | strip_pos(SecuredPred, Result). | |
212 | strip_pos(b(Node, Type, Info), b(NewNode, Type, Info)) :- | |
213 | node_split(Node, Name, Children), | |
214 | maplist(strip_pos, Children, NewChildren), | |
215 | node_split(NewNode, Name, NewChildren). | |
216 | ||
217 | ||
218 | node_split(Node, Name, [b(Child1, T1, I1) | OtherChildren]) :- | |
219 | Node =.. [Name, b(Child1, T1, I1) | OtherChildren], !. | |
220 | node_split(Node, Name, [[A|As] | OtherChildren]) :- | |
221 | Node =.. [Name, [A|As] | OtherChildren], !. | |
222 | node_split(Node, Node, []). % Catches things like integer(42) or integer_set('NAT'). |