Revert "Sema: Remove one-way constraint handling from computeConnectedComponents()"

This reverts commit dd3e49c3ac.
This commit is contained in:
Slava Pestov
2025-02-01 23:55:16 -05:00
parent 8af183cca8
commit fa73be2415
3 changed files with 376 additions and 8 deletions

View File

@@ -621,6 +621,26 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
TypeVariableType *typeVar, GatheringKind kind,
llvm::function_ref<bool(Constraint *)> acceptConstraintFn) {
llvm::TinyPtrVector<Constraint *> constraints;
// Whether we should consider this constraint at all.
auto shouldConsiderConstraint = [&](Constraint *constraint) {
// For a one-way constraint, only consider it when the left-hand side of
// the binding is one of the type variables currently under consideration,
// as only such constraints need solving for this component. Note that we
// don't perform any other filtering, as the constraint system should be
// responsible for checking any other conditions.
if (constraint->isOneWayConstraint()) {
auto lhsTypeVar = constraint->getFirstType()->castTo<TypeVariableType>();
return CS.isActiveTypeVariable(lhsTypeVar);
}
return true;
};
auto acceptConstraint = [&](Constraint *constraint) {
return shouldConsiderConstraint(constraint) &&
acceptConstraintFn(constraint);
};
llvm::SmallPtrSet<TypeVariableType *, 4> typeVars;
llvm::SmallPtrSet<Constraint *, 8> visitedConstraints;
@@ -633,7 +653,7 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
return typeVars.insert(typeVar).second;
},
[&](Constraint *constraint) {
if (acceptConstraintFn(constraint))
if (acceptConstraint(constraint))
constraints.push_back(constraint);
// Don't recurse into the constraint's type variables.
@@ -653,7 +673,7 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
for (auto constraint : (*this)[adjTypeVar].getConstraints()) {
if (visitedConstraints.insert(constraint).second &&
acceptConstraintFn(constraint))
acceptConstraint(constraint))
constraints.push_back(constraint);
}
};
@@ -668,7 +688,7 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
for (auto constraint : node.getConstraints()) {
if (visitedConstraints.insert(constraint).second &&
acceptConstraintFn(constraint))
acceptConstraint(constraint))
constraints.push_back(constraint);
}
@@ -695,6 +715,23 @@ namespace {
/// we merge equivalence classes.
unsigned validComponentCount = 0;
/// Describes the one-way incoming and outcoming adjacencies of
/// a component within the directed graph of one-way constraints.
struct OneWayComponent {
/// The (uniqued) set of type variable representatives to which this
/// component has an outgoing edge.
TinyPtrVector<TypeVariableType *> outAdjacencies;
/// The (uniqued) set of type variable representatives from which this
/// component has an incoming edge.
TinyPtrVector<TypeVariableType *> inAdjacencies;
};
// Adjacency list representation of the directed graph of edges for
// one-way constraints, using type variable representatives as the
// nodes.
llvm::SmallDenseMap<TypeVariableType *, OneWayComponent> oneWayDigraph;
public:
using Component = ConstraintGraph::Component;
@@ -704,7 +741,14 @@ namespace {
ArrayRef<TypeVariableType *> typeVars)
: cg(cg), typeVars(typeVars)
{
connectedComponents();
auto oneWayConstraints = connectedComponents();
// If there were no one-way constraints, we're done.
if (oneWayConstraints.empty())
return;
// Build the directed one-way constraint graph.
buildOneWayConstraintGraph(oneWayConstraints);
}
/// Retrieve the set of components.
@@ -755,11 +799,51 @@ namespace {
if (constraintTypeVars.empty())
continue;
TypeVariableType *typeVar = constraintTypeVars.front();
TypeVariableType *typeVar;
if (constraint.isOneWayConstraint()) {
// For one-way constraints, associate the constraint with the
// left-hand type variable.
typeVar = constraint.getFirstType()->castTo<TypeVariableType>();
} else {
typeVar = constraintTypeVars.front();
}
auto rep = typeVar->getImpl().getComponent();
getComponent(rep).addConstraint(&constraint);
}
// If we have any one-way constraint information, compute the ordering
// of representative type variables needed to respect one-way
// constraints while solving.
if (!oneWayDigraph.empty()) {
// Sort the representative type variables based on the disjunction
// count, so
std::sort(representativeTypeVars.begin(), representativeTypeVars.end(),
[&](TypeVariableType *lhs, TypeVariableType *rhs) {
return getComponent(lhs).getNumDisjunctions() >
getComponent(rhs).getNumDisjunctions();
});
representativeTypeVars =
computeOneWayComponentOrdering(representativeTypeVars);
// Fill in one-way dependency information for all of the components.
for (auto typeVar : representativeTypeVars) {
auto knownOneWayComponent = oneWayDigraph.find(typeVar);
if (knownOneWayComponent == oneWayDigraph.end())
continue;
auto &oneWayComponent = knownOneWayComponent->second;
auto &component = getComponent(typeVar);
for (auto inAdj : oneWayComponent.inAdjacencies) {
if (!inAdj->getImpl().isValidComponent())
continue;
component.recordDependency(getComponent(inAdj));
}
}
}
// Flatten the set of components.
flatComponents.reserve(
representativeTypeVars.size() + cg.getOrphanedConstraints().size());
@@ -782,7 +866,10 @@ namespace {
// sort the orphaned constraints at the back. In the absence of
// one-way constraints, sort everything.
if (components.size() > 1) {
std::sort(flatComponents.begin(), flatComponents.end(),
auto sortStart = oneWayDigraph.empty()
? flatComponents.begin()
: flatComponents.end() - cg.getOrphanedConstraints().size();
std::sort(sortStart, flatComponents.end(),
[&](const Component &lhs, const Component &rhs) {
return lhs.getNumDisjunctions() > rhs.getNumDisjunctions();
});
@@ -822,8 +909,13 @@ namespace {
return true;
}
/// Compute the connected components of the graph.
void connectedComponents() {
/// Perform the connected components algorithm, skipping one-way
/// constraints.
///
/// \returns the set of one-way constraints that were skipped.
TinyPtrVector<Constraint *> connectedComponents() {
TinyPtrVector<Constraint *> oneWayConstraints;
auto &cs = cg.getConstraintSystem();
for (auto typeVar : typeVars) {
@@ -850,6 +942,15 @@ namespace {
}
for (auto &constraint : cs.getConstraints()) {
if (constraint.isOneWayConstraint()) {
oneWayConstraints.push_back(&constraint);
auto *typeVar = constraint.getFirstType()->castTo<TypeVariableType>();
typeVar = typeVar->getImpl().getComponent();
if (typeVar->getImpl().markValidComponent())
++validComponentCount;
continue;
}
auto typeVars = constraint.getTypeVariables();
if (typeVars.empty())
continue;
@@ -861,6 +962,258 @@ namespace {
for (auto *otherTypeVar : typeVars.slice(1))
unionSets(firstTypeVar, otherTypeVar);
}
return oneWayConstraints;
}
/// Insert the given type variable into the given vector if it isn't
/// already present.
static void insertIfUnique(TinyPtrVector<TypeVariableType *> &vector,
TypeVariableType *typeVar) {
if (std::find(vector.begin(), vector.end(), typeVar) == vector.end())
vector.push_back(typeVar);
}
/// Retrieve the (uniqued) set of type variable representations that occur
/// within the given type.
TinyPtrVector<TypeVariableType *>
getRepresentativesInType(Type type) const {
TinyPtrVector<TypeVariableType *> results;
SmallPtrSet<TypeVariableType *, 2> typeVars;
type->getTypeVariables(typeVars);
for (auto typeVar : typeVars) {
auto rep = typeVar->getImpl().getComponent();
insertIfUnique(results, rep);
}
return results;
}
/// Add all of the one-way constraints to the one-way digraph
void addOneWayConstraintEdges(ArrayRef<Constraint *> oneWayConstraints) {
for (auto constraint : oneWayConstraints) {
auto lhsTypeReps =
getRepresentativesInType(constraint->getFirstType());
auto rhsTypeReps =
getRepresentativesInType(constraint->getSecondType());
// Add an edge from the type representatives on the right-hand side
// of the one-way constraint to the type representatives on the
// left-hand side, because the right-hand type variables need to
// be solved before the left-hand type variables.
for (auto lhsTypeRep : lhsTypeReps) {
for (auto rhsTypeRep : rhsTypeReps) {
if (lhsTypeRep == rhsTypeRep)
continue;
insertIfUnique(oneWayDigraph[rhsTypeRep].outAdjacencies,lhsTypeRep);
insertIfUnique(oneWayDigraph[lhsTypeRep].inAdjacencies,rhsTypeRep);
}
}
}
}
using TypeVariablePair = std::pair<TypeVariableType *, TypeVariableType *>;
/// Build the directed graph of one-way constraints among components.
void buildOneWayConstraintGraph(ArrayRef<Constraint *> oneWayConstraints) {
auto &cs = cg.getConstraintSystem();
auto &ctx = cs.getASTContext();
bool contractedCycle = false;
do {
// Construct the one-way digraph from scratch.
oneWayDigraph.clear();
addOneWayConstraintEdges(oneWayConstraints);
// Minimize the in-adjacencies, detecting cycles along the way.
SmallVector<TypeVariablePair, 4> cycleEdges;
removeIndirectOneWayInAdjacencies(cycleEdges);
// For any contractions we need to perform due to cycles, perform a
// union the connected components based on the type variable pairs.
contractedCycle = false;
for (const auto &edge : cycleEdges) {
if (unionSets(edge.first, edge.second)) {
if (cs.isDebugMode()) {
auto &log = llvm::errs();
if (cs.solverState)
log.indent(cs.solverState->getCurrentIndent());
log << "Collapsing one-way components for $T"
<< edge.first->getID() << " and $T" << edge.second->getID()
<< " due to cycle.\n";
}
if (ctx.Stats) {
++ctx.Stats->getFrontendCounters()
.NumCyclicOneWayComponentsCollapsed;
}
contractedCycle = true;
}
}
} while (contractedCycle);
}
/// Perform a depth-first search to produce a from the given type variable,
/// notifying the function object.
///
/// \param getAdjacencies Called to retrieve the set of type variables
/// that are adjacent to the given type variable.
///
/// \param preVisit Called before visiting the adjacencies of the given
/// type variable. When it returns \c true, the adjacencies of this type
/// variable will be visited. When \c false, the adjacencies will not be
/// visited and \c postVisit will not be called.
///
/// \param postVisit Called after visiting the adjacencies of the given
/// type variable.
static void postorderDepthFirstSearchRec(
TypeVariableType *typeVar,
llvm::function_ref<
ArrayRef<TypeVariableType *>(TypeVariableType *)> getAdjacencies,
llvm::function_ref<bool(TypeVariableType *)> preVisit,
llvm::function_ref<void(TypeVariableType *)> postVisit) {
if (!preVisit(typeVar))
return;
for (auto adj : getAdjacencies(typeVar)) {
postorderDepthFirstSearchRec(adj, getAdjacencies, preVisit, postVisit);
}
postVisit(typeVar);
}
/// Minimize the incoming adjacencies for one of the nodes in the one-way
/// directed graph by eliminating any in-adjacencies that can also be
/// found indirectly.
void removeIndirectOneWayInAdjacencies(
TypeVariableType *typeVar,
OneWayComponent &component,
SmallVectorImpl<TypeVariablePair> &cycleEdges) {
// Perform a depth-first search from each of the in adjacencies to
// this type variable, traversing each of the one-way edges backwards
// to find all of the components whose type variables must be
// bound before this component can be solved.
SmallPtrSet<TypeVariableType *, 4> visited;
SmallPtrSet<TypeVariableType *, 4> indirectlyReachable;
SmallVector<TypeVariableType *, 4> currentPath;
for (auto inAdj : component.inAdjacencies) {
postorderDepthFirstSearchRec(
inAdj,
[&](TypeVariableType *typeVar) -> ArrayRef<TypeVariableType *> {
// Traverse the outgoing adjacencies for the subcomponent
auto oneWayComponent = oneWayDigraph.find(typeVar);
if (oneWayComponent == oneWayDigraph.end()) {
return { };
}
return oneWayComponent->second.inAdjacencies;
},
[&](TypeVariableType *typeVar) {
// If we haven't seen this type variable yet, add it to the
// path.
if (visited.insert(typeVar).second) {
currentPath.push_back(typeVar);
return true;
}
// Add edges between this type variable and every other type
// variable in the path.
for (auto otherTypeVar : llvm::reverse(currentPath)) {
// When we run into our own type variable, we're done.
if (otherTypeVar == typeVar)
break;
cycleEdges.push_back({typeVar, otherTypeVar});
}
return false;
},
[&](TypeVariableType *dependsOn) {
// Remove this type variable from the path.
assert(currentPath.back() == dependsOn);
currentPath.pop_back();
// Don't record dependency on ourselves.
if (dependsOn == inAdj)
return;
indirectlyReachable.insert(dependsOn);
});
// Remove any in-adjacency of this component that is indirectly
// reachable.
component.inAdjacencies.erase(
std::remove_if(component.inAdjacencies.begin(),
component.inAdjacencies.end(),
[&](TypeVariableType *inAdj) {
return indirectlyReachable.count(inAdj) > 0;
}),
component.inAdjacencies.end());
}
}
/// Minimize the incoming adjacencies for all of the nodes in the one-way
/// directed graph by eliminating any in-adjacencies that can also be
/// found indirectly.
void removeIndirectOneWayInAdjacencies(
SmallVectorImpl<TypeVariablePair> &cycleEdges) {
for (auto &oneWayEntry : oneWayDigraph) {
auto typeVar = oneWayEntry.first;
auto &component = oneWayEntry.second;
removeIndirectOneWayInAdjacencies(typeVar, component, cycleEdges);
}
}
/// Compute the order in which the components should be visited to respect
/// one-way constraints.
///
/// \param representativeTypeVars the set of type variables that
/// represent the components, in a preferred ordering that does not
/// account for one-way constraints.
/// \returns the set of type variables that represent the components, in
/// an ordering that ensures that components containing type variables
/// that occur on the left-hand side of a one-way constraint will be
/// solved after the components for type variables on the right-hand
/// side of that constraint.
SmallVector<TypeVariableType *, 4> computeOneWayComponentOrdering(
ArrayRef<TypeVariableType *> representativeTypeVars) const {
SmallVector<TypeVariableType *, 4> orderedReps;
orderedReps.reserve(representativeTypeVars.size());
SmallPtrSet<TypeVariableType *, 4> visited;
for (auto rep : llvm::reverse(representativeTypeVars)) {
// Perform a postorder depth-first search through the one-way digraph,
// starting at this representative, to establish the dependency
// ordering amongst components that are reachable
// to establish the dependency ordering for the representative type
// variables.
postorderDepthFirstSearchRec(
rep,
[&](TypeVariableType *typeVar) -> ArrayRef<TypeVariableType *> {
// Traverse the outgoing adjacencies for the subcomponent
assert(typeVar == typeVar->getImpl().getComponent());
auto oneWayComponent = oneWayDigraph.find(typeVar);
if (oneWayComponent == oneWayDigraph.end()) {
return { };
}
return oneWayComponent->second.outAdjacencies;
},
[&](TypeVariableType *typeVar) {
return visited.insert(typeVar).second;
},
[&](TypeVariableType *typeVar) {
// Record this type variable, if it's one of the representative
// type variables.
if (typeVar->getImpl().isValidComponent())
orderedReps.push_back(typeVar);
});
}
assert(orderedReps.size() == representativeTypeVars.size());
return orderedReps;
}
};
}