Skip to content

Commit 0d33af7

Browse files
committed
[CSOptimizer] Add support for opened existential arguments
Check whether it would be possible to match a parameter type by opening an existential type of the candidate argument. Resolves: rdar://158159462
1 parent 90224bf commit 0d33af7

File tree

3 files changed

+46
-11
lines changed

3 files changed

+46
-11
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "TypeChecker.h"
18+
#include "OpenedExistentials.h"
1819
#include "swift/AST/ConformanceLookup.h"
1920
#include "swift/AST/ExistentialLayout.h"
2021
#include "swift/AST/GenericSignature.h"
@@ -1197,18 +1198,20 @@ static void determineBestChoicesInContext(
11971198
// - Superclass conversion
11981199
// - Array-to-pointer conversion
11991200
// - Value to existential conversion
1201+
// - Existential opening
12001202
// - Exact match on top-level types
12011203
//
12021204
// In situations when it's not possible to determine whether a candidate
12031205
// type matches a parameter type (i.e. when partially resolved generic
12041206
// types are matched) this function is going to produce \c std::nullopt
12051207
// instead of `0` that indicates "not a match".
1206-
std::function<std::optional<double>(GenericSignature, ValueDecl *, Type,
1207-
Type, MatchOptions)>
1208+
std::function<std::optional<double>(GenericSignature, ValueDecl *,
1209+
std::optional<unsigned>, Type, Type,
1210+
MatchOptions)>
12081211
scoreCandidateMatch =
12091212
[&](GenericSignature genericSig, ValueDecl *choice,
1210-
Type candidateType, Type paramType,
1211-
MatchOptions options) -> std::optional<double> {
1213+
std::optional<unsigned> paramIdx, Type candidateType,
1214+
Type paramType, MatchOptions options) -> std::optional<double> {
12121215
auto areEqual = [&](Type a, Type b) {
12131216
return a->getDesugaredType()->isEqual(b->getDesugaredType());
12141217
};
@@ -1260,7 +1263,7 @@ static void determineBestChoicesInContext(
12601263
// This helps to determine whether there are any generic
12611264
// overloads that are a possible match.
12621265
auto score =
1263-
scoreCandidateMatch(genericSig, choice, candidateType,
1266+
scoreCandidateMatch(genericSig, choice, paramIdx, candidateType,
12641267
paramType, options - MatchFlag::Literal);
12651268
if (score == 0)
12661269
return 0;
@@ -1347,8 +1350,8 @@ static void determineBestChoicesInContext(
13471350
if ((paramOptionals.empty() &&
13481351
paramType->is<GenericTypeParamType>()) ||
13491352
paramOptionals.size() >= candidateOptionals.size()) {
1350-
auto score = scoreCandidateMatch(genericSig, choice, candidateType,
1351-
paramType, options);
1353+
auto score = scoreCandidateMatch(genericSig, choice, paramIdx,
1354+
candidateType, paramType, options);
13521355

13531356
if (score > 0) {
13541357
// Injection lowers the score slightly to comply with
@@ -1394,6 +1397,21 @@ static void determineBestChoicesInContext(
13941397
if (paramType->isAny())
13951398
return 1;
13961399

1400+
// Check if a candidate could be matched to a parameter by
1401+
// an existential opening.
1402+
if (options.contains(MatchFlag::OnParam) &&
1403+
candidateType->getMetatypeInstanceType()->isExistentialType()) {
1404+
if (auto *genericParam = paramType->getMetatypeInstanceType()
1405+
->getAs<GenericTypeParamType>()) {
1406+
if (canOpenExistentialAt(choice, *paramIdx, genericParam,
1407+
candidateType->getMetatypeInstanceType())) {
1408+
// Lower the score slightly for operators to make sure that
1409+
// concrete overloads are always preferred over generic ones.
1410+
return choice->isOperator() ? 0.9 : 1;
1411+
}
1412+
}
1413+
}
1414+
13971415
// Check protocol requirement(s) if this parameter is a
13981416
// generic parameter type.
13991417
if (genericSig && paramType->isTypeParameter()) {
@@ -1661,9 +1679,10 @@ static void determineBestChoicesInContext(
16611679
options |= MatchFlag::StringInterpolation;
16621680

16631681
// The specifier for a candidate only matters for `inout` check.
1664-
auto candidateScore = scoreCandidateMatch(
1665-
genericSig, decl, candidate.type->getWithoutSpecifierType(),
1666-
paramType, options);
1682+
auto candidateScore =
1683+
scoreCandidateMatch(genericSig, decl, paramIdx,
1684+
candidate.type->getWithoutSpecifierType(),
1685+
paramType, options);
16671686

16681687
if (!candidateScore)
16691688
continue;
@@ -1706,6 +1725,7 @@ static void determineBestChoicesInContext(
17061725
(score > 0 || !hasArgumentCandidates)) {
17071726
if (llvm::any_of(resultTypes, [&](const Type candidateResultTy) {
17081727
return scoreCandidateMatch(genericSig, decl,
1728+
/*paramIdx=*/std::nullopt,
17091729
overloadType->getResult(),
17101730
candidateResultTy,
17111731
/*options=*/{}) > 0;

test/Constraints/opened_existentials.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,3 +591,18 @@ protocol PP3 {
591591
associatedtype A
592592
}
593593

594+
protocol PP4 {
595+
}
596+
597+
do {
598+
func test<T>(env: T) where T: PP4 {}
599+
600+
func test(env: PP4? = nil) {
601+
guard let env else {
602+
return
603+
}
604+
605+
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
606+
test(env: env)
607+
}
608+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::isArgumentGenericFunction(swift::Type, swift::Expr*)","signatureAssert":"Assertion failed: (!getFixedType(tyvar)), function getUnboundBindOverloadDisjunction"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
{
44
print($0) $00 + 0. / 1

0 commit comments

Comments
 (0)