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 "TypeChecker.h"
|
||||||
|
#include "OpenedExistentials.h"
|
||||||
#include "swift/AST/ConformanceLookup.h"
|
#include "swift/AST/ConformanceLookup.h"
|
||||||
#include "swift/AST/ExistentialLayout.h"
|
#include "swift/AST/ExistentialLayout.h"
|
||||||
#include "swift/AST/GenericSignature.h"
|
#include "swift/AST/GenericSignature.h"
|
||||||
@@ -1197,18 +1198,20 @@ static void determineBestChoicesInContext(
|
|||||||
// - Superclass conversion
|
// - Superclass conversion
|
||||||
// - Array-to-pointer conversion
|
// - Array-to-pointer conversion
|
||||||
// - Value to existential conversion
|
// - Value to existential conversion
|
||||||
|
// - Existential opening
|
||||||
// - Exact match on top-level types
|
// - Exact match on top-level types
|
||||||
//
|
//
|
||||||
// In situations when it's not possible to determine whether a candidate
|
// In situations when it's not possible to determine whether a candidate
|
||||||
// type matches a parameter type (i.e. when partially resolved generic
|
// type matches a parameter type (i.e. when partially resolved generic
|
||||||
// types are matched) this function is going to produce \c std::nullopt
|
// types are matched) this function is going to produce \c std::nullopt
|
||||||
// instead of `0` that indicates "not a match".
|
// instead of `0` that indicates "not a match".
|
||||||
std::function<std::optional<double>(GenericSignature, ValueDecl *, Type,
|
std::function<std::optional<double>(GenericSignature, ValueDecl *,
|
||||||
Type, MatchOptions)>
|
std::optional<unsigned>, Type, Type,
|
||||||
|
MatchOptions)>
|
||||||
scoreCandidateMatch =
|
scoreCandidateMatch =
|
||||||
[&](GenericSignature genericSig, ValueDecl *choice,
|
[&](GenericSignature genericSig, ValueDecl *choice,
|
||||||
Type candidateType, Type paramType,
|
std::optional<unsigned> paramIdx, Type candidateType,
|
||||||
MatchOptions options) -> std::optional<double> {
|
Type paramType, MatchOptions options) -> std::optional<double> {
|
||||||
auto areEqual = [&](Type a, Type b) {
|
auto areEqual = [&](Type a, Type b) {
|
||||||
return a->getDesugaredType()->isEqual(b->getDesugaredType());
|
return a->getDesugaredType()->isEqual(b->getDesugaredType());
|
||||||
};
|
};
|
||||||
@@ -1260,7 +1263,7 @@ static void determineBestChoicesInContext(
|
|||||||
// This helps to determine whether there are any generic
|
// This helps to determine whether there are any generic
|
||||||
// overloads that are a possible match.
|
// overloads that are a possible match.
|
||||||
auto score =
|
auto score =
|
||||||
scoreCandidateMatch(genericSig, choice, candidateType,
|
scoreCandidateMatch(genericSig, choice, paramIdx, candidateType,
|
||||||
paramType, options - MatchFlag::Literal);
|
paramType, options - MatchFlag::Literal);
|
||||||
if (score == 0)
|
if (score == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1347,8 +1350,8 @@ static void determineBestChoicesInContext(
|
|||||||
if ((paramOptionals.empty() &&
|
if ((paramOptionals.empty() &&
|
||||||
paramType->is<GenericTypeParamType>()) ||
|
paramType->is<GenericTypeParamType>()) ||
|
||||||
paramOptionals.size() >= candidateOptionals.size()) {
|
paramOptionals.size() >= candidateOptionals.size()) {
|
||||||
auto score = scoreCandidateMatch(genericSig, choice, candidateType,
|
auto score = scoreCandidateMatch(genericSig, choice, paramIdx,
|
||||||
paramType, options);
|
candidateType, paramType, options);
|
||||||
|
|
||||||
if (score > 0) {
|
if (score > 0) {
|
||||||
// Injection lowers the score slightly to comply with
|
// Injection lowers the score slightly to comply with
|
||||||
@@ -1394,6 +1397,21 @@ static void determineBestChoicesInContext(
|
|||||||
if (paramType->isAny())
|
if (paramType->isAny())
|
||||||
return 1;
|
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
|
// Check protocol requirement(s) if this parameter is a
|
||||||
// generic parameter type.
|
// generic parameter type.
|
||||||
if (genericSig && paramType->isTypeParameter()) {
|
if (genericSig && paramType->isTypeParameter()) {
|
||||||
@@ -1661,9 +1679,10 @@ static void determineBestChoicesInContext(
|
|||||||
options |= MatchFlag::StringInterpolation;
|
options |= MatchFlag::StringInterpolation;
|
||||||
|
|
||||||
// The specifier for a candidate only matters for `inout` check.
|
// The specifier for a candidate only matters for `inout` check.
|
||||||
auto candidateScore = scoreCandidateMatch(
|
auto candidateScore =
|
||||||
genericSig, decl, candidate.type->getWithoutSpecifierType(),
|
scoreCandidateMatch(genericSig, decl, paramIdx,
|
||||||
paramType, options);
|
candidate.type->getWithoutSpecifierType(),
|
||||||
|
paramType, options);
|
||||||
|
|
||||||
if (!candidateScore)
|
if (!candidateScore)
|
||||||
continue;
|
continue;
|
||||||
@@ -1706,6 +1725,7 @@ static void determineBestChoicesInContext(
|
|||||||
(score > 0 || !hasArgumentCandidates)) {
|
(score > 0 || !hasArgumentCandidates)) {
|
||||||
if (llvm::any_of(resultTypes, [&](const Type candidateResultTy) {
|
if (llvm::any_of(resultTypes, [&](const Type candidateResultTy) {
|
||||||
return scoreCandidateMatch(genericSig, decl,
|
return scoreCandidateMatch(genericSig, decl,
|
||||||
|
/*paramIdx=*/std::nullopt,
|
||||||
overloadType->getResult(),
|
overloadType->getResult(),
|
||||||
candidateResultTy,
|
candidateResultTy,
|
||||||
/*options=*/{}) > 0;
|
/*options=*/{}) > 0;
|
||||||
|
|||||||
@@ -591,3 +591,18 @@ protocol PP3 {
|
|||||||
associatedtype A
|
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"}
|
// {"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
|
print($0) $00 + 0. / 1
|
||||||
Reference in New Issue
Block a user