Skip to content

Commit b88269b

Browse files
authored
Merge pull request #17231 from graydon/rdar-40526328-emit-SIGINT-for-cancelled-batch-constituents-swift-4.2-branch
2 parents 88d04e7 + 027db8d commit b88269b

13 files changed

+136
-60
lines changed

include/swift/AST/DiagnosticConsumer.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,16 @@ class DiagnosticConsumer {
100100
const DiagnosticInfo &Info) = 0;
101101

102102
/// \returns true if an error occurred while finishing-up.
103-
virtual bool finishProcessing(SourceManager &) { return false; }
103+
virtual bool finishProcessing() { return false; }
104+
105+
/// In batch mode, any error causes failure for all primary files, but
106+
/// anyone consulting .dia files will only see an error for a particular
107+
/// primary in that primary's serialized diagnostics file. For other
108+
/// primaries' serialized diagnostics files, do something to signal the driver
109+
/// what happened. This is only meaningful for SerializedDiagnosticConsumers,
110+
/// so here's a placeholder.
111+
112+
virtual void informDriverOfIncompleteBatchModeCompilation() {}
104113
};
105114

106115
/// \brief DiagnosticConsumer that discards all diagnostics.
@@ -193,13 +202,14 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
193202
ArrayRef<DiagnosticArgument> FormatArgs,
194203
const DiagnosticInfo &Info) override;
195204

196-
bool finishProcessing(SourceManager &) override;
205+
bool finishProcessing() override;
197206

198207
private:
199208
/// In batch mode, any error causes failure for all primary files, but
200209
/// Xcode will only see an error for a particular primary in that primary's
201-
/// serialized diagnostics file. So, emit errors for all other primaries here.
202-
void addNonSpecificErrors(SourceManager &SM);
210+
/// serialized diagnostics file. So, tell the subconsumers to inform the
211+
/// driver of incomplete batch mode compilation.
212+
void tellSubconsumersToInformDriverOfIncompleteBatchModeCompilation() const;
203213

204214
void computeConsumersOrderedByRange(SourceManager &SM);
205215

