Skip to content

[ConstraintSystem] Improve connected components printing in the type inference algorithm debug output #59971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 23, 2022
2 changes: 0 additions & 2 deletions include/swift/Sema/CSBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,6 @@ class BindingSet {
}

void dump(llvm::raw_ostream &out, unsigned indent) const;
void dump(TypeVariableType *typeVar, llvm::raw_ostream &out,
unsigned indent = 0) const LLVM_ATTRIBUTE_USED;

private:
void addBinding(PotentialBinding binding);
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Sema/ConstraintGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ class ConstraintGraph {
/// Print the graph.
void print(ArrayRef<TypeVariableType *> typeVars, llvm::raw_ostream &out);
void dump(llvm::raw_ostream &out);
void dumpActiveScopeChanges(llvm::raw_ostream &out, unsigned indent = 0);

// FIXME: Potentially side-effectful.
SWIFT_DEBUG_HELPER(void dump());
Expand Down Expand Up @@ -458,6 +459,7 @@ class ConstraintGraph {
/// Each change can be undone (once, and in reverse order) by calling the
/// undo() method.
class Change {
public:
/// The kind of change.
ChangeKind Kind;

Expand All @@ -482,7 +484,6 @@ class ConstraintGraph {
} Binding;
};

public:
Change() : Kind(ChangeKind::AddedTypeVariable), TypeVar(nullptr) { }

/// Create a change that added a type variable.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Sema/ConstraintGraphScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class ConstraintGraphScope {
public:
explicit ConstraintGraphScope(ConstraintGraph &CG);
~ConstraintGraphScope();

/// Get number of changes recorded at the start of the current active scope.
unsigned getStartIdx() {
return NumChanges;
}
};

} // end namespace constraints
Expand Down
5 changes: 4 additions & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -5557,7 +5557,10 @@ class ConstraintSystem {
Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel,
ConstraintKind matchKind, ConstraintLocatorBuilder locator);

Optional<BindingSet> determineBestBindings();
/// Determine whether given type variable with its set of bindings is viable
/// to be attempted on the next step of the solver.
Optional<BindingSet> determineBestBindings(
llvm::function_ref<void(const BindingSet &)> onCandidate);

/// Get bindings for the given type variable based on current
/// state of the constraint system.
Expand Down
22 changes: 8 additions & 14 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,8 @@ BindingSet::BindingScore BindingSet::formBindingScore(const BindingSet &b) {
-numNonDefaultableBindings);
}

