Skip to content

Commit 334bde6

Browse files
authored
Merge pull request #39852 from hamishknight/enigma-variadics
[CS] Add special case to preserve compatibility for rdar://84279742
2 parents a11325c + 6c6d3c9 commit 334bde6

File tree

3 files changed

+150
-14
lines changed

3 files changed

+150
-14
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -766,10 +766,32 @@ static void addKeyPathDynamicMemberOverloads(
766766
}
767767
}
768768

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+
769791
/// Given the bound types of two constructor overloads, returns their parameter
770792
/// list types as tuples to compare for solution ranking, or \c None if they
771793
/// shouldn't be compared.
772-
static Optional<std::pair<Type, Type>>
794+
static Optional<TypeBindingsToCompare>
773795
getConstructorParamsAsTuples(ASTContext &ctx, Type boundTy1, Type boundTy2) {
774796
auto choiceTy1 =
775797
boundTy1->lookThroughAllOptionalTypes()->getAs<FunctionType>();
@@ -793,11 +815,43 @@ getConstructorParamsAsTuples(ASTContext &ctx, Type boundTy1, Type boundTy2) {
793815
if (initParams1[idx].isVariadic() != initParams2[idx].isVariadic())
794816
return None;
795817
}
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+
796850
auto tuple1 = AnyFunctionType::composeTuple(ctx, initParams1,
797851
/*wantParamFlags*/ false);
798852
auto tuple2 = AnyFunctionType::composeTuple(ctx, initParams2,
799853
/*wantParamFlags*/ false);
800-
return std::make_pair(tuple1, tuple2);
854+
return TypeBindingsToCompare(tuple1, tuple2);
801855
}
802856

803857
SolutionCompareResult ConstraintSystem::compareSolutions(
@@ -1154,7 +1208,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11541208
}
11551209

11561210
// Compare the type variable bindings.
1157-
llvm::DenseMap<TypeVariableType *, std::pair<Type, Type>> typeDiff;
1211+
llvm::DenseMap<TypeVariableType *, TypeBindingsToCompare> typeDiff;
11581212

11591213
const auto &bindings1 = solutions[idx1].typeBindings;
11601214
const auto &bindings2 = solutions[idx2].typeBindings;
@@ -1185,8 +1239,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11851239
if (binding2 == bindings2.end())
11861240
continue;
11871241

1188-
auto concreteType1 = binding1.second;
1189-
auto concreteType2 = binding2->second;
1242+
TypeBindingsToCompare typesToCompare(binding1.second, binding2->second);
11901243

11911244
// For short-form and self-delegating init calls, we want to prefer
11921245
// parameter lists with subtypes over supertypes. To do this, compose tuples
@@ -1197,20 +1250,21 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11971250
// have some ranking and subtyping rules specific to tuples that we may need
11981251
// to preserve to avoid breaking source.
11991252
if (isShortFormOrSelfDelegatingConstructorBinding) {
1200-
auto diffs = getConstructorParamsAsTuples(cs.getASTContext(),
1201-
concreteType1, concreteType2);
1253+
auto diffs = getConstructorParamsAsTuples(
1254+
cs.getASTContext(), typesToCompare.Type1, typesToCompare.Type2);
12021255
if (!diffs)
12031256
continue;
1204-
std::tie(concreteType1, concreteType2) = *diffs;
1257+
typesToCompare = *diffs;
12051258
}
12061259

1207-
if (!concreteType1->isEqual(concreteType2))
1208-
typeDiff.insert({typeVar, {concreteType1, concreteType2}});
1260+
if (!typesToCompare.areSameTypes())
1261+
typeDiff.insert({typeVar, typesToCompare});
12091262
}
12101263

12111264
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;
12141268

