|
27 | 27 | // describing how this rule is derived from existing rules. See RewriteLoop.cpp |
28 | 28 | // for a discussion of rewrite loops. |
29 | 29 | // |
30 | | -// This implementation also extends Knuth-Bendix to introduce new _generators_, |
31 | | -// in addition to new relations as in the standard algorithm. See the comment at |
32 | | -// the top of RewriteSystem::processMergedAssociatedTypes() for a description. |
33 | | -// |
34 | 30 | //===----------------------------------------------------------------------===// |
35 | 31 |
|
36 | 32 | #include "swift/Basic/Range.h" |
@@ -74,269 +70,6 @@ Symbol Symbol::prependPrefixToConcreteSubstitutions( |
74 | 70 | }, ctx); |
75 | 71 | } |
76 | 72 |
|
77 | | -/// If we have two symbols [P:T] and [Q:T], produce a merged symbol: |
78 | | -/// |
79 | | -/// - If P inherits from Q, this is just [P:T]. |
80 | | -/// - If Q inherits from P, this is just [Q:T]. |
81 | | -/// - If P and Q are unrelated, this is [P&Q:T]. |
82 | | -Symbol RewriteContext::mergeAssociatedTypes(Symbol lhs, Symbol rhs) { |
83 | | - auto key = std::make_pair(lhs, rhs); |
84 | | - |
85 | | - auto found = MergedAssocTypes.find(key); |
86 | | - if (found != MergedAssocTypes.end()) |
87 | | - return found->second; |
88 | | - |
89 | | - // Check preconditions that were established by RewriteSystem::addRule(). |
90 | | - assert(lhs.getKind() == Symbol::Kind::AssociatedType); |
91 | | - assert(rhs.getKind() == Symbol::Kind::AssociatedType); |
92 | | - assert(lhs.getName() == rhs.getName()); |
93 | | - assert(*lhs.compare(rhs, *this) > 0); |
94 | | - |
95 | | - auto protos = lhs.getProtocols(); |
96 | | - auto otherProtos = rhs.getProtocols(); |
97 | | - |
98 | | - // This must follow from lhs > rhs. |
99 | | - assert(getProtocolSupport(protos) <= getProtocolSupport(otherProtos)); |
100 | | - |
101 | | - // Compute sorted and merged list of protocols, with duplicates. |
102 | | - llvm::TinyPtrVector<const ProtocolDecl *> newProtos; |
103 | | - std::merge(protos.begin(), protos.end(), |
104 | | - otherProtos.begin(), otherProtos.end(), |
105 | | - std::back_inserter(newProtos), |
106 | | - [&](const ProtocolDecl *lhs, |
107 | | - const ProtocolDecl *rhs) -> int { |
108 | | - return compareProtocols(lhs, rhs) < 0; |
109 | | - }); |
110 | | - |
111 | | - // Prune duplicates and protocols that are inherited by other |
112 | | - // protocols. |
113 | | - llvm::TinyPtrVector<const ProtocolDecl *> minimalProtos; |
114 | | - for (const auto *newProto : newProtos) { |
115 | | - auto inheritsFrom = [&](const ProtocolDecl *thisProto) { |
116 | | - if (thisProto == newProto) |
117 | | - return true; |
118 | | - |
119 | | - const auto &inherited = getInheritedProtocols(thisProto); |
120 | | - return std::find(inherited.begin(), |
121 | | - inherited.end(), |
122 | | - newProto) != inherited.end(); |
123 | | - }; |
124 | | - |
125 | | - if (std::find_if(minimalProtos.begin(), minimalProtos.end(), |
126 | | - inheritsFrom) |
127 | | - == minimalProtos.end()) { |
128 | | - minimalProtos.push_back(newProto); |
129 | | - } |
130 | | - } |
131 | | - |
132 | | - // The two input sets are minimal already, so the merged set |
133 | | - // should have at least as many elements as the smallest |
134 | | - // input set. |
135 | | - assert(minimalProtos.size() >= std::min(protos.size(), otherProtos.size())); |
136 | | - |
137 | | - // The merged set cannot contain more elements than the union |
138 | | - // of the two sets. |
139 | | - assert(minimalProtos.size() <= protos.size() + otherProtos.size()); |
140 | | - |
141 | | - auto result = Symbol::forAssociatedType(minimalProtos, lhs.getName(), *this); |
142 | | - auto inserted = MergedAssocTypes.insert(std::make_pair(key, result)); |
143 | | - assert(inserted.second); |
144 | | - (void) inserted; |
145 | | - |
146 | | - return result; |
147 | | -} |
148 | | - |
149 | | -/// Consider the following example: |
150 | | -/// |
151 | | -/// protocol P1 { associatedtype T : P1 } |
152 | | -/// protocol P2 { associatedtype T : P2 } |
153 | | -/// struct G<T : P1 & P2> {} |
154 | | -/// |
155 | | -/// We start with these rewrite rules: |
156 | | -/// |
157 | | -/// [P1].T => [P1:T] |
158 | | -/// [P2].T => [P2:T] |
159 | | -/// [P1:T].[P1] => [P1:T] |
160 | | -/// [P2:T].[P1] => [P2:T] |
161 | | -/// τ_0_0.[P1] => τ_0_0 |
162 | | -/// τ_0_0.[P2] => τ_0_0 |
163 | | -/// τ_0_0.T => τ_0_0.[P1:T] |
164 | | -/// τ_0_0.[P2:T] => τ_0_0.[P1:T] |
165 | | -/// |
166 | | -/// The completion procedure ends up adding an infinite series of rules of the |
167 | | -/// form |
168 | | -/// |
169 | | -/// τ_0_0.[P1:T].[P2] => τ_0_0.[P1:T] |
170 | | -/// τ_0_0.[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T] |
171 | | -/// |
172 | | -/// τ_0_0.[P1:T].[P1:T].[P2] => τ_0_0.[P1:T].[P1:T] |
173 | | -/// τ_0_0.[P1:T].[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T].[P1:T] |
174 | | -/// |
175 | | -/// τ_0_0.[P1:T].[P1:T].[P1:T].[P2] => τ_0_0.[P1:T].[P1:T].[P1.T] |
176 | | -/// τ_0_0.[P1:T].[P1:T].[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T].[P1:T].[P1.T] |
177 | | -/// |
178 | | -/// The difficulty here stems from the fact that an arbitrary sequence of |
179 | | -/// [P1:T] following a τ_0_0 is known to conform to P2, but P1:T itself |
180 | | -/// does not conform to P2. |
181 | | -/// |
182 | | -/// We use a heuristic to compute a completion in this case by using |
183 | | -/// merged associated type terms. |
184 | | -/// |
185 | | -/// The key is the following rewrite rule: |
186 | | -/// |
187 | | -/// τ_0_0.[P2:T] => τ_0_0.[P1:T] |
188 | | -/// |
189 | | -/// When we add this rule, we introduce a new merged symbol [P1&P2:T] and |
190 | | -/// a new rule: |
191 | | -/// |
192 | | -/// τ_0_0.[P1:T] => τ_0_0.[P1&P2:T] |
193 | | -/// |
194 | | -/// We also look for any existing rules of the form [P1:T].[Q] => [P1:T] |
195 | | -/// or [P2:T].[Q] => [P2:T], and introduce a new rule: |
196 | | -/// |
197 | | -/// [P1&P2:T].[Q] => [P1&P2:T] |
198 | | -/// |
199 | | -/// In the above example, we have such a rule for Q == P1 and Q == P2, so |
200 | | -/// in total we end up adding the following four rules: |
201 | | -/// |
202 | | -/// τ_0_0.[P1:T] => τ_0_0.[P1&P2:T] |
203 | | -/// [P1&P2:T].[P1] => [P1&P2:T] |
204 | | -/// [P1&P2:T].[P2] => [P1&P2:T] |
205 | | -/// |
206 | | -/// Intuitively, since the conformance requirements on the merged term |
207 | | -/// are not prefixed by the root τ_0_0, they apply at any level; we've |
208 | | -/// "tied off" the recursion, and the rewrite system is now convergent. |
209 | | -void RewriteSystem::processMergedAssociatedTypes() { |
210 | | - if (MergedAssociatedTypes.empty()) |
211 | | - return; |
212 | | - |
213 | | - unsigned i = 0; |
214 | | - |
215 | | - // Chase the end of the vector, since addRule() might add new elements below. |
216 | | - while (i < MergedAssociatedTypes.size()) { |
217 | | - // Copy the entry out, since addRule() might add new elements below. |
218 | | - auto entry = MergedAssociatedTypes[i++]; |
219 | | - |
220 | | - if (Debug.contains(DebugFlags::Merge)) { |
221 | | - llvm::dbgs() << "## Processing associated type merge with "; |
222 | | - llvm::dbgs() << entry.rhs << ", "; |
223 | | - llvm::dbgs() << entry.lhsSymbol << ", "; |
224 | | - llvm::dbgs() << entry.mergedSymbol << "\n"; |
225 | | - } |
226 | | - |
227 | | - // If we have X.[P2:T] => Y.[P1:T], add a new rule: |
228 | | - // X.[P1:T] => X.[P1&P2:T] |
229 | | - MutableTerm lhs(entry.rhs); |
230 | | - |
231 | | - // Build the term X.[P1&P2:T]. |
232 | | - MutableTerm rhs(entry.rhs); |
233 | | - rhs.back() = entry.mergedSymbol; |
234 | | - |
235 | | - // Add the rule X.[P1:T] => X.[P1&P2:T]. |
236 | | - addRule(lhs, rhs); |
237 | | - |
238 | | - // Collect new rules here so that we're not adding rules while traversing |
239 | | - // the trie. |
240 | | - SmallVector<std::pair<MutableTerm, MutableTerm>, 2> inducedRules; |
241 | | - |
242 | | - // Look for conformance requirements on [P1:T] and [P2:T]. |
243 | | - auto visitRule = [&](unsigned ruleID) { |
244 | | - const auto &otherRule = getRule(ruleID); |
245 | | - const auto &otherLHS = otherRule.getLHS(); |
246 | | - if (otherLHS.size() == 2 && |
247 | | - otherLHS[1].getKind() == Symbol::Kind::Protocol) { |
248 | | - if (otherLHS[0] == entry.lhsSymbol || |
249 | | - otherLHS[0] == entry.rhs.back()) { |
250 | | - // We have a rule of the form |
251 | | - // |
252 | | - // [P1:T].[Q] => [P1:T] |
253 | | - // |
254 | | - // or |
255 | | - // |
256 | | - // [P2:T].[Q] => [P2:T] |
257 | | - if (Debug.contains(DebugFlags::Merge)) { |
258 | | - llvm::dbgs() << "### Lifting conformance rule " << otherRule << "\n"; |
259 | | - } |
260 | | - |
261 | | - // We know that [P1:T] or [P2:T] conforms to Q, therefore the |
262 | | - // merged type [P1&P2:T] must conform to Q as well. Add a new rule |
263 | | - // of the form: |
264 | | - // |
265 | | - // [P1&P2:T].[Q] => [P1&P2:T] |
266 | | - // |
267 | | - MutableTerm newLHS; |
268 | | - newLHS.add(entry.mergedSymbol); |
269 | | - newLHS.add(otherLHS[1]); |
270 | | - |
271 | | - MutableTerm newRHS; |
272 | | - newRHS.add(entry.mergedSymbol); |
273 | | - |
274 | | - inducedRules.emplace_back(newLHS, newRHS); |
275 | | - } |
276 | | - } |
277 | | - }; |
278 | | - |
279 | | - // Visit rhs first to preserve the ordering of protocol requirements in the |
280 | | - // the property map. This is just for aesthetic purposes in the debug dump, |
281 | | - // it doesn't change behavior. |
282 | | - Trie.findAll(entry.rhs.back(), visitRule); |
283 | | - Trie.findAll(entry.lhsSymbol, visitRule); |
284 | | - |
285 | | - // Now add the new rules. |
286 | | - for (const auto &pair : inducedRules) |
287 | | - addRule(pair.first, pair.second); |
288 | | - } |
289 | | - |
290 | | - MergedAssociatedTypes.clear(); |
291 | | -} |
292 | | - |
293 | | -/// Check if we have a rule of the form |
294 | | -/// |
295 | | -/// X.[P1:T] => X.[P2:T] |
296 | | -/// |
297 | | -/// If so, record this rule for later. We'll try to merge the associated |
298 | | -/// types in RewriteSystem::processMergedAssociatedTypes(). |
299 | | -void RewriteSystem::checkMergedAssociatedType(Term lhs, Term rhs) { |
300 | | - // FIXME: Figure out 3-cell representation for merged associated types |
301 | | - if (RecordLoops || |
302 | | - !Context.getASTContext().LangOpts.RequirementMachineMergedAssociatedTypes) |
303 | | - return; |
304 | | - |
305 | | - if (lhs.size() == rhs.size() && |
306 | | - std::equal(lhs.begin(), lhs.end() - 1, rhs.begin()) && |
307 | | - lhs.back().getKind() == Symbol::Kind::AssociatedType && |
308 | | - rhs.back().getKind() == Symbol::Kind::AssociatedType && |
309 | | - lhs.back().getName() == rhs.back().getName()) { |
310 | | - if (Debug.contains(DebugFlags::Merge)) { |
311 | | - llvm::dbgs() << "## Associated type merge candidate "; |
312 | | - llvm::dbgs() << lhs << " => " << rhs << "\n\n"; |
313 | | - } |
314 | | - |
315 | | - auto mergedSymbol = Context.mergeAssociatedTypes(lhs.back(), rhs.back()); |
316 | | - if (Debug.contains(DebugFlags::Merge)) { |
317 | | - llvm::dbgs() << "### Merged symbol " << mergedSymbol << "\n"; |
318 | | - } |
319 | | - |
320 | | - // We must have mergedSymbol <= rhs < lhs, therefore mergedSymbol != lhs. |
321 | | - assert(lhs.back() != mergedSymbol && |
322 | | - "Left hand side should not already end with merged symbol?"); |
323 | | - assert(*mergedSymbol.compare(rhs.back(), Context) <= 0); |
324 | | - assert(*rhs.back().compare(lhs.back(), Context) < 0); |
325 | | - |
326 | | - // If the merge didn't actually produce a new symbol, there is nothing else |
327 | | - // to do. |
328 | | - if (rhs.back() == mergedSymbol) { |
329 | | - if (Debug.contains(DebugFlags::Merge)) { |
330 | | - llvm::dbgs() << "### Skipping\n"; |
331 | | - } |
332 | | - |
333 | | - return; |
334 | | - } |
335 | | - |
336 | | - MergedAssociatedTypes.push_back({rhs, lhs.back(), mergedSymbol}); |
337 | | - } |
338 | | -} |
339 | | - |
340 | 73 | /// Compute a critical pair from the left hand sides of two rewrite rules, |
341 | 74 | /// where \p rhs begins at \p from, which must be an iterator pointing |
342 | 75 | /// into \p lhs. |
@@ -629,16 +362,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount, |
629 | 362 |
|
630 | 363 | simplifyRightHandSides(); |
631 | 364 | simplifyLeftHandSideSubstitutions(); |
632 | | - |
633 | | - // If the added rules merged any associated types, process the merges now |
634 | | - // before we continue with the completion procedure. This is important |
635 | | - // to perform incrementally since merging is required to repair confluence |
636 | | - // violations. |
637 | | - processMergedAssociatedTypes(); |
638 | 365 | } while (again); |
639 | 366 |
|
640 | | - assert(MergedAssociatedTypes.empty() && |
641 | | - "Should have processed all merge candidates"); |
642 | | - |
643 | 367 | return std::make_pair(CompletionResult::Success, 0); |
644 | 368 | } |
0 commit comments