bool DeclContext::lookupQualified(Type type, DeclName member, NLOptions options, LazyResolver *typeResolver, SmallVectorImpl<ValueDecl *> &decls) const { using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); if (type->is<ErrorType>()) return false; auto checkLookupCascading = [this, options]() -> Optional<bool> { switch (static_cast<unsigned>(options & NL_KnownDependencyMask)) { case 0: return isCascadingContextForLookup(/*excludeFunctions=*/false); case NL_KnownNonCascadingDependency: return false; case NL_KnownCascadingDependency: return true; case NL_KnownNoDependency: return None; default: // FIXME: Use llvm::CountPopulation_64 when that's declared constexpr. static_assert(__builtin_popcountll(NL_KnownDependencyMask) == 2, "mask should only include four values"); llvm_unreachable("mask only includes four values"); } }; // Look through lvalue and inout types. type = type->getLValueOrInOutObjectType(); // Look through metatypes. if (auto metaTy = type->getAs<AnyMetatypeType>()) type = metaTy->getInstanceType(); // Look through DynamicSelf. if (auto dynamicSelf = type->getAs<DynamicSelfType>()) type = dynamicSelf->getSelfType(); // Look for module references. if (auto moduleTy = type->getAs<ModuleType>()) { Module *module = moduleTy->getModule(); auto topLevelScope = getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { if (auto maybeLookupCascade = checkLookupCascading()) { recordLookupOfTopLevelName(topLevelScope, member, maybeLookupCascade.getValue()); } lookupInModule(module, /*accessPath=*/{}, member, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, typeResolver, topLevelScope); } else { // Note: This is a lookup into another module. Unless we're compiling // multiple modules at once, or if the other module re-exports this one, // it shouldn't be possible to have a dependency from that module on // anything in this one. // Perform the lookup in all imports of this module. forAllVisibleModules(this, [&](const Module::ImportedModule &import) -> bool { if (import.second != module) return true; lookupInModule(import.second, import.first, member, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, typeResolver, topLevelScope); // If we're able to do an unscoped lookup, we see everything. No need // to keep going. return !import.first.empty(); }); } llvm::SmallPtrSet<ValueDecl *, 4> knownDecls; decls.erase(std::remove_if(decls.begin(), decls.end(), [&](ValueDecl *vd) -> bool { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(vd)) return true; return !knownDecls.insert(vd).second; }), decls.end()); if (auto *debugClient = topLevelScope->getParentModule()->getDebugClient()) filterForDiscriminator(decls, debugClient); return !decls.empty(); } auto &ctx = getASTContext(); if (!ctx.LangOpts.EnableAccessControl) options |= NL_IgnoreAccessibility; // The set of nominal type declarations we should (and have) visited. SmallVector<NominalTypeDecl *, 4> stack; llvm::SmallPtrSet<NominalTypeDecl *, 4> visited; // Handle nominal types. bool wantProtocolMembers = false; bool wantLookupInAllClasses = false; if (auto nominal = type->getAnyNominal()) { visited.insert(nominal); stack.push_back(nominal); wantProtocolMembers = (options & NL_ProtocolMembers) && !isa<ProtocolDecl>(nominal); // If we want dynamic lookup and we're searching in the // AnyObject protocol, note this for later. if (options & NL_DynamicLookup) { if (auto proto = dyn_cast<ProtocolDecl>(nominal)) { if (proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) wantLookupInAllClasses = true; } } } // Handle archetypes else if (auto archetypeTy = type->getAs<ArchetypeType>()) { // Look in the protocols to which the archetype conforms (always). for (auto proto : archetypeTy->getConformsTo()) if (visited.insert(proto).second) stack.push_back(proto); // If requested, look into the superclasses of this archetype. if (options & NL_VisitSupertypes) { if (auto superclassTy = archetypeTy->getSuperclass()) { if (auto superclassDecl = superclassTy->getAnyNominal()) { if (visited.insert(superclassDecl).second) { stack.push_back(superclassDecl); wantProtocolMembers = (options & NL_ProtocolMembers) && !isa<ProtocolDecl>(superclassDecl); } } } } } // Handle protocol compositions. else if (auto compositionTy = type->getAs<ProtocolCompositionType>()) { SmallVector<ProtocolDecl *, 4> protocols; if (compositionTy->isExistentialType(protocols)) { for (auto proto : protocols) { if (visited.insert(proto).second) { stack.push_back(proto); // If we want dynamic lookup and this is the AnyObject // protocol, note this for later. if ((options & NL_DynamicLookup) && proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) wantLookupInAllClasses = true; } } } } // Allow filtering of the visible declarations based on various // criteria. bool onlyCompleteObjectInits = false; auto isAcceptableDecl = [&](NominalTypeDecl *current, Decl *decl) -> bool { // If the decl is currently being type checked, then we have something // cyclic going on. Instead of poking at parts that are potentially not // set up, just assume it is acceptable. This will make sure we produce an // error later. if (decl->isBeingTypeChecked()) return true; // Filter out designated initializers, if requested. if (onlyCompleteObjectInits) { if (auto ctor = dyn_cast<ConstructorDecl>(decl)) { if (!ctor->isInheritable()) return false; } else { return false; } } // Ignore stub implementations. if (auto ctor = dyn_cast<ConstructorDecl>(decl)) { if (ctor->hasStubImplementation()) return false; } // Check access. if (!(options & NL_IgnoreAccessibility)) if (auto VD = dyn_cast<ValueDecl>(decl)) return VD->isAccessibleFrom(this); return true; }; ReferencedNameTracker *tracker = nullptr; if (auto containingSourceFile = dyn_cast<SourceFile>(getModuleScopeContext())) tracker = containingSourceFile->getReferencedNameTracker(); bool isLookupCascading; if (tracker) { if (auto maybeLookupCascade = checkLookupCascading()) isLookupCascading = maybeLookupCascade.getValue(); else tracker = nullptr; } // Visit all of the nominal types we know about, discovering any others // we need along the way. while (!stack.empty()) { auto current = stack.back(); stack.pop_back(); if (tracker) tracker->addUsedMember({current, member.getBaseName()},isLookupCascading); // Make sure we've resolved implicit constructors, if we need them. if (member.getBaseName() == ctx.Id_init && typeResolver) typeResolver->resolveImplicitConstructors(current); // Look for results within the current nominal type and its extensions. bool currentIsProtocol = isa<ProtocolDecl>(current); for (auto decl : current->lookupDirect(member)) { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(decl)) continue; // Resolve the declaration signature when we find the // declaration. if (typeResolver && !decl->isBeingTypeChecked()) { typeResolver->resolveDeclSignature(decl); if (!decl->hasType()) continue; } if (isAcceptableDecl(current, decl)) decls.push_back(decl); } // If we're not supposed to visit our supertypes, we're done. if ((options & NL_VisitSupertypes) == 0) continue; // Visit superclass. if (auto classDecl = dyn_cast<ClassDecl>(current)) { // If we're looking for initializers, only look at the superclass if the // current class permits inheritance. Even then, only find complete // object initializers. bool visitSuperclass = true; if (member.getBaseName() == ctx.Id_init) { if (classDecl->inheritsSuperclassInitializers(typeResolver)) onlyCompleteObjectInits = true; else visitSuperclass = false; } if (visitSuperclass) { if (auto superclassType = classDecl->getSuperclass()) if (auto superclassDecl = superclassType->getClassOrBoundGenericClass()) if (visited.insert(superclassDecl).second) stack.push_back(superclassDecl); } } // If we're not looking at a protocol and we're not supposed to // visit the protocols that this type conforms to, skip the next // step. if (!wantProtocolMembers && !currentIsProtocol) continue; SmallVector<ProtocolDecl *, 4> protocols; for (auto proto : current->getAllProtocols()) { if (visited.insert(proto).second) { stack.push_back(proto); } } // For a class, we don't need to visit the protocol members of the // superclass: that's already handled. if (isa<ClassDecl>(current)) wantProtocolMembers = false; } // If we want to perform lookup into all classes, do so now. if (wantLookupInAllClasses) { if (tracker) tracker->addDynamicLookupName(member.getBaseName(), isLookupCascading); // Collect all of the visible declarations. SmallVector<ValueDecl *, 4> allDecls; forAllVisibleModules(this, [&](Module::ImportedModule import) { import.second->lookupClassMember(import.first, member, allDecls); }); // For each declaration whose context is not something we've // already visited above, add it to the list of declarations. llvm::SmallPtrSet<ValueDecl *, 4> knownDecls; for (auto decl : allDecls) { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(decl)) continue; if (typeResolver && !decl->isBeingTypeChecked()) { typeResolver->resolveDeclSignature(decl); if (!decl->hasType()) continue; } // If the declaration has an override, name lookup will also have // found the overridden method. Skip this declaration, because we // prefer the overridden method. if (decl->getOverriddenDecl()) continue; auto dc = decl->getDeclContext(); auto nominal = dyn_cast<NominalTypeDecl>(dc); if (!nominal) { auto ext = cast<ExtensionDecl>(dc); nominal = ext->getExtendedType()->getAnyNominal(); assert(nominal && "Couldn't find nominal type?"); } // If we didn't visit this nominal type above, add this // declaration to the list. if (!visited.count(nominal) && knownDecls.insert(decl).second && isAcceptableDecl(nominal, decl)) decls.push_back(decl); } } // If we're supposed to remove overridden declarations, do so now. if (options & NL_RemoveOverridden) removeOverriddenDecls(decls); // If we're supposed to remove shadowed/hidden declarations, do so now. Module *M = getParentModule(); if (options & NL_RemoveNonVisible) removeShadowedDecls(decls, M, typeResolver); if (auto *debugClient = M->getDebugClient()) filterForDiscriminator(decls, debugClient); // We're done. Report success/failure. return !decls.empty(); }