1 //===- Diagnostics.h - MLIR Diagnostics -------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines utilities for emitting diagnostics.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef MLIR_IR_DIAGNOSTICS_H
14 #define MLIR_IR_DIAGNOSTICS_H
15
16 #include "mlir/IR/Location.h"
17 #include <functional>
18
19 namespace llvm {
20 class MemoryBuffer;
21 class SMLoc;
22 class SourceMgr;
23 } // namespace llvm
24
25 namespace mlir {
26 class DiagnosticEngine;
27 struct LogicalResult;
28 class MLIRContext;
29 class Operation;
30 class OperationName;
31 class OpPrintingFlags;
32 class Type;
33 class Value;
34
35 namespace detail {
36 struct DiagnosticEngineImpl;
37 } // namespace detail
38
39 /// Defines the different supported severity of a diagnostic.
40 enum class DiagnosticSeverity {
41 Note,
42 Warning,
43 Error,
44 Remark,
45 };
46
47 //===----------------------------------------------------------------------===//
48 // DiagnosticArgument
49 //===----------------------------------------------------------------------===//
50
51 /// A variant type that holds a single argument for a diagnostic.
52 class DiagnosticArgument {
53 public:
54 /// Note: The constructors below are only exposed due to problems accessing
55 /// constructors from type traits, they should not be used directly by users.
56 // Construct from an Attribute.
57 explicit DiagnosticArgument(Attribute attr);
58 // Construct from a floating point number.
DiagnosticArgument(double val)59 explicit DiagnosticArgument(double val)
60 : kind(DiagnosticArgumentKind::Double), doubleVal(val) {}
DiagnosticArgument(float val)61 explicit DiagnosticArgument(float val) : DiagnosticArgument(double(val)) {}
62 // Construct from a signed integer.
63 template <typename T>
64 explicit DiagnosticArgument(
65 T val,
66 typename std::enable_if<std::is_signed<T>::value &&
67 std::numeric_limits<T>::is_integer &&
68 sizeof(T) <= sizeof(int64_t)>::type * = nullptr)
kind(DiagnosticArgumentKind::Integer)69 : kind(DiagnosticArgumentKind::Integer), opaqueVal(int64_t(val)) {}
70 // Construct from an unsigned integer.
71 template <typename T>
72 explicit DiagnosticArgument(
73 T val,
74 typename std::enable_if<std::is_unsigned<T>::value &&
75 std::numeric_limits<T>::is_integer &&
76 sizeof(T) <= sizeof(uint64_t)>::type * = nullptr)
kind(DiagnosticArgumentKind::Unsigned)77 : kind(DiagnosticArgumentKind::Unsigned), opaqueVal(uint64_t(val)) {}
78 // Construct from a string reference.
DiagnosticArgument(StringRef val)79 explicit DiagnosticArgument(StringRef val)
80 : kind(DiagnosticArgumentKind::String), stringVal(val) {}
81 // Construct from a Type.
82 explicit DiagnosticArgument(Type val);
83
84 /// Enum that represents the different kinds of diagnostic arguments
85 /// supported.
86 enum class DiagnosticArgumentKind {
87 Attribute,
88 Double,
89 Integer,
90 String,
91 Type,
92 Unsigned,
93 };
94
95 /// Outputs this argument to a stream.
96 void print(raw_ostream &os) const;
97
98 /// Returns the kind of this argument.
getKind()99 DiagnosticArgumentKind getKind() const { return kind; }
100
101 /// Returns this argument as an Attribute.
102 Attribute getAsAttribute() const;
103
104 /// Returns this argument as a double.
getAsDouble()105 double getAsDouble() const {
106 assert(getKind() == DiagnosticArgumentKind::Double);
107 return doubleVal;
108 }
109
110 /// Returns this argument as a signed integer.
getAsInteger()111 int64_t getAsInteger() const {
112 assert(getKind() == DiagnosticArgumentKind::Integer);
113 return static_cast<int64_t>(opaqueVal);
114 }
115
116 /// Returns this argument as a string.
getAsString()117 StringRef getAsString() const {
118 assert(getKind() == DiagnosticArgumentKind::String);
119 return stringVal;
120 }
121
122 /// Returns this argument as a Type.
123 Type getAsType() const;
124
125 /// Returns this argument as an unsigned integer.
getAsUnsigned()126 uint64_t getAsUnsigned() const {
127 assert(getKind() == DiagnosticArgumentKind::Unsigned);
128 return static_cast<uint64_t>(opaqueVal);
129 }
130
131 private:
132 friend class Diagnostic;
133
134 /// The kind of this argument.
135 DiagnosticArgumentKind kind;
136
137 /// The value of this argument.
138 union {
139 double doubleVal;
140 intptr_t opaqueVal;
141 StringRef stringVal;
142 };
143 };
144
145 inline raw_ostream &operator<<(raw_ostream &os, const DiagnosticArgument &arg) {
146 arg.print(os);
147 return os;
148 }
149
150 //===----------------------------------------------------------------------===//
151 // Diagnostic
152 //===----------------------------------------------------------------------===//
153
154 /// This class contains all of the information necessary to report a diagnostic
155 /// to the DiagnosticEngine. It should generally not be constructed directly,
156 /// and instead used transitively via InFlightDiagnostic.
157 class Diagnostic {
158 using NoteVector = std::vector<std::unique_ptr<Diagnostic>>;
159
160 public:
Diagnostic(Location loc,DiagnosticSeverity severity)161 Diagnostic(Location loc, DiagnosticSeverity severity)
162 : loc(loc), severity(severity) {}
163 Diagnostic(Diagnostic &&) = default;
164 Diagnostic &operator=(Diagnostic &&) = default;
165
166 /// Returns the severity of this diagnostic.
getSeverity()167 DiagnosticSeverity getSeverity() const { return severity; }
168
169 /// Returns the source location for this diagnostic.
getLocation()170 Location getLocation() const { return loc; }
171
172 /// Returns the current list of diagnostic arguments.
getArguments()173 MutableArrayRef<DiagnosticArgument> getArguments() { return arguments; }
getArguments()174 ArrayRef<DiagnosticArgument> getArguments() const { return arguments; }
175
176 /// Stream operator for inserting new diagnostic arguments.
177 template <typename Arg>
178 typename std::enable_if<
179 !std::is_convertible<Arg, StringRef>::value &&
180 std::is_constructible<DiagnosticArgument, Arg>::value,
181 Diagnostic &>::type
182 operator<<(Arg &&val) {
183 arguments.push_back(DiagnosticArgument(std::forward<Arg>(val)));
184 return *this;
185 }
186 Diagnostic &operator<<(StringAttr val);
187
188 /// Stream in a string literal.
189 Diagnostic &operator<<(const char *val) {
190 arguments.push_back(DiagnosticArgument(val));
191 return *this;
192 }
193
194 /// Stream in a Twine argument.
195 Diagnostic &operator<<(char val);
196 Diagnostic &operator<<(const Twine &val);
197 Diagnostic &operator<<(Twine &&val);
198
199 /// Stream in an OperationName.
200 Diagnostic &operator<<(OperationName val);
201
202 /// Stream in an Operation.
203 Diagnostic &operator<<(Operation &val);
204 Diagnostic &operator<<(Operation *val) { return *this << *val; }
205 /// Append an operation with the given printing flags.
206 Diagnostic &appendOp(Operation &val, const OpPrintingFlags &flags);
207
208 /// Stream in a Value.
209 Diagnostic &operator<<(Value val);
210
211 /// Stream in a range.
212 template <typename T, typename ValueT = llvm::detail::ValueOfRange<T>>
213 std::enable_if_t<!std::is_constructible<DiagnosticArgument, T>::value,
214 Diagnostic &>
215 operator<<(T &&range) {
216 return appendRange(range);
217 }
218
219 /// Append a range to the diagnostic. The default delimiter between elements
220 /// is ','.
221 template <typename T>
222 Diagnostic &appendRange(const T &c, const char *delim = ", ") {
223 llvm::interleave(
224 c, [this](const auto &a) { *this << a; }, [&]() { *this << delim; });
225 return *this;
226 }
227
228 /// Append arguments to the diagnostic.
229 template <typename Arg1, typename Arg2, typename... Args>
append(Arg1 && arg1,Arg2 && arg2,Args &&...args)230 Diagnostic &append(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) {
231 append(std::forward<Arg1>(arg1));
232 return append(std::forward<Arg2>(arg2), std::forward<Args>(args)...);
233 }
234 /// Append one argument to the diagnostic.
235 template <typename Arg>
append(Arg && arg)236 Diagnostic &append(Arg &&arg) {
237 *this << std::forward<Arg>(arg);
238 return *this;
239 }
240
241 /// Outputs this diagnostic to a stream.
242 void print(raw_ostream &os) const;
243
244 /// Converts the diagnostic to a string.
245 std::string str() const;
246
247 /// Attaches a note to this diagnostic. A new location may be optionally
248 /// provided, if not, then the location defaults to the one specified for this
249 /// diagnostic. Notes may not be attached to other notes.
250 Diagnostic &attachNote(Optional<Location> noteLoc = llvm::None);
251
252 using note_iterator = llvm::pointee_iterator<NoteVector::iterator>;
253 using const_note_iterator =
254 llvm::pointee_iterator<NoteVector::const_iterator>;
255
256 /// Returns the notes held by this diagnostic.
getNotes()257 iterator_range<note_iterator> getNotes() {
258 return llvm::make_pointee_range(notes);
259 }
getNotes()260 iterator_range<const_note_iterator> getNotes() const {
261 return llvm::make_pointee_range(notes);
262 }
263
264 /// Allow a diagnostic to be converted to 'failure'.
265 operator LogicalResult() const;
266
267 /// Allow a diagnostic to be converted to 'failure'.
ParseResult()268 operator ParseResult() const { return ParseResult(LogicalResult(*this)); }
269
270 /// Allow a diagnostic to be converted to FailureOr<T>. Always results in
271 /// 'failure' because this cast cannot possibly return an object of 'T'.
272 template <typename T>
273 operator FailureOr<T>() const {
274 return failure();
275 }
276
277 private:
278 Diagnostic(const Diagnostic &rhs) = delete;
279 Diagnostic &operator=(const Diagnostic &rhs) = delete;
280
281 /// The source location.
282 Location loc;
283
284 /// The severity of this diagnostic.
285 DiagnosticSeverity severity;
286
287 /// The current list of arguments.
288 SmallVector<DiagnosticArgument, 4> arguments;
289
290 /// A list of string values used as arguments. This is used to guarantee the
291 /// liveness of non-constant strings used in diagnostics.
292 std::vector<std::unique_ptr<char[]>> strings;
293
294 /// A list of attached notes.
295 NoteVector notes;
296 };
297
298 inline raw_ostream &operator<<(raw_ostream &os, const Diagnostic &diag) {
299 diag.print(os);
300 return os;
301 }
302
303 //===----------------------------------------------------------------------===//
304 // InFlightDiagnostic
305 //===----------------------------------------------------------------------===//
306
307 /// This class represents a diagnostic that is inflight and set to be reported.
308 /// This allows for last minute modifications of the diagnostic before it is
309 /// emitted by a DiagnosticEngine.
310 class InFlightDiagnostic {
311 public:
312 InFlightDiagnostic() = default;
InFlightDiagnostic(InFlightDiagnostic && rhs)313 InFlightDiagnostic(InFlightDiagnostic &&rhs)
314 : owner(rhs.owner), impl(std::move(rhs.impl)) {
315 // Reset the rhs diagnostic.
316 rhs.impl.reset();
317 rhs.abandon();
318 }
~InFlightDiagnostic()319 ~InFlightDiagnostic() {
320 if (isInFlight())
321 report();
322 }
323
324 /// Stream operator for new diagnostic arguments.
325 template <typename Arg>
326 InFlightDiagnostic &operator<<(Arg &&arg) & {
327 return append(std::forward<Arg>(arg));
328 }
329 template <typename Arg>
330 InFlightDiagnostic &&operator<<(Arg &&arg) && {
331 return std::move(append(std::forward<Arg>(arg)));
332 }
333
334 /// Append arguments to the diagnostic.
335 template <typename... Args>
append(Args &&...args)336 InFlightDiagnostic &append(Args &&...args) & {
337 assert(isActive() && "diagnostic not active");
338 if (isInFlight())
339 impl->append(std::forward<Args>(args)...);
340 return *this;
341 }
342 template <typename... Args>
append(Args &&...args)343 InFlightDiagnostic &&append(Args &&...args) && {
344 return std::move(append(std::forward<Args>(args)...));
345 }
346
347 /// Attaches a note to this diagnostic.
348 Diagnostic &attachNote(Optional<Location> noteLoc = llvm::None) {
349 assert(isActive() && "diagnostic not active");
350 return impl->attachNote(noteLoc);
351 }
352
353 /// Reports the diagnostic to the engine.
354 void report();
355
356 /// Abandons this diagnostic so that it will no longer be reported.
357 void abandon();
358
359 /// Allow an inflight diagnostic to be converted to 'failure', otherwise
360 /// 'success' if this is an empty diagnostic.
361 operator LogicalResult() const;
362
363 /// Allow an inflight diagnostic to be converted to 'failure', otherwise
364 /// 'success' if this is an empty diagnostic.
ParseResult()365 operator ParseResult() const { return ParseResult(LogicalResult(*this)); }
366
367 /// Allow an inflight diagnostic to be converted to FailureOr<T>. Always
368 /// results in 'failure' because this cast cannot possibly return an object of
369 /// 'T'.
370 template <typename T>
371 operator FailureOr<T>() const {
372 return failure();
373 }
374
375 private:
376 InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete;
377 InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete;
InFlightDiagnostic(DiagnosticEngine * owner,Diagnostic && rhs)378 InFlightDiagnostic(DiagnosticEngine *owner, Diagnostic &&rhs)
379 : owner(owner), impl(std::move(rhs)) {}
380
381 /// Returns true if the diagnostic is still active, i.e. it has a live
382 /// diagnostic.
isActive()383 bool isActive() const { return impl.has_value(); }
384
385 /// Returns true if the diagnostic is still in flight to be reported.
isInFlight()386 bool isInFlight() const { return owner; }
387
388 // Allow access to the constructor.
389 friend DiagnosticEngine;
390
391 /// The engine that this diagnostic is to report to.
392 DiagnosticEngine *owner = nullptr;
393
394 /// The raw diagnostic that is inflight to be reported.
395 Optional<Diagnostic> impl;
396 };
397
398 //===----------------------------------------------------------------------===//
399 // DiagnosticEngine
400 //===----------------------------------------------------------------------===//
401
402 /// This class is the main interface for diagnostics. The DiagnosticEngine
403 /// manages the registration of diagnostic handlers as well as the core API for
404 /// diagnostic emission. This class should not be constructed directly, but
405 /// instead interfaced with via an MLIRContext instance.
406 class DiagnosticEngine {
407 public:
408 ~DiagnosticEngine();
409
410 // Diagnostic handler registration and use. MLIR supports the ability for the
411 // IR to carry arbitrary metadata about operation location information. If a
412 // problem is detected by the compiler, it can invoke the emitError /
413 // emitWarning / emitRemark method on an Operation and have it get reported
414 // through this interface.
415 //
416 // Tools using MLIR are encouraged to register error handlers and define a
417 // schema for their location information. If they don't, then warnings and
418 // notes will be dropped and errors will be emitted to errs.
419
420 /// The handler type for MLIR diagnostics. This function takes a diagnostic as
421 /// input, and returns success if the handler has fully processed this
422 /// diagnostic. Returns failure otherwise.
423 using HandlerTy = llvm::unique_function<LogicalResult(Diagnostic &)>;
424
425 /// A handle to a specific registered handler object.
426 using HandlerID = uint64_t;
427
428 /// Register a new handler for diagnostics to the engine. Diagnostics are
429 /// process by handlers in stack-like order, meaning that the last added
430 /// handlers will process diagnostics first. This function returns a unique
431 /// identifier for the registered handler, which can be used to unregister
432 /// this handler at a later time.
433 HandlerID registerHandler(HandlerTy handler);
434
435 /// Set the diagnostic handler with a function that returns void. This is a
436 /// convenient wrapper for handlers that always completely process the given
437 /// diagnostic.
438 template <typename FuncTy, typename RetT = decltype(std::declval<FuncTy>()(
439 std::declval<Diagnostic &>()))>
440 std::enable_if_t<std::is_same<RetT, void>::value, HandlerID>
registerHandler(FuncTy && handler)441 registerHandler(FuncTy &&handler) {
442 return registerHandler([=](Diagnostic &diag) {
443 handler(diag);
444 return success();
445 });
446 }
447
448 /// Erase the registered diagnostic handler with the given identifier.
449 void eraseHandler(HandlerID id);
450
451 /// Create a new inflight diagnostic with the given location and severity.
emit(Location loc,DiagnosticSeverity severity)452 InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity) {
453 assert(severity != DiagnosticSeverity::Note &&
454 "notes should not be emitted directly");
455 return InFlightDiagnostic(this, Diagnostic(loc, severity));
456 }
457
458 /// Emit a diagnostic using the registered issue handler if present, or with
459 /// the default behavior if not. The diagnostic instance is consumed in the
460 /// process.
461 void emit(Diagnostic &&diag);
462
463 private:
464 friend class MLIRContextImpl;
465 DiagnosticEngine();
466
467 /// The internal implementation of the DiagnosticEngine.
468 std::unique_ptr<detail::DiagnosticEngineImpl> impl;
469 };
470
471 /// Utility method to emit an error message using this location.
472 InFlightDiagnostic emitError(Location loc);
473 InFlightDiagnostic emitError(Location loc, const Twine &message);
474
475 /// Utility method to emit a warning message using this location.
476 InFlightDiagnostic emitWarning(Location loc);
477 InFlightDiagnostic emitWarning(Location loc, const Twine &message);
478
479 /// Utility method to emit a remark message using this location.
480 InFlightDiagnostic emitRemark(Location loc);
481 InFlightDiagnostic emitRemark(Location loc, const Twine &message);
482
483 /// Overloads of the above emission functions that take an optionally null
484 /// location. If the location is null, no diagnostic is emitted and a failure is
485 /// returned. Given that the provided location may be null, these methods take
486 /// the diagnostic arguments directly instead of relying on the returned
487 /// InFlightDiagnostic.
488 template <typename... Args>
emitOptionalError(Optional<Location> loc,Args &&...args)489 LogicalResult emitOptionalError(Optional<Location> loc, Args &&...args) {
490 if (loc)
491 return emitError(*loc).append(std::forward<Args>(args)...);
492 return failure();
493 }
494 template <typename... Args>
emitOptionalWarning(Optional<Location> loc,Args &&...args)495 LogicalResult emitOptionalWarning(Optional<Location> loc, Args &&...args) {
496 if (loc)
497 return emitWarning(*loc).append(std::forward<Args>(args)...);
498 return failure();
499 }
500 template <typename... Args>
emitOptionalRemark(Optional<Location> loc,Args &&...args)501 LogicalResult emitOptionalRemark(Optional<Location> loc, Args &&...args) {
502 if (loc)
503 return emitRemark(*loc).append(std::forward<Args>(args)...);
504 return failure();
505 }
506
507 //===----------------------------------------------------------------------===//
508 // ScopedDiagnosticHandler
509 //===----------------------------------------------------------------------===//
510
511 /// This diagnostic handler is a simple RAII class that registers and erases a
512 /// diagnostic handler on a given context. This class can be either be used
513 /// directly, or in conjunction with a derived diagnostic handler.
514 class ScopedDiagnosticHandler {
515 public:
ScopedDiagnosticHandler(MLIRContext * ctx)516 explicit ScopedDiagnosticHandler(MLIRContext *ctx) : handlerID(0), ctx(ctx) {}
517 template <typename FuncTy>
ScopedDiagnosticHandler(MLIRContext * ctx,FuncTy && handler)518 ScopedDiagnosticHandler(MLIRContext *ctx, FuncTy &&handler)
519 : handlerID(0), ctx(ctx) {
520 setHandler(std::forward<FuncTy>(handler));
521 }
522 ~ScopedDiagnosticHandler();
523
524 protected:
525 /// Set the handler to manage via RAII.
526 template <typename FuncTy>
setHandler(FuncTy && handler)527 void setHandler(FuncTy &&handler) {
528 auto &diagEngine = ctx->getDiagEngine();
529 if (handlerID)
530 diagEngine.eraseHandler(handlerID);
531 handlerID = diagEngine.registerHandler(std::forward<FuncTy>(handler));
532 }
533
534 private:
535 /// The unique id for the scoped handler.
536 DiagnosticEngine::HandlerID handlerID;
537
538 /// The context to erase the handler from.
539 MLIRContext *ctx;
540 };
541
542 //===----------------------------------------------------------------------===//
543 // SourceMgrDiagnosticHandler
544 //===----------------------------------------------------------------------===//
545
546 namespace detail {
547 struct SourceMgrDiagnosticHandlerImpl;
548 } // namespace detail
549
550 /// This class is a utility diagnostic handler for use with llvm::SourceMgr.
551 class SourceMgrDiagnosticHandler : public ScopedDiagnosticHandler {
552 public:
553 /// This type represents a functor used to filter out locations when printing
554 /// a diagnostic. It should return true if the provided location is okay to
555 /// display, false otherwise. If all locations in a diagnostic are filtered
556 /// out, the first location is used as the sole location. When deciding
557 /// whether or not to filter a location, this function should not recurse into
558 /// any nested location. This recursion is handled automatically by the
559 /// caller.
560 using ShouldShowLocFn = llvm::unique_function<bool(Location)>;
561
562 SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx,
563 raw_ostream &os,
564 ShouldShowLocFn &&shouldShowLocFn = {});
565 SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx,
566 ShouldShowLocFn &&shouldShowLocFn = {});
567 ~SourceMgrDiagnosticHandler();
568
569 /// Emit the given diagnostic information with the held source manager.
570 void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind,
571 bool displaySourceLine = true);
572
573 protected:
574 /// Emit the given diagnostic with the held source manager.
575 void emitDiagnostic(Diagnostic &diag);
576
577 /// Get a memory buffer for the given file, or nullptr if no file is
578 /// available.
579 const llvm::MemoryBuffer *getBufferForFile(StringRef filename);
580
581 /// The source manager that we are wrapping.
582 llvm::SourceMgr &mgr;
583
584 /// The output stream to use when printing diagnostics.
585 raw_ostream &os;
586
587 /// A functor used when determining if a location for a diagnostic should be
588 /// shown. If null, all locations should be shown.
589 ShouldShowLocFn shouldShowLocFn;
590
591 private:
592 /// Convert a location into the given memory buffer into an SMLoc.
593 SMLoc convertLocToSMLoc(FileLineColLoc loc);
594
595 /// Given a location, returns the first nested location (including 'loc') that
596 /// can be shown to the user.
597 Optional<Location> findLocToShow(Location loc);
598
599 /// The maximum depth that a call stack will be printed.
600 /// TODO: This should be a tunable flag.
601 unsigned callStackLimit = 10;
602
603 std::unique_ptr<detail::SourceMgrDiagnosticHandlerImpl> impl;
604 };
605
606 //===----------------------------------------------------------------------===//
607 // SourceMgrDiagnosticVerifierHandler
608 //===----------------------------------------------------------------------===//
609
610 namespace detail {
611 struct SourceMgrDiagnosticVerifierHandlerImpl;
612 } // namespace detail
613
614 /// This class is a utility diagnostic handler for use with llvm::SourceMgr that
615 /// verifies that emitted diagnostics match 'expected-*' lines on the
616 /// corresponding line of the source file.
617 class SourceMgrDiagnosticVerifierHandler : public SourceMgrDiagnosticHandler {
618 public:
619 SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx,
620 raw_ostream &out);
621 SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx);
622 ~SourceMgrDiagnosticVerifierHandler();
623
624 /// Returns the status of the handler and verifies that all expected
625 /// diagnostics were emitted. This return success if all diagnostics were
626 /// verified correctly, failure otherwise.
627 LogicalResult verify();
628
629 private:
630 /// Process a single diagnostic.
631 void process(Diagnostic &diag);
632
633 /// Process a FileLineColLoc diagnostic.
634 void process(FileLineColLoc loc, StringRef msg, DiagnosticSeverity kind);
635
636 std::unique_ptr<detail::SourceMgrDiagnosticVerifierHandlerImpl> impl;
637 };
638
639 //===----------------------------------------------------------------------===//
640 // ParallelDiagnosticHandler
641 //===----------------------------------------------------------------------===//
642
643 namespace detail {
644 struct ParallelDiagnosticHandlerImpl;
645 } // namespace detail
646
647 /// This class is a utility diagnostic handler for use when multi-threading some
648 /// part of the compiler where diagnostics may be emitted. This handler ensures
649 /// a deterministic ordering to the emitted diagnostics that mirrors that of a
650 /// single-threaded compilation.
651 class ParallelDiagnosticHandler {
652 public:
653 ParallelDiagnosticHandler(MLIRContext *ctx);
654 ~ParallelDiagnosticHandler();
655
656 /// Set the order id for the current thread. This is required to be set by
657 /// each thread that will be emitting diagnostics to this handler. The orderID
658 /// corresponds to the order in which diagnostics would be emitted when
659 /// executing synchronously. For example, if we were processing a list
660 /// of operations [a, b, c] on a single-thread. Diagnostics emitted while
661 /// processing operation 'a' would be emitted before those for 'b' or 'c'.
662 /// This corresponds 1-1 with the 'orderID'. The thread that is processing 'a'
663 /// should set the orderID to '0'; the thread processing 'b' should set it to
664 /// '1'; and so on and so forth. This provides a way for the handler to
665 /// deterministically order the diagnostics that it receives given the thread
666 /// that it is receiving on.
667 void setOrderIDForThread(size_t orderID);
668
669 /// Remove the order id for the current thread. This removes the thread from
670 /// diagnostics tracking.
671 void eraseOrderIDForThread();
672
673 private:
674 std::unique_ptr<detail::ParallelDiagnosticHandlerImpl> impl;
675 };
676 } // namespace mlir
677
678 #endif
679