mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Revert "Sema: Remove one-way constraint handling from computeConnectedComponents()"
This reverts commit dd3e49c3ac.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user