diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index e068955bdc7aa..07f360c4bc592 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -31,6 +31,7 @@ #include "swift/AST/TypeRepr.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/PointerUnion.h" @@ -654,11 +655,44 @@ class GenericSignatureBuilder { bool isRedundantExplicitRequirement(const ExplicitRequirement &req) const; private: - void computeRedundantRequirements(const ProtocolDecl *requirementSignatureSelfProto); + using GetKindAndRHS = llvm::function_ref()>; + void getBaseRequirements( + GetKindAndRHS getKindAndRHS, + const RequirementSource *source, + const ProtocolDecl *requirementSignatureSelfProto, + SmallVectorImpl &result); + + /// Determine if an explicit requirement can be derived from the + /// requirement given by \p otherSource and \p otherRHS, using the + /// knowledge of any existing redundant requirements discovered so far. + Optional + isValidRequirementDerivationPath( + llvm::SmallDenseSet &visited, + RequirementKind otherKind, + const RequirementSource *otherSource, + RequirementRHS otherRHS, + const ProtocolDecl *requirementSignatureSelfProto); + + /// Determine if the explicit requirement \p req can be derived from any + /// of the constraints in \p constraints, using the knowledge of any + /// existing redundant requirements discovered so far. + /// + /// Use \p filter to screen out less-specific and conflicting constraints + /// if the requirement is a superclass, concrete type or layout requirement. + template + void checkIfRequirementCanBeDerived( + const ExplicitRequirement &req, + const std::vector> &constraints, + const ProtocolDecl *requirementSignatureSelfProto, + Filter filter); + + void computeRedundantRequirements( + const ProtocolDecl *requirementSignatureSelfProto); void diagnoseRedundantRequirements() const; - void diagnoseConflictingConcreteTypeRequirements() const; + void diagnoseConflictingConcreteTypeRequirements( + const ProtocolDecl *requirementSignatureSelfProto); /// Describes the relationship between a given constraint and /// the canonical constraint of the equivalence class. @@ -707,23 +741,6 @@ class GenericSignatureBuilder { TypeArrayView genericParams, EquivalenceClass *equivClass); - /// Check the superclass constraints within the equivalence - /// class of the given potential archetype. - void checkSuperclassConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass); - - /// Check conformance constraints within the equivalence class of the - /// given potential archetype. - void checkConformanceConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass); - - /// Check layout constraints within the equivalence class of the given - /// potential archetype. - void checkLayoutConstraints(TypeArrayView genericParams, - EquivalenceClass *equivClass); - /// Check same-type constraints within the equivalence class of the /// given potential archetype. void checkSameTypeConstraints( @@ -1242,8 +1259,7 @@ class GenericSignatureBuilder::RequirementSource final /// requirement redundant, because without said original requirement, the /// derived requirement ceases to hold. bool isSelfDerivedSource(GenericSignatureBuilder &builder, - Type type, - bool &derivedViaConcrete) const; + Type type) const; /// For a requirement source that describes the requirement \c type:proto, /// retrieve the minimal subpath of this requirement source that will @@ -1255,8 +1271,7 @@ class GenericSignatureBuilder::RequirementSource final const RequirementSource *getMinimalConformanceSource( GenericSignatureBuilder &builder, Type type, - ProtocolDecl *proto, - bool &derivedViaConcrete) const; + ProtocolDecl *proto) const; /// Retrieve a source location that corresponds to the requirement. SourceLoc getLoc() const; diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index a0db12f991109..ad18f3d00cb30 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -224,6 +224,13 @@ FRONTEND_STATISTIC(Sema, NumAccessorBodiesSynthesized) /// amount of work the GSB does analyzing type signatures. FRONTEND_STATISTIC(Sema, NumGenericSignatureBuilders) +/// Number of steps in the GSB's redundant requirements algorithm, which is in +/// the worst-case exponential. +FRONTEND_STATISTIC(Sema, NumRedundantRequirementSteps) + +/// Number of conformance access paths we had to compute. +FRONTEND_STATISTIC(Sema, NumConformanceAccessPathsRecorded) + /// Number of lazy requirement signatures registered. FRONTEND_STATISTIC(Sema, NumLazyRequirementSignatures) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 8f9db02afed99..b2c9bb9678d68 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -445,21 +445,6 @@ class GenericSignatureBuilder::ExplicitRequirement { this->rhs = type->getCanonicalType(); } - /// For a Constraint with an explicit requirement source, we recover the - /// subject type from the requirement source and the right hand side from the - /// constraint. - /// - /// The requirement kind must be passed in since Constraint's kind is - /// implicit by virtue of which list it appears under inside its equivalence - /// class, and is not stored inside the constraint. - template - static ExplicitRequirement fromExplicitConstraint(RequirementKind kind, - Constraint constraint) { - assert(!constraint.source->isDerivedNonRootRequirement()); - - return ExplicitRequirement(kind, constraint.source, constraint.value); - } - static std::pair getKindAndRHS(const RequirementSource *source, ASTContext &ctx) { @@ -532,6 +517,10 @@ class GenericSignatureBuilder::ExplicitRequirement { return std::make_pair(RequirementKind::Superclass, type); } + case RequirementSource::RequirementSignatureSelf: + return std::make_pair(RequirementKind::Conformance, + source->getProtocolDecl()); + default: { source->dump(llvm::errs(), &ctx.SourceMgr, 0); llvm::errs() << "\n"; @@ -540,49 +529,6 @@ class GenericSignatureBuilder::ExplicitRequirement { } } - static ExplicitRequirement fromRequirementSource(const RequirementSource *source, - ASTContext &ctx) { - auto *parent = source->parent; - - RequirementKind kind; - RequirementRHS rhs; - std::tie(kind, rhs) = getKindAndRHS(source, ctx); - - return ExplicitRequirement(kind, parent, rhs); - } - - /// To recover the root explicit requirement from a Constraint, we walk the - /// requirement source up the root, and look at both the root and the next - /// innermost child. - /// Together, these give us the subject type (via the root) and the right hand - /// side of the requirement (from the next innermost child). - /// - /// The requirement kind must be passed in since Constraint's kind is - /// implicit by virtue of which list it appears under inside its equivalence - /// class, and is not stored inside the constraint. - /// - /// The constraint's actual value is ignored; that's the right hand - /// side of the derived constraint, not the right hand side of the original - /// explicit requirement. - template - static ExplicitRequirement fromDerivedConstraint(Constraint constraint, - ASTContext &ctx) { - assert(constraint.source->isDerivedNonRootRequirement()); - - const RequirementSource *root = constraint.source; - const RequirementSource *child = nullptr; - - while (root->parent && root->isDerivedRequirement()) { - child = root; - root = root->parent; - } - - assert(child != nullptr); - assert(root != nullptr); - - return fromRequirementSource(child, ctx); - } - RequirementKind getKind() const { return sourceAndKind.getInt(); } @@ -661,7 +607,6 @@ template<> struct DenseMapInfo> redundantSubpath; bool isSelfDerived = visitPotentialArchetypesAlongPath( @@ -1092,37 +1030,12 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( switch (source->kind) { case ProtocolRequirement: case InferredProtocolRequirement: { - // Special handling for top-level requirement signature requirements; - // pretend the root type is the subject type as written in the - // protocol, and not 'Self', so that we can consider this requirement - // self-derived if it depends on one of the conformances that make - // the root type valid. - if (requirementSignatureSelfProto) { - if (source->getProtocolDecl() == requirementSignatureSelfProto && - source->parent->kind == RequirementSource::RequirementSignatureSelf) { - rootType = source->getAffectedType(); - return false; - } - } - - // Note that we've seen a protocol requirement. - sawProtocolRequirement = true; - // If the base has been made concrete, note it. auto parentEquivClass = builder.resolveEquivalenceClass(parentType, ArchetypeResolutionKind::WellFormed); assert(parentEquivClass && "Not a well-formed type?"); - if (requirementSignatureSelfProto) { - if (parentEquivClass->concreteType) - derivedViaConcrete = true; - else if (parentEquivClass->superclass && - builder.lookupConformance(parentEquivClass->superclass, - source->getProtocolDecl())) - derivedViaConcrete = true; - } - // The parent potential archetype must conform to the protocol in which // this requirement resides. Add this constraint. if (auto startOfPath = @@ -1168,7 +1081,6 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( case Explicit: case Inferred: case NestedTypeNameMatch: - rootType = parentType; return false; } llvm_unreachable("unhandled kind"); @@ -1191,27 +1103,13 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( redundantSubpath->first, redundantSubpath->second); return shorterSource - ->getMinimalConformanceSource(builder, currentType, proto, derivedViaConcrete); + ->getMinimalConformanceSource(builder, currentType, proto); } // It's self-derived but we don't have a redundant subpath to eliminate. if (isSelfDerived) return nullptr; - // If we haven't seen a protocol requirement, we're done. - if (!sawProtocolRequirement) return this; - - // The root might be a nested type, which implies constraints - // for each of the protocols of the associated types referenced (if any). - for (auto depMemTy = rootType->getAs(); depMemTy; - depMemTy = depMemTy->getBase()->getAs()) { - auto assocType = depMemTy->getAssocType(); - assert(assocType); - if (addTypeConstraint(depMemTy->getBase(), assocType->getProtocol(), - nullptr)) - return nullptr; - } - return this; } @@ -2883,6 +2781,7 @@ static void concretizeNestedTypeFromConcreteParent( assert(parentEquiv->conformsTo.count(proto) > 0 && "No conformance requirement"); const RequirementSource *parentConcreteSource = nullptr; + for (const auto &constraint : parentEquiv->conformsTo.find(proto)->second) { if (!isSuperclassConstrained) { if (constraint.source->kind == RequirementSource::Concrete) { @@ -4145,7 +4044,25 @@ GenericSignatureBuilder::getConformanceAccessPath(Type type, return found->second; } - FrontendStatsTracer(Context.Stats, "get-conformance-access-path"); + auto *Stats = Context.Stats; + + FrontendStatsTracer(Stats, "get-conformance-access-path"); + + auto recordPath = [&](CanType type, ProtocolDecl *proto, + ConformanceAccessPath path) { + // Add the path to the buffer. + Impl->CurrentConformanceAccessPaths.emplace_back(type, path); + + // Add the path to the map. + auto key = std::make_pair(type, proto); + auto inserted = Impl->ConformanceAccessPaths.insert( + std::make_pair(key, path)); + assert(inserted.second); + (void) inserted; + + if (Stats) + ++Stats->getFrontendCounters().NumConformanceAccessPathsRecorded; + }; // If this is the first time we're asked to look up a conformance access path, // visit all of the root conformance requirements in our generic signature and @@ -4163,19 +4080,12 @@ GenericSignatureBuilder::getConformanceAccessPath(Type type, ArrayRef path(root); ConformanceAccessPath result(Context.AllocateCopy(path)); - // Add the path to the buffer. - Impl->CurrentConformanceAccessPaths.emplace_back(rootType, result); - - // Add the path to the map. - auto key = std::make_pair(rootType, rootProto); - auto inserted = Impl->ConformanceAccessPaths.insert( - std::make_pair(key, result)); - assert(inserted.second); - (void) inserted; + recordPath(rootType, rootProto, result); } } - // We keep going until we find the path we are looking for. + // We enumerate conformance access paths in lexshort order until we find the + // path whose corresponding type canonicalizes to the one we are looking for. while (true) { auto found = Impl->ConformanceAccessPaths.find( std::make_pair(canType, protocol)); @@ -4185,11 +4095,13 @@ GenericSignatureBuilder::getConformanceAccessPath(Type type, assert(Impl->CurrentConformanceAccessPaths.size() > 0); - // Refill the buffer. - std::vector> morePaths; + // The buffer consists of all conformance access paths of length N. + // Swap it out with an empty buffer, and fill it with all paths of + // length N+1. + std::vector> oldPaths; + std::swap(Impl->CurrentConformanceAccessPaths, oldPaths); - // From each path in the buffer, compute all paths of length plus one. - for (const auto &pair : Impl->CurrentConformanceAccessPaths) { + for (const auto &pair : oldPaths) { const auto &lastElt = pair.second.back(); auto *lastProto = lastElt.second; @@ -4213,12 +4125,12 @@ GenericSignatureBuilder::getConformanceAccessPath(Type type, if (!nextCanType->isTypeParameter()) continue; - // Check if we already have a conformance access path for this anchor. - auto key = std::make_pair(nextCanType, nextProto); - // If we've already seen a path for this conformance, skip it and - // don't add it to the buffer. - if (Impl->ConformanceAccessPaths.count(key)) + // don't add it to the buffer. Note that because we iterate over + // conformance access paths in lexshort order, the existing + // conformance access path is shorter than the one we found just now. + if (Impl->ConformanceAccessPaths.count( + std::make_pair(nextCanType, nextProto))) continue; if (entries.empty()) { @@ -4233,18 +4145,9 @@ GenericSignatureBuilder::getConformanceAccessPath(Type type, ConformanceAccessPath result = Context.AllocateCopy(entries); entries.pop_back(); - // Add the path to the buffer. - morePaths.emplace_back(nextCanType, result); - - // Add the path to the map. - auto inserted = Impl->ConformanceAccessPaths.insert( - std::make_pair(key, result)); - assert(inserted.second); - (void) inserted; + recordPath(nextCanType, nextProto, result); } } - - std::swap(morePaths, Impl->CurrentConformanceAccessPaths); } } @@ -5180,7 +5083,7 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( updateLayout(T1, equivClass2->layout); equivClass->layoutConstraints.insert( equivClass->layoutConstraints.end(), - equivClass2->layoutConstraints.begin() + 1, + equivClass2->layoutConstraints.begin(), equivClass2->layoutConstraints.end()); } } @@ -5772,13 +5675,13 @@ namespace { bool thisIsNonRedundantExplicit = (!constraint.source->isDerivedNonRootRequirement() && !builder.isRedundantExplicitRequirement( - ExplicitRequirement::fromExplicitConstraint( - kind, constraint))); + ExplicitRequirement( + kind, constraint.source, constraint.value))); bool representativeIsNonRedundantExplicit = (!representativeConstraint->source->isDerivedNonRootRequirement() && !builder.isRedundantExplicitRequirement( - ExplicitRequirement::fromExplicitConstraint( - kind, *representativeConstraint))); + ExplicitRequirement( + kind, representativeConstraint->source, representativeConstraint->value))); if (thisIsNonRedundantExplicit != representativeIsNonRedundantExplicit) { if (thisIsNonRedundantExplicit) @@ -5847,16 +5750,21 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder, bool alreadyFound = false; const RequirementSource *conformsSource = nullptr; for (const auto &constraint : conforms.second) { - if (constraint.source->getAffectedType()->isEqual(dependentType)) { + auto *minimal = constraint.source->getMinimalConformanceSource( + builder, constraint.getSubjectDependentType({ }), proto); + + if (minimal == nullptr) + continue; + + if (minimal->getAffectedType()->isEqual(dependentType)) { alreadyFound = true; break; } // Capture the source for later use, skipping if (!conformsSource && - constraint.source->kind - != RequirementSource::RequirementSignatureSelf) - conformsSource = constraint.source; + minimal->kind != RequirementSource::RequirementSignatureSelf) + conformsSource = minimal; } if (alreadyFound) continue; @@ -5872,510 +5780,6 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder, } } -static bool compareSourceLocs(SourceManager &SM, SourceLoc lhsLoc, SourceLoc rhsLoc) { - if (lhsLoc.isValid() != rhsLoc.isValid()) - return lhsLoc.isValid(); - - if (lhsLoc.isValid() && rhsLoc.isValid()) { - unsigned lhsBuffer = SM.findBufferContainingLoc(lhsLoc); - unsigned rhsBuffer = SM.findBufferContainingLoc(rhsLoc); - - // If the buffers are the same, use source location ordering. - if (lhsBuffer == rhsBuffer) { - if (SM.isBeforeInBuffer(lhsLoc, rhsLoc)) - return true; - } else { - // Otherwise, order by buffer identifier. - if (SM.getIdentifierForBuffer(lhsBuffer) - .compare(SM.getIdentifierForBuffer(lhsBuffer))) - return true; - } - } - - return false; -} - -/// A directed graph where the vertices are explicit requirement sources and -/// an edge (v, w) records that the explicit source v implies the explicit -/// source w. -/// -/// The roots of the strongly connected component graph are the basic set of -/// explicit requirements which imply everything else. We pick the best -/// representative from each root strongly connected component to form the -/// final set of non-redundant explicit requirements. -class RedundantRequirementGraph { - /// 2**16 explicit requirements ought to be enough for everybody. - using VertexID = uint16_t; - using ComponentID = uint16_t; - - struct Vertex { - ExplicitRequirement req; - - explicit Vertex(ExplicitRequirement req) : req(req) {} - - /// The SCC that this vertex belongs to. - ComponentID component = UndefinedComponent; - - /// State for the SCC algorithm. - VertexID index = UndefinedIndex; - VertexID lowLink = -1; - bool onStack = false; - - /// Outgoing edges. - SmallVector successors; - -#ifndef NDEBUG - bool sawVertex = false; -#endif - - static const ComponentID UndefinedComponent = -1; - static const VertexID UndefinedIndex = -1; - }; - - /// Maps each requirement source to a unique 16-bit ID. - llvm::SmallDenseMap reqs; - - SmallVector vertices; - - ComponentID componentCount = 0; - VertexID vertexCount = 0; - - SmallVector stack; - -public: - template - void handleConstraintsImpliedByConcrete( - RequirementKind kind, - const SmallVectorImpl> &impliedByConcrete, - const Optional &concreteTypeRequirement) { - // This can occur if the combination of a superclass requirement and - // protocol conformance requirement force a type to become concrete. - if (!concreteTypeRequirement) - return; - - for (auto constraint : impliedByConcrete) { - if (constraint.source->isDerivedNonRootRequirement()) - continue; - - auto req = ExplicitRequirement::fromExplicitConstraint(kind, constraint); - addEdge(*concreteTypeRequirement, req); - } - } - - template - Optional - addConstraintsFromEquivClass( - RequirementKind kind, - const SmallVectorImpl> &exact, - const SmallVectorImpl> &lessSpecific, - ASTContext &ctx) { - // The set of 'redundant explicit requirements', which are known to be less - // specific than some other explicit requirements. An example is a type - // parameter with multiple superclass constraints: - // - // class A {} - // class B : A {} - // class C : B {} - // - // func f(_: T) where T : A, T : B, T : C {} - // - // 'T : C' supercedes 'T : A' and 'T : B', because C is a subclass of - // 'A' and 'B'. - SmallVector redundantExplicitReqs; - - // The set of all other explicit requirements, which are known to be the most - // specific. These imply the less specific 'redundant explicit sources' - // above. - // - // Also, if there is more than one most specific explicit requirements, they - // all imply each other. - SmallVector explicitReqs; - - // The set of root explicit requirements for each derived requirements. - // - // These 'root requirements' imply the 'explicit requirements'. - SmallVector rootReqs; - - for (auto constraint : exact) { - if (constraint.source->isDerivedNonRootRequirement()) { - auto req = ExplicitRequirement::fromDerivedConstraint( - constraint, ctx); - - rootReqs.push_back(req); - } else { - auto req = ExplicitRequirement::fromExplicitConstraint( - kind, constraint); - - VertexID v = addVertex(req); -#ifndef NDEBUG - // Record that we saw an actual explicit requirement rooted at this vertex, - // for verification purposes. - vertices[v].sawVertex = true; -#endif - (void) v; - - explicitReqs.push_back(req); - } - } - - for (auto constraint : lessSpecific) { - if (constraint.source->isDerivedNonRootRequirement()) - continue; - - auto req = ExplicitRequirement::fromExplicitConstraint(kind, constraint); - - VertexID v = addVertex(req); -#ifndef NDEBUG - // Record that we saw an actual explicit requirement rooted at this vertex, - // for verification purposes. - vertices[v].sawVertex = true; -#endif - (void) v; - - redundantExplicitReqs.push_back(req); - } - - // Get the "best" explicit requirement that implies the rest. - Optional result; - if (!rootReqs.empty()) - result = rootReqs[0]; - else if (!explicitReqs.empty()) - result = explicitReqs[0]; - - // If all requirements are derived, there is nothing to do. - if (explicitReqs.empty()) { - if (!redundantExplicitReqs.empty()) { - assert(!rootReqs.empty()); - for (auto rootReq : rootReqs) { - for (auto redundantReq : redundantExplicitReqs) { - addEdge(rootReq, redundantReq); - } - } - } - return result; - } - - // If there are multiple most specific explicit requirements, they will form a cycle. - if (explicitReqs.size() > 1) { - for (unsigned index : indices(explicitReqs)) { - auto firstReq = explicitReqs[index]; - auto secondReq = explicitReqs[(index + 1) % explicitReqs.size()]; - addEdge(firstReq, secondReq); - } - } - - // The cycle of most specific explicit requirements implies all less specific - // explicit requirements. - for (auto redundantReq : redundantExplicitReqs) { - addEdge(explicitReqs[0], redundantReq); - } - - // The root explicit requirement of each derived requirement implies the cycle of - // most specific explicit requirements. - for (auto rootReq : rootReqs) { - addEdge(rootReq, explicitReqs[0]); - } - - return result; - } - -private: - /// If we haven't seen this requirement yet, add it and - /// return a new ID. Otherwise, return an existing ID. - VertexID addVertex(ExplicitRequirement req) { - assert(!req.getSource()->isDerivedNonRootRequirement()); - - VertexID id = vertices.size(); - - // Check for wrap-around. - if (id != vertices.size()) { - llvm::errs() << "Too many explicit requirements"; - abort(); - } - - assert(reqs.size() == vertices.size()); - - auto inserted = reqs.insert(std::make_pair(req, id)); - - // Check if we've already seen this vertex. - if (!inserted.second) - return inserted.first->second; - - vertices.emplace_back(req); - return id; - } - - /// Add a directed edge from one requirement to another. - /// If we haven't seen either requirement yet, we add a - /// new vertex; this allows visiting equivalence classes in - /// a single pass, without having to build the set of vertices - /// first. - void addEdge(ExplicitRequirement fromReq, - ExplicitRequirement toReq) { - assert(!fromReq.getSource()->isDerivedNonRootRequirement()); - assert(!toReq.getSource()->isDerivedNonRootRequirement()); - - VertexID from = addVertex(fromReq); - VertexID to = addVertex(toReq); - - vertices[from].successors.push_back(to); - } - - /// Tarjan's algorithm. - void computeSCCs() { - for (VertexID v : indices(vertices)) { - if (vertices[v].index == Vertex::UndefinedIndex) - strongConnect(v); - } - } - - void strongConnect(VertexID v) { - // Set the depth index for v to the smallest unused index. - assert(vertices[v].index == Vertex::UndefinedIndex); - vertices[v].index = vertexCount; - assert(vertices[v].lowLink == Vertex::UndefinedIndex); - vertices[v].lowLink = vertexCount; - - vertexCount++; - - stack.push_back(v); - assert(!vertices[v].onStack); - vertices[v].onStack = true; - - // Consider successors of v. - for (VertexID w : vertices[v].successors) { - if (vertices[w].index == Vertex::UndefinedIndex) { - // Successor w has not yet been visited; recurse on it. - strongConnect(w); - - assert(vertices[w].lowLink != Vertex::UndefinedIndex); - vertices[v].lowLink = std::min(vertices[v].lowLink, - vertices[w].lowLink); - } else if (vertices[w].onStack) { - // Successor w is in stack S and hence in the current SCC. - // - // If w is not on stack, then (v, w) is an edge pointing - // to an SCC already found and must be ignored. - assert(vertices[w].lowLink != Vertex::UndefinedIndex); - vertices[v].lowLink = std::min(vertices[v].lowLink, - vertices[w].index); - } - } - - // If v is a root node, pop the stack and generate an SCC. - if (vertices[v].lowLink == vertices[v].index) { - VertexID w = Vertex::UndefinedIndex; - - do { - w = stack.back(); - assert(vertices[w].onStack); - stack.pop_back(); - - vertices[w].onStack = false; - assert(vertices[w].component == Vertex::UndefinedComponent); - - vertices[w].component = componentCount; - } while (v != w); - - componentCount++; - } - } - -public: - using RedundantRequirementMap = - llvm::DenseMap>; - - void computeRedundantRequirements(SourceManager &SM, - RedundantRequirementMap &redundant) { - // First, compute SCCs. - computeSCCs(); - - // The set of edges pointing to each connected component. - SmallVector, 2> inboundComponentEdges; - inboundComponentEdges.resize(componentCount); - - // The set of edges originating from this connected component. - SmallVector, 2> outboundComponentEdges; - outboundComponentEdges.resize(componentCount); - - // Visit all vertices and build the adjacency sets for the connected - // component graph. - for (const auto &vertex : vertices) { - assert(vertex.component != Vertex::UndefinedComponent); - for (auto successor : vertex.successors) { - ComponentID otherComponent = vertices[successor].component; - if (vertex.component != otherComponent) { - inboundComponentEdges[otherComponent].push_back(vertex.component); - outboundComponentEdges[vertex.component].push_back(otherComponent); - } - } - } - - auto isRootComponent = [&](ComponentID component) -> bool { - return inboundComponentEdges[component].empty(); - }; - - // The set of root components. - llvm::SmallDenseSet rootComponents; - - // The best explicit requirement for each root component. - SmallVector, 2> bestExplicitReq; - bestExplicitReq.resize(componentCount); - - // Visit all vertices and find the best requirement for each root component. - for (const auto &vertex : vertices) { - if (isRootComponent(vertex.component)) { - rootComponents.insert(vertex.component); - - // If this vertex is part of a root SCC, see if the requirement is - // better than the one we have so far. - auto &best = bestExplicitReq[vertex.component]; - if (isBetterExplicitRequirement(SM, best, vertex.req)) - best = vertex.req; - } - } - - // The set of root components that each component is reachable from. - SmallVector, 2> reachableFromRoot; - reachableFromRoot.resize(componentCount); - - // Traverse the graph of connected components starting from the roots. - for (auto rootComponent : rootComponents) { - SmallVector worklist; - - auto addToWorklist = [&](ComponentID nextComponent) { - if (!reachableFromRoot[nextComponent].count(rootComponent)) - worklist.push_back(nextComponent); - }; - - addToWorklist(rootComponent); - - while (!worklist.empty()) { - auto component = worklist.back(); - worklist.pop_back(); - - reachableFromRoot[component].insert(rootComponent); - - for (auto nextComponent : outboundComponentEdges[component]) - addToWorklist(nextComponent); - } - } - - // Compute the mapping of redundant requirements to the best root - // requirement that implies them. - for (const auto &vertex : vertices) { - if (isRootComponent(vertex.component)) { - // A root component is reachable from itself, and itself only. - assert(reachableFromRoot[vertex.component].size() == 1); - assert(reachableFromRoot[vertex.component].count(vertex.component) == 1); - } else { - assert(!bestExplicitReq[vertex.component].hasValue() && - "Recorded best requirement for non-root SCC?"); - } - - // We have a non-root component. This requirement is always - // redundant. - auto reachableFromRootSet = reachableFromRoot[vertex.component]; - assert(reachableFromRootSet.size() > 0); - - for (auto rootComponent : reachableFromRootSet) { - assert(isRootComponent(rootComponent)); - - auto best = bestExplicitReq[rootComponent]; - assert(best.hasValue() && - "Did not record best requirement for root SCC?"); - - assert(vertex.req != *best || vertex.component == rootComponent); - if (vertex.req != *best) { - redundant[vertex.req].insert(*best); - } - } - } - } - -private: - bool isBetterExplicitRequirement(SourceManager &SM, - Optional optExisting, - ExplicitRequirement current) { - if (!optExisting.hasValue()) - return true; - - auto existing = *optExisting; - - auto *existingSource = existing.getSource(); - auto *currentSource = current.getSource(); - - // Prefer explicit sources over inferred ones, so that you can - // write either of the following without a warning: - // - // func foo(_: Set) {} - // func foo(_: Set) {} - bool existingInferred = existingSource->isInferredRequirement(); - bool currentInferred = currentSource->isInferredRequirement(); - if (existingInferred != currentInferred) - return currentInferred; - - // Prefer a RequirementSignatureSelf over anything else. - if ((currentSource->kind == RequirementSource::RequirementSignatureSelf) || - (existingSource->kind == RequirementSource::RequirementSignatureSelf)) - return (currentSource->kind == RequirementSource::RequirementSignatureSelf); - - // Prefer sources with a shorter type. - auto existingType = existingSource->getStoredType(); - auto currentType = currentSource->getStoredType(); - int cmpTypes = compareDependentTypes(currentType, existingType); - if (cmpTypes != 0) - return cmpTypes < 0; - - auto existingLoc = existingSource->getLoc(); - auto currentLoc = currentSource->getLoc(); - - return compareSourceLocs(SM, currentLoc, existingLoc); - } - -public: - void dump(llvm::raw_ostream &out, SourceManager *SM) const { - out << "* Vertices:\n"; - for (const auto &vertex : vertices) { - out << "Component " << vertex.component << ", "; - vertex.req.dump(out, SM); - out << "\n"; - } - - out << "* Edges:\n"; - for (const auto &from : vertices) { - for (VertexID w : from.successors) { - const auto &to = vertices[w]; - - from.req.dump(out, SM); - out << " -> "; - to.req.dump(out, SM); - out << "\n"; - } - } - } - -#ifndef NDEBUG - void verifyAllVerticesWereSeen(SourceManager *SM) const { - for (const auto &vertex : vertices) { - if (!vertex.sawVertex) { - // We found a vertex that was referenced by an edge, but was - // never added explicitly. This means we have a derived - // requirement rooted at the source, but we never saw the - // corresponding explicit requirement. This should not happen. - llvm::errs() << "Found an orphaned vertex:\n"; - vertex.req.dump(llvm::errs(), SM); - llvm::errs() << "\nRedundant requirement graph:\n"; - dump(llvm::errs(), SM); - abort(); - } - } - } -#endif - -}; - void GenericSignatureBuilder::ExplicitRequirement::dump( llvm::raw_ostream &out, SourceManager *SM) const { switch (getKind()) { @@ -6434,258 +5838,509 @@ static bool isConcreteConformance(const EquivalenceClass &equivClass, return false; } -void GenericSignatureBuilder::computeRedundantRequirements( - const ProtocolDecl *requirementSignatureSelfProto) { - assert(!Impl->computedRedundantRequirements && - "Already computed redundant requirements"); -#ifndef NDEBUG - Impl->computedRedundantRequirements = true; -#endif +void GenericSignatureBuilder::getBaseRequirements( + GenericSignatureBuilder::GetKindAndRHS getKindAndRHS, + const RequirementSource *source, + const ProtocolDecl *requirementSignatureSelfProto, + SmallVectorImpl &result) { + // If we're building a generic signature, the base requirement is + // built from the root of the path. + if (source->parent == nullptr) { + RequirementKind kind; + RequirementRHS rhs; + std::tie(kind, rhs) = getKindAndRHS(); + result.push_back(ExplicitRequirement(kind, source, rhs)); + return; + } - RedundantRequirementGraph graph; + // If we're building a requirement signature, there can be multiple + // base requirements from the same protocol appearing along the path. + if (requirementSignatureSelfProto != nullptr && + source->isProtocolRequirement() && + source->getProtocolDecl() == requirementSignatureSelfProto) { + auto *shortSource = source->withoutRedundantSubpath( + *this, source->getRoot(), source->parent); + RequirementKind kind; + RequirementRHS rhs; + std::tie(kind, rhs) = getKindAndRHS(); + result.push_back(ExplicitRequirement(kind, shortSource, rhs)); + } - // Visit each equivalence class, recording all explicit requirement sources, - // and the root of each derived requirement source that implies an explicit - // source. - for (auto &equivClass : Impl->EquivalenceClasses) { - for (auto &entry : equivClass.conformsTo) { - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; + getBaseRequirements( + [&]() { return ExplicitRequirement::getKindAndRHS(source, Context); }, + source->parent, requirementSignatureSelfProto, result); +} - for (const auto &constraint : entry.second) { - auto *source = constraint.source; - - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), entry.first, - derivedViaConcrete) - != source) - continue; +Optional +GenericSignatureBuilder::isValidRequirementDerivationPath( + llvm::SmallDenseSet &visited, + RequirementKind otherKind, + const RequirementSource *otherSource, + RequirementRHS otherRHS, + const ProtocolDecl *requirementSignatureSelfProto) { + if (auto *Stats = Context.Stats) + ++Stats->getFrontendCounters().NumRedundantRequirementSteps; + + SmallVector result; + getBaseRequirements( + [&]() { return std::make_pair(otherKind, otherRHS); }, + otherSource, requirementSignatureSelfProto, result); + assert(result.size() > 0); + + for (const auto &otherReq : result) { + // Don't consider paths that are based on the requirement + // itself; such a path doesn't "prove" this requirement, + // since if we drop the requirement the path is no longer + // valid. + if (visited.count(otherReq)) + return None; - if (derivedViaConcrete) - continue; + SWIFT_DEFER { + visited.erase(otherReq); + }; + visited.insert(otherReq); - // FIXME: Check for a conflict via the concrete type. - exact.push_back(constraint); + auto otherSubjectType = otherReq.getSubjectType(); - if (!source->isDerivedRequirement()) { - if (isConcreteConformance(equivClass, entry.first, *this)) { - Impl->ExplicitConformancesImpliedByConcrete.insert( - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Conformance, constraint)); + // If our requirement is based on a path involving some other + // redundant requirement, see if we can derive the redundant + // requirement using requirements we haven't visited yet. + // If not, we go ahead and drop it from consideration. + // + // We need to do this because sometimes, we don't record all possible + // requirement sources that derive a given requirement. + // + // For example, when a nested type of a type parameter is concretized by + // adding a superclass requirement, we only record the requirement source + // for the concrete conformance the first time. A subsequently-added + // superclass requirement on the same parent type does not record a + // redundant concrete conformance for the child type. + if (isRedundantExplicitRequirement(otherReq)) { + // If we have a redundant explicit requirement source, it really is + // redundant; there's no other derivation that would not be redundant. + if (!otherSource->isDerivedNonRootRequirement()) + return None; + + auto *equivClass = resolveEquivalenceClass(otherSubjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); + + switch (otherReq.getKind()) { + case RequirementKind::Conformance: { + auto *proto = otherReq.getRHS().get(); + + auto found = equivClass->conformsTo.find(proto); + assert(found != equivClass->conformsTo.end()); + + bool foundValidDerivation = false; + for (const auto &constraint : found->second) { + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, proto, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; } } - } - graph.addConstraintsFromEquivClass(RequirementKind::Conformance, - exact, lessSpecific, Context); - } + if (!foundValidDerivation) + return None; - Type resolvedConcreteType; - Optional concreteTypeRequirement; + break; + } - if (equivClass.concreteType) { - resolvedConcreteType = - getCanonicalTypeInContext(equivClass.concreteType, { }); + case RequirementKind::Superclass: { + auto superclass = getCanonicalTypeInContext( + otherReq.getRHS().get(), { }); + + for (const auto &constraint : equivClass->superclassConstraints) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + + if (superclass->isExactSuperclassOf(otherSuperclass)) { + bool foundValidDerivation = false; + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, otherSuperclass, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } + + if (!foundValidDerivation) + return None; + } + } - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; + break; + } - for (const auto &constraint : equivClass.concreteTypeConstraints) { - auto *source = constraint.source; - Type t = constraint.value; + case RequirementKind::Layout: { + auto layout = otherReq.getRHS().get(); - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; + for (const auto &constraint : equivClass->layoutConstraints) { + auto otherLayout = constraint.value; - if (derivedViaConcrete) - continue; + if (layout == otherLayout) { + bool foundValidDerivation = false; + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, otherLayout, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } - if (t->isEqual(resolvedConcreteType)) { - exact.push_back(constraint); - continue; + if (!foundValidDerivation) + return None; + } } - auto resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isEqual(resolvedConcreteType)) { - exact.push_back(constraint); - } + break; + } - // Record the conflict. - if (!source->isDerivedRequirement()) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::SameType, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, resolvedConcreteType)); - } + case RequirementKind::SameType: + llvm_unreachable("Should not see same type requirements here"); + } + } - lessSpecific.push_back(constraint); + if (auto *depMemType = otherSubjectType->getAs()) { + // If 'req' is based on some other conformance requirement + // `T.[P.]A : Q', we want to make sure that we have a + // non-redundant derivation for 'T : P'. + auto baseType = depMemType->getBase(); + auto *proto = depMemType->getAssocType()->getProtocol(); + + auto *equivClass = resolveEquivalenceClass(baseType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); + + auto found = equivClass->conformsTo.find(proto); + assert(found != equivClass->conformsTo.end()); + + bool foundValidDerivation = false; + for (const auto &constraint : found->second) { + if (isValidRequirementDerivationPath( + visited, RequirementKind::Conformance, + constraint.source, proto, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } } - concreteTypeRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::SameType, - exact, lessSpecific, Context); + if (!foundValidDerivation) + return None; } + } - Type resolvedSuperclass; - Optional superclassRequirement; + return result.front(); +} - if (equivClass.superclass) { - // Resolve any thus-far-unresolved dependent types. - resolvedSuperclass = - getCanonicalTypeInContext(equivClass.superclass, getGenericParams()); - - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - SmallVector, 2> impliedByConcrete; - - for (const auto &constraint : equivClass.superclassConstraints) { - auto *source = constraint.source; - Type t = constraint.value; - - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; +template +void GenericSignatureBuilder::checkIfRequirementCanBeDerived( + const ExplicitRequirement &req, + const std::vector> &constraints, + const ProtocolDecl *requirementSignatureSelfProto, + Filter filter) { + assert(!constraints.empty()); - if (derivedViaConcrete) - continue; + for (const auto &constraint : constraints) { + if (filter(constraint)) + continue; - if (resolvedConcreteType) { - Type resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isExactSuperclassOf(resolvedConcreteType)) - impliedByConcrete.push_back(constraint); - } + // If this requirement can be derived from a set of + // non-redundant base requirements, then this requirement + // is redundant. + llvm::SmallDenseSet visited; + visited.insert(req); - if (t->isEqual(resolvedSuperclass)) { - exact.push_back(constraint); - continue; - } + if (auto representative = isValidRequirementDerivationPath( + visited, req.getKind(), + constraint.source, + constraint.value, + requirementSignatureSelfProto)) { + Impl->RedundantRequirements[req].insert(*representative); + } + } +} - Type resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isEqual(resolvedSuperclass)) { - exact.push_back(constraint); - continue; - } +void GenericSignatureBuilder::computeRedundantRequirements( + const ProtocolDecl *requirementSignatureSelfProto) { + assert(!Impl->computedRedundantRequirements && + "Already computed redundant requirements"); +#ifndef NDEBUG + Impl->computedRedundantRequirements = true; +#endif - // Check for a conflict. - if (!source->isDerivedRequirement() && - !resolvedType->isExactSuperclassOf(resolvedSuperclass)) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Superclass, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, resolvedSuperclass)); - } + FrontendStatsTracer tracer(Context.Stats, + "compute-redundant-requirements"); - lessSpecific.push_back(constraint); - } + // This sort preserves iteration order with the legacy algorithm. + SmallVector requirements( + Impl->ExplicitRequirements.begin(), + Impl->ExplicitRequirements.end()); + std::stable_sort(requirements.begin(), requirements.end(), + [](const ExplicitRequirement &req1, + const ExplicitRequirement &req2) { + if (req1.getSource()->isInferredRequirement() && + !req2.getSource()->isInferredRequirement()) + return true; - superclassRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::Superclass, - exact, lessSpecific, Context); + if (compareDependentTypes(req1.getSubjectType(), + req2.getSubjectType()) > 0) + return true; + + return false; + }); + + for (const auto &req : requirements) { + auto subjectType = req.getSubjectType(); + + auto *equivClass = resolveEquivalenceClass(subjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); + + Type resolvedConcreteType; + if (equivClass->concreteType) { + resolvedConcreteType = + getCanonicalTypeInContext(equivClass->concreteType, + getGenericParams()); + } - graph.handleConstraintsImpliedByConcrete(RequirementKind::Superclass, - impliedByConcrete, - concreteTypeRequirement); + Type resolvedSuperclass; + if (equivClass->superclass) { + resolvedSuperclass = + getCanonicalTypeInContext(equivClass->superclass, + getGenericParams()); } - Optional layoutRequirement; + switch (req.getKind()) { + case RequirementKind::Conformance: { + // TODO: conflicts with concrete + auto *proto = req.getRHS().get(); - if (equivClass.layout) { - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - SmallVector, 2> impliedByConcrete; + auto found = equivClass->conformsTo.find(proto); + assert(found != equivClass->conformsTo.end()); - for (const auto &constraint : equivClass.layoutConstraints) { - auto *source = constraint.source; - auto layout = constraint.value; + checkIfRequirementCanBeDerived( + req, found->second, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + return false; + }); - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; + if (isConcreteConformance(*equivClass, proto, *this)) + Impl->ExplicitConformancesImpliedByConcrete.insert(req); - if (derivedViaConcrete) - continue; + break; + } - if (resolvedConcreteType) { - if (typeImpliesLayoutConstraint(resolvedConcreteType, layout)) { - impliedByConcrete.push_back(constraint); + case RequirementKind::Superclass: { + auto superclass = getCanonicalTypeInContext( + req.getRHS().get(), { }); - if (!source->isDerivedRequirement()) { - Impl->ExplicitConformancesImpliedByConcrete.insert( - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Layout, constraint)); - } + if (!superclass->isExactSuperclassOf(resolvedSuperclass)) { + // Case 1. We have a requirement 'T : C', and we've + // previously resolved the superclass of 'T' to some + // class 'D' where 'C' is not a superclass of 'D'. + // + // This means 'T : C' conflicts with some other + // requirement that implies 'T : D'. + Impl->ConflictingRequirements[req] = resolvedSuperclass; + + checkIfRequirementCanBeDerived( + req, equivClass->superclassConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + // Filter out requirements where the superclass + // is not equal to the resolved superclass. + if (!resolvedSuperclass->isEqual(otherSuperclass)) + return true; + + return false; + }); + } else { + // Case 2. We have a requirement 'T : C', and we've + // previously resolved the superclass of 'T' to some + // class 'D' where 'C' is a superclass of 'D'. + // + // This means that 'T : C' is made redundant by any + // requirement that implies 'T : B', such that 'C' is a + // superclass of 'B'. + checkIfRequirementCanBeDerived( + req, equivClass->superclassConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + // Filter out requirements where the superclass is less + // specific than the original requirement. + if (!superclass->isExactSuperclassOf(otherSuperclass)) + return true; + + return false; + }); + + if (resolvedConcreteType) { + // We have a superclass requirement 'T : C' and a same-type + // requirement 'T == D'. + if (resolvedSuperclass->isExactSuperclassOf(resolvedConcreteType)) { + // 'C' is a superclass of 'D', so 'T : C' is redundant. + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + if (!resolvedSuperclass->isExactSuperclassOf(otherType)) + return true; + + return false; + }); + } else { + // 'C' is not a superclass of 'D'; we have a conflict. + Impl->ConflictingConcreteTypeRequirements.push_back( + {req, resolvedConcreteType, resolvedSuperclass}); } } + } - if (layout == equivClass.layout) { - exact.push_back(constraint); - continue; - } + break; + } - // Check for a conflict. - if (!source->isDerivedRequirement() && - !layout.merge(equivClass.layout)->isKnownLayout()) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Layout, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, equivClass.layout)); + case RequirementKind::Layout: { + // TODO: less-specific constraints + // TODO: conflicts with other layout constraints + // TODO: conflicts with concrete and superclass + auto layout = req.getRHS().get(); + + if (!layout.merge(equivClass->layout)->isKnownLayout()) { + // Case 1. We have a requirement 'T : L1', and we've + // previously resolved the layout of 'T' to some + // layout constraint 'L2', where 'L1' and 'L2' are not + // mergeable. + // + // This means that 'T : L1' conflicts with some other + // requirement that implies 'T : L2'. + Impl->ConflictingRequirements[req] = equivClass->layout; + + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherLayout = constraint.value; + // Filter out requirements where the layout is not + // equal to the resolved layout. + if (equivClass->layout != otherLayout) + return true; + + return false; + }); + } else if (layout == equivClass->layout) { + // Case 2. We have a requirement 'T : L1', and we know + // the most specific resolved layout for 'T' is also 'L1'. + // + // This means that 'T : L1' is made redundant by any + // requirement that implies 'T : L1'. + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the implied layout is + // not equal to the resolved layout. + auto otherLayout = constraint.value; + if (layout != otherLayout) + return true; + + return false; + }); + + if (resolvedConcreteType) { + auto layout = req.getRHS().get(); + if (typeImpliesLayoutConstraint(resolvedConcreteType, + layout)) { + Impl->ExplicitConformancesImpliedByConcrete.insert(req); + + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the concrete type + // does not have the resolved layout. + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + if (!typeImpliesLayoutConstraint(otherType, layout)) + return true; + + return false; + }); + } else if (typeConflictsWithLayoutConstraint(resolvedConcreteType, + layout)) { + // We have a concrete type requirement 'T == C' and 'C' does + // not satisfy 'L1'. + Impl->ConflictingConcreteTypeRequirements.push_back( + {req, resolvedConcreteType, equivClass->layout}); + } } + } else { + // Case 3. We have a requirement 'T : L1', and we know + // the most specific resolved layout for 'T' is some other + // layout 'L2', such that 'L2' is mergeable with 'L1'. + // + // This means that 'T : L1' is made redundant by any + // requirement that implies 'T : L3', where L3 is + // mergeable with L1. + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherLayout = constraint.value; + // Filter out requirements where the implied layout + // is not mergeable with the original requirement's + // layout. + if (!layout.merge(otherLayout)->isKnownLayout()) + return true; - lessSpecific.push_back(constraint); - } + return false; + }); - layoutRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::Layout, - exact, lessSpecific, Context); + if (resolvedConcreteType) { + auto layout = req.getRHS().get(); + if (typeImpliesLayoutConstraint(resolvedConcreteType, layout)) { + Impl->ExplicitConformancesImpliedByConcrete.insert(req); + + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the concrete type + // does not have the resolved layout. + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + + if (!typeImpliesLayoutConstraint(otherType, + equivClass->layout)) + return true; + + return false; + }); + } + } + } - graph.handleConstraintsImpliedByConcrete(RequirementKind::Layout, - impliedByConcrete, - concreteTypeRequirement); + break; } - if (resolvedConcreteType && resolvedSuperclass) { - if (!resolvedSuperclass->isExactSuperclassOf(resolvedConcreteType)) { - Impl->ConflictingConcreteTypeRequirements.push_back( - {*concreteTypeRequirement, *superclassRequirement, - resolvedConcreteType, resolvedSuperclass}); - } - } else if (resolvedConcreteType && equivClass.layout) { - if (typeConflictsWithLayoutConstraint(resolvedConcreteType, - equivClass.layout)) { - Impl->ConflictingConcreteTypeRequirements.push_back( - {*concreteTypeRequirement, *layoutRequirement, - resolvedConcreteType, equivClass.layout}); - } + case RequirementKind::SameType: + llvm_unreachable("Should not see same type requirements here"); } } - - auto &SM = getASTContext().SourceMgr; - -#ifndef NDEBUG - graph.verifyAllVerticesWereSeen(&SM); -#endif - - // Determine which explicit requirement sources are actually redundant. - assert(Impl->RedundantRequirements.empty()); - graph.computeRedundantRequirements(SM, Impl->RedundantRequirements); } void @@ -6697,7 +6352,7 @@ GenericSignatureBuilder::finalize(TypeArrayView genericPar computeRedundantRequirements(requirementSignatureSelfProto); diagnoseRedundantRequirements(); - diagnoseConflictingConcreteTypeRequirements(); + diagnoseConflictingConcreteTypeRequirements(requirementSignatureSelfProto); assert(!Impl->finalized && "Already finalized builder"); #ifndef NDEBUG @@ -6824,14 +6479,9 @@ GenericSignatureBuilder::finalize(TypeArrayView genericPar } equivClass.recursiveSuperclassType = true; - } else { - checkSuperclassConstraints(genericParams, &equivClass); } } - - checkConformanceConstraints(genericParams, &equivClass); - checkLayoutConstraints(genericParams, &equivClass); - }; + } if (!Impl->ExplicitSameTypeRequirements.empty()) { // FIXME: Expand all conformance requirements. This is expensive :( @@ -7053,27 +6703,21 @@ void GenericSignatureBuilder::processDelayedRequirements() { namespace { /// Remove self-derived sources from the given vector of constraints. - /// - /// \returns true if any derived-via-concrete constraints were found. template - bool removeSelfDerived(GenericSignatureBuilder &builder, + void removeSelfDerived(GenericSignatureBuilder &builder, std::vector> &constraints, ProtocolDecl *proto, - bool dropDerivedViaConcrete = true, bool allCanBeSelfDerived = false) { auto genericParams = builder.getGenericParams(); - bool anyDerivedViaConcrete = false; - Optional> remainingConcrete; SmallVector, 4> minimalSources; constraints.erase( std::remove_if(constraints.begin(), constraints.end(), [&](const Constraint &constraint) { - bool derivedViaConcrete; auto minimalSource = constraint.source->getMinimalConformanceSource( builder, constraint.getSubjectDependentType(genericParams), - proto, derivedViaConcrete); + proto); if (minimalSource != constraint.source) { // The minimal source is smaller than the original source, so the // original source is self-derived. @@ -7090,21 +6734,8 @@ namespace { return true; } - if (!derivedViaConcrete) - return false; - - anyDerivedViaConcrete = true; - - if (!dropDerivedViaConcrete) - return false; - - // Drop derived-via-concrete requirements. - if (!remainingConcrete) - remainingConcrete = constraint; - - ++NumSelfDerived; - return true; - }), + return false; + }), constraints.end()); // If we found any minimal sources, add them now, avoiding introducing any @@ -7124,13 +6755,8 @@ namespace { } } - // If we only had concrete conformances, put one back. - if (constraints.empty() && remainingConcrete) - constraints.push_back(*remainingConcrete); - assert((!constraints.empty() || allCanBeSelfDerived) && "All constraints were self-derived!"); - return anyDerivedViaConcrete; } } // end anonymous namespace @@ -7302,18 +6928,6 @@ static bool isRedundantlyInheritableObjCProtocol( return true; } -void GenericSignatureBuilder::checkConformanceConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - for (auto &entry : equivClass->conformsTo) { - // Remove self-derived constraints. - assert(!entry.second.empty() && "No constraints to work with?"); - - // Remove any self-derived constraints. - removeSelfDerived(*this, entry.second, entry.first); - } -} - void GenericSignatureBuilder::diagnoseRedundantRequirements() const { for (const auto &req : Impl->ExplicitRequirements) { auto *source = req.getSource(); @@ -7474,15 +7088,43 @@ void GenericSignatureBuilder::diagnoseRedundantRequirements() const { } } -void GenericSignatureBuilder::diagnoseConflictingConcreteTypeRequirements() const { +void GenericSignatureBuilder::diagnoseConflictingConcreteTypeRequirements( + const ProtocolDecl *requirementSignatureSelfProto) { for (auto pair : Impl->ConflictingConcreteTypeRequirements) { - SourceLoc loc = pair.concreteTypeRequirement.getSource()->getLoc(); + auto subjectType = pair.otherRequirement.getSubjectType(); + + auto *equivClass = resolveEquivalenceClass(subjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); + + auto foundConcreteRequirement = llvm::find_if( + equivClass->concreteTypeConstraints, + [&](const Constraint &constraint) { + SmallVector result; + getBaseRequirements( + [&]() { + return std::make_pair(RequirementKind::SameType, + constraint.value); + }, + constraint.source, requirementSignatureSelfProto, result); + + for (const auto &otherReq : result) { + if (isRedundantExplicitRequirement(otherReq)) + return false; + } + + return true; + }); + + assert(foundConcreteRequirement != equivClass->concreteTypeConstraints.end()); + + SourceLoc loc = foundConcreteRequirement->source->getLoc(); SourceLoc otherLoc = pair.otherRequirement.getSource()->getLoc(); if (loc.isInvalid() && otherLoc.isInvalid()) continue; - auto subjectType = pair.concreteTypeRequirement.getSubjectType(); SourceLoc subjectLoc = (loc.isInvalid() ? otherLoc : loc); Impl->HadAnyError = true; @@ -7680,9 +7322,7 @@ static void computeDerivedSameTypeComponents( // construction of self-derived sources really don't work, because we // discover more information later, so we need a more on-line or // iterative approach. - bool derivedViaConcrete; - if (concrete.source->isSelfDerivedSource(builder, subjectType, - derivedViaConcrete)) + if (concrete.source->isSelfDerivedSource(builder, subjectType)) continue; // If it has a better source than we'd seen before for this component, @@ -8016,13 +7656,10 @@ void GenericSignatureBuilder::checkSameTypeConstraints( if (!equivClass->derivedSameTypeComponents.empty()) return; - bool anyDerivedViaConcrete = false; // Remove self-derived constraints. - if (removeSelfDerived(*this, equivClass->sameTypeConstraints, - /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/false, - /*allCanBeSelfDerived=*/true)) - anyDerivedViaConcrete = true; + removeSelfDerived(*this, equivClass->sameTypeConstraints, + /*proto=*/nullptr, + /*allCanBeSelfDerived=*/true); // Sort the constraints, so we get a deterministic ordering of diagnostics. llvm::array_pod_sort(equivClass->sameTypeConstraints.begin(), @@ -8100,16 +7737,6 @@ void GenericSignatureBuilder::checkSameTypeConstraints( IntercomponentEdge(firstComponentIdx, secondComponentIdx, constraint)); } - // If there were any derived-via-concrete constraints, drop them now before - // we emit other diagnostics. - if (anyDerivedViaConcrete) { - // Remove derived-via-concrete constraints. - (void)removeSelfDerived(*this, equivClass->sameTypeConstraints, - /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/true, - /*allCanBeSelfDerived=*/true); - } - // Walk through each of the components, checking the intracomponent edges. // This will diagnose any explicitly-specified requirements within a // component, all of which are redundant. @@ -8307,7 +7934,6 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints( removeSelfDerived(*this, equivClass->concreteTypeConstraints, /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/true, /*allCanBeSelfDerived=*/true); // This can occur if the combination of a superclass requirement and @@ -8345,22 +7971,6 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints( diag::same_type_redundancy_here); } -void GenericSignatureBuilder::checkSuperclassConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - assert(equivClass->superclass && "No superclass constraint?"); - - removeSelfDerived(*this, equivClass->superclassConstraints, /*proto=*/nullptr); -} - -void GenericSignatureBuilder::checkLayoutConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - if (!equivClass->layout) return; - - removeSelfDerived(*this, equivClass->layoutConstraints, /*proto=*/nullptr); -} - bool GenericSignatureBuilder::isRedundantExplicitRequirement( const ExplicitRequirement &req) const { assert(Impl->computedRedundantRequirements && @@ -8712,7 +8322,31 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir for (auto param : getGenericParams()) newBuilder.addGenericParameter(param); - auto newSource = FloatingRequirementSource::forAbstract(); + const RequirementSource *requirementSignatureSource = nullptr; + if (auto *proto = const_cast(requirementSignatureSelfProto)) { + auto selfType = proto->getSelfInterfaceType(); + requirementSignatureSource = + RequirementSource::forRequirementSignature(newBuilder, selfType, proto); + + // Add the conformance requirement 'Self : Proto' directly without going + // through addConformanceRequirement(), since the latter calls + // expandConformanceRequirement(), which we want to skip since we're + // re-adding the requirements directly below. + auto resolvedType = ResolvedType(newBuilder.Impl->PotentialArchetypes[0]); + auto equivClass = resolvedType.getEquivalenceClass(newBuilder); + + (void) equivClass->recordConformanceConstraint(newBuilder, resolvedType, proto, + requirementSignatureSource); + } + + auto newSource = [&]() { + if (auto *proto = const_cast(requirementSignatureSelfProto)) { + return FloatingRequirementSource::viaProtocolRequirement( + requirementSignatureSource, proto, /*inferred=*/false); + } + + return FloatingRequirementSource::forAbstract(); + }(); for (const auto &req : Impl->ExplicitRequirements) { assert(req.getKind() != RequirementKind::SameType && @@ -8801,9 +8435,6 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( requirementSignatureSelfProto); if (rebuildingWithoutRedundantConformances) { - assert(requirementSignatureSelfProto == nullptr && - "Rebuilding a requirement signature?"); - assert(!Impl->HadAnyError && "Rebuilt signature had errors"); @@ -8825,7 +8456,6 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( // // Also, don't do this when building a requirement signature. if (!rebuildingWithoutRedundantConformances && - requirementSignatureSelfProto == nullptr && !Impl->HadAnyError && !Impl->ExplicitConformancesImpliedByConcrete.empty()) { return std::move(*this).rebuildSignatureWithoutRedundantRequirements( diff --git a/test/Generics/concrete_same_type_versus_anyobject.swift b/test/Generics/concrete_same_type_versus_anyobject.swift index de063a87970ef..0654fc2315edd 100644 --- a/test/Generics/concrete_same_type_versus_anyobject.swift +++ b/test/Generics/concrete_same_type_versus_anyobject.swift @@ -30,3 +30,13 @@ extension G2 where U == C, U : AnyObject {} extension G2 where U : C, U : AnyObject {} // expected-warning@-1 {{redundant constraint 'U' : 'AnyObject'}} // expected-note@-2 {{constraint 'U' : 'AnyObject' implied here}} + +// Explicit AnyObject conformance vs derived same-type +protocol P { + associatedtype A where A == C +} + +// CHECK-LABEL: Generic signature: +func explicitAnyObjectIsRedundant(_: T) where T.A : AnyObject {} +// expected-warning@-1 {{redundant constraint 'T.A' : 'AnyObject'}} +// expected-note@-2 {{constraint 'T.A' : 'AnyObject' implied here}} diff --git a/test/Generics/derived_via_concrete.swift b/test/Generics/derived_via_concrete.swift new file mode 100644 index 0000000000000..b9b08438397c4 --- /dev/null +++ b/test/Generics/derived_via_concrete.swift @@ -0,0 +1,73 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -debug-generic-signatures -typecheck %s 2>&1 | %FileCheck %s + +protocol P {} +class C {} + +class X : U {} +class Y : V {} +class Z : W {} + +protocol U { + associatedtype T : P +} + +protocol V { + associatedtype T : C +} + +protocol W { + associatedtype T : AnyObject +} + +// CHECK: Generic signature: , B : P> +func derivedViaConcreteX1(_: A, _: B) + where A : U, A : X {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}} +// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}} + +// FIXME: We should not diagnose 'B : P' as redundant, even though it +// really is, because it was made redundant by an inferred requirement. + +// CHECK: Generic signature: , B : P> +func derivedViaConcreteX2(_: A, _: B) + where A : U, B : P, A : X {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}} +// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}} +// expected-warning@-3 {{redundant conformance constraint 'B' : 'P'}} +// expected-note@-4 {{conformance constraint 'B' : 'P' implied here}} + +// CHECK: Generic signature: , B : C> +func derivedViaConcreteY1(_: A, _: B) + where A : V, A : Y {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}} +// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}} + +// FIXME: We should not diagnose 'B : C' as redundant, even though it +// really is, because it was made redundant by an inferred requirement. + +// CHECK: Generic signature: , B : C> +func derivedViaConcreteY2(_: A, _: B) + where A : V, B : C, A : Y {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}} +// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}} +// expected-warning@-3 {{redundant superclass constraint 'B' : 'C'}} +// expected-note@-4 {{superclass constraint 'B' : 'C' implied here}} + +// CHECK: Generic signature: , B : AnyObject> +func derivedViaConcreteZ1(_: A, _: B) + where A : W, A : Z {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}} +// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}} + +// FIXME: We should not diagnose 'B : AnyObject' as redundant, even +// though it really is, because it was made redundant by an inferred +// requirement. + +// CHECK: Generic signature: , B : AnyObject> +func derivedViaConcreteZ2(_: A, _: B) + where A : W, B : AnyObject, A : Z {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}} +// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}} +// expected-warning@-3 {{redundant constraint 'B' : 'AnyObject'}} +// expected-note@-4 {{constraint 'B' : 'AnyObject' implied here}} \ No newline at end of file diff --git a/test/Generics/explicit_requirements_perf.swift b/test/Generics/explicit_requirements_perf.swift new file mode 100644 index 0000000000000..62bea10911ee9 --- /dev/null +++ b/test/Generics/explicit_requirements_perf.swift @@ -0,0 +1,9 @@ +// RUN: %scale-test --begin 1 --end 20 --step 1 --select NumRedundantRequirementSteps --polynomial-threshold 2 %s + +protocol P {} + +func f(_: T) where +%for i in range(0, N): + T : P, +%end + T : P {} diff --git a/test/Generics/rdar62903491.swift b/test/Generics/rdar62903491.swift index 19736d59784b1..7fc6b3a6f6831 100644 --- a/test/Generics/rdar62903491.swift +++ b/test/Generics/rdar62903491.swift @@ -5,7 +5,7 @@ protocol P { associatedtype X : P } -// Anything that mentions 'T : P' minimizes to 'U : P'. +// Anything that mentions 'T : P' and 'U : P' minimizes to 'U : P'. // expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} // expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} @@ -31,6 +31,8 @@ func oneProtocol4(_: T, _: U) where U : P, T.X == U, T : P, U.X == T {} // CHECK-LABEL: oneProtocol4 // CHECK: Generic signature: +// Anything that only mentions 'T : P' minimizes to 'T : P'. + func oneProtocol5(_: T, _: U) where T : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol5 // CHECK: Generic signature: @@ -39,14 +41,12 @@ func oneProtocol6(_: T, _: U) where T.X == U, U.X == T, T : P {} // CHECK-LABEL: oneProtocol6 // CHECK: Generic signature: -// Anything that mentions 'U : P' but not 'T : P' minimizes to 'U : P'. +// Anything that only mentions 'U : P' minimizes to 'U : P'. -// FIXME: Need to emit warning here too func oneProtocol7(_: T, _: U) where U : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol7 // CHECK: Generic signature: -// FIXME: Need to emit warning here too func oneProtocol8(_: T, _: U) where T.X == U, U.X == T, U : P {} // CHECK-LABEL: oneProtocol8 // CHECK: Generic signature: diff --git a/test/Generics/rdar74890907.swift b/test/Generics/rdar74890907.swift new file mode 100644 index 0000000000000..cf3be77a324e5 --- /dev/null +++ b/test/Generics/rdar74890907.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype A : P2 +} + +protocol P2 { + associatedtype A +} + +// CHECK: Generic signature: +func testTU(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} +// expected-warning@-1 {{redundant conformance constraint 'U' : 'P2'}} +// expected-note@-2 {{conformance constraint 'U' : 'P2' implied here}} + +// CHECK: Generic signature: +func testU(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} + +// CHECK: Generic signature: +func testT(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} diff --git a/test/Generics/rdar75656022.swift b/test/Generics/rdar75656022.swift index 88160472487d3..2abd67031a8c9 100644 --- a/test/Generics/rdar75656022.swift +++ b/test/Generics/rdar75656022.swift @@ -48,9 +48,9 @@ struct OriginalExampleWithWarning where A : P2, B : P2, A.T == B.T { init(_: C) where C : P1, D : P1, // expected-warning {{redundant conformance constraint 'D' : 'P1'}} - C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}} - A == S1>, // expected-note {{conformance constraint 'D' : 'P1' implied here}} // expected-note@-1 {{conformance constraint 'C.T' : 'P1' implied here}} + C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}} + A == S1>, C.T == D, E == D.T { } } @@ -72,7 +72,6 @@ struct WithoutBogusGenericParametersWithWarning where A : P2, B : P2, A.T where C : P1, C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}} A == S1> {} - // expected-note@-1 {{conformance constraint 'C.T' : 'P1' implied here}} } // Same as above but without unnecessary generic parameters @@ -82,4 +81,4 @@ struct WithoutBogusGenericParametersWithoutWarning where A : P2, B : P2, A init(_: C) where C : P1, A == S1> {} -} \ No newline at end of file +} diff --git a/test/Generics/requirement_inference.swift b/test/Generics/requirement_inference.swift index e69ee8d5d64c2..102a3aaac0310 100644 --- a/test/Generics/requirement_inference.swift +++ b/test/Generics/requirement_inference.swift @@ -328,7 +328,9 @@ struct X24 : P24 { // CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A == X24<τ_0_0.B>, τ_0_0.B : P20> protocol P25a { associatedtype A: P24 // expected-warning{{redundant conformance constraint 'Self.A' : 'P24'}} + // expected-note@-1 {{conformance constraint 'Self.B' : 'P20' implied here}} associatedtype B: P20 where A == X24 // expected-note{{conformance constraint 'Self.A' : 'P24' implied here}} + // expected-warning@-1 {{redundant conformance constraint 'Self.B' : 'P20'}} } // CHECK-LABEL: .P25b@ @@ -363,7 +365,13 @@ struct X26 : P26 { // CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A == X26<τ_0_0.B>, τ_0_0.B : X3> protocol P27a { associatedtype A: P26 // expected-warning{{redundant conformance constraint 'Self.A' : 'P26'}} + // expected-note@-1 {{superclass constraint 'Self.B' : 'X3' implied here}} + associatedtype B: X3 where A == X26 // expected-note{{conformance constraint 'Self.A' : 'P26' implied here}} + // expected-warning@-1 {{redundant superclass constraint 'Self.B' : 'X3'}} + + // FIXME: The above warning should not be emitted -- while the requirement + // really is redundant, it is made redundant by an inferred requirement. } // CHECK-LABEL: .P27b@ diff --git a/test/Generics/sr14510.swift b/test/Generics/sr14510.swift new file mode 100644 index 0000000000000..c7e2452ef2b64 --- /dev/null +++ b/test/Generics/sr14510.swift @@ -0,0 +1,35 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -debug-generic-signatures -typecheck %s 2>&1 | %FileCheck %s + +// CHECK-LABEL: Requirement signature: +public protocol Adjoint { + associatedtype Dual: Adjoint where Self.Dual.Dual == Self +} + +// CHECK-LABEL: Requirement signature: +public protocol Diffable { + associatedtype Patch +} + +// CHECK-LABEL: Requirement signature: +public protocol AdjointDiffable: Adjoint & Diffable +where Self.Patch: Adjoint, Self.Dual: AdjointDiffable, + Self.Patch.Dual == Self.Dual.Patch { +} + +// This is another example used to hit the same problem. Any one of the three +// conformance requirements 'A : P', 'B : P' or 'C : P' can be dropped and +// proven from the other two, but dropping two or more conformance requirements +// leaves us with an invalid signature. +// +// Note that this minimization is still not quite correct; the requirement +// 'Self.B.C == Self.A.A.A' is unnecessary. + +// CHECK-LABEL: Requirement signature: +protocol P { + associatedtype A : P where A == B.C + associatedtype B : P where B == A.C + // expected-note@-1 {{conformance constraint 'Self.C' : 'P' implied here}} + associatedtype C : P where C == A.B + // expected-warning@-1 {{redundant conformance constraint 'Self.C' : 'P'}} +} \ No newline at end of file diff --git a/test/Generics/sr14580.swift b/test/Generics/sr14580.swift new file mode 100644 index 0000000000000..49831c3bfacd2 --- /dev/null +++ b/test/Generics/sr14580.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +public protocol ScalarProtocol: ScalarMultiplicative where Self == Scalar { +} + +public protocol ScalarMultiplicative { + associatedtype Scalar: ScalarProtocol +} + +public protocol MapReduceArithmetic: ScalarMultiplicative, Collection where Element: ScalarMultiplicative {} + +public protocol Tensor: MapReduceArithmetic where Element == Scalar { +} + +// FIXME: The same-type requirement canonicalization still produces bugus results here, but +// at least we don't crash. + +// CHECK-LABEL: Requirement signature: +public protocol ColorModel: Tensor where Scalar == Double { + associatedtype Float16Components: ColorComponents where Float16Components.Model == Self, Float16Components.Scalar == Double + associatedtype Float32Components: ColorComponents where Float32Components.Model == Self, Float32Components.Scalar == Double +} + +public protocol ColorComponents: Tensor { + associatedtype Model: ColorModel +} + +extension Double : ScalarMultiplicative {} +extension Double : ScalarProtocol { + public typealias Scalar = Self +} diff --git a/test/Generics/superclass_constraint.swift b/test/Generics/superclass_constraint.swift index 4e8b8d131657f..2b1436b222a56 100644 --- a/test/Generics/superclass_constraint.swift +++ b/test/Generics/superclass_constraint.swift @@ -213,3 +213,16 @@ _ = G() // expected-error {{'G' requires that 'Base & P' inherit from func badClassConstrainedType(_: G) {} // expected-error@-1 {{'G' requires that 'Base & P' inherit from 'Base'}} + +// Reduced from CoreStore in source compat suite +public protocol Pony {} + +public class Teddy: Pony {} + +public struct Paddock {} + +public struct Barn { + // CHECK-DAG: Barn.foo@ + // CHECK: Generic signature: + public func foo(_: S, _: Barn, _: Paddock) {} +}