From ea20a1d1372a6a032d2bdbf348d1d982cdedb7ab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 1 Jun 2017 08:25:42 -0700 Subject: [PATCH 1/4] [AST] Add DeclContext::get(Syntactic|Semantic)Depth. Utilities functions used when comparing declaration contexts. Only the syntactic form is currently used (for the owning DC of a generic environment). --- include/swift/AST/DeclContext.h | 13 +++++++++++++ lib/AST/DeclContext.cpp | 24 ++++++++++++++++++++++++ lib/AST/GenericEnvironment.cpp | 15 ++------------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index aafc243887132..b3ba6625ad90e 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -494,6 +494,19 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { = nullptr, bool sorted = false) const; + /// Retrieve the syntactic depth of this declaration context, i.e., + /// the number of non-module-scoped contexts. + /// + /// For an extension of a nested type, the extension is depth 1. + unsigned getSyntacticDepth() const; + + /// Retrieve the semantic depth of this declaration context, i.e., + /// the number of non-module-scoped contexts. + /// + /// For an extension of a nested type, the depth of the nested type itself + /// is also included. + unsigned getSemanticDepth() const; + /// \returns true if traversal was aborted, false otherwise. bool walkContext(ASTWalker &Walker); diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 4926db30679a9..4d5bfdbf3b5b6 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -656,6 +656,30 @@ DeclContext::isCascadingContextForLookup(bool functionsAreNonCascading) const { return getParent()->isCascadingContextForLookup(true); } +unsigned DeclContext::getSyntacticDepth() const { + // Module scope == depth 0. + if (isModuleScopeContext()) + return 0; + + return 1 + getParent()->getSyntacticDepth(); +} + +unsigned DeclContext::getSemanticDepth() const { + // For extensions, count the depth of the nominal type being extended. + if (auto ext = dyn_cast(this)) { + if (auto nominal = getAsNominalTypeOrNominalTypeExtensionContext()) + return nominal->getSemanticDepth(); + + return 1; + } + + // Module scope == depth 0. + if (isModuleScopeContext()) + return 0; + + return 1 + getParent()->getSemanticDepth(); +} + bool DeclContext::walkContext(ASTWalker &Walker) { switch (getContextKind()) { case DeclContextKind::Module: diff --git a/lib/AST/GenericEnvironment.cpp b/lib/AST/GenericEnvironment.cpp index 1292e2f976e0d..979f8995d8033 100644 --- a/lib/AST/GenericEnvironment.cpp +++ b/lib/AST/GenericEnvironment.cpp @@ -33,17 +33,6 @@ GenericEnvironment::GenericEnvironment(GenericSignature *signature, Type()); } -/// Compute the depth of the \c DeclContext chain. -static unsigned declContextDepth(const DeclContext *dc) { - unsigned depth = 0; - while (auto parentDC = dc->getParent()) { - ++depth; - dc = parentDC; - } - - return depth; -} - void GenericEnvironment::setOwningDeclContext(DeclContext *newOwningDC) { if (!OwningDC) { OwningDC = newOwningDC; @@ -54,8 +43,8 @@ void GenericEnvironment::setOwningDeclContext(DeclContext *newOwningDC) { return; // Find the least common ancestor context to be the owner. - unsigned oldDepth = declContextDepth(OwningDC); - unsigned newDepth = declContextDepth(newOwningDC); + unsigned oldDepth = OwningDC->getSyntacticDepth(); + unsigned newDepth = newOwningDC->getSyntacticDepth(); while (oldDepth > newDepth) { OwningDC = OwningDC->getParent(); From 5628580ffb8ea6afca908dc993fc8c636ba7bd08 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 1 Jun 2017 08:26:26 -0700 Subject: [PATCH 2/4] [AST] Introduce TypeDecl::compare() to provide an ordering for type declarations. Generalizes ProtocolType::compareProtocols() for more general use. --- include/swift/AST/Decl.h | 3 +++ lib/AST/Decl.cpp | 33 +++++++++++++++++++++++++++++++++ lib/AST/Type.cpp | 12 +----------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 14852dcc3a2f4..9416e6d3f4e76 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2372,6 +2372,9 @@ class TypeDecl : public ValueDecl { return D->getKind() >= DeclKind::First_TypeDecl && D->getKind() <= DeclKind::Last_TypeDecl; } + + /// Compute an ordering between two type declarations that is ABI-stable. + static int compare(const TypeDecl *type1, const TypeDecl *type2); }; /// A type declaration that can have generic parameters attached to it. Because diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 412a0abbb7a8c..f9ab2d6bf2d1b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2067,6 +2067,39 @@ Type TypeDecl::getDeclaredInterfaceType() const { return interfaceType->castTo()->getInstanceType(); } +int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) { + // Order based on the enclosing declaration. + auto dc1 = type1->getDeclContext(); + auto dc2 = type2->getDeclContext(); + + // Prefer lower depths. + auto depth1 = dc1->getSemanticDepth(); + auto depth2 = dc2->getSemanticDepth(); + if (depth1 != depth2) + return depth1 < depth2 ? -1 : +1; + + // Prefer module names earlier in the alphabet. + if (dc1->isModuleScopeContext() && dc2->isModuleScopeContext()) { + auto module1 = dc1->getParentModule(); + auto module2 = dc2->getParentModule(); + if (int result = module1->getName().str().compare(module2->getName().str())) + return result; + } + + auto nominal1 = dc1->getAsNominalTypeOrNominalTypeExtensionContext(); + auto nominal2 = dc2->getAsNominalTypeOrNominalTypeExtensionContext(); + if (static_cast(nominal1) != static_cast(nominal2)) { + return static_cast(nominal1) ? -1 : +1; + } + if (nominal1 && nominal2) { + if (int result = compare(nominal1, nominal2)) + return result; + } + + return type1->getBaseName().getIdentifier().str().compare( + type2->getBaseName().getIdentifier().str()); +} + bool NominalTypeDecl::hasFixedLayout() const { // Private and (unversioned) internal types always have a // fixed layout. diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index eff1302ffe9f0..59dfc2ee35d80 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -948,17 +948,7 @@ static void addMinimumProtocols(Type T, /// \brief Compare two protocols to establish an ordering between them. int ProtocolType::compareProtocols(ProtocolDecl * const* PP1, ProtocolDecl * const* PP2) { - auto *P1 = *PP1; - auto *P2 = *PP2; - ModuleDecl *M1 = P1->getParentModule(); - ModuleDecl *M2 = P2->getParentModule(); - - // Try ordering based on module name, first. - if (int result = M1->getName().str().compare(M2->getName().str())) - return result; - - // Order based on protocol name. - return P1->getName().str().compare(P2->getName().str()); + return TypeDecl::compare(*PP1, *PP2); } bool ProtocolType::visitAllProtocols( From 899e68ef0905c4a286baae50a6d661ea182bc3d5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 1 Jun 2017 13:52:39 -0700 Subject: [PATCH 3/4] [GSB] Generalize potential archetype from "typealias" to all concrete types. Potential archetypes can resolve to either an associated type or a typealias. Generalize the latter to "any concrete type", both because the current implementation is unnecessarily narrow (typealiases aren't actually special in this regard) and to get us closer to handling lookups via superclass constraints when resolving these types. --- include/swift/AST/GenericSignatureBuilder.h | 38 ++-- lib/AST/Decl.cpp | 12 +- lib/AST/GenericSignatureBuilder.cpp | 198 +++++++++----------- lib/Sema/TypeCheckGeneric.cpp | 13 +- 4 files changed, 128 insertions(+), 133 deletions(-) diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index cac90b3c6f2f1..3c33ab0d06b15 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -1248,7 +1248,7 @@ class GenericSignatureBuilder::PotentialArchetype { Identifier name; /// The associated type or typealias for a resolved nested type. - TypeDecl *assocTypeOrAlias; + TypeDecl *assocTypeOrConcrete; /// The generic parameter key for a root. GenericParamKey genericParam; @@ -1256,10 +1256,10 @@ class GenericSignatureBuilder::PotentialArchetype { PAIdentifier(Identifier name) : name(name) { } PAIdentifier(AssociatedTypeDecl *assocType) - : assocTypeOrAlias(assocType) { } + : assocTypeOrConcrete(assocType) { } - PAIdentifier(TypeAliasDecl *typeAlias) - : assocTypeOrAlias(typeAlias) { } + PAIdentifier(TypeDecl *concreteDecl) + : assocTypeOrConcrete(concreteDecl) { } PAIdentifier(GenericParamKey genericParam) : genericParam(genericParam) { } } identifier; @@ -1342,9 +1342,9 @@ class GenericSignatureBuilder::PotentialArchetype { assert(parent != nullptr && "Not an associated type?"); } - /// \brief Construct a new potential archetype for a type alias. - PotentialArchetype(PotentialArchetype *parent, TypeAliasDecl *typeAlias) - : parentOrBuilder(parent), identifier(typeAlias), + /// \brief Construct a new potential archetype for a concrete declaration. + PotentialArchetype(PotentialArchetype *parent, TypeDecl *concreteDecl) + : parentOrBuilder(parent), identifier(concreteDecl), isUnresolvedNestedType(false), IsRecursive(false), Invalid(false), DiagnosedRename(false) @@ -1396,7 +1396,7 @@ class GenericSignatureBuilder::PotentialArchetype { if (isUnresolvedNestedType) return nullptr; - return dyn_cast(identifier.assocTypeOrAlias); + return dyn_cast(identifier.assocTypeOrConcrete); } /// Resolve the potential archetype to the given associated type. @@ -1404,8 +1404,8 @@ class GenericSignatureBuilder::PotentialArchetype { GenericSignatureBuilder &builder); /// Resolve the potential archetype to the given typealias. - void resolveTypeAlias(TypeAliasDecl *typealias, - GenericSignatureBuilder &builder); + void resolveConcreteType(TypeDecl *concreteDecl, + GenericSignatureBuilder &builder); /// Determine whether this is a generic parameter. bool isGenericParam() const { @@ -1436,16 +1436,19 @@ class GenericSignatureBuilder::PotentialArchetype { if (isUnresolvedNestedType) return identifier.name; - return identifier.assocTypeOrAlias->getName(); + return identifier.assocTypeOrConcrete->getName(); } - /// Retrieve the type alias. - TypeAliasDecl *getTypeAliasDecl() const { + /// Retrieve the concrete type declaration. + TypeDecl *getConcreteTypeDecl() const { assert(getParent() && "not a nested type"); if (isUnresolvedNestedType) return nullptr; - return dyn_cast(identifier.assocTypeOrAlias); + if (isa(identifier.assocTypeOrConcrete)) + return nullptr; + + return identifier.assocTypeOrConcrete; } /// Retrieve the set of protocols to which this potential archetype @@ -1548,8 +1551,9 @@ class GenericSignatureBuilder::PotentialArchetype { PotentialArchetype *getNestedType(AssociatedTypeDecl *assocType, GenericSignatureBuilder &builder); - /// \brief Retrieve (or create) a nested type with a known typealias. - PotentialArchetype *getNestedType(TypeAliasDecl *typealias, + /// \brief Retrieve (or create) a nested type with a known concrete type + /// declaration. + PotentialArchetype *getNestedType(TypeDecl *concreteDecl, GenericSignatureBuilder &builder); /// Describes the kind of update that is performed. @@ -1581,7 +1585,7 @@ class GenericSignatureBuilder::PotentialArchetype { /// type or typealias of the given protocol, unless the \c kind implies that /// a potential archetype should not be created if it's missing. PotentialArchetype *updateNestedTypeForConformance( - PointerUnion type, + PointerUnion type, NestedTypeUpdate kind); /// Update the named nested type when we know this type conforms to the given diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f9ab2d6bf2d1b..fc702ac4af5e3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2096,8 +2096,16 @@ int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) { return result; } - return type1->getBaseName().getIdentifier().str().compare( - type2->getBaseName().getIdentifier().str()); + if (int result = type1->getBaseName().getIdentifier().str().compare( + type2->getBaseName().getIdentifier().str())) + return result; + + // Error case: two type declarations that cannot be distinguished. + if (type1 < type2) + return -1; + if (type1 > type2) + return +1; + return 0; } bool NominalTypeDecl::hasFixedLayout() const { diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 4e4ca684f72c2..d68661a8f270a 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -1057,8 +1057,9 @@ std::string GenericSignatureBuilder::PotentialArchetype::getDebugName() const { ProtocolDecl *proto = nullptr; if (auto assocType = getResolvedAssociatedType()) { proto = assocType->getProtocol(); - } else if (auto typeAlias = getTypeAliasDecl()) { - proto = typeAlias->getParent()->getAsProtocolOrProtocolExtensionContext(); + } else if (auto concreteDecl = getConcreteTypeDecl()) { + proto = concreteDecl->getDeclContext() + ->getAsProtocolOrProtocolExtensionContext(); } if (proto) { @@ -1086,20 +1087,20 @@ void GenericSignatureBuilder::PotentialArchetype::resolveAssociatedType( GenericSignatureBuilder &builder) { assert(isUnresolvedNestedType && "associated type is already resolved"); isUnresolvedNestedType = false; - identifier.assocTypeOrAlias = assocType; + identifier.assocTypeOrConcrete = assocType; assert(assocType->getName() == getNestedName()); assert(builder.Impl->NumUnresolvedNestedTypes > 0 && "Mismatch in number of unresolved nested types"); --builder.Impl->NumUnresolvedNestedTypes; } -void GenericSignatureBuilder::PotentialArchetype::resolveTypeAlias( - TypeAliasDecl *typealias, +void GenericSignatureBuilder::PotentialArchetype::resolveConcreteType( + TypeDecl *concreteDecl, GenericSignatureBuilder &builder) { assert(isUnresolvedNestedType && "nested type is already resolved"); isUnresolvedNestedType = false; - identifier.assocTypeOrAlias = typealias; - assert(typealias->getName() == getNestedName()); + identifier.assocTypeOrConcrete = concreteDecl; + assert(concreteDecl->getName() == getNestedName()); assert(builder.Impl->NumUnresolvedNestedTypes > 0 && "Mismatch in number of unresolved nested types"); --builder.Impl->NumUnresolvedNestedTypes; @@ -1408,30 +1409,6 @@ static int compareAssociatedTypes(AssociatedTypeDecl *assocType1, return 0; } -/// Compare two typealiases in protocols. -static int compareTypeAliases(TypeAliasDecl *typealias1, - TypeAliasDecl *typealias2) { - // - by name. - if (int result = typealias1->getName().str().compare( - typealias2->getName().str())) - return result; - - // - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q) - auto proto1 = - typealias1->getDeclContext()->getAsProtocolOrProtocolExtensionContext(); - auto proto2 = - typealias2->getDeclContext()->getAsProtocolOrProtocolExtensionContext(); - if (int compareProtocols = ProtocolType::compareProtocols(&proto1, &proto2)) - return compareProtocols; - - // Error case: if we have two associated types with the same name in the - // same protocol, just tie-break based on address. - if (typealias1 != typealias2) - return typealias1 < typealias2 ? -1 : +1; - - return 0; -} - /// Canonical ordering for dependent types in generic signatures. static int compareDependentTypes(PotentialArchetype * const* pa, PotentialArchetype * const* pb) { @@ -1441,12 +1418,12 @@ static int compareDependentTypes(PotentialArchetype * const* pa, if (a == b) return 0; - // Typealiases must be ordered *after* everything else, to ensure they - // don't become representatives in the case where a typealias is equated + // Concrete types must be ordered *after* everything else, to ensure they + // don't become representatives in the case where a concrete type is equated // with an associated type. if (a->getParent() && b->getParent() && - !!a->getTypeAliasDecl() != !!b->getTypeAliasDecl()) - return a->getTypeAliasDecl() ? +1 : -1; + !!a->getConcreteTypeDecl() != !!b->getConcreteTypeDecl()) + return a->getConcreteTypeDecl() ? +1 : -1; // Types that are equivalent to concrete types follow types that are still // type parameters. @@ -1489,12 +1466,13 @@ static int compareDependentTypes(PotentialArchetype * const* pa, return +1; } - // Make sure typealiases are properly ordered, to avoid crashers. - if (auto *aa = a->getTypeAliasDecl()) { - auto *ab = b->getTypeAliasDecl(); + // Make sure concrete type declarations are properly ordered, to avoid + // crashers. + if (auto *aa = a->getConcreteTypeDecl()) { + auto *ab = b->getConcreteTypeDecl(); assert(ab != nullptr && "Should have handled this case above"); - if (int result = compareTypeAliases(aa, ab)) + if (int result = TypeDecl::compare(aa, ab)) return result; } @@ -1575,11 +1553,11 @@ namespace { PotentialArchetype *pa; void operator()(Type type1, Type type2) const { - if (pa->getParent() && pa->getTypeAliasDecl() && + if (pa->getParent() && pa->getConcreteTypeDecl() && source->getLoc().isInvalid()) { - diags.diagnose(pa->getTypeAliasDecl()->getLoc(), + diags.diagnose(pa->getConcreteTypeDecl()->getLoc(), diag::protocol_typealias_conflict, - pa->getTypeAliasDecl()->getName(), + pa->getConcreteTypeDecl()->getName(), type1, type2); return; } @@ -1658,9 +1636,9 @@ PotentialArchetype *PotentialArchetype::getNestedType( } PotentialArchetype *PotentialArchetype::getNestedType( - TypeAliasDecl *typealias, + TypeDecl *getConcreteTypeDecl, GenericSignatureBuilder &builder) { - return updateNestedTypeForConformance(typealias, + return updateNestedTypeForConformance(getConcreteTypeDecl, NestedTypeUpdate::AddIfMissing); } @@ -1668,24 +1646,24 @@ PotentialArchetype *PotentialArchetype::getNestedArchetypeAnchor( Identifier name, GenericSignatureBuilder &builder, NestedTypeUpdate kind) { - // Look for the best associated type or typealias within the protocols + // Look for the best associated type or concrete type within the protocols // we know about. AssociatedTypeDecl *bestAssocType = nullptr; - TypeAliasDecl *bestTypeAlias = nullptr; - SmallVector typealiases; + TypeDecl *bestConcreteDecl = nullptr; + SmallVector concreteDecls; auto rep = getRepresentative(); for (auto proto : rep->getConformsTo()) { - // Look for an associated type and/or typealias with this name. + // Look for an associated type and/or concrete type with this name. AssociatedTypeDecl *assocType = nullptr; - TypeAliasDecl *typealias = nullptr; + TypeDecl *concreteDecl = nullptr; for (auto member : proto->lookupDirect(name, /*ignoreNewExtensions=*/true)) { if (!assocType) assocType = dyn_cast(member); - // FIXME: Filter out typealiases that aren't in the protocol itself? - if (!typealias) - typealias = dyn_cast(member); + // FIXME: Filter out type declarations that aren't in the protocol itself? + if (!concreteDecl && !isa(member)) + concreteDecl = dyn_cast(member); } if (assocType && @@ -1693,13 +1671,14 @@ PotentialArchetype *PotentialArchetype::getNestedArchetypeAnchor( compareAssociatedTypes(assocType, bestAssocType) < 0)) bestAssocType = assocType; - if (typealias) { - // Record every typealias. - typealiases.push_back(typealias); + if (concreteDecl) { + // Record every concrete type. + concreteDecls.push_back(concreteDecl); - // Track the best typealias. - if (!bestTypeAlias || compareTypeAliases(typealias, bestTypeAlias) < 0) - bestTypeAlias = typealias; + // Track the best concrete type. + if (!bestConcreteDecl || + TypeDecl::compare(concreteDecl, bestConcreteDecl) < 0) + bestConcreteDecl = concreteDecl; } } @@ -1710,25 +1689,25 @@ PotentialArchetype *PotentialArchetype::getNestedArchetypeAnchor( NestedTypeUpdate::AddIfMissing); } - // If we have an associated type, drop any typealiases that aren't in + // If we have an associated type, drop any concrete decls that aren't in // the same module as the protocol. // FIXME: This is an unprincipled hack for an unprincipled feature. - typealiases.erase( - std::remove_if(typealiases.begin(), typealiases.end(), - [&](TypeAliasDecl *typealias) { - return typealias->getParentModule() != - typealias->getDeclContext() + concreteDecls.erase( + std::remove_if(concreteDecls.begin(), concreteDecls.end(), + [&](TypeDecl *concreteDecl) { + return concreteDecl->getDeclContext()->getParentModule() != + concreteDecl->getDeclContext() ->getAsNominalTypeOrNominalTypeExtensionContext()->getParentModule(); }), - typealiases.end()); + concreteDecls.end()); - // Update for all of the typealiases with this name, which will introduce + // Update for all of the concrete decls with this name, which will introduce // various same-type constraints. - for (auto typealias : typealiases) { - auto typealiasPA = updateNestedTypeForConformance(typealias, + for (auto concreteDecl : concreteDecls) { + auto concreteDeclPA = updateNestedTypeForConformance(concreteDecl, NestedTypeUpdate::AddIfMissing); - if (!resultPA && typealias == bestTypeAlias) - resultPA = typealiasPA; + if (!resultPA && concreteDecl == bestConcreteDecl) + resultPA = concreteDeclPA; } if (resultPA) @@ -1771,48 +1750,49 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( Identifier name, ProtocolDecl *proto, NestedTypeUpdate kind) { - /// Determine whether there is an associated type or typealias with this name - /// in this protocol. If not, there's nothing to do. + /// Determine whether there is an associated type or concrete type with this + /// name in this protocol. If not, there's nothing to do. AssociatedTypeDecl *assocType = nullptr; - TypeAliasDecl *typealias = nullptr; + TypeDecl *concreteDecl = nullptr; for (auto member : proto->lookupDirect(name, /*ignoreNewExtensions=*/true)) { if (!assocType) assocType = dyn_cast(member); - // FIXME: Filter out typealiases that aren't in the protocol itself? - if (!typealias) - typealias = dyn_cast(member); + // FIXME: Filter out concrete types that aren't in the protocol itself? + if (!concreteDecl && !isa(member)) + concreteDecl = dyn_cast(member); } - // There is no associated type or typealias with this name in this protocol - if (!assocType && !typealias) + // There is no associated type or concrete type with this name in this + // protocol + if (!assocType && !concreteDecl) return nullptr; - // If we had both an associated type and a typealias, ignore the latter. This - // is for ill-formed code. + // If we had both an associated type and a concrete type, ignore the latter. + // This is for ill-formed code. if (assocType) return updateNestedTypeForConformance(assocType, kind); - return updateNestedTypeForConformance(typealias, kind); + return updateNestedTypeForConformance(concreteDecl, kind); } PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( - PointerUnion type, + PointerUnion type, NestedTypeUpdate kind) { auto *assocType = type.dyn_cast(); - auto *typealias = type.dyn_cast(); - if (!assocType && !typealias) + auto *concreteDecl = type.dyn_cast(); + if (!assocType && !concreteDecl) return nullptr; - Identifier name = assocType ? assocType->getName() : typealias->getName(); + Identifier name = assocType ? assocType->getName() : concreteDecl->getName(); ProtocolDecl *proto = assocType ? assocType->getProtocol() - : typealias->getDeclContext() + : concreteDecl->getDeclContext() ->getAsProtocolOrProtocolExtensionContext(); // Look for either an unresolved potential archetype (which we can resolve // now) or a potential archetype with the appropriate associated type or - // typealias. + // concrete type. PotentialArchetype *resultPA = nullptr; auto knownNestedTypes = NestedTypes.find(name); bool shouldUpdatePA = false; @@ -1824,7 +1804,7 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( if (assocType) { existingPA->resolveAssociatedType(assocType, builder); } else { - existingPA->resolveTypeAlias(typealias, builder); + existingPA->resolveConcreteType(concreteDecl, builder); } // We've resolved this nested type; nothing more to do. @@ -1839,8 +1819,8 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( break; } - // Do we have a typealias match? - if (typealias && existingPA->getTypeAliasDecl() == typealias) { + // Do we have a concrete type match? + if (concreteDecl && existingPA->getConcreteTypeDecl() == concreteDecl) { resultPA = existingPA; break; } @@ -1860,7 +1840,7 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( if (assocType) resultPA = new PotentialArchetype(this, assocType); else - resultPA = new PotentialArchetype(this, typealias); + resultPA = new PotentialArchetype(this, concreteDecl); auto &allNested = NestedTypes[name]; allNested.push_back(resultPA); @@ -1903,20 +1883,21 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( // If we have a potential archetype that requires more processing, do so now. if (shouldUpdatePA) { - // For typealiases, introduce a same-type requirement to the aliased type. - if (typealias) { + // For concrete types, introduce a same-type requirement to the aliased + // type. + if (concreteDecl) { // FIXME (recursive decl validation): if the alias doesn't have an // interface type when getNestedType is called while building a // protocol's generic signature (i.e. during validation), then it'll // fail completely, because building that alias's interface type // requires the protocol to be validated. This seems to occur when the // alias's RHS involves archetypes from the protocol. - if (!typealias->hasInterfaceType()) - builder.getLazyResolver()->resolveDeclSignature(typealias); - if (typealias->hasInterfaceType()) { - // The protocol typealias has an underlying type written in terms + if (!concreteDecl->hasInterfaceType()) + builder.getLazyResolver()->resolveDeclSignature(concreteDecl); + if (concreteDecl->hasInterfaceType()) { + // The protocol concrete type has an underlying type written in terms // of the protocol's 'Self' type. - auto type = typealias->getDeclaredInterfaceType(); + auto type = concreteDecl->getDeclaredInterfaceType(); // Substitute in the type of the current PotentialArchetype in // place of 'Self' here. @@ -2399,16 +2380,16 @@ auto GenericSignatureBuilder::resolve(UnresolvedType paOrT, } auto rep = pa->getRepresentative(); - if (!rep->getParent() || !rep->getTypeAliasDecl()) + if (!rep->getParent() || !rep->getConcreteTypeDecl()) return ResolvedType::forPotentialArchetype(pa); // We're assuming that an equivalence class with a type alias representative // doesn't have a "true" (i.e. associated type) potential archetype. assert(llvm::all_of(rep->getEquivalenceClassMembers(), [&](PotentialArchetype *pa) { - return pa->getParent() && pa->getTypeAliasDecl(); + return pa->getParent() && pa->getConcreteTypeDecl(); }) && - "unexpected typealias representative with non-typealias equivalent"); + "unexpected concrete type representative with non-concrete equivalent"); return ResolvedType::forPotentialArchetype(pa); } @@ -2607,16 +2588,17 @@ ConstraintResult GenericSignatureBuilder::addConformanceRequirement( assocType); } - if (auto typealias = dyn_cast(typeDecl)) { - // Resolve the underlying type, if we haven't done so yet. - if (!typealias->hasInterfaceType()) { - getLazyResolver()->resolveDeclSignature(typealias); - } + // Resolve the underlying type, if we haven't done so yet. + if (!typeDecl->hasInterfaceType()) { + getLazyResolver()->resolveDeclSignature(typeDecl); + } + if (auto typealias = dyn_cast(typeDecl)) { return typealias->getUnderlyingTypeLoc().getType(); } - return Type(); + // FIXME: Substitutions required! + return typeDecl->getDeclaredInterfaceType(); }; // An inferred same-type requirement between the two type declarations @@ -4024,7 +4006,7 @@ GenericSignatureBuilder::finalize(SourceLoc loc, visitPotentialArchetypes([&](PotentialArchetype *pa) { // We only care about nested types that haven't been resolved. if (pa->getParent() == nullptr || pa->getResolvedAssociatedType() || - pa->getTypeAliasDecl() || + pa->getConcreteTypeDecl() || /* FIXME: Should be able to handle this earlier */pa->getSuperclass()) return; diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index d1f4f49158d6f..5ef1362da767d 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -209,12 +209,13 @@ Type CompleteGenericTypeResolver::resolveDependentMemberType( return DependentMemberType::get(baseTy, assocType); } - // If the nested type comes from a type alias, use either the alias's - // concrete type, or resolve its components down to another dependent member. - if (auto alias = nestedPA->getTypeAliasDecl()) { - assert(!alias->getGenericParams() && "Generic typealias in protocol"); - ref->setValue(alias); - return TC.substMemberTypeWithBase(DC->getParentModule(), alias, baseTy); + // If the nested type comes from a concrete type, substitute the base type + // into it. + if (auto concrete = nestedPA->getConcreteTypeDecl()) { + ref->setValue(concrete); + if (baseTy->isTypeParameter()) + return DependentMemberType::get(baseTy, concrete->getName()); + return TC.substMemberTypeWithBase(DC->getParentModule(), concrete, baseTy); } Identifier name = ref->getIdentifier(); From 4fbb3ec611d89985eef15b309c0f326f2db27dc5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 1 Jun 2017 16:18:02 -0700 Subject: [PATCH 4/4] [GSB] Resolve nested types by looking into superclass constraints. This was previously handled very late, by the type checker, which led to weird ordering dependencies and meant that we could end up with well-formed code where the GSB was left with unresolved types. We want such states to never exist, so make sure we can resolve everything in the GSB. --- include/swift/AST/GenericSignatureBuilder.h | 3 + lib/AST/GenericSignatureBuilder.cpp | 87 ++++++++++++++------- lib/Sema/TypeCheckGeneric.cpp | 39 ++++----- test/Generics/superclass_constraint.swift | 15 ++++ 4 files changed, 98 insertions(+), 46 deletions(-) diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 3c33ab0d06b15..f44ad76962d91 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -1399,6 +1399,9 @@ class GenericSignatureBuilder::PotentialArchetype { return dyn_cast(identifier.assocTypeOrConcrete); } + /// Determine whether this PA is still unresolved. + bool isUnresolved() const { return isUnresolvedNestedType; } + /// Resolve the potential archetype to the given associated type. void resolveAssociatedType(AssociatedTypeDecl *assocType, GenericSignatureBuilder &builder); diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index d68661a8f270a..283779ad1e0ab 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -1286,7 +1286,7 @@ static void maybeAddSameTypeRequirementForNestedType( if (!superSource) return; auto assocType = nestedPA->getResolvedAssociatedType(); - assert(assocType && "Not resolved to an associated type?"); + if (!assocType) return; // Dig out the type witness. auto superConformance = superSource->getProtocolConformance(); @@ -1701,6 +1701,28 @@ PotentialArchetype *PotentialArchetype::getNestedArchetypeAnchor( }), concreteDecls.end()); + // If we haven't found anything yet but have a superclass, look for a type + // in the superclass. + if (!resultPA && concreteDecls.empty()) { + if (auto superclass = getSuperclass()) { + if (auto classDecl = superclass->getClassOrBoundGenericClass()) { + SmallVector superclassMembers; + classDecl->getParentModule()->lookupQualified(superclass, name, NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, nullptr, + superclassMembers); + for (auto member : superclassMembers) { + if (auto concreteDecl = dyn_cast(member)) { + // Track the best concrete type. + if (!bestConcreteDecl || + TypeDecl::compare(concreteDecl, bestConcreteDecl) < 0) + bestConcreteDecl = concreteDecl; + + concreteDecls.push_back(concreteDecl); + } + } + } + } + } + // Update for all of the concrete decls with this name, which will introduce // various same-type constraints. for (auto concreteDecl : concreteDecls) { @@ -1899,13 +1921,22 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( // of the protocol's 'Self' type. auto type = concreteDecl->getDeclaredInterfaceType(); - // Substitute in the type of the current PotentialArchetype in - // place of 'Self' here. - auto subMap = SubstitutionMap::getProtocolSubstitutions( - proto, getDependentType(/*genericParams=*/{}, - /*allowUnresolved=*/true), - ProtocolConformanceRef(proto)); - type = type.subst(subMap, SubstFlags::UseErrorType); + if (proto) { + // Substitute in the type of the current PotentialArchetype in + // place of 'Self' here. + auto subMap = SubstitutionMap::getProtocolSubstitutions( + proto, getDependentType(/*genericParams=*/{}, + /*allowUnresolved=*/true), + ProtocolConformanceRef(proto)); + type = type.subst(subMap, SubstFlags::UseErrorType); + } else { + // Substitute in the superclass type. + auto superclass = getSuperclass(); + auto superclassDecl = superclass->getClassOrBoundGenericClass(); + type = superclass->getTypeOfMember( + superclassDecl->getParentModule(), concreteDecl, + concreteDecl->getDeclaredInterfaceType()); + } builder.addSameTypeRequirement( UnresolvedType(resultPA), @@ -1917,8 +1948,12 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance( // If there's a superclass constraint that conforms to the protocol, // add the appropriate same-type relationship. - if (auto superSource = builder.resolveSuperConformance(this, proto)) - maybeAddSameTypeRequirementForNestedType(resultPA, superSource, builder); + if (proto) { + if (auto superSource = builder.resolveSuperConformance(this, proto)) { + maybeAddSameTypeRequirementForNestedType(resultPA, superSource, + builder); + } + } // We know something concrete about the parent PA, so we need to propagate // that information to this new archetype. @@ -2379,18 +2414,6 @@ auto GenericSignatureBuilder::resolve(UnresolvedType paOrT, if (!pa) return None; } - auto rep = pa->getRepresentative(); - if (!rep->getParent() || !rep->getConcreteTypeDecl()) - return ResolvedType::forPotentialArchetype(pa); - - // We're assuming that an equivalence class with a type alias representative - // doesn't have a "true" (i.e. associated type) potential archetype. - assert(llvm::all_of(rep->getEquivalenceClassMembers(), - [&](PotentialArchetype *pa) { - return pa->getParent() && pa->getConcreteTypeDecl(); - }) && - "unexpected concrete type representative with non-concrete equivalent"); - return ResolvedType::forPotentialArchetype(pa); } @@ -2597,7 +2620,6 @@ ConstraintResult GenericSignatureBuilder::addConformanceRequirement( return typealias->getUnderlyingTypeLoc().getType(); } - // FIXME: Substitutions required! return typeDecl->getDeclaredInterfaceType(); }; @@ -2827,11 +2849,24 @@ void GenericSignatureBuilder::updateSuperclass( } }; + // Local function to resolve nested types found in the superclass. + auto updateSuperclassNestedTypes = [&] { + for (auto &nested : T->getNestedTypes()) { + if (nested.second.empty()) continue; + if (nested.second.front()->isUnresolved()) { + (void)T->getNestedArchetypeAnchor(nested.first, *this, + PotentialArchetype::NestedTypeUpdate::ResolveExisting); + } + } + }; + // If we haven't yet recorded a superclass constraint for this equivalence // class, do so now. if (!equivClass->superclass) { equivClass->superclass = superclass; updateSuperclassConformances(); + updateSuperclassNestedTypes(); + // Presence of a superclass constraint implies a _Class layout // constraint. auto layoutReqSource = source->viaSuperclass(*this, nullptr); @@ -2862,6 +2897,7 @@ void GenericSignatureBuilder::updateSuperclass( // We've strengthened the bound, so update superclass conformances. updateSuperclassConformances(); + updateSuperclassNestedTypes(); return; } @@ -4005,10 +4041,7 @@ GenericSignatureBuilder::finalize(SourceLoc loc, if (Impl->NumUnresolvedNestedTypes > 0) { visitPotentialArchetypes([&](PotentialArchetype *pa) { // We only care about nested types that haven't been resolved. - if (pa->getParent() == nullptr || pa->getResolvedAssociatedType() || - pa->getConcreteTypeDecl() || - /* FIXME: Should be able to handle this earlier */pa->getSuperclass()) - return; + if (!pa->isUnresolved()) return; // Try to typo correct to a nested type name. Identifier correction = typoCorrectNestedType(pa); diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 5ef1362da767d..7d76f182e2e1a 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -213,32 +213,33 @@ Type CompleteGenericTypeResolver::resolveDependentMemberType( // into it. if (auto concrete = nestedPA->getConcreteTypeDecl()) { ref->setValue(concrete); - if (baseTy->isTypeParameter()) - return DependentMemberType::get(baseTy, concrete->getName()); - return TC.substMemberTypeWithBase(DC->getParentModule(), concrete, baseTy); - } - - Identifier name = ref->getIdentifier(); - SourceLoc nameLoc = ref->getIdLoc(); - // Check whether the name can be found in the superclass. - // FIXME: The generic signature builder should be doing this and mapping down to a - // concrete type. - if (auto superclassTy = basePA->getSuperclass()) { - if (auto lookup = TC.lookupMemberType(DC, superclassTy, name)) { - if (lookup.isAmbiguous()) { - TC.diagnoseAmbiguousMemberType(baseTy, baseRange, name, nameLoc, - lookup); - return ErrorType::get(TC.Context); + if (baseTy->isTypeParameter()) { + if (auto proto = + concrete->getDeclContext() + ->getAsProtocolOrProtocolExtensionContext()) { + auto subMap = SubstitutionMap::getProtocolSubstitutions( + proto, baseTy, ProtocolConformanceRef(proto)); + return concrete->getDeclaredInterfaceType().subst(subMap); + } + + if (auto superclass = basePA->getSuperclass()) { + return superclass->getTypeOfMember( + DC->getParentModule(), concrete, + concrete->getDeclaredInterfaceType()); } - ref->setValue(lookup.front().first); - // FIXME: Record (via type sugar) that this was referenced via baseTy. - return lookup.front().second; + llvm_unreachable("shouldn't have a concrete decl here"); } + + return TC.substMemberTypeWithBase(DC->getParentModule(), concrete, baseTy); } + assert(nestedPA->isUnresolved() && "meaningless unresolved type"); + // Complain that there is no suitable type. + Identifier name = ref->getIdentifier(); + SourceLoc nameLoc = ref->getIdLoc(); TC.diagnose(nameLoc, diag::invalid_member_type, name, baseTy) .highlight(baseRange); return ErrorType::get(TC.Context); diff --git a/test/Generics/superclass_constraint.swift b/test/Generics/superclass_constraint.swift index efb35a3eef15a..0d585e3f36d3a 100644 --- a/test/Generics/superclass_constraint.swift +++ b/test/Generics/superclass_constraint.swift @@ -152,3 +152,18 @@ class Classical : Elementary { } func genericFunc(_: T, _: U) where T.Element == U.Element {} + +// Lookup within superclass constraints. +protocol P8 { + associatedtype B +} + +class C8 { + struct A { } +} + +func superclassLookup1(_: T) where T.A == T.B { } + +func superclassLookup2(_: T) where T.A == T.B, T: C8 { } + +func superclassLookup3(_: T) where T.A == T.B, T: C8, T: P8 { }