include/swift/AST/DiagnosticEngine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ namespace swift {
759759

760760
/// \returns true if any diagnostic consumer gave an error while invoking
761761
//// \c finishProcessing.
762-
bool finishProcessing(SourceManager &);
762+
bool finishProcessing();
763763

764764
/// \brief Format the given diagnostic text and place the result in the given
765765
/// buffer.

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,6 @@ WARNING(cannot_assign_value_to_conditional_compilation_flag,none,
235235
ERROR(error_optimization_remark_pattern, none, "%0 in '%1'",
236236
(StringRef, StringRef))
237237

238-
ERROR(error_compilation_stopped_by_errors_in_other_files,none, "compilation stopped by errors in other files", ())
239-
240238
#ifndef DIAG_NO_UNDEF
241239
# if defined(DIAG)
242240
# undef DIAG

lib/AST/DiagnosticConsumer.cpp

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -189,41 +189,25 @@ void FileSpecificDiagnosticConsumer::handleDiagnostic(
189189
Kind == DiagnosticKind::Error;
190190
}
191191

192-
bool FileSpecificDiagnosticConsumer::finishProcessing(SourceManager &SM) {
193-
addNonSpecificErrors(SM);
192+
bool FileSpecificDiagnosticConsumer::finishProcessing() {
193+
tellSubconsumersToInformDriverOfIncompleteBatchModeCompilation();
194194

195195
// Deliberately don't use std::any_of here because we don't want early-exit
196196
// behavior.
197197

198198
bool hadError = false;
199199
for (auto &subConsumer : SubConsumers)
200-
hadError |= subConsumer.second && subConsumer.second->finishProcessing(SM);
200+
hadError |= subConsumer.second && subConsumer.second->finishProcessing();
201201
return hadError;
202202
}
203203

204-
static void produceNonSpecificError(
205-
FileSpecificDiagnosticConsumer::ConsumerSpecificInformation &info,
206-
SourceManager &SM) {
207-
Diagnostic diagnostic(
208-
diag::error_compilation_stopped_by_errors_in_other_files);
209-
210-
// Stolen from DiagnosticEngine::emitDiagnostic
211-
DiagnosticInfo Info;
212-
Info.ID = diagnostic.getID();
213-
214-
info.consumer->handleDiagnostic(
215-
SM, info.range.getStart(), DiagnosticKind::Error,
216-
DiagnosticEngine::diagnosticStringFor(diagnostic.getID()), {}, Info);
217-
}
218-
219-
void FileSpecificDiagnosticConsumer::addNonSpecificErrors(SourceManager &SM) {
204+
void FileSpecificDiagnosticConsumer::
205+
tellSubconsumersToInformDriverOfIncompleteBatchModeCompilation() const {
220206
if (!HasAnErrorBeenConsumed)
221207
return;
222208
for (auto &info : ConsumersOrderedByRange) {
223-
if (!info.hasAnErrorBeenEmitted && info.consumer) {
224-
produceNonSpecificError(info, SM);
225-
info.hasAnErrorBeenEmitted = true;
226-
}
209+
if (!info.hasAnErrorBeenEmitted && info.consumer)
210+
info.consumer->informDriverOfIncompleteBatchModeCompilation();
227211
}
228212
}
229213

lib/AST/DiagnosticEngine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,10 @@ bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
251251
return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken;
252252
}
253253

254-
bool DiagnosticEngine::finishProcessing(SourceManager &SM) {
254+
bool DiagnosticEngine::finishProcessing() {
255255
bool hadError = false;
256256
for (auto &Consumer : Consumers) {
257-
hadError |= Consumer->finishProcessing(SM);
257+
hadError |= Consumer->finishProcessing();
258258
}
259259
return hadError;
260260
}

lib/Driver/Compilation.cpp

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
#include "CompilationRecord.h"
4747

48+
#include <signal.h>
49+
4850
#define DEBUG_TYPE "batch-mode"
4951

5052
// Batch-mode has a sub-mode for testing that randomizes batch partitions,
@@ -459,6 +461,35 @@ namespace driver {
459461
}
460462
}
461463

464+
/// Check to see if a job produced a zero-length serialized diagnostics
465+
/// file, which is used to indicate batch-constituents that were batched
466+
/// together with a failing constituent but did not, themselves, produce any
467+
/// errors.
468+
bool jobWasBatchedWithFailingJobs(const Job *J) const {
469+
auto DiaPath =
470+
J->getOutput().getAnyOutputForType(file_types::TY_SerializedDiagnostics);
471+
if (DiaPath.empty())
472+
return false;
473+
if (!llvm::sys::fs::is_regular_file(DiaPath))
474+
return false;
475+
uint64_t Size;
476+
auto EC = llvm::sys::fs::file_size(DiaPath, Size);
477+
if (EC)
478+
return false;
479+
return Size == 0;
480+
}
481+
482+
/// If a batch-constituent job happens to be batched together with a job
483+
/// that exits with an error, the batch-constituent may be considered
484+
/// "cancelled".
485+
bool jobIsCancelledBatchConstituent(int ReturnCode,
486+
const Job *ContainerJob,
487+
const Job *ConstituentJob) {
488+
return ReturnCode != 0 &&
489+
isBatchJob(ContainerJob) &&
490+
jobWasBatchedWithFailingJobs(ConstituentJob);
491+
}
492+
462493
/// Unpack a \c BatchJob that has finished into its constituent \c Job
463494
/// members, and call \c taskFinished on each, propagating any \c
464495
/// TaskFinishedResponse other than \c
@@ -482,6 +513,26 @@ namespace driver {
482513
return res;
483514
}
484515

516+
void
517+
emitParseableOutputForEachFinishedJob(ProcessId Pid, int ReturnCode,
518+
StringRef Output,
519+
const Job *FinishedCmd) {
520+
FinishedCmd->forEachContainedJobAndPID(Pid, [&](const Job *J,
521+
Job::PID P) {
522+
if (jobIsCancelledBatchConstituent(ReturnCode, FinishedCmd, J)) {
523+
// Simulate SIGINT-interruption to parseable-output consumer for any
524+
// constituent of a failing batch job that produced no errors of its
525+
// own.
526+
parseable_output::emitSignalledMessage(llvm::errs(), *J, P,
527+
"cancelled batch constituent",
528+
"", SIGINT);
529+
} else {
530+
parseable_output::emitFinishedMessage(llvm::errs(), *J, P, ReturnCode,
531+
Output);
532+
}
533+
});
534+
}
535+
485536
/// Callback which will be called immediately after a task has finished
486537
/// execution. Determines if execution should continue, and also schedule
487538
/// any additional Jobs which we now know we need to run.
@@ -508,12 +559,8 @@ namespace driver {
508559
llvm::errs() << Output;
509560
break;
510561
case OutputLevel::Parseable:
511-
// Parseable output was requested.
512-
FinishedCmd->forEachContainedJobAndPID(
513-
Pid, [&](const Job *J, Job::PID P) {
514-
parseable_output::emitFinishedMessage(llvm::errs(), *J, P,
515-
ReturnCode, Output);
516-
});
562+
emitParseableOutputForEachFinishedJob(Pid, ReturnCode, Output,
563+
FinishedCmd);
517564
break;
518565
}
519566
}

lib/Frontend/SerializedDiagnosticConsumer.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
130130
/// \brief State shared among the various clones of this diagnostic consumer.
131131
llvm::IntrusiveRefCntPtr<SharedState> State;
132132
bool CalledFinishProcessing = false;
133+
bool CompilationWasComplete = true;
134+
133135
public:
134136
SerializedDiagnosticConsumer(StringRef serializedDiagnosticsPath)
135137
: State(new SharedState(serializedDiagnosticsPath)) {
@@ -140,7 +142,7 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
140142
assert(CalledFinishProcessing && "did not call finishProcessing()");
141143
}
142144

143-
bool finishProcessing(SourceManager &SM) override {
145+
bool finishProcessing() override {
144146
assert(!CalledFinishProcessing &&
145147
"called finishProcessing() multiple times");
146148
CalledFinishProcessing = true;
@@ -160,19 +162,35 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
160162
llvm::sys::fs::F_None));
161163
if (EC) {
162164
// Create a temporary diagnostics engine to print the error to stderr.
163-
DiagnosticEngine DE(SM);
165+
SourceManager dummyMgr;
166+
DiagnosticEngine DE(dummyMgr);
164167
PrintingDiagnosticConsumer PDC;
165168
DE.addConsumer(PDC);
166169
DE.diagnose(SourceLoc(), diag::cannot_open_serialized_file,
167170
State->SerializedDiagnosticsPath, EC.message());
168171
return true;
169172
}
170173

171-
OS->write((char *)&State->Buffer.front(), State->Buffer.size());
172-
OS->flush();
174+
if (CompilationWasComplete) {
175+
OS->write((char *)&State->Buffer.front(), State->Buffer.size());
176+
OS->flush();
177+
}
173178
return false;
174179
}
175180

181+
/// In batch mode, if any error occurs, no primaries can be compiled.
182+
/// Some primaries will have errors in their diagnostics files and so
183+
/// a client (such as Xcode) can see that those primaries failed.
184+
/// Other primaries will have no errors in their diagnostics files.
185+
/// In order for the driver to distinguish the two cases without parsing
186+
/// the diagnostics, the frontend emits a truncated diagnostics file
187+
/// for the latter case.
188+
/// The unfortunate aspect is that the truncation discards warnings, etc.
189+
190+
void informDriverOfIncompleteBatchModeCompilation() override {
191+
CompilationWasComplete = false;
192+
}
193+
176194
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
177195
DiagnosticKind Kind,
178196
StringRef FormatString,

lib/FrontendTool/FrontendTool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ class JSONFixitWriter
404404
}
405405
}
406406

