AST: Update ProtocolCompositionType::get() for subclass existentials

Don't assume all members are existential types; we could have
a class (or a class-constrained existential) also, and this
needs to be handled.

When building a canonical protocol composition, the subclass
requirement always comes first in the list of types. This is
an important invariant allowing ExistentialLayout to be
calculated quickly.

Also, calculate the recursive properties of the composition
type from the recursive properties of its members, since they're
no longer trivial if the composition contains a class member
with generic parameters.
This commit is contained in:
Slava Pestov
2017-04-09 04:03:31 -07:00
parent d7c1d817e3
commit 32a7505847
3 changed files with 50 additions and 26 deletions

View File

@@ -3689,10 +3689,11 @@ private:
static ProtocolCompositionType *build(const ASTContext &C,
ArrayRef<Type> Protocols);
ProtocolCompositionType(const ASTContext *Ctx, ArrayRef<Type> Protocols)
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/Ctx,
RecursiveTypeProperties()),
Protocols(Protocols) { }
ProtocolCompositionType(const ASTContext *ctx, ArrayRef<Type> protocols,
RecursiveTypeProperties properties)
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/ctx,
properties),
Protocols(protocols) { }
};
BEGIN_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)
/// In the canonical representation, these are all ProtocolTypes.

View File

@@ -270,6 +270,7 @@ struct ASTContext::Implementation {
llvm::FoldingSet<UnboundGenericType> UnboundGenericTypes;
llvm::FoldingSet<BoundGenericType> BoundGenericTypes;
llvm::FoldingSet<ProtocolType> ProtocolTypes;
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
llvm::FoldingSet<LayoutConstraintInfo> LayoutConstraints;
/// The set of normal protocol conformances.
@@ -312,7 +313,6 @@ struct ASTContext::Implementation {
llvm::DenseMap<CanType, SILBlockStorageType *> SILBlockStorageTypes;
llvm::FoldingSet<SILBoxType> SILBoxTypes;
llvm::DenseMap<BuiltinIntegerWidth, BuiltinIntegerType*> IntegerTypes;
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
llvm::FoldingSet<BuiltinVectorType> BuiltinVectorTypes;
llvm::FoldingSet<GenericSignature> GenericSignatures;
llvm::FoldingSet<DeclName::CompoundDeclName> CompoundNames;
@@ -2798,23 +2798,31 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Protocols) {
void *InsertPos = nullptr;
llvm::FoldingSetNodeID ID;
ProtocolCompositionType::Profile(ID, Protocols);
if (ProtocolCompositionType *Result
= C.Impl.ProtocolCompositionTypes.FindNodeOrInsertPos(ID, InsertPos))
return Result;
bool isCanonical = true;
RecursiveTypeProperties properties;
for (Type t : Protocols) {
if (!t->isCanonical())
isCanonical = false;
properties |= t->getRecursiveProperties();
}
// Create a new protocol composition type.
ProtocolCompositionType *New
= new (C, AllocationArena::Permanent)
auto arena = getArena(properties);
if (auto compTy
= C.Impl.getArena(arena).ProtocolCompositionTypes
.FindNodeOrInsertPos(ID, InsertPos))
return compTy;
auto compTy
= new (C, arena)
ProtocolCompositionType(isCanonical ? &C : nullptr,
C.AllocateCopy(Protocols));
C.Impl.ProtocolCompositionTypes.InsertNode(New, InsertPos);
return New;
C.AllocateCopy(Protocols),
properties);
C.Impl.getArena(arena).ProtocolCompositionTypes
.InsertNode(compTy, InsertPos);
return compTy;
}
ReferenceStorageType *ReferenceStorageType::get(Type T, Ownership ownership,

View File

@@ -894,13 +894,24 @@ Type TypeBase::getRValueInstanceType() {
/// \brief Collect the protocols in the existential type T into the given
/// vector.
static void addProtocols(Type T, SmallVectorImpl<ProtocolDecl *> &Protocols) {
static void addProtocols(Type T, SmallVectorImpl<ProtocolDecl *> &Protocols,
Type &Superclass) {
if (auto Proto = T->getAs<ProtocolType>()) {
Protocols.push_back(Proto->getDecl());
} else if (auto PC = T->getAs<ProtocolCompositionType>()) {
for (auto P : PC->getProtocols())
addProtocols(P, Protocols);
return;
}
if (auto PC = T->getAs<ProtocolCompositionType>()) {
for (auto P : PC->getProtocols())
addProtocols(P, Protocols, Superclass);
return;
}
assert(isa<ClassDecl>(T->getAnyNominal()) && "Non-class, non-protocol "
"member in protocol composition");
assert((!Superclass || Superclass->isEqual(T)) &&
"Should have diagnosed multiple superclasses by now");
Superclass = T;
}
/// \brief Add the protocol (or protocols) in the type T to the stack of
@@ -2751,33 +2762,37 @@ bool ProtocolCompositionType::requiresClass() const {
}
Type ProtocolCompositionType::get(const ASTContext &C,
ArrayRef<Type> ProtocolTypes) {
for (Type t : ProtocolTypes) {
ArrayRef<Type> MemberTypes) {
for (Type t : MemberTypes) {
if (!t->isCanonical())
return build(C, ProtocolTypes);
return build(C, MemberTypes);
}
Type Superclass;
SmallVector<ProtocolDecl *, 4> Protocols;
for (Type t : ProtocolTypes)
addProtocols(t, Protocols);
for (Type t : MemberTypes) {
addProtocols(t, Protocols, Superclass);
}
// Minimize the set of protocols composed together.
ProtocolType::canonicalizeProtocols(Protocols);
// If one protocol remains, its nominal type is the canonical type.
if (Protocols.size() == 1)
if (Protocols.size() == 1 && !Superclass)
return Protocols.front()->getDeclaredType();
// Form the set of canonical protocol types from the protocol
// declarations, and use that to build the canonical composition type.
SmallVector<Type, 4> CanProtocolTypes;
SmallVector<Type, 4> CanTypes;
if (Superclass)
CanTypes.push_back(Superclass->getCanonicalType());
std::transform(Protocols.begin(), Protocols.end(),
std::back_inserter(CanProtocolTypes),
std::back_inserter(CanTypes),
[](ProtocolDecl *Proto) {
return Proto->getDeclaredType();
});
return build(C, CanProtocolTypes);
return build(C, CanTypes);
}
FunctionType *