Skip to content

Commit b993c6e

Browse files
committed
[Constraint solver] Move tryOptimizeGenericDisjunction() into partitioning
This narrow favoring rule makes more sense as part of disjunction partitioning, because it is not dependent on the use site at all and should only kick in when other options fail.
1 parent fae2d1b commit b993c6e

File tree

4 files changed

+82
-92
lines changed

4 files changed

+82
-92
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5036,34 +5036,6 @@ Type ConstraintSystem::simplifyAppliedOverloads(
50365036
break;
50375037
}
50385038

5039-
// Collect the active overload choices.
5040-
SmallVector<OverloadChoice, 4> choices;
5041-
for (auto constraint : disjunction->getNestedConstraints()) {
5042-
if (constraint->isDisabled())
5043-
continue;
5044-
choices.push_back(constraint->getOverloadChoice());
5045-
}
5046-
5047-
// If we can favor one generic result over another, do so.
5048-
if (auto favoredChoice = tryOptimizeGenericDisjunction(choices)) {
5049-
unsigned favoredIndex = favoredChoice - choices.data();
5050-
for (auto constraint : disjunction->getNestedConstraints()) {
5051-
if (constraint->isDisabled())
5052-
continue;
5053-
5054-
if (favoredIndex == 0) {
5055-
if (solverState)
5056-
solverState->favorConstraint(constraint);
5057-
else
5058-
constraint->setFavored();
5059-
5060-
break;
5061-
} else {
5062-
--favoredIndex;
5063-
}
5064-
}
5065-
}
5066-
50675039
// If there was a constraint that we couldn't reason about, don't use the
50685040
// results of any common-type computations.
50695041
if (hasUnhandledConstraints)

lib/Sema/CSSolver.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,9 +1907,73 @@ void ConstraintSystem::partitionForDesignatedTypes(
19071907
}
19081908
}
19091909

