@@ -5878,6 +5878,8 @@ GenericSignatureBuilder::isValidRequirementDerivationPath(
58785878 const RequirementSource *otherSource,
58795879 RequirementRHS otherRHS,
58805880 const ProtocolDecl *requirementSignatureSelfProto) {
5881+ if (auto *Stats = Context.Stats )
5882+ ++Stats->getFrontendCounters ().NumRedundantRequirementSteps ;
58815883
58825884 SmallVector<ExplicitRequirement, 2 > result;
58835885 getBaseRequirements (
@@ -5893,18 +5895,116 @@ GenericSignatureBuilder::isValidRequirementDerivationPath(
58935895 if (visited.count (otherReq))
58945896 return None;
58955897
5896- // Don't consider paths based on requirements that are already
5897- // known to be redundant either, since those paths become
5898- // invalid once redundant requirements are dropped.
5899- if (isRedundantExplicitRequirement (otherReq))
5900- return None;
5901-
59025898 SWIFT_DEFER {
59035899 visited.erase (otherReq);
59045900 };
59055901 visited.insert (otherReq);
59065902
59075903 auto otherSubjectType = otherReq.getSubjectType ();
5904+
5905+ // If our requirement is based on a path involving some other
5906+ // redundant requirement, see if we can derive the redundant
5907+ // requirement using requirements we haven't visited yet.
5908+ // If not, we go ahead and drop it from consideration.
5909+ //
5910+ // We need to do this because sometimes, we don't record all possible
5911+ // requirement sources that derive a given requirement.
5912+ //
5913+ // For example, when a nested type of a type parameter is concretized by
5914+ // adding a superclass requirement, we only record the requirement source
5915+ // for the concrete conformance the first time. A subsequently-added
5916+ // superclass requirement on the same parent type does not record a
5917+ // redundant concrete conformance for the child type.
5918+ if (isRedundantExplicitRequirement (otherReq)) {
5919+ // If we have a redundant explicit requirement source, it really is
5920+ // redundant; there's no other derivation that would not be redundant.
5921+ if (!otherSource->isDerivedNonRootRequirement ())
5922+ return None;
5923+
5924+ auto *equivClass = resolveEquivalenceClass (otherSubjectType,
5925+ ArchetypeResolutionKind::AlreadyKnown);
5926+ assert (equivClass &&
5927+ " Explicit requirement names an unknown equivalence class?" );
5928+
5929+ switch (otherReq.getKind ()) {
5930+ case RequirementKind::Conformance: {
5931+ auto *proto = otherReq.getRHS ().get <ProtocolDecl *>();
5932+
5933+ auto found = equivClass->conformsTo .find (proto);
5934+ assert (found != equivClass->conformsTo .end ());
5935+
5936+ bool foundValidDerivation = false ;
5937+ for (const auto &constraint : found->second ) {
5938+ if (isValidRequirementDerivationPath (
5939+ visited, otherReq.getKind (),
5940+ constraint.source , proto,
5941+ requirementSignatureSelfProto)) {
5942+ foundValidDerivation = true ;
5943+ break ;
5944+ }
5945+ }
5946+
5947+ if (!foundValidDerivation)
5948+ return None;
5949+
5950+ break ;
5951+ }
5952+
5953+ case RequirementKind::Superclass: {
5954+ auto superclass = getCanonicalTypeInContext (
5955+ otherReq.getRHS ().get <Type>(), { });
5956+
5957+ for (const auto &constraint : equivClass->superclassConstraints ) {
5958+ auto otherSuperclass = getCanonicalTypeInContext (
5959+ constraint.value , { });
5960+
5961+ if (superclass->isExactSuperclassOf (otherSuperclass)) {
5962+ bool foundValidDerivation = false ;
5963+ if (isValidRequirementDerivationPath (
5964+ visited, otherReq.getKind (),
5965+ constraint.source , otherSuperclass,
5966+ requirementSignatureSelfProto)) {
5967+ foundValidDerivation = true ;
5968+ break ;
5969+ }
5970+
5971+ if (!foundValidDerivation)
5972+ return None;
5973+ }
5974+ }
5975+
5976+ break ;
5977+ }
5978+
5979+ case RequirementKind::Layout: {
5980+ auto layout = otherReq.getRHS ().get <LayoutConstraint>();
5981+
5982+ for (const auto &constraint : equivClass->layoutConstraints ) {
5983+ auto otherLayout = constraint.value ;
5984+
5985+ if (layout == otherLayout) {
5986+ bool foundValidDerivation = false ;
5987+ if (isValidRequirementDerivationPath (
5988+ visited, otherReq.getKind (),
5989+ constraint.source , otherLayout,
5990+ requirementSignatureSelfProto)) {
5991+ foundValidDerivation = true ;
5992+ break ;
5993+ }
5994+
5995+ if (!foundValidDerivation)
5996+ return None;
5997+ }
5998+ }
5999+
6000+ break ;
6001+ }
6002+
6003+ case RequirementKind::SameType:
6004+ llvm_unreachable (" Should not see same type requirements here" );
6005+ }
6006+ }
6007+
59086008 if (auto *depMemType = otherSubjectType->getAs <DependentMemberType>()) {
59096009 // If 'req' is based on some other conformance requirement
59106010 // `T.[P.]A : Q', we want to make sure that we have a
@@ -5975,6 +6075,9 @@ void GenericSignatureBuilder::computeRedundantRequirements(
59756075 Impl->computedRedundantRequirements = true ;
59766076#endif
59776077
6078+ FrontendStatsTracer tracer (Context.Stats ,
6079+ " compute-redundant-requirements" );
6080+
59786081 // This sort preserves iteration order with the legacy algorithm.
59796082 SmallVector<ExplicitRequirement, 2 > requirements (
59806083 Impl->ExplicitRequirements .begin (),
0 commit comments