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