Skip to content

Commit 4cfc99d

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 0cde2dd commit 4cfc99d

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
@@ -1334,10 +1334,14 @@ Type TypeBase::getMetatypeInstanceType() {
13341334
return this;
13351335
}
13361336

1337+
using ParameterizedProtocolMap =
1338+
llvm::DenseMap<ProtocolDecl *, ParameterizedProtocolType *>;
1339+
13371340
/// Collect the protocols in the existential type T into the given
13381341
/// vector.
13391342
static void addProtocols(Type T,
13401343
SmallVectorImpl<ProtocolDecl *> &Protocols,
1344+
ParameterizedProtocolMap &Parameterized,
13411345
Type &Superclass,
13421346
bool &HasExplicitAnyObject) {
13431347
if (auto Proto = T->getAs<ProtocolType>()) {
@@ -1349,7 +1353,14 @@ static void addProtocols(Type T,
13491353
if (PC->hasExplicitAnyObject())
13501354
HasExplicitAnyObject = true;
13511355
for (auto P : PC->getMembers())
1352-
addProtocols(P, Protocols, Superclass, HasExplicitAnyObject);
1356+
addProtocols(P, Protocols, Parameterized, Superclass,
1357+
HasExplicitAnyObject);
1358+
return;
1359+
}
1360+
1361+
if (auto PP = T->getAs<ParameterizedProtocolType>()) {
1362+
Parameterized.insert({PP->getProtocol(), PP});
1363+
Protocols.push_back(PP->getProtocol());
13531364
return;
13541365
}
13551366

@@ -1391,8 +1402,8 @@ bool ProtocolType::visitAllProtocols(
13911402
return false;
13921403
}
13931404

1394-
void ProtocolType::canonicalizeProtocols(
1395-
SmallVectorImpl<ProtocolDecl *> &protocols) {
1405+
static void canonicalizeProtocols(SmallVectorImpl<ProtocolDecl *> &protocols,
1406+
ParameterizedProtocolMap *parameterized) {
13961407
llvm::SmallDenseMap<ProtocolDecl *, unsigned> known;
13971408
bool zappedAny = false;
13981409

@@ -1424,6 +1435,10 @@ void ProtocolType::canonicalizeProtocols(
14241435

14251436
auto found = known.find(inherited);
14261437
if (found != known.end()) {
1438+
// Don't zap protocols associated with parameterized types.
1439+
if (parameterized && parameterized->count(inherited))
1440+
return TypeWalker::Action::Continue;
1441+
14271442
protocols[found->second] = nullptr;
14281443
zappedAny = true;
14291444
}
@@ -1442,6 +1457,11 @@ void ProtocolType::canonicalizeProtocols(
14421457
llvm::array_pod_sort(protocols.begin(), protocols.end(), TypeDecl::compare);
14431458
}
14441459

1460+
void ProtocolType::canonicalizeProtocols(
1461+
SmallVectorImpl<ProtocolDecl *> &protocols) {
1462+
return ::canonicalizeProtocols(protocols, nullptr);
1463+
}
1464+
14451465
static void
14461466
getCanonicalParams(AnyFunctionType *funcType,
14471467
CanGenericSignature genericSig,
@@ -4020,25 +4040,39 @@ Type ProtocolCompositionType::get(const ASTContext &C,
40204040

40214041
Type Superclass;
40224042
SmallVector<ProtocolDecl *, 4> Protocols;
4043+
ParameterizedProtocolMap Parameterized;
40234044
for (Type t : Members) {
4024-
addProtocols(t, Protocols, Superclass, HasExplicitAnyObject);
4045+
addProtocols(t, Protocols, Parameterized, Superclass, HasExplicitAnyObject);
40254046
}
4026-
4027-
// Minimize the set of protocols composed together.
4028-
ProtocolType::canonicalizeProtocols(Protocols);
40294047

40304048
// The presence of a superclass constraint makes AnyObject redundant.
40314049
if (Superclass)
40324050
HasExplicitAnyObject = false;
40334051

4034-
// Form the set of canonical protocol types from the protocol
4035-
// declarations, and use that to build the canonical composition type.
4052+
// If there are any parameterized protocols, the canonicalization
4053+
// algorithm gets more complex.
4054+
4055+
// Form the set of canonical component types.
40364056
SmallVector<Type, 4> CanTypes;
40374057
if (Superclass)
40384058
CanTypes.push_back(Superclass->getCanonicalType());
4039-
llvm::transform(
4040-
Protocols, std::back_inserter(CanTypes),
4041-
[](ProtocolDecl *Proto) { return Proto->getDeclaredInterfaceType(); });
4059+
4060+
canonicalizeProtocols(Protocols, &Parameterized);
4061+
4062+
for (auto proto: Protocols) {
4063+
// If we have a parameterized type for this protocol, use the
4064+
// canonical type of that. Sema should prevent us from building
4065+
// compositions with the same protocol and conflicting constraints.
4066+
if (!Parameterized.empty()) {
4067+
auto it = Parameterized.find(proto);
4068+
if (it != Parameterized.end()) {
4069+
CanTypes.push_back(it->second->getCanonicalType());
4070+
continue;
4071+
}
4072+
}
4073+
4074+
CanTypes.push_back(proto->getDeclaredInterfaceType());
4075+
}
40424076

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

0 commit comments

Comments
 (0)