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().
This commit is contained in:
Slava Pestov
2019-06-17 18:40:27 -04:00
parent 194dba6c69
commit c365ef32c6
4 changed files with 81 additions and 2 deletions

View File

@@ -1639,7 +1639,44 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
// Match up the replacement types of the respective substitution maps.
return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
}
// Handle protocol compositions.
if (auto existential1 = type1->getAs<ProtocolCompositionType>()) {
if (auto existential2 = type2->getAs<ProtocolCompositionType>()) {
auto layout1 = existential1->getExistentialLayout();
auto layout2 = existential2->getExistentialLayout();
// Explicit AnyObject and protocols must match exactly.
if (layout1.hasExplicitAnyObject != layout2.hasExplicitAnyObject)
return getTypeMatchFailure(locator);
if (layout1.getProtocols().size() != layout2.getProtocols().size())
return getTypeMatchFailure(locator);
for (unsigned i: indices(layout1.getProtocols())) {
if (!layout1.getProtocols()[i]->isEqual(layout2.getProtocols()[i]))
return getTypeMatchFailure(locator);
}
// This is the only interesting case. We might have type variables
// on either side of the superclass constraint, so make sure we
// recursively call matchTypes() here.
if (layout1.explicitSuperclass || layout2.explicitSuperclass) {
if (!layout1.explicitSuperclass || !layout2.explicitSuperclass)
return getTypeMatchFailure(locator);
auto result = matchTypes(layout1.explicitSuperclass,
layout2.explicitSuperclass,
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::ExistentialSuperclassType));
if (result.isFailure())
return result;
}
return getTypeMatchSuccess();
}
}
// Handle nominal types that are not directly generic.
if (auto nominal1 = type1->getAs<NominalType>()) {
auto nominal2 = type2->castTo<NominalType>();
@@ -2631,7 +2668,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
llvm_unreachable("Polymorphic function type should have been opened");
case TypeKind::ProtocolComposition:
// Existential types handled below.
switch (kind) {
case ConstraintKind::Equal:
case ConstraintKind::Bind:
case ConstraintKind::BindParam:
// If we are matching types for equality, we might still have
// type variables inside the protocol composition's superclass
// constraint.
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
break;
default:
// Subtype constraints where the RHS is an existential type are
// handled below.
break;
}
break;
case TypeKind::LValue: