@@ -766,6 +766,38 @@ static void addKeyPathDynamicMemberOverloads(
766
766
}
767
767
}
768
768
769
+ // / Given the bound types of two constructor overloads, returns their parameter
770
+ // / list types as tuples to compare for solution ranking, or \c None if they
771
+ // / shouldn't be compared.
772
+ static Optional<std::pair<Type, Type>>
773
+ getConstructorParamsAsTuples (ASTContext &ctx, Type boundTy1, Type boundTy2) {
774
+ // If the bound types are placeholders, they haven't been resolved, so let's
775
+ // not try and rank them.
776
+ if (boundTy1->isPlaceholder () || boundTy2->isPlaceholder ())
777
+ return None;
778
+
779
+ auto choiceTy1 = boundTy1->lookThroughAllOptionalTypes ();
780
+ auto choiceTy2 = boundTy2->lookThroughAllOptionalTypes ();
781
+
782
+ auto initParams1 = choiceTy1->castTo <FunctionType>()->getParams ();
783
+ auto initParams2 = choiceTy2->castTo <FunctionType>()->getParams ();
784
+ if (initParams1.size () != initParams2.size ())
785
+ return None;
786
+
787
+ // Don't compare if there are variadic differences. This preserves the
788
+ // behavior of when we'd compare through matchTupleTypes with the parameter
789
+ // flags intact.
790
+ for (auto idx : indices (initParams1)) {
791
+ if (initParams1[idx].isVariadic () != initParams2[idx].isVariadic ())
792
+ return None;
793
+ }
794
+ auto tuple1 = AnyFunctionType::composeTuple (ctx, initParams1,
795
+ /* wantParamFlags*/ false );
796
+ auto tuple2 = AnyFunctionType::composeTuple (ctx, initParams2,
797
+ /* wantParamFlags*/ false );
798
+ return std::make_pair (tuple1, tuple2);
799
+ }
800
+
769
801
SolutionCompareResult ConstraintSystem::compareSolutions (
770
802
ConstraintSystem &cs, ArrayRef<Solution> solutions,
771
803
const SolutionDiff &diff, unsigned idx1, unsigned idx2) {
@@ -1127,11 +1159,23 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1127
1159
1128
1160
for (const auto &binding1 : bindings1) {
1129
1161
auto *typeVar = binding1.first ;
1162
+ auto *loc = typeVar->getImpl ().getLocator ();
1163
+
1164
+ // Check whether this is the overload type for a short-form init call
1165
+ // 'X(...)' or 'self.init(...)' call.
1166
+ auto isShortFormOrSelfDelegatingConstructorBinding = false ;
1167
+ if (auto initMemberTypeElt =
1168
+ loc->getLastElementAs <LocatorPathElt::ConstructorMemberType>()) {
1169
+ isShortFormOrSelfDelegatingConstructorBinding =
1170
+ initMemberTypeElt->isShortFormOrSelfDelegatingConstructor ();
1171
+ }
1130
1172
1131
1173
// If the type variable isn't one for which we should be looking at the
1132
1174
// bindings, don't.
1133
- if (!typeVar->getImpl ().prefersSubtypeBinding ())
1175
+ if (!typeVar->getImpl ().prefersSubtypeBinding () &&
1176
+ !isShortFormOrSelfDelegatingConstructorBinding) {
1134
1177
continue ;
1178
+ }
1135
1179
1136
1180
// If both solutions have a binding for this type variable
1137
1181
// let's consider it.
@@ -1142,9 +1186,24 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1142
1186
auto concreteType1 = binding1.second ;
1143
1187
auto concreteType2 = binding2->second ;
1144
1188
1145
- if (!concreteType1->isEqual (concreteType2)) {
1146
- typeDiff.insert ({typeVar, {concreteType1, concreteType2}});
1189
+ // For short-form and self-delegating init calls, we want to prefer
1190
+ // parameter lists with subtypes over supertypes. To do this, compose tuples
1191
+ // for the bound parameter lists, and compare them in the type diff. This
1192
+ // logic preserves the behavior of when we used to bind the parameter list
1193
+ // as a tuple to a TVO_PrefersSubtypeBinding type variable for such calls.
1194
+ // FIXME: We should come up with a better way of doing this, though note we
1195
+ // have some ranking and subtyping rules specific to tuples that we may need
1196
+ // to preserve to avoid breaking source.
1197
+ if (isShortFormOrSelfDelegatingConstructorBinding) {
1198
+ auto diffs = getConstructorParamsAsTuples (cs.getASTContext (),
1199
+ concreteType1, concreteType2);
1200
+ if (!diffs)
1201
+ continue ;
1202
+ std::tie (concreteType1, concreteType2) = *diffs;
1147
1203
}
1204
+
1205
+ if (!concreteType1->isEqual (concreteType2))
1206
+ typeDiff.insert ({typeVar, {concreteType1, concreteType2}});
1148
1207
}
1149
1208
1150
1209
for (auto &binding : typeDiff) {
0 commit comments