Skip to content

Commit fee31c6

Browse files
committed
[CS] Move constructor ranking rule into CSRanking
Previously we were introducing a type variable to mark a constructor's parameter list as `TVO_PrefersSubtypeBinding`. Unfortunately this relies on representing the parameter list as a tuple, which will no longer be properly supported once param flags are removed from tuple types. Move the logic into CSRanking such that we pick up and compare the parameter lists when comparing overload bindings. For now, this still relies on comparing the parameter lists as tuples, as there's some subtle tuple subtyping rules that could potentially affect source compatibility here, but at least we can explicitly strip the parameter flags and localise the hack to CSRanking rather than exposing it as a constraint.
1 parent a1abcee commit fee31c6

File tree

3 files changed

+62
-30
lines changed

3 files changed

+62
-30
lines changed

lib/Sema/CSGen.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,21 +1512,6 @@ namespace {
15121512
auto methodTy =
15131513
CS.createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape);
15141514

1515-
// FIXME: Once TVO_PrefersSubtypeBinding is replaced with something
1516-
// better, we won't need the second type variable at all.
1517-
{
1518-
auto argTy = CS.createTypeVariable(
1519-
CS.getConstraintLocator(expr,
1520-
ConstraintLocator::ApplyArgument),
1521-
(TVO_CanBindToLValue |
1522-
TVO_CanBindToInOut |
1523-
TVO_CanBindToNoEscape |
1524-
TVO_PrefersSubtypeBinding));
1525-
CS.addConstraint(
1526-
ConstraintKind::FunctionInput, methodTy, argTy,
1527-
CS.getConstraintLocator(expr));
1528-
}
1529-
15301515
CS.addValueMemberConstraint(
15311516
baseTy, expr->getName(), methodTy, CurDC,
15321517
expr->getFunctionRefKind(),

lib/Sema/CSRanking.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,38 @@ static void addKeyPathDynamicMemberOverloads(
766766
}
767767
}
768768

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+
769801
SolutionCompareResult ConstraintSystem::compareSolutions(
770802
ConstraintSystem &cs, ArrayRef<Solution> solutions,
771803
const SolutionDiff &diff, unsigned idx1, unsigned idx2) {
@@ -1127,11 +1159,23 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11271159

11281160
for (const auto &binding1 : bindings1) {
11291161
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+
}
11301172

11311173
// If the type variable isn't one for which we should be looking at the
11321174
// bindings, don't.
1133-
if (!typeVar->getImpl().prefersSubtypeBinding())
1175+
if (!typeVar->getImpl().prefersSubtypeBinding() &&
1176+
!isShortFormOrSelfDelegatingConstructorBinding) {
11341177
continue;
1178+
}
11351179

11361180
// If both solutions have a binding for this type variable
11371181
// let's consider it.
@@ -1142,9 +1186,24 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11421186
auto concreteType1 = binding1.second;
11431187
auto concreteType2 = binding2->second;
11441188

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;
11471203
}
1204+
1205+
if (!concreteType1->isEqual(concreteType2))
1206+
typeDiff.insert({typeVar, {concreteType1, concreteType2}});
11481207
}
11491208

11501209
for (auto &binding : typeDiff) {

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6232,18 +6232,6 @@ ConstraintSystem::simplifyConstructionConstraint(
62326232
fnLocator,
62336233
ConstraintLocator::ConstructorMember));
62346234

6235-
// FIXME: Once TVO_PrefersSubtypeBinding is replaced with something
6236-
// better, we won't need the second type variable at all.
6237-
{
6238-
auto argType = createTypeVariable(
6239-
getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
6240-
(TVO_CanBindToLValue |
6241-
TVO_CanBindToInOut |
6242-
TVO_CanBindToNoEscape |
6243-
TVO_PrefersSubtypeBinding));
6244-
addConstraint(ConstraintKind::FunctionInput, memberType, argType, locator);
6245-
}
6246-
62476235
addConstraint(ConstraintKind::ApplicableFunction, fnType, memberType,
62486236
fnLocator);
62496237

0 commit comments

Comments
 (0)