Skip to content

AST: Cache ProtocolDecl::getInheritedProtocols() [5.0] #21971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ class alignas(1 << DeclAlignInBits) Decl {
HasLazyConformances : 1
);

SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+2+8+16,
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+2+1+8+16,
/// Whether the \c RequiresClass bit is valid.
RequiresClassValid : 1,

Expand All @@ -514,6 +514,9 @@ class alignas(1 << DeclAlignInBits) Decl {
/// The stage of the circularity check for this protocol.
Circularity : 2,

/// Whether we've computed the inherited protocols list yet.
InheritedProtocolsValid : 1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to set this to false in the constructor!


: NumPadBits,

/// If this is a compiler-known protocol, this will be a KnownProtocolKind
Expand Down Expand Up @@ -3892,15 +3895,7 @@ struct SelfReferenceKind {
class ProtocolDecl final : public NominalTypeDecl {
SourceLoc ProtocolLoc;

/// The generic signature representing exactly the new requirements introduced
/// by this protocol.
const Requirement *RequirementSignature = nullptr;

bool requiresClassSlow();

bool existentialConformsToSelfSlow();

bool existentialTypeSupportedSlow(LazyResolver *resolver);
ArrayRef<ProtocolDecl *> InheritedProtocols;

struct {
/// The superclass decl and a bit to indicate whether the
Expand All @@ -3912,6 +3907,18 @@ class ProtocolDecl final : public NominalTypeDecl {
llvm::PointerIntPair<Type, 1, bool> SuperclassType;
} LazySemanticInfo;

/// The generic signature representing exactly the new requirements introduced
/// by this protocol.
const Requirement *RequirementSignature = nullptr;

bool requiresClassSlow();

bool existentialConformsToSelfSlow();

bool existentialTypeSupportedSlow(LazyResolver *resolver);

ArrayRef<ProtocolDecl *> getInheritedProtocolsSlow();

friend class SuperclassDeclRequest;
friend class SuperclassTypeRequest;
friend class TypeChecker;
Expand All @@ -3924,7 +3931,12 @@ class ProtocolDecl final : public NominalTypeDecl {
using Decl::getASTContext;

/// Retrieve the set of protocols inherited from this protocol.
llvm::TinyPtrVector<ProtocolDecl *> getInheritedProtocols() const;
ArrayRef<ProtocolDecl *> getInheritedProtocols() const {
if (Bits.ProtocolDecl.InheritedProtocolsValid)
return InheritedProtocols;

return const_cast<ProtocolDecl *>(this)->getInheritedProtocolsSlow();
}

/// Determine whether this protocol has a superclass.
bool hasSuperclass() const { return (bool)getSuperclassDecl(); }
Expand Down
17 changes: 11 additions & 6 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3856,16 +3856,19 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
Bits.ProtocolDecl.Circularity
= static_cast<unsigned>(CircularityCheck::Unchecked);
Bits.ProtocolDecl.InheritedProtocolsValid = 0;
Bits.ProtocolDecl.NumRequirementsInSignature = 0;
Bits.ProtocolDecl.HasMissingRequirements = false;
Bits.ProtocolDecl.KnownProtocol = 0;
setTrailingWhereClause(TrailingWhere);
setTrailingWhereClause(TrailingWhere);
}

llvm::TinyPtrVector<ProtocolDecl *>
ProtocolDecl::getInheritedProtocols() const {
llvm::TinyPtrVector<ProtocolDecl *> result;
SmallPtrSet<const ProtocolDecl *, 4> known;
ArrayRef<ProtocolDecl *>
ProtocolDecl::getInheritedProtocolsSlow() {
Bits.ProtocolDecl.InheritedProtocolsValid = true;

llvm::SmallVector<ProtocolDecl *, 2> result;
SmallPtrSet<const ProtocolDecl *, 2> known;
known.insert(this);
bool anyObject = false;
for (const auto found :
Expand All @@ -3877,7 +3880,9 @@ ProtocolDecl::getInheritedProtocols() const {
}
}

return result;
auto &ctx = getASTContext();
InheritedProtocols = ctx.AllocateCopy(result);
return InheritedProtocols;
}

llvm::TinyPtrVector<AssociatedTypeDecl *>
Expand Down
10 changes: 9 additions & 1 deletion lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,14 @@ SelfBoundsFromWhereClauseRequest::evaluate(
auto *extDecl = decl.dyn_cast<ExtensionDecl *>();

DeclContext *dc = protoDecl ? (DeclContext *)protoDecl : (DeclContext *)extDecl;

// A protocol or extension 'where' clause can reference associated types of
// the protocol itself, so we have to start unqualified lookup from 'dc'.
//
// However, the right hand side of a 'Self' conformance constraint must be
// resolved before unqualified lookup into 'dc' can work, so we make an
// exception here and begin lookup from the parent context instead.
auto *lookupDC = dc->getParent();
auto requirements = protoDecl ? protoDecl->getTrailingWhereClause()
: extDecl->getTrailingWhereClause();

Expand Down Expand Up @@ -619,7 +627,7 @@ SelfBoundsFromWhereClauseRequest::evaluate(
// Resolve the right-hand side.
DirectlyReferencedTypeDecls rhsDecls;
if (auto typeRepr = req.getConstraintRepr()) {
rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, dc);
rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, lookupDC);
} else if (Type type = req.getConstraint()) {
rhsDecls = directReferencesForType(type);
}
Expand Down
12 changes: 12 additions & 0 deletions test/CircularReferences/protocols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %target-typecheck-verify-swift -debug-cycles 2>&1 | %FileCheck --allow-empty %s

// Verify that protocol where clause lookups don't cause cyclic dependencies.

// expected-no-diagnostics

class C { }
protocol Q { }
protocol P where Self : Q, Self : C { }

// CHECK-NOT: CYCLE DETECTED