mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Solver: Keep track of a solution's score as we're computing it.
No functionality change here; just staging for some future optimizations. Swift SVN r11028
This commit is contained in:
@@ -3353,54 +3353,3 @@ Solution::convertToArrayBound(Expr *expr, ConstraintLocator *locator) const {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Solution::getFixedScore() const {
|
||||
if (fixedScore)
|
||||
return *fixedScore;
|
||||
|
||||
int score = 0;
|
||||
|
||||
// Consider overload choices.
|
||||
for (auto overload : overloadChoices) {
|
||||
auto choice = overload.second.choice;
|
||||
if (choice.getKind() != OverloadChoiceKind::Decl)
|
||||
continue;
|
||||
|
||||
// -3 penalty for each user-defined conversion.
|
||||
if (choice.getDecl()->getAttrs().isConversion())
|
||||
score -= 3;
|
||||
}
|
||||
|
||||
// Consider type bindings.
|
||||
auto &tc = getConstraintSystem().getTypeChecker();
|
||||
for (auto binding : typeBindings) {
|
||||
// Look for type variables corresponding directly to an expression.
|
||||
auto typeVar = binding.first;
|
||||
auto locator = typeVar->getImpl().getLocator();
|
||||
if (!locator || !locator->getAnchor() || !locator->getPath().empty())
|
||||
continue;
|
||||
|
||||
// Check whether there is a literal protocol corresponding to the
|
||||
// anchor expression.
|
||||
auto literalProtocol
|
||||
= tc.getLiteralProtocol(locator->getAnchor());
|
||||
if (!literalProtocol)
|
||||
continue;
|
||||
|
||||
// Retrieve the default type for this literal protocol, if there is one.
|
||||
auto defaultType = tc.getDefaultType(literalProtocol,
|
||||
getConstraintSystem().DC);
|
||||
if (!defaultType)
|
||||
continue;
|
||||
|
||||
// +1 if the bound type matches the default type for this literal protocol.
|
||||
// Literal types are always nominal, so we simply check the nominal
|
||||
// declaration. This covers e.g., Slice vs. Slice<T>.
|
||||
if (defaultType->getAnyNominal() == binding.second->getAnyNominal())
|
||||
++score;
|
||||
}
|
||||
|
||||
// Save the fixed score.
|
||||
fixedScore = score;
|
||||
return score;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,20 @@ using namespace constraints;
|
||||
#define DEBUG_TYPE "Constraint solver overall"
|
||||
STATISTIC(NumDiscardedSolutions, "# of solutions discarded");
|
||||
|
||||
void ConstraintSystem::increaseScore(ScoreKind kind) {
|
||||
unsigned index = static_cast<unsigned>(kind);
|
||||
++CurrentScore.Data[index];
|
||||
}
|
||||
|
||||
llvm::raw_ostream &constraints::operator<<(llvm::raw_ostream &out,
|
||||
const Score &score) {
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
if (i) out << ' ';
|
||||
out << score.Data[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// \brief Remove the initializers from any tuple types within the
|
||||
/// given type.
|
||||
static Type stripInitializers(TypeChecker &tc, Type origType) {
|
||||
@@ -446,6 +460,13 @@ Comparison TypeChecker::compareDeclarations(DeclContext *dc,
|
||||
return decl1Better? Comparison::Better : Comparison::Worse;
|
||||
}
|
||||
|
||||
/// Simplify a score into a single integer.
|
||||
/// FIXME: Temporary hack.
|
||||
static int simplifyScore(const Score &score) {
|
||||
return (int)score.Data[SK_UserConversion] * -3
|
||||
+ (int)score.Data[SK_NonDefaultLiteral] * -1;
|
||||
}
|
||||
|
||||
SolutionCompareResult ConstraintSystem::compareSolutions(
|
||||
ConstraintSystem &cs,
|
||||
ArrayRef<Solution> solutions,
|
||||
@@ -458,8 +479,8 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
|
||||
// Solution comparison uses a scoring system to determine whether one
|
||||
// solution is better than the other. Retrieve the fixed scores for each of
|
||||
// the solutions, which we'll modify with relative scoring.
|
||||
int score1 = solutions[idx1].getFixedScore();
|
||||
int score2 = solutions[idx2].getFixedScore();
|
||||
int score1 = simplifyScore(solutions[idx1].getFixedScore());
|
||||
int score2 = simplifyScore(solutions[idx2].getFixedScore());
|
||||
|
||||
// Compare overload sets.
|
||||
for (auto &overload : diff.overloads) {
|
||||
|
||||
@@ -526,6 +526,9 @@ tryUserConversion(ConstraintSystem &cs, Type type, ConstraintKind kind,
|
||||
cs.addConstraint(kind, outputTV, otherType, resultLocator);
|
||||
}
|
||||
|
||||
// We're adding a user-defined conversion.
|
||||
cs.increaseScore(SK_UserConversion);
|
||||
|
||||
return ConstraintSystem::SolutionKind::Solved;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ static Optional<Type> checkTypeOfBinding(ConstraintSystem &cs,
|
||||
Solution ConstraintSystem::finalize(
|
||||
FreeTypeVariableBinding allowFreeTypeVariables) {
|
||||
// Create the solution.
|
||||
Solution solution(*this);
|
||||
Solution solution(*this, CurrentScore);
|
||||
|
||||
// For any of the type variables that has no associated fixed type, assign a
|
||||
// fresh generic type parameters.
|
||||
@@ -123,6 +123,9 @@ Solution ConstraintSystem::finalize(
|
||||
}
|
||||
|
||||
void ConstraintSystem::applySolution(const Solution &solution) {
|
||||
// Update the score.
|
||||
CurrentScore += solution.getFixedScore();
|
||||
|
||||
// Assign fixed types to the type variables solved by this solution.
|
||||
llvm::SmallPtrSet<TypeVariableType *, 4>
|
||||
knownTypeVariables(TypeVariables.begin(), TypeVariables.end());
|
||||
@@ -134,7 +137,7 @@ void ConstraintSystem::applySolution(const Solution &solution) {
|
||||
// If we don't already have a fixed type for this type variable,
|
||||
// assign the fixed type from the solution.
|
||||
if (!getFixedType(binding.first) && !binding.second->hasTypeVariable())
|
||||
assignFixedType(binding.first, binding.second);
|
||||
assignFixedType(binding.first, binding.second, /*updateScore=*/false);
|
||||
}
|
||||
|
||||
// Register overload choices.
|
||||
@@ -583,6 +586,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
|
||||
numConstraintRestrictions = cs.solverState->constraintRestrictions.size();
|
||||
oldGeneratedConstraints = cs.solverState->generatedConstraints;
|
||||
cs.solverState->generatedConstraints = &generatedConstraints;
|
||||
PreviousScore = cs.CurrentScore;
|
||||
|
||||
++cs.solverState->NumStatesExplored;
|
||||
|
||||
@@ -618,6 +622,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
|
||||
// Reset the prior generated-constraints pointer.
|
||||
cs.solverState->generatedConstraints = oldGeneratedConstraints;
|
||||
|
||||
// Reset the previous score.
|
||||
cs.CurrentScore = PreviousScore;
|
||||
|
||||
// Clear out other "failed" state.
|
||||
cs.failedConstraint = nullptr;
|
||||
}
|
||||
@@ -932,7 +939,8 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
|
||||
auto solution = finalize(allowFreeTypeVariables);
|
||||
if (TC.getLangOpts().DebugConstraintSolver) {
|
||||
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
||||
log.indent(solverState->depth * 2) << "(found solution)\n";
|
||||
log.indent(solverState->depth * 2)
|
||||
<< "(found solution " << CurrentScore << ")\n";
|
||||
}
|
||||
|
||||
solutions.push_back(std::move(solution));
|
||||
@@ -1069,6 +1077,11 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
|
||||
// Move the type variables back, clear out constraints; we're
|
||||
// ready for the next component.
|
||||
TypeVariables = std::move(allTypeVariables);
|
||||
|
||||
// For each of the partial solutions, substract off the current score.
|
||||
// It doesn't contribute.
|
||||
for (auto &solution : partialSolutions[component])
|
||||
solution.getFixedScore() -= CurrentScore;
|
||||
}
|
||||
|
||||
// Move the constraints back. The system is back in a normal state.
|
||||
@@ -1103,7 +1116,8 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
|
||||
auto solution = finalize(allowFreeTypeVariables);
|
||||
if (TC.getLangOpts().DebugConstraintSolver) {
|
||||
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
||||
log.indent(solverState->depth * 2) << "(composed solution)\n";
|
||||
log.indent(solverState->depth * 2)
|
||||
<< "(composed solution " << CurrentScore << ")\n";
|
||||
}
|
||||
|
||||
// Save this solution.
|
||||
|
||||
@@ -69,9 +69,51 @@ void ConstraintSystem::mergeEquivalenceClasses(TypeVariableType *typeVar1,
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type) {
|
||||
void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type,
|
||||
bool updateScore) {
|
||||
typeVar->getImpl().assignFixedType(type, getSavedBindings());
|
||||
|
||||
if (updateScore && !type->is<TypeVariableType>()) {
|
||||
// If this type variable represents a literal, check whether we picked the
|
||||
// default literal type. First, find the corresponding protocol.
|
||||
ProtocolDecl *literalProtocol = nullptr;
|
||||
if (CG) {
|
||||
// If we have the constraint graph, we can check all type variables in
|
||||
// the equivalence class. This is the More Correct path.
|
||||
// FIXME: Eliminate the less-correct path.
|
||||
auto typeVarRep = getRepresentative(typeVar);
|
||||
for (auto tv : (*CG)[typeVarRep].getEquivalenceClass()) {
|
||||
auto locator = tv->getImpl().getLocator();
|
||||
if (!locator || !locator->getPath().empty())
|
||||
continue;
|
||||
|
||||
auto anchor = locator->getAnchor();
|
||||
if (!anchor)
|
||||
continue;
|
||||
|
||||
literalProtocol = TC.getLiteralProtocol(anchor);
|
||||
if (literalProtocol)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// FIXME: This is the less-correct path.
|
||||
auto locator = typeVar->getImpl().getLocator();
|
||||
if (locator && locator->getPath().empty() && locator->getAnchor()) {
|
||||
literalProtocol = TC.getLiteralProtocol(locator->getAnchor());
|
||||
}
|
||||
}
|
||||
|
||||
// If the protocol has a default type, check it.
|
||||
if (literalProtocol) {
|
||||
if (auto defaultType = TC.getDefaultType(literalProtocol, DC)) {
|
||||
// Check whether the nominal types match. This makes sure that we
|
||||
// properly handle Slice vs. Slice<T>.
|
||||
if (defaultType->getAnyNominal() != type->getAnyNominal())
|
||||
increaseScore(SK_NonDefaultLiteral);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the constraint graph.
|
||||
if (CG) {
|
||||
CG->bindTypeVariable(typeVar, type);
|
||||
|
||||
@@ -611,6 +611,94 @@ struct SelectedOverload {
|
||||
Type openedType;
|
||||
};
|
||||
|
||||
/// Describes an aspect of a solution that affects is overall score, i.e., a
|
||||
/// user-defined conversions.
|
||||
enum ScoreKind {
|
||||
/// A user-defined conversion.
|
||||
SK_UserConversion = 0,
|
||||
/// A literal expression bound to a non-default literal type.
|
||||
SK_NonDefaultLiteral = 1
|
||||
};
|
||||
|
||||
/// The number of score kinds.
|
||||
const unsigned NumScoreKinds = 2;
|
||||
|
||||
/// Describes the fixed score of a solution to the constraint system.
|
||||
struct Score {
|
||||
unsigned Data[NumScoreKinds] = { 0, 0 };
|
||||
|
||||
friend Score &operator+=(Score &x, const Score &y) {
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
x.Data[i] += y.Data[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
friend Score operator+(const Score &x, const Score &y) {
|
||||
Score result;
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
result.Data[i] = x.Data[i] + y.Data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
friend Score operator-(const Score &x, const Score &y) {
|
||||
Score result;
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
result.Data[i] = x.Data[i] - y.Data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
friend Score &operator-=(Score &x, const Score &y) {
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
x.Data[i] -= y.Data[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
friend bool operator==(const Score &x, const Score &y) {
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
if (x.Data[i] != y.Data[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Score &x, const Score &y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
friend bool operator<(const Score &x, const Score &y) {
|
||||
for (unsigned i = 0; i != NumScoreKinds; ++i) {
|
||||
if (x.Data[i] < y.Data[i])
|
||||
return true;
|
||||
|
||||
if (x.Data[i] > y.Data[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator<=(const Score &x, const Score &y) {
|
||||
return !(y < x);
|
||||
}
|
||||
|
||||
friend bool operator>(const Score &x, const Score &y) {
|
||||
return y < x;
|
||||
}
|
||||
|
||||
friend bool operator>=(const Score &x, const Score &y) {
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Display a score.
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score);
|
||||
|
||||
/// \brief A complete solution to a constraint system.
|
||||
///
|
||||
/// A solution to a constraint system consists of type variable bindings to
|
||||
@@ -622,31 +710,19 @@ class Solution {
|
||||
ConstraintSystem *constraintSystem;
|
||||
|
||||
/// \brief The fixed score for this solution.
|
||||
mutable Optional<int> fixedScore;
|
||||
Score FixedScore;
|
||||
|
||||
public:
|
||||
/// \brief Create a solution for the given constraint system.
|
||||
Solution(ConstraintSystem &cs) : constraintSystem(&cs) {}
|
||||
Solution(ConstraintSystem &cs, const Score &score)
|
||||
: constraintSystem(&cs), FixedScore(score) {}
|
||||
|
||||
// Solution is a non-copyable type for performance reasons.
|
||||
Solution(const Solution &other) = delete;
|
||||
Solution &operator=(const Solution &other) = delete;
|
||||
|
||||
Solution(Solution &&other)
|
||||
: constraintSystem(other.constraintSystem),
|
||||
typeBindings(std::move(other.typeBindings)),
|
||||
overloadChoices(std::move(other.overloadChoices)),
|
||||
constraintRestrictions(std::move(other.constraintRestrictions))
|
||||
{
|
||||
}
|
||||
|
||||
Solution &operator=(Solution &&other) {
|
||||
constraintSystem = other.constraintSystem;
|
||||
typeBindings = std::move(other.typeBindings);
|
||||
overloadChoices = std::move(other.overloadChoices);
|
||||
constraintRestrictions = std::move(other.constraintRestrictions);
|
||||
return *this;
|
||||
}
|
||||
Solution(Solution &&other) = default;
|
||||
Solution &operator=(Solution &&other) = default;
|
||||
|
||||
/// \brief Retrieve the constraint system that this solution solves.
|
||||
ConstraintSystem &getConstraintSystem() const { return *constraintSystem; }
|
||||
@@ -726,9 +802,11 @@ public:
|
||||
Type openedType,
|
||||
SmallVectorImpl<Substitution> &substitutions) const;
|
||||
|
||||
/// \brief Retrieve the fixed score of this solution, which considers
|
||||
/// the number of user-defined conversions.
|
||||
int getFixedScore() const;
|
||||
/// \brief Retrieve the fixed score of this solution
|
||||
const Score &getFixedScore() const { return FixedScore; }
|
||||
|
||||
/// \brief Retrieve the fixed score of this solution
|
||||
Score &getFixedScore() { return FixedScore; }
|
||||
|
||||
/// \brief Retrieve the fixed type for the given type variable.
|
||||
Type getFixedType(TypeVariableType *typeVar) const;
|
||||
@@ -911,6 +989,10 @@ private:
|
||||
/// \brief The overload sets that have been resolved along the current path.
|
||||
ResolvedOverloadSetListItem *resolvedOverloadSets = nullptr;
|
||||
|
||||
/// The current fixed score for this constraint system and the (partial)
|
||||
/// solution it represents.
|
||||
Score CurrentScore;
|
||||
|
||||
SmallVector<TypeVariableType *, 16> TypeVariables;
|
||||
ConstraintList Constraints;
|
||||
|
||||
@@ -1012,6 +1094,9 @@ public:
|
||||
/// \brief The length of \c constraintRestrictions.
|
||||
unsigned numConstraintRestrictions;
|
||||
|
||||
/// The previous score.
|
||||
Score PreviousScore;
|
||||
|
||||
/// Constraint graph scope associated with this solver scope.
|
||||
///
|
||||
/// FIXME: This is optional so we can easily enabled/disable the
|
||||
@@ -1314,7 +1399,12 @@ public:
|
||||
bool wantRValue);
|
||||
|
||||
/// \brief Assign a fixed type to the given type variable.
|
||||
void assignFixedType(TypeVariableType *typeVar, Type type);
|
||||
///
|
||||
/// \param typeVar The type variable to bind.
|
||||
/// \param type The fixed type to which the type variable will be bound.
|
||||
/// \param updateScore Whether to update the score based on this binding.
|
||||
void assignFixedType(TypeVariableType *typeVar, Type type,
|
||||
bool updateScore = true);
|
||||
|
||||
private:
|
||||
/// Introduce the constraints associated with the given type variable
|
||||
@@ -1720,6 +1810,10 @@ private:
|
||||
unsigned idx2);
|
||||
|
||||
public:
|
||||
/// Increase the score of the given kind for the current (partial) solution
|
||||
/// along the.
|
||||
void increaseScore(ScoreKind kind);
|
||||
|
||||
/// \brief Given a set of viable solutions, find the best
|
||||
/// solution.
|
||||
///
|
||||
|
||||
@@ -1504,7 +1504,8 @@ void Solution::dump(SourceManager *sm) const {
|
||||
}
|
||||
|
||||
void Solution::dump(SourceManager *sm, raw_ostream &out) const {
|
||||
out << "Fixed score: " << getFixedScore() << "\n\n";
|
||||
out << "Fixed score: " << FixedScore << "\n";
|
||||
|
||||
out << "Type variables:\n";
|
||||
for (auto binding : typeBindings) {
|
||||
out.indent(2);
|
||||
@@ -1552,6 +1553,7 @@ void ConstraintSystem::dump() {
|
||||
}
|
||||
|
||||
void ConstraintSystem::dump(raw_ostream &out) {
|
||||
out << "Score: " << CurrentScore << "\n";
|
||||
out << "Type Variables:\n";
|
||||
for (auto tv : TypeVariables) {
|
||||
out.indent(2);
|
||||
|
||||
@@ -111,6 +111,7 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) {
|
||||
if (isa<InterpolatedStringLiteralExpr>(expr))
|
||||
return getProtocol(expr->getLoc(),
|
||||
KnownProtocolKind::StringInterpolationConvertible);
|
||||
|
||||
if (auto E = dyn_cast<MagicIdentifierLiteralExpr>(expr)) {
|
||||
switch (E->getKind()) {
|
||||
case MagicIdentifierLiteralExpr::File:
|
||||
@@ -124,7 +125,7 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) {
|
||||
}
|
||||
}
|
||||
|
||||
llvm_unreachable("Unhandled literal kind");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Module *TypeChecker::getStdlibModule(const DeclContext *dc) {
|
||||
|
||||
Reference in New Issue
Block a user