[CSOptimizer] Avoid favoring overloads that mismatch context on async

This is a fix for the ported "calls with a single unlabeled argument"
hack. If overload doesn't match context on async effect, let's not favor
it because that is more important than defaulted parameters.

Resolves: rdar://164269641
This commit is contained in:
Pavel Yaskevich
2025-11-07 13:59:28 -08:00
parent 4e0484201d
commit 513caf2c9b
2 changed files with 41 additions and 2 deletions

View File

@@ -17,6 +17,7 @@
#include "OpenedExistentials.h"
#include "TypeChecker.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericSignature.h"
@@ -768,7 +769,7 @@ static std::optional<DisjunctionInfo> preserveFavoringOfUnlabeledUnaryArgument(
SmallVector<Constraint *, 2> favoredChoices;
forEachDisjunctionChoice(
cs, disjunction,
[&argumentType, &favoredChoices, &argument](
[&cs, &argumentType, &favoredChoices, &argument](
Constraint *choice, ValueDecl *decl, FunctionType *overloadType) {
if (decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>())
return;
@@ -784,8 +785,15 @@ static std::optional<DisjunctionInfo> preserveFavoringOfUnlabeledUnaryArgument(
(isa<LiteralExpr>(argument) || isa<BinaryExpr>(argument)))
return;
if (argumentType->isEqual(param.getPlainType()))
if (argumentType->isEqual(param.getPlainType())) {
if (auto *func = dyn_cast<AbstractFunctionDecl>(decl)) {
if (func->isAsyncContext() !=
cs.isAsynchronousContext(choice->getDeclContext()))
return;
}
favoredChoices.push_back(choice);
}
});
return DisjunctionInfoBuilder(/*score=*/favoredChoices.empty() ? 0 : 1,

View File

@@ -383,3 +383,34 @@ do {
}
}
}
// Calls with single unlabeled arguments shouldn't favor overloads that don't match on async.
do {
struct V {
var data: Int = 0
}
func test(_: Int) -> Int { 42 }
func test(_: Int, v: Int = 42) async -> V? { nil }
func doAsync<T>(_ fn: () async -> T) async -> T { await fn() }
func computeAsync(v: Int) async {
let v1 = await test(v)
if let v1 {
_ = v1.data // Ok
}
let v2 = await doAsync { await test(v) }
if let v2 {
_ = v2.data // Ok
}
_ = await doAsync {
let v = await test(v)
if let v {
_ = v.data // Ok
}
}
}
}