Files
swift-mirror/lib/Frontend/Frontend.cpp
Doug Gregor a6f46584ec [Evaluator] Indirect evaluation of uncached requests through a separate table.
The bundling of the form of a request (e.g., the storage that makes up a request)
with the function that evaluates the request value requires us to perform
ad hoc indirection to address the AST —> Sema layering violation. For
example, ClassDecl::getSuperclass() calls through the LazyResolver (when
available) to form the appropriate request. This means that we cannot
use the the request-evaluator’s cache when LazyResolver is null, forcing
all cached state into the AST.

Provide the evaluator with a zone-based registration system, where each
request “zone” (e.g., the type checker’s requests) registers
callbacks to evaluate each kind of request within that zone. The
evaluator indirects through this table of function pointers, allowing
the request classes themselves to be available at a lower level (AST)
than the functions that perform the computation when the value isn’t
in the cache (e.g., Sema).

We are not taking advantage of the indirection yet; that’ll come in a
follow-up commit.
2018-06-29 15:41:55 -07:00

916 lines
33 KiB
C++

//===--- Frontend.cpp - frontend utility methods --------------------------===//
//
// 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 contains utility methods for parsing and performing semantic
// on modules.
//
//===----------------------------------------------------------------------===//
#include "swift/Frontend/Frontend.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Module.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/Parse/DelayedParsingCallbacks.h"
#include "swift/Parse/Lexer.h"
#include "swift/SIL/SILModule.h"
#include "swift/Serialization/SerializedModuleLoader.h"
#include "swift/Strings.h"
#include "swift/Subsystems.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
using namespace swift;
CompilerInstance::CompilerInstance() = default;
CompilerInstance::~CompilerInstance() = default;
std::string CompilerInvocation::getPCHHash() const {
using llvm::hash_code;
using llvm::hash_value;
using llvm::hash_combine;
auto Code = hash_value(LangOpts.getPCHHashComponents());
Code = hash_combine(Code, FrontendOpts.getPCHHashComponents());
Code = hash_combine(Code, ClangImporterOpts.getPCHHashComponents());
Code = hash_combine(Code, SearchPathOpts.getPCHHashComponents());
Code = hash_combine(Code, DiagnosticOpts.getPCHHashComponents());
Code = hash_combine(Code, SILOpts.getPCHHashComponents());
Code = hash_combine(Code, IRGenOpts.getPCHHashComponents());
return llvm::APInt(64, Code).toString(36, /*Signed=*/false);
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForAtMostOnePrimary() const {
return getFrontendOptions().getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForPrimary(
StringRef filename) const {
return getFrontendOptions().getPrimarySpecificPathsForPrimary(filename);
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForSourceFile(
const SourceFile &SF) const {
return getPrimarySpecificPathsForPrimary(SF.getFilename());
}
std::string CompilerInvocation::getOutputFilenameForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary().OutputFilename;
}
std::string
CompilerInvocation::getMainInputFilenameForDebugInfoForAtMostOnePrimary()
const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.MainInputFilenameForDebugInfo;
}
std::string
CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ObjCHeaderOutputPath;
}
std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ModuleOutputPath;
}
std::string CompilerInvocation::getReferenceDependenciesFilePathForPrimary(
StringRef filename) const {
return getPrimarySpecificPathsForPrimary(filename)
.SupplementaryOutputs.ReferenceDependenciesFilePath;
}
std::string
CompilerInvocation::getSerializedDiagnosticsPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.SerializedDiagnosticsPath;
}
std::string CompilerInvocation::getTBDPathForWholeModule() const {
assert(getFrontendOptions().InputsAndOutputs.isWholeModule() &&
"TBDPath only makes sense in WMO mode");
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.TBDPath;
}
void CompilerInstance::createSILModule() {
assert(MainModule && "main module not created yet");
// Assume WMO if a -primary-file option was not provided.
TheSILModule = SILModule::createEmptyModule(
getMainModule(), Invocation.getSILOptions(),
Invocation.getFrontendOptions().InputsAndOutputs.isWholeModule());
}
void CompilerInstance::setSILModule(std::unique_ptr<SILModule> M) {
TheSILModule = std::move(M);
}
void CompilerInstance::recordPrimaryInputBuffer(unsigned BufID) {
PrimaryBufferIDs.insert(BufID);
}
void CompilerInstance::recordPrimarySourceFile(SourceFile *SF) {
assert(MainModule && "main module not created yet");
PrimarySourceFiles.push_back(SF);
SF->createReferencedNameTracker();
if (SF->getBufferID().hasValue())
recordPrimaryInputBuffer(SF->getBufferID().getValue());
}
bool CompilerInstance::setup(const CompilerInvocation &Invok) {
Invocation = Invok;
setUpLLVMArguments();
setUpDiagnosticOptions();
// If we are asked to emit a module documentation file, configure lexing and
// parsing to remember comments.
if (Invocation.getFrontendOptions().InputsAndOutputs.hasModuleDocOutputPath())
Invocation.getLangOptions().AttachCommentsToDecls = true;
// If we are doing index-while-building, configure lexing and parsing to
// remember comments.
if (!Invocation.getFrontendOptions().IndexStorePath.empty()) {
Invocation.getLangOptions().AttachCommentsToDecls = true;
}
Context.reset(ASTContext::get(Invocation.getLangOptions(),
Invocation.getSearchPathOptions(), SourceMgr,
Diagnostics));
registerTypeCheckerRequestFunctions(Context->evaluator);
if (setUpModuleLoaders())
return true;
assert(Lexer::isIdentifier(Invocation.getModuleName()));
if (isInSILMode())
Invocation.getLangOptions().EnableAccessControl = false;
return setUpInputs();
}
void CompilerInstance::setUpLLVMArguments() {
// Honor -Xllvm.
if (!Invocation.getFrontendOptions().LLVMArgs.empty()) {
llvm::SmallVector<const char *, 4> Args;
Args.push_back("swift (LLVM option parsing)");
for (unsigned i = 0, e = Invocation.getFrontendOptions().LLVMArgs.size();
i != e; ++i)
Args.push_back(Invocation.getFrontendOptions().LLVMArgs[i].c_str());
Args.push_back(nullptr);
llvm::cl::ParseCommandLineOptions(Args.size()-1, Args.data());
}
}
void CompilerInstance::setUpDiagnosticOptions() {
if (Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
Diagnostics.setShowDiagnosticsAfterFatalError();
}
if (Invocation.getDiagnosticOptions().SuppressWarnings) {
Diagnostics.setSuppressWarnings(true);
}
if (Invocation.getDiagnosticOptions().WarningsAsErrors) {
Diagnostics.setWarningsAsErrors(true);
}
}
bool CompilerInstance::setUpModuleLoaders() {
if (hasSourceImport()) {
bool immediate = FrontendOptions::isActionImmediate(
Invocation.getFrontendOptions().RequestedAction);
bool enableResilience = Invocation.getFrontendOptions().EnableResilience;
Context->addModuleLoader(SourceLoader::create(*Context,
!immediate,
enableResilience,
getDependencyTracker()));
}
{
auto SML = SerializedModuleLoader::create(*Context, getDependencyTracker());
this->SML = SML.get();
Context->addModuleLoader(std::move(SML));
}
{
// Wire up the Clang importer. If the user has specified an SDK, use it.
// Otherwise, we just keep it around as our interface to Clang's ABI
// knowledge.
auto clangImporter =
ClangImporter::create(*Context, Invocation.getClangImporterOptions(),
Invocation.getPCHHash(), getDependencyTracker());
if (!clangImporter) {
Diagnostics.diagnose(SourceLoc(), diag::error_clang_importer_create_fail);
return true;
}
Context->addModuleLoader(std::move(clangImporter), /*isClang*/ true);
}
return false;
}
Optional<unsigned> CompilerInstance::setUpCodeCompletionBuffer() {
Optional<unsigned> codeCompletionBufferID;
auto codeCompletePoint = Invocation.getCodeCompletionPoint();
if (codeCompletePoint.first) {
auto memBuf = codeCompletePoint.first;
// CompilerInvocation doesn't own the buffers, copy to a new buffer.
codeCompletionBufferID = SourceMgr.addMemBufferCopy(memBuf);
InputSourceCodeBufferIDs.push_back(*codeCompletionBufferID);
SourceMgr.setCodeCompletionPoint(*codeCompletionBufferID,
codeCompletePoint.second);
}
return codeCompletionBufferID;
}
bool CompilerInstance::setUpInputs() {
// Adds to InputSourceCodeBufferIDs, so may need to happen before the
// per-input setup.
const Optional<unsigned> codeCompletionBufferID = setUpCodeCompletionBuffer();
for (const InputFile &input :
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs())
if (setUpForInput(input))
return true;
// Set the primary file to the code-completion point if one exists.
if (codeCompletionBufferID.hasValue() &&
!isPrimaryInput(*codeCompletionBufferID)) {
assert(PrimaryBufferIDs.empty() && "re-setting PrimaryBufferID");
recordPrimaryInputBuffer(*codeCompletionBufferID);
}
if (isInputSwift() && MainBufferID == NO_SUCH_BUFFER &&
InputSourceCodeBufferIDs.size() == 1)
MainBufferID = InputSourceCodeBufferIDs.front();
return false;
}
bool CompilerInstance::setUpForInput(const InputFile &input) {
bool failed = false;
Optional<unsigned> bufferID = getRecordedBufferID(input, failed);
if (failed)
return true;
if (!bufferID)
return false;
if (isInSILMode() ||
(isInputSwift() &&
llvm::sys::path::filename(input.file()) == "main.swift")) {
assert(MainBufferID == NO_SUCH_BUFFER && "re-setting MainBufferID");
MainBufferID = *bufferID;
}
if (input.isPrimary()) {
recordPrimaryInputBuffer(*bufferID);
}
return false;
}
Optional<unsigned> CompilerInstance::getRecordedBufferID(const InputFile &input,
bool &failed) {
if (!input.buffer()) {
if (Optional<unsigned> existingBufferID =
SourceMgr.getIDForBufferIdentifier(input.file())) {
return existingBufferID;
}
}
std::pair<std::unique_ptr<llvm::MemoryBuffer>,
std::unique_ptr<llvm::MemoryBuffer>>
buffers = getInputBufferAndModuleDocBufferIfPresent(input);
if (!buffers.first) {
failed = true;
return None;
}
// FIXME: The fact that this test happens twice, for some cases,
// suggests that setupInputs could use another round of refactoring.
if (serialization::isSerializedAST(buffers.first->getBuffer())) {
PartialModules.push_back(
{std::move(buffers.first), std::move(buffers.second)});
return None;
}
assert(buffers.second.get() == nullptr);
// Transfer ownership of the MemoryBuffer to the SourceMgr.
unsigned bufferID = SourceMgr.addNewSourceBuffer(std::move(buffers.first));
InputSourceCodeBufferIDs.push_back(bufferID);
return bufferID;
}
std::pair<std::unique_ptr<llvm::MemoryBuffer>,
std::unique_ptr<llvm::MemoryBuffer>>
CompilerInstance::getInputBufferAndModuleDocBufferIfPresent(
const InputFile &input) {
if (auto b = input.buffer()) {
return std::make_pair(llvm::MemoryBuffer::getMemBufferCopy(
b->getBuffer(), b->getBufferIdentifier()),
nullptr);
}
// FIXME: Working with filenames is fragile, maybe use the real path
// or have some kind of FileManager.
using FileOrError = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>;
FileOrError inputFileOrErr = llvm::MemoryBuffer::getFileOrSTDIN(input.file());
if (!inputFileOrErr) {
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file, input.file(),
inputFileOrErr.getError().message());
return std::make_pair(nullptr, nullptr);
}
if (!serialization::isSerializedAST((*inputFileOrErr)->getBuffer()))
return std::make_pair(std::move(*inputFileOrErr), nullptr);
if (Optional<std::unique_ptr<llvm::MemoryBuffer>> moduleDocBuffer =
openModuleDoc(input)) {
return std::make_pair(std::move(*inputFileOrErr),
std::move(*moduleDocBuffer));
}
return std::make_pair(nullptr, nullptr);
}
Optional<std::unique_ptr<llvm::MemoryBuffer>>
CompilerInstance::openModuleDoc(const InputFile &input) {
llvm::SmallString<128> moduleDocFilePath(input.file());
llvm::sys::path::replace_extension(moduleDocFilePath,
SERIALIZED_MODULE_DOC_EXTENSION);
using FileOrError = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>;
FileOrError moduleDocFileOrErr =
llvm::MemoryBuffer::getFileOrSTDIN(moduleDocFilePath);
if (moduleDocFileOrErr)
return std::move(*moduleDocFileOrErr);
if (moduleDocFileOrErr.getError() == std::errc::no_such_file_or_directory)
return std::unique_ptr<llvm::MemoryBuffer>();
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file,
moduleDocFilePath,
moduleDocFileOrErr.getError().message());
return None;
}
ModuleDecl *CompilerInstance::getMainModule() {
if (!MainModule) {
Identifier ID = Context->getIdentifier(Invocation.getModuleName());
MainModule = ModuleDecl::create(ID, *Context);
if (Invocation.getFrontendOptions().EnableTesting)
MainModule->setTestingEnabled();
if (Invocation.getFrontendOptions().EnableResilience)
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
}
return MainModule;
}
static void addAdditionalInitialImportsTo(
SourceFile *SF, const CompilerInstance::ImplicitImports &implicitImports) {
using ImportPair =
std::pair<ModuleDecl::ImportedModule, SourceFile::ImportOptions>;
SmallVector<ImportPair, 4> additionalImports;
if (implicitImports.objCModuleUnderlyingMixedFramework)
additionalImports.push_back(
{{/*accessPath=*/{},
implicitImports.objCModuleUnderlyingMixedFramework},
SourceFile::ImportFlags::Exported});
if (implicitImports.headerModule)
additionalImports.push_back(
{{/*accessPath=*/{}, implicitImports.headerModule},
SourceFile::ImportFlags::Exported});
if (!implicitImports.modules.empty()) {
for (auto &importModule : implicitImports.modules) {
additionalImports.push_back({{/*accessPath=*/{}, importModule}, {}});
}
}
SF->addImports(additionalImports);
}
static bool shouldImportSwiftOnoneModuleIfNoneOrImplicitOptimization(
FrontendOptions::ActionType RequestedAction) {
return RequestedAction == FrontendOptions::ActionType::EmitObject ||
RequestedAction == FrontendOptions::ActionType::Immediate ||
RequestedAction == FrontendOptions::ActionType::EmitSIL;
}
/// Implicitly import the SwiftOnoneSupport module in non-optimized
/// builds. This allows for use of popular specialized functions
/// from the standard library, which makes the non-optimized builds
/// execute much faster.
static bool
shouldImplicityImportSwiftOnoneSupportModule(CompilerInvocation &Invocation) {
if (Invocation.getImplicitModuleImportKind() !=
SourceFile::ImplicitModuleImportKind::Stdlib)
return false;
if (Invocation.getSILOptions().shouldOptimize())
return false;
if (shouldImportSwiftOnoneModuleIfNoneOrImplicitOptimization(
Invocation.getFrontendOptions().RequestedAction)) {
return true;
}
return Invocation.getFrontendOptions().isCreatingSIL();
}
void CompilerInstance::performSema() {
FrontendStatsTracer tracer(Context->Stats, "perform-sema");
Context->LoadedModules[MainModule->getName()] = getMainModule();
if (Invocation.getInputKind() == InputFileKind::IFK_SIL) {
assert(!InputSourceCodeBufferIDs.empty());
assert(InputSourceCodeBufferIDs.size() == 1);
assert(MainBufferID != NO_SUCH_BUFFER);
createSILModule();
}
if (Invocation.getImplicitModuleImportKind() ==
SourceFile::ImplicitModuleImportKind::Stdlib) {
if (!loadStdlib())
return;
}
if (shouldImplicityImportSwiftOnoneSupportModule(Invocation)) {
Invocation.getFrontendOptions().ImplicitImportModuleNames.push_back(
SWIFT_ONONE_SUPPORT);
}
const ImplicitImports implicitImports(*this);
if (Invocation.getInputKind() == InputFileKind::IFK_Swift_REPL) {
createREPLFile(implicitImports);
return;
}
// Make sure the main file is the first file in the module, so do this now.
if (MainBufferID != NO_SUCH_BUFFER)
addMainFileToModule(implicitImports);
parseAndCheckTypes(implicitImports);
}
CompilerInstance::ImplicitImports::ImplicitImports(CompilerInstance &compiler) {
kind = compiler.Invocation.getImplicitModuleImportKind();
objCModuleUnderlyingMixedFramework =
compiler.Invocation.getFrontendOptions().ImportUnderlyingModule
? compiler.importUnderlyingModule()
: nullptr;
compiler.getImplicitlyImportedModules(modules);
headerModule = compiler.importBridgingHeader();
}
bool CompilerInstance::loadStdlib() {
FrontendStatsTracer tracer(Context->Stats, "load-stdlib");
ModuleDecl *M = Context->getStdlibModule(true);
if (!M) {
Diagnostics.diagnose(SourceLoc(), diag::error_stdlib_not_found,
Invocation.getTargetTriple());
return false;
}
// If we failed to load, we should have already diagnosed
if (M->failedToLoad()) {
assert(Diagnostics.hadAnyError() &&
"Module failed to load but nothing was diagnosed?");
return false;
}
return true;
}
ModuleDecl *CompilerInstance::importUnderlyingModule() {
FrontendStatsTracer tracer(Context->Stats, "import-underlying-module");
ModuleDecl *objCModuleUnderlyingMixedFramework =
static_cast<ClangImporter *>(Context->getClangModuleLoader())
->loadModule(SourceLoc(),
std::make_pair(MainModule->getName(), SourceLoc()));
if (objCModuleUnderlyingMixedFramework)
return objCModuleUnderlyingMixedFramework;
Diagnostics.diagnose(SourceLoc(), diag::error_underlying_module_not_found,
MainModule->getName());
return nullptr;
}
ModuleDecl *CompilerInstance::importBridgingHeader() {
FrontendStatsTracer tracer(Context->Stats, "import-bridging-header");
const StringRef implicitHeaderPath =
Invocation.getFrontendOptions().ImplicitObjCHeaderPath;
auto clangImporter =
static_cast<ClangImporter *>(Context->getClangModuleLoader());
if (implicitHeaderPath.empty() ||
clangImporter->importBridgingHeader(implicitHeaderPath, MainModule))
return nullptr;
ModuleDecl *importedHeaderModule = clangImporter->getImportedHeaderModule();
assert(importedHeaderModule);
return importedHeaderModule;
}
void CompilerInstance::getImplicitlyImportedModules(
SmallVectorImpl<ModuleDecl *> &importModules) {
FrontendStatsTracer tracer(Context->Stats, "get-implicitly-imported-modules");
for (auto &ImplicitImportModuleName :
Invocation.getFrontendOptions().ImplicitImportModuleNames) {
if (Lexer::isIdentifier(ImplicitImportModuleName)) {
auto moduleID = Context->getIdentifier(ImplicitImportModuleName);
ModuleDecl *importModule =
Context->getModule(std::make_pair(moduleID, SourceLoc()));
if (importModule) {
importModules.push_back(importModule);
} else {
Diagnostics.diagnose(SourceLoc(), diag::sema_no_import,
ImplicitImportModuleName);
if (Invocation.getSearchPathOptions().SDKPath.empty() &&
llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) {
Diagnostics.diagnose(SourceLoc(), diag::sema_no_import_no_sdk);
Diagnostics.diagnose(SourceLoc(), diag::sema_no_import_no_sdk_xcrun);
}
}
} else {
Diagnostics.diagnose(SourceLoc(), diag::error_bad_module_name,
ImplicitImportModuleName, false);
}
}
}
void CompilerInstance::createREPLFile(const ImplicitImports &implicitImports) {
auto *SingleInputFile = createSourceFileForMainModule(
Invocation.getSourceFileKind(), implicitImports.kind, None);
addAdditionalInitialImportsTo(SingleInputFile, implicitImports);
}
std::unique_ptr<DelayedParsingCallbacks>
CompilerInstance::computeDelayedParsingCallback(bool isPrimary) {
if (Invocation.isCodeCompletion())
return llvm::make_unique<CodeCompleteDelayedCallbacks>(
SourceMgr.getCodeCompletionLoc());
if (!isPrimary)
return llvm::make_unique<AlwaysDelayedCallbacks>();
return nullptr;
}
void CompilerInstance::addMainFileToModule(
const ImplicitImports &implicitImports) {
const InputFileKind Kind = Invocation.getInputKind();
assert(Kind == InputFileKind::IFK_Swift || Kind == InputFileKind::IFK_SIL);
if (Kind == InputFileKind::IFK_Swift)
SourceMgr.setHashbangBufferID(MainBufferID);
auto *MainFile = createSourceFileForMainModule(
Invocation.getSourceFileKind(), implicitImports.kind, MainBufferID);
addAdditionalInitialImportsTo(MainFile, implicitImports);
}
void CompilerInstance::parseAndCheckTypes(
const ImplicitImports &implicitImports) {
FrontendStatsTracer tracer(Context->Stats, "parse-and-check-types");
// Delayed parsing callback for the primary file, or all files
// in non-WMO mode.
std::unique_ptr<DelayedParsingCallbacks> PrimaryDelayedCB{
computeDelayedParsingCallback(true)};
// Delayed parsing callback for non-primary files. Not used in
// WMO mode.
std::unique_ptr<DelayedParsingCallbacks> SecondaryDelayedCB{
computeDelayedParsingCallback(false)};
PersistentParserState PersistentState;
bool hadLoadError = parsePartialModulesAndLibraryFiles(
implicitImports, PersistentState,
PrimaryDelayedCB.get(),
SecondaryDelayedCB.get());
if (Invocation.isCodeCompletion()) {
// When we are doing code completion, make sure to emit at least one
// diagnostic, so that ASTContext is marked as erroneous. In this case
// various parts of the compiler (for example, AST verifier) have less
// strict assumptions about the AST.
Diagnostics.diagnose(SourceLoc(), diag::error_doing_code_completion);
}
if (hadLoadError)
return;
OptionSet<TypeCheckingFlags> TypeCheckOptions = computeTypeCheckingOptions();
// Type-check main file after parsing all other files so that
// it can use declarations from other files.
// In addition, the main file has parsing and type-checking
// interwined.
if (MainBufferID != NO_SUCH_BUFFER) {
parseAndTypeCheckMainFile(PersistentState, PrimaryDelayedCB.get(),
TypeCheckOptions);
}
assert(llvm::all_of(MainModule->getFiles(), [](const FileUnit *File) -> bool {
auto *SF = dyn_cast<SourceFile>(File);
if (!SF)
return true;
return SF->ASTStage >= SourceFile::NameBound;
}) && "some files have not yet had their imports resolved");
MainModule->setHasResolvedImports();
const auto &options = Invocation.getFrontendOptions();
forEachFileToTypeCheck([&](SourceFile &SF) {
performTypeChecking(SF, PersistentState.getTopLevelContext(),
TypeCheckOptions, /*curElem*/ 0,
options.WarnLongFunctionBodies,
options.WarnLongExpressionTypeChecking,
options.SolverExpressionTimeThreshold,
options.SwitchCheckingInvocationThreshold);
});
// Even if there were no source files, we should still record known
// protocols.
if (auto *stdlib = Context->getStdlibModule())
Context->recordKnownProtocols(stdlib);
if (Invocation.isCodeCompletion()) {
performDelayedParsing(MainModule, PersistentState,
Invocation.getCodeCompletionFactory());
}
finishTypeChecking(TypeCheckOptions);
}
void CompilerInstance::parseLibraryFile(
unsigned BufferID, const ImplicitImports &implicitImports,
PersistentParserState &PersistentState,
DelayedParsingCallbacks *PrimaryDelayedCB,
DelayedParsingCallbacks *SecondaryDelayedCB) {
FrontendStatsTracer tracer(Context->Stats, "parse-library-file");
auto *NextInput = createSourceFileForMainModule(
SourceFileKind::Library, implicitImports.kind, BufferID);
addAdditionalInitialImportsTo(NextInput, implicitImports);
auto IsPrimary = isWholeModuleCompilation() || isPrimaryInput(BufferID);
auto *DelayedCB = IsPrimary ? PrimaryDelayedCB : SecondaryDelayedCB;
auto &Diags = NextInput->getASTContext().Diags;
auto DidSuppressWarnings = Diags.getSuppressWarnings();
Diags.setSuppressWarnings(DidSuppressWarnings || !IsPrimary);
bool Done;
do {
// Parser may stop at some erroneous constructions like #else, #endif
// or '}' in some cases, continue parsing until we are done
parseIntoSourceFile(*NextInput, BufferID, &Done, nullptr, &PersistentState,
DelayedCB);
} while (!Done);
Diags.setSuppressWarnings(DidSuppressWarnings);
performNameBinding(*NextInput);
}
OptionSet<TypeCheckingFlags> CompilerInstance::computeTypeCheckingOptions() {
OptionSet<TypeCheckingFlags> TypeCheckOptions;
if (isWholeModuleCompilation()) {
TypeCheckOptions |= TypeCheckingFlags::DelayWholeModuleChecking;
}
const auto &options = Invocation.getFrontendOptions();
if (options.DebugTimeFunctionBodies) {
TypeCheckOptions |= TypeCheckingFlags::DebugTimeFunctionBodies;
}
if (FrontendOptions::isActionImmediate(options.RequestedAction)) {
TypeCheckOptions |= TypeCheckingFlags::ForImmediateMode;
}
if (options.DebugTimeExpressionTypeChecking) {
TypeCheckOptions |= TypeCheckingFlags::DebugTimeExpressions;
}
return TypeCheckOptions;
}
bool CompilerInstance::parsePartialModulesAndLibraryFiles(
const ImplicitImports &implicitImports,
PersistentParserState &PersistentState,
DelayedParsingCallbacks *PrimaryDelayedCB,
DelayedParsingCallbacks *SecondaryDelayedCB) {
FrontendStatsTracer tracer(Context->Stats,
"parse-partial-modules-and-library-files");
bool hadLoadError = false;
// Parse all the partial modules first.
for (auto &PM : PartialModules) {
assert(PM.ModuleBuffer);
if (!SML->loadAST(*MainModule, SourceLoc(), std::move(PM.ModuleBuffer),
std::move(PM.ModuleDocBuffer)))
hadLoadError = true;
}
// Then parse all the library files.
for (auto BufferID : InputSourceCodeBufferIDs) {
if (BufferID != MainBufferID) {
parseLibraryFile(BufferID, implicitImports, PersistentState,
PrimaryDelayedCB, SecondaryDelayedCB);
}
}
return hadLoadError;
}
void CompilerInstance::parseAndTypeCheckMainFile(
PersistentParserState &PersistentState,
DelayedParsingCallbacks *DelayedParseCB,
OptionSet<TypeCheckingFlags> TypeCheckOptions) {
FrontendStatsTracer tracer(Context->Stats,
"parse-and-typecheck-main-file");
bool mainIsPrimary =
(isWholeModuleCompilation() || isPrimaryInput(MainBufferID));
SourceFile &MainFile =
MainModule->getMainSourceFile(Invocation.getSourceFileKind());
auto &Diags = MainFile.getASTContext().Diags;
auto DidSuppressWarnings = Diags.getSuppressWarnings();
Diags.setSuppressWarnings(DidSuppressWarnings || !mainIsPrimary);
SILParserState SILContext(TheSILModule.get());
unsigned CurTUElem = 0;
bool Done;
do {
// Pump the parser multiple times if necessary. It will return early
// after parsing any top level code in a main module, or in SIL mode when
// there are chunks of swift decls (e.g. imports and types) interspersed
// with 'sil' definitions.
parseIntoSourceFile(MainFile, MainFile.getBufferID().getValue(), &Done,
TheSILModule ? &SILContext : nullptr, &PersistentState,
DelayedParseCB);
if (mainIsPrimary) {
const auto &options = Invocation.getFrontendOptions();
performTypeChecking(MainFile, PersistentState.getTopLevelContext(),
TypeCheckOptions, CurTUElem,
options.WarnLongFunctionBodies,
options.WarnLongExpressionTypeChecking,
options.SolverExpressionTimeThreshold,
options.SwitchCheckingInvocationThreshold);
}
CurTUElem = MainFile.Decls.size();
} while (!Done);
Diags.setSuppressWarnings(DidSuppressWarnings);
if (mainIsPrimary && !Context->hadError() &&
Invocation.getFrontendOptions().DebuggerTestingTransform) {
performDebuggerTestingTransform(MainFile);
}
if (mainIsPrimary && !Context->hadError() &&
Invocation.getFrontendOptions().PCMacro) {
performPCMacro(MainFile, PersistentState.getTopLevelContext());
}
// Playground transform knows to look out for PCMacro's changes and not
// to playground log them.
if (mainIsPrimary && !Context->hadError() &&
Invocation.getFrontendOptions().PlaygroundTransform)
performPlaygroundTransform(
MainFile, Invocation.getFrontendOptions().PlaygroundHighPerformance);
if (!mainIsPrimary) {
performNameBinding(MainFile);
}
}
static void
forEachSourceFileIn(ModuleDecl *module,
llvm::function_ref<void(SourceFile &)> fn) {
for (auto fileName : module->getFiles()) {
if (auto SF = dyn_cast<SourceFile>(fileName))
fn(*SF);
}
}
void CompilerInstance::forEachFileToTypeCheck(
llvm::function_ref<void(SourceFile &)> fn) {
if (isWholeModuleCompilation()) {
forEachSourceFileIn(MainModule, [&](SourceFile &SF) { fn(SF); });
} else {
for (auto *SF : PrimarySourceFiles) {
fn(*SF);
}
}
}
void CompilerInstance::finishTypeChecking(
OptionSet<TypeCheckingFlags> TypeCheckOptions) {
if (TypeCheckOptions & TypeCheckingFlags::DelayWholeModuleChecking) {
forEachSourceFileIn(MainModule, [&](SourceFile &SF) {
performWholeModuleTypeChecking(SF);
});
}
}
SourceFile *CompilerInstance::createSourceFileForMainModule(
SourceFileKind fileKind, SourceFile::ImplicitModuleImportKind importKind,
Optional<unsigned> bufferID) {
ModuleDecl *mainModule = getMainModule();
SourceFile *inputFile = new (*Context)
SourceFile(*mainModule, fileKind, bufferID, importKind,
Invocation.getLangOptions().CollectParsedToken,
Invocation.getLangOptions().BuildSyntaxTree);
MainModule->addFile(*inputFile);
if (bufferID && isPrimaryInput(*bufferID)) {
recordPrimarySourceFile(inputFile);
}
return inputFile;
}
void CompilerInstance::performParseOnly(bool EvaluateConditionals) {
const InputFileKind Kind = Invocation.getInputKind();
ModuleDecl *const MainModule = getMainModule();
Context->LoadedModules[MainModule->getName()] = MainModule;
assert((Kind == InputFileKind::IFK_Swift ||
Kind == InputFileKind::IFK_Swift_Library) &&
"only supports parsing .swift files");
(void)Kind;
// Make sure the main file is the first file in the module but parse it last,
// to match the parsing logic used when performing Sema.
if (MainBufferID != NO_SUCH_BUFFER) {
assert(Kind == InputFileKind::IFK_Swift);
SourceMgr.setHashbangBufferID(MainBufferID);
createSourceFileForMainModule(Invocation.getSourceFileKind(),
SourceFile::ImplicitModuleImportKind::None,
MainBufferID);
}
PersistentParserState PersistentState;
PersistentState.PerformConditionEvaluation = EvaluateConditionals;
// Parse all the library files.
for (auto BufferID : InputSourceCodeBufferIDs) {
if (BufferID == MainBufferID)
continue;
SourceFile *NextInput = createSourceFileForMainModule(
SourceFileKind::Library, SourceFile::ImplicitModuleImportKind::None,
BufferID);
bool Done;
do {
// Parser may stop at some erroneous constructions like #else, #endif
// or '}' in some cases, continue parsing until we are done
parseIntoSourceFile(*NextInput, BufferID, &Done, nullptr,
&PersistentState, nullptr);
} while (!Done);
}
// Now parse the main file.
if (MainBufferID != NO_SUCH_BUFFER) {
SourceFile &MainFile =
MainModule->getMainSourceFile(Invocation.getSourceFileKind());
MainFile.SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache();
bool Done;
do {
parseIntoSourceFile(MainFile, MainFile.getBufferID().getValue(), &Done,
nullptr, &PersistentState, nullptr);
} while (!Done);
}
assert(Context->LoadedModules.size() == 1 &&
"Loaded a module during parse-only");
}
void CompilerInstance::freeASTContext() {
Context.reset();
MainModule = nullptr;
SML = nullptr;
PrimaryBufferIDs.clear();
PrimarySourceFiles.clear();
}
void CompilerInstance::freeSILModule() { TheSILModule.reset(); }
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForWholeModuleOptimizationMode()
const {
return getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForAtMostOnePrimary() const {
return Invocation.getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForPrimary(StringRef filename) const {
return Invocation.getPrimarySpecificPathsForPrimary(filename);
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForSourceFile(
const SourceFile &SF) const {
return Invocation.getPrimarySpecificPathsForSourceFile(SF);
}