407-
bool finishProcessing(SourceManager &) override {
407+
bool finishProcessing() override {
408408
std::error_code EC;
409409
std::unique_ptr<llvm::raw_fd_ostream> OS;
410410
OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath,
@@ -1651,7 +1651,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
16511651

16521652
auto finishDiagProcessing = [&](int retValue) -> int {
16531653
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
1654-
bool err = Instance->getDiags().finishProcessing(Instance->getSourceMgr());
1654+
bool err = Instance->getDiags().finishProcessing();
16551655
return retValue ? retValue : err;
16561656
};
16571657

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: touch %t/file-01.swift
3+
// RUN: echo 'public func main() { help_an_error_happened() }' >%t/main.swift
4+
//
5+
// RUN: not %swiftc_driver -enable-batch-mode -parseable-output -serialize-diagnostics -c -emit-module -module-name main -j 1 %t/file-01.swift %t/main.swift 2>&1 | %FileCheck %s
6+
//
7+
// CHECK: "kind": "signalled",
8+
// CHECK-NEXT: "name": "compile",
9+
// CHECK-NEXT: "pid": -{{[1-9][0-9]*}},
10+
// CHECK-NEXT: "error-message": "{{.*}}",
11+
// CHECK-NEXT: "signal": 2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Ensure that an error in a non-primary causes an error in the errorless primary.
2+
//
3+
// RUN: rm -f %t.*
4+
5+
// RUN: not %target-swift-frontend -typecheck -primary-file %s -serialize-diagnostics-path %t.main.dia -primary-file %S/../Inputs/empty.swift -serialize-diagnostics-path %t.empty.dia %S/Inputs/serialized-diagnostics-batch-mode-suppression-helper.swift 2> %t.stderr.txt
6+
7+
// RUN: test -e %t.main.dia -a ! -s %t.empty.dia
8+
// RUN: test -e %t.empty.dia -a ! -s %t.empty.dia
9+
10+
func test(x: SomeType) {
11+
}

test/Misc/serialized-diagnostics-batch-mode-nonprimary-errors.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
// RUN: not %target-swift-frontend -typecheck -primary-file %s -serialize-diagnostics-path %t.main.dia -primary-file %S/../Inputs/empty.swift -serialize-diagnostics-path %t.empty.dia %S/Inputs/serialized-diagnostics-batch-mode-suppression-helper.swift 2> %t.stderr.txt
66
// RUN: c-index-test -read-diagnostics %t.main.dia 2> %t.main.txt
7-
// RUN: c-index-test -read-diagnostics %t.empty.dia 2> %t.empty.txt
87

98
// RUN: %FileCheck -check-prefix=NO-GENERAL-ERROR-OCCURRED %s <%t.main.txt
10-
// RUN: %FileCheck -check-prefix=GENERAL-ERROR-OCCURRED %s <%t.empty.txt
9+
// RUN: test -e %t.empty.dia -a ! -s %t.empty.dia
1110
// GENERAL-ERROR-OCCURRED: compilation stopped by errors in other files
1211
// NO-GENERAL-ERROR-OCCURRED-NOT: compilation stopped by errors in other files
1312

test/Misc/serialized-diagnostics-batch-mode-primary-errors.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44

55
// RUN: not %target-swift-frontend -typecheck -primary-file %s -serialize-diagnostics-path %t.main.dia -primary-file %S/../Inputs/empty.swift -serialize-diagnostics-path %t.empty.dia 2> %t.stderr.txt
66
// RUN: c-index-test -read-diagnostics %t.main.dia 2> %t.main.txt
7-
// RUN: c-index-test -read-diagnostics %t.empty.dia 2> %t.empty.txt
87

98
// RUN: %FileCheck -check-prefix=ERROR %s <%t.main.txt
109
// RUN: %FileCheck -check-prefix=NO-NONSPECIFIC-ERROR %s <%t.main.txt
11-
// RUN: %FileCheck -check-prefix=NONSPECIFIC-ERROR %s <%t.empty.txt
10+
// RUN: test -e %t.empty.dia -a ! -s %t.empty.dia
1211

1312
// ERROR: error:
1413
// NONSPECIFIC-ERROR: error: compilation stopped by errors in other files
@@ -17,4 +16,3 @@
1716
func test(x: SomeType) {
1817
nonexistent()
1918
}
20-

0 commit comments

Comments
 (0)