Skip to content

Commit 81ac321

Browse files
authored
Merge pull request swiftlang#40425 from slavapestov/rqm-concrete-conformance
RequirementMachine: Improved modeling of concrete conformances
2 parents f6bb2cb + 26293c4 commit 81ac321

17 files changed

+946
-411
lines changed

lib/AST/RequirementMachine/GeneratingConformances.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "llvm/Support/Debug.h"
6565
#include "llvm/Support/raw_ostream.h"
6666
#include <algorithm>
67+
#include "RewriteContext.h"
6768
#include "RewriteSystem.h"
6869

6970
using namespace swift;
@@ -134,11 +135,13 @@ void RewriteLoop::findProtocolConformanceRules(
134135
case RewriteStep::AdjustConcreteType:
135136
case RewriteStep::Shift:
136137
case RewriteStep::Decompose:
138+
case RewriteStep::ConcreteConformance:
139+
case RewriteStep::SuperclassConformance:
137140
break;
138141
}
139142
}
140143

141-
step.apply(evaluator, system);
144+
evaluator.apply(step, system);
142145
}
143146
}
144147

@@ -596,6 +599,7 @@ static const ProtocolDecl *getParentConformanceForTerm(Term lhs) {
596599
case Symbol::Kind::Layout:
597600
case Symbol::Kind::Superclass:
598601
case Symbol::Kind::ConcreteType:
602+
case Symbol::Kind::ConcreteConformance:
599603
break;
600604
}
601605

@@ -678,8 +682,15 @@ void RewriteSystem::computeGeneratingConformances(
678682
}
679683
}
680684

685+
Context.ConformanceRulesHistogram.add(conformanceRules.size());
686+
681687
computeCandidateConformancePaths(conformancePaths);
682688

