//===--- Module.h - Swift Language Module ASTs ------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines the Module class and its subclasses. // //===----------------------------------------------------------------------===// #ifndef SWIFT_MODULE_H #define SWIFT_MODULE_H #include "swift/AST/DeclContext.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" #include "swift/Basic/Optional.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/STLExtras.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/ErrorHandling.h" namespace clang { class Module; } namespace swift { class ASTContext; class ASTWalker; class BraceStmt; class Decl; enum class DeclKind : uint8_t; class ExtensionDecl; class DebuggerClient; class DerivedFileUnit; class FileUnit; class FuncDecl; class InfixOperatorDecl; class LinkLibrary; class LookupCache; class ModuleLoader; class NameAliasType; class NominalTypeDecl; class EnumElementDecl; class OperatorDecl; class PostfixOperatorDecl; class PrefixOperatorDecl; class ProtocolConformance; class ProtocolDecl; struct PrintOptions; class TupleType; class Type; class ValueDecl; class VisibleDeclConsumer; /// NLKind - This is a specifier for the kind of name lookup being performed /// by various query methods. enum class NLKind { UnqualifiedLookup, QualifiedLookup }; /// Constants used to customize name lookup. enum NameLookupOptions { /// Visit supertypes (such as superclasses or inherited protocols) /// and their extensions as well as the current extension. NL_VisitSupertypes = 0x01, /// Consider declarations within protocols to which the context type conforms. NL_ProtocolMembers = 0x02, /// Remove non-visible declarations from the set of results. NL_RemoveNonVisible = 0x04, /// Remove overridden declarations from the set of results. NL_RemoveOverridden = 0x08, /// For existentials involving the special \c AnyObject protocol, /// allow lookups to find members of all classes. NL_DynamicLookup = 0x10, /// The default set of options used for qualified name lookup. /// /// FIXME: Eventually, add NL_ProtocolMembers to this, once all of the /// callers can handle it. NL_QualifiedDefault = NL_VisitSupertypes | NL_RemoveNonVisible | NL_RemoveOverridden, /// The default set of options used for unqualified name lookup. NL_UnqualifiedDefault = NL_VisitSupertypes | NL_RemoveNonVisible | NL_RemoveOverridden }; /// Describes the result of looking for the conformance of a given type /// to a specific protocol. enum class ConformanceKind { /// The type does not conform to the protocol. DoesNotConform, /// The type conforms to the protocol, with the given conformance. Conforms, /// The type is specified to conform to the protocol, but that conformance /// has not yet been checked. UncheckedConforms }; /// The result of looking for a specific conformance. typedef llvm::PointerIntPair LookupConformanceResult; /// Discriminator for file-units. enum class FileUnitKind { /// For a .swift source file. Source, /// For the compiler Builtin module. Builtin, /// A serialized Swift AST. SerializedAST, /// An imported Clang module. ClangModule, /// A derived declaration. Derived, }; enum class SourceFileKind { Library, ///< A normal .swift file. Main, ///< A .swift file that can have top-level code. REPL, ///< A virtual file that holds the user's input in the REPL. SIL ///< Came from a .sil file. }; /// The minimum unit of compilation. /// /// A module is made up of several file-units, which are all part of the same /// output binary and logical module (such as a single library or executable). /// /// \sa FileUnit class Module : public DeclContext { public: typedef ArrayRef> AccessPathTy; typedef std::pair ImportedModule; /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. class OrderImportedModules { public: bool operator()(const ImportedModule &lhs, const ImportedModule &rhs) const { if (lhs.second != rhs.second) return std::less()(lhs.second, rhs.second); if (lhs.first.data() != rhs.first.data()) return std::less()(lhs.first.begin(), rhs.first.begin()); return lhs.first.size() < rhs.first.size(); } }; public: ASTContext &Ctx; Identifier Name; private: /// If non-NULL, an plug-in that should be used when performing external /// lookups. // FIXME: Do we really need to bloat all modules with this? DebuggerClient *DebugClient = nullptr; SmallVector Files; Module(Identifier name, ASTContext &ctx); public: static Module *create(Identifier name, ASTContext &ctx) { return new (ctx) Module(name, ctx); } ArrayRef getFiles() { return Files; } ArrayRef getFiles() const { return { Files.begin(), Files.size() }; } void addFile(FileUnit &newFile); void removeFile(FileUnit &existingFile); /// Convenience accessor for clients that know what kind of file they're /// dealing with. SourceFile &getMainSourceFile(SourceFileKind expectedKind) const; /// Convenience accessor for clients that know what kind of file they're /// dealing with. FileUnit &getMainFile(FileUnitKind expectedKind) const; DerivedFileUnit &getDerivedFileUnit() const; DebuggerClient *getDebugClient() const { return DebugClient; } void setDebugClient(DebuggerClient *R) { assert(!DebugClient && "Debugger client already set"); DebugClient = R; } /// Look up a (possibly overloaded) value set at top-level scope /// (but with the specified access path, which may come from an import decl) /// within the current module. /// /// This does a simple local lookup, not recursively looking through imports. void lookupValue(AccessPathTy AccessPath, Identifier Name, NLKind LookupKind, SmallVectorImpl &Result) const; /// Find ValueDecls in the module and pass them to the given consumer object. /// /// This does a simple local lookup, not recursively looking through imports. void lookupVisibleDecls(AccessPathTy AccessPath, VisibleDeclConsumer &Consumer, NLKind LookupKind) const; /// @{ /// Look up the given operator in this module. /// /// If the operator is not found, or if there is an ambiguity, returns null. InfixOperatorDecl *lookupInfixOperator(Identifier name, SourceLoc diagLoc = {}); PrefixOperatorDecl *lookupPrefixOperator(Identifier name, SourceLoc diagLoc = {}); PostfixOperatorDecl *lookupPostfixOperator(Identifier name, SourceLoc diagLoc = {}); /// @} /// Finds all class members defined in this module. /// /// This does a simple local lookup, not recursively looking through imports. void lookupClassMembers(AccessPathTy accessPath, VisibleDeclConsumer &consumer) const; /// Finds class members defined in this module with the given name. /// /// This does a simple local lookup, not recursively looking through imports. void lookupClassMember(AccessPathTy accessPath, Identifier name, SmallVectorImpl &results) const; /// Look for the conformance of the given type to the given protocol. /// /// This routine determines whether the given \c type conforms to the given /// \c protocol. It only looks for explicit conformances (which are /// required by the language), and will return a \c ProtocolConformance* /// describing the conformance. /// /// During type-checking, it is possible that this routine will find an /// explicit declaration of conformance that has not yet been type-checked, /// in which case it will return note the presence of an unchecked /// conformance. /// /// \param type The type for which we are computing conformance. /// /// \param protocol The protocol to which we are computing conformance. /// /// \param resolver The lazy resolver. /// /// \returns The result of the conformance search, with a conformance /// structure when possible. LookupConformanceResult lookupConformance(Type type, ProtocolDecl *protocol, LazyResolver *resolver); /// \sa getImportedModules enum class ImportFilter { All, Public, Private }; /// Looks up which modules are imported by this module. /// /// \p filter controls whether public, private, or any imports are included /// in this list. void getImportedModules(SmallVectorImpl &imports, ImportFilter filter = ImportFilter::Public) const; /// Finds all top-level decls of this module. /// /// This does a simple local lookup, not recursively looking through imports. /// The order of the results is not guaranteed to be meaningful. void getTopLevelDecls(SmallVectorImpl &Results) const; /// Finds all top-level decls that should be displayed to a client of this /// module. /// /// This includes types, variables, functions, and extensions. /// This does a simple local lookup, not recursively looking through imports. /// The order of the results is not guaranteed to be meaningful. /// /// This can differ from \c getTopLevelDecls, e.g. it returns decls from a /// shadowed clang module. void getDisplayDecls(SmallVectorImpl &results) const; /// @{ /// Perform an action for every module visible from this module. /// /// This only includes modules with at least one declaration visible: if two /// import access paths are incompatible, the indirect module will be skipped. /// /// \param topLevelAccessPath If present, include the top-level module in the /// results, with the given access path. /// \param includePrivateTopLevelImports If true, imports listed in all /// file units within this module are traversed. Otherwise (the /// default), only re-exported imports are traversed. /// \param fn A callback of type bool(ImportedModule) or void(ImportedModule). /// Return \c false to abort iteration. /// /// \return True if the traversal ran to completion, false if it ended early /// due to the callback. bool forAllVisibleModules(AccessPathTy topLevelAccessPath, bool includePrivateTopLevelImports, std::function fn); bool forAllVisibleModules(AccessPathTy topLevelAccessPath, bool includePrivateTopLevelImports, std::function fn) { return forAllVisibleModules(topLevelAccessPath, includePrivateTopLevelImports, [=](const ImportedModule &import) -> bool { fn(import); return true; }); } template bool forAllVisibleModules(AccessPathTy topLevelAccessPath, bool includePrivateTopLevelImports, const Fn &fn) { using RetTy = typename as_function::type::result_type; std::function wrapped = std::cref(fn); return forAllVisibleModules(topLevelAccessPath, includePrivateTopLevelImports, wrapped); } template bool forAllVisibleModules(AccessPathTy topLevelAccessPath, const Fn &fn) { return forAllVisibleModules(topLevelAccessPath, false, fn); } /// @} using LinkLibraryCallback = std::function; /// @{ /// Generate the list of libraries needed to link this module, based on its /// imports. void collectLinkLibraries(LinkLibraryCallback callback); template void collectLinkLibraries(const Fn &fn) { LinkLibraryCallback wrapped = std::cref(fn); collectLinkLibraries(wrapped); } /// @} /// Returns true if the two access paths contain the same chain of /// identifiers. /// /// Source locations are ignored here. static bool isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs); /// \brief Get the path for the file that this module came from, or an empty /// string if this is not applicable. StringRef getModuleFilename() const; /// \returns true if this module is the "swift" standard library module. bool isStdlibModule() const; /// \returns true if this module is a system module; note that the StdLib is /// considered a system module. bool isSystemModule() const; /// \returns true if traversal was aborted, false otherwise. bool walk(ASTWalker &Walker); static bool classof(const DeclContext *DC) { return DC->getContextKind() == DeclContextKind::Module; } private: // Make placement new and vanilla new/delete illegal for Modules. void *operator new(size_t Bytes) throw() = delete; void operator delete(void *Data) throw() = delete; void *operator new(size_t Bytes, void *Mem) throw() = delete; public: // Only allow allocation of Modules using the allocator in ASTContext // or by doing a placement new. void *operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(Module)); }; /// A container for module-scope declarations that itself provides a scope; the /// smallest unit of code organization. /// /// FileUnit is an abstract base class; its subclasses represent different /// sorts of containers that can each provide a set of decls, e.g. a source /// file. A module can contain several file-units. class FileUnit : public DeclContext { virtual void anchor(); // FIXME: Stick this in a PointerIntPair. const FileUnitKind Kind; protected: FileUnit(FileUnitKind kind, Module &M) : DeclContext(DeclContextKind::FileUnit, &M), Kind(kind) { } virtual ~FileUnit() = default; public: FileUnitKind getKind() const { return Kind; } /// Look up a (possibly overloaded) value set at top-level scope /// (but with the specified access path, which may come from an import decl) /// within this file. /// /// This does a simple local lookup, not recursively looking through imports. virtual void lookupValue(Module::AccessPathTy accessPath, Identifier name, NLKind lookupKind, SmallVectorImpl &result) const = 0; /// Find ValueDecls in the module and pass them to the given consumer object. /// /// This does a simple local lookup, not recursively looking through imports. virtual void lookupVisibleDecls(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind) const {} /// Finds all class members defined in this file. /// /// This does a simple local lookup, not recursively looking through imports. virtual void lookupClassMembers(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer) const {} /// Finds class members defined in this file with the given name. /// /// This does a simple local lookup, not recursively looking through imports. virtual void lookupClassMember(Module::AccessPathTy accessPath, Identifier name, SmallVectorImpl &results) const {} /// Finds all top-level decls in this file. /// /// This does a simple local lookup, not recursively looking through imports. /// The order of the results is not guaranteed to be meaningful. virtual void getTopLevelDecls(SmallVectorImpl &results) const {} /// Adds all top-level decls to the given vector. /// /// This includes all decls that should be displayed to clients of the module. /// The order of the results is not guaranteed to be meaningful. /// /// This can differ from \c getTopLevelDecls, e.g. it returns decls from a /// shadowed clang module. virtual void getDisplayDecls(SmallVectorImpl &results) const { getTopLevelDecls(results); } /// Looks up which modules are imported by this file. /// /// \p filter controls whether public, private, or any imports are included /// in this list. virtual void getImportedModules(SmallVectorImpl &imports, Module::ImportFilter filter) const {} /// Generates the list of libraries needed to link this file, based on its /// imports. virtual void collectLinkLibraries(Module::LinkLibraryCallback callback) const {} /// @{ /// Perform an action for every module visible from this file. /// /// \param fn A callback of type bool(ImportedModule) or void(ImportedModule). /// Return \c false to abort iteration. /// /// \return True if the traversal ran to completion, false if it ended early /// due to the callback. bool forAllVisibleModules(std::function fn); bool forAllVisibleModules(std::function fn) { forAllVisibleModules([=](const Module::ImportedModule &import) -> bool { fn(import); return true; }); return true; } template bool forAllVisibleModules(const Fn &fn) { using RetTy = typename as_function::type::result_type; std::function wrapped = std::cref(fn); return forAllVisibleModules(wrapped); } /// @} /// Traverse the decls within this file. /// /// \returns true if traversal was aborted, false if it completed /// successfully. virtual bool walk(ASTWalker &walker); // Efficiency override for DeclContext::getParentModule(). Module *getParentModule() const { return const_cast(cast(getParent())); } // Efficiency override for DeclContext::getASTContext(). ASTContext &getASTContext() const { return getParentModule()->Ctx; } static bool classof(const DeclContext *DC) { return DC->getContextKind() == DeclContextKind::FileUnit; } private: // Make placement new and vanilla new/delete illegal for FileUnits. void *operator new(size_t Bytes) throw() = delete; void *operator new(size_t Bytes, void *Mem) throw() = delete; protected: // Unfortunately we can't remove this altogether because the virtual // destructor requires it to be accessible. void operator delete(void *Data) throw() { llvm_unreachable("Don't use operator delete on a SourceFile"); } public: // Only allow allocation of FileUnits using the allocator in ASTContext // or by doing a placement new. void *operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(FileUnit)); }; /// A container for a module-level definition derived as part of an implicit /// protocol conformance. class DerivedFileUnit final : public FileUnit { TinyPtrVector DerivedDecls; public: DerivedFileUnit(Module &M); ~DerivedFileUnit() = default; void addDerivedDecl(FuncDecl *FD) { DerivedDecls.push_back(FD); } void lookupValue(Module::AccessPathTy accessPath, Identifier name, NLKind lookupKind, SmallVectorImpl &result) const override; void lookupVisibleDecls(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind) const override; void getTopLevelDecls(SmallVectorImpl &results) const override; static bool classof(const FileUnit *file) { return file->getKind() == FileUnitKind::Derived; } static bool classof(const DeclContext *DC) { return isa(DC) && classof(cast(DC)); } }; /// A file containing Swift source code. /// /// This is a .swift or .sil file (or a virtual file, such as the contents of /// the REPL). Since it contains raw source, it must be parsed and name-bound /// before being used for anything; a full type-check is also necessary for /// IR generation. class SourceFile final : public FileUnit { public: class LookupCache; private: std::unique_ptr Cache; LookupCache &getCache() const; /// This is the list of modules that are imported by this module, with the /// second element of the pair declaring whether the module is reexported. /// /// This is filled in by the Name Binding phase. ArrayRef> Imports; /// \brief The ID for the memory buffer containing this file's source. /// /// May be -1, to indicate no association with a buffer. int BufferID; friend ASTContext; ~SourceFile(); public: /// The list of top-level declarations in the source file. std::vector Decls; template using OperatorMap = llvm::DenseMap>; OperatorMap InfixOperators; OperatorMap PostfixOperators; OperatorMap PrefixOperators; /// Describes what kind of file this is, which can affect some type checking /// and other behavior. const SourceFileKind Kind; enum ASTStage_t { /// Parsing is underway. Parsing, /// Parsing has completed. Parsed, /// Name binding has completed. NameBound, /// Type checking has completed. TypeChecked }; /// Defines what phases of parsing and semantic analysis are complete for a /// source file. /// /// Only files that have been fully processed (i.e. type-checked) will be /// forwarded on to IRGen. ASTStage_t ASTStage = Parsing; SourceFile(Module &M, SourceFileKind K, Optional bufferID, bool hasBuiltinModuleAccess = false); ArrayRef> getImports() const { assert(ASTStage >= Parsed || Kind == SourceFileKind::SIL); return Imports; } void setImports(ArrayRef> IM) { Imports = IM; } void clearLookupCache(); void cacheVisibleDecls(SmallVectorImpl &&globals) const; const SmallVectorImpl &getCachedVisibleDecls() const; virtual void lookupValue(Module::AccessPathTy accessPath, Identifier name, NLKind lookupKind, SmallVectorImpl &result) const override; virtual void lookupVisibleDecls(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind) const override; virtual void lookupClassMembers(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer) const override; virtual void lookupClassMember(Module::AccessPathTy accessPath, Identifier name, SmallVectorImpl &results) const override; virtual void getTopLevelDecls(SmallVectorImpl &results) const override; virtual void getImportedModules(SmallVectorImpl &imports, Module::ImportFilter filter) const override; virtual void collectLinkLibraries(Module::LinkLibraryCallback callback) const override; virtual bool walk(ASTWalker &walker) override; /// @{ /// Look up the given operator in this file. /// /// The file must be name-bound already. If the operator is not found, or if /// there is an ambiguity, returns null. InfixOperatorDecl *lookupInfixOperator(Identifier name, SourceLoc diagLoc = {}); PrefixOperatorDecl *lookupPrefixOperator(Identifier name, SourceLoc diagLoc = {}); PostfixOperatorDecl *lookupPostfixOperator(Identifier name, SourceLoc diagLoc = {}); /// @} /// \brief The buffer ID for the file that was imported, or Nothing if there /// is no associated buffer. Optional getBufferID() const { if (BufferID == -1) return Nothing; return BufferID; } /// If this buffer corresponds to a file on disk, returns the path. /// Otherwise, return an empty string. StringRef getFilename() const; void dump() const; void dump(raw_ostream &os) const; /// \brief Pretty-print the contents of this source file. /// /// \param Printer The AST printer used for printing the contents. /// \param PO Options controlling the printing process. void print(ASTPrinter &Printer, const PrintOptions &PO); void print(raw_ostream &OS, const PrintOptions &PO); static bool classof(const FileUnit *file) { return file->getKind() == FileUnitKind::Source; } static bool classof(const DeclContext *DC) { return isa(DC) && classof(cast(DC)); } /// True if this is a "script mode" source file that admits top-level code. bool isScriptMode() const { switch (Kind) { case SourceFileKind::Main: case SourceFileKind::REPL: return true; case SourceFileKind::Library: case SourceFileKind::SIL: return false; } } }; /// This represents the compiler's implicitly generated declarations in the /// Builtin module. class BuiltinUnit final : public FileUnit { public: class LookupCache; private: std::unique_ptr Cache; LookupCache &getCache() const; friend ASTContext; ~BuiltinUnit() = default; public: explicit BuiltinUnit(Module &M); virtual void lookupValue(Module::AccessPathTy accessPath, Identifier name, NLKind lookupKind, SmallVectorImpl &result) const override; static bool classof(const FileUnit *file) { return file->getKind() == FileUnitKind::Builtin; } static bool classof(const DeclContext *DC) { return isa(DC) && classof(cast(DC)); } }; /// Represents an externally-loaded file of some kind. class LoadedFile : public FileUnit { protected: ~LoadedFile() = default; LoadedFile(FileUnitKind Kind, Module &M) noexcept : FileUnit(Kind, M) { assert(classof(this) && "invalid kind"); } public: /// Returns an arbitrary string representing the storage backing this file. /// /// This is usually a filesystem path. virtual StringRef getFilename() const; /// Look up an operator declaration. /// /// \param name The operator name ("+", ">>", etc.) /// /// \param fixity One of PrefixOperator, InfixOperator, or PostfixOperator. virtual OperatorDecl *lookupOperator(Identifier name, DeclKind fixity) const { return nullptr; } virtual bool isSystemModule() const { return false; } static bool classof(const FileUnit *file) { return file->getKind() == FileUnitKind::SerializedAST || file->getKind() == FileUnitKind::ClangModule; } static bool classof(const DeclContext *DC) { return isa(DC) && classof(cast(DC)); } }; inline SourceFile &Module::getMainSourceFile(SourceFileKind expectedKind) const{ assert(!Files.empty() && "No files added yet"); assert(cast(Files.front())->Kind == expectedKind); return *cast(Files.front()); } inline FileUnit &Module::getMainFile(FileUnitKind expectedKind) const { assert(expectedKind != FileUnitKind::Source && "must use specific source kind; see getMainSourceFile"); assert(!Files.empty() && "No files added yet"); assert(Files.front()->getKind() == expectedKind); return *Files.front(); } } // end namespace swift namespace llvm { template <> class DenseMapInfo { using Module = swift::Module; public: static Module::ImportedModule getEmptyKey() { return {{}, llvm::DenseMapInfo::getEmptyKey()}; } static Module::ImportedModule getTombstoneKey() { return {{}, llvm::DenseMapInfo::getTombstoneKey()}; } static unsigned getHashValue(const Module::ImportedModule &val) { auto pair = std::make_pair(val.first.size(), val.second); return llvm::DenseMapInfo::getHashValue(pair); } static bool isEqual(const Module::ImportedModule &lhs, const Module::ImportedModule &rhs) { return lhs.second == rhs.second && Module::isSameAccessPath(lhs.first, rhs.first); } }; } #endif