Skip to content

Commit e30587b

Browse files
committed
[CSOptimizer] A more comprehensive generic overload checking when candidates are fully resolved
Use `checkGenericRequirements` to check whether all of the requirements placed on a particular parameter match, that gives us a lot of confidence that the overload should be favored.
1 parent a3a3ec4 commit e30587b

File tree

1 file changed

+59
-20
lines changed

1 file changed

+59
-20
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,13 @@ static Constraint *determineBestChoicesInContext(
479479

480480
if (!candidateOptionals.empty() || !paramOptionals.empty()) {
481481
if (paramOptionals.size() >= candidateOptionals.size()) {
482-
return scoreCandidateMatch(genericSig, candidateType, paramType,
483-
options);
482+
auto score = scoreCandidateMatch(genericSig, candidateType,
483+
paramType, options);
484+
// Injection lowers the score slightly to comply with
485+
// old behavior where exact matches on operator parameter
486+
// types were always preferred.
487+
return score == 1 && isOperatorDisjunction(disjunction) ? 0.9
488+
: score;
484489
}
485490

486491
// Optionality mismatch.
@@ -512,26 +517,60 @@ static Constraint *determineBestChoicesInContext(
512517

513518
// Check protocol requirement(s) if this parameter is a
514519
// generic parameter type.
515-
if (genericSig && paramType->isTypeParameter()) {
516-
auto protocolRequirements = genericSig->getRequiredProtocols(paramType);
517-
// It's a generic parameter or dependent member which might
518-
// be connected via ame-type constraints to other generic
519-
// parameters or dependent member but we cannot check that here,
520-
// so let's add a tiny score just to acknowledge that it could
521-
// possibly match.
522-
if (protocolRequirements.empty())
523-
return 0.01;
524-
525-
if (llvm::all_of(protocolRequirements, [&](ProtocolDecl *protocol) {
526-
return bool(cs.lookupConformance(candidateType, protocol));
527-
})) {
528-
if (auto *GP = paramType->getAs<GenericTypeParamType>()) {
529-
auto *paramDecl = GP->getDecl();
530-
if (paramDecl && paramDecl->isOpaqueType())
531-
return 1.0;
520+
if (genericSig && paramType->is<GenericTypeParamType>()) {
521+
// If candidate is not fully resolved, check conformances only
522+
// and lower the score.
523+
if (candidateType->hasTypeVariable()) {
524+
auto protocolRequirements =
525+
genericSig->getRequiredProtocols(paramType);
526+
if (llvm::all_of(protocolRequirements, [&](ProtocolDecl *protocol) {
527+
return bool(cs.lookupConformance(candidateType, protocol));
528+
})) {
529+
if (auto *GP = paramType->getAs<GenericTypeParamType>()) {
530+
auto *paramDecl = GP->getDecl();
531+
if (paramDecl && paramDecl->isOpaqueType())
532+
return 1.0;
533+
}
534+
return 0.7;
532535
}
533-
return 0.7;
536+
537+
return 0;
538+
}
539+
540+
// If the candidate type is fully resolved, let's check all of
541+
// the requirements that are associated with the corresponding
542+
// parameter, if all of them are satisfied this candidate is
543+
// an exact match.
544+
545+
auto isParameterType = [&paramType](Type type) {
546+
return type->isEqual(paramType);
547+
};
548+
549+
SmallVector<Requirement, 4> requirements;
550+
for (const auto &requirement : genericSig.getRequirements()) {
551+
if (requirement.getFirstType().findIf(isParameterType) ||
552+
(requirement.getKind() != RequirementKind::Layout &&
553+
requirement.getSecondType().findIf(isParameterType)))
554+
requirements.push_back(requirement);
534555
}
556+
557+
auto result = checkRequirements(
558+
requirements,
559+
[&paramType, &candidateType](SubstitutableType *type) -> Type {
560+
if (type->isEqual(paramType))
561+
return candidateType;
562+
return ErrorType::get(type);
563+
},
564+
SubstOptions(std::nullopt));
565+
566+
// Concrete operator overloads are always more preferable to
567+
// generic ones if there are exact or subtype matches, for
568+
// everything else the solver should try both concrete and
569+
// generic and disambiguate during ranking.
570+
if (result == CheckRequirementsResult::Success)
571+
return isOperatorDisjunction(disjunction) ? 0.9 : 1.0;
572+
573+
return 0;
535574
}
536575

537576
// Parameter is generic, let's check whether top-level

0 commit comments

Comments
 (0)