@@ -766,10 +766,32 @@ static void addKeyPathDynamicMemberOverloads(
766
766
}
767
767
}
768
768
769
+ namespace {
770
+ // / A set of type variable bindings to compare for ranking.
771
+ struct TypeBindingsToCompare {
772
+ Type Type1;
773
+ Type Type2;
774
+
775
+ // These bits are used in the case where we need to compare a lone unlabeled
776
+ // parameter with a labeled parameter, and allow us to prefer the unlabeled
777
+ // one.
778
+ bool Type1WasLabeled = false ;
779
+ bool Type2WasLabeled = false ;
780
+
781
+ TypeBindingsToCompare (Type type1, Type type2)
782
+ : Type1(type1), Type2(type2) {}
783
+
784
+ // / Whether the type bindings to compare are known to be the same.
785
+ bool areSameTypes () const {
786
+ return !Type1WasLabeled && !Type2WasLabeled && Type1->isEqual (Type2);
787
+ }
788
+ };
789
+ }; // end anonymous namespace
790
+
769
791
// / Given the bound types of two constructor overloads, returns their parameter
770
792
// / list types as tuples to compare for solution ranking, or \c None if they
771
793
// / shouldn't be compared.
772
- static Optional<std::pair<Type, Type> >
794
+ static Optional<TypeBindingsToCompare >
773
795
getConstructorParamsAsTuples (ASTContext &ctx, Type boundTy1, Type boundTy2) {
774
796
auto choiceTy1 =
775
797
boundTy1->lookThroughAllOptionalTypes ()->getAs <FunctionType>();
@@ -793,11 +815,43 @@ getConstructorParamsAsTuples(ASTContext &ctx, Type boundTy1, Type boundTy2) {
793
815
if (initParams1[idx].isVariadic () != initParams2[idx].isVariadic ())
794
816
return None;
795
817
}
818
+
819
+ // Awful hack needed to preserve source compatibility: If we have single
820
+ // variadic parameters to compare, where one has a label and the other does
821
+ // not, e.g (x: Int...) and (Int...), compare the parameter types by
822
+ // themselves, and make a note of which one has the label.
823
+ //
824
+ // This is needed because previously we would build a TupleType for a single
825
+ // unlabeled variadic parameter (Int...), which would let us compare it with
826
+ // a labeled parameter (x: Int...) and prefer the unlabeled version. With the
827
+ // parameter flags stripped however, (Int...) would become a paren type,
828
+ // which we wouldn't compare with the tuple type (x: Int...). To preserve the
829
+ // previous behavior in this case, just do a type comparison for the param
830
+ // types, and record where we stripped a label. The ranking logic can then use
831
+ // this to prefer the unlabeled variant. This is only needed in the single
832
+ // parameter case, as other cases will compare as tuples the same as before.
833
+ // In cases where variadics aren't used, we may end up trying to compare
834
+ // parens with tuples, but that's consistent with what we previously did.
835
+ //
836
+ // Note we can just do checks on initParams1, as we've already established
837
+ // sizes and variadic bits are consistent.
838
+ if (initParams1.size () == 1 && initParams1[0 ].isVariadic () &&
839
+ initParams1[0 ].hasLabel () != initParams2[0 ].hasLabel ()) {
840
+ TypeBindingsToCompare bindings (initParams1[0 ].getParameterType (),
841
+ initParams2[0 ].getParameterType ());
842
+ if (initParams1[0 ].hasLabel ()) {
843
+ bindings.Type1WasLabeled = true ;
844
+ } else {
845
+ bindings.Type2WasLabeled = true ;
846
+ }
847
+ return bindings;
848
+ }
849
+
796
850
auto tuple1 = AnyFunctionType::composeTuple (ctx, initParams1,
797
851
/* wantParamFlags*/ false );
798
852
auto tuple2 = AnyFunctionType::composeTuple (ctx, initParams2,
799
853
/* wantParamFlags*/ false );
800
- return std::make_pair (tuple1, tuple2);
854
+ return TypeBindingsToCompare (tuple1, tuple2);
801
855
}
802
856
803
857
SolutionCompareResult ConstraintSystem::compareSolutions (
@@ -1154,7 +1208,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1154
1208
}
1155
1209
1156
1210
// Compare the type variable bindings.
1157
- llvm::DenseMap<TypeVariableType *, std::pair<Type, Type> > typeDiff;
1211
+ llvm::DenseMap<TypeVariableType *, TypeBindingsToCompare > typeDiff;
1158
1212
1159
1213
const auto &bindings1 = solutions[idx1].typeBindings ;
1160
1214
const auto &bindings2 = solutions[idx2].typeBindings ;
@@ -1185,8 +1239,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1185
1239
if (binding2 == bindings2.end ())
1186
1240
continue ;
1187
1241
1188
- auto concreteType1 = binding1.second ;
1189
- auto concreteType2 = binding2->second ;
1242
+ TypeBindingsToCompare typesToCompare (binding1.second , binding2->second );
1190
1243
1191
1244
// For short-form and self-delegating init calls, we want to prefer
1192
1245
// parameter lists with subtypes over supertypes. To do this, compose tuples
@@ -1197,20 +1250,21 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1197
1250
// have some ranking and subtyping rules specific to tuples that we may need
1198
1251
// to preserve to avoid breaking source.
1199
1252
if (isShortFormOrSelfDelegatingConstructorBinding) {
1200
- auto diffs = getConstructorParamsAsTuples (cs. getASTContext (),
1201
- concreteType1, concreteType2 );
1253
+ auto diffs = getConstructorParamsAsTuples (
1254
+ cs. getASTContext (), typesToCompare. Type1 , typesToCompare. Type2 );
1202
1255
if (!diffs)
1203
1256
continue ;
1204
- std::tie (concreteType1, concreteType2) = *diffs;
1257
+ typesToCompare = *diffs;
1205
1258
}
1206
1259
1207
- if (!concreteType1-> isEqual (concreteType2 ))
1208
- typeDiff.insert ({typeVar, {concreteType1, concreteType2} });
1260
+ if (!typesToCompare. areSameTypes ( ))
1261
+ typeDiff.insert ({typeVar, typesToCompare });
1209
1262
}
1210
1263
1211
1264
for (auto &binding : typeDiff) {
1212
- auto type1 = binding.second .first ;
1213
- auto type2 = binding.second .second ;
1265
+ auto types = binding.second ;
1266
+ auto type1 = types.Type1 ;
1267
+ auto type2 = types.Type2 ;
1214
1268
1215
1269
// If either of the types still contains type variables, we can't
1216
1270
// compare them.
@@ -1251,11 +1305,11 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
1251
1305
auto unlabeled1 = getUnlabeledType (type1, cs.getASTContext ());
1252
1306
auto unlabeled2 = getUnlabeledType (type2, cs.getASTContext ());
1253
1307
if (unlabeled1->isEqual (unlabeled2)) {
1254
- if (type1->isEqual (unlabeled1)) {
1308
+ if (type1->isEqual (unlabeled1) && !types. Type1WasLabeled ) {
1255
1309
++score1;
1256
1310
continue ;
1257
1311
}
1258
- if (type2->isEqual (unlabeled2)) {
1312
+ if (type2->isEqual (unlabeled2) && !types. Type2WasLabeled ) {
1259
1313
++score2;
1260
1314
continue ;
1261
1315
}
0 commit comments