//===--- Module.cpp - Swift Language Module Implementation ----------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements the Module class and subclasses. // //===----------------------------------------------------------------------===// #include "swift/AST/Module.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/AccessScope.h" #include "swift/AST/Builtins.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ImportCache.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParseRequests.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ReferencedNameTracker.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Token.h" #include "swift/Strings.h" #include "swift/Syntax/SyntaxNodes.h" #include "clang/Basic/Module.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" using namespace swift; static_assert(IsTriviallyDestructible::value, "FileUnits are BumpPtrAllocated; the d'tor may not be called"); static_assert(IsTriviallyDestructible::value, "LoadedFiles are BumpPtrAllocated; the d'tor may not be called"); //===----------------------------------------------------------------------===// // Builtin Module Name lookup //===----------------------------------------------------------------------===// class BuiltinUnit::LookupCache { /// The cache of identifiers we've already looked up. We use a /// single hashtable for both types and values as a minor /// optimization; this prevents us from having both a builtin type /// and a builtin value with the same name, but that's okay. llvm::DenseMap Cache; public: void lookupValue(Identifier Name, NLKind LookupKind, const BuiltinUnit &M, SmallVectorImpl &Result); }; BuiltinUnit::LookupCache &BuiltinUnit::getCache() const { // FIXME: This leaks. Sticking this into ASTContext isn't enough because then // the DenseMap will leak. if (!Cache) const_cast(this)->Cache = std::make_unique(); return *Cache; } void BuiltinUnit::LookupCache::lookupValue( Identifier Name, NLKind LookupKind, const BuiltinUnit &M, SmallVectorImpl &Result) { // Only qualified lookup ever finds anything in the builtin module. if (LookupKind != NLKind::QualifiedLookup) return; ValueDecl *&Entry = Cache[Name]; ASTContext &Ctx = M.getParentModule()->getASTContext(); if (!Entry) { if (Type Ty = getBuiltinType(Ctx, Name.str())) { auto *TAD = new (Ctx) TypeAliasDecl(SourceLoc(), SourceLoc(), Name, SourceLoc(), /*genericparams*/nullptr, const_cast(&M)); TAD->setUnderlyingType(Ty); TAD->setAccess(AccessLevel::Public); Entry = TAD; } } if (!Entry) Entry = getBuiltinValueDecl(Ctx, Name); if (Entry) Result.push_back(Entry); } // Out-of-line because std::unique_ptr wants LookupCache to be complete. BuiltinUnit::BuiltinUnit(ModuleDecl &M) : FileUnit(FileUnitKind::Builtin, M) { M.getASTContext().addDestructorCleanup(*this); } //===----------------------------------------------------------------------===// // Normal Module Name Lookup //===----------------------------------------------------------------------===// SourceFile::~SourceFile() = default; /// A utility for caching global lookups into SourceFiles and modules of /// SourceFiles. This is used for lookup of top-level declarations, as well /// as operator lookup (which looks into types) and AnyObject dynamic lookup /// (which looks at all class members). class swift::SourceLookupCache { /// A lookup map for value decls. When declarations are added they are added /// under all variants of the name they can be found under. class DeclMap { llvm::DenseMap> Members; public: void add(ValueDecl *VD) { if (!VD->hasName()) return; VD->getFullName().addToLookupTable(Members, VD); } void clear() { Members.shrink_and_clear(); } decltype(Members)::const_iterator begin() const { return Members.begin(); } decltype(Members)::const_iterator end() const { return Members.end(); } decltype(Members)::const_iterator find(DeclName Name) const { return Members.find(Name); } }; DeclMap TopLevelValues; DeclMap ClassMembers; bool MemberCachePopulated = false; template void addToUnqualifiedLookupCache(Range decls, bool onlyOperators); template void addToMemberCache(Range decls); public: typedef ModuleDecl::AccessPathTy AccessPathTy; SourceLookupCache(const SourceFile &SF); SourceLookupCache(const ModuleDecl &Mod); /// Throw away as much memory as possible. void invalidate(); void lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result); void lookupVisibleDecls(AccessPathTy AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind); void populateMemberCache(const SourceFile &SF); void populateMemberCache(const ModuleDecl &Mod); void lookupClassMembers(AccessPathTy AccessPath, VisibleDeclConsumer &consumer); void lookupClassMember(AccessPathTy accessPath, DeclName name, SmallVectorImpl &results); SmallVector AllVisibleValues; }; SourceLookupCache &SourceFile::getCache() const { if (!Cache) { const_cast(this)->Cache = std::make_unique(*this); } return *Cache; } template void SourceLookupCache::addToUnqualifiedLookupCache(Range decls, bool onlyOperators) { for (Decl *D : decls) { if (auto *VD = dyn_cast(D)) { if (onlyOperators ? VD->isOperator() : VD->hasName()) { // Cache the value under both its compound name and its full name. TopLevelValues.add(VD); } } if (auto *NTD = dyn_cast(D)) if (!NTD->hasUnparsedMembers() || NTD->maybeHasOperatorDeclarations()) addToUnqualifiedLookupCache(NTD->getMembers(), true); if (auto *ED = dyn_cast(D)) { // Avoid populating the cache with the members of invalid extension // declarations. These members can be used to point validation inside of // a malformed context. if (ED->isInvalid()) continue; if (!ED->hasUnparsedMembers() || ED->maybeHasOperatorDeclarations()) addToUnqualifiedLookupCache(ED->getMembers(), true); } } } void SourceLookupCache::populateMemberCache(const SourceFile &SF) { if (MemberCachePopulated) return; FrontendStatsTracer tracer(SF.getASTContext().Stats, "populate-source-file-class-member-cache"); addToMemberCache(SF.getTopLevelDecls()); MemberCachePopulated = true; } void SourceLookupCache::populateMemberCache(const ModuleDecl &Mod) { if (MemberCachePopulated) return; FrontendStatsTracer tracer(Mod.getASTContext().Stats, "populate-module-class-member-cache"); for (const FileUnit *file : Mod.getFiles()) { auto &SF = *cast(file); addToMemberCache(SF.getTopLevelDecls()); } MemberCachePopulated = true; } template void SourceLookupCache::addToMemberCache(Range decls) { for (Decl *D : decls) { if (auto *NTD = dyn_cast(D)) { if (!NTD->hasUnparsedMembers() || NTD->maybeHasNestedClassDeclarations() || NTD->mayContainMembersAccessedByDynamicLookup()) addToMemberCache(NTD->getMembers()); } else if (auto *ED = dyn_cast(D)) { if (!ED->hasUnparsedMembers() || ED->maybeHasNestedClassDeclarations() || ED->mayContainMembersAccessedByDynamicLookup()) addToMemberCache(ED->getMembers()); } else if (auto *VD = dyn_cast(D)) { if (VD->canBeAccessedByDynamicLookup()) ClassMembers.add(VD); } } } /// Populate our cache on the first name lookup. SourceLookupCache::SourceLookupCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "source-file-populate-cache"); addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { FrontendStatsTracer tracer(M.getASTContext().Stats, "module-populate-cache"); for (const FileUnit *file : M.getFiles()) { auto &SF = *cast(file); addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } } void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result) { auto I = TopLevelValues.find(Name); if (I == TopLevelValues.end()) return; Result.reserve(I->second.size()); for (ValueDecl *Elt : I->second) Result.push_back(Elt); } void SourceLookupCache::lookupVisibleDecls(AccessPathTy AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind) { assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); if (!AccessPath.empty()) { auto I = TopLevelValues.find(AccessPath.front().Item); if (I == TopLevelValues.end()) return; for (auto vd : I->second) Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); return; } for (auto &tlv : TopLevelValues) { for (ValueDecl *vd : tlv.second) { // Declarations are added under their full and simple names. Skip the // entry for the simple name so that we report each declaration once. if (tlv.first.isSimpleName() && !vd->getFullName().isSimpleName()) continue; Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); } } } void SourceLookupCache::lookupClassMembers(AccessPathTy accessPath, VisibleDeclConsumer &consumer) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); if (!accessPath.empty()) { for (auto &member : ClassMembers) { // Non-simple names are also stored under their simple name, so make // sure to only report them once. if (!member.first.isSimpleName()) continue; for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); if (nominal && nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } } return; } for (auto &member : ClassMembers) { // Non-simple names are also stored under their simple name, so make sure to // only report them once. if (!member.first.isSimpleName()) continue; for (ValueDecl *vd : member.second) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } } void SourceLookupCache::lookupClassMember(AccessPathTy accessPath, DeclName name, SmallVectorImpl &results) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); auto iter = ClassMembers.find(name); if (iter == ClassMembers.end()) return; if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); if (nominal && nominal->getName() == accessPath.front().Item) results.push_back(vd); } return; } results.append(iter->second.begin(), iter->second.end()); } void SourceLookupCache::invalidate() { TopLevelValues.clear(); ClassMembers.clear(); MemberCachePopulated = false; // std::move AllVisibleValues into a temporary to destroy its contents. using SameSizeSmallVector = decltype(AllVisibleValues); (void)SameSizeSmallVector{std::move(AllVisibleValues)}; } //===----------------------------------------------------------------------===// // Module Implementation //===----------------------------------------------------------------------===// ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx) : DeclContext(DeclContextKind::Module, nullptr), TypeDecl(DeclKind::Module, &ctx, name, SourceLoc(), { }) { ctx.addDestructorCleanup(*this); setImplicit(); setInterfaceType(ModuleType::get(this)); setAccess(AccessLevel::Public); } bool ModuleDecl::isClangModule() const { return findUnderlyingClangModule() != nullptr; } void ModuleDecl::addFile(FileUnit &newFile) { // Require Main and REPL files to be the first file added. assert(Files.empty() || !isa(newFile) || cast(newFile).Kind == SourceFileKind::Library || cast(newFile).Kind == SourceFileKind::SIL); Files.push_back(&newFile); clearLookupCache(); } void ModuleDecl::removeFile(FileUnit &existingFile) { // Do a reverse search; usually the file to be deleted will be at the end. std::reverse_iterator I(Files.end()), E(Files.begin()); I = std::find(I, E, &existingFile); assert(I != E); // Adjust for the std::reverse_iterator offset. ++I; Files.erase(I.base()); clearLookupCache(); } #define FORWARD(name, args) \ for (const FileUnit *file : getFiles()) \ file->name args; SourceLookupCache &ModuleDecl::getSourceLookupCache() const { if (!Cache) { const_cast(this)->Cache = std::make_unique(*this); } return *Cache; } ModuleDecl *ModuleDecl::getTopLevelModule() { // If this is a Clang module, ask the Clang importer for the top-level module. // We need to check isNonSwiftModule() to ensure we don't look through // overlays. if (isNonSwiftModule()) { if (auto *underlying = findUnderlyingClangModule()) { auto &ctx = getASTContext(); auto *clangLoader = ctx.getClangModuleLoader(); return clangLoader->getWrapperForModule(underlying->getTopLevelModule()); } } // Swift modules don't currently support submodules. return this; } static bool isParsedModule(const ModuleDecl *mod) { // FIXME: If we ever get mixed modules that contain both SourceFiles and other // kinds of file units, this will break; there all callers of this function should // themselves assert that all file units in the module are SourceFiles when this // function returns true. auto files = mod->getFiles(); return (files.size() > 0 && isa(files[0]) && cast(files[0])->Kind != SourceFileKind::SIL); } void ModuleDecl::lookupValue(DeclName Name, NLKind LookupKind, SmallVectorImpl &Result) const { auto *stats = getASTContext().Stats; if (stats) stats->getFrontendCounters().NumModuleLookupValue++; if (isParsedModule(this)) { getSourceLookupCache().lookupValue(Name, LookupKind, Result); return; } FORWARD(lookupValue, (Name, LookupKind, Result)); } TypeDecl * ModuleDecl::lookupLocalType(StringRef MangledName) const { for (auto file : getFiles()) { auto TD = file->lookupLocalType(MangledName); if (TD) return TD; } return nullptr; } OpaqueTypeDecl * ModuleDecl::lookupOpaqueResultType(StringRef MangledName) { for (auto file : getFiles()) { auto OTD = file->lookupOpaqueResultType(MangledName); if (OTD) return OTD; } return nullptr; } void ModuleDecl::lookupMember(SmallVectorImpl &results, DeclContext *container, DeclName name, Identifier privateDiscriminator) const { size_t oldSize = results.size(); bool alreadyInPrivateContext = false; auto containerDecl = container->getAsDecl(); // If FileUnit, then use FileUnit::lookupValue instead. assert(containerDecl != nullptr && "This context does not support lookup."); if (auto nominal = dyn_cast(containerDecl)) { auto lookupResults = nominal->lookupDirect(name); // Filter out declarations from other modules. llvm::copy_if(lookupResults, std::back_inserter(results), [this](const ValueDecl *VD) -> bool { return VD->getModuleContext() == this; }); auto AS = nominal->getFormalAccessScope(); if (AS.isPrivate() || AS.isFileScope()) alreadyInPrivateContext = true; } else if (isa(containerDecl)) { assert(container == this); this->lookupValue(name, NLKind::QualifiedLookup, results); } else if (!isa(containerDecl)) { // If ExtensionDecl, then use ExtensionDecl::lookupDirect instead. llvm_unreachable("This context does not support lookup."); } // Filter by private-discriminator, or filter out private decls if there isn't // one...unless we're already in a private context, in which case everything // is private and a discriminator is unnecessary. if (alreadyInPrivateContext) { assert(privateDiscriminator.empty() && "unnecessary private discriminator"); // Don't remove anything; everything here is private anyway. } else if (privateDiscriminator.empty()) { auto newEnd = std::remove_if(results.begin()+oldSize, results.end(), [](const ValueDecl *VD) -> bool { return VD->getFormalAccess() <= AccessLevel::FilePrivate; }); results.erase(newEnd, results.end()); } else { auto newEnd = std::remove_if(results.begin()+oldSize, results.end(), [=](const ValueDecl *VD) -> bool { if (VD->getFormalAccess() > AccessLevel::FilePrivate) return true; auto enclosingFile = cast(VD->getDeclContext()->getModuleScopeContext()); auto discriminator = enclosingFile->getDiscriminatorForPrivateValue(VD); return discriminator != privateDiscriminator; }); results.erase(newEnd, results.end()); } } void ModuleDecl::lookupObjCMethods( ObjCSelector selector, SmallVectorImpl &results) const { FORWARD(lookupObjCMethods, (selector, results)); } void ModuleDecl::lookupImportedSPIGroups(const ModuleDecl *importedModule, SmallVectorImpl &spiGroups) const { FORWARD(lookupImportedSPIGroups, (importedModule, spiGroups)); } void BuiltinUnit::lookupValue(DeclName name, NLKind lookupKind, SmallVectorImpl &result) const { getCache().lookupValue(name.getBaseIdentifier(), lookupKind, *this, result); } void BuiltinUnit::lookupObjCMethods( ObjCSelector selector, SmallVectorImpl &results) const { // No @objc methods in the Builtin module. } void SourceFile::lookupValue(DeclName name, NLKind lookupKind, SmallVectorImpl &result) const { getCache().lookupValue(name, lookupKind, result); } void ModuleDecl::lookupVisibleDecls(AccessPathTy AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind) const { if (isParsedModule(this)) return getSourceLookupCache().lookupVisibleDecls( AccessPath, Consumer, LookupKind); FORWARD(lookupVisibleDecls, (AccessPath, Consumer, LookupKind)); } void SourceFile::lookupVisibleDecls(ModuleDecl::AccessPathTy AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind) const { getCache().lookupVisibleDecls(AccessPath, Consumer, LookupKind); } void ModuleDecl::lookupClassMembers(AccessPathTy accessPath, VisibleDeclConsumer &consumer) const { if (isParsedModule(this)) { auto &cache = getSourceLookupCache(); cache.populateMemberCache(*this); cache.lookupClassMembers(accessPath, consumer); return; } FORWARD(lookupClassMembers, (accessPath, consumer)); } void SourceFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, VisibleDeclConsumer &consumer) const { auto &cache = getCache(); cache.populateMemberCache(*this); cache.lookupClassMembers(accessPath, consumer); } void ModuleDecl::lookupClassMember(AccessPathTy accessPath, DeclName name, SmallVectorImpl &results) const { auto *stats = getASTContext().Stats; if (stats) stats->getFrontendCounters().NumModuleLookupClassMember++; if (isParsedModule(this)) { FrontendStatsTracer tracer(getASTContext().Stats, "source-file-lookup-class-member"); auto &cache = getSourceLookupCache(); cache.populateMemberCache(*this); cache.lookupClassMember(accessPath, name, results); return; } FORWARD(lookupClassMember, (accessPath, name, results)); } void SourceFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, DeclName name, SmallVectorImpl &results) const { FrontendStatsTracer tracer(getASTContext().Stats, "source-file-lookup-class-member"); auto &cache = getCache(); cache.populateMemberCache(*this); cache.lookupClassMember(accessPath, name, results); } void SourceFile::lookupObjCMethods( ObjCSelector selector, SmallVectorImpl &results) const { // FIXME: Make sure this table is complete, somehow. auto known = ObjCMethods.find(selector); if (known == ObjCMethods.end()) return; results.append(known->second.begin(), known->second.end()); } void ModuleDecl::getLocalTypeDecls(SmallVectorImpl &Results) const { FORWARD(getLocalTypeDecls, (Results)); } void ModuleDecl::getTopLevelDecls(SmallVectorImpl &Results) const { FORWARD(getTopLevelDecls, (Results)); } void ModuleDecl::getTopLevelDeclsWhereAttributesMatch( SmallVectorImpl &Results, llvm::function_ref matchAttributes) const { FORWARD(getTopLevelDeclsWhereAttributesMatch, (Results, matchAttributes)); } void SourceFile::getTopLevelDecls(SmallVectorImpl &Results) const { auto decls = getTopLevelDecls(); Results.append(decls.begin(), decls.end()); } void ModuleDecl::getPrecedenceGroups( SmallVectorImpl &Results) const { FORWARD(getPrecedenceGroups, (Results)); } void SourceFile::getPrecedenceGroups( SmallVectorImpl &Results) const { for (auto pair : PrecedenceGroups) { if (pair.second.getPointer() && pair.second.getInt()) { Results.push_back(pair.second.getPointer()); } } } void SourceFile::getLocalTypeDecls(SmallVectorImpl &Results) const { Results.append(LocalTypeDecls.begin(), LocalTypeDecls.end()); } void SourceFile::getOpaqueReturnTypeDecls(SmallVectorImpl &Results) const { auto result = const_cast(this)->getOpaqueReturnTypeDecls(); llvm::copy(result, std::back_inserter(Results)); } TypeDecl *SourceFile::lookupLocalType(llvm::StringRef mangledName) const { ASTContext &ctx = getASTContext(); for (auto typeDecl : LocalTypeDecls) { auto typeMangledName = evaluateOrDefault(ctx.evaluator, MangleLocalTypeDeclRequest { typeDecl }, std::string()); if (mangledName == typeMangledName) return typeDecl; } return nullptr; } Optional SourceFile::getBasicLocsForDecl(const Decl *D) const { auto *FileCtx = D->getDeclContext()->getModuleScopeContext(); assert(FileCtx == this && "D doesn't belong to this source file"); if (FileCtx != this) { // D doesn't belong to this file. This shouldn't happen in practice. return None; } if (D->getLoc().isInvalid()) return None; SourceManager &SM = getASTContext().SourceMgr; BasicDeclLocs Result; Result.SourceFilePath = SM.getDisplayNameForLoc(D->getLoc()); for (const auto &SRC : D->getRawComment().Comments) { Result.DocRanges.push_back(std::make_pair( LineColumn { SRC.StartLine, SRC.StartColumn }, SRC.Range.getByteLength()) ); } auto setLineColumn = [&SM](LineColumn &Home, SourceLoc Loc) { if (Loc.isValid()) { std::tie(Home.Line, Home.Column) = SM.getLineAndColumn(Loc); } }; #define SET(X) setLineColumn(Result.X, D->get##X()); SET(Loc) SET(StartLoc) SET(EndLoc) #undef SET return Result; } void ModuleDecl::getDisplayDecls(SmallVectorImpl &Results) const { // FIXME: Should this do extra access control filtering? FORWARD(getDisplayDecls, (Results)); } ProtocolConformanceRef ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { ASTContext &ctx = getASTContext(); assert(type->isExistentialType()); // 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()) return ProtocolConformanceRef::forInvalid(); auto layout = type->getExistentialLayout(); // Due to an IRGen limitation, witness tables cannot be passed from an // existential to an archetype parameter, so for now we restrict this to // @objc protocols. if (!layout.isObjC()) { // There's a specific exception for protocols with self-conforming // witness tables, but the existential has to be *exactly* that type. // TODO: synthesize witness tables on-demand for protocol compositions // that can satisfy the requirement. if (protocol->requiresSelfConformanceWitnessTable() && type->is() && type->castTo()->getDecl() == protocol) return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); return ProtocolConformanceRef::forInvalid(); } // If the existential is class-constrained, the class might conform // concretely. if (auto superclass = layout.explicitSuperclass) { if (auto result = lookupConformance(superclass, protocol)) return result; } // Otherwise, the existential might conform abstractly. for (auto proto : layout.getProtocols()) { auto *protoDecl = proto->getDecl(); // If we found the protocol we're looking for, return an abstract // conformance to it. if (protoDecl == protocol) return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); // If the protocol has a superclass constraint, we might conform // concretely. if (auto superclass = protoDecl->getSuperclass()) { if (auto result = lookupConformance(superclass, protocol)) return result; } // Now check refined protocols. if (protoDecl->inheritsFrom(protocol)) return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); } // We didn't find our protocol in the existential's list; it doesn't // conform. return ProtocolConformanceRef::forInvalid(); } ProtocolConformanceRef ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { ASTContext &ctx = getASTContext(); // A dynamic Self type conforms to whatever its underlying type // conforms to. if (auto selfType = type->getAs()) type = selfType->getSelfType(); // An archetype conforms to a protocol if the protocol is listed in the // archetype's list of conformances, or if the archetype has a superclass // constraint and the superclass conforms to the protocol. if (auto archetype = type->getAs()) { // The generic signature builder drops conformance requirements that are made // redundant by a superclass requirement, so check for a concrete // conformance first, since an abstract conformance might not be // able to be resolved by a substitution that makes the archetype // concrete. if (auto super = archetype->getSuperclass()) { if (auto inheritedConformance = lookupConformance(super, protocol)) { return ProtocolConformanceRef(ctx.getInheritedConformance( type, inheritedConformance.getConcrete())); } } for (auto ap : archetype->getConformsTo()) { if (ap == protocol || ap->inheritsFrom(protocol)) return ProtocolConformanceRef(protocol); } return ProtocolConformanceRef::forInvalid(); } // An existential conforms to a protocol if the protocol is listed in the // existential's list of conformances and the existential conforms to // itself. if (type->isExistentialType()) return lookupExistentialConformance(type, protocol); // Type variables have trivial conformances. if (type->isTypeVariableOrMember()) return ProtocolConformanceRef(protocol); // UnresolvedType is a placeholder for an unknown type used when generating // diagnostics. We consider it to conform to all protocols, since the // intended type might have. if (type->is()) return ProtocolConformanceRef(protocol); auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. if (!nominal || isa(nominal)) return ProtocolConformanceRef::forInvalid(); // Find the (unspecialized) conformance. SmallVector conformances; if (!nominal->lookupConformance(this, protocol, conformances)) return ProtocolConformanceRef::forInvalid(); // FIXME: Ambiguity resolution. auto conformance = conformances.front(); // Rebuild inherited conformances based on the root normal conformance. // FIXME: This is a hack to work around our inability to handle multiple // levels of substitution through inherited conformances elsewhere in the // compiler. if (auto inherited = dyn_cast(conformance)) { // Dig out the conforming nominal type. auto rootConformance = inherited->getRootConformance(); auto conformingClass = rootConformance->getType()->getClassOrBoundGenericClass(); // Map up to our superclass's type. auto superclassTy = type->getSuperclassForDecl(conformingClass); // Compute the conformance for the inherited type. auto inheritedConformance = lookupConformance(superclassTy, protocol); assert(inheritedConformance && "We already found the inherited conformance"); // Create the inherited conformance entry. conformance = ctx.getInheritedConformance(type, inheritedConformance.getConcrete()); return ProtocolConformanceRef(conformance); } // If the type is specialized, find the conformance for the generic type. if (type->isSpecialized()) { // Figure out the type that's explicitly conforming to this protocol. Type explicitConformanceType = conformance->getType(); DeclContext *explicitConformanceDC = conformance->getDeclContext(); // If the explicit conformance is associated with a type that is different // from the type we're checking, retrieve generic conformance. if (!explicitConformanceType->isEqual(type)) { // Gather the substitutions we need to map the generic conformance to // the specialized conformance. auto subMap = type->getContextSubstitutionMap(this, explicitConformanceDC); // Create the specialized conformance entry. auto result = ctx.getSpecializedConformance(type, conformance, subMap); return ProtocolConformanceRef(result); } } // Record and return the simple conformance. return ProtocolConformanceRef(conformance); } namespace { template using OperatorMap = SourceFile::OperatorMap; template struct OperatorLookup { // Don't fold this into the static_assert: this would trigger an MSVC bug // that causes the assertion to fail. static constexpr T* ptr = static_cast(nullptr); static_assert(ptr, "Only usable with operators"); }; template <> struct OperatorLookup { constexpr static auto map_ptr = &SourceFile::PrefixOperators; static PrefixOperatorDecl *lookup(Evaluator &eval, const OperatorLookupDescriptor &desc) { // We can return the first prefix operator. All prefix operators of the // same name are equivalent. DirectOperatorLookupRequest req{desc, OperatorFixity::Prefix}; auto results = evaluateOrDefault(eval, req, {}); return results.empty() ? nullptr : cast(results[0]); } }; template <> struct OperatorLookup { constexpr static auto map_ptr = &SourceFile::InfixOperators; static InfixOperatorDecl *lookup(Evaluator &eval, const OperatorLookupDescriptor &desc) { // Return the first result if it exists. DirectOperatorLookupRequest req{desc, OperatorFixity::Infix}; auto results = evaluateOrDefault(eval, req, {}); return results.empty() ? nullptr : cast(results[0]); } }; template <> struct OperatorLookup { constexpr static auto map_ptr = &SourceFile::PostfixOperators; static PostfixOperatorDecl *lookup(Evaluator &eval, const OperatorLookupDescriptor &desc) { // We can return the first postfix operator. All postfix operators of the // same name are equivalent. DirectOperatorLookupRequest req{desc, OperatorFixity::Postfix}; auto results = evaluateOrDefault(eval, req, {}); return results.empty() ? nullptr : cast(results[0]); } }; template <> struct OperatorLookup { constexpr static auto map_ptr = &SourceFile::PrecedenceGroups; static PrecedenceGroupDecl *lookup(Evaluator &eval, const OperatorLookupDescriptor &desc) { // Return the first result if it exists. auto results = evaluateOrDefault(eval, DirectPrecedenceGroupLookupRequest{desc}, {}); return results.empty() ? nullptr : results[0]; } }; } // end anonymous namespace /// A helper class to sneak around C++ access control rules. class SourceFile::Impl { public: /// Only intended for use by lookupOperatorDeclForName. static ArrayRef getImportsForSourceFile(const SourceFile &SF) { return SF.Imports; } }; struct SourceFile::SourceFileSyntaxInfo { const bool Enable; /// The root of the syntax tree representing the source file. Optional SyntaxRoot; SourceFileSyntaxInfo(bool Enable): Enable(Enable) {} }; bool SourceFile::hasSyntaxRoot() const { return SyntaxInfo->SyntaxRoot.hasValue(); } syntax::SourceFileSyntax SourceFile::getSyntaxRoot() const { assert(hasSyntaxRoot() && "no syntax root is set."); return *SyntaxInfo->SyntaxRoot; } void SourceFile::setSyntaxRoot(syntax::SourceFileSyntax &&Root) { SyntaxInfo->SyntaxRoot.emplace(Root); } template static Optional lookupOperatorDeclForName(ModuleDecl *M, SourceLoc Loc, Identifier Name, bool isCascading); template using ImportedOperatorsMap = llvm::SmallDenseMap; template static typename ImportedOperatorsMap::iterator checkOperatorConflicts(const SourceFile &SF, SourceLoc loc, ImportedOperatorsMap &importedOperators) { // Check for conflicts. auto i = importedOperators.begin(), end = importedOperators.end(); auto start = i; for (++i; i != end; ++i) { if (i->first->conflictsWith(start->first)) { if (loc.isValid()) { ASTContext &C = SF.getASTContext(); C.Diags.diagnose(loc, diag::ambiguous_operator_decls); start->first->diagnose(diag::found_this_operator_decl); i->first->diagnose(diag::found_this_operator_decl); } return end; } } return start; } template<> typename ImportedOperatorsMap::iterator checkOperatorConflicts(const SourceFile &SF, SourceLoc loc, ImportedOperatorsMap &importedGroups) { if (importedGroups.size() == 1) return importedGroups.begin(); // Any sort of ambiguity is an error. if (loc.isValid()) { ASTContext &C = SF.getASTContext(); C.Diags.diagnose(loc, diag::ambiguous_precedence_groups); for (auto &entry : importedGroups) { entry.first->diagnose(diag::found_this_precedence_group); } } return importedGroups.end(); } // Returns None on error, Optional(nullptr) if no operator decl found, or // Optional(decl) if decl was found. template static Optional lookupOperatorDeclForName(const FileUnit &File, SourceLoc Loc, Identifier Name, bool includePrivate, bool isCascading) { switch (File.getKind()) { case FileUnitKind::Builtin: // The Builtin module declares no operators. return nullptr; case FileUnitKind::Source: break; case FileUnitKind::SerializedAST: case FileUnitKind::ClangModule: case FileUnitKind::DWARFModule: { auto &eval = File.getASTContext().evaluator; auto desc = OperatorLookupDescriptor::forFile(const_cast(&File), Name, isCascading, /*diagLoc*/ SourceLoc()); return OperatorLookup::lookup(eval, desc); } } auto &SF = cast(File); assert(SF.ASTStage >= SourceFile::NameBound); // Look for an operator declaration in the current module. const auto OP_MAP = OperatorLookup::map_ptr; auto found = (SF.*OP_MAP).find(Name); if (found != (SF.*OP_MAP).end() && (includePrivate || found->second.getInt())) return found->second.getPointer(); // Look for imported operator decls. // Record whether they come from re-exported modules. // FIXME: We ought to prefer operators elsewhere in this module before we // check imports. auto ownModule = SF.getParentModule(); ImportedOperatorsMap importedOperators; for (auto &imported : SourceFile::Impl::getImportsForSourceFile(SF)) { // Protect against source files that contrive to import their own modules. if (imported.module.second == ownModule) continue; bool isExported = imported.importOptions.contains(SourceFile::ImportFlags::Exported); if (!includePrivate && !isExported) continue; Optional maybeOp = lookupOperatorDeclForName(imported.module.second, Loc, Name, isCascading); if (!maybeOp) return None; if (OP_DECL *op = *maybeOp) importedOperators[op] |= isExported; } typename OperatorMap::mapped_type result = { nullptr, true }; if (!importedOperators.empty()) { auto start = checkOperatorConflicts(SF, Loc, importedOperators); if (start == importedOperators.end()) return None; result = { start->first, start->second }; } if (includePrivate) { // Cache the mapping so we don't need to troll imports next time. // It's not safe to cache the non-private results because we didn't search // private imports there, but in most non-private cases the result will // be cached in the final lookup. auto &mutableOpMap = const_cast &>(SF.*OP_MAP); mutableOpMap[Name] = result; } if (includePrivate || result.getInt()) return result.getPointer(); return nullptr; } template static Optional lookupOperatorDeclForName(ModuleDecl *M, SourceLoc Loc, Identifier Name, bool isCascading) { OP_DECL *result = nullptr; for (const FileUnit *File : M->getFiles()) { auto next = lookupOperatorDeclForName(*File, Loc, Name, false, isCascading); if (!next.hasValue()) return next; // FIXME: Diagnose ambiguity. if (*next && result) return None; if (*next) result = *next; } return result; } template llvm::Expected LookupOperatorRequest::evaluate( Evaluator &evaluator, OperatorLookupDescriptor desc) const { auto *file = desc.fileOrModule.get(); auto result = lookupOperatorDeclForName(*file, desc.diagLoc, desc.name, /*includePrivate*/ true, desc.isCascading); if (!result.hasValue()) return nullptr; if (!result.getValue() || result.getValue()->getDeclContext()->getModuleScopeContext() != file) { namelookup::recordLookupOfTopLevelName(file, desc.name, desc.isCascading); } if (!result.getValue()) { result = lookupOperatorDeclForName(file->getParentModule(), desc.diagLoc, desc.name, desc.isCascading); } return result.hasValue() ? result.getValue() : nullptr; } #define LOOKUP_OPERATOR(Kind) \ Kind##Decl *ModuleDecl::lookup##Kind(Identifier name, SourceLoc loc) { \ auto result = \ lookupOperatorDeclForName(this, loc, name, \ /*isCascading*/ false); \ return result ? *result : nullptr; \ } \ template llvm::Expected \ LookupOperatorRequest::evaluate(Evaluator &e, \ OperatorLookupDescriptor d) const; LOOKUP_OPERATOR(PrefixOperator) LOOKUP_OPERATOR(InfixOperator) LOOKUP_OPERATOR(PostfixOperator) LOOKUP_OPERATOR(PrecedenceGroup) #undef LOOKUP_OPERATOR llvm::Expected> DirectOperatorLookupRequest::evaluate(Evaluator &evaluator, OperatorLookupDescriptor descriptor, OperatorFixity fixity) const { // Query each file. // TODO: Module-level caching. TinyPtrVector results; for (auto *file : descriptor.getFiles()) file->lookupOperatorDirect(descriptor.name, fixity, results); return std::move(results); } void SourceFile::lookupOperatorDirect( Identifier name, OperatorFixity fixity, TinyPtrVector &results) const { OperatorDecl *op = nullptr; switch (fixity) { case OperatorFixity::Infix: { auto result = InfixOperators.find(name); if (result != InfixOperators.end()) op = result->second.getPointer(); break; } case OperatorFixity::Postfix: { auto result = PostfixOperators.find(name); if (result != PostfixOperators.end()) op = result->second.getPointer(); break; } case OperatorFixity::Prefix: { auto result = PrefixOperators.find(name); if (result != PrefixOperators.end()) op = result->second.getPointer(); break; } } // We currently can use the operator maps to cache lookup results from other // modules. Make sure we only return results from the source file. if (op && op->getDeclContext()->getParentSourceFile() == this) results.push_back(op); } llvm::Expected> DirectPrecedenceGroupLookupRequest::evaluate( Evaluator &evaluator, OperatorLookupDescriptor descriptor) const { // Query each file. // TODO: Module-level caching. TinyPtrVector results; for (auto *file : descriptor.getFiles()) file->lookupPrecedenceGroupDirect(descriptor.name, results); return std::move(results); } void SourceFile::lookupPrecedenceGroupDirect( Identifier name, TinyPtrVector &results) const { auto result = PrecedenceGroups.find(name); if (result == PrecedenceGroups.end()) return; // We currently can use the operator maps to cache lookup results from other // modules. Make sure we only return results from the source file. auto *group = result->second.getPointer(); if (group->getDeclContext()->getParentSourceFile() == this) results.push_back(group); } void ModuleDecl::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { FORWARD(getImportedModules, (modules, filter)); } void SourceFile::getImportedModules(SmallVectorImpl &modules, ModuleDecl::ImportFilter filter) const { // FIXME: Ideally we should assert that the file has been name bound before // calling this function. However unfortunately that can cause issues for // overlays which can depend on a Clang submodule for the underlying framework // they are overlaying, which causes us to attempt to load the overlay again. // We need to find a way to ensure that an overlay dependency with the same // name as the overlay always loads the underlying Clang module. We currently // handle this for a direct import from the overlay, but not when it happens // through other imports. assert(filter && "no imports requested?"); for (auto desc : Imports) { ModuleDecl::ImportFilter requiredFilter; if (desc.importOptions.contains(ImportFlags::Exported)) requiredFilter |= ModuleDecl::ImportFilterKind::Public; else if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; else if (desc.importOptions.contains(ImportFlags::SPIAccessControl)) requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl; else requiredFilter |= ModuleDecl::ImportFilterKind::Private; if (!separatelyImportedOverlays.lookup(desc.module.second).empty()) requiredFilter |= ModuleDecl::ImportFilterKind::ShadowedBySeparateOverlay; if (filter.contains(requiredFilter)) modules.push_back(desc.module); } } void ModuleDecl::getImportedModulesForLookup( SmallVectorImpl &modules) const { FORWARD(getImportedModulesForLookup, (modules)); } bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) { using AccessPathElem = Located; if (lhs.size() != rhs.size()) return false; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { return lElem.Item == rElem.Item; }); } ModuleDecl::ReverseFullNameIterator::ReverseFullNameIterator( const ModuleDecl *M) { assert(M); // Note: This will look through overlays as well, but that's fine for name // generation purposes. The point of an overlay is to if (auto *clangModule = M->findUnderlyingClangModule()) current = clangModule; else current = M; } StringRef ModuleDecl::ReverseFullNameIterator::operator*() const { assert(current && "all name components exhausted"); if (auto *swiftModule = current.dyn_cast()) return swiftModule->getName().str(); auto *clangModule = static_cast(current.get()); return clangModule->Name; } ModuleDecl::ReverseFullNameIterator & ModuleDecl::ReverseFullNameIterator::operator++() { if (!current) return *this; if (current.is()) { current = nullptr; return *this; } auto *clangModule = static_cast(current.get()); if (clangModule->Parent) current = clangModule->Parent; else current = nullptr; return *this; } void ModuleDecl::ReverseFullNameIterator::printForward(raw_ostream &out, StringRef delim) const { SmallVector elements(*this, {}); swift::interleave(llvm::reverse(elements), [&out](StringRef next) { out << next; }, [&out, delim] { out << delim; }); } void ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { std::sort(imports.begin(), imports.end(), [](const ImportedModule &lhs, const ImportedModule &rhs) -> bool { // Arbitrarily sort by name to get a deterministic order. if (lhs.second != rhs.second) { return std::lexicographical_compare( lhs.second->getReverseFullModuleName(), {}, rhs.second->getReverseFullModuleName(), {}); } using AccessPathElem = Located; return std::lexicographical_compare(lhs.first.begin(), lhs.first.end(), rhs.first.begin(), rhs.first.end(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { return lElem.Item.str() < rElem.Item.str(); }); }); auto last = std::unique(imports.begin(), imports.end(), [](const ImportedModule &lhs, const ImportedModule &rhs) -> bool { if (lhs.second != rhs.second) return false; return ModuleDecl::isSameAccessPath(lhs.first, rhs.first); }); imports.erase(last, imports.end()); } StringRef ModuleDecl::getModuleFilename() const { // FIXME: Audit uses of this function and figure out how to migrate them to // per-file names. Modules can consist of more than one file. StringRef Result; for (auto F : getFiles()) { if (auto SF = dyn_cast(F)) { if (!Result.empty()) return StringRef(); Result = SF->getFilename(); continue; } if (auto LF = dyn_cast(F)) { if (!Result.empty()) return StringRef(); Result = LF->getFilename(); continue; } return StringRef(); } return Result; } bool ModuleDecl::isStdlibModule() const { return !getParent() && getName() == getASTContext().StdlibModuleName; } bool ModuleDecl::isSwiftShimsModule() const { return !getParent() && getName() == getASTContext().SwiftShimsModuleName; } bool ModuleDecl::isOnoneSupportModule() const { return !getParent() && getName().str() == SWIFT_ONONE_SUPPORT; } bool ModuleDecl::isBuiltinModule() const { return this == getASTContext().TheBuiltinModule; } bool SourceFile::registerMainClass(ClassDecl *mainClass, SourceLoc diagLoc) { if (mainClass == MainClass) return false; ArtificialMainKind kind = mainClass->getArtificialMainKind(); if (getParentModule()->registerEntryPointFile(this, diagLoc, kind)) return true; MainClass = mainClass; MainClassDiagLoc = diagLoc; return false; } bool ModuleDecl::registerEntryPointFile(FileUnit *file, SourceLoc diagLoc, Optional kind) { if (!EntryPointInfo.hasEntryPoint()) { EntryPointInfo.setEntryPointFile(file); return false; } if (diagLoc.isInvalid()) return true; assert(kind.hasValue() && "multiple entry points without attributes"); // %select indices for UI/NSApplication-related diagnostics. enum : unsigned { UIApplicationMainClass = 0, NSApplicationMainClass = 1, } mainClassDiagKind; switch (kind.getValue()) { case ArtificialMainKind::UIApplicationMain: mainClassDiagKind = UIApplicationMainClass; break; case ArtificialMainKind::NSApplicationMain: mainClassDiagKind = NSApplicationMainClass; break; } FileUnit *existingFile = EntryPointInfo.getEntryPointFile(); const ClassDecl *existingClass = existingFile->getMainClass(); SourceLoc existingDiagLoc; if (auto *sourceFile = dyn_cast(existingFile)) { if (existingClass) { existingDiagLoc = sourceFile->getMainClassDiagLoc(); } else { if (auto bufID = sourceFile->getBufferID()) existingDiagLoc = getASTContext().SourceMgr.getLocForBufferStart(*bufID); } } if (existingClass) { if (EntryPointInfo.markDiagnosedMultipleMainClasses()) { // If we already have a main class, and we haven't diagnosed it, // do so now. if (existingDiagLoc.isValid()) { getASTContext().Diags.diagnose(existingDiagLoc, diag::attr_ApplicationMain_multiple, mainClassDiagKind); } else { getASTContext().Diags.diagnose(existingClass, diag::attr_ApplicationMain_multiple, mainClassDiagKind); } } // Always diagnose the new class. getASTContext().Diags.diagnose(diagLoc, diag::attr_ApplicationMain_multiple, mainClassDiagKind); } else { // We don't have an existing class, but we /do/ have a file in script mode. // Diagnose that. if (EntryPointInfo.markDiagnosedMainClassWithScript()) { getASTContext().Diags.diagnose(diagLoc, diag::attr_ApplicationMain_with_script, mainClassDiagKind); if (existingDiagLoc.isValid()) { getASTContext().Diags.diagnose(existingDiagLoc, diag::attr_ApplicationMain_script_here); } } } return true; } void ModuleDecl::collectLinkLibraries(LinkLibraryCallback callback) const { // FIXME: The proper way to do this depends on the decls used. FORWARD(collectLinkLibraries, (callback)); } void SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { llvm::SmallDenseSet visited; SmallVector stack; ModuleDecl::ImportFilter filter = ModuleDecl::ImportFilterKind::Public; filter |= ModuleDecl::ImportFilterKind::Private; filter |= ModuleDecl::ImportFilterKind::SPIAccessControl; auto *topLevel = getParentModule(); ModuleDecl::ImportFilter topLevelFilter = filter; topLevelFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; topLevel->getImportedModules(stack, topLevelFilter); // Make sure the top-level module is first; we want pre-order-ish traversal. stack.emplace_back(ModuleDecl::AccessPathTy(), const_cast(topLevel)); while (!stack.empty()) { auto next = stack.pop_back_val().second; if (!visited.insert(next).second) continue; if (next->getName() != getParentModule()->getName()) { // Hack: Assume other REPL files already have their libraries linked. if (!next->getFiles().empty()) if (auto *nextSource = dyn_cast(next->getFiles().front())) if (nextSource->Kind == SourceFileKind::REPL) continue; next->collectLinkLibraries(callback); } next->getImportedModules(stack, filter); } } bool ModuleDecl::walk(ASTWalker &Walker) { llvm::SaveAndRestore SAR(Walker.Parent, this); for (auto SF : getFiles()) if (SF->walk(Walker)) return true; return false; } const clang::Module *ModuleDecl::findUnderlyingClangModule() const { for (auto *FU : getFiles()) { if (auto *Mod = FU->getUnderlyingClangModule()) return Mod; } return nullptr; } //===----------------------------------------------------------------------===// // Cross-Import Overlays //===----------------------------------------------------------------------===// namespace swift { /// Represents a file containing information about cross-module overlays. class OverlayFile { /// The file that data should be loaded from. StringRef filePath; /// The list of module names; empty if loading failed. llvm::TinyPtrVector overlayModuleNames; enum class State { Pending, Loaded, Failed }; State state = State::Pending; /// Actually loads the overlay module name list. This should mutate /// \c overlayModuleNames, but not \c filePath. /// /// \returns \c true on success, \c false on failure. Diagnoses any failures /// before returning. bool loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc, Identifier bystandingModule); public: // Only allocate in ASTContexts. void *operator new(size_t bytes) = delete; void *operator new(size_t bytes, const ASTContext &ctx, unsigned alignment = alignof(OverlayFile)) { return ctx.Allocate(bytes, alignment); } OverlayFile(StringRef filePath) : filePath(filePath) { assert(!filePath.empty()); } /// Returns the list of additional modules that should be imported if both /// the primary and secondary modules have been imported. This may load a /// file; if so, it will diagnose any errors itself and arrange for the file /// to not be loaded again. /// /// The result can be empty, either because of an error or because the file /// didn't contain any overlay module names. ArrayRef getOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc, Identifier bystandingModule) { if (state == State::Pending) { state = loadOverlayModuleNames(M, diagLoc, bystandingModule) ? State::Loaded : State::Failed; } return overlayModuleNames; } }; } void ModuleDecl::addCrossImportOverlayFile(StringRef file) { auto &ctx = getASTContext(); Identifier secondaryModule = ctx.getIdentifier(llvm::sys::path::stem(file)); declaredCrossImports[secondaryModule] .push_back(new (ctx) OverlayFile(ctx.AllocateCopy(file))); } bool ModuleDecl::mightDeclareCrossImportOverlays() const { return !declaredCrossImports.empty(); } void ModuleDecl:: findDeclaredCrossImportOverlays(Identifier bystanderName, SmallVectorImpl &overlayNames, SourceLoc diagLoc) const { if (getName() == bystanderName) // We don't currently support self-cross-imports. return; for (auto &crossImportFile : declaredCrossImports.lookup(bystanderName)) llvm::copy(crossImportFile->getOverlayModuleNames(this, diagLoc, bystanderName), std::back_inserter(overlayNames)); } void ModuleDecl::getDeclaredCrossImportBystanders( SmallVectorImpl &otherModules) { for (auto &pair : declaredCrossImports) otherModules.push_back(std::get<0>(pair)); } namespace { struct OverlayFileContents { struct Module { std::string name; }; unsigned version; std::vector modules; static llvm::ErrorOr load(std::unique_ptr input, SmallVectorImpl &errorMessages); }; } // end anonymous namespace namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, OverlayFileContents::Module &module) { io.mapRequired("name", module.name); } }; template <> struct SequenceElementTraits { static const bool flow = false; }; template <> struct MappingTraits { static void mapping(IO &io, OverlayFileContents &contents) { io.mapRequired("version", contents.version); io.mapRequired("modules", contents.modules); } }; } } // end namespace 'llvm' static void pushYAMLError(const llvm::SMDiagnostic &diag, void *Context) { auto &errorMessages = *static_cast *>(Context); errorMessages.emplace_back(diag.getMessage()); } llvm::ErrorOr OverlayFileContents::load(std::unique_ptr input, SmallVectorImpl &errorMessages) { llvm::yaml::Input yamlInput(input->getBuffer(), /*Ctxt=*/nullptr, pushYAMLError, &errorMessages); OverlayFileContents contents; yamlInput >> contents; if (auto error = yamlInput.error()) return error; if (contents.version > 1) { auto message = Twine("key 'version' has invalid value: ") + Twine(contents.version); errorMessages.push_back(message.str()); return make_error_code(std::errc::result_out_of_range); } return contents; } bool OverlayFile::loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc, Identifier bystanderName) { auto &ctx = M->getASTContext(); llvm::vfs::FileSystem &fs = *ctx.SourceMgr.getFileSystem(); auto bufOrError = fs.getBufferForFile(filePath); if (!bufOrError) { ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file, M->getName(), bystanderName, bufOrError.getError().message(), filePath); return false; } SmallVector errorMessages; auto contentsOrErr = OverlayFileContents::load(std::move(bufOrError.get()), errorMessages); if (!contentsOrErr) { if (errorMessages.empty()) errorMessages.push_back(contentsOrErr.getError().message()); for (auto message : errorMessages) ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file, M->getName(), bystanderName, message, filePath); return false; } auto contents = std::move(*contentsOrErr); for (const auto &module : contents.modules) { auto moduleIdent = ctx.getIdentifier(module.name); overlayModuleNames.push_back(moduleIdent); } return true; } //===----------------------------------------------------------------------===// // SourceFile Implementation //===----------------------------------------------------------------------===// void SourceFile::print(raw_ostream &OS, const PrintOptions &PO) { StreamPrinter Printer(OS); print(Printer, PO); } void SourceFile::print(ASTPrinter &Printer, const PrintOptions &PO) { std::set MajorDeclKinds = {DeclKind::Class, DeclKind::Enum, DeclKind::Extension, DeclKind::Protocol, DeclKind::Struct}; for (auto decl : getTopLevelDecls()) { if (!decl->shouldPrintInContext(PO)) continue; // For a major decl, we print an empty line before it. if (MajorDeclKinds.find(decl->getKind()) != MajorDeclKinds.end()) Printer << "\n"; if (decl->print(Printer, PO)) Printer << "\n"; } } void SourceFile::addImports(ArrayRef IM) { if (IM.empty()) return; ASTContext &ctx = getASTContext(); auto newBuf = ctx.AllocateUninitialized(Imports.size() + IM.size()); auto iter = newBuf.begin(); iter = std::uninitialized_copy(Imports.begin(), Imports.end(), iter); iter = std::uninitialized_copy(IM.begin(), IM.end(), iter); assert(iter == newBuf.end()); Imports = newBuf; // Update the HasImplementationOnlyImports flag. if (!HasImplementationOnlyImports) { for (auto &desc : IM) { if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) HasImplementationOnlyImports = true; } } } bool SourceFile::hasTestableOrPrivateImport( AccessLevel accessLevel, const swift::ValueDecl *ofDecl, SourceFile::ImportQueryKind queryKind) const { auto *module = ofDecl->getModuleContext(); switch (accessLevel) { case AccessLevel::Internal: case AccessLevel::Public: // internal/public access only needs an import marked as @_private. The // filename does not need to match (and we don't serialize it for such // decls). return std::any_of( Imports.begin(), Imports.end(), [module, queryKind](ImportedModuleDesc desc) -> bool { if (queryKind == ImportQueryKind::TestableAndPrivate) return desc.module.second == module && (desc.importOptions.contains(ImportFlags::PrivateImport) || desc.importOptions.contains(ImportFlags::Testable)); else if (queryKind == ImportQueryKind::TestableOnly) return desc.module.second == module && desc.importOptions.contains(ImportFlags::Testable); else { assert(queryKind == ImportQueryKind::PrivateOnly); return desc.module.second == module && desc.importOptions.contains(ImportFlags::PrivateImport); } }); case AccessLevel::Open: return true; case AccessLevel::FilePrivate: case AccessLevel::Private: // Fallthrough. break; } if (queryKind == ImportQueryKind::TestableOnly) return false; auto *DC = ofDecl->getDeclContext(); if (!DC) return false; auto *scope = DC->getModuleScopeContext(); if (!scope) return false; StringRef filename; if (auto *file = dyn_cast(scope)) { filename = file->getFilenameForPrivateDecl(ofDecl); } else return false; if (filename.empty()) return false; return std::any_of(Imports.begin(), Imports.end(), [module, filename](ImportedModuleDesc desc) -> bool { return desc.module.second == module && desc.importOptions.contains( ImportFlags::PrivateImport) && desc.filename == filename; }); } bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const { // Implementation-only imports are (currently) always source-file-specific, // so if we don't have any, we know the search is complete. if (!hasImplementationOnlyImports()) return false; auto &imports = getASTContext().getImportCache(); // Look at the imports of this source file. for (auto &desc : Imports) { // Ignore implementation-only imports. if (desc.importOptions.contains(ImportFlags::ImplementationOnly)) continue; // If the module is imported this way, it's not imported // implementation-only. if (imports.isImportedBy(module, desc.module.second)) return false; } // Now check this file's enclosing module in case there are re-exports. return !imports.isImportedBy(module, getParentModule()); } void SourceFile::lookupImportedSPIGroups(const ModuleDecl *importedModule, SmallVectorImpl &spiGroups) const { for (auto &import : Imports) { if (import.importOptions.contains(ImportFlags::SPIAccessControl) && importedModule == std::get(import.module)) { auto importedSpis = import.spiGroups; spiGroups.append(importedSpis.begin(), importedSpis.end()); } } } bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const { auto targetModule = targetDecl->getModuleContext(); SmallVector importedSPIGroups; lookupImportedSPIGroups(targetModule, importedSPIGroups); if (importedSPIGroups.empty()) return false; auto declSPIGroups = targetDecl->getSPIGroups(); // Note: If we reach a point where there are many SPI imports or SPI groups // on decls we could optimize this further by using a set. for (auto importedSPI : importedSPIGroups) for (auto declSPI : declSPIGroups) if (importedSPI == declSPI) return true; return false; } bool Decl::isSPI() const { return !getSPIGroups().empty(); } ArrayRef Decl::getSPIGroups() const { if (auto vd = dyn_cast(this)) { if (vd->getFormalAccess() < AccessLevel::Public) return ArrayRef(); } else if (!isa(this)) return ArrayRef(); return evaluateOrDefault(getASTContext().evaluator, SPIGroupsRequest{ this }, ArrayRef()); } llvm::Expected> SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const { // Applies only to public ValueDecls and ExtensionDecls. if (auto vd = dyn_cast(decl)) assert(vd->getFormalAccess() >= AccessLevel::Public); else assert(isa(decl)); // First, look for local attributes. llvm::SetVector spiGroups; for (auto attr : decl->getAttrs().getAttributes()) for (auto spi : attr->getSPIGroups()) spiGroups.insert(spi); auto &ctx = decl->getASTContext(); if (spiGroups.empty()) { // Then in the extended nominal type. if (auto extension = dyn_cast(decl)) { auto extended = extension->getExtendedNominal(); auto extSPIs = extended->getSPIGroups(); if (!extSPIs.empty()) return extSPIs; } // And finally in the parent context. auto parent = decl->getDeclContext(); if (auto parentD = parent->getAsDecl()) { if (!isa(parentD)) { return parentD->getSPIGroups(); } } } return ctx.AllocateCopy(spiGroups.getArrayRef()); } bool SourceFile::shouldCrossImport() const { return Kind != SourceFileKind::SIL && Kind != SourceFileKind::Interface && getASTContext().LangOpts.EnableCrossImportOverlays; } ModuleDecl* SourceFile::getModuleShadowedBySeparatelyImportedOverlay(const ModuleDecl *overlay) { if (separatelyImportedOverlaysReversed.empty() && !separatelyImportedOverlays.empty()) { for (auto &entry: separatelyImportedOverlays) { ModuleDecl *shadowed = entry.first; for (ModuleDecl *overlay: entry.second) { // If for some reason the same overlay shadows more than one module, // pick the one whose name is alphabetically first. ModuleDecl *old = separatelyImportedOverlaysReversed[overlay]; if (!old || shadowed->getNameStr() < old->getNameStr()) separatelyImportedOverlaysReversed[overlay] = shadowed; } } } auto i = separatelyImportedOverlaysReversed.find(overlay); return i != separatelyImportedOverlaysReversed.end() ? std::get<1>(*i) : nullptr; }; void ModuleDecl::clearLookupCache() { getASTContext().getImportCache().clear(); if (!Cache) return; // Abandon any current cache. We'll rebuild it on demand. Cache->invalidate(); Cache.reset(); } void SourceFile::cacheVisibleDecls(SmallVectorImpl &&globals) const { SmallVectorImpl &cached = getCache().AllVisibleValues; cached = std::move(globals); } const SmallVectorImpl & SourceFile::getCachedVisibleDecls() const { return getCache().AllVisibleValues; } static void performAutoImport( SourceFile &SF, SourceFile::ImplicitModuleImportKind implicitModuleImportKind) { if (SF.Kind == SourceFileKind::SIL) assert(implicitModuleImportKind == SourceFile::ImplicitModuleImportKind::None); ASTContext &Ctx = SF.getASTContext(); ModuleDecl *M = nullptr; switch (implicitModuleImportKind) { case SourceFile::ImplicitModuleImportKind::None: return; case SourceFile::ImplicitModuleImportKind::Builtin: M = Ctx.TheBuiltinModule; break; case SourceFile::ImplicitModuleImportKind::Stdlib: M = Ctx.getStdlibModule(true); break; } assert(M && "unable to auto-import module"); // FIXME: These will be the same for most source files, but we copy them // over and over again. auto Imports = SourceFile::ImportedModuleDesc( ModuleDecl::ImportedModule({}, M), SourceFile::ImportOptions()); SF.addImports(Imports); } llvm::StringMap SourceFile::getInfoForUsedFilePaths() const { llvm::StringMap result; if (BufferID != -1) { result[getFilename()].physicalFileLoc = getASTContext().SourceMgr.getLocForBufferStart(BufferID); } for (auto &vpath : VirtualFilePaths) { result[vpath.Item].virtualFileLocs.insert(vpath.Loc); } return result; } /// Returns a map of filenames to a map of file paths to SourceFilePathInfo /// instances, for all SourceFiles in the module. static llvm::StringMap> getInfoForUsedFileNames(const ModuleDecl *module) { llvm::StringMap> result; for (auto *file : module->getFiles()) { auto *sourceFile = dyn_cast(file); if (!sourceFile) continue; for (auto &pair : sourceFile->getInfoForUsedFilePaths()) { StringRef fullPath = pair.first(); StringRef fileName = llvm::sys::path::filename(fullPath); auto &info = pair.second; result[fileName][fullPath].merge(info); } } return result; } static void computeMagicFileString(const ModuleDecl *module, StringRef name, SmallVectorImpl &result) { result.assign(module->getNameStr().begin(), module->getNameStr().end()); result.push_back('/'); result.append(name.begin(), name.end()); } static StringRef resolveMagicNameConflicts(const ModuleDecl *module, StringRef fileString, const llvm::StringMap &paths, bool shouldDiagnose) { assert(paths.size() > 1); assert(module->getASTContext().LangOpts.EnableConcisePoundFile); /// The path we consider to be "correct"; we will emit fix-its changing the /// other paths to match this one. StringRef winner = ""; // First, select a winner. for (const auto &pathPair : paths) { // If there is a physical file with this name, we use its path and stop // looking. if (pathPair.second.physicalFileLoc.isValid()) { winner = pathPair.first(); break; } // Otherwise, we favor the lexicographically "smaller" path. if (winner.empty() || winner > pathPair.first()) { winner = pathPair.first(); } } // If we're not diagnosing, that's all we need to do. if (!shouldDiagnose) return winner; SmallString<64> winnerLiteral; llvm::raw_svector_ostream winnerLiteralStream{winnerLiteral}; swift::printAsQuotedString(winnerLiteralStream, winner); auto &diags = module->getASTContext().Diags; // Diagnose the conflict at each #sourceLocation that specifies it. for (const auto &pathPair : paths) { bool isWinner = (pathPair.first() == winner); // Don't diagnose #sourceLocations that match the physical file. if (pathPair.second.physicalFileLoc.isValid()) { assert(isWinner && "physical files should always win; duplicate name?"); continue; } for (auto loc : pathPair.second.virtualFileLocs) { diags.diagnose(loc, diag::pound_source_location_creates_pound_file_conflicts, fileString); // Offer a fix-it unless it would be tautological. if (!isWinner) diags.diagnose(loc, diag::fixit_correct_source_location_file, winner) .fixItReplace(loc, winnerLiteral); } } return winner; } llvm::StringMap> ModuleDecl::computeMagicFileStringMap(bool shouldDiagnose) const { llvm::StringMap> result; SmallString<64> scratch; if (!getASTContext().LangOpts.EnableConcisePoundFile) return result; for (auto &namePair : getInfoForUsedFileNames(this)) { computeMagicFileString(this, namePair.first(), scratch); auto &infoForPaths = namePair.second; assert(!infoForPaths.empty()); // TODO: In the future, we'd like to handle these conflicts gracefully by // generating a unique `#file` string for each conflicting name. For now, we // will simply warn about conflicts. StringRef winner = infoForPaths.begin()->first(); if (infoForPaths.size() > 1) winner = resolveMagicNameConflicts(this, scratch, infoForPaths, shouldDiagnose); for (auto &pathPair : infoForPaths) { result[pathPair.first()] = std::make_pair(scratch.str().str(), pathPair.first() == winner); } } return result; } SourceFile::SourceFile(ModuleDecl &M, SourceFileKind K, Optional bufferID, ImplicitModuleImportKind ModImpKind, bool KeepParsedTokens, bool BuildSyntaxTree, ParsingOptions parsingOpts) : FileUnit(FileUnitKind::Source, M), BufferID(bufferID ? *bufferID : -1), ParsingOpts(parsingOpts), Kind(K), SyntaxInfo(new SourceFileSyntaxInfo(BuildSyntaxTree)) { M.getASTContext().addDestructorCleanup(*this); performAutoImport(*this, ModImpKind); if (isScriptMode()) { bool problem = M.registerEntryPointFile(this, SourceLoc(), None); assert(!problem && "multiple main files?"); (void)problem; } if (KeepParsedTokens) { AllCorrectedTokens = std::vector(); } } std::vector &SourceFile::getTokenVector() { assert(shouldCollectToken() && "Disabled"); return *AllCorrectedTokens; } ArrayRef SourceFile::getAllTokens() const { assert(shouldCollectToken() && "Disabled"); return *AllCorrectedTokens; } bool SourceFile::shouldCollectToken() const { switch (Kind) { case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::Interface: return (bool)AllCorrectedTokens; case SourceFileKind::REPL: case SourceFileKind::SIL: return false; } llvm_unreachable("unhandled kind"); } bool SourceFile::shouldBuildSyntaxTree() const { return canBeParsedInFull() && SyntaxInfo->Enable; } bool SourceFile::canBeParsedInFull() const { switch (Kind) { case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::Interface: return true; case SourceFileKind::REPL: case SourceFileKind::SIL: return false; } llvm_unreachable("unhandled kind"); } bool SourceFile::hasDelayedBodyParsing() const { if (ParsingOpts.contains(ParsingFlags::DisableDelayedBodies)) return false; // Not supported right now. if (Kind == SourceFileKind::REPL || Kind == SourceFileKind::SIL) return false; if (hasInterfaceHash()) return false; if (shouldCollectToken()) return false; if (shouldBuildSyntaxTree()) return false; return true; } ArrayRef SourceFile::getTopLevelDecls() const { auto &ctx = getASTContext(); auto *mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, ParseSourceFileRequest{mutableThis}, {}); } bool FileUnit::walk(ASTWalker &walker) { SmallVector Decls; getTopLevelDecls(Decls); llvm::SaveAndRestore SAR(walker.Parent, getParentModule()); for (Decl *D : Decls) { #ifndef NDEBUG PrettyStackTraceDecl debugStack("walking into decl", D); #endif if (D->walk(walker)) return true; if (walker.shouldWalkAccessorsTheOldWay()) { // Pretend that accessors share a parent with the storage. // // FIXME: Update existing ASTWalkers to deal with accessors appearing as // children of the storage instead. if (auto *ASD = dyn_cast(D)) { for (auto AD : ASD->getAllAccessors()) { if (AD->walk(walker)) return true; } } } } return false; } bool SourceFile::walk(ASTWalker &walker) { llvm::SaveAndRestore SAR(walker.Parent, getParentModule()); for (Decl *D : getTopLevelDecls()) { #ifndef NDEBUG PrettyStackTraceDecl debugStack("walking into decl", D); #endif if (D->walk(walker)) return true; if (walker.shouldWalkAccessorsTheOldWay()) { // Pretend that accessors share a parent with the storage. // // FIXME: Update existing ASTWalkers to deal with accessors appearing as // children of the storage instead. if (auto *ASD = dyn_cast(D)) { for (auto AD : ASD->getAllAccessors()) { if (AD->walk(walker)) return true; } } } } return false; } StringRef SourceFile::getFilename() const { if (BufferID == -1) return ""; SourceManager &SM = getASTContext().SourceMgr; return SM.getIdentifierForBuffer(BufferID); } ASTScope &SourceFile::getScope() { assert(isSuitableForASTScopes() && "Should not be creating scope tree"); if (!Scope) Scope = std::unique_ptr(new (getASTContext()) ASTScope(this)); return *Scope.get(); } Identifier SourceFile::getDiscriminatorForPrivateValue(const ValueDecl *D) const { assert(D->getDeclContext()->getModuleScopeContext() == this); if (!PrivateDiscriminator.empty()) return PrivateDiscriminator; StringRef name = getFilename(); if (name.empty()) { assert(1 == count_if(getParentModule()->getFiles(), [](const FileUnit *FU) -> bool { return isa(FU) && cast(FU)->getFilename().empty(); }) && "can't promise uniqueness if multiple source files are nameless"); // We still need a discriminator, so keep going. } // Use a hash of the basename of the source file as our discriminator. // This keeps us from leaking information about the original filename // while still providing uniqueness. Using the basename makes the // discriminator invariant across source checkout locations. // FIXME: Use a faster hash here? We don't need security, just uniqueness. llvm::MD5 hash; hash.update(getParentModule()->getName().str()); hash.update(llvm::sys::path::filename(name)); llvm::MD5::MD5Result result; hash.final(result); // Use the hash as a hex string, prefixed with an underscore to make sure // it is a valid identifier. // FIXME: There are more compact ways to encode a 16-byte value. SmallString<33> buffer{"_"}; SmallString<32> hashString; llvm::MD5::stringifyResult(result, hashString); buffer += hashString; PrivateDiscriminator = getASTContext().getIdentifier(buffer.str().upper()); return PrivateDiscriminator; } TypeRefinementContext *SourceFile::getTypeRefinementContext() { return TRC; } void SourceFile::setTypeRefinementContext(TypeRefinementContext *Root) { TRC = Root; } void SourceFile::createReferencedNameTracker() { assert(!ReferencedNames && "This file already has a name tracker."); ReferencedNames.emplace(ReferencedNameTracker()); } ArrayRef SourceFile::getOpaqueReturnTypeDecls() { for (auto *vd : UnvalidatedDeclsWithOpaqueReturnTypes) { if (auto opaqueDecl = vd->getOpaqueResultTypeDecl()) { auto inserted = ValidatedOpaqueReturnTypes.insert( {opaqueDecl->getOpaqueReturnTypeIdentifier().str(), opaqueDecl}); if (inserted.second) { OpaqueReturnTypes.push_back(opaqueDecl); } } } UnvalidatedDeclsWithOpaqueReturnTypes.clear(); return OpaqueReturnTypes; } OpaqueTypeDecl * SourceFile::lookupOpaqueResultType(StringRef MangledName) { // Check already-validated decls. auto found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; // If there are unvalidated decls with opaque types, go through and validate // them now. (void) getOpaqueReturnTypeDecls(); found = ValidatedOpaqueReturnTypes.find(MangledName); if (found != ValidatedOpaqueReturnTypes.end()) return found->second; // Otherwise, we don't have a matching opaque decl. return nullptr; } //===----------------------------------------------------------------------===// // Miscellaneous //===----------------------------------------------------------------------===// void FileUnit::anchor() {} void *FileUnit::operator new(size_t Bytes, ASTContext &C, unsigned Alignment) { return C.Allocate(Bytes, Alignment); } void FileUnit::getTopLevelDeclsWhereAttributesMatch( SmallVectorImpl &Results, llvm::function_ref matchAttributes) const { auto prevSize = Results.size(); getTopLevelDecls(Results); // Filter out unwanted decls that were just added to Results. // Note: We could apply this check in all implementations of // getTopLevelDecls instead or in everything that creates a Decl. auto newEnd = std::remove_if(Results.begin() + prevSize, Results.end(), [&matchAttributes](const Decl *D) -> bool { return !matchAttributes(D->getAttrs()); }); Results.erase(newEnd, Results.end()); } void swift::simple_display(llvm::raw_ostream &out, const FileUnit *file) { if (!file) { out << "(null)"; return; } switch (file->getKind()) { case FileUnitKind::Source: out << '\"' << cast(file)->getFilename() << '\"'; return; case FileUnitKind::Builtin: out << "(Builtin)"; return; case FileUnitKind::DWARFModule: case FileUnitKind::ClangModule: case FileUnitKind::SerializedAST: out << '\"' << cast(file)->getFilename() << '\"'; return; } llvm_unreachable("Unhandled case in switch"); } StringRef LoadedFile::getFilename() const { return ""; } static const clang::Module * getClangModule(llvm::PointerUnion Union) { return static_cast(Union.get()); } StringRef ModuleEntity::getName() const { assert(!Mod.isNull()); if (auto SwiftMod = Mod.dyn_cast()) return SwiftMod->getName().str(); return getClangModule(Mod)->Name; } std::string ModuleEntity::getFullName() const { assert(!Mod.isNull()); if (auto SwiftMod = Mod.dyn_cast()) return std::string(SwiftMod->getName()); return getClangModule(Mod)->getFullModuleName(); } bool ModuleEntity::isSystemModule() const { assert(!Mod.isNull()); if (auto SwiftMod = Mod.dyn_cast()) return SwiftMod->isSystemModule(); return getClangModule(Mod)->IsSystem; } bool ModuleEntity::isBuiltinModule() const { assert(!Mod.isNull()); if (auto SwiftMod = Mod.dyn_cast()) return SwiftMod->isBuiltinModule(); return false; } const ModuleDecl* ModuleEntity::getAsSwiftModule() const { assert(!Mod.isNull()); if (auto SwiftMod = Mod.dyn_cast()) return SwiftMod; return nullptr; } const clang::Module* ModuleEntity::getAsClangModule() const { assert(!Mod.isNull()); if (Mod.is()) return nullptr; return getClangModule(Mod); } // See swift/Basic/Statistic.h for declaration: this enables tracing SourceFiles, is // defined here to avoid too much layering violation / circular linkage // dependency. struct SourceFileTraceFormatter : public UnifiedStatsReporter::TraceFormatter { void traceName(const void *Entity, raw_ostream &OS) const { if (!Entity) return; const SourceFile *SF = static_cast(Entity); OS << llvm::sys::path::filename(SF->getFilename()); } void traceLoc(const void *Entity, SourceManager *SM, clang::SourceManager *CSM, raw_ostream &OS) const { // SourceFiles don't have SourceLocs of their own; they contain them. } }; static SourceFileTraceFormatter TF; template<> const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; }