Skip to content

Commit 4bd370f

Browse files
authored
Merge pull request #85451 from xedin/rdar-164201746
[CSOptimizer] Skip `??` if it's involved in optional chain with unres…
2 parents 669f6dc + a01692a commit 4bd370f

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@ static bool isStandardInfixLogicalOperator(Constraint *disjunction) {
261261
return false;
262262
}
263263

264+
static bool isNilCoalescingOperator(Constraint *disjunction) {
265+
auto *choice = disjunction->getNestedConstraints()[0];
266+
if (auto *decl = getOverloadChoiceDecl(choice))
267+
return decl->isOperator() &&
268+
decl->getBaseIdentifier().isNilCoalescingOperator();
269+
return false;
270+
}
271+
264272
static bool isArithmeticOperator(ValueDecl *decl) {
265273
return decl->isOperator() && decl->getBaseIdentifier().isArithmeticOperator();
266274
}
@@ -1086,6 +1094,30 @@ static void determineBestChoicesInContext(
10861094
if (auto *typeVar = resultType->getAs<TypeVariableType>()) {
10871095
auto bindingSet = cs.getBindingsFor(typeVar);
10881096

1097+
// `??` operator is overloaded on optionality of its result. When the
1098+
// first argument matches exactly, the ranking is going to be skewed
1099+
// towards selecting an overload choice that returns a non-optional type.
1100+
// This is not always correct i.e. when operator is involved in optional
1101+
// chaining. To avoid producing an incorrect favoring, let's skip the this
1102+
// disjunction when constraints associated with result type indicate
1103+
// that it should be optional.
1104+
//
1105+
// Simply adding it as a binding won't work because if the second argument
1106+
// is non-optional the overload that returns `T?` would still have a lower
1107+
// score.
1108+
if (!bindingSet && isNilCoalescingOperator(disjunction)) {
1109+
auto &cg = cs.getConstraintGraph();
1110+
if (llvm::any_of(cg[typeVar].getConstraints(),
1111+
[&typeVar](Constraint *constraint) {
1112+
if (constraint->getKind() !=
1113+
ConstraintKind::OptionalObject)
1114+
return false;
1115+
return constraint->getFirstType()->isEqual(typeVar);
1116+
})) {
1117+
continue;
1118+
}
1119+
}
1120+
10891121
for (const auto &binding : bindingSet.Bindings) {
10901122
resultTypes.push_back(binding.BindingType);
10911123
}

test/Constraints/nil-coalescing-favoring.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,12 @@ func test_no_incorrect_favoring(v: Int?, o: Int) {
6262
sameType(s1, as: Int?.self)
6363
sameType(s2, as: Int?.self)
6464
}
65+
66+
extension Int {
67+
func test() -> Int { 42 }
68+
}
69+
70+
func test_optional_chaining(v: Int?, defaultV: Int) {
71+
// CHECK: declref_expr type="(consuming Int?, @autoclosure () throws -> Int?) throws -> Int?" {{.*}} decl="Swift.(file).??
72+
_ = (v ?? defaultV)?.test() // Ok (no warnings)
73+
}

0 commit comments

Comments
 (0)