Optional<BindingSet> ConstraintSystem::determineBestBindings() {
Optional<BindingSet> ConstraintSystem::determineBestBindings(
llvm::function_ref<void(const BindingSet &)> onCandidate) {
// Look for potential type variable bindings.
Optional<BindingSet> bestBindings;
llvm::SmallDenseMap<TypeVariableType *, BindingSet> cache;
Expand Down Expand Up @@ -804,9 +805,7 @@ Optional<BindingSet> ConstraintSystem::determineBestBindings() {
if (!bindings || !isViable)
continue;

if (isDebugMode()) {
bindings.dump(typeVar, llvm::errs(), solverState->getCurrentIndent());
}
onCandidate(bindings);

// If these are the first bindings, or they are better than what
// we saw before, use them instead.
Expand Down Expand Up @@ -1652,21 +1651,15 @@ static std::string getCollectionLiteralAsString(KnownProtocolKind KPK) {
#undef ENTRY
}

void BindingSet::dump(TypeVariableType *typeVar, llvm::raw_ostream &out,
unsigned indent) const {
out.indent(indent);
out << "(";
if (typeVar)
out << "$T" << typeVar->getImpl().getID();
dump(out, 1);
out << ")\n";
}

void BindingSet::dump(llvm::raw_ostream &out, unsigned indent) const {
PrintOptions PO;
PO.PrintTypesForDebugging = true;

out.indent(indent);
out << "(";
if (auto typeVar = getTypeVariable())
out << "$T" << typeVar->getImpl().getID() << " ";

std::vector<std::string> attributes;
if (isDirectHole())
attributes.push_back("hole");
Expand Down Expand Up @@ -1778,6 +1771,7 @@ void BindingSet::dump(llvm::raw_ostream &out, unsigned indent) const {
}
out << "] ";
}
out << ")\n";
}

// Given a possibly-Optional type, return the direct superclass of the
Expand Down
56 changes: 38 additions & 18 deletions lib/Sema/CSStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,45 @@ StepResult ComponentStep::take(bool prevFailed) {

/// Try to figure out what this step is going to be,
/// after the scope has been established.
SmallString<64> potentialBindings;
llvm::raw_svector_ostream bos(potentialBindings);

auto bestBindings = CS.determineBestBindings([&](const BindingSet &bindings) {
if (CS.isDebugMode() && bindings.hasViableBindings()) {
bindings.dump(bos, CS.solverState->getCurrentIndent() + 2);
}
});

auto *disjunction = CS.selectDisjunction();
auto bestBindings = CS.determineBestBindings();
auto *conjunction = CS.selectConjunction();

if (CS.isDebugMode()) {
PrintOptions PO;
PO.PrintTypesForDebugging = true;

auto &log = getDebugLogger();
if (!potentialBindings.empty()) {
log << "(Potential Binding(s): " << '\n';
log << potentialBindings;
}
log.indent(CS.solverState->getCurrentIndent());

if (disjunction) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and conjunction section prints a selected disjunction/conjunction instead of all of them but let’s address that in a follow up PR instead of holding this one.

log.indent(2);
log << "Disjunction(s) = [";
auto constraints = disjunction->getNestedConstraints();
log << constraints[0]->getFirstType()->getString(PO);
log << "]";
}
if (conjunction) {
log.indent(2);
log << "Conjunction(s) = [";
auto constraints = conjunction->getNestedConstraints();
log << constraints[0]->getFirstType()->getString(PO);
log << "]";
}
log << ")\n";
}

if (CS.shouldAttemptFixes()) {
if ((bestBindings &&
Expand Down Expand Up @@ -486,23 +523,6 @@ StepResult ComponentStep::finalize(bool isSuccess) {

void TypeVariableStep::setup() {
++CS.solverState->NumTypeVariablesBound;
if (CS.isDebugMode()) {
PrintOptions PO;
PO.PrintTypesForDebugging = true;
auto &log = getDebugLogger();

auto initialBindings = Producer.getCurrentBindings();
log << "Initial bindings: ";
interleave(
initialBindings.begin(), initialBindings.end(),
[&](const Binding &binding) {
log << TypeVar->getString(PO)
<< " := " << binding.BindingType->getString(PO);
},
[&log] { log << ", "; });

log << '\n';
}
}

bool TypeVariableStep::attempt(const TypeVariableBinding &choice) {
Expand Down
33 changes: 29 additions & 4 deletions lib/Sema/CSStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,11 +466,29 @@ class ComponentStep final : public SolverStep {
// to preliminary modify constraint system or log anything.
if (IsSingle)
return;

if (CS.isDebugMode())
getDebugLogger() << "(solving component #" << Index << '\n';


if (CS.isDebugMode()) {
auto &log = getDebugLogger();
log << "(solving component #" << Index << '\n';
}

ComponentScope = std::make_unique<Scope>(*this);

if (CS.isDebugMode()) {
auto &log = getDebugLogger();
log << "Type variables in scope = "
<< "[";
auto typeVars = CS.getTypeVariables();
PrintOptions PO;
PO.PrintTypesForDebugging = true;
interleave(typeVars, [&](TypeVariableType *typeVar) {
Type(typeVar).print(log, PO);
},
[&] {
log << ", ";
});
log << "]" << '\n';
}

// If this component has orphaned constraint attached,
// let's return it to the graph.
Expand Down Expand Up @@ -527,6 +545,13 @@ template <typename P> class BindingStep : public SolverStep {
auto scope = std::make_unique<Scope>(CS);
if (attempt(*choice)) {
ActiveChoice.emplace(std::move(scope), *choice);

if (CS.isDebugMode()) {
auto &log = llvm::errs();
auto &CG = CS.getConstraintGraph();
CG.dumpActiveScopeChanges(log, CS.solverState->getCurrentIndent());
}

return suspend(std::make_unique<SplitterStep>(CS, Solutions));
}
}
Expand Down
117 changes: 117 additions & 0 deletions lib/Sema/ConstraintGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,123 @@ void ConstraintGraph::dump(llvm::raw_ostream &out) {
print(CS.getTypeVariables(), out);
}

void ConstraintGraph::dumpActiveScopeChanges(llvm::raw_ostream &out,
unsigned indent) {
if (Changes.empty())
return;

// Collect Changes for printing.
std::map<TypeVariableType *, TypeBase *> tvWithboundTypes;
std::vector<TypeVariableType *> addedTypeVars;
std::vector<TypeVariableType *> equivTypeVars;
std::set<Constraint *> addedConstraints;
std::set<Constraint *> removedConstraints;
for (unsigned int i = ActiveScope->getStartIdx(); i < Changes.size(); i++) {
auto change = Changes[i];
switch (change.Kind) {
case ChangeKind::BoundTypeVariable:
tvWithboundTypes.insert(std::pair<TypeVariableType *, TypeBase *>(
change.Binding.TypeVar, change.Binding.FixedType));
break;
case ChangeKind::AddedTypeVariable:
addedTypeVars.push_back(change.TypeVar);
break;
case ChangeKind::ExtendedEquivalenceClass:
equivTypeVars.push_back(change.EquivClass.TypeVar);
break;
case ChangeKind::AddedConstraint:
addedConstraints.insert(change.TheConstraint);
break;
case ChangeKind::RemovedConstraint:
removedConstraints.insert(change.TheConstraint);
break;
}
}

// If there are any constraints that were both added and removed in this set
// of Changes, remove them from both.
std::set<Constraint *> intersects;
set_intersection(addedConstraints.begin(), addedConstraints.end(),
removedConstraints.begin(), removedConstraints.end(),
std::inserter(intersects, intersects.begin()));
llvm::set_subtract(addedConstraints, intersects);
llvm::set_subtract(removedConstraints, intersects);

// Print out Changes.
PrintOptions PO;
PO.PrintTypesForDebugging = true;
out.indent(indent);
out << "(Changes:\n";
if (!tvWithboundTypes.empty()) {
out.indent(indent + 2);
out << "(Newly Bound: \n";
for (const auto &tvWithType : tvWithboundTypes) {
out.indent(indent + 4);
out << "> $T" << tvWithType.first->getImpl().getID() << " := ";
tvWithType.second->print(out, PO);
out << '\n';
}
out.indent(indent + 2);
out << ")\n";
}
if (!addedTypeVars.empty()) {
out.indent(indent + 2);
auto heading = (addedTypeVars.size() > 1) ? "(New Type Variables: \n"
: "(New Type Variable: \n";
out << heading;
for (const auto &typeVar : addedTypeVars) {
out.indent(indent + 4);
out << "> $T" << typeVar->getImpl().getID();
out << '\n';
}
out.indent(indent + 2);
out << ")\n";
}
if (!equivTypeVars.empty()) {
out.indent(indent + 2);
auto heading = (equivTypeVars.size() > 1) ? "(New Equivalences: \n"
: "(New Equivalence: \n";
out << heading;
for (const auto &typeVar : equivTypeVars) {
out.indent(indent + 4);
out << "> $T" << typeVar->getImpl().getID();
out << '\n';
}
out.indent(indent + 2);
out << ")\n";
}
if (!addedConstraints.empty()) {
out.indent(indent + 2);
auto heading = (addedConstraints.size() > 1) ? "(Added Constraints: \n"
: "(Added Constraint: \n";
out << heading;
for (const auto &constraint : addedConstraints) {
out.indent(indent + 4);
out << "> ";
constraint->print(out, &CS.getASTContext().SourceMgr);
out << '\n';
}
out.indent(indent + 2);
out << ")\n";
}
if (!removedConstraints.empty()) {
out.indent(indent + 2);
auto heading = (removedConstraints.size() > 1) ? "(Removed Constraints: \n"
: "(Removed Constraint: \n";
out << heading;
for (const auto &constraint : removedConstraints) {
out.indent(indent + 4);
out << "> ";
constraint->print(out, &CS.getASTContext().SourceMgr);
out << '\n';
}
out.indent(indent + 2);
out << ")\n";
}
out.indent(indent);
out << ")\n";
}

void ConstraintGraph::printConnectedComponents(
ArrayRef<TypeVariableType *> typeVars,
llvm::raw_ostream &out) {
Expand Down
8 changes: 4 additions & 4 deletions test/Constraints/one_way_solve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ func testTernaryOneWayOverload(b: Bool) {
// CHECK: 0: $T2 $T3 $T4

// CHECK: solving component #1
// CHECK: Initial bindings: $T11 := Int8, $T11 := Int16
// CHECK: (attempting type variable $T11 := Int8

// CHECK: solving component #1
// CHECK: Initial bindings: $T11 := Int8, $T11 := Int16
// CHECK: (attempting type variable $T11 := Int8

// CHECK: solving component #1
// CHECK: Initial bindings: $T11 := Int8, $T11 := Int16
// CHECK: (attempting type variable $T11 := Int8

// CHECK: solving component #1
// CHECK: Initial bindings: $T11 := Int8
// CHECK: (attempting type variable $T11 := Int8
// CHECK: (found solution: [non-default literal(s) = 2] [use of overloaded unapplied function(s) = 2])

// CHECK: (composed solution: [non-default literal(s) = 2] [use of overloaded unapplied function(s) = 2])
Expand Down