Skip to content

Commit 806a8cf

Browse files
committed
Sema: Handle protocol compositions containing type variables in matchTypes()
We would previously fail to match something like (C<$T1> & P) against (C<Int> & P) when the constraint kind was <= Subtype, because we would fall back to a type equality test in that case. However, this was only valid for protocol compositions without superclass constraints since the superclass could contain a type variable on one side of the constraint. Fix this by adding support for protocol composition types to matchDeepEqualityTypes().
1 parent b2758ca commit 806a8cf

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,44 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
16061606
// Match up the replacement types of the respective substitution maps.
16071607
return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
16081608
}
1609-
1609+
1610+
// Handle protocol compositions.
1611+
if (auto existential1 = type1->getAs<ProtocolCompositionType>()) {
1612+
if (auto existential2 = type2->getAs<ProtocolCompositionType>()) {
1613+
auto layout1 = existential1->getExistentialLayout();
1614+
auto layout2 = existential2->getExistentialLayout();
1615+
1616+
// Explicit AnyObject and protocols must match exactly.
1617+
if (layout1.hasExplicitAnyObject != layout2.hasExplicitAnyObject)
1618+
return getTypeMatchFailure(locator);
1619+
1620+
if (layout1.getProtocols().size() != layout2.getProtocols().size())
1621+
return getTypeMatchFailure(locator);
1622+
1623+
for (unsigned i: indices(layout1.getProtocols())) {
1624+
if (!layout1.getProtocols()[i]->isEqual(layout2.getProtocols()[i]))
1625+
return getTypeMatchFailure(locator);
1626+
}
1627+
1628+
// This is the only interesting case. We might have type variables
1629+
// on either side of the superclass constraint, so make sure we
1630+
// recursively call matchTypes() here.
1631+
if (layout1.explicitSuperclass || layout2.explicitSuperclass) {
1632+
if (!layout1.explicitSuperclass || !layout2.explicitSuperclass)
1633+
return getTypeMatchFailure(locator);
1634+
1635+
auto result = matchTypes(layout1.explicitSuperclass,
1636+
layout2.explicitSuperclass,
1637+
ConstraintKind::Bind, subflags,
1638+
locator.withPathElement(
1639+
ConstraintLocator::ExistentialSuperclassType));
1640+
if (result.isFailure())
1641+
return result;
1642+
}
1643+
1644+
return getTypeMatchSuccess();
1645+
}
1646+
}
16101647
// Handle nominal types that are not directly generic.
16111648
if (auto nominal1 = type1->getAs<NominalType>()) {
16121649
auto nominal2 = type2->castTo<NominalType>();
@@ -2533,7 +2570,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
25332570
llvm_unreachable("Polymorphic function type should have been opened");
25342571

25352572
case TypeKind::ProtocolComposition:
2536-
// Existential types handled below.
2573+
switch (kind) {
2574+
case ConstraintKind::Equal:
2575+
case ConstraintKind::Bind:
2576+
case ConstraintKind::BindParam:
2577+
// If we are matching types for equality, we might still have
2578+
// type variables inside the protocol composition's superclass
2579+
// constraint.
2580+
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
2581+
break;
2582+
2583+
default:
2584+
// Subtype constraints where the RHS is an existential type are
2585+
// handled below.
2586+
break;
2587+
}
2588+
25372589
break;
25382590

25392591
case TypeKind::LValue:

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
6363
case RValueAdjustment:
6464
case ClosureResult:
6565
case ParentType:
66+
case ExistentialSuperclassType:
6667
case InstanceType:
6768
case SequenceElementType:
6869
case AutoclosureResult:
@@ -308,6 +309,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
308309
out << "parent type";
309310
break;
310311

312+
case ExistentialSuperclassType:
313+
out << "existential superclass type";
314+
break;
315+
311316
case LValueConversion:
312317
out << "@lvalue-to-inout conversion";
313318
break;

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
9696
ClosureResult,
9797
/// The parent of a nested type.
9898
ParentType,
99+
/// The superclass of a protocol existential type.
100+
ExistentialSuperclassType,
99101
/// The instance of a metatype type.
100102
InstanceType,
101103
/// The element type of a sequence in a for ... in ... loop.
@@ -155,6 +157,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
155157
case ClosureResult:
156158
case ParentType:
157159
case InstanceType:
160+
case ExistentialSuperclassType:
158161
case SequenceElementType:
159162
case AutoclosureResult:
160163
case Requirement:
@@ -212,6 +215,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
212215
case MemberRefBase:
213216
case UnresolvedMember:
214217
case ParentType:
218+
case ExistentialSuperclassType:
215219
case LValueConversion:
216220
case RValueAdjustment:
217221
case SubscriptMember:

test/type/subclass_composition.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,21 @@ struct DerivedBox<T : Derived> {}
530530

531531
func takesBoxWithP3(_: DerivedBox<Derived & P3>) {}
532532
// expected-error@-1 {{'DerivedBox' requires that 'Derived & P3' inherit from 'Derived'}}
533+
534+
// A bit of a tricky setup -- the real problem is that matchTypes() did the
535+
// wrong thing when solving a Bind constraint where both sides were protocol
536+
// compositions, but one of them had a superclass constraint containing type
537+
// variables. We were checking type equality in this case, which is not
538+
// correct; we have to do a 'deep equality' check, recursively matching the
539+
// superclass types.
540+
struct Generic<T> {
541+
var _x: (Base<T> & P2)!
542+
543+
var x: (Base<T> & P2)? {
544+
get { return _x }
545+
set { _x = newValue }
546+
_modify {
547+
yield &_x
548+
}
549+
}
550+
}

0 commit comments

Comments
 (0)