Use a list of module loaders instead of a single Clang importer.

This paves the way for having a Swift module importer. The eventual goal
here is to eliminate all explicit uses of the Clang module loader, but
I'm not going to push too hard on that for now.

Swift SVN r5092
This commit is contained in:
Jordan Rose
2013-05-08 18:09:33 +00:00
parent 22859a119b
commit ccae995f61
10 changed files with 291 additions and 166 deletions

View File

@@ -305,14 +305,28 @@ public:
const Type TheIEEE128Type; /// TheIEEE128Type - 128-bit IEEE floating point
const Type ThePPC128Type; /// ThePPC128Type - 128-bit PowerPC 2xDouble
/// \brief Determine whether this ASTContext has a module loader.
bool hasModuleLoader() const;
/// \brief Adds a module loader to this AST context.
///
/// \param loader The new module loader, which will be added after any
/// existing module loaders.
/// \param isClang \c true if this module loader is responsible for loading
/// Clang modules, which are special-cased in some parts of the
/// compiler.
void addModuleLoader(llvm::IntrusiveRefCntPtr<ModuleLoader> loader,
bool isClang = false);
/// \brief Set the module loader for this ASTContext.
void setModuleLoader(llvm::IntrusiveRefCntPtr<ModuleLoader> loader);
/// \brief Retrieve the Clang module loader for this ASTContext.
///
/// If there is no Clang module loader, returns a null smart pointer.
llvm::IntrusiveRefCntPtr<ModuleLoader> getClangModuleLoader() const;
/// \brief Retrieve the module loader for this ASTContext.
ModuleLoader &getModuleLoader() const;
/// \brief Attempts to load a module into this ASTContext.
///
/// If a module by this name has already been loaded, the existing module will
/// be returned.
///
/// \returns The requested module, or NULL if the module cannot be found.
Module *getModule(ArrayRef<std::pair<Identifier, SourceLoc>> modulePath);
private:
friend class Decl;

View File

