mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TypeChecker.h"
|
||||
#include "OpenedExistentials.h"
|
||||
#include "swift/AST/ConformanceLookup.h"
|
||||
#include "swift/AST/ExistentialLayout.h"
|
||||
#include "swift/AST/GenericSignature.h"
|
||||
@@ -1197,18 +1198,20 @@ static void determineBestChoicesInContext(
|
||||
// - Superclass conversion
|
||||
// - Array-to-pointer conversion
|
||||
// - Value to existential conversion
|
||||
// - Existential opening
|
||||
// - Exact match on top-level types
|
||||
//
|
||||
// In situations when it's not possible to determine whether a candidate
|
||||
// type matches a parameter type (i.e. when partially resolved generic
|
||||
// types are matched) this function is going to produce \c std::nullopt
|
||||
// instead of `0` that indicates "not a match".
|
||||
std::function<std::optional<double>(GenericSignature, ValueDecl *, Type,
|
||||
Type, MatchOptions)>
|
||||
std::function<std::optional<double>(GenericSignature, ValueDecl *,
|
||||
std::optional<unsigned>, Type, Type,
|
||||
MatchOptions)>
|
||||
scoreCandidateMatch =
|
||||
[&](GenericSignature genericSig, ValueDecl *choice,
|
||||
Type candidateType, Type paramType,
|
||||
MatchOptions options) -> std::optional<double> {
|
||||
std::optional<unsigned> paramIdx, Type candidateType,
|
||||
Type paramType, MatchOptions options) -> std::optional<double> {
|
||||
auto areEqual = [&](Type a, Type b) {
|
||||
return a->getDesugaredType()->isEqual(b->getDesugaredType());
|
||||
};
|
||||
@@ -1260,7 +1263,7 @@ static void determineBestChoicesInContext(
|
||||
// This helps to determine whether there are any generic
|
||||
// overloads that are a possible match.
|
||||
auto score =
|
||||
scoreCandidateMatch(genericSig, choice, candidateType,
|
||||
scoreCandidateMatch(genericSig, choice, paramIdx, candidateType,
|
||||
paramType, options - MatchFlag::Literal);
|
||||
if (score == 0)
|
||||
return 0;
|
||||
@@ -1347,8 +1350,8 @@ static void determineBestChoicesInContext(
|
||||
if ((paramOptionals.empty() &&
|
||||
paramType->is<GenericTypeParamType>()) ||
|
||||
paramOptionals.size() >= candidateOptionals.size()) {
|
||||
auto score = scoreCandidateMatch(genericSig, choice, candidateType,
|
||||
paramType, options);
|
||||
auto score = scoreCandidateMatch(genericSig, choice, paramIdx,
|
||||
candidateType, paramType, options);
|
||||
|
||||
if (score > 0) {
|
||||
// Injection lowers the score slightly to comply with
|
||||
@@ -1394,6 +1397,21 @@ static void determineBestChoicesInContext(
|
||||
if (paramType->isAny())
|
||||
return 1;
|
||||
|
||||
// Check if a candidate could be matched to a parameter by
|
||||
// an existential opening.
|
||||
if (options.contains(MatchFlag::OnParam) &&
|
||||
candidateType->getMetatypeInstanceType()->isExistentialType()) {
|
||||
if (auto *genericParam = paramType->getMetatypeInstanceType()
|
||||
->getAs<GenericTypeParamType>()) {
|
||||
if (canOpenExistentialAt(choice, *paramIdx, genericParam,
|
||||
candidateType->getMetatypeInstanceType())) {
|
||||
// Lower the score slightly for operators to make sure that
|
||||
// concrete overloads are always preferred over generic ones.
|
||||
return choice->isOperator() ? 0.9 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check protocol requirement(s) if this parameter is a
|
||||
// generic parameter type.
|
||||
if (genericSig && paramType->isTypeParameter()) {
|
||||
@@ -1661,9 +1679,10 @@ static void determineBestChoicesInContext(
|
||||
options |= MatchFlag::StringInterpolation;
|
||||
|
||||
// The specifier for a candidate only matters for `inout` check.
|
||||
auto candidateScore = scoreCandidateMatch(
|
||||
genericSig, decl, candidate.type->getWithoutSpecifierType(),
|
||||
paramType, options);
|
||||
auto candidateScore =
|
||||
scoreCandidateMatch(genericSig, decl, paramIdx,
|
||||
candidate.type->getWithoutSpecifierType(),
|
||||
paramType, options);
|
||||
|
||||
if (!candidateScore)
|
||||
continue;
|
||||
@@ -1706,6 +1725,7 @@ static void determineBestChoicesInContext(
|
||||
(score > 0 || !hasArgumentCandidates)) {
|
||||
if (llvm::any_of(resultTypes, [&](const Type candidateResultTy) {
|
||||
return scoreCandidateMatch(genericSig, decl,
|
||||
/*paramIdx=*/std::nullopt,
|
||||
overloadType->getResult(),
|
||||
candidateResultTy,
|
||||
/*options=*/{}) > 0;
|
||||
|
||||
@@ -591,3 +591,18 @@ protocol PP3 {
|
||||
associatedtype A
|
||||
}
|
||||
|
||||
protocol PP4 {
|
||||
}
|
||||
|
||||
do {
|
||||
func test<T>(env: T) where T: PP4 {}
|
||||
|
||||
func test(env: PP4? = nil) {
|
||||
guard let env else {
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
|
||||
test(env: env)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::isArgumentGenericFunction(swift::Type, swift::Expr*)","signatureAssert":"Assertion failed: (!getFixedType(tyvar)), function getUnboundBindOverloadDisjunction"}
|
||||
// RUN: not --crash %target-swift-frontend -typecheck %s
|
||||
// RUN: not %target-swift-frontend -typecheck %s
|
||||
{
|
||||
print($0) $00 + 0. / 1
|
||||
Reference in New Issue
Block a user