Skip to content

Commit e2e088e

Browse files
committed
RequirementMachine: Remove merged associated types from completion
1 parent 0e6d10b commit e2e088e

18 files changed

+11
-377
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,6 @@ namespace swift {
498498
ASTVerifierOverrideKind ASTVerifierOverride =
499499
ASTVerifierOverrideKind::NoOverride;
500500

501-
/// Enables merged associated type support, which might go away.
502-
bool RequirementMachineMergedAssociatedTypes = false;
503-
504501
/// Enables dumping rewrite systems from the requirement machine.
505502
bool DumpRequirementMachine = false;
506503

include/swift/Option/FrontendOptions.td

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,6 @@ def requirement_machine_max_concrete_nesting : Joined<["-"], "requirement-machin
355355
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
356356
HelpText<"Set the maximum concrete type nesting depth before giving up">;
357357

358-
def disable_requirement_machine_merged_associated_types : Flag<["-"], "disable-requirement-machine-merged-associated-types">,
359-
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
360-
HelpText<"Disable merged associated types">;
361-
362-
def enable_requirement_machine_merged_associated_types : Flag<["-"], "enable-requirement-machine-merged-associated-types">,
363-
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
364-
HelpText<"Enable merged associated types">;
365-
366358
def debug_generic_signatures : Flag<["-"], "debug-generic-signatures">,
367359
HelpText<"Debug generic signatures">;
368360

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 0 additions & 276 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@
2727
// describing how this rule is derived from existing rules. See RewriteLoop.cpp
2828
// for a discussion of rewrite loops.
2929
//
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-
//
3430
//===----------------------------------------------------------------------===//
3531

3632
#include "swift/Basic/Range.h"
@@ -74,269 +70,6 @@ Symbol Symbol::prependPrefixToConcreteSubstitutions(
7470
}, ctx);
7571
}
7672

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-
34073
/// Compute a critical pair from the left hand sides of two rewrite rules,
34174
/// where \p rhs begins at \p from, which must be an iterator pointing
34275
/// into \p lhs.
@@ -629,16 +362,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
629362

630363
simplifyRightHandSides();
631364
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();
638365
} while (again);
639366

640-
assert(MergedAssociatedTypes.empty() &&
641-
"Should have processed all merge candidates");
642-
643367
return std::make_pair(CompletionResult::Success, 0);
644368
}

lib/AST/RequirementMachine/RewriteContext.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,6 @@ class RewriteContext final {
182182

183183
AssociatedTypeDecl *getAssociatedTypeForSymbol(Symbol symbol);
184184

185-
Symbol mergeAssociatedTypes(Symbol lhs, Symbol rhs);
186-
187185
//////////////////////////////////////////////////////////////////////////////
188186
///
189187
/// Construction of requirement machines for connected components in the

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,7 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
449449

450450
unsigned newRuleID = Rules.size();
451451

452-
auto uniquedLHS = Term::get(lhs, Context);
453-
auto uniquedRHS = Term::get(rhs, Context);
454-
Rules.emplace_back(uniquedLHS, uniquedRHS);
452+
Rules.emplace_back(Term::get(lhs, Context), Term::get(rhs, Context));
455453

456454
if (path) {
457455
// We have a rewrite path from the simplified lhs to the simplified rhs;
@@ -482,8 +480,6 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
482480
abort();
483481
}
484482

485-
checkMergedAssociatedType(uniquedLHS, uniquedRHS);
486-
487483
// Tell the caller that we added a new rule.
488484
return true;
489485
}

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -359,29 +359,6 @@ class RewriteSystem final {
359359
std::vector<CriticalPair> &pairs,
360360
std::vector<RewriteLoop> &loops) const;
361361

362-
/// Constructed from a rule of the form X.[P2:T] => X.[P1:T] by
363-
/// checkMergedAssociatedType().
364-
struct MergedAssociatedType {
365-
/// The *right* hand side of the original rule, X.[P1:T].
366-
Term rhs;
367-
368-
/// The associated type symbol appearing at the end of the *left*
369-
/// hand side of the original rule, [P2:T].
370-
Symbol lhsSymbol;
371-
372-
/// The merged associated type symbol, [P1&P2:T].
373-
Symbol mergedSymbol;
374-
};
375-
376-
/// A list of pending terms for the associated type merging completion
377-
/// heuristic. Entries are added by checkMergedAssociatedType(), and
378-
/// consumed in processMergedAssociatedTypes().
379-
std::vector<MergedAssociatedType> MergedAssociatedTypes;
380-
381-
void processMergedAssociatedTypes();
382-
383-
void checkMergedAssociatedType(Term lhs, Term rhs);
384-
385362
//////////////////////////////////////////////////////////////////////////////
386363
///
387364
/// Relations are "pseudo-rules" introduced by the property map

lib/Frontend/CompilerInvocation.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -914,12 +914,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
914914
A->getAsString(Args), A->getValue());
915915
}
916916

917-
if (auto A = Args.getLastArg(OPT_enable_requirement_machine_merged_associated_types,
918-
OPT_disable_requirement_machine_merged_associated_types)) {
919-
Opts.RequirementMachineMergedAssociatedTypes
920-
= A->getOption().matches(OPT_enable_requirement_machine_merged_associated_types);
921-
}
922-
923917
Opts.DumpRequirementMachine = Args.hasArg(
924918
OPT_dump_requirement_machine);
925919
Opts.AnalyzeRequirementMachine = Args.hasArg(

0 commit comments

Comments
 (0)