Skip to content

Commit f6ce290

Browse files
committed
[Associated type inference] De-duplicate solutions as early as possible.
Associated type inference can end up visiting a large number of potential solution states, which it ends up having to deduplicate. Perform the de-duplication step earlier, before we do extensive checking of the solution itself, to eliminate a bunch of extra work. Addresses the compile-time regression in rdar://problem/37165620. (cherry picked from commit a57f306)
1 parent 02a39ae commit f6ce290

File tree

1 file changed

+58
-35
lines changed

1 file changed

+58
-35
lines changed

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,22 @@
2525
#include "swift/AST/Types.h"
2626
#include "swift/Basic/Defer.h"
2727
#include "swift/ClangImporter/ClangModule.h"
28+
#include "llvm/ADT/Statistic.h"
2829
#include "llvm/ADT/TinyPtrVector.h"
2930

3031
#define DEBUG_TYPE "Associated type inference"
3132
#include "llvm/Support/Debug.h"
3233

34+
STATISTIC(NumSolutionStates, "# of solution states visited");
35+
STATISTIC(NumSolutionStatesFailedCheck,
36+
"# of solution states that failed constraints check");
37+
STATISTIC(NumConstrainedExtensionChecks,
38+
"# of constrained extension checks");
39+
STATISTIC(NumConstrainedExtensionChecksFailed,
40+
"# of constrained extension checks failed");
41+
STATISTIC(NumDuplicateSolutionStates,
42+
"# of duplicate solution states ");
43+
3344
using namespace swift;
3445

3546
void InferredAssociatedTypesByWitness::dump() const {
@@ -1042,26 +1053,6 @@ AssociatedTypeInference::getSubstOptionsWithCurrentTypeWitnesses() {
10421053
bool AssociatedTypeInference::checkCurrentTypeWitnesses(
10431054
const SmallVectorImpl<std::pair<ValueDecl *, ValueDecl *>>
10441055
&valueWitnesses) {
1045-
// Fold the dependent member types within this type.
1046-
for (auto assocType : proto->getAssociatedTypeMembers()) {
1047-
if (conformance->hasTypeWitness(assocType))
1048-
continue;
1049-
1050-
// If the type binding does not have a type parameter, there's nothing
1051-
// to do.
1052-
auto known = typeWitnesses.begin(assocType);
1053-
assert(known != typeWitnesses.end());
1054-
if (!known->first->hasTypeParameter() &&
1055-
!known->first->hasDependentMember())
1056-
continue;
1057-
1058-
Type replaced = substCurrentTypeWitnesses(known->first);
1059-
if (replaced.isNull())
1060-
return true;
1061-
1062-
known->first = replaced;
1063-
}
1064-
10651056
// If we don't have a requirement signature for this protocol, bail out.
10661057
// FIXME: We should never get to this point. Or we should always fail.
10671058
if (!proto->isRequirementSignatureComputed()) return false;
@@ -1090,6 +1081,7 @@ bool AssociatedTypeInference::checkCurrentTypeWitnesses(
10901081
switch (result) {
10911082
case RequirementCheckResult::Failure:
10921083
case RequirementCheckResult::UnsatisfiedDependency:
1084+
++NumSolutionStatesFailedCheck;
10931085
return true;
10941086

10951087
case RequirementCheckResult::Success:
@@ -1113,7 +1105,11 @@ bool AssociatedTypeInference::checkCurrentTypeWitnesses(
11131105
if (!ext->isConstrainedExtension()) continue;
11141106
if (!checkedExtensions.insert(ext).second) continue;
11151107

1116-
if (checkConstrainedExtension(ext)) return true;
1108+
++NumConstrainedExtensionChecks;
1109+
if (checkConstrainedExtension(ext)) {
1110+
++NumConstrainedExtensionChecksFailed;
1111+
return true;
1112+
}
11171113
}
11181114

11191115
return false;
@@ -1204,27 +1200,54 @@ void AssociatedTypeInference::findSolutionsRec(
12041200
return;
12051201
}
12061202

1207-
/// Check the current set of type witnesses.
1208-
bool invalid = checkCurrentTypeWitnesses(valueWitnesses);
1203+
++NumSolutionStates;
1204+
1205+
// Fold the dependent member types within this type.
1206+
for (auto assocType : proto->getAssociatedTypeMembers()) {
1207+
if (conformance->hasTypeWitness(assocType))
1208+
continue;
12091209

1210-
// Determine whether there is already a solution with the same
1211-
// bindings.
1212-
for (const auto &solution : solutions) {
1213-
bool sameSolution = true;
1210+
// If the type binding does not have a type parameter, there's nothing
1211+
// to do.
1212+
auto known = typeWitnesses.begin(assocType);
1213+
assert(known != typeWitnesses.end());
1214+
if (!known->first->hasTypeParameter() &&
1215+
!known->first->hasDependentMember())
1216+
continue;
1217+
1218+
Type replaced = substCurrentTypeWitnesses(known->first);
1219+
if (replaced.isNull())
1220+
return;
1221+
1222+
known->first = replaced;
1223+
}
1224+
1225+
// Check whether our current solution matches the given solution.
1226+
auto matchesSolution =
1227+
[&](const InferredTypeWitnessesSolution &solution) {
12141228
for (const auto &existingTypeWitness : solution.TypeWitnesses) {
12151229
auto typeWitness = typeWitnesses.begin(existingTypeWitness.first);
1216-
if (!typeWitness->first->isEqual(existingTypeWitness.second.first)) {
1217-
sameSolution = false;
1218-
break;
1219-
}
1230+
if (!typeWitness->first->isEqual(existingTypeWitness.second.first))
1231+
return false;
12201232
}
12211233

1222-
// We found the same solution. There is no point in recording
1223-
// a new one.
1224-
if (sameSolution)
1225-
return;
1234+
return true;
1235+
};
1236+
1237+
// If we've seen this solution already, bail out; there's no point in
1238+
// checking further.
1239+
if (std::find_if(solutions.begin(), solutions.end(), matchesSolution)
1240+
!= solutions.end() ||
1241+
std::find_if(nonViableSolutions.begin(), nonViableSolutions.end(),
1242+
matchesSolution)
1243+
!= nonViableSolutions.end()) {
1244+
++NumDuplicateSolutionStates;
1245+
return;
12261246
}
12271247

1248+
/// Check the current set of type witnesses.
1249+
bool invalid = checkCurrentTypeWitnesses(valueWitnesses);
1250+
12281251
auto &solutionList = invalid ? nonViableSolutions : solutions;
12291252
solutionList.push_back(InferredTypeWitnessesSolution());
12301253
auto &solution = solutionList.back();

0 commit comments

Comments
 (0)