1910+
// Performance hack: if there are two generic overloads, and one is
1911+
// more specialized than the other, prefer the more-specialized one.
1912+
static Constraint *tryOptimizeGenericDisjunction(
1913+
TypeChecker &tc,
1914+
DeclContext *dc,
1915+
ArrayRef<Constraint *> constraints) {
1916+
if (constraints.size() != 2 ||
1917+
constraints[0]->getKind() != ConstraintKind::BindOverload ||
1918+
constraints[1]->getKind() != ConstraintKind::BindOverload ||
1919+
constraints[0]->isFavored() ||
1920+
constraints[1]->isFavored())
1921+
return nullptr;
1922+
1923+
OverloadChoice choiceA = constraints[0]->getOverloadChoice();
1924+
OverloadChoice choiceB = constraints[1]->getOverloadChoice();
1925+
1926+
if (!choiceA.isDecl() || !choiceB.isDecl())
1927+
return nullptr;
1928+
1929+
auto isViable = [](ValueDecl *decl) -> bool {
1930+
assert(decl);
1931+
1932+
auto *AFD = dyn_cast<AbstractFunctionDecl>(decl);
1933+
if (!AFD || !AFD->isGeneric())
1934+
return false;
1935+
1936+
auto funcType = AFD->getInterfaceType();
1937+
auto hasAnyOrOptional = funcType.findIf([](Type type) -> bool {
1938+
if (type->getOptionalObjectType())
1939+
return true;
1940+
1941+
return type->isAny();
1942+
});
1943+
1944+
// If function declaration references `Any` or `Any?` type
1945+
// let's not attempt it, because it's unclear
1946+
// without solving which overload is going to be better.
1947+
return !hasAnyOrOptional;
1948+
};
1949+
1950+
auto *declA = choiceA.getDecl();
1951+
auto *declB = choiceB.getDecl();
1952+
1953+
if (!isViable(declA) || !isViable(declB))
1954+
return nullptr;
1955+
1956+
switch (tc.compareDeclarations(dc, declA, declB)) {
1957+
case Comparison::Better:
1958+
return constraints[0];
1959+
1960+
case Comparison::Worse:
1961+
return constraints[1];
1962+
1963+
case Comparison::Unordered:
1964+
return nullptr;
1965+
}
1966+
}
1967+
19101968
void ConstraintSystem::partitionDisjunction(
19111969
ArrayRef<Constraint *> Choices, SmallVectorImpl<unsigned> &Ordering,
19121970
SmallVectorImpl<unsigned> &PartitionBeginning) {
1971+
// Apply a special-case rule for favoring one generic function over
1972+
// another.
1973+
if (auto favored = tryOptimizeGenericDisjunction(TC, DC, Choices)) {
1974+
favorConstraint(favored);
1975+
}
1976+
19131977
SmallSet<Constraint *, 16> taken;
19141978

19151979
// Local function used to iterate over the untaken choices from the

lib/Sema/ConstraintSystem.cpp

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,58 +1371,6 @@ ConstraintSystem::getTypeOfMemberReference(
13711371
return { openedType, type };
13721372
}
13731373

1374-
// Performance hack: if there are two generic overloads, and one is
1375-
// more specialized than the other, prefer the more-specialized one.
1376-
OverloadChoice *ConstraintSystem::tryOptimizeGenericDisjunction(
1377-
ArrayRef<OverloadChoice> choices) {
1378-
if (choices.size() != 2)
1379-
return nullptr;
1380-
1381-
const auto &choiceA = choices[0];
1382-
const auto &choiceB = choices[1];
1383-
1384-
if (!choiceA.isDecl() || !choiceB.isDecl())
1385-
return nullptr;
1386-
1387-
auto isViable = [](ValueDecl *decl) -> bool {
1388-
assert(decl);
1389-
1390-
auto *AFD = dyn_cast<AbstractFunctionDecl>(decl);
1391-
if (!AFD || !AFD->isGeneric())
1392-
return false;
1393-
1394-
auto funcType = AFD->getInterfaceType();
1395-
auto hasAnyOrOptional = funcType.findIf([](Type type) -> bool {
1396-
if (type->getOptionalObjectType())
1397-
return true;
1398-
1399-
return type->isAny();
1400-
});
1401-
1402-
// If function declaration references `Any` or `Any?` type
1403-
// let's not attempt it, because it's unclear
1404-
// without solving which overload is going to be better.
1405-
return !hasAnyOrOptional;
1406-
};
1407-
1408-
auto *declA = choiceA.getDecl();
1409-
auto *declB = choiceB.getDecl();
1410-
1411-
if (!isViable(declA) || !isViable(declB))
1412-
return nullptr;
1413-
1414-
switch (TC.compareDeclarations(DC, declA, declB)) {
1415-
case Comparison::Better:
1416-
return const_cast<OverloadChoice *>(&choiceA);
1417-
1418-
case Comparison::Worse:
1419-
return const_cast<OverloadChoice *>(&choiceB);
1420-
1421-
case Comparison::Unordered:
1422-
return nullptr;
1423-
}
1424-
}
1425-
14261374
/// If there are any SIMD operators in the overload set, partition the set so
14271375
/// that the SIMD operators come at the end.
14281376
static ArrayRef<OverloadChoice> partitionSIMDOperators(
@@ -1576,9 +1524,6 @@ void ConstraintSystem::addOverloadSet(Type boundType,
15761524
return;
15771525
}
15781526

1579-
if (!favoredChoice)
1580-
favoredChoice = tryOptimizeGenericDisjunction(choices);
1581-
15821527
SmallVector<OverloadChoice, 4> scratchChoices;
15831528
choices = partitionSIMDOperators(choices, scratchChoices);
15841529

lib/Sema/ConstraintSystem.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,10 +1352,10 @@ class ConstraintSystem {
13521352
/// Favor the given constraint; this change will be rolled back
13531353
/// when we exit the current solver scope.
13541354
void favorConstraint(Constraint *constraint) {
1355-
if (!constraint->isFavored()) {
1356-
constraint->setFavored();
1357-
favoredConstraints.push_back(constraint);
1358-
}
1355+
assert(!constraint->isFavored());
1356+
1357+
constraint->setFavored();
1358+
favoredConstraints.push_back(constraint);
13591359
}
13601360

13611361
private:
@@ -2085,6 +2085,20 @@ class ConstraintSystem {
20852085
removeInactiveConstraint(constraint);
20862086
}
20872087

2088+
/// Note that this constraint is "favored" within its disjunction, and
2089+
/// should be tried first to the exclusion of non-favored constraints in
2090+
/// the same disjunction.
2091+
void favorConstraint(Constraint *constraint) {
2092+
if (constraint->isFavored())
2093+
return;
2094+
2095+
if (solverState) {
2096+
solverState->favorConstraint(constraint);
2097+
} else {
2098+
constraint->setFavored();
2099+
}
2100+
}
2101+
20882102
/// Retrieve the list of inactive constraints.
20892103
ConstraintList &getConstraints() { return InactiveConstraints; }
20902104

@@ -3187,11 +3201,6 @@ class ConstraintSystem {
31873201

31883202
Constraint *selectApplyDisjunction();
31893203

3190-
/// Look at the set of overload choices to determine if there is a best
3191-
/// generic overload to favor.
3192-
OverloadChoice *tryOptimizeGenericDisjunction(
3193-
ArrayRef<OverloadChoice> choices);
3194-
31953204
/// Solve the system of constraints generated from provided expression.
31963205
///
31973206
/// \param expr The expression to generate constraints from.

0 commit comments

Comments
 (0)