Skip to content

Commit 077f0c9

Browse files
committed
Properly canonicalize protocol compositions that include parameters.
Parameterized composition components are preserved over non-parameterized components on the same protocol. They are also preserved even if they are redundant with parameterized derived protocols. Otherwise, they're just put in usual protocol order. This assumes that argument lists on the same protocol are always redundant with each other, which may not be correct when we add abstract constraints. We are also, of course, still not making an effort to diagnose conflicts.
1 parent 730603a commit 077f0c9

File tree

1 file changed

+46
-12
lines changed

1 file changed

+46
-12
lines changed

lib/AST/Type.cpp

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,10 +1320,14 @@ Type TypeBase::getMetatypeInstanceType() {
13201320
return this;
13211321
}
13221322

1323+
using ParameterizedProtocolMap =
1324+
llvm::DenseMap<ProtocolDecl *, ParameterizedProtocolType *>;
1325+
13231326
/// Collect the protocols in the existential type T into the given
13241327
/// vector.
13251328
static void addProtocols(Type T,
13261329
SmallVectorImpl<ProtocolDecl *> &Protocols,
1330+
ParameterizedProtocolMap &Parameterized,
13271331
Type &Superclass,
13281332
bool &HasExplicitAnyObject) {
13291333
if (auto Proto = T->getAs<ProtocolType>()) {
@@ -1335,7 +1339,14 @@ static void addProtocols(Type T,
13351339
if (PC->hasExplicitAnyObject())
13361340
HasExplicitAnyObject = true;
13371341
for (auto P : PC->getMembers())
1338-
addProtocols(P, Protocols, Superclass, HasExplicitAnyObject);
1342+
addProtocols(P, Protocols, Parameterized, Superclass,
1343+
HasExplicitAnyObject);
1344+
return;
1345+
}
1346+
1347+
if (auto PP = T->getAs<ParameterizedProtocolType>()) {
1348+
Parameterized.insert({PP->getProtocol(), PP});
1349+
Protocols.push_back(PP->getProtocol());
13391350
return;
13401351
}
13411352

@@ -1377,8 +1388,8 @@ bool ProtocolType::visitAllProtocols(
13771388
return false;
13781389
}
13791390

1380-
void ProtocolType::canonicalizeProtocols(
1381-
SmallVectorImpl<ProtocolDecl *> &protocols) {
1391+
static void canonicalizeProtocols(SmallVectorImpl<ProtocolDecl *> &protocols,
1392+
ParameterizedProtocolMap *parameterized) {
13821393
llvm::SmallDenseMap<ProtocolDecl *, unsigned> known;
13831394
bool zappedAny = false;
13841395

@@ -1410,6 +1421,10 @@ void ProtocolType::canonicalizeProtocols(
14101421

14111422
auto found = known.find(inherited);
14121423
if (found != known.end()) {
1424+
// Don't zap protocols associated with parameterized types.
1425+
if (parameterized && parameterized->count(inherited))
1426+
return TypeWalker::Action::Continue;
1427+
14131428
protocols[found->second] = nullptr;
14141429
zappedAny = true;
14151430
}
@@ -1428,6 +1443,11 @@ void ProtocolType::canonicalizeProtocols(
14281443
llvm::array_pod_sort(protocols.begin(), protocols.end(), TypeDecl::compare);
14291444
}
14301445

1446+
void ProtocolType::canonicalizeProtocols(
1447+
SmallVectorImpl<ProtocolDecl *> &protocols) {
1448+
return ::canonicalizeProtocols(protocols, nullptr);
1449+
}
1450+
14311451
static void
14321452
getCanonicalParams(AnyFunctionType *funcType,
14331453
CanGenericSignature genericSig,
@@ -4006,25 +4026,39 @@ Type ProtocolCompositionType::get(const ASTContext &C,
40064026

40074027
Type Superclass;
40084028
SmallVector<ProtocolDecl *, 4> Protocols;
4029+
ParameterizedProtocolMap Parameterized;
40094030
for (Type t : Members) {
4010-
addProtocols(t, Protocols, Superclass, HasExplicitAnyObject);
4031+
addProtocols(t, Protocols, Parameterized, Superclass, HasExplicitAnyObject);
40114032
}
4012-
4013-
// Minimize the set of protocols composed together.
4014-
ProtocolType::canonicalizeProtocols(Protocols);
40154033

40164034
// The presence of a superclass constraint makes AnyObject redundant.
40174035
if (Superclass)
40184036
HasExplicitAnyObject = false;
40194037

4020-
// Form the set of canonical protocol types from the protocol
4021-
// declarations, and use that to build the canonical composition type.
4038+
// If there are any parameterized protocols, the canonicalization
4039+
// algorithm gets more complex.
4040+
4041+
// Form the set of canonical component types.
40224042
SmallVector<Type, 4> CanTypes;
40234043
if (Superclass)
40244044
CanTypes.push_back(Superclass->getCanonicalType());
4025-
llvm::transform(
4026-
Protocols, std::back_inserter(CanTypes),
4027-
[](ProtocolDecl *Proto) { return Proto->getDeclaredInterfaceType(); });
4045+
4046+
canonicalizeProtocols(Protocols, &Parameterized);
4047+
4048+
for (auto proto: Protocols) {
4049+
// If we have a parameterized type for this protocol, use the
4050+
// canonical type of that. Sema should prevent us from building
4051+
// compositions with the same protocol and conflicting constraints.
4052+
if (!Parameterized.empty()) {
4053+
auto it = Parameterized.find(proto);
4054+
if (it != Parameterized.end()) {
4055+
CanTypes.push_back(it->second->getCanonicalType());
4056+
continue;
4057+
}
4058+
}
4059+
4060+
CanTypes.push_back(proto->getDeclaredInterfaceType());
4061+
}
40284062

40294063
// If one member remains and no layout constraint, return that type.
40304064
if (CanTypes.size() == 1 && !HasExplicitAnyObject)

0 commit comments

Comments
 (0)