12151269
// If either of the types still contains type variables, we can't
12161270
// compare them.
@@ -1251,11 +1305,11 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
12511305
auto unlabeled1 = getUnlabeledType(type1, cs.getASTContext());
12521306
auto unlabeled2 = getUnlabeledType(type2, cs.getASTContext());
12531307
if (unlabeled1->isEqual(unlabeled2)) {
1254-
if (type1->isEqual(unlabeled1)) {
1308+
if (type1->isEqual(unlabeled1) && !types.Type1WasLabeled) {
12551309
++score1;
12561310
continue;
12571311
}
1258-
if (type2->isEqual(unlabeled2)) {
1312+
if (type2->isEqual(unlabeled2) && !types.Type2WasLabeled) {
12591313
++score2;
12601314
continue;
12611315
}

test/Constraints/ranking.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,55 @@ struct S4 {
343343
}
344344
}
345345

346+
// rdar://84279742 – Make sure we prefer the unlabeled init here.
347+
struct S5 {
348+
init(x: Int...) {}
349+
init(_ x: Int...) {}
350+
351+
// CHECK-LABEL: sil hidden [ossa] @$s7ranking2S5V15testInitRankingyyF
352+
func testInitRanking() {
353+
// CHECK: function_ref @$s7ranking2S5VyACSid_tcfC : $@convention(method) (@owned Array<Int>, @thin S5.Type) -> S5
354+
_ = S5()
355+
}
356+
}
357+
358+
// We should also prefer the unlabeled case here.
359+
struct S6 {
360+
init(_: Int = 0, x: Int...) {}
361+
init(_: Int = 0, _: Int...) {}
362+
363+
// CHECK-LABEL: sil hidden [ossa] @$s7ranking2S6V15testInitRankingyyF
364+
func testInitRanking() {
365+
// CHECK: function_ref @$s7ranking2S6VyACSi_SidtcfC : $@convention(method) (Int, @owned Array<Int>, @thin S6.Type) -> S6
366+
_ = S6()
367+
}
368+
}
369+
370+
// However subtyping rules take precedence over labeling rules, so we should
371+
// prefer the labeled init here.
372+
struct S7 {
373+
init(x: Int...) {}
374+
init(_: Int?...) {}
375+
376+
// CHECK-LABEL: sil hidden [ossa] @$s7ranking2S7V15testInitRankingyyF
377+
func testInitRanking() {
378+
// CHECK: function_ref @$s7ranking2S7V1xACSid_tcfC : $@convention(method) (@owned Array<Int>, @thin S7.Type) -> S7
379+
_ = S7()
380+
}
381+
}
382+
383+
// Subtyping rules also let us prefer the Int... init here.
384+
struct S8 {
385+
init(_: Int?...) {}
386+
init(_: Int...) {}
387+
388+
// CHECK-LABEL: sil hidden [ossa] @$s7ranking2S8V15testInitRankingyyF
389+
func testInitRanking() {
390+
// CHECK: function_ref @$s7ranking2S8VyACSid_tcfC : $@convention(method) (@owned Array<Int>, @thin S8.Type) -> S8
391+
_ = S8()
392+
}
393+
}
394+
346395
//--------------------------------------------------------------------
347396
// Pointer conversions
348397
//--------------------------------------------------------------------

test/Constraints/ranking_ambiguities.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,36 @@ struct S1 {
2525
_ = S1.init() // expected-error {{ambiguous use of 'init'}}
2626
}
2727
}
28+
29+
// Ambiguous because we don't prefer one label over the other.
30+
struct S2 {
31+
init(x: Int...) {} // expected-note {{found this candidate}}
32+
init(y: Int...) {} // expected-note {{found this candidate}}
33+
34+
func testInitRanking() {
35+
_ = S2() // expected-error {{ambiguous use of 'init'}}
36+
}
37+
}
38+
39+
// Ambiguous because we don't apply the prefer-unlabeled rule if the types
40+
// aren't compatible.
41+
struct S3 {
42+
init(x: Int...) {} // expected-note {{found this candidate}}
43+
init(_: String...) {} // expected-note {{found this candidate}}
44+
45+
func testInitRanking() {
46+
_ = S3() // expected-error {{ambiguous use of 'init'}}
47+
}
48+
}
49+
50+
// Ambiguous because this ends up being a comparison between a paren and tuple
51+
// parameter list, and we don't have a special case for it. Ideally we would
52+
// align this behavior with the variadic behavior.
53+
struct S4 {
54+
init(x: Int = 0) {} // expected-note {{found this candidate}}
55+
init(_: Int = 0) {} // expected-note {{found this candidate}}
56+
57+
func testInitRanking() {
58+
_ = S4() // expected-error {{ambiguous use of 'init'}}
59+
}
60+
}

0 commit comments

Comments
 (0)