689+
for (const auto &pair : conformancePaths) {
690+
if (pair.second.size() > 1)
691+
Context.GeneratingConformancesHistogram.add(pair.second.size());
692+
}
693+
683694
if (Debug.contains(DebugFlags::GeneratingConformances)) {
684695
llvm::dbgs() << "Initial set of equations:\n";
685696
for (const auto &pair : conformancePaths) {

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ RequirementMachine::getLongestValidPrefix(const MutableTerm &term) const {
240240
case Symbol::Kind::Layout:
241241
case Symbol::Kind::Superclass:
242242
case Symbol::Kind::ConcreteType:
243+
case Symbol::Kind::ConcreteConformance:
243244
llvm_unreachable("Property symbol cannot appear in a type term");
244245
}
245246

@@ -663,3 +664,85 @@ RequirementMachine::lookupNestedType(Type depType, Identifier name) const {
663664

664665
return nullptr;
665666
}
667+
668+
void RequirementMachine::verify(const MutableTerm &term) const {
669+
#ifndef NDEBUG
670+
// If the term is in the generic parameter domain, ensure we have a valid
671+
// generic parameter.
672+
if (term.begin()->getKind() == Symbol::Kind::GenericParam) {
673+
auto *genericParam = term.begin()->getGenericParam();
674+
TypeArrayView<GenericTypeParamType> genericParams = getGenericParams();
675+
auto found = std::find(genericParams.begin(),
676+
genericParams.end(),
677+
genericParam);
678+
if (found == genericParams.end()) {
679+
llvm::errs() << "Bad generic parameter in " << term << "\n";
680+
dump(llvm::errs());
681+
abort();
682+
}
683+
}
684+
685+
MutableTerm erased;
686+
687+
// First, "erase" resolved associated types from the term, and try
688+
// to simplify it again.
689+
for (auto symbol : term) {
690+
if (erased.empty()) {
691+
switch (symbol.getKind()) {
692+
case Symbol::Kind::Protocol:
693+
case Symbol::Kind::GenericParam:
694+
erased.add(symbol);
695+
continue;
696+
697+
case Symbol::Kind::AssociatedType:
698+
erased.add(Symbol::forProtocol(symbol.getProtocols()[0], Context));
699+
break;
700+
701+
case Symbol::Kind::Name:
702+
case Symbol::Kind::Layout:
703+
case Symbol::Kind::Superclass:
704+
case Symbol::Kind::ConcreteType:
705+
case Symbol::Kind::ConcreteConformance:
706+
llvm::errs() << "Bad initial symbol in " << term << "\n";
707+
abort();
708+
break;
709+
}
710+
}
711+
712+
switch (symbol.getKind()) {
713+
case Symbol::Kind::Name:
714+
assert(!erased.empty());
715+
erased.add(symbol);
716+
break;
717+
718+
case Symbol::Kind::AssociatedType:
719+
erased.add(Symbol::forName(symbol.getName(), Context));
720+
break;
721+
722+
case Symbol::Kind::Protocol:
723+
case Symbol::Kind::GenericParam:
724+
case Symbol::Kind::Layout:
725+
case Symbol::Kind::Superclass:
726+
case Symbol::Kind::ConcreteType:
727+
case Symbol::Kind::ConcreteConformance:
728+
llvm::errs() << "Bad interior symbol " << symbol << " in " << term << "\n";
729+
abort();
730+
break;
731+
}
732+
}
733+
734+
MutableTerm simplified = erased;
735+
System.simplify(simplified);
736+
737+
// We should end up with the same term.
738+
if (simplified != term) {
739+
llvm::errs() << "Term verification failed\n";
740+
llvm::errs() << "Initial term: " << term << "\n";
741+
llvm::errs() << "Erased term: " << erased << "\n";
742+
llvm::errs() << "Simplified term: " << simplified << "\n";
743+
llvm::errs() << "\n";
744+
dump(llvm::errs());
745+
abort();
746+
}
747+
#endif
748+
}

lib/AST/RequirementMachine/Histogram.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Histogram {
3535
unsigned Start;
3636
std::vector<unsigned> Buckets;
3737
unsigned OverflowBucket;
38+
unsigned MaxValue = 0;
3839

3940
const unsigned MaxWidth = 40;
4041

@@ -73,6 +74,9 @@ class Histogram {
7374
++OverflowBucket;
7475
else
7576
++Buckets[value];
77+
78+
if (value > MaxValue)
79+
MaxValue = value;
7680
}
7781

7882
/// Print a nice-looking graphical representation of the histogram.
@@ -140,6 +144,8 @@ class Histogram {
140144

141145
out << std::string(maxLabelWidth, ' ') << " | ";
142146
out << "Total: " << sumValues << "\n";
147+
out << std::string(maxLabelWidth, ' ') << " | ";
148+
out << "Max: " << MaxValue << "\n";
143149
}
144150
};
145151

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,12 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
9696
case RewriteStep::AdjustConcreteType:
9797
case RewriteStep::Shift:
9898
case RewriteStep::Decompose:
99+
case RewriteStep::ConcreteConformance:
100+
case RewriteStep::SuperclassConformance:
99101
break;
100102
}
101103

102-
step.apply(evaluator, system);
104+
evaluator.apply(step, system);
103105
}
104106

105107
// Collect all rules that we saw exactly once in empty context.
@@ -210,6 +212,8 @@ RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const {
210212
case RewriteStep::AdjustConcreteType:
211213
case RewriteStep::Shift:
212214
case RewriteStep::Decompose:
215+
case RewriteStep::ConcreteConformance:
216+
case RewriteStep::SuperclassConformance:
213217
break;
214218
}
215219

@@ -306,6 +310,8 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
306310
case RewriteStep::AdjustConcreteType:
307311
case RewriteStep::Shift:
308312
case RewriteStep::Decompose:
313+
case RewriteStep::ConcreteConformance:
314+
case RewriteStep::SuperclassConformance:
309315
newSteps.push_back(step);
310316
break;
311317
}
@@ -339,6 +345,10 @@ bool RewriteStep::isInverseOf(const RewriteStep &other) const {
339345

340346
case RewriteStep::Decompose:
341347
return RuleID == other.RuleID;
348+
349+
case RewriteStep::ConcreteConformance:
350+
case RewriteStep::SuperclassConformance:
351+
return true;
342352
}
343353

