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