Skip to content

Commit 2646efa

Browse files
committed
[CSOptimizer] Expand literal support to bools, strings and dictionaries
Optimizer now covers all of the most common ExpressibleBy*Literal protocols.
1 parent 0e72686 commit 2646efa

File tree

1 file changed

+82
-55
lines changed

1 file changed

+82
-55
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ static bool isUnboundArrayType(Type type) {
6969
return false;
7070
}
7171

72+
static bool isUnboundDictionaryType(Type type) {
73+
if (auto *UGT = type->getAs<UnboundGenericType>())
74+
return UGT->getDecl() == type->getASTContext().getDictionaryDecl();
75+
return false;
76+
}
77+
7278
static bool isSupportedOperator(Constraint *disjunction) {
7379
if (!isOperatorDisjunction(disjunction))
7480
return false;
@@ -749,31 +755,17 @@ static void determineBestChoicesInContext(
749755
}
750756
}
751757

752-
// Match `[...]` to Array<...> and/or `ExpressibleByArrayLiteral`
753-
// conforming types.
754-
if (options.contains(MatchFlag::OnParam) &&
755-
options.contains(MatchFlag::Literal) &&
756-
isUnboundArrayType(candidateType)) {
758+
if (options.contains(MatchFlag::ExactOnly)) {
757759
// If an exact match is requested favor only `[...]` to `Array<...>`
758760
// since everything else is going to increase to score.
759-
if (options.contains(MatchFlag::ExactOnly))
760-
return paramType->isArray() ? 1 : 0;
761-
762-
// Otherwise, check if the other side conforms to
763-
// `ExpressibleByArrayLiteral` protocol (in some way).
764-
// We want an overly optimistic result here to avoid
765-
// under-favoring.
766-
auto &ctx = cs.getASTContext();
767-
return checkConformanceWithoutContext(
768-
paramType,
769-
ctx.getProtocol(
770-
KnownProtocolKind::ExpressibleByArrayLiteral),
771-
/*allowMissing=*/true)
772-
? 0.3
773-
: 0;
774-
}
761+
if (options.contains(MatchFlag::Literal)) {
762+
if (isUnboundArrayType(candidateType))
763+
return paramType->isArrayType() ? 0.3 : 0;
764+
765+
if (isUnboundDictionaryType(candidateType))
766+
return cs.isDictionaryType(paramType) ? 0.3 : 0;
767+
}
775768

776-
if (options.contains(MatchFlag::ExactOnly)) {
777769
if (!areEqual(candidateType, paramType))
778770
return 0;
779771
return options.contains(MatchFlag::Literal) ? 0.3 : 1;
@@ -785,39 +777,73 @@ static void determineBestChoicesInContext(
785777
}
786778

787779
if (options.contains(MatchFlag::Literal)) {
788-
if (candidateType->isInt() || candidateType->isDouble()) {
789-
if (paramType->hasTypeParameter() ||
790-
paramType->isAnyExistentialType()) {
791-
// Attempt to match literal default to generic parameter.
792-
// This helps to determine whether there are any generic
793-
// overloads that are a possible match.
794-
auto score =
795-
scoreCandidateMatch(genericSig, choice, candidateType,
796-
paramType, options - MatchFlag::Literal);
797-
if (score == 0)
798-
return 0;
799-
800-
// Optional injection lowers the score for operators to match
801-
// pre-optimizer behavior.
802-
return choice->isOperator() && paramType->getOptionalObjectType()
803-
? 0.2
804-
: 0.3;
805-
} else {
806-
// Integer and floating-point literals can match any parameter
807-
// type that conforms to `ExpressibleBy{Integer, Float}Literal`
808-
// protocol. Since this assessment is done in isolation we don't
809-
// lower the score even though this would be a non-default binding
810-
// for a literal.
811-
if (candidateType->isInt() &&
812-
TypeChecker::conformsToKnownProtocol(
813-
paramType, KnownProtocolKind::ExpressibleByIntegerLiteral))
814-
return 0.3;
815-
816-
if (candidateType->isDouble() &&
817-
TypeChecker::conformsToKnownProtocol(
818-
paramType, KnownProtocolKind::ExpressibleByFloatLiteral))
819-
return 0.3;
820-
}
780+
if (paramType->hasTypeParameter() ||
781+
paramType->isAnyExistentialType()) {
782+
// Attempt to match literal default to generic parameter.
783+
// This helps to determine whether there are any generic
784+
// overloads that are a possible match.
785+
auto score =
786+
scoreCandidateMatch(genericSig, choice, candidateType,
787+
paramType, options - MatchFlag::Literal);
788+
if (score == 0)
789+
return 0;
790+
791+
// Optional injection lowers the score for operators to match
792+
// pre-optimizer behavior.
793+
return choice->isOperator() && paramType->getOptionalObjectType()
794+
? 0.2
795+
: 0.3;
796+
} else {
797+
// Integer and floating-point literals can match any parameter
798+
// type that conforms to `ExpressibleBy{Integer, Float}Literal`
799+
// protocol. Since this assessment is done in isolation we don't
800+
// lower the score even though this would be a non-default binding
801+
// for a literal.
802+
if (candidateType->isInt() &&
803+
TypeChecker::conformsToKnownProtocol(
804+
paramType, KnownProtocolKind::ExpressibleByIntegerLiteral))
805+
return 0.3;
806+
807+
if (candidateType->isDouble() &&
808+
TypeChecker::conformsToKnownProtocol(
809+
paramType, KnownProtocolKind::ExpressibleByFloatLiteral))
810+
return 0.3;
811+
812+
if (candidateType->isBool() &&
813+
TypeChecker::conformsToKnownProtocol(
814+
paramType, KnownProtocolKind::ExpressibleByBooleanLiteral))
815+
return 0.3;
816+
817+
if (candidateType->isString() &&
818+
(TypeChecker::conformsToKnownProtocol(
819+
paramType, KnownProtocolKind::ExpressibleByStringLiteral) ||
820+
TypeChecker::conformsToKnownProtocol(
821+
paramType,
822+
KnownProtocolKind::ExpressibleByStringInterpolation)))
823+
return 0.3;
824+
825+
auto &ctx = cs.getASTContext();
826+
827+
// Check if the other side conforms to `ExpressibleByArrayLiteral`
828+
// protocol (in some way). We want an overly optimistic result
829+
// here to avoid under-favoring.
830+
if (candidateType->isArray() &&
831+
checkConformanceWithoutContext(
832+
paramType,
833+
ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral),
834+
/*allowMissing=*/true))
835+
return 0.3;
836+
837+
// Check if the other side conforms to
838+
// `ExpressibleByDictionaryLiteral` protocol (in some way).
839+
// We want an overly optimistic result here to avoid under-favoring.
840+
if (candidateType->isDictionary() &&
841+
checkConformanceWithoutContext(
842+
paramType,
843+
ctx.getProtocol(
844+
KnownProtocolKind::ExpressibleByDictionaryLiteral),
845+
/*allowMissing=*/true))
846+
return 0.3;
821847
}
822848

823849
return 0;
@@ -896,6 +922,7 @@ static void determineBestChoicesInContext(
896922
// dependent member type (i.e. `Self.T`), let's check conformances
897923
// only and lower the score.
898924
if (candidateType->hasTypeVariable() ||
925+
candidateType->hasUnboundGenericType() ||
899926
paramType->is<DependentMemberType>()) {
900927
return checkProtocolRequirementsOnly();
901928
}

0 commit comments

Comments
 (0)