Skip to content

Commit 7ae3de2

Browse files
committed
[Constraint graph] Associate all constraints with components.
The connected components computation was not forming components when all of the type variables in a component were already bound. Any remaining constraints involving those type variables would then arbitrarily end up in component 0, due to the the default-construction behavior of a map’s operator[] with missing keys, artificially increasing the size of that component with (typically) additional disjunctions. Ensure that all constraints get into a component, creating one to hold the a constraint even when all of the type variables are already bound. Then, assert the invariant that every constraint is associated with a component. In time, we should probably eliminate this notion of disjunctions that remain even when the type variable has been bound. For now, strengthen the invariant to at least ensure that they get solved in isolation. (cherry picked from commit 805b02d)
1 parent eaafa04 commit 7ae3de2

File tree

2 files changed

+52
-49
lines changed

2 files changed

+52
-49
lines changed

lib/Sema/CSStep.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ void SplitterStep::computeFollowupSteps(
172172
while (!workList.empty()) {
173173
auto *constraint = &workList.front();
174174
workList.pop_front();
175+
assert(constraintComponent.count(constraint) > 0 && "Missed a constraint");
175176
componentSteps[constraintComponent[constraint]]->record(constraint);
176177
}
177178

lib/Sema/ConstraintGraph.cpp

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -488,101 +488,103 @@ static void depthFirstSearch(
488488
visitAdjacencies(node.getFixedBindings());
489489
}
490490

491-
/// Perform a depth-first search.
492-
///
493-
/// \param cg The constraint graph.
494-
/// \param typeVar The type variable we're searching from.
495-
/// \param preVisitNode Called before traversing a node. Must return \c
496-
/// false when the node has already been visited.
497-
/// \param visitConstraint Called before considering a constraint. If it
498-
/// returns \c false, that constraint will be skipped.
499-
static void depthFirstSearch(
500-
ConstraintGraph &cg,
491+
/// Find the representative for the given type variable within the set
492+
/// of representatives in a union-find data structure.
493+
static TypeVariableType *findRepresentative(
501494
TypeVariableType *typeVar,
502-
llvm::function_ref<bool(TypeVariableType *)> preVisitNode,
503-
llvm::function_ref<bool(Constraint *)> visitConstraint) {
504-
llvm::DenseSet<Constraint *> visitedConstraints;
505-
depthFirstSearch(cg, typeVar, preVisitNode, visitConstraint,
506-
visitedConstraints);
495+
llvm::SmallDenseMap<TypeVariableType *, TypeVariableType *> &representatives) {
496+
// If we don't have a record of this type variable, it is it's own
497+
// representative.
498+
auto known = representatives.find(typeVar);
499+
if (known == representatives.end() || known->second == typeVar)
500+
return typeVar;
501+
502+
// Find the representative of the parent.
503+
auto parent = known->second;
504+
auto rep = findRepresentative(parent, representatives);
505+
representatives[typeVar] = rep;
506+
507+
return rep;
507508
}
508509

509510
unsigned ConstraintGraph::computeConnectedComponents(
510511
std::vector<TypeVariableType *> &typeVars,
511512
std::vector<unsigned> &components) {
512-
llvm::SmallDenseMap<TypeVariableType *, unsigned> componentsMap;
513+
llvm::SmallDenseMap<TypeVariableType *, TypeVariableType *> representatives;
513514

514515
// Perform a depth-first search from each type variable to identify
515516
// what component it is in.
516-
unsigned numComponents = 0;
517+
llvm::DenseSet<Constraint *> visitedConstraints;
517518
for (auto typeVar : typeVars) {
518-
// If we've already assigned a component to this type variable, we're done.
519-
if (componentsMap.count(typeVar) > 0)
519+
// If we've already assigned a representative to this type variable,
520+
// we're done.
521+
if (representatives.count(typeVar) > 0)
520522
continue;
521523

522-
// Record this component.
523-
unsigned component = numComponents++;
524-
525524
// Perform a depth-first search to mark those type variables that are
526525
// in the same component as this type variable.
527526
depthFirstSearch(
528527
*this, typeVar,
529-
[&](TypeVariableType *typeVar) {
528+
[&](TypeVariableType *found) {
530529
// If we have already seen this node, we're done.
531-
if (componentsMap.count(typeVar) > 0) {
532-
assert(componentsMap[typeVar] == component && "Wrong component?");
533-
return false;
534-
}
530+
auto inserted = representatives.insert({found, typeVar});
531+
assert((inserted.second || inserted.first->second == typeVar) &&
532+
"Wrong component?");
535533

536-
componentsMap[typeVar] = component;
537-
return true;
534+
return inserted.second;
538535
},
539536
[&](Constraint *constraint) {
540537
return true;
541-
});
538+
},
539+
visitedConstraints);
542540
}
543541

544-
// Figure out which components have unbound type variables; these
545-
// are the only components and type variables we want to report.
546-
SmallVector<bool, 4> componentHasUnboundTypeVar(numComponents, false);
542+
// Figure out which components have unbound type variables and/or constraints.
543+
// These are the only components we want to report.
544+
llvm::SmallDenseSet<TypeVariableType *> validComponents;
547545
for (auto typeVar : typeVars) {
548546
// If this type variable has a fixed type, skip it.
549547
if (CS.getFixedType(typeVar))
550548
continue;
551549

552-
assert(componentsMap.count(typeVar) > 0);
553-
componentHasUnboundTypeVar[componentsMap[typeVar]] = true;
550+
auto rep = findRepresentative(typeVar, representatives);
551+
validComponents.insert(rep);
554552
}
555553

556-
// Renumber the old components to the new components.
557-
SmallVector<unsigned, 4> componentRenumbering(numComponents, 0);
558-
numComponents = 0;
559-
for (unsigned i : indices(componentRenumbering)) {
560-
// Skip components that have no unbound type variables.
561-
if (!componentHasUnboundTypeVar[i])
562-
continue;
563-
564-
componentRenumbering[i] = numComponents++;
554+
for (auto constraint : visitedConstraints) {
555+
for (auto typeVar : constraint->getTypeVariables()) {
556+
auto rep = findRepresentative(typeVar, representatives);
557+
validComponents.insert(rep);
558+
}
565559
}
566560

567561
// Remove type variables in dead components and provide component
568562
// numbers for those that remain.
563+
llvm::SmallDenseMap<TypeVariableType *, unsigned> componentNumbers;
564+
auto getComponentNumber = [&](TypeVariableType *typeVar) {
565+
assert(typeVar == findRepresentative(typeVar, representatives));
566+
auto inserted = componentNumbers.insert({typeVar, componentNumbers.size()});
567+
return inserted.first->second;
568+
};
569+
569570
typeVars.erase(
570571
std::remove_if(
571572
typeVars.begin(), typeVars.end(),
572573
[&](TypeVariableType *typeVar) {
573-
assert(componentsMap.count(typeVar) > 0);
574-
unsigned component = componentsMap[typeVar];
574+
auto rep = findRepresentative(typeVar, representatives);
575+
575576
// Remove type variables in dead components.
576-
if (!componentHasUnboundTypeVar[component])
577+
if (validComponents.count(rep) == 0)
577578
return true;
578579

579580
// Record the (renumbered) component.
580-
components.push_back(componentRenumbering[component]);
581+
components.push_back(getComponentNumber(rep));
581582
return false;
582583
}),
583584
typeVars.end());
584585

585-
return numComponents + getOrphanedConstraints().size();
586+
assert(typeVars.size() == components.size());
587+
return componentNumbers.size() + getOrphanedConstraints().size();
586588
}
587589

588590

0 commit comments

Comments
 (0)