|
2 | 2 |
|
3 | 3 | import java.util.ArrayList; |
4 | 4 | import java.util.HashMap; |
| 5 | +import java.util.HashSet; |
| 6 | +import java.util.LinkedHashSet; |
5 | 7 | import java.util.List; |
6 | 8 | import java.util.Map; |
7 | 9 | import java.util.Optional; |
@@ -56,8 +58,14 @@ record Scope(TypeChecker tc, StateSolver solver, List<State> states, List<List<S |
56 | 58 | /** The effects with any arguments, whose entries a condition on the result does not change, or null */ |
57 | 59 | private final CallVariant.Effects any; |
58 | 60 |
|
59 | | - private final Map<Integer, Map<State, CallVariant.Entry>> entries = new HashMap<>(); |
| 61 | + /** By state refinement, then by the states before the call in the state sets its {@code from} names */ |
| 62 | + private final Map<Integer, Map<List<String>, CallVariant.Entry>> entries = new HashMap<>(); |
60 | 63 | private final Map<Integer, Map<State, Optional<Set<State>>>> afters = new HashMap<>(); |
| 64 | + /** |
| 65 | + * By state refinement, then by the states before the call in the sets its {@code to} names for {@code old(this)}: |
| 66 | + * the states it can lead to in the sets it names |
| 67 | + */ |
| 68 | + private final Map<Integer, Map<List<String>, Set<List<String>>>> reachedBefore = new HashMap<>(); |
61 | 69 |
|
62 | 70 | /** |
63 | 71 | * @param changes |
@@ -99,13 +107,17 @@ public CallVariant.Entry entry(int index, State state) { |
99 | 107 | return CallVariant.Entry.HOLDS; // no state refinement: allowed in every state |
100 | 108 | if (onResult && any != null) |
101 | 109 | return any.entry(index, state); // the result is not known before the call |
102 | | - return entries.computeIfAbsent(index, i -> new HashMap<>()).computeIfAbsent(state, s -> { |
| 110 | + // the states of the other sets are about other ghost functions, so they cannot change the answer |
| 111 | + Predicate from = changes.get(index).getFrom(); |
| 112 | + List<String> named = namedSets(from).stream().map(state.names()::get).toList(); |
| 113 | + return entries.computeIfAbsent(index, i -> new HashMap<>()).computeIfAbsent(named, n -> { |
103 | 114 | Optional<CallVariant.Entry> entry = withScope(() -> { |
104 | | - Predicate before = before(s); |
105 | | - Predicate from = changes.get(index).getFrom().substituteVariable(Keys.THIS, BEFORE); |
106 | | - if (scope.solver().implies(before, from)) |
| 115 | + Predicate before = before(state); |
| 116 | + Predicate fromBefore = from.substituteVariable(Keys.THIS, BEFORE); |
| 117 | + if (scope.solver().implies(before, fromBefore)) |
107 | 118 | return CallVariant.Entry.HOLDS; |
108 | | - return scope.solver().compatible(before, from) ? CallVariant.Entry.UNDECIDED : CallVariant.Entry.FAILS; |
| 119 | + return scope.solver().compatible(before, fromBefore) ? CallVariant.Entry.UNDECIDED |
| 120 | + : CallVariant.Entry.FAILS; |
109 | 121 | }); |
110 | 122 | return entry.orElse(CallVariant.Entry.UNDECIDED); |
111 | 123 | }); |
@@ -133,11 +145,23 @@ private Optional<Set<State>> computeAfter(int index, State state, Set<State> can |
133 | 145 | ObjectState change = changes.get(index); |
134 | 146 | if (!change.hasTo()) |
135 | 147 | return Set.of(s); |
136 | | - Predicate to = change.getTo().substituteVariable(Keys.THIS, AFTER).changeOldMentions(BEFORE, AFTER); |
137 | | - Predicate known = Predicate.createConjunction(before(s), to); |
138 | | - if (onResult) |
139 | | - known = Predicate.createConjunction(known, condition); |
140 | | - Set<State> next = possibleStates(candidates, known, AFTER); |
| 148 | + List<Integer> named = namedSets(change.getTo()).stream().sorted().toList(); |
| 149 | + Set<State> kept = Set.copyOf(StateMachine.keeping(candidates, s, Set.copyOf(named))); |
| 150 | + // where it leads in the sets it names depends only on the state before in those it names for old(this) |
| 151 | + List<String> before = StateMachine.namedSetsBefore(change.getTo(), scope.stateSets()).stream().sorted() |
| 152 | + .map(s.names()::get).toList(); |
| 153 | + Map<List<String>, Set<List<String>>> reached = reachedBefore.computeIfAbsent(index, i -> new HashMap<>()); |
| 154 | + if (!reached.containsKey(before)) { |
| 155 | + Predicate to = change.getTo().substituteVariable(Keys.THIS, AFTER).changeOldMentions(BEFORE, AFTER); |
| 156 | + Predicate known = Predicate.createConjunction(before(s), to); |
| 157 | + if (onResult) |
| 158 | + known = Predicate.createConjunction(known, condition); |
| 159 | + reached.put(before, possibleStates(kept, known, AFTER).stream() |
| 160 | + .map(c -> named.stream().map(c.names()::get).toList()).collect(Collectors.toSet())); |
| 161 | + } |
| 162 | + Set<State> next = scope.states().stream().filter(kept::contains) |
| 163 | + .filter(c -> reached.get(before).contains(named.stream().map(c.names()::get).toList())) |
| 164 | + .collect(Collectors.toCollection(LinkedHashSet::new)); |
141 | 165 | // no state at all: the condition contradicts the refinement here, so nothing can be claimed |
142 | 166 | return next.isEmpty() && !onResult ? null : next; |
143 | 167 | }); |
@@ -171,6 +195,13 @@ private Set<State> possibleStates(Set<State> candidates, Predicate known, String |
171 | 195 | return scope.solver().possibleStates(combined, known, variable); |
172 | 196 | } |
173 | 197 |
|
| 198 | + /** The state sets that {@code predicate} or this variant's condition may name a state of */ |
| 199 | + private Set<Integer> namedSets(Predicate predicate) { |
| 200 | + Set<Integer> named = new HashSet<>(StateMachine.namedSets(predicate, scope.stateSets())); |
| 201 | + named.addAll(StateMachine.namedSets(condition, scope.stateSets())); |
| 202 | + return named; |
| 203 | + } |
| 204 | + |
174 | 205 | /** What is known before the call in {@code state} */ |
175 | 206 | private Predicate before(State state) { |
176 | 207 | Predicate before = state.predicate().substituteVariable(Keys.THIS, BEFORE); |
|
0 commit comments