Skip to content

Commit b264e27

Browse files
committed
[CSOptimizer] A narrow fix for nil coalescing operator optimization
`??` is overloaded on optionality of the second parameter, prevent ranking the argument candidates for this parameter if there are candidates that come from failable initializer overloads because non-optional candidates are always going to be better and that can skew the selection. Resolves: rdar://156853018
1 parent 47b0e91 commit b264e27

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,8 @@ static void determineBestChoicesInContext(
970970

971971
bool hasArgumentCandidates = false;
972972
bool isOperator = isOperatorDisjunction(disjunction);
973+
bool isNilCoalescingOperator =
974+
isOperator && isOperatorNamed(disjunction, "??");
973975

974976
for (unsigned i = 0, n = argFuncType->getNumParams(); i != n; ++i) {
975977
const auto &param = argFuncType->getParams()[i];
@@ -1603,6 +1605,20 @@ static void determineBestChoicesInContext(
16031605
double bestCandidateScore = 0;
16041606
llvm::BitVector mismatches(argumentCandidates[argIdx].size());
16051607

1608+
// `??` is overloaded on optionality of the second parameter,
1609+
// prevent ranking the argument candidates for this parameter
1610+
// if there are candidates that come from failable initializer
1611+
// overloads because non-optional candidates are always going
1612+
// to be better and that can skew the selection.
1613+
if (isNilCoalescingOperator && argIdx == 1) {
1614+
if (llvm::any_of(argumentCandidates[argIdx],
1615+
[](const auto &candidate) {
1616+
return candidate.fromInitializerCall &&
1617+
candidate.type->getOptionalObjectType();
1618+
}))
1619+
continue;
1620+
}
1621+
16061622
for (unsigned candidateIdx :
16071623
indices(argumentCandidates[argIdx])) {
16081624
// If one of the candidates matched exactly there is no reason

test/Constraints/optional.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,37 @@ func testPassingOptionalChainAsWrongArgument() {
618618
test.fn(arr?.first) // expected-error {{cannot convert value of type 'Int?' to expected argument type 'String?'}}
619619
}
620620
}
621+
622+
// Make sure that optimizer doesn't select ??(T?, T) -> T
623+
protocol Syntax {
624+
init?(_: some Syntax)
625+
}
626+
627+
protocol SubSyntax: Syntax {}
628+
629+
extension SubSyntax {
630+
init(_: String) throws { fatalError() }
631+
}
632+
633+
extension Optional : Syntax where Wrapped == Int {
634+
init?(_: some Syntax) { self = .some(42) }
635+
}
636+
637+
do {
638+
struct TestSyntax : SubSyntax {
639+
init?(_: some Syntax) {
640+
}
641+
}
642+
643+
struct S {
644+
var value: Int
645+
}
646+
647+
648+
func context(from: TestSyntax?, root: some Syntax) {
649+
}
650+
651+
func test(v: some Syntax, other: [S]) {
652+
context(from: TestSyntax(v) ?? TestSyntax(other.first?.value), root: v) // Ok (no warnings)
653+
}
654+
}

0 commit comments

Comments
 (0)