Skip to content

Commit 5aa3859

Browse files
committed
[CSOptimizer] Disable unary argument hack if overload set has requirements or variadic overloads
This matches the behavior of the old hack where favoring choices were rolled back if `mustConsider` produced `true` which happened only for protocol requirements and variadic overload choice regardless of their viability.
1 parent e280569 commit 5aa3859

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,20 @@ static unsigned numOverloadChoicesMatchingOnArity(OverloadedDeclRefExpr *ODRE,
529529
});
530530
}
531531

532+
static bool isVariadicGenericOverload(ValueDecl *choice) {
533+
auto genericContext = choice->getAsGenericContext();
534+
if (!genericContext)
535+
return false;
536+
537+
auto *GPL = genericContext->getGenericParams();
538+
if (!GPL)
539+
return false;
540+
541+
return llvm::any_of(GPL->getParams(), [&](const GenericTypeParamDecl *GP) {
542+
return GP->isParameterPack();
543+
});
544+
}
545+
532546
/// This maintains an "old hack" behavior where overloads of some
533547
/// `OverloadedDeclRef` calls were favored purely based on number of
534548
/// argument and (non-defaulted) parameters matching.
@@ -542,20 +556,6 @@ static void findFavoredChoicesBasedOnArity(
542556
if (numOverloadChoicesMatchingOnArity(ODRE, argumentList) > 1)
543557
return;
544558

545-
auto isVariadicGenericOverload = [&](ValueDecl *choice) {
546-
auto genericContext = choice->getAsGenericContext();
547-
if (!genericContext)
548-
return false;
549-
550-
auto *GPL = genericContext->getGenericParams();
551-
if (!GPL)
552-
return false;
553-
554-
return llvm::any_of(GPL->getParams(), [&](const GenericTypeParamDecl *GP) {
555-
return GP->isParameterPack();
556-
});
557-
};
558-
559559
bool hasVariadicGenerics = false;
560560
SmallVector<Constraint *> favored;
561561

@@ -591,6 +591,21 @@ static std::optional<DisjunctionInfo> preserveFavoringOfUnlabeledUnaryArgument(
591591
cs.getParentExpr(argumentList->getUnlabeledUnaryExpr())))
592592
return std::nullopt;
593593

594+
// The hack rolled back favoring choices if one of the overloads was a
595+
// protocol requirement or variadic generic.
596+
//
597+
// Note that it doesn't matter whether such overload choices are viable
598+
// or not, their presence disabled this "optimization".
599+
if (llvm::any_of(disjunction->getNestedConstraints(), [](Constraint *choice) {
600+
auto *decl = getOverloadChoiceDecl(choice);
601+
if (!decl)
602+
return false;
603+
604+
return isa<ProtocolDecl>(decl->getDeclContext()) ||
605+
isVariadicGenericOverload(decl);
606+
}))
607+
return std::nullopt;
608+
594609
auto ODRE = isOverloadedDeclRef(disjunction);
595610
bool preserveFavoringOfUnlabeledUnaryArgument =
596611
!ODRE || numOverloadChoicesMatchingOnArity(ODRE, argumentList) < 2;

test/Constraints/old_hack_related_ambiguities.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,39 @@ func test_that_unary_argument_hacks_do_not_apply_to_subscripts(dict: [String: My
290290
let value = dict["hello"]
291291
let _: MyValue? = value // Ok
292292
}
293+
294+
// Unlabeled unary argument hack was disabled if there were any protocol requirements
295+
// or variadic generic overloads present in the result set (regadless of their viability).
296+
//
297+
// Remove the requirement and variadic overloads and this code would start failing even
298+
// though it shouldn't!
299+
300+
struct Future<T> {
301+
}
302+
303+
protocol DB {
304+
func get(_: Int, _: Int) -> Future<Int?>
305+
}
306+
307+
extension DB {
308+
func get(_: Int, _: Int = 42) async throws -> Int? { nil }
309+
func get(_: Int) -> Future<Int?> { .init() }
310+
311+
func fetch(_: Int, _: Int = 42) async throws -> Int? { nil }
312+
func fetch(_: Int) -> Future<Int?> { .init() }
313+
func fetch<each T>(values: repeat each T) -> Int { 42 }
314+
}
315+
316+
struct TestUnary {
317+
var db: any DB
318+
319+
func get(v: Int) async throws {
320+
guard let _ = try await self.db.get(v) else { // Ok
321+
return
322+
}
323+
324+
guard let _ = try await self.db.fetch(v) else { // Ok
325+
return
326+
}
327+
}
328+
}

0 commit comments

Comments
 (0)