Skip to content

Commit 27b1394

Browse files
committed
[ConstraintSystem] Within the generic disjunction choice partition,
attempt the most specific choices first. Then, if the solver finds a solution with one choice, it can skip any subsequent choices that can be unconditionally used in place of the successful chioce and produce the same solution.
1 parent 50212a0 commit 27b1394

File tree

2 files changed

+96
-2
lines changed

2 files changed

+96
-2
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,15 +2174,41 @@ void ConstraintSystem::partitionDisjunction(
21742174
};
21752175

21762176
// Gather the remaining options.
2177+
2178+
SmallVector<unsigned, 4> genericOverloads;
2179+
21772180
forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool {
2181+
if (!isForCodeCompletion()) {
2182+
// Collect generic overload choices separately, and sort these choices
2183+
// by specificity in order to try the most specific choice first.
2184+
if (constraint->getKind() == ConstraintKind::BindOverload) {
2185+
if (auto *decl = constraint->getOverloadChoice().getDeclOrNull()) {
2186+
auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl);
2187+
if (fnDecl && fnDecl->isGeneric()) {
2188+
genericOverloads.push_back(index);
2189+
return true;
2190+
}
2191+
}
2192+
}
2193+
}
2194+
21782195
everythingElse.push_back(index);
21792196
return true;
21802197
});
2198+
2199+
llvm::sort(genericOverloads, [&](unsigned lhs, unsigned rhs) -> bool {
2200+
auto *declA = dyn_cast<ValueDecl>(Choices[lhs]->getOverloadChoice().getDecl());
2201+
auto *declB = dyn_cast<ValueDecl>(Choices[rhs]->getOverloadChoice().getDecl());
2202+
2203+
auto result = TypeChecker::compareDeclarations(DC, declA, declB);
2204+
return result == Comparison::Better;
2205+
});
2206+
2207+
everythingElse.append(genericOverloads.begin(), genericOverloads.end());
2208+
21812209
appendPartition(favored);
21822210
appendPartition(everythingElse);
21832211
appendPartition(simdOperators);
2184-
2185-
// Now create the remaining partitions from what we previously collected.
21862212
appendPartition(unavailable);
21872213
appendPartition(disabled);
21882214

lib/Sema/CSStep.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "CSStep.h"
1919
#include "TypeChecker.h"
2020
#include "swift/AST/Types.h"
21+
#include "swift/AST/GenericEnvironment.h"
2122
#include "swift/Sema/ConstraintSystem.h"
2223
#include "llvm/ADT/ArrayRef.h"
2324
#include "llvm/ADT/SmallVector.h"
@@ -528,6 +529,60 @@ StepResult DisjunctionStep::resume(bool prevFailed) {
528529
return take(prevFailed);
529530
}
530531

532+
static bool isDeclSubstitutable(ValueDecl *declA, ValueDecl *declB) {
533+
auto *typeA = declA->getInterfaceType()->getAs<GenericFunctionType>();
534+
auto *typeB = declB->getInterfaceType()->getAs<GenericFunctionType>();
535+
536+
if (!typeA || !typeB)
537+
return false;
538+
539+
auto genericSignatureA = typeA->getGenericSignature();
540+
auto genericSignatureB = typeB->getGenericSignature();
541+
542+
// Substitute generic parameters with their archetypes in each generic function.
543+
Type substTypeA = typeA->substGenericArgs(
544+
genericSignatureA->getGenericEnvironment()->getForwardingSubstitutionMap());
545+
Type substTypeB = typeB->substGenericArgs(
546+
genericSignatureB->getGenericEnvironment()->getForwardingSubstitutionMap());
547+
548+
// Attempt to substitute archetypes from the second type with archetypes in the
549+
// same structural position in the first type.
550+
TypeSubstitutionMap substMap;
551+
substTypeB = substTypeB->substituteBindingsTo(substTypeA,
552+
[&](ArchetypeType *origType, CanType substType,
553+
ArchetypeType *, ArrayRef<ProtocolConformanceRef>) -> CanType {
554+
auto interfaceTy = origType->getInterfaceType()->getCanonicalType();
555+
substMap[interfaceTy->getAs<SubstitutableType>()] = substType;
556+
return substType;
557+
});
558+
559+
if (!substTypeB)
560+
return false;
561+
562+
auto result = TypeChecker::checkGenericArguments(
563+
declA->getDeclContext(), SourceLoc(), SourceLoc(), typeB,
564+
genericSignatureB->getGenericParams(),
565+
genericSignatureB->getRequirements(),
566+
QueryTypeSubstitutionMap{ substMap });
567+
568+
if (result != RequirementCheckResult::Success)
569+
return false;
570+
571+
return substTypeA->isEqual(substTypeB);
572+
}
573+
574+
static bool isGenericDisjunctionChoice(Constraint *constraint) {
575+
if (constraint->getKind() != ConstraintKind::BindOverload)
576+
return false;
577+
578+
auto choice = constraint->getOverloadChoice();
579+
if (!choice.isDecl())
580+
return false;
581+
582+
auto *funcDecl = dyn_cast<AbstractFunctionDecl>(choice.getDecl());
583+
return funcDecl && funcDecl->isGeneric();
584+
}
585+
531586
bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const {
532587
auto &ctx = CS.getASTContext();
533588

@@ -556,6 +611,19 @@ bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const {
556611
if (ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks)
557612
return false;
558613

614+
// If the solver already found a solution with a better overload choice that
615+
// can be unconditionally substituted by the current choice, skip the current
616+
// choice.
617+
if (LastSolvedChoice && isGenericDisjunctionChoice(choice)) {
618+
auto *declA = LastSolvedChoice->first->getOverloadChoice().getDecl();
619+
auto *declB = static_cast<Constraint *>(choice)->getOverloadChoice().getDecl();
620+
621+
if (TypeChecker::compareDeclarations(CS.DC, declA, declB) == Comparison::Better) {
622+
if (isDeclSubstitutable(declA, /*by=*/declB))
623+
return skip("subtype");
624+
}
625+
}
626+
559627
// If the solver already found a solution with a choice that did not
560628
// introduce any conversions (i.e., the score is not worse than the
561629
// current score), we can skip any generic operators with conformance

0 commit comments

Comments
 (0)