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