Skip to content

[Constraint solver] Handle disjunctions as separate connected components #7655

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 1 commit into from
Feb 21, 2017
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
46 changes: 33 additions & 13 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2089,6 +2089,15 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions,
constraintComponent[constraint] = components[i];
}

// Add the orphaned components to the mapping from constraints to components.
unsigned firstOrphanedConstraint =
numComponents - CG.getOrphanedConstraints().size();
{
unsigned component = firstOrphanedConstraint;
for (auto constraint : CG.getOrphanedConstraints())
constraintComponent[constraint] = component++;
}

// Sort the constraints into buckets based on component number.
std::unique_ptr<ConstraintList[]> constraintBuckets(
new ConstraintList[numComponents]);
Expand All @@ -2098,6 +2107,9 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions,
constraintBuckets[constraintComponent[constraint]].push_back(constraint);
}

// Remove all of the orphaned constraints; we'll introduce them as needed.
auto allOrphanedConstraints = CG.takeOrphanedConstraints();

// Function object that returns all constraints placed into buckets
// back to the list of constraints.
auto returnAllConstraints = [&] {
Expand All @@ -2106,6 +2118,7 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions,
InactiveConstraints.splice(InactiveConstraints.end(),
constraintBuckets[component]);
}
CG.setOrphanedConstraints(std::move(allOrphanedConstraints));
};

// Compute the partial solutions produced for each connected component.
Expand All @@ -2118,24 +2131,31 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions,
++solverState->NumComponentsSplit;

// Collect the constraints for this component.
InactiveConstraints.splice(InactiveConstraints.end(),
InactiveConstraints.splice(InactiveConstraints.end(),
constraintBuckets[component]);

// Collect the type variables that are not part of a different
// component; this includes type variables that are part of the
// component as well as already-resolved type variables.
// FIXME: The latter could be avoided if we had already
// substituted all of those other type variables through.
llvm::SmallVector<TypeVariableType *, 16> allTypeVariables
llvm::SmallVector<TypeVariableType *, 16> allTypeVariables
= std::move(TypeVariables);
for (auto typeVar : allTypeVariables) {
auto known = typeVarComponent.find(typeVar);
if (known != typeVarComponent.end() && known->second != component)
continue;

TypeVariables.push_back(typeVar);
Constraint *orphaned = nullptr;
if (component < firstOrphanedConstraint) {
// Collect the type variables that are not part of a different
// component; this includes type variables that are part of the
// component as well as already-resolved type variables.
for (auto typeVar : allTypeVariables) {
auto known = typeVarComponent.find(typeVar);
if (known != typeVarComponent.end() && known->second != component)
continue;

TypeVariables.push_back(typeVar);
}
} else {
// Get the orphaned constraint.
assert(InactiveConstraints.size() == 1 && "supposed to be an orphan!");
orphaned = &InactiveConstraints.front();
}

CG.setOrphanedConstraint(orphaned);

// Solve for this component. If it fails, we're done.
bool failed;
if (TC.getLangOpts().DebugConstraintSolver) {
Expand Down
25 changes: 21 additions & 4 deletions lib/Sema/ConstraintGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ void ConstraintGraph::addConstraint(Constraint *constraint) {
}
}

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

// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::addedConstraint(constraint));
Expand All @@ -375,6 +381,16 @@ void ConstraintGraph::removeConstraint(Constraint *constraint) {
}
}

// If this is an orphaned constraint, remove it from the list.
if (referencedTypeVars.empty()) {
auto known = std::find(OrphanedConstraints.begin(),
OrphanedConstraints.end(),
constraint);
assert(known != OrphanedConstraints.end() && "missing orphaned constraint");
*known = OrphanedConstraints.back();
OrphanedConstraints.pop_back();
}

// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::removedConstraint(constraint));
Expand Down Expand Up @@ -578,9 +594,9 @@ unsigned ConstraintGraph::computeConnectedComponents(
if (CS.getFixedType(TypeVariables[i]))
continue;

// If we only care about a subset, and this type variable isn't in that
// subset, skip it.
if (!typeVarSubset.empty() && typeVarSubset.count(TypeVariables[i]) == 0)
// If this type variable isn't in the subset of type variables we care
// about, skip it.
if (typeVarSubset.count(TypeVariables[i]) == 0)
continue;

componentHasUnboundTypeVar[components[i]] = true;
Expand Down Expand Up @@ -611,7 +627,7 @@ unsigned ConstraintGraph::computeConnectedComponents(
}
components.erase(components.begin() + outIndex, components.end());

return numComponents;
return numComponents + getOrphanedConstraints().size();
}


Expand Down Expand Up @@ -861,6 +877,7 @@ void ConstraintGraph::dump() {

void ConstraintGraph::printConnectedComponents(llvm::raw_ostream &out) {
SmallVector<TypeVariableType *, 16> typeVars;
typeVars.append(TypeVariables.begin(), TypeVariables.end());
SmallVector<unsigned, 16> components;
unsigned numComponents = computeConnectedComponents(typeVars, components);
for (unsigned component = 0; component != numComponents; ++component) {
Expand Down
40 changes: 36 additions & 4 deletions lib/Sema/ConstraintGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,18 +237,47 @@ class ConstraintGraph {

/// Compute the connected components of the graph.
///
/// \param typeVars The type variables that occur within the
/// connected components. If a non-empty vector is passed in, the algorithm
/// will only consider type variables reachable from the initial set.
/// \param typeVars The type variables that should be included in the
/// set of connected components that are returned.
///
/// \param components Receives the component numbers for each type variable
/// in \c typeVars.
///
/// \returns the number of connected components in the graph.
/// \returns the number of connected components in the graph, which includes
/// one component for each of the constraints produced by
/// \c getOrphanedConstraints().
unsigned computeConnectedComponents(
SmallVectorImpl<TypeVariableType *> &typeVars,
SmallVectorImpl<unsigned> &components);

/// Retrieve the set of "orphaned" constraints, which are known to the
/// constraint graph but have no type variables to anchor them.
ArrayRef<Constraint *> getOrphanedConstraints() const {
return OrphanedConstraints;
}

/// Replace the orphaned constraints with the constraints in the given list,
/// returning the old set of orphaned constraints.
SmallVector<Constraint *, 4> takeOrphanedConstraints() {
auto result = std::move(OrphanedConstraints);
OrphanedConstraints.clear();
return result;
}

/// Set the orphaned constraints.
void setOrphanedConstraints(SmallVector<Constraint *, 4> &&newConstraints) {
OrphanedConstraints = std::move(newConstraints);
}

/// Set the list of orphaned constraints to a single constraint.
///
/// If \c orphaned is null, just clear out the list.
void setOrphanedConstraint(Constraint *orphaned) {
OrphanedConstraints.clear();
if (orphaned)
OrphanedConstraints.push_back(orphaned);
}

/// Print the graph.
void print(llvm::raw_ostream &out);

Expand Down Expand Up @@ -300,6 +329,9 @@ class ConstraintGraph {
/// The type variables in this graph, in stable order.
SmallVector<TypeVariableType *, 4> TypeVariables;

/// Constraints that are "orphaned" because they contain no type variables.
SmallVector<Constraint *, 4> OrphanedConstraints;

/// The kind of change made to the graph.
enum class ChangeKind {
/// Added a type variable.
Expand Down
19 changes: 19 additions & 0 deletions validation-test/Sema/explicit_coercions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %target-swift-frontend -typecheck %s

public class Foo : CustomReflectable {
public var booleanValue : Bool?
public var customMirror: Mirror {
return Mirror(self, children: [
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any,
"booleanValue": booleanValue as Bool? as Any
])
}
}