mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -1640,6 +1640,43 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
|
||||
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:
|
||||
|
||||
@@ -63,6 +63,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
|
||||
case RValueAdjustment:
|
||||
case ClosureResult:
|
||||
case ParentType:
|
||||
case ExistentialSuperclassType:
|
||||
case InstanceType:
|
||||
case SequenceElementType:
|
||||
case AutoclosureResult:
|
||||
@@ -309,6 +310,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
|
||||
out << "parent type";
|
||||
break;
|
||||
|
||||
case ExistentialSuperclassType:
|
||||
out << "existential superclass type";
|
||||
break;
|
||||
|
||||
case LValueConversion:
|
||||
out << "@lvalue-to-inout conversion";
|
||||
break;
|
||||
|
||||
@@ -96,6 +96,8 @@ public:
|
||||
ClosureResult,
|
||||
/// The parent of a nested type.
|
||||
ParentType,
|
||||
/// The superclass of a protocol existential type.
|
||||
ExistentialSuperclassType,
|
||||
/// The instance of a metatype type.
|
||||
InstanceType,
|
||||
/// The element type of a sequence in a for ... in ... loop.
|
||||
@@ -157,6 +159,7 @@ public:
|
||||
case ClosureResult:
|
||||
case ParentType:
|
||||
case InstanceType:
|
||||
case ExistentialSuperclassType:
|
||||
case SequenceElementType:
|
||||
case AutoclosureResult:
|
||||
case Requirement:
|
||||
@@ -215,6 +218,7 @@ public:
|
||||
case MemberRefBase:
|
||||
case UnresolvedMember:
|
||||
case ParentType:
|
||||
case ExistentialSuperclassType:
|
||||
case LValueConversion:
|
||||
case RValueAdjustment:
|
||||
case SubscriptMember:
|
||||
|
||||
@@ -530,3 +530,21 @@ struct DerivedBox<T : Derived> {}
|
||||
|
||||
func takesBoxWithP3(_: DerivedBox<Derived & P3>) {}
|
||||
// expected-error@-1 {{'DerivedBox' requires that 'Derived & P3' inherit from 'Derived'}}
|
||||
|
||||
// A bit of a tricky setup -- the real problem is that matchTypes() did the
|
||||
// wrong thing when solving a Bind constraint where both sides were protocol
|
||||
// compositions, but one of them had a superclass constraint containing type
|
||||
// variables. We were checking type equality in this case, which is not
|
||||
// correct; we have to do a 'deep equality' check, recursively matching the
|
||||
// superclass types.
|
||||
struct Generic<T> {
|
||||
var _x: (Base<T> & P2)!
|
||||
|
||||
var x: (Base<T> & P2)? {
|
||||
get { return _x }
|
||||
set { _x = newValue }
|
||||
_modify {
|
||||
yield &_x
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user