Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
291 changes: 242 additions & 49 deletions lib/Sema/ConstraintGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,74 @@ void ConstraintGraphNode::removeConstraint(Constraint *constraint) {
Constraints.pop_back();
}

ConstraintGraphNode::Adjacency &
ConstraintGraphNode::getAdjacency(TypeVariableType *typeVar) {
assert(typeVar != TypeVar && "Cannot be adjacent to oneself");

// Look for existing adjacency information.
auto pos = AdjacencyInfo.find(typeVar);

if (pos != AdjacencyInfo.end())
return pos->second;

// If we weren't already adjacent to this type variable, add it to the
// list of adjacencies.
pos = AdjacencyInfo.insert(
{ typeVar, { static_cast<unsigned>(Adjacencies.size()), 0 } })
.first;
Adjacencies.push_back(typeVar);
return pos->second;
}

void ConstraintGraphNode::modifyAdjacency(
TypeVariableType *typeVar,
llvm::function_ref<void(Adjacency& adj)> modify) {
// Find the adjacency information.
auto pos = AdjacencyInfo.find(typeVar);
assert(pos != AdjacencyInfo.end() && "Type variables not adjacent");
assert(Adjacencies[pos->second.Index] == typeVar && "Mismatched adjacency");

// Perform the modification .
modify(pos->second);

// If the adjacency is not empty, leave the information in there.
if (!pos->second.empty())
return;

// Remove this adjacency from the mapping.
unsigned index = pos->second.Index;
AdjacencyInfo.erase(pos);

// If this adjacency is last in the vector, just pop it off.
unsigned lastIndex = Adjacencies.size()-1;
if (index == lastIndex) {
Adjacencies.pop_back();
return;
}

// This adjacency is somewhere in the middle; swap it with the last
// adjacency so we can remove the adjacency from the vector in O(1) time
// rather than O(n) time.
auto lastTypeVar = Adjacencies[lastIndex];
Adjacencies[index] = lastTypeVar;
AdjacencyInfo[lastTypeVar].Index = index;
Adjacencies.pop_back();
}

void ConstraintGraphNode::addAdjacency(TypeVariableType *typeVar) {
auto &adjacency = getAdjacency(typeVar);

// Bump the degree of the adjacency.
++adjacency.NumConstraints;
}

void ConstraintGraphNode::removeAdjacency(TypeVariableType *typeVar) {
modifyAdjacency(typeVar, [](Adjacency &adj) {
assert(adj.NumConstraints > 0 && "No adjacency to remove?");
--adj.NumConstraints;
});
}

void ConstraintGraphNode::addToEquivalenceClass(
ArrayRef<TypeVariableType *> typeVars) {
assert(TypeVar == TypeVar->getImpl().getRepresentative(nullptr) &&
Expand Down Expand Up @@ -258,52 +326,80 @@ void ConstraintGraph::removeNode(TypeVariableType *typeVar) {
TypeVariables.pop_back();
}

void ConstraintGraph::addConstraint(Constraint *constraint) {
// For the nodes corresponding to each type variable...
/// Enumerate the adjacency edges for the given constraint.
static void enumerateAdjacencies(
Constraint *constraint,
llvm::function_ref<void(TypeVariableType *, TypeVariableType *)> visitor) {
// Don't record adjacencies for one-way constraints.
if (constraint->isOneWayConstraint())
return;

// O(N^2) update for all of the adjacent type variables.
auto referencedTypeVars = constraint->getTypeVariables();
for (auto typeVar : referencedTypeVars) {
// Find the node for this type variable.
auto &node = (*this)[typeVar];
for (auto otherTypeVar : referencedTypeVars) {
if (typeVar == otherTypeVar)
continue;

visitor(typeVar, otherTypeVar);
}
}
}

// Note the constraint within the node for that type variable.
node.addConstraint(constraint);
void ConstraintGraph::addConstraint(Constraint *constraint) {
// Record the change, if there are active scopes.
if (ActiveScope) {
Changes.push_back(Change::addedConstraint(constraint));
}

// If the constraint doesn't reference any type variables, it's orphaned;
// track it as such.
if (referencedTypeVars.empty()) {
if (constraint->getTypeVariables().empty()) {
// A constraint that doesn't reference any type variables is orphaned;
// track it as such.
OrphanedConstraints.push_back(constraint);
return;
}

// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::addedConstraint(constraint));
// Record this constraint in each type variable.
for (auto typeVar : constraint->getTypeVariables()) {
(*this)[typeVar].addConstraint(constraint);
}

// Record adjacencies.
enumerateAdjacencies(constraint,
[&](TypeVariableType *lhs, TypeVariableType *rhs) {
assert(lhs != rhs);
(*this)[lhs].addAdjacency(rhs);
});
}

void ConstraintGraph::removeConstraint(Constraint *constraint) {
// For the nodes corresponding to each type variable...
auto referencedTypeVars = constraint->getTypeVariables();
for (auto typeVar : referencedTypeVars) {
// Find the node for this type variable.
auto &node = (*this)[typeVar];

// Remove the constraint.
node.removeConstraint(constraint);
}
// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::removedConstraint(constraint));

// If this is an orphaned constraint, remove it from the list.
if (referencedTypeVars.empty()) {
if (constraint->getTypeVariables().empty()) {
// A constraint that doesn't reference any type variables is orphaned;
// remove it from the list of orphaned constraints.
auto known = std::find(OrphanedConstraints.begin(),
OrphanedConstraints.end(),
constraint);
assert(known != OrphanedConstraints.end() && "missing orphaned constraint");
*known = OrphanedConstraints.back();
OrphanedConstraints.pop_back();
return;
}

// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::removedConstraint(constraint));
// Remove the constraint from each type variable.
for (auto typeVar : constraint->getTypeVariables()) {
(*this)[typeVar].removeConstraint(constraint);
}

// Remove all adjacencies for all type variables.
enumerateAdjacencies(constraint,
[&](TypeVariableType *lhs, TypeVariableType *rhs) {
assert(lhs != rhs);
(*this)[lhs].removeAdjacency(rhs);
});
}

void ConstraintGraph::mergeNodes(TypeVariableType *typeVar1,
Expand Down Expand Up @@ -377,7 +473,6 @@ 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 rep = CS.getRepresentative(typeVar);
auto shouldConsiderConstraint = [&](Constraint *constraint) {
Expand Down Expand Up @@ -409,19 +504,29 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(

// Add constraints for the given adjacent type variable.
llvm::SmallPtrSet<TypeVariableType *, 4> typeVars;

// Local function to add constraints
llvm::SmallPtrSet<Constraint *, 4> visitedConstraints;
auto addAdjacentConstraints = [&](TypeVariableType *adjTypeVar) {
auto adjTypeVarsToVisit =
(*this)[CS.getRepresentative(adjTypeVar)].getEquivalenceClass();
auto addConstraintsOfAdjacency = [&](TypeVariableType *adjTypeVar) {
ArrayRef<TypeVariableType *> adjTypeVarsToVisit;
switch (kind) {
case GatheringKind::EquivalenceClass:
adjTypeVarsToVisit = adjTypeVar;
break;

case GatheringKind::AllMentions:
adjTypeVarsToVisit
= (*this)[CS.getRepresentative(adjTypeVar)].getEquivalenceClass();
break;
}

for (auto adjTypeVarEquiv : adjTypeVarsToVisit) {
if (!typeVars.insert(adjTypeVarEquiv).second)
continue;

for (auto constraint : (*this)[adjTypeVarEquiv].getConstraints()) {
if (!visitedConstraints.insert(constraint).second)
continue;

if (acceptConstraint(constraint))
if (visitedConstraints.insert(constraint).second &&
acceptConstraint(constraint))
constraints.push_back(constraint);
}
}
Expand All @@ -430,29 +535,34 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
auto &reprNode = (*this)[CS.getRepresentative(typeVar)];
auto equivClass = reprNode.getEquivalenceClass();
for (auto typeVar : equivClass) {
auto &node = (*this)[typeVar];
for (auto constraint : node.getConstraints()) {
if (!typeVars.insert(typeVar).second)
continue;

for (auto constraint : (*this)[typeVar].getConstraints()) {
if (visitedConstraints.insert(constraint).second &&
acceptConstraint(constraint))
constraints.push_back(constraint);
}

// If we want all mentions, visit type variables within each of our
// constraints.
if (kind == GatheringKind::AllMentions) {
if (!shouldConsiderConstraint(constraint))
continue;
auto &node = (*this)[typeVar];

for (auto adjTypeVar : constraint->getTypeVariables()) {
addAdjacentConstraints(adjTypeVar);
}
}
for (auto adjTypeVar : node.getFixedBindings()) {
addConstraintsOfAdjacency(adjTypeVar);
}

// For any type variable mentioned in a fixed binding, add adjacent
// constraints.
for (auto adjTypeVar : node.getFixedBindings()) {
addAdjacentConstraints(adjTypeVar);
switch (kind) {
case GatheringKind::EquivalenceClass:
break;

case GatheringKind::AllMentions:
// Retrieve the constraints from adjacent bindings.
for (auto adjTypeVar : node.getAdjacencies()) {
addConstraintsOfAdjacency(adjTypeVar);
}

break;
}

}

return constraints;
Expand Down Expand Up @@ -1228,6 +1338,28 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) {
}
}

if (!Adjacencies.empty()) {
out.indent(indent + 2);
out << "Adjacencies:";
SmallVector<TypeVariableType *, 4> sortedAdjacencies(Adjacencies.begin(),
Adjacencies.end());
std::sort(sortedAdjacencies.begin(), sortedAdjacencies.end(),
[](TypeVariableType *lhs, TypeVariableType *rhs) {
return lhs->getID() < rhs->getID();
});
for (auto adj : sortedAdjacencies) {
out << ' ';
adj->print(out);

auto &info = AdjacencyInfo[adj];
auto degree = info.NumConstraints;
if (degree > 1) {
out << " (" << degree << ")";
}
}
out << "\n";
}

// Print fixed bindings.
if (!FixedBindings.empty()) {
out.indent(indent + 2);
Expand Down Expand Up @@ -1396,6 +1528,67 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) {
requireSameValue(info.first, Constraints[info.second],
"constraint map provides wrong index into vector");
}

// Verify that the adjacency map/vector haven't gotten out of sync.
requireSameValue(Adjacencies.size(), AdjacencyInfo.size(),
"adjacency vector and map have different sizes");
for (auto info : AdjacencyInfo) {
require(info.second.Index < Adjacencies.size(),
"adjacency index out-of-range");
requireSameValue(info.first, Adjacencies[info.second.Index],
"adjacency map provides wrong index into vector");
require(!info.second.empty(),
"adjacency information should have been removed");
require(info.second.NumConstraints <= Constraints.size(),
"adjacency information has higher degree than # of constraints");
}

// Based on the constraints we have, build up a representation of what
// we expect the adjacencies to look like.
llvm::DenseMap<TypeVariableType *, unsigned> expectedAdjacencies;
for (auto constraint : Constraints) {
if (constraint->isOneWayConstraint())
continue;

for (auto adjTypeVar : constraint->getTypeVariables()) {
if (adjTypeVar == TypeVar)
continue;

++expectedAdjacencies[adjTypeVar];
}
}

// Make sure that the adjacencies we expect are the adjacencies we have.
for (auto adj : expectedAdjacencies) {
auto knownAdj = AdjacencyInfo.find(adj.first);
requireWithContext(knownAdj != AdjacencyInfo.end(),
"missing adjacency information for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString() << 'n';
});

requireWithContext(adj.second == knownAdj->second.NumConstraints,
"wrong number of adjacencies for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString()
<< " (" << adj.second << " vs. "
<< knownAdj->second.NumConstraints
<< ")\n";
});
}

if (AdjacencyInfo.size() != expectedAdjacencies.size()) {
// The adjacency information has something extra in it. Find the
// extraneous type variable.
for (auto adj : AdjacencyInfo) {
requireWithContext(AdjacencyInfo.count(adj.first) > 0,
"extraneous adjacency info for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString() << '\n';
});
}
}

#undef requireSameValue
#undef requireWithContext
#undef require
Expand Down
Loading