@@ -729,6 +729,8 @@ ERROR(constraint_assign_type_check_fail,sema,none,
// Name Binding
//------------------------------------------------------------------------------
ERROR(sema_no_import,sema_nb,none,
"no such module '%0'", (StringRef))
ERROR(sema_opening_import,sema_nb,none,
"opening import file '%0.swift': %1", (StringRef, StringRef))
ERROR(invalid_declaration_imported,sema_nb,none,

View File

@@ -0,0 +1,56 @@
//===--- SourceLoader.h - Import .swift files as modules --------*- 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_SOURCELOADER_H
#define SWIFT_SEMA_SOURCELOADER_H
#include "swift/AST/ModuleLoader.h"
namespace swift {
class ASTContext;
class Module;
/// \brief Imports serialized Swift modules into an ASTContext.
class SourceLoader : public ModuleLoader {
private:
ASTContext &Ctx;
explicit SourceLoader(ASTContext &ctx) : Ctx(ctx) {}
public:
static SourceLoader *create(ASTContext &ctx) {
return new SourceLoader(ctx);
}
SourceLoader(const SourceLoader &) = delete;
SourceLoader(SourceLoader &&) = delete;
SourceLoader &operator=(const SourceLoader &) = delete;
SourceLoader &operator=(SourceLoader &&) = delete;
/// \brief Import a module with the given module path.
///
/// \param importLoc The location of the 'import' keyword.
///
/// \param path A sequence of (identifier, location) pairs that denote
/// the dotted module name to load, e.g., AppKit.NSWindow.
///
/// \returns the module referenced, if it could be loaded. Otherwise,
/// returns NULL.
virtual Module *
loadModule(SourceLoc importLoc,
ArrayRef<std::pair<Identifier, SourceLoc>> path) override;
};
}
#endif

View File

@@ -37,8 +37,13 @@ struct ASTContext::Implementation {
llvm::BumpPtrAllocator Allocator; // used in later initializations
llvm::StringMap<char, llvm::BumpPtrAllocator&> IdentifierTable;
/// \brief The module loader used to load modules.
llvm::IntrusiveRefCntPtr<swift::ModuleLoader> ModuleLoader;
/// \brief The various module loaders that import external modules into this
/// ASTContext.
SmallVector<llvm::IntrusiveRefCntPtr<swift::ModuleLoader>, 4> ModuleLoaders;
/// \brief The module loader used to load Clang modules.
// FIXME: We shouldn't be special-casing Clang.
llvm::IntrusiveRefCntPtr<swift::ModuleLoader> ClangModuleLoader;
/// \brief The set of AST mutation listeners.
SmallVector<ASTMutationListener *, 4> MutationListeners;
@@ -229,21 +234,36 @@ void ASTContext::setSubstitutions(BoundGenericType* Bound,
Impl.BoundGenericSubstitutions[Bound] = Subs;
}
/// \brief Determine whether this ASTContext has a module loader.
bool ASTContext::hasModuleLoader() const {
return Impl.ModuleLoader;
void ASTContext::addModuleLoader(llvm::IntrusiveRefCntPtr<ModuleLoader> loader,
bool IsClang) {
Impl.ModuleLoaders.push_back(loader);
if (IsClang) {
assert(!Impl.ClangModuleLoader && "Already have a Clang module loader");
Impl.ClangModuleLoader = std::move(loader);
}
}
/// \brief Set the module loader for this ASTContext.
void ASTContext::setModuleLoader(llvm::IntrusiveRefCntPtr<ModuleLoader> loader){
assert(!hasModuleLoader() && "Already has a module loader");
Impl.ModuleLoader = loader;
llvm::IntrusiveRefCntPtr<ModuleLoader> ASTContext::getClangModuleLoader() const{
return Impl.ClangModuleLoader;
}
/// \brief Retrieve the module loader for this ASTContext.
ModuleLoader &ASTContext::getModuleLoader() const {
assert(hasModuleLoader() && "No module loader!");
return *Impl.ModuleLoader;
Module *
ASTContext::getModule(ArrayRef<std::pair<Identifier, SourceLoc>> modulePath) {
assert(!modulePath.empty());
auto moduleID = modulePath[0];
// TODO: Swift submodules.
if (modulePath.size() == 1) {
if (Module *M = LoadedModules.lookup(moduleID.first.str()))
return M;
}
for (auto importer : Impl.ModuleLoaders) {
if (Module *M = importer->loadModule(moduleID.second, modulePath))
return M;
}
return nullptr;
}
ClangNode ASTContext::getClangNode(Decl *decl) {

View File

@@ -234,7 +234,8 @@ ArrayRef<ExtensionDecl*> Module::lookupExtensions(Type T) {
return Cache.getExtensions(T->getCanonicalType());
}
return Ctx.getModuleLoader().lookupExtensions(cast<ClangModule>(this), T);
assert(isa<ClangModule>(this));
return Ctx.getClangModuleLoader()->lookupExtensions(this, T);
}
//===----------------------------------------------------------------------===//
@@ -260,7 +261,8 @@ void Module::lookupValue(AccessPathTy AccessPath, Identifier Name,
.lookupValue(AccessPath, Name, LookupKind, *TU, Result);
}
return Ctx.getModuleLoader().lookupValue(cast<ClangModule>(this), AccessPath,
assert(isa<ClangModule>(this));
return Ctx.getClangModuleLoader()->lookupValue(this, AccessPath,
Name, LookupKind, Result);
}

View File

@@ -90,6 +90,11 @@ ClangImporter *ClangImporter::create(ASTContext &ctx, StringRef sdkroot,
auto clangDiags(CompilerInstance::createDiagnostics(
new clang::DiagnosticOptions, 0, nullptr));
// Don't stop emitting messages if we ever can't find a file.
// FIXME: This is actually a general problem: any "fatal" error could mess up
// the CompilerInvocation.
clangDiags->setDiagnosticErrorAsFatal(clang::diag::err_module_not_found,
false);
// Construct the invocation arguments for Objective-C ARC with the current
// target.
@@ -245,6 +250,9 @@ Module *ClangImporter::loadModule(
// FIXME: The source location here is completely bogus. It can't be
// invalid, and it can't be the same thing twice in a row, so we just use
// a counter. Having real source locations would be far, far better.
// FIXME: This should not print a message if we just can't find a Clang
// module -- that's Swift's responsibility, since there could in theory be a
// later module loader.
auto &srcMgr = clangContext.getSourceManager();
clang::SourceLocation clangImportLoc
= srcMgr.getLocForStartOfFile(srcMgr.getMainFileID())

View File

@@ -2,6 +2,7 @@ add_swift_library(swiftSema
ArchetypeBuilder.cpp
CaptureAnalysis.cpp
NameBinding.cpp
SourceLoader.cpp
TypeChecker.cpp
TypeCheckCoercion.cpp
TypeCheckConstraints.cpp

View File

@@ -77,72 +77,24 @@ namespace {
/// fills in the Components.
bool resolveIdentifierType(IdentifierType *DNT, DeclContext *DC);
llvm::error_code findModule(StringRef Module,
SourceLoc ImportLoc,
llvm::OwningPtr<llvm::MemoryBuffer> &Buffer);
private:
/// getModule - Load a module referenced by an import statement,
/// emitting an error at the specified location and returning null on
/// failure.
/// Load a module referenced by an import statement.
///
/// Returns null if no module can be loaded.
Module *getModule(llvm::ArrayRef<std::pair<Identifier,SourceLoc>> ModuleID);
};
}
llvm::error_code NameBinder::findModule(StringRef Module,
SourceLoc ImportLoc,
llvm::OwningPtr<llvm::MemoryBuffer> &Buffer) {
std::string ModuleFilename = Module.str() + std::string(".swift");
llvm::SmallString<128> InputFilename;
// First, search in the directory corresponding to the import location.
// FIXME: This screams for a proper FileManager abstraction.
llvm::SourceMgr &SourceMgr = Context.SourceMgr;
int CurrentBufferID = SourceMgr.FindBufferContainingLoc(ImportLoc.Value);
if (CurrentBufferID >= 0) {
const llvm::MemoryBuffer *ImportingBuffer
= SourceMgr.getBufferInfo(CurrentBufferID).Buffer;
StringRef CurrentDirectory
= llvm::sys::path::parent_path(ImportingBuffer->getBufferIdentifier());
if (!CurrentDirectory.empty()) {
InputFilename = CurrentDirectory;
llvm::sys::path::append(InputFilename, ModuleFilename);
llvm::error_code Err = llvm::MemoryBuffer::getFile(InputFilename, Buffer);
if (!Err)
return Err;
}
}
// Second, search in the current directory.
llvm::error_code Err = llvm::MemoryBuffer::getFile(ModuleFilename, Buffer);
if (!Err)
return Err;
// If we fail, search each import search path.
for (auto Path : Context.ImportSearchPaths) {
InputFilename = Path;
llvm::sys::path::append(InputFilename, ModuleFilename);
Err = llvm::MemoryBuffer::getFile(InputFilename, Buffer);
if (!Err)
return Err;
}
return Err;
}
Module *NameBinder::getModule(
ArrayRef<std::pair<Identifier, SourceLoc>> ModulePath) {
// TODO: Swift submodules.
assert(ModulePath.size() >= 1 && "empty import path");
auto ModuleID = ModulePath[0];
Module *
NameBinder::getModule(ArrayRef<std::pair<Identifier, SourceLoc>> modulePath) {
assert(!modulePath.empty());
auto moduleID = modulePath[0];
// TODO: We currently just recursively parse referenced modules. This works
// fine for now since they are each a single file. Ultimately we'll want a
// compiled form of AST's like clang's that support lazy deserialization.
// FIXME: We shouldn't really allow arbitrary modules to import Builtin.
if (ModuleID.first.str() == "Builtin") {
if (moduleID.first.str() == "Builtin") {
ImportedBuiltinModule = true;
return TU->Ctx.TheBuiltinModule;
}
@@ -150,84 +102,32 @@ Module *NameBinder::getModule(
// If the imported module name is the same as the current translation unit,
// skip the Swift module loader and use the Clang module loader instead.
// This allows a Swift module to extend a Clang module of the same name.
bool useClangModule = false;
if (ModuleID.first == TU->Name && Context.hasModuleLoader())
useClangModule = true;
Module *M = nullptr;
// FIXME: For now, ignore submodule paths except for Clang modules.
if (ModulePath.size() > 1 && Context.hasModuleLoader()) {
useClangModule = true;
} else {
M = Context.LoadedModules.lookup(ModuleID.first.str());
if (M && !(useClangModule && !isa<ClangModule>(M)))
return M;
if (moduleID.first == TU->Name && modulePath.size() == 1) {
if (auto importer = Context.getClangModuleLoader())
return importer->loadModule(moduleID.second, modulePath);
return nullptr;
}
// Open the input file.
llvm::OwningPtr<llvm::MemoryBuffer> InputFile;
if (!useClangModule) {
if (llvm::error_code Err = findModule(ModuleID.first.str(), ModuleID.second,
InputFile)) {
if (Err.value() != llvm::errc::no_such_file_or_directory ||
!Context.hasModuleLoader()) {
diagnose(ModuleID.second, diag::sema_opening_import,
ModuleID.first.str(), Err.message());
return 0;
}
// There was no Swift module with this name, so try a Clang module.
useClangModule = true;
}
}
if (useClangModule) {
// FIXME: We're assuming that 'externally loaded module' == 'clang module',
// which is clearly nonsense. We almost certainy want to have a chain
// of external module loaders, with the Swift one first and the Clang one
// as a fallback.
// FIXME: Bad location info?
return Context.getModuleLoader().loadModule(ModuleID.second,
ModulePath);
}
unsigned BufferID =
Context.SourceMgr.AddNewSourceBuffer(InputFile.take(),
ModuleID.second.Value);
// FIXME: Turn off the constraint-based type checker for the imported 'swift'
// module.
llvm::SaveAndRestore<bool> saveUseCS(Context.LangOpts.UseConstraintSolver,
(Context.LangOpts.UseConstraintSolver &&
ModuleID.first.str() != "swift"));
// For now, treat all separate modules as unique components.
Component *Comp = new (Context.Allocate<Component>(1)) Component();
TranslationUnit *ImportedTU;
ImportedTU = new (Context) TranslationUnit(ModuleID.first, Comp, Context,
/*IsMainModule*/false,
/*IsReplModule*/false);
Context.LoadedModules[ModuleID.first.str()] = ImportedTU;
parseIntoTranslationUnit(ImportedTU, BufferID);
// We have to do name binding on it to ensure that types are fully resolved.
// This should eventually be eliminated by having actual fully resolved binary
// dumps of the code instead of reparsing though.
// FIXME: We also need to deal with circular imports!
performNameBinding(ImportedTU);
performTypeChecking(ImportedTU);
return ImportedTU;
return Context.getModule(modulePath);
}
void NameBinder::addImport(ImportDecl *ID,
SmallVectorImpl<ImportedModule> &Result) {
ArrayRef<ImportDecl::AccessPathElement> Path = ID->getAccessPath();
Module *M = getModule(Path);
if (M == 0) return;
// FIXME: This is a hack to allow /either/ Clang submodules /or/ importing
// declarations from within Swift translation units...but not both. We may
// need to design this carefully: what does "Foundation.NSString" refer to?
auto importPath = Path;
if (!Context.getClangModuleLoader())
importPath = importPath.slice(0, 1);
Module *M = getModule(importPath);
if (M == 0) {
diagnose(Path[0].second, diag::sema_no_import, Path[0].first.str());
return;
}
// FIXME: Validate the access path against the module. Reject things like
// import swift.aslkdfja
@@ -242,7 +142,7 @@ void NameBinder::addImport(ImportDecl *ID,
// module with the same name, add that Clang module to our own set of imports.
// FIXME: This is a horrible, horrible way to provide transitive inclusion.
// We really need a re-export syntax.
if (Context.hasModuleLoader()) {
if (Context.getClangModuleLoader()) {
if (auto tu = dyn_cast<TranslationUnit>(M)) {
for (auto imported : tu->getImportedModules()) {
// Only check for Clang modules.
@@ -570,6 +470,7 @@ void swift::performNameBinding(TranslationUnit *TU, unsigned StartElem) {
visited);
}
ASTContext &ctx = TU->getASTContext();
llvm::DenseMap<clang::Module *, bool> topModules;
for (auto clangImport : importedClangModules) {
// Look at the top-level module.
@@ -579,13 +480,15 @@ void swift::performNameBinding(TranslationUnit *TU, unsigned StartElem) {
if (knownTop == topModules.end()) {
// If we haven't looked for a Swift module corresponding to the
// top-level module yet, do so now.
llvm::OwningPtr<llvm::MemoryBuffer> buffer; // FIXME: wasteful!
if (Binder.findModule(topClangMod->Name, clangImport.second, buffer)) {
hasSwiftModule = false;
} else {
hasSwiftModule = true;
}
ImportDecl::AccessPathElement importPair =
{ ctx.getIdentifier(topClangMod->Name), clangImport.second };
// It's a little wasteful to load the module here and then again below,
// but the one below should pick up the same (already-loaded) module.
auto module = ctx.getModule(importPair);
hasSwiftModule = !isa<ClangModule>(module);
topModules[topClangMod] = hasSwiftModule;
} else {
hasSwiftModule = knownTop->second;
@@ -596,9 +499,8 @@ void swift::performNameBinding(TranslationUnit *TU, unsigned StartElem) {
// Form an implicit import of the Swift module.
SmallVector<ImportDecl::AccessPathElement, 4> accessPath;
ASTContext &context = TU->getASTContext();
for (auto mod = clangImport.first; mod; mod = mod->Parent) {
accessPath.push_back({ context.getIdentifier(mod->Name),
accessPath.push_back({ ctx.getIdentifier(mod->Name),
clangImport.second });
}
std::reverse(accessPath.begin(), accessPath.end());
@@ -606,7 +508,7 @@ void swift::performNameBinding(TranslationUnit *TU, unsigned StartElem) {
// Create an implicit import declaration and import it.
// FIXME: Mark as implicit!
// FIXME: Actually pass through the whole access path.
auto import = ImportDecl::create(context, TU, clangImport.second,
auto import = ImportDecl::create(ctx, TU, clangImport.second,
accessPath[0]);
TU->Decls.push_back(import);
Binder.addImport(import, ImportedModules);

123
lib/Sema/SourceLoader.cpp Normal file
View File

@@ -0,0 +1,123 @@
//===--- SourceLoader.cpp - Import .swift files as modules ------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file A simple module loader that loads .swift source files as
/// TranslationUnit modules.
///
//===----------------------------------------------------------------------===//
#include "swift/Sema/SourceLoader.h"
#include "swift/Subsystems.h"
#include "swift/AST/AST.h"
#include "swift/AST/Component.h"
#include "swift/AST/Diagnostics.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/system_error.h"
using namespace swift;
static llvm::error_code findModule(ASTContext &ctx, StringRef moduleID,
SourceLoc importLoc,
llvm::OwningPtr<llvm::MemoryBuffer> &buffer){
llvm::SmallString<64> moduleFilename(moduleID);
moduleFilename += ".swift";
llvm::SmallString<128> inputFilename;
// First, search in the directory corresponding to the import location.
// FIXME: This screams for a proper FileManager abstraction.
int currentBufferID = ctx.SourceMgr.FindBufferContainingLoc(importLoc.Value);
if (currentBufferID >= 0) {
const llvm::MemoryBuffer *importingBuffer
= ctx.SourceMgr.getBufferInfo(currentBufferID).Buffer;
StringRef currentDirectory
= llvm::sys::path::parent_path(importingBuffer->getBufferIdentifier());
if (!currentDirectory.empty()) {
inputFilename = currentDirectory;
llvm::sys::path::append(inputFilename, moduleFilename.str());
llvm::error_code err = llvm::MemoryBuffer::getFile(inputFilename, buffer);
if (!err)
return err;
}
}
// Second, search in the current directory.
llvm::error_code err = llvm::MemoryBuffer::getFile(moduleFilename, buffer);
if (!err)
return err;
// If we fail, search each import search path.
for (auto Path : ctx.ImportSearchPaths) {
inputFilename = Path;
llvm::sys::path::append(inputFilename, moduleFilename.str());
err = llvm::MemoryBuffer::getFile(inputFilename, buffer);
if (!err)
return err;
}
return err;
}
Module *SourceLoader::loadModule(
SourceLoc importLoc,
ArrayRef<std::pair<Identifier, SourceLoc>> path) {
// FIXME: Swift submodules?
if (path.size() > 1)
return nullptr;
auto moduleID = path[0];
llvm::OwningPtr<llvm::MemoryBuffer> inputFile;
if (llvm::error_code err = findModule(Ctx, moduleID.first.str(),
moduleID.second, inputFile)) {
if (err.value() != llvm::errc::no_such_file_or_directory) {
Ctx.Diags.diagnose(moduleID.second, diag::sema_opening_import,
moduleID.first.str(), err.message());
}
return nullptr;
}
unsigned bufferID = Ctx.SourceMgr.AddNewSourceBuffer(inputFile.take(),
moduleID.second.Value);
// FIXME: Turn off the constraint-based type checker for the imported 'swift'
// module.
llvm::SaveAndRestore<bool> saveUseCS(Ctx.LangOpts.UseConstraintSolver,
(Ctx.LangOpts.UseConstraintSolver &&
moduleID.first.str() != "swift"));
// For now, treat all separate modules as unique components.
Component *comp = new (Ctx.Allocate<Component>(1)) Component();
TranslationUnit *importTU = new (Ctx) TranslationUnit(moduleID.first, comp,
Ctx,
/*IsMainModule*/false,
/*IsReplModule*/false);
Ctx.LoadedModules[moduleID.first.str()] = importTU;
parseIntoTranslationUnit(importTU, bufferID);
// We have to do name binding on it to ensure that types are fully resolved.
// This should eventually be eliminated by having actual fully resolved binary
// dumps of the code instead of reparsing though.
// FIXME: We also need to deal with circular imports!
performNameBinding(importTU);
performTypeChecking(importTU);
return importTU;
}

View File

@@ -337,13 +337,10 @@ public:
// TODO: Integrate Clang LookupVisibleDecls with Swift LookupVisibleDecls.
// Doing so now makes REPL interaction too slow.
if (!context.getDeclContext()->getASTContext().hasModuleLoader())
return;
ASTContext &ast = context.getDeclContext()->getASTContext();
if (auto clangImporter = ast.getClangModuleLoader())
static_cast<ClangImporter&>(*clangImporter).lookupVisibleDecls(*this);
ClangImporter &clangImporter
= static_cast<ClangImporter&>(
context.getDeclContext()->getASTContext().getModuleLoader());
clangImporter.lookupVisibleDecls(*this);
} else {
lookupVisibleDecls(*this, context.getBaseType());