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