//===--- NameLookupRequests.cpp - Name Lookup Requests --------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Evaluator.h" #include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PotentialMacroExpansions.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Assertions.h" #include "swift/ClangImporter/ClangImporterRequests.h" #include "swift/Subsystems.h" using namespace swift; namespace swift { // Implement the name lookup type zone. #define SWIFT_TYPEID_ZONE NameLookup #define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def" #include "swift/Basic/ImplementTypeIDZone.h" #undef SWIFT_TYPEID_ZONE #undef SWIFT_TYPEID_HEADER } //----------------------------------------------------------------------------// // Referenced inherited decls computation. //----------------------------------------------------------------------------// SourceLoc InheritedDeclsReferencedRequest::getNearestLoc() const { const auto &storage = getStorage(); auto inheritedTypes = InheritedTypes(std::get<0>(storage)); return inheritedTypes.getEntry(std::get<1>(storage)).getLoc(); } //----------------------------------------------------------------------------// // Superclass declaration computation. //----------------------------------------------------------------------------// void SuperclassDeclRequest::diagnoseCycle(DiagnosticEngine &diags) const { // FIXME: Improve this diagnostic. auto nominalDecl = std::get<0>(getStorage()); diags.diagnose(nominalDecl, diag::circular_class_inheritance, nominalDecl->getName()); } void SuperclassDeclRequest::noteCycleStep(DiagnosticEngine &diags) const { auto decl = std::get<0>(getStorage()); diags.diagnose(decl, diag::decl_declared_here_with_kind, decl); } std::optional SuperclassDeclRequest::getCachedResult() const { auto nominalDecl = std::get<0>(getStorage()); if (auto *classDecl = dyn_cast(nominalDecl)) if (classDecl->LazySemanticInfo.SuperclassDecl.getInt()) return classDecl->LazySemanticInfo.SuperclassDecl.getPointer(); if (auto *protocolDecl = dyn_cast(nominalDecl)) if (protocolDecl->LazySemanticInfo.SuperclassDecl.getInt()) return protocolDecl->LazySemanticInfo.SuperclassDecl.getPointer(); return std::nullopt; } void SuperclassDeclRequest::cacheResult(ClassDecl *value) const { auto nominalDecl = std::get<0>(getStorage()); if (auto *classDecl = dyn_cast(nominalDecl)) classDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true); if (auto *protocolDecl = dyn_cast(nominalDecl)) protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true); } //----------------------------------------------------------------------------// // InheritedProtocolsRequest computation. //----------------------------------------------------------------------------// std::optional> InheritedProtocolsRequest::getCachedResult() const { auto proto = std::get<0>(getStorage()); if (!proto->areInheritedProtocolsValid()) return std::nullopt; return proto->InheritedProtocols; } void InheritedProtocolsRequest::cacheResult(ArrayRef PDs) const { auto proto = std::get<0>(getStorage()); proto->InheritedProtocols = PDs; proto->setInheritedProtocolsValid(); } void InheritedProtocolsRequest::writeDependencySink( evaluator::DependencyCollector &tracker, ArrayRef PDs) const { for (auto *parentProto : PDs) { tracker.addPotentialMember(parentProto); } } //----------------------------------------------------------------------------// // AllInheritedProtocolsRequest computation. //----------------------------------------------------------------------------// std::optional> AllInheritedProtocolsRequest::getCachedResult() const { auto proto = std::get<0>(getStorage()); if (!proto->areAllInheritedProtocolsValid()) return std::nullopt; return proto->AllInheritedProtocols; } void AllInheritedProtocolsRequest::cacheResult(ArrayRef PDs) const { auto proto = std::get<0>(getStorage()); proto->AllInheritedProtocols = PDs; proto->setAllInheritedProtocolsValid(); } //----------------------------------------------------------------------------// // ProtocolRequirementsRequest computation. //----------------------------------------------------------------------------// std::optional> ProtocolRequirementsRequest::getCachedResult() const { auto proto = std::get<0>(getStorage()); if (!proto->areProtocolRequirementsValid()) return std::nullopt; return proto->ProtocolRequirements; } void ProtocolRequirementsRequest::cacheResult(ArrayRef PDs) const { auto proto = std::get<0>(getStorage()); proto->ProtocolRequirements = PDs; proto->setProtocolRequirementsValid(); } //----------------------------------------------------------------------------// // Missing designated initializers computation //----------------------------------------------------------------------------// std::optional HasMissingDesignatedInitializersRequest::getCachedResult() const { auto classDecl = std::get<0>(getStorage()); return classDecl->getCachedHasMissingDesignatedInitializers(); } void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const { auto classDecl = std::get<0>(getStorage()); classDecl->setHasMissingDesignatedInitializers(result); } bool HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator, ClassDecl *subject) const { // Short-circuit and check for the attribute here. if (subject->getAttrs().hasAttribute()) return true; AccessScope scope = subject->getFormalAccessScope(/*useDC*/nullptr, /*treatUsableFromInlineAsPublic*/true); // This flag only makes sense for public types that will be written in the // module. if (!scope.isPublic()) return false; // Make sure any implicit constructors are synthesized. (void)evaluateOrDefault( evaluator, ResolveImplicitMemberRequest{subject, ImplicitMemberAction::ResolveImplicitInit}, {}); auto constructors = subject->lookupDirect(DeclBaseName::createConstructor()); return llvm::any_of(constructors, [&](ValueDecl *decl) { auto init = cast(decl); if (!init->isDesignatedInit()) return false; AccessScope scope = init->getFormalAccessScope(/*useDC*/nullptr, /*treatUsableFromInlineAsPublic*/true); return !scope.isPublic(); }); } //----------------------------------------------------------------------------// // Extended nominal computation. //----------------------------------------------------------------------------// std::optional ExtendedNominalRequest::getCachedResult() const { // Note: if we fail to compute any nominal declaration, it's considered // a cache miss. This allows us to recompute the extended nominal types // during extension binding. // This recomputation is also what allows you to extend types defined inside // other extensions, regardless of source file order. See \c bindExtensions(), // which uses a worklist algorithm that attempts to bind everything until // fixed point. auto ext = std::get<0>(getStorage()); if (!ext->hasBeenBound() || !ext->getExtendedNominal()) return std::nullopt; return ext->getExtendedNominal(); } void ExtendedNominalRequest::cacheResult(NominalTypeDecl *value) const { auto ext = std::get<0>(getStorage()); ext->setExtendedNominal(value); } void ExtendedNominalRequest::writeDependencySink( evaluator::DependencyCollector &tracker, NominalTypeDecl *value) const { if (!value) return; // Ensure this extension comes from a source file. auto *SF = std::get<0>(getStorage())->getParentSourceFile(); if (!SF) return; tracker.addPotentialMember(value); } //----------------------------------------------------------------------------// // Destructor computation. //----------------------------------------------------------------------------// std::optional GetDestructorRequest::getCachedResult() const { auto *classDecl = std::get<0>(getStorage()); auto results = classDecl->lookupDirect(DeclBaseName::createDestructor()); if (results.empty()) return std::nullopt; return cast(results.front()); } void GetDestructorRequest::cacheResult(DestructorDecl *value) const { auto *classDecl = std::get<0>(getStorage()); classDecl->getImplementationContext()->addMember(value); } //----------------------------------------------------------------------------// // GenericParamListRequest computation. //----------------------------------------------------------------------------// std::optional GenericParamListRequest::getCachedResult() const { using GenericParamsState = GenericContext::GenericParamsState; auto *decl = std::get<0>(getStorage()); switch (decl->GenericParamsAndState.getInt()) { case GenericParamsState::TypeChecked: case GenericParamsState::ParsedAndTypeChecked: return decl->GenericParamsAndState.getPointer(); case GenericParamsState::Parsed: return std::nullopt; } } void GenericParamListRequest::cacheResult(GenericParamList *params) const { using GenericParamsState = GenericContext::GenericParamsState; auto *context = std::get<0>(getStorage()); if (params) params->setDeclContext(context); assert(context->GenericParamsAndState.getInt() == GenericParamsState::Parsed); bool hadParsedGenericParams = context->GenericParamsAndState.getPointer() != nullptr; auto newState = hadParsedGenericParams ? GenericParamsState::ParsedAndTypeChecked : GenericParamsState::TypeChecked; context->GenericParamsAndState.setPointerAndInt(params, newState); } //----------------------------------------------------------------------------// // UnqualifiedLookupRequest computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, const UnqualifiedLookupDescriptor &desc) { out << "looking up "; simple_display(out, desc.Name); out << " from "; simple_display(out, desc.DC); out << " with options "; simple_display(out, desc.Options); } SourceLoc swift::extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc) { return extractNearestSourceLoc(desc.DC); } //----------------------------------------------------------------------------// // DirectLookupRequest computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, const DirectLookupDescriptor &desc) { out << "directly looking up "; simple_display(out, desc.Name); out << " on "; simple_display(out, desc.DC); out << " with options "; simple_display(out, desc.Options); } SourceLoc swift::extractNearestSourceLoc(const DirectLookupDescriptor &desc) { return extractNearestSourceLoc(desc.DC); } //----------------------------------------------------------------------------// // LookupOperatorRequest computation. //----------------------------------------------------------------------------// OperatorLookupDescriptor OperatorLookupDescriptor::forDC(const DeclContext *DC, Identifier name) { auto *moduleDC = DC->getModuleScopeContext(); if (auto *file = dyn_cast(moduleDC)) { return OperatorLookupDescriptor::forFile(file, name); } else { auto *mod = cast(moduleDC->getAsDecl()); return OperatorLookupDescriptor::forModule(mod, name); } } ArrayRef OperatorLookupDescriptor::getFiles() const { if (auto *module = getModule()) return module->getFiles(); // Return an ArrayRef pointing to the FileUnit in the union. return llvm::ArrayRef(*fileOrModule.getAddrOfPtr1()); } void swift::simple_display(llvm::raw_ostream &out, const OperatorLookupDescriptor &desc) { out << "looking up operator "; simple_display(out, desc.name); out << " in "; simple_display(out, desc.fileOrModule); } SourceLoc swift::extractNearestSourceLoc(const OperatorLookupDescriptor &desc) { return extractNearestSourceLoc(desc.fileOrModule); } void DirectLookupRequest::writeDependencySink( evaluator::DependencyCollector &tracker, const TinyPtrVector &result) const { auto &desc = std::get<0>(getStorage()); // Add used members from the perspective of // 1) The decl context they are found in // 2) The decl context of the request // This gets us a dependency not just on `Foo.bar` but on `extension Foo.bar`. for (const auto *member : result) { tracker.addUsedMember(member->getDeclContext(), desc.Name.getBaseName()); } tracker.addUsedMember(desc.DC, desc.Name.getBaseName()); } //----------------------------------------------------------------------------// // LookupInModuleRequest computation. //----------------------------------------------------------------------------// void LookupInModuleRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, const QualifiedLookupResult &l) const { auto *DC = std::get<0>(getStorage()); auto member = std::get<1>(getStorage()); // Decline to record lookups if the module in question has no incremental // dependency information available. auto *module = DC->getParentModule(); if (module->isMainModule() || module->hasIncrementalInfo()) { reqTracker.addTopLevelName(member.getBaseName()); } } void swift::simple_display(llvm::raw_ostream &out, const LookupConformanceDescriptor &desc) { out << "looking up conformance to "; simple_display(out, desc.PD); out << " for "; out << desc.Ty.getString(); } //----------------------------------------------------------------------------// // AnyObjectLookupRequest computation. //----------------------------------------------------------------------------// void AnyObjectLookupRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, const QualifiedLookupResult &l) const { auto member = std::get<1>(getStorage()); reqTracker.addDynamicLookupName(member.getBaseName()); } SourceLoc swift::extractNearestSourceLoc(const LookupConformanceDescriptor &desc) { return SourceLoc(); } //----------------------------------------------------------------------------// // ModuleQualifiedLookupRequest computation. //----------------------------------------------------------------------------// void ModuleQualifiedLookupRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, const QualifiedLookupResult &l) const { auto *module = std::get<1>(getStorage()); auto member = std::get<2>(getStorage()); // Decline to record lookups if the module in question has no incremental // dependency information available. if (module->isMainModule() || module->hasIncrementalInfo()) { reqTracker.addTopLevelName(member.getBaseName()); } } //----------------------------------------------------------------------------// // LookupConformanceInModuleRequest computation. //----------------------------------------------------------------------------// void LookupConformanceInModuleRequest::writeDependencySink( evaluator::DependencyCollector &reqTracker, ProtocolConformanceRef lookupResult) const { if (lookupResult.isInvalid() || !lookupResult.isConcrete()) return; auto &desc = std::get<0>(getStorage()); auto *Adoptee = desc.Ty->getAnyNominal(); if (!Adoptee) return; // Decline to record lookups if the module in question has no incremental // dependency information available. auto *conformance = lookupResult.getConcrete(); auto *module = conformance->getDeclContext()->getParentModule(); if (module->isMainModule() || module->hasIncrementalInfo()) { reqTracker.addPotentialMember(Adoptee); } } //----------------------------------------------------------------------------// // UnqualifiedLookupRequest computation. //----------------------------------------------------------------------------// void UnqualifiedLookupRequest::writeDependencySink( evaluator::DependencyCollector &track, const LookupResult &res) const { auto &desc = std::get<0>(getStorage()); track.addTopLevelName(desc.Name.getBaseName()); } //----------------------------------------------------------------------------// // SPIGroupsRequest computation. //----------------------------------------------------------------------------// std::optional> SPIGroupsRequest::getCachedResult() const { auto *decl = std::get<0>(getStorage()); if (decl->hasNoSPIGroups()) return ArrayRef(); return decl->getASTContext().evaluator.getCachedNonEmptyOutput(*this); } void SPIGroupsRequest::cacheResult(llvm::ArrayRef result) const { auto *decl = std::get<0>(getStorage()); if (result.empty()) { const_cast(decl)->setHasNoSPIGroups(); return; } decl->getASTContext().evaluator.cacheNonEmptyOutput(*this, std::move(result)); } // The following clang importer requests have some definitions here to prevent // linker errors when building lib syntax parser (which doesn't link with the // clang importer). //----------------------------------------------------------------------------// // ClangDirectLookupRequest computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, const ClangDirectLookupDescriptor &desc) { out << "Looking up "; simple_display(out, desc.name); out << " in "; simple_display(out, desc.decl); } SourceLoc swift::extractNearestSourceLoc(const ClangDirectLookupDescriptor &desc) { return extractNearestSourceLoc(desc.decl); } //----------------------------------------------------------------------------// // CXXNamespaceMemberLookup computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, const CXXNamespaceMemberLookupDescriptor &desc) { out << "Looking up "; simple_display(out, desc.name); out << " in "; simple_display(out, desc.namespaceDecl); } SourceLoc swift::extractNearestSourceLoc(const CXXNamespaceMemberLookupDescriptor &desc) { return extractNearestSourceLoc(desc.namespaceDecl); } //----------------------------------------------------------------------------// // ClangRecordMemberLookup computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, const ClangRecordMemberLookupDescriptor &desc) { out << "Looking up "; simple_display(out, desc.name); out << " in "; simple_display(out, desc.recordDecl); if (desc.recordDecl != desc.inheritingDecl) out << " inherited by "; simple_display(out, desc.inheritingDecl); } SourceLoc swift::extractNearestSourceLoc(const ClangRecordMemberLookupDescriptor &desc) { return extractNearestSourceLoc(desc.recordDecl); } //----------------------------------------------------------------------------// // CustomRefCountingOperation computation. //----------------------------------------------------------------------------// void swift::simple_display(llvm::raw_ostream &out, CustomRefCountingOperationDescriptor desc) { out << "Finding custom (foreign reference) reference counting operation '" << (desc.kind == CustomRefCountingOperationKind::retain ? "retain" : "release") << "' for '" << desc.decl->getNameStr() << "'.\n"; } SourceLoc swift::extractNearestSourceLoc(CustomRefCountingOperationDescriptor desc) { return SourceLoc(); } //----------------------------------------------------------------------------// // Macro-related adjustments to name lookup requests. //----------------------------------------------------------------------------// // // Macros introduced a significant wrinkle into Swift's name lookup mechanism. // Specifically, when resolving names (and, really, anything else) within the // arguments to a macro expansion, name lookup must not try to expand any // macros, because doing so trivially creates a cyclic dependency amongst the // macro expansions that will be detected by the request-evaluator. // // We use source locations to answer the question "is this part of an argument // to a macro?" through `namelookup::isInMacroArgument`. If the answer is yes, // then we adjust the options used for the name lookup request we are forming // to exclude macro expansions. The evaluation of that request will then avoid // expanding any macros, and not produce any results that involve entries in // already-expanded macros. By adjusting the request itself, we still // distinguish between requests that can and cannot look into macro expansions, // so it doesn't break caching for those immediate requests. /// Exclude macros in the unqualified lookup descriptor if we need to. static UnqualifiedLookupDescriptor contextualizeOptions( UnqualifiedLookupDescriptor descriptor) { if (!descriptor.Options.contains( UnqualifiedLookupFlags::ExcludeMacroExpansions) && namelookup::isInMacroArgument( descriptor.DC->getParentSourceFile(), descriptor.Loc)) descriptor.Options |= UnqualifiedLookupFlags::ExcludeMacroExpansions; if (!descriptor.Options.contains(UnqualifiedLookupFlags::ABIProviding) && namelookup::isInABIAttr( descriptor.DC->getParentSourceFile(), descriptor.Loc)) descriptor.Options |= UnqualifiedLookupFlags::ABIProviding; return descriptor; } /// Exclude macros in the direct lookup descriptor if we need to. static DirectLookupDescriptor contextualizeOptions( DirectLookupDescriptor descriptor, SourceLoc loc) { if (!descriptor.Options.contains( NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions) && namelookup::isInMacroArgument( descriptor.DC->getParentSourceFile(), loc)) descriptor.Options |= NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions; if (!descriptor.Options.contains( NominalTypeDecl::LookupDirectFlags::ABIProviding) && namelookup::isInABIAttr( descriptor.DC->getParentSourceFile(), loc)) descriptor.Options |= NominalTypeDecl::LookupDirectFlags::ABIProviding; return descriptor; } /// Exclude macros in the name lookup options if we need to. static NLOptions contextualizeOptions(const DeclContext *dc, SourceLoc loc, NLOptions options) { if (!(options & NL_ExcludeMacroExpansions) && namelookup::isInMacroArgument(dc->getParentSourceFile(), loc)) options |= NL_ExcludeMacroExpansions; if (!(options & NL_ABIProviding) && namelookup::isInABIAttr(dc->getParentSourceFile(), loc)) options |= NL_ABIProviding; return options; } UnqualifiedLookupRequest::UnqualifiedLookupRequest( UnqualifiedLookupDescriptor descriptor ) : SimpleRequest(contextualizeOptions(descriptor)) { } LookupInModuleRequest::LookupInModuleRequest( const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, namelookup::ResolutionKind resolutionKind, const DeclContext *moduleScopeContext, SourceLoc loc, NLOptions options ) : SimpleRequest(moduleOrFile, name, lookupKind, resolutionKind, moduleScopeContext, contextualizeOptions(moduleOrFile, loc, options)) { } ModuleQualifiedLookupRequest::ModuleQualifiedLookupRequest( const DeclContext *dc, ModuleDecl *module, DeclNameRef name, SourceLoc loc, NLOptions options ) : SimpleRequest(dc, module, name, contextualizeOptions(dc, loc, options)) { } QualifiedLookupRequest::QualifiedLookupRequest( const DeclContext *dc, SmallVector decls, DeclNameRef name, SourceLoc loc, NLOptions options ) : SimpleRequest(dc, std::move(decls), name, contextualizeOptions(dc, loc, options)) { } DirectLookupRequest::DirectLookupRequest(DirectLookupDescriptor descriptor, SourceLoc loc) : SimpleRequest(contextualizeOptions(descriptor, loc)) { } // Implement the clang importer type zone. #define SWIFT_TYPEID_ZONE ClangImporter #define SWIFT_TYPEID_HEADER "swift/ClangImporter/ClangImporterTypeIDZone.def" #include "swift/Basic/ImplementTypeIDZone.h" #undef SWIFT_TYPEID_ZONE #undef SWIFT_TYPEID_HEADER // Define request evaluation functions for each of the name lookup requests. static AbstractRequestFunction *nameLookupRequestFunctions[] = { #define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \ reinterpret_cast(&Name::evaluateRequest), #include "swift/AST/NameLookupTypeIDZone.def" #undef SWIFT_REQUEST }; void swift::registerNameLookupRequestFunctions(Evaluator &evaluator) { evaluator.registerRequestFunctions(Zone::NameLookup, nameLookupRequestFunctions); }