diff --git a/lib/Sema/CSOptimizer.cpp b/lib/Sema/CSOptimizer.cpp index 2f1fd1cb565..6e31d135b0d 100644 --- a/lib/Sema/CSOptimizer.cpp +++ b/lib/Sema/CSOptimizer.cpp @@ -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(GenericSignature, ValueDecl *, Type, - Type, MatchOptions)> + std::function(GenericSignature, ValueDecl *, + std::optional, Type, Type, + MatchOptions)> scoreCandidateMatch = [&](GenericSignature genericSig, ValueDecl *choice, - Type candidateType, Type paramType, - MatchOptions options) -> std::optional { + std::optional paramIdx, Type candidateType, + Type paramType, MatchOptions options) -> std::optional { 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()) || 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()) { + 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; diff --git a/test/Constraints/opened_existentials.swift b/test/Constraints/opened_existentials.swift index ebbcae1227f..ae28b515b62 100644 --- a/test/Constraints/opened_existentials.swift +++ b/test/Constraints/opened_existentials.swift @@ -591,3 +591,18 @@ protocol PP3 { associatedtype A } +protocol PP4 { +} + +do { + func test(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) + } +} diff --git a/validation-test/compiler_crashers_2/cad1ff6235b5b3d.swift b/validation-test/compiler_crashers_2_fixed/cad1ff6235b5b3d.swift similarity index 82% rename from validation-test/compiler_crashers_2/cad1ff6235b5b3d.swift rename to validation-test/compiler_crashers_2_fixed/cad1ff6235b5b3d.swift index 05625423a68..66d43cda3d2 100644 --- a/validation-test/compiler_crashers_2/cad1ff6235b5b3d.swift +++ b/validation-test/compiler_crashers_2_fixed/cad1ff6235b5b3d.swift @@ -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