Skip to content

Commit 15c773b

Browse files
committed
[CSOptimizer] Make a light-weight generic overload check if some requirements are unsatisfiable
If some of the requirements of a generic overload reference other generic parameters, the optimizer won't be able to satisfy them because it only has candidates for one (current) parameter. In cases like that, let's fallback to a light-weight protocol conformance check instead of skipping an overload choice altogether.
1 parent c2a5588 commit 15c773b

File tree

2 files changed

+68
-17
lines changed

2 files changed

+68
-17
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -555,11 +555,9 @@ static void determineBestChoicesInContext(
555555
// Check protocol requirement(s) if this parameter is a
556556
// generic parameter type.
557557
if (genericSig && paramType->isTypeParameter()) {
558-
// If candidate is not fully resolved or is matched against a
559-
// dependent member type (i.e. `Self.T`), let's check conformances
560-
// only and lower the score.
561-
if (candidateType->hasTypeVariable() ||
562-
paramType->is<DependentMemberType>()) {
558+
// Light-weight check if cases where `checkRequirements` is not
559+
// applicable.
560+
auto checkProtocolRequirementsOnly = [&]() -> double {
563561
auto protocolRequirements =
564562
genericSig->getRequiredProtocols(paramType);
565563
if (llvm::all_of(protocolRequirements, [&](ProtocolDecl *protocol) {
@@ -574,29 +572,64 @@ static void determineBestChoicesInContext(
574572
}
575573

576574
return 0;
575+
};
576+
577+
// If candidate is not fully resolved or is matched against a
578+
// dependent member type (i.e. `Self.T`), let's check conformances
579+
// only and lower the score.
580+
if (candidateType->hasTypeVariable() ||
581+
paramType->is<DependentMemberType>()) {
582+
return checkProtocolRequirementsOnly();
577583
}
578584

579585
// Cannot match anything but generic type parameters here.
580586
if (!paramType->is<GenericTypeParamType>())
581587
return std::nullopt;
582588

583-
// If the candidate type is fully resolved, let's check all of
584-
// the requirements that are associated with the corresponding
585-
// parameter, if all of them are satisfied this candidate is
586-
// an exact match.
587-
588-
auto isParameterType = [&paramType](Type type) {
589-
return type->isEqual(paramType);
590-
};
591-
589+
bool hasUnsatisfiableRequirements = false;
592590
SmallVector<Requirement, 4> requirements;
591+
593592
for (const auto &requirement : genericSig.getRequirements()) {
594-
if (requirement.getFirstType().findIf(isParameterType) ||
595-
(requirement.getKind() != RequirementKind::Layout &&
596-
requirement.getSecondType().findIf(isParameterType)))
593+
if (hasUnsatisfiableRequirements)
594+
break;
595+
596+
llvm::SmallPtrSet<GenericTypeParamType *, 2> toExamine;
597+
598+
auto recordReferencesGenericParams = [&toExamine](Type type) {
599+
type.visit([&toExamine](Type innerTy) {
600+
if (auto *GP = innerTy->getAs<GenericTypeParamType>())
601+
toExamine.insert(GP);
602+
});
603+
};
604+
605+
recordReferencesGenericParams(requirement.getFirstType());
606+
607+
if (requirement.getKind() != RequirementKind::Layout)
608+
recordReferencesGenericParams(requirement.getSecondType());
609+
610+
if (llvm::any_of(toExamine, [&](GenericTypeParamType *GP) {
611+
return paramType->isEqual(GP);
612+
})) {
597613
requirements.push_back(requirement);
614+
// If requirement mentions other generic parameters
615+
// `checkRequirements` would because we don't have
616+
// candidate substitutions for anything but the current
617+
// parameter type.
618+
hasUnsatisfiableRequirements |= toExamine.size() > 1;
619+
}
598620
}
599621

622+
// If some of the requirements cannot be satisfied, because
623+
// they reference other generic parameters, for example:
624+
// `<T, U, where T.Element == U.Element>`, let's perform a
625+
// light-weight check instead of skipping this overload choice.
626+
if (hasUnsatisfiableRequirements)
627+
return checkProtocolRequirementsOnly();
628+
629+
// If the candidate type is fully resolved, let's check all of
630+
// the requirements that are associated with the corresponding
631+
// parameter, if all of them are satisfied this candidate is
632+
// an exact match.
600633
auto result = checkRequirements(
601634
requirements,
602635
[&paramType, &candidateType](SubstitutableType *type) -> Type {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s -Xfrontend=-typecheck
2+
// REQUIRES: asserts, no_asan
3+
4+
struct Value: RandomAccessCollection, RangeReplaceableCollection {
5+
let startIndex = 0
6+
let endIndex = 0
7+
8+
subscript(_: Int) -> Int { 0 }
9+
10+
func replaceSubrange<C: Collection>(_: Range<Int>, with: C) {}
11+
}
12+
13+
func f(v: Value) {
14+
let _ = v
15+
%for i in range(0, N):
16+
+ v
17+
%end
18+
}

0 commit comments

Comments
 (0)