Skip to content

Commit a49b93c

Browse files
committed
GSB: New redundant requirements algorithm
This rewrites the existing redundant requirements algorithm to be simpler, and fix an incorrect behavior in the case where we're building a protocol requirement signature. Consider the following example: protocol P { associatedtype A : P associatedtype B : P where A.B == B } The requirement B : P has two conformance paths here: (B : P) (A : P)(B : P) The naive redundancy algorithm would conclude that (B : P) is redundant because it can be derived as (A : P)(B : P). However, if we drop (B : P), we lose the derived conformance path as well, since it involves the same requirement (B : P). The above example actually worked before this change, because we handled this case in getMinimalConformanceSource() by dropping any derived conformance paths that involve the requirement itself appearing in the "middle" of the path. However, this is insufficient because you can have a "cycle" here with length more than 1. For example, protocol P { associatedtype A : P where A == B.C associatedtype B : P where B == A.C associatedtype C : P where C == A.B } The requirement A : P has two conformance paths here: (A : P) (B : P)(C : P) Similarly, B : P has these two paths: (B : P) (A : P)(C : P) And C : P has these two paths: (C : P) (A : P)(B : P) Since each one of A : P, B : P and C : P has a derived conformance path that does not involve itself, we would conclude that all three were redundant. But this was wrong; while (B : P)(C : P) is a valid derived path for A : P that allows us to drop A : P, once we commit to dropping A : P, we can no longer use the other derived paths (A : P)(C : P) for B : P, and (A : P)(B : P) for C : P, respectively, because they involve A : P, which we dropped. The problem is that we were losing information here. The explicit requirement A : P can be derived as (B : P)(C : P), but we would just say that it was implied by B : P alone. For non-protocol generic signatures, just looking at the root is still sufficient. However, when building a requirement signature of a self-recursive protocol, instead of looking at the root explicit requirement only, we need to look at _all_ intermediate steps in the path that involve the same protocol. This is implemented in a new getBaseRequirements() method, which generalizes the operation of getting the explicit requirement at the root of a derived conformance path by returning a vector of one or more explicit requirements that appear in the path. Also the new algorithm computes redundancy online instead of building a directed graph and then computing SCCs. This is possible by recording newly-discovered redundant requirements immediately, and then using the set of so-far-redundant requirements when evaluating a path. This commit introduces a small regression in an existing test case involving a protocol with a 'derived via concrete' requirement. Subsequent commits in this PR fix the regression and remove the 'derived via concrete' mechanism, since it is no longer necessary. Fixes https://bugs.swift.org/browse/SR-14510 / rdar://problem/76883924.
1 parent ac3cbe5 commit a49b93c

File tree

7 files changed

+431
-790
lines changed

7 files changed

+431
-790
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,11 +654,27 @@ class GenericSignatureBuilder {
654654
bool isRedundantExplicitRequirement(const ExplicitRequirement &req) const;
655655

656656
private:
657-
void computeRedundantRequirements(const ProtocolDecl *requirementSignatureSelfProto);
657+
using GetKindAndRHS = llvm::function_ref<std::pair<RequirementKind, RequirementRHS>()>;
658+
void getBaseRequirements(
659+
GetKindAndRHS getKindAndRHS,
660+
const RequirementSource *source,
661+
const ProtocolDecl *requirementSignatureSelfProto,
662+
ASTContext &ctx, SmallVectorImpl<ExplicitRequirement> &result);
663+
664+
template<typename T, typename Filter>
665+
void checkRequirementRedundancy(
666+
const ExplicitRequirement &req,
667+
const std::vector<Constraint<T>> &constraints,
668+
const ProtocolDecl *requirementSignatureSelfProto,
669+
Filter filter);
670+
671+
void computeRedundantRequirements(
672+
const ProtocolDecl *requirementSignatureSelfProto);
658673

659674
void diagnoseRedundantRequirements() const;
660675

661-
void diagnoseConflictingConcreteTypeRequirements() const;
676+
void diagnoseConflictingConcreteTypeRequirements(
677+
const ProtocolDecl *requirementSignatureSelfProto);
662678

663679
/// Describes the relationship between a given constraint and
664680
/// the canonical constraint of the equivalence class.

0 commit comments

Comments
 (0)