Skip to content

[Name lookup] Use decl-based name lookup for protocol-related queries #18530

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
23 changes: 23 additions & 0 deletions include/swift/AST/NameLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,29 @@ void lookupInModule(ModuleDecl *module, ModuleDecl::AccessPathTy accessPath,
ArrayRef<ModuleDecl::ImportedModule> extraImports = {});

} // end namespace namelookup

/// Retrieve the set of nominal type declarations that are directly
/// "inherited" by the given declaration at a particular position in the
/// list of "inherited" types.
///
/// Add anything we find to the \c result vector. If we come across the
/// AnyObject type, set \c anyObject true.
void getDirectlyInheritedNominalTypeDecls(
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
unsigned i,
llvm::SmallVectorImpl<std::pair<SourceLoc, NominalTypeDecl *>> &result,
bool &anyObject);

/// Retrieve the set of nominal type declarations that are directly
/// "inherited" by the given declaration, looking through typealiases
/// and splitting out the components of compositions.
///
/// If we come across the AnyObject type, set \c anyObject true.
SmallVector<std::pair<SourceLoc, NominalTypeDecl *>, 4>
getDirectlyInheritedNominalTypeDecls(
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
bool &anyObject);

} // end namespace swift

#endif
23 changes: 7 additions & 16 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/ProtocolConformanceRef.h"
#include "llvm/Support/SaveAndRestore.h"
Expand Down Expand Up @@ -488,22 +489,12 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc,
void ConformanceLookupTable::addInheritedProtocols(
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
ConformanceSource source) {
// Visit each of the types in the inheritance list to find
// protocols.
auto typeDecl = decl.dyn_cast<TypeDecl *>();
auto extDecl = decl.dyn_cast<ExtensionDecl *>();
unsigned numInherited = typeDecl ? typeDecl->getInherited().size()
: extDecl->getInherited().size();
for (auto index : range(numInherited)) {
Type inheritedType = typeDecl ? typeDecl->getInheritedType(index)
: extDecl->getInheritedType(index);
if (!inheritedType || !inheritedType->isExistentialType())
continue;
SourceLoc loc = typeDecl ? typeDecl->getInherited()[index].getLoc()
: extDecl->getInherited()[index].getLoc();
auto layout = inheritedType->getExistentialLayout();
for (auto *proto : layout.getProtocols())
addProtocol(proto->getDecl(), loc, source);
// Find all of the protocols in the inheritance list.
bool anyObject = false;
for (const auto &found :
getDirectlyInheritedNominalTypeDecls(decl, anyObject)) {
if (auto proto = dyn_cast<ProtocolDecl>(found.second))
addProtocol(proto, found.first, source);
}
}

Expand Down
102 changes: 43 additions & 59 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "swift/AST/LazyResolver.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
Expand Down Expand Up @@ -3538,41 +3539,20 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
llvm::TinyPtrVector<ProtocolDecl *>
ProtocolDecl::getInheritedProtocols() const {
llvm::TinyPtrVector<ProtocolDecl *> result;

// FIXME: Gather inherited protocols from the "inherited" list.
// We shouldn't need this, but it shows up in recursive invocations.
if (!isRequirementSignatureComputed()) {
SmallPtrSet<ProtocolDecl *, 4> known;
if (Bits.ProtocolDecl.ComputingInheritedProtocols) return result;

auto *self = const_cast<ProtocolDecl *>(this);
self->Bits.ProtocolDecl.ComputingInheritedProtocols = true;
for (unsigned index : indices(getInherited())) {
if (auto type = getInheritedType(index)) {
// Only protocols can appear in the inheritance clause
// of a protocol -- anything else should get diagnosed
// elsewhere.
if (type->isExistentialType()) {
auto layout = type->getExistentialLayout();
for (auto protoTy : layout.getProtocols()) {
auto *protoDecl = protoTy->getDecl();
if (known.insert(protoDecl).second)
result.push_back(protoDecl);
}
}
}
SmallPtrSet<const ProtocolDecl *, 4> known;
known.insert(this);
bool anyObject = false;
for (const auto &found :
getDirectlyInheritedNominalTypeDecls(
const_cast<ProtocolDecl *>(this), anyObject)) {
if (auto proto = dyn_cast<ProtocolDecl>(found.second)) {
if (known.insert(proto).second)
result.push_back(proto);
}
self->Bits.ProtocolDecl.ComputingInheritedProtocols = false;
return result;
}

// Gather inherited protocols from the requirement signature.
auto selfType = getProtocolSelfType();
for (const auto &req : getRequirementSignature()) {
if (req.getKind() == RequirementKind::Conformance &&
req.getFirstType()->isEqual(selfType))
result.push_back(req.getSecondType()->castTo<ProtocolType>()->getDecl());
}
// FIXME: ComputingInheritedProtocols is dynamically dead.

return result;
}

Expand Down Expand Up @@ -3643,40 +3623,44 @@ bool ProtocolDecl::walkInheritedProtocols(
bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
if (this == super)
return false;

auto allProtocols = getLocalProtocols();
return std::find(allProtocols.begin(), allProtocols.end(), super)
!= allProtocols.end();

return walkInheritedProtocols([super](ProtocolDecl *inherited) {
if (inherited == super)
return TypeWalker::Action::Stop;

return TypeWalker::Action::Continue;
});
}

bool ProtocolDecl::requiresClassSlow() {
// Set this first to catch (invalid) circular inheritance.
Bits.ProtocolDecl.RequiresClassValid = true;
Bits.ProtocolDecl.RequiresClass = false;

// Quick check: @objc protocols require a class.
if (isObjC()) {
Bits.ProtocolDecl.RequiresClass = true;
return true;
}

// Otherwise, check if the inheritance clause contains a
// class-constrained existential.
//
// FIXME: Use the requirement signature if available.
Bits.ProtocolDecl.RequiresClass = false;
for (unsigned i : indices(getInherited())) {
Type type = getInheritedType(i);
assert(type && "Should have type checked inheritance clause by now");
if (type->isExistentialType()) {
auto layout = type->getExistentialLayout();
if (layout.requiresClass()) {
Bits.ProtocolDecl.RequiresClass = true;
return true;
}
}
if (type->getClassOrBoundGenericClass()) {
Bits.ProtocolDecl.RequiresClass = true;
return true;
if (isObjC())
return Bits.ProtocolDecl.RequiresClass = true;

// Determine the set of nominal types that this protocol inherits.
bool anyObject = false;
auto allInheritedNominals =
getDirectlyInheritedNominalTypeDecls(this, anyObject);

// Quick check: do we inherit AnyObject?
if (anyObject)
return Bits.ProtocolDecl.RequiresClass = true;

// Look through all of the inherited nominals for a superclass or a
// class-bound protocol.
for (const auto &found : allInheritedNominals) {
// Superclass bound.
if (isa<ClassDecl>(found.second))
return Bits.ProtocolDecl.RequiresClass = true;

// A protocol that might be class-constrained;
if (auto proto = dyn_cast<ProtocolDecl>(found.second)) {
if (proto->requiresClass())
return Bits.ProtocolDecl.RequiresClass = true;
}
}

Expand Down
10 changes: 10 additions & 0 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ ProtocolDecl *DeclContext::getAsProtocolExtensionContext() const {
GenericTypeParamType *DeclContext::getProtocolSelfType() const {
assert(getAsProtocolOrProtocolExtensionContext() && "not a protocol");

auto genericParams = getGenericParamsOfContext();
Copy link
Contributor

Choose a reason for hiding this comment

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

ProtocolDecl::createGenericParamsIfMissing() is lighter weight

Copy link
Member Author

Choose a reason for hiding this comment

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

Indeed, thank you!


if (!genericParams) {
if (auto proto = dyn_cast<ProtocolDecl>(this)) {
getASTContext().getLazyResolver()
->resolveDeclSignature(const_cast<ProtocolDecl *>(proto));
genericParams = getGenericParamsOfContext();
}
}

return getGenericParamsOfContext()->getParams().front()
->getDeclaredInterfaceType()
->castTo<GenericTypeParamType>();
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3016,6 +3016,11 @@ void ArchetypeType::resolveNestedType(
builder.resolveEquivalenceClass(
memberInterfaceType,
ArchetypeResolutionKind::CompleteWellFormed);
if (!equivClass) {
nested.second = ErrorType::get(interfaceType);
return;
}

auto result = equivClass->getTypeInContext(builder, genericEnv);
assert(!nested.second ||
nested.second->isEqual(result) ||
Expand Down
4 changes: 0 additions & 4 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,6 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
// existential's list of conformances and the existential conforms to
// itself.
if (type->isExistentialType()) {
// FIXME: Recursion break.
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure this is not needed? existentialConformsToSelf() can call back into validateDecl() IIRC

Copy link
Member Author

Choose a reason for hiding this comment

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

No longer! It uses simpler queries that don't go through validateDecl(), now that getInheritedProtocols() is all decl-lookup-based.

if (!protocol->hasValidSignature())
return None;

// If the existential type cannot be represented or the protocol does not
// conform to itself, there's no point in looking further.
if (!protocol->existentialConformsToSelf())
Expand Down
Loading