@@ -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+
264272static 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 }
0 commit comments