Merge remote-tracking branch 'origin/main' into manually-merge-main-to-rebranch

Conflicts:
  - `include/swift/AST/PluginRegistry.h`
This commit is contained in:
Ben Barham
2024-06-27 14:56:11 -07:00
181 changed files with 3845 additions and 1645 deletions

View File

@@ -40,7 +40,6 @@
#include "swift/Basic/Edit.h"
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/ParseableOutput.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/Basic/SourceManager.h"
@@ -50,15 +49,13 @@
#include "swift/Basic/Version.h"
#include "swift/ConstExtract/ConstExtract.h"
#include "swift/DependencyScan/ScanDependencies.h"
#include "swift/Frontend/AccumulatingDiagnosticConsumer.h"
#include "swift/Frontend/CachedDiagnostics.h"
#include "swift/Frontend/CachingUtils.h"
#include "swift/Frontend/CompileJobCacheKey.h"
#include "swift/Frontend/DiagnosticHelper.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/ModuleInterfaceLoader.h"
#include "swift/Frontend/ModuleInterfaceSupport.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/Frontend/SerializedDiagnosticConsumer.h"
#include "swift/IRGen/TBDGen.h"
#include "swift/Immediate/Immediate.h"
#include "swift/Index/IndexRecord.h"
@@ -82,15 +79,15 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Option/Option.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualOutputBackend.h"
#include "llvm/Support/VirtualOutputBackends.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/FileSystem.h"
#if __has_include(<unistd.h>)
#include <unistd.h>
@@ -103,7 +100,6 @@
#include <utility>
using namespace swift;
using namespace swift::parseable_output;
static std::string displayName(StringRef MainExecutablePath) {
std::string Name = llvm::sys::path::stem(MainExecutablePath).str();
@@ -207,56 +203,6 @@ printModuleInterfaceIfNeeded(llvm::vfs::OutputBackend &outputBackend,
});
}
namespace {
/// If there is an error with fixits it writes the fixits as edits in json
/// format.
class JSONFixitWriter
: public DiagnosticConsumer, public migrator::FixitFilter {
std::string FixitsOutputPath;
std::unique_ptr<llvm::raw_ostream> OSPtr;
bool FixitAll;
SourceEdits AllEdits;
public:
JSONFixitWriter(std::string fixitsOutputPath,
const DiagnosticOptions &DiagOpts)
: FixitsOutputPath(std::move(fixitsOutputPath)),
FixitAll(DiagOpts.FixitCodeForAllDiagnostics) {}
private:
void handleDiagnostic(SourceManager &SM,
const DiagnosticInfo &Info) override {
if (!(FixitAll || shouldTakeFixit(Info)))
return;
for (const auto &Fix : Info.FixIts)
AllEdits.addEdit(SM, Fix.getRange(), Fix.getText());
}
bool finishProcessing() override {
std::error_code EC;
std::unique_ptr<llvm::raw_fd_ostream> OS;
OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath,
EC,
llvm::sys::fs::OF_None));
if (EC) {
// Create a temporary diagnostics engine to print the error to stderr.
SourceManager dummyMgr;
DiagnosticEngine DE(dummyMgr);
PrintingDiagnosticConsumer PDC;
DE.addConsumer(PDC);
DE.diagnose(SourceLoc(), diag::cannot_open_file,
FixitsOutputPath, EC.message());
return true;
}
swift::writeEditsInJson(AllEdits, *OS);
return false;
}
};
} // anonymous namespace
// This is a separate function so that it shows up in stack traces.
LLVM_ATTRIBUTE_NOINLINE
static void debugFailWithAssertion() {
@@ -574,108 +520,6 @@ static bool emitReferenceDependencies(CompilerInstance &Instance,
});
}
static const char *
mapFrontendInvocationToAction(const CompilerInvocation &Invocation) {
FrontendOptions::ActionType ActionType =
Invocation.getFrontendOptions().RequestedAction;
switch (ActionType) {
case FrontendOptions::ActionType::REPL:
return "repl";
case FrontendOptions::ActionType::MergeModules:
return "merge-module";
case FrontendOptions::ActionType::Immediate:
return "interpret";
case FrontendOptions::ActionType::TypecheckModuleFromInterface:
return "verify-module-interface";
case FrontendOptions::ActionType::EmitPCH:
return "generate-pch";
case FrontendOptions::ActionType::EmitIR:
case FrontendOptions::ActionType::EmitBC:
case FrontendOptions::ActionType::EmitAssembly:
case FrontendOptions::ActionType::EmitObject:
// Whether or not these actions correspond to a "compile" job or a
// "backend" job, depends on the input kind.
if (Invocation.getFrontendOptions().InputsAndOutputs.shouldTreatAsLLVM())
return "backend";
else
return "compile";
case FrontendOptions::ActionType::EmitModuleOnly:
return "emit-module";
default:
return "compile";
}
// The following Driver/Parseable-output actions do not correspond to
// possible Frontend invocations:
// ModuleWrapJob, AutolinkExtractJob, GenerateDSYMJob, VerifyDebugInfoJob,
// StaticLinkJob, DynamicLinkJob
}
// TODO: Apply elsewhere in the compiler
static swift::file_types::ID computeFileTypeForPath(const StringRef Path) {
if (!llvm::sys::path::has_extension(Path))
return swift::file_types::ID::TY_INVALID;
auto Extension = llvm::sys::path::extension(Path).str();
auto FileType = file_types::lookupTypeForExtension(Extension);
if (FileType == swift::file_types::ID::TY_INVALID) {
auto PathStem = llvm::sys::path::stem(Path);
// If this path has a multiple '.' extension (e.g. .abi.json),
// then iterate over all preceeding possible extension variants.
while (llvm::sys::path::has_extension(PathStem)) {
auto NextExtension = llvm::sys::path::extension(PathStem);
PathStem = llvm::sys::path::stem(PathStem);
Extension = NextExtension.str() + Extension;
FileType = file_types::lookupTypeForExtension(Extension);
if (FileType != swift::file_types::ID::TY_INVALID)
break;
}
}
return FileType;
}
static DetailedTaskDescription
constructDetailedTaskDescription(const CompilerInvocation &Invocation,
ArrayRef<InputFile> PrimaryInputs,
ArrayRef<const char *> Args,
bool isEmitModuleOnly = false) {
// Command line and arguments
std::string Executable = Invocation.getFrontendOptions().MainExecutablePath;
SmallVector<std::string, 16> Arguments;
std::string CommandLine;
SmallVector<CommandInput, 4> Inputs;
SmallVector<OutputPair, 8> Outputs;
CommandLine += Executable;
for (const auto &A : Args) {
Arguments.push_back(A);
CommandLine += std::string(" ") + A;
}
// Primary Inputs
for (const auto &input : PrimaryInputs) {
Inputs.push_back(CommandInput(input.getFileName()));
}
for (const auto &input : PrimaryInputs) {
if (!isEmitModuleOnly) {
// Main per-input outputs
auto OutputFile = input.outputFilename();
if (!OutputFile.empty())
Outputs.push_back(OutputPair(computeFileTypeForPath(OutputFile), OutputFile));
}
// Supplementary outputs
const auto &primarySpecificFiles = input.getPrimarySpecificPaths();
const auto &supplementaryOutputPaths =
primarySpecificFiles.SupplementaryOutputs;
supplementaryOutputPaths.forEachSetOutput([&](const std::string &output) {
Outputs.push_back(OutputPair(computeFileTypeForPath(output), output));
});
}
return DetailedTaskDescription{Executable, Arguments, CommandLine, Inputs,
Outputs};
}
static void emitSwiftdepsForAllPrimaryInputsIfNeeded(
CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
@@ -2006,119 +1850,6 @@ static void emitIndexDataForSourceFile(SourceFile *PrimarySourceFile,
}
}
/// Creates a diagnostic consumer that handles dispatching diagnostics to
/// multiple output files, based on the supplementary output paths specified by
/// \p inputsAndOutputs.
///
/// If no output files are needed, returns null.
static std::unique_ptr<DiagnosticConsumer>
createDispatchingDiagnosticConsumerIfNeeded(
const FrontendInputsAndOutputs &inputsAndOutputs,
llvm::function_ref<std::unique_ptr<DiagnosticConsumer>(const InputFile &)>
maybeCreateConsumerForDiagnosticsFrom) {
// The "4" here is somewhat arbitrary. In practice we're going to have one
// sub-consumer for each diagnostic file we're trying to output, which (again
// in practice) is going to be 1 in WMO mode and equal to the number of
// primary inputs in batch mode. That in turn is going to be "the number of
// files we need to recompile in this build, divided by the number of jobs".
// So a value of "4" here means that there would be no heap allocation on a
// clean build of a module with up to 32 files on an 8-core machine, if the
// user doesn't customize anything.
SmallVector<FileSpecificDiagnosticConsumer::Subconsumer, 4> subconsumers;
inputsAndOutputs.forEachInputProducingSupplementaryOutput(
[&](const InputFile &input) -> bool {
if (auto consumer = maybeCreateConsumerForDiagnosticsFrom(input))
subconsumers.emplace_back(input.getFileName(), std::move(consumer));
return false;
});
// For batch mode, the compiler must sometimes swallow diagnostics pertaining
// to non-primary files in order to avoid Xcode showing the same diagnostic
// multiple times. So, create a diagnostic "eater" for those non-primary
// files.
//
// This routine gets called in cases where no primary subconsumers are created.
// Don't bother to create non-primary subconsumers if there aren't any primary
// ones.
//
// To avoid introducing bugs into WMO or single-file modes, test for multiple
// primaries.
if (!subconsumers.empty() && inputsAndOutputs.hasMultiplePrimaryInputs()) {
inputsAndOutputs.forEachNonPrimaryInput(
[&](const InputFile &input) -> bool {
subconsumers.emplace_back(input.getFileName(), nullptr);
return false;
});
}
return FileSpecificDiagnosticConsumer::consolidateSubconsumers(subconsumers);
}
/// Creates a diagnostic consumer that handles serializing diagnostics, based on
/// the supplementary output paths specified by \p inputsAndOutputs.
///
/// The returned consumer will handle producing multiple serialized diagnostics
/// files if necessary, by using sub-consumers for each file and dispatching to
/// the right one.
///
/// If no serialized diagnostics are being produced, returns null.
static std::unique_ptr<DiagnosticConsumer>
createSerializedDiagnosticConsumerIfNeeded(
const FrontendInputsAndOutputs &inputsAndOutputs,
bool emitMacroExpansionFiles
) {
return createDispatchingDiagnosticConsumerIfNeeded(
inputsAndOutputs,
[emitMacroExpansionFiles](
const InputFile &input
) -> std::unique_ptr<DiagnosticConsumer> {
auto serializedDiagnosticsPath = input.getSerializedDiagnosticsPath();
if (serializedDiagnosticsPath.empty())
return nullptr;
return serialized_diagnostics::createConsumer(
serializedDiagnosticsPath, emitMacroExpansionFiles);
});
}
/// Creates a diagnostic consumer that accumulates all emitted diagnostics as compilation
/// proceeds. The accumulated diagnostics are then emitted in the frontend's parseable-output.
static std::unique_ptr<DiagnosticConsumer>
createAccumulatingDiagnosticConsumer(
const FrontendInputsAndOutputs &InputsAndOutputs,
llvm::StringMap<std::vector<std::string>> &FileSpecificDiagnostics) {
return createDispatchingDiagnosticConsumerIfNeeded(
InputsAndOutputs,
[&](const InputFile &Input) -> std::unique_ptr<DiagnosticConsumer> {
FileSpecificDiagnostics.try_emplace(Input.getFileName(),
std::vector<std::string>());
auto &DiagBufferRef = FileSpecificDiagnostics[Input.getFileName()];
return std::make_unique<AccumulatingFileDiagnosticConsumer>(DiagBufferRef);
});
}
/// Creates a diagnostic consumer that handles serializing diagnostics, based on
/// the supplementary output paths specified in \p options.
///
/// The returned consumer will handle producing multiple serialized diagnostics
/// files if necessary, by using sub-consumers for each file and dispatching to
/// the right one.
///
/// If no serialized diagnostics are being produced, returns null.
static std::unique_ptr<DiagnosticConsumer>
createJSONFixItDiagnosticConsumerIfNeeded(
const CompilerInvocation &invocation) {
return createDispatchingDiagnosticConsumerIfNeeded(
invocation.getFrontendOptions().InputsAndOutputs,
[&](const InputFile &input) -> std::unique_ptr<DiagnosticConsumer> {
auto fixItsOutputPath = input.getFixItsOutputPath();
if (fixItsOutputPath.empty())
return nullptr;
return std::make_unique<JSONFixitWriter>(
fixItsOutputPath.str(), invocation.getDiagnosticOptions());
});
}
/// A PrettyStackTraceEntry to print frontend information useful for debugging.
class PrettyStackTraceFrontend : public llvm::PrettyStackTraceEntry {
const CompilerInvocation &Invocation;
@@ -2148,50 +1879,22 @@ int swift::performFrontend(ArrayRef<const char *> Args,
llvm::setBugReportMsg(SWIFT_CRASH_BUG_REPORT_MESSAGE "\n");
llvm::EnablePrettyStackTraceOnSigInfoForThisThread();
PrintingDiagnosticConsumer PDC;
std::unique_ptr<CompilerInstance> Instance =
std::make_unique<CompilerInstance>();
DiagnosticHelper DH = DiagnosticHelper::create(*Instance);
// Hopefully we won't trigger any LLVM-level fatal errors, but if we do try
// to route them through our usual textual diagnostics before crashing.
//
// Unfortunately it's not really safe to do anything else, since very
// low-level operations in LLVM can trigger fatal errors.
auto diagnoseFatalError = [&PDC](const char *reason, bool shouldCrash) {
static const char *recursiveFatalError = nullptr;
if (recursiveFatalError) {
// Report the /original/ error through LLVM's default handler, not
// whatever we encountered.
llvm::remove_fatal_error_handler();
llvm::report_fatal_error(recursiveFatalError, shouldCrash);
}
recursiveFatalError = reason;
SourceManager dummyMgr;
DiagnosticInfo errorInfo(
DiagID(0), SourceLoc(), DiagnosticKind::Error,
"fatal error encountered during compilation; " SWIFT_BUG_REPORT_MESSAGE,
{}, StringRef(), SourceLoc(), {}, {}, {}, false);
DiagnosticInfo noteInfo(DiagID(0), SourceLoc(), DiagnosticKind::Note,
reason, {}, StringRef(), SourceLoc(), {}, {}, {},
false);
PDC.handleDiagnostic(dummyMgr, errorInfo);
PDC.handleDiagnostic(dummyMgr, noteInfo);
if (shouldCrash)
abort();
};
llvm::ScopedFatalErrorHandler handler(
[](void *rawCallback, const char *reason, bool shouldCrash) {
auto *callback =
static_cast<decltype(&diagnoseFatalError)>(rawCallback);
(*callback)(reason, shouldCrash);
auto *helper = static_cast<DiagnosticHelper *>(rawCallback);
helper->diagnoseFatalError(reason, shouldCrash);
},
&diagnoseFatalError);
std::unique_ptr<CompilerInstance> Instance =
std::make_unique<CompilerInstance>();
// In parseable output, avoid printing diagnostics
Instance->addDiagnosticConsumer(&PDC);
&DH);
struct FinishDiagProcessingCheckRAII {
bool CalledFinishDiagProcessing = false;
@@ -2203,7 +1906,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
auto finishDiagProcessing = [&](int retValue, bool verifierEnabled) -> int {
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
PDC.setSuppressOutput(false);
DH.setSuppressOutput(false);
if (auto *CDP = Instance->getCachingDiagnosticsProcessor()) {
// Don't cache if build failed.
if (retValue)
@@ -2301,143 +2004,13 @@ int swift::performFrontend(ArrayRef<const char *> Args,
return finishDiagProcessing(1, /*verifierEnabled*/ false);
}
llvm::StringMap<std::vector<std::string>> FileSpecificDiagnostics;
std::unique_ptr<DiagnosticConsumer> FileSpecificAccumulatingConsumer;
if (Invocation.getFrontendOptions().FrontendParseableOutput) {
// We need a diagnostic consumer that will, per-file, collect all
// diagnostics to be reported in parseable-output
FileSpecificAccumulatingConsumer = createAccumulatingDiagnosticConsumer(
Invocation.getFrontendOptions().InputsAndOutputs,
FileSpecificDiagnostics);
Instance->addDiagnosticConsumer(FileSpecificAccumulatingConsumer.get());
// If we got this far, we need to suppress the output of the
// PrintingDiagnosticConsumer to ensure that only the parseable-output
// is emitted
PDC.setSuppressOutput(true);
}
auto emitParseableBeganMessage = [&Invocation, &Args]() {
const auto &IO = Invocation.getFrontendOptions().InputsAndOutputs;
const auto OSPid = getpid();
const auto ProcInfo = sys::TaskProcessInformation(OSPid);
// Parseable output clients may not understand the idea of a batch
// compilation. We assign each primary in a batch job a quasi process id,
// making sure it cannot collide with a real PID (always positive). Non-batch
// compilation gets a real OS PID.
int64_t Pid = IO.hasUniquePrimaryInput() ? OSPid : QUASI_PID_START;
if (IO.hasPrimaryInputs()) {
IO.forEachPrimaryInputWithIndex([&](const InputFile &Input,
unsigned idx) -> bool {
ArrayRef<InputFile> Inputs(Input);
emitBeganMessage(llvm::errs(),
mapFrontendInvocationToAction(Invocation),
constructDetailedTaskDescription(Invocation,
Inputs,
Args), Pid - idx,
ProcInfo);
return false;
});
} else {
// If no primary inputs are present, we are in WMO or EmitModule.
bool isEmitModule =
Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::EmitModuleOnly;
emitBeganMessage(llvm::errs(),
mapFrontendInvocationToAction(Invocation),
constructDetailedTaskDescription(Invocation, IO.getAllInputs(),
Args, isEmitModule),
OSPid, ProcInfo);
}
};
auto emitParseableFinishedMessage = [&Invocation, &FileSpecificDiagnostics](
int ExitStatus) {
const auto &IO = Invocation.getFrontendOptions().InputsAndOutputs;
const auto OSPid = getpid();
const auto ProcInfo = sys::TaskProcessInformation(OSPid);
// Parseable output clients may not understand the idea of a batch
// compilation. We assign each primary in a batch job a quasi process id,
// making sure it cannot collide with a real PID (always positive). Non-batch
// compilation gets a real OS PID.
int64_t Pid = IO.hasUniquePrimaryInput() ? OSPid : QUASI_PID_START;
if (IO.hasPrimaryInputs()) {
IO.forEachPrimaryInputWithIndex([&](const InputFile &Input,
unsigned idx) -> bool {
assert(FileSpecificDiagnostics.count(Input.getFileName()) != 0 &&
"Expected diagnostic collection for input.");
// Join all diagnostics produced for this file into a single output.
auto PrimaryDiags = FileSpecificDiagnostics.lookup(Input.getFileName());
const char *const Delim = "";
std::ostringstream JoinedDiags;
std::copy(PrimaryDiags.begin(), PrimaryDiags.end(),
std::ostream_iterator<std::string>(JoinedDiags, Delim));
emitFinishedMessage(llvm::errs(),
mapFrontendInvocationToAction(Invocation),
JoinedDiags.str(), ExitStatus, Pid - idx, ProcInfo);
return false;
});
} else {
// If no primary inputs are present, we are in WMO.
std::vector<std::string> AllDiagnostics;
for (const auto &FileDiagnostics : FileSpecificDiagnostics) {
AllDiagnostics.insert(AllDiagnostics.end(),
FileDiagnostics.getValue().begin(),
FileDiagnostics.getValue().end());
}
const char *const Delim = "";
std::ostringstream JoinedDiags;
std::copy(AllDiagnostics.begin(), AllDiagnostics.end(),
std::ostream_iterator<std::string>(JoinedDiags, Delim));
emitFinishedMessage(llvm::errs(),
mapFrontendInvocationToAction(Invocation),
JoinedDiags.str(), ExitStatus, OSPid, ProcInfo);
}
};
// Because the serialized diagnostics consumer is initialized here,
// diagnostics emitted above, within CompilerInvocation::parseArgs, are never
// serialized. This is a non-issue because, in nearly all cases, frontend
// arguments are generated by the driver, not directly by a user. The driver
// is responsible for emitting diagnostics for its own errors.
// See https://github.com/apple/swift/issues/45288 for details.
std::unique_ptr<DiagnosticConsumer> SerializedConsumerDispatcher =
createSerializedDiagnosticConsumerIfNeeded(
Invocation.getFrontendOptions().InputsAndOutputs,
Invocation.getDiagnosticOptions().EmitMacroExpansionFiles);
if (SerializedConsumerDispatcher)
Instance->addDiagnosticConsumer(SerializedConsumerDispatcher.get());
std::unique_ptr<DiagnosticConsumer> FixItsConsumer =
createJSONFixItDiagnosticConsumerIfNeeded(Invocation);
if (FixItsConsumer)
Instance->addDiagnosticConsumer(FixItsConsumer.get());
if (Invocation.getDiagnosticOptions().UseColor)
PDC.forceColors();
PDC.setPrintEducationalNotes(
Invocation.getDiagnosticOptions().PrintEducationalNotes);
PDC.setFormattingStyle(
Invocation.getDiagnosticOptions().PrintedFormattingStyle);
PDC.setEmitMacroExpansionFiles(
Invocation.getDiagnosticOptions().EmitMacroExpansionFiles);
DH.initDiagConsumers(Invocation);
if (Invocation.getFrontendOptions().PrintStats) {
llvm::EnableStatistics();
}
if (Invocation.getFrontendOptions().FrontendParseableOutput)
emitParseableBeganMessage();
DH.beginMessage(Invocation, Args);
const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions();
bool verifierEnabled = diagOpts.VerifyMode != DiagnosticOptions::NoVerify;
@@ -2445,8 +2018,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
std::string InstanceSetupError;
if (Instance->setup(Invocation, InstanceSetupError, Args)) {
int ReturnCode = 1;
if (Invocation.getFrontendOptions().FrontendParseableOutput)
emitParseableFinishedMessage(ReturnCode);
DH.endMessage(ReturnCode);
return finishDiagProcessing(ReturnCode, /*verifierEnabled*/ false);
}
@@ -2459,7 +2031,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
if (verifierEnabled) {
// Suppress printed diagnostic output during the compile if the verifier is
// enabled.
PDC.setSuppressOutput(true);
DH.setSuppressOutput(true);
}
CompilerInstance::HashingBackendPtrTy HashBackend = nullptr;
@@ -2487,7 +2059,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
if (diags.hasFatalErrorOccurred() &&
!Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
diags.resetHadAnyError();
PDC.setSuppressOutput(false);
DH.setSuppressOutput(false);
diags.diagnose(SourceLoc(), diag::verify_encountered_fatal);
HadError = true;
}
@@ -2535,9 +2107,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
if (auto *StatsReporter = Instance->getStatsReporter())
StatsReporter->noteCurrentProcessExitStatus(r);
if (Invocation.getFrontendOptions().FrontendParseableOutput)
emitParseableFinishedMessage(r);
DH.endMessage(r);
return r;
}