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