344354
assert(EndOffset == other.EndOffset && "Bad whiskering?");
@@ -465,7 +475,7 @@ bool RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
465475
break;
466476

467477
// Update the basepoint by applying the first step in the path.
468-
left.apply(evaluator, system);
478+
evaluator.apply(left, system);
469479

470480
++count;
471481
}
@@ -522,14 +532,16 @@ bool RewriteLoop::isInContext(const RewriteSystem &system) const {
522532
case RewriteStep::AdjustConcreteType:
523533
case RewriteStep::Shift:
524534
case RewriteStep::Decompose:
535+
case RewriteStep::ConcreteConformance:
536+
case RewriteStep::SuperclassConformance:
525537
break;
526538
}
527539

528540
if (minStartOffset == 0 && minEndOffset == 0)
529541
break;
530542
}
531543

532-
step.apply(evaluator, system);
544+
evaluator.apply(step, system);
533545
}
534546

535547
return (minStartOffset > 0 || minEndOffset > 0);
@@ -844,7 +856,7 @@ void RewriteSystem::verifyRewriteLoops() const {
844856
RewritePathEvaluator evaluator(loop.Basepoint);
845857

846858
for (const auto &step : loop.Path) {
847-
step.apply(evaluator, *this);
859+
evaluator.apply(step, *this);
848860
}
849861

850862
if (evaluator.getCurrentTerm() != loop.Basepoint) {

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
451451
// perform the concrete type adjustment:
452452
//
453453
// (σ - T)
454-
if (xv.back().isSuperclassOrConcreteType() &&
454+
if (xv.back().hasSubstitutions() &&
455455
!xv.back().getSubstitutions().empty() &&
456456
t.size() > 0) {
457457
path.add(RewriteStep::forAdjustment(t.size(), /*inverse=*/true));

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ void PropertyBag::copyPropertiesFrom(const PropertyBag *next,
190190
// Conformances and the layout constraint, if any, can be copied over
191191
// unmodified.
192192
ConformsTo = next->ConformsTo;
193+
ConformsToRules = next->ConformsToRules;
193194
Layout = next->Layout;
195+
LayoutRule = next->LayoutRule;
194196

195197
// If the property bag of V has superclass or concrete type
196198
// substitutions {X1, ..., Xn}, then the property bag of
@@ -200,14 +202,33 @@ void PropertyBag::copyPropertiesFrom(const PropertyBag *next,
200202
if (next->Superclass) {
201203
Superclass = next->Superclass->prependPrefixToConcreteSubstitutions(
202204
prefix, ctx);
205+
SuperclassRule = next->SuperclassRule;
203206
}
204207

205208
if (next->ConcreteType) {
206209
ConcreteType = next->ConcreteType->prependPrefixToConcreteSubstitutions(
207210
prefix, ctx);
211+
ConcreteTypeRule = next->ConcreteTypeRule;
208212
}
209213
}
210214

215+
void PropertyBag::verify(const RewriteSystem &system) const {
216+
#ifndef NDEBUG
217+
assert(ConformsTo.size() == ConformsToRules.size());
218+
for (unsigned i : indices(ConformsTo)) {
219+
auto symbol = system.getRule(ConformsToRules[i]).getLHS().back();
220+
assert(symbol.getKind() == Symbol::Kind::Protocol);
221+
assert(symbol.getProtocol() == ConformsTo[i]);
222+
}
223+
224+
// FIXME: Once unification introduces new rules, add asserts requiring
225+
// that the layout, superclass and concrete type symbols match, as above
226+
assert(!Layout.isNull() == LayoutRule.hasValue());
227+
assert(Superclass.hasValue() == SuperclassRule.hasValue());
228+
assert(ConcreteType.hasValue() == ConcreteTypeRule.hasValue());
229+
#endif
230+
}
231+
211232
PropertyMap::~PropertyMap() {
212233
Trie.updateHistograms(Context.PropertyTrieHistogram,
213234
Context.PropertyTrieRootHistogram);
@@ -289,11 +310,12 @@ void PropertyMap::clear() {
289310
/// Record a protocol conformance, layout or superclass constraint on the given
290311
/// key. Must be called in monotonically non-decreasing key order.
291312
void PropertyMap::addProperty(
292-
Term key, Symbol property,
293-
SmallVectorImpl<std::pair<MutableTerm, MutableTerm>> &inducedRules) {
313+
Term key, Symbol property, unsigned ruleID,
314+
SmallVectorImpl<InducedRule> &inducedRules) {
294315
assert(property.isProperty());
316+
assert(*System.getRule(ruleID).isPropertyRule() == property);
295317
auto *props = getOrCreateProperties(key);
296-
props->addProperty(property, Context,
318+
props->addProperty(property, ruleID, Context,
297319
inducedRules, Debug.contains(DebugFlags::ConcreteUnification));
298320
}
299321

@@ -314,11 +336,17 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
314336
unsigned maxDepth) {
315337
clear();
316338

339+
struct Property {
340+
Term key;
341+
Symbol symbol;
342+
unsigned ruleID;
343+
};
344+
317345
// PropertyMap::addRule() requires that shorter rules are added
318346
// before longer rules, so that it can perform lookups on suffixes and call
319347
// PropertyBag::copyPropertiesFrom(). However, we don't have to perform a
320348
// full sort by term order here; a bucket sort by term length suffices.
321-
SmallVector<std::vector<std::pair<Term, Symbol>>, 4> properties;
349+
SmallVector<std::vector<Property>, 4> properties;
322350

323351
for (const auto &rule : System.getRules()) {
324352
if (rule.isSimplified())
@@ -336,16 +364,18 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
336364
unsigned length = rhs.size();
337365
if (length >= properties.size())
338366
properties.resize(length + 1);
339-
properties[length].emplace_back(rhs, *property);
367+
368+
unsigned ruleID = System.getRuleID(rule);
369+
properties[length].push_back({rhs, *property, ruleID});
340370
}
341371

342372
// Merging multiple superclass or concrete type rules can induce new rules
343373
// to unify concrete type constructor arguments.
344-
SmallVector<std::pair<MutableTerm, MutableTerm>, 3> inducedRules;
374+
SmallVector<InducedRule, 3> inducedRules;
345375

346376
for (const auto &bucket : properties) {
347-
for (auto pair : bucket) {
348-
addProperty(pair.first, pair.second, inducedRules);
377+
for (auto property : bucket) {
378+
addProperty(property.key, property.symbol, property.ruleID, inducedRules);
349379
}
350380
}
351381

@@ -358,11 +388,17 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
358388
// the concrete type witnesses in the concrete type's conformance.
359389
concretizeNestedTypesFromConcreteParents(inducedRules);
360390

391+
// Finally, introduce concrete conformance rules, relating conformance rules
392+
// to concrete type and superclass rules.
393+
recordConcreteConformanceRules(inducedRules);
394+
361395
// Some of the induced rules might be trivial; only count the induced rules
362396
// where the left hand side is not already equivalent to the right hand side.
363397
unsigned addedNewRules = 0;
364398
for (auto pair : inducedRules) {
365-
if (System.addRule(pair.first, pair.second)) {
399+
// FIXME: Eventually, all induced rules will have a rewrite path.
400+
if (System.addRule(pair.LHS, pair.RHS,
401+
pair.Path.empty() ? nullptr : &pair.Path)) {
366402
++addedNewRules;
367403

368404
const auto &newRule = System.getRules().back();
@@ -371,6 +407,9 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
371407
}
372408
}
373409

410+
// Check invariants of the constructed property map.
411+
verify();
412+
374413
if (System.getRules().size() > maxIterations)
375414
return std::make_pair(CompletionResult::MaxIterations, addedNewRules);
376415

@@ -385,4 +424,11 @@ void PropertyMap::dump(llvm::raw_ostream &out) const {
385424
out << "\n";
386425
}
387426
out << "}\n";
427+
}
428+
429+
void PropertyMap::verify() const {
430+
#ifndef NDEBUG
431+
for (const auto &props : Entries)
432+
props->verify(System);
433+
#endif
388434
}

0 commit comments

Comments
 (0)