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 | struct LogicalResult; |
29 | class MLIRContext; |
30 | class Operation; |
31 | class OperationName; |
32 | class OpPrintingFlags; |
33 | class Type; |
34 | class Value; |
35 | |
36 | namespace detail { |
37 | struct DiagnosticEngineImpl; |
38 | } // namespace detail |
39 | |
40 | /// Defines the different supported severity of a diagnostic. |
41 | enum class DiagnosticSeverity { |
42 | Note, |
43 | Warning, |
44 | Error, |
45 | , |
46 | }; |
47 | |
48 | //===----------------------------------------------------------------------===// |
49 | // DiagnosticArgument |
50 | //===----------------------------------------------------------------------===// |
51 | |
52 | /// A variant type that holds a single argument for a diagnostic. |
53 | class DiagnosticArgument { |
54 | public: |
55 | /// Note: The constructors below are only exposed due to problems accessing |
56 | /// constructors from type traits, they should not be used directly by users. |
57 | // Construct from an Attribute. |
58 | explicit DiagnosticArgument(Attribute attr); |
59 | // Construct from a floating point number. |
60 | explicit DiagnosticArgument(double val) |
61 | : kind(DiagnosticArgumentKind::Double), doubleVal(val) {} |
62 | explicit DiagnosticArgument(float val) : DiagnosticArgument(double(val)) {} |
63 | // Construct from a signed integer. |
64 | template <typename T> |
65 | explicit DiagnosticArgument( |
66 | T val, std::enable_if_t<std::is_signed<T>::value && |
67 | std::numeric_limits<T>::is_integer && |
68 | sizeof(T) <= sizeof(int64_t)> * = nullptr) |
69 | : kind(DiagnosticArgumentKind::Integer), opaqueVal(int64_t(val)) {} |
70 | // Construct from an unsigned integer. |
71 | template <typename T> |
72 | explicit DiagnosticArgument( |
73 | T val, std::enable_if_t<std::is_unsigned<T>::value && |
74 | std::numeric_limits<T>::is_integer && |
75 | sizeof(T) <= sizeof(uint64_t)> * = nullptr) |
76 | : kind(DiagnosticArgumentKind::Unsigned), opaqueVal(uint64_t(val)) {} |
77 | // Construct from a string reference. |
78 | explicit DiagnosticArgument(StringRef val) |
79 | : kind(DiagnosticArgumentKind::String), stringVal(val) {} |
80 | // Construct from a Type. |
81 | explicit DiagnosticArgument(Type val); |
82 | |
83 | /// Enum that represents the different kinds of diagnostic arguments |
84 | /// supported. |
85 | enum class DiagnosticArgumentKind { |
86 | Attribute, |
87 | Double, |
88 | Integer, |
89 | String, |
90 | Type, |
91 | Unsigned, |
92 | }; |
93 | |
94 | /// Outputs this argument to a stream. |
95 | void print(raw_ostream &os) const; |
96 | |
97 | /// Returns the kind of this argument. |
98 | DiagnosticArgumentKind getKind() const { return kind; } |
99 | |
100 | /// Returns this argument as an Attribute. |
101 | Attribute getAsAttribute() const; |
102 | |
103 | /// Returns this argument as a double. |
104 | double getAsDouble() const { |
105 | assert(getKind() == DiagnosticArgumentKind::Double); |
106 | return doubleVal; |
107 | } |
108 | |
109 | /// Returns this argument as a signed integer. |
110 | int64_t getAsInteger() const { |
111 | assert(getKind() == DiagnosticArgumentKind::Integer); |
112 | return static_cast<int64_t>(opaqueVal); |
113 | } |
114 | |
115 | /// Returns this argument as a string. |
116 | StringRef getAsString() const { |
117 | assert(getKind() == DiagnosticArgumentKind::String); |
118 | return stringVal; |
119 | } |
120 | |
121 | /// Returns this argument as a Type. |
122 | Type getAsType() const; |
123 | |
124 | /// Returns this argument as an unsigned integer. |
125 | uint64_t getAsUnsigned() const { |
126 | assert(getKind() == DiagnosticArgumentKind::Unsigned); |
127 | return static_cast<uint64_t>(opaqueVal); |
128 | } |
129 | |
130 | private: |
131 | friend class Diagnostic; |
132 | |
133 | /// The kind of this argument. |
134 | DiagnosticArgumentKind kind; |
135 | |
136 | /// The value of this argument. |
137 | union { |
138 | double doubleVal; |
139 | intptr_t opaqueVal; |
140 | StringRef stringVal; |
141 | }; |
142 | }; |
143 | |
144 | inline raw_ostream &operator<<(raw_ostream &os, const DiagnosticArgument &arg) { |
145 | arg.print(os); |
146 | return os; |
147 | } |
148 | |
149 | //===----------------------------------------------------------------------===// |
150 | // Diagnostic |
151 | //===----------------------------------------------------------------------===// |
152 | |
153 | /// This class contains all of the information necessary to report a diagnostic |
154 | /// to the DiagnosticEngine. It should generally not be constructed directly, |
155 | /// and instead used transitively via InFlightDiagnostic. |
156 | class Diagnostic { |
157 | using NoteVector = std::vector<std::unique_ptr<Diagnostic>>; |
158 | |
159 | public: |
160 | Diagnostic(Location loc, DiagnosticSeverity severity) |
161 | : loc(loc), severity(severity) {} |
162 | Diagnostic(Diagnostic &&) = default; |
163 | Diagnostic &operator=(Diagnostic &&) = default; |
164 | |
165 | /// Returns the severity of this diagnostic. |
166 | DiagnosticSeverity getSeverity() const { return severity; } |
167 | |
168 | /// Returns the source location for this diagnostic. |
169 | Location getLocation() const { return loc; } |
170 | |
171 | /// Returns the current list of diagnostic arguments. |
172 | MutableArrayRef<DiagnosticArgument> getArguments() { return arguments; } |
173 | ArrayRef<DiagnosticArgument> getArguments() const { return arguments; } |
174 | |
175 | /// Stream operator for inserting new diagnostic arguments. |
176 | template <typename Arg> |
177 | std::enable_if_t<!std::is_convertible<Arg, StringRef>::value && |
178 | std::is_constructible<DiagnosticArgument, Arg>::value, |
179 | Diagnostic &> |
180 | operator<<(Arg &&val) { |
181 | arguments.push_back(Elt: DiagnosticArgument(std::forward<Arg>(val))); |
182 | return *this; |
183 | } |
184 | Diagnostic &operator<<(StringAttr val); |
185 | |
186 | /// Stream in a string literal. |
187 | Diagnostic &operator<<(const char *val) { |
188 | arguments.push_back(Elt: 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(Range&: notes); |
257 | } |
258 | iterator_range<const_note_iterator> getNotes() const { |
259 | return llvm::make_pointee_range(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 | 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; |
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 | } |
317 | ~InFlightDiagnostic() { |
318 | if (isInFlight()) |
319 | report(); |
320 | } |
321 | |
322 | /// Stream operator for new diagnostic arguments. |
323 | template <typename Arg> |
324 | InFlightDiagnostic &operator<<(Arg &&arg) & { |
325 | return append(std::forward<Arg>(arg)); |
326 | } |
327 | template <typename Arg> |
328 | InFlightDiagnostic &&operator<<(Arg &&arg) && { |
329 | return std::move(append(std::forward<Arg>(arg))); |
330 | } |
331 | |
332 | /// Append arguments to the diagnostic. |
333 | template <typename... Args> |
334 | InFlightDiagnostic &append(Args &&...args) & { |
335 | assert(isActive() && "diagnostic not active" ); |
336 | if (isInFlight()) |
337 | impl->append(std::forward<Args>(args)...); |
338 | return *this; |
339 | } |
340 | template <typename... Args> |
341 | InFlightDiagnostic &&append(Args &&...args) && { |
342 | return std::move(append(std::forward<Args>(args)...)); |
343 | } |
344 | |
345 | /// Attaches a note to this diagnostic. |
346 | Diagnostic &attachNote(std::optional<Location> noteLoc = std::nullopt) { |
347 | assert(isActive() && "diagnostic not active" ); |
348 | return impl->attachNote(noteLoc); |
349 | } |
350 | |
351 | /// Returns the underlying diagnostic or nullptr if this diagnostic isn't |
352 | /// active. |
353 | Diagnostic *getUnderlyingDiagnostic() { return impl ? &*impl : nullptr; } |
354 | |
355 | /// Reports the diagnostic to the engine. |
356 | void report(); |
357 | |
358 | /// Abandons this diagnostic so that it will no longer be reported. |
359 | void abandon(); |
360 | |
361 | /// Allow an inflight diagnostic to be converted to 'failure', otherwise |
362 | /// 'success' if this is an empty diagnostic. |
363 | operator LogicalResult() const; |
364 | |
365 | /// Allow an inflight diagnostic to be converted to 'failure', otherwise |
366 | /// 'success' if this is an empty diagnostic. |
367 | operator ParseResult() const { return ParseResult(LogicalResult(*this)); } |
368 | |
369 | /// Allow an inflight diagnostic to be converted to FailureOr<T>. Always |
370 | /// results in 'failure' because this cast cannot possibly return an object of |
371 | /// 'T'. |
372 | template <typename T> |
373 | operator FailureOr<T>() const { |
374 | return failure(); |
375 | } |
376 | |
377 | private: |
378 | InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete; |
379 | InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete; |
380 | InFlightDiagnostic(DiagnosticEngine *owner, Diagnostic &&rhs) |
381 | : owner(owner), impl(std::move(rhs)) {} |
382 | |
383 | /// Returns true if the diagnostic is still active, i.e. it has a live |
384 | /// diagnostic. |
385 | bool isActive() const { return impl.has_value(); } |
386 | |
387 | /// Returns true if the diagnostic is still in flight to be reported. |
388 | bool isInFlight() const { return owner; } |
389 | |
390 | // Allow access to the constructor. |
391 | friend DiagnosticEngine; |
392 | |
393 | /// The engine that this diagnostic is to report to. |
394 | DiagnosticEngine *owner = nullptr; |
395 | |
396 | /// The raw diagnostic that is inflight to be reported. |
397 | std::optional<Diagnostic> impl; |
398 | }; |
399 | |
400 | //===----------------------------------------------------------------------===// |
401 | // DiagnosticEngine |
402 | //===----------------------------------------------------------------------===// |
403 | |
404 | /// This class is the main interface for diagnostics. The DiagnosticEngine |
405 | /// manages the registration of diagnostic handlers as well as the core API for |
406 | /// diagnostic emission. This class should not be constructed directly, but |
407 | /// instead interfaced with via an MLIRContext instance. |
408 | class DiagnosticEngine { |
409 | public: |
410 | ~DiagnosticEngine(); |
411 | |
412 | // Diagnostic handler registration and use. MLIR supports the ability for the |
413 | // IR to carry arbitrary metadata about operation location information. If a |
414 | // problem is detected by the compiler, it can invoke the emitError / |
415 | // emitWarning / emitRemark method on an Operation and have it get reported |
416 | // through this interface. |
417 | // |
418 | // Tools using MLIR are encouraged to register error handlers and define a |
419 | // schema for their location information. If they don't, then warnings and |
420 | // notes will be dropped and errors will be emitted to errs. |
421 | |
422 | /// The handler type for MLIR diagnostics. This function takes a diagnostic as |
423 | /// input, and returns success if the handler has fully processed this |
424 | /// diagnostic. Returns failure otherwise. |
425 | using HandlerTy = llvm::unique_function<LogicalResult(Diagnostic &)>; |
426 | |
427 | /// A handle to a specific registered handler object. |
428 | using HandlerID = uint64_t; |
429 | |
430 | /// Register a new handler for diagnostics to the engine. Diagnostics are |
431 | /// process by handlers in stack-like order, meaning that the last added |
432 | /// handlers will process diagnostics first. This function returns a unique |
433 | /// identifier for the registered handler, which can be used to unregister |
434 | /// this handler at a later time. |
435 | HandlerID registerHandler(HandlerTy handler); |
436 | |
437 | /// Set the diagnostic handler with a function that returns void. This is a |
438 | /// convenient wrapper for handlers that always completely process the given |
439 | /// diagnostic. |
440 | template <typename FuncTy, typename RetT = decltype(std::declval<FuncTy>()( |
441 | std::declval<Diagnostic &>()))> |
442 | std::enable_if_t<std::is_same<RetT, void>::value, HandlerID> |
443 | registerHandler(FuncTy &&handler) { |
444 | return registerHandler([=](Diagnostic &diag) { |
445 | handler(diag); |
446 | return success(); |
447 | }); |
448 | } |
449 | |
450 | /// Erase the registered diagnostic handler with the given identifier. |
451 | void eraseHandler(HandlerID id); |
452 | |
453 | /// Create a new inflight diagnostic with the given location and severity. |
454 | InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity) { |
455 | assert(severity != DiagnosticSeverity::Note && |
456 | "notes should not be emitted directly" ); |
457 | return InFlightDiagnostic(this, Diagnostic(loc, severity)); |
458 | } |
459 | |
460 | /// Emit a diagnostic using the registered issue handler if present, or with |
461 | /// the default behavior if not. The diagnostic instance is consumed in the |
462 | /// process. |
463 | void emit(Diagnostic &&diag); |
464 | |
465 | private: |
466 | friend class MLIRContextImpl; |
467 | DiagnosticEngine(); |
468 | |
469 | /// The internal implementation of the DiagnosticEngine. |
470 | std::unique_ptr<detail::DiagnosticEngineImpl> impl; |
471 | }; |
472 | |
473 | /// Utility method to emit an error message using this location. |
474 | InFlightDiagnostic emitError(Location loc); |
475 | InFlightDiagnostic emitError(Location loc, const Twine &message); |
476 | |
477 | /// Utility method to emit a warning message using this location. |
478 | InFlightDiagnostic emitWarning(Location loc); |
479 | InFlightDiagnostic emitWarning(Location loc, const Twine &message); |
480 | |
481 | /// Utility method to emit a remark message using this location. |
482 | InFlightDiagnostic (Location loc); |
483 | InFlightDiagnostic (Location loc, const Twine &message); |
484 | |
485 | /// Overloads of the above emission functions that take an optionally null |
486 | /// location. If the location is null, no diagnostic is emitted and a failure is |
487 | /// returned. Given that the provided location may be null, these methods take |
488 | /// the diagnostic arguments directly instead of relying on the returned |
489 | /// InFlightDiagnostic. |
490 | template <typename... Args> |
491 | LogicalResult emitOptionalError(std::optional<Location> loc, Args &&...args) { |
492 | if (loc) |
493 | return emitError(loc: *loc).append(std::forward<Args>(args)...); |
494 | return failure(); |
495 | } |
496 | template <typename... Args> |
497 | LogicalResult emitOptionalWarning(std::optional<Location> loc, Args &&...args) { |
498 | if (loc) |
499 | return emitWarning(loc: *loc).append(std::forward<Args>(args)...); |
500 | return failure(); |
501 | } |
502 | template <typename... Args> |
503 | LogicalResult (std::optional<Location> loc, Args &&...args) { |
504 | if (loc) |
505 | return emitRemark(loc: *loc).append(std::forward<Args>(args)...); |
506 | return failure(); |
507 | } |
508 | |
509 | //===----------------------------------------------------------------------===// |
510 | // ScopedDiagnosticHandler |
511 | //===----------------------------------------------------------------------===// |
512 | |
513 | /// This diagnostic handler is a simple RAII class that registers and erases a |
514 | /// diagnostic handler on a given context. This class can be either be used |
515 | /// directly, or in conjunction with a derived diagnostic handler. |
516 | class ScopedDiagnosticHandler { |
517 | public: |
518 | explicit ScopedDiagnosticHandler(MLIRContext *ctx) : handlerID(0), ctx(ctx) {} |
519 | template <typename FuncTy> |
520 | ScopedDiagnosticHandler(MLIRContext *ctx, FuncTy &&handler) |
521 | : handlerID(0), ctx(ctx) { |
522 | setHandler(std::forward<FuncTy>(handler)); |
523 | } |
524 | ~ScopedDiagnosticHandler(); |
525 | |
526 | protected: |
527 | /// Set the handler to manage via RAII. |
528 | template <typename FuncTy> |
529 | void setHandler(FuncTy &&handler) { |
530 | auto &diagEngine = ctx->getDiagEngine(); |
531 | if (handlerID) |
532 | diagEngine.eraseHandler(id: handlerID); |
533 | handlerID = diagEngine.registerHandler(std::forward<FuncTy>(handler)); |
534 | } |
535 | |
536 | private: |
537 | /// The unique id for the scoped handler. |
538 | DiagnosticEngine::HandlerID handlerID; |
539 | |
540 | /// The context to erase the handler from. |
541 | MLIRContext *ctx; |
542 | }; |
543 | |
544 | //===----------------------------------------------------------------------===// |
545 | // SourceMgrDiagnosticHandler |
546 | //===----------------------------------------------------------------------===// |
547 | |
548 | namespace detail { |
549 | struct SourceMgrDiagnosticHandlerImpl; |
550 | } // namespace detail |
551 | |
552 | /// This class is a utility diagnostic handler for use with llvm::SourceMgr. |
553 | class SourceMgrDiagnosticHandler : public ScopedDiagnosticHandler { |
554 | public: |
555 | /// This type represents a functor used to filter out locations when printing |
556 | /// a diagnostic. It should return true if the provided location is okay to |
557 | /// display, false otherwise. If all locations in a diagnostic are filtered |
558 | /// out, the first location is used as the sole location. When deciding |
559 | /// whether or not to filter a location, this function should not recurse into |
560 | /// any nested location. This recursion is handled automatically by the |
561 | /// caller. |
562 | using ShouldShowLocFn = llvm::unique_function<bool(Location)>; |
563 | |
564 | SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, |
565 | raw_ostream &os, |
566 | ShouldShowLocFn &&shouldShowLocFn = {}); |
567 | SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, |
568 | ShouldShowLocFn &&shouldShowLocFn = {}); |
569 | ~SourceMgrDiagnosticHandler(); |
570 | |
571 | /// Emit the given diagnostic information with the held source manager. |
572 | void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind, |
573 | bool displaySourceLine = true); |
574 | |
575 | protected: |
576 | /// Emit the given diagnostic with the held source manager. |
577 | void emitDiagnostic(Diagnostic &diag); |
578 | |
579 | /// Get a memory buffer for the given file, or nullptr if no file is |
580 | /// available. |
581 | const llvm::MemoryBuffer *getBufferForFile(StringRef filename); |
582 | |
583 | /// The source manager that we are wrapping. |
584 | llvm::SourceMgr &mgr; |
585 | |
586 | /// The output stream to use when printing diagnostics. |
587 | raw_ostream &os; |
588 | |
589 | /// A functor used when determining if a location for a diagnostic should be |
590 | /// shown. If null, all locations should be shown. |
591 | ShouldShowLocFn shouldShowLocFn; |
592 | |
593 | private: |
594 | /// Convert a location into the given memory buffer into an SMLoc. |
595 | SMLoc convertLocToSMLoc(FileLineColLoc loc); |
596 | |
597 | /// Given a location, returns the first nested location (including 'loc') that |
598 | /// can be shown to the user. |
599 | std::optional<Location> findLocToShow(Location loc); |
600 | |
601 | /// The maximum depth that a call stack will be printed. |
602 | /// TODO: This should be a tunable flag. |
603 | unsigned callStackLimit = 10; |
604 | |
605 | std::unique_ptr<detail::SourceMgrDiagnosticHandlerImpl> impl; |
606 | }; |
607 | |
608 | //===----------------------------------------------------------------------===// |
609 | // SourceMgrDiagnosticVerifierHandler |
610 | //===----------------------------------------------------------------------===// |
611 | |
612 | namespace detail { |
613 | struct SourceMgrDiagnosticVerifierHandlerImpl; |
614 | } // namespace detail |
615 | |
616 | /// This class is a utility diagnostic handler for use with llvm::SourceMgr that |
617 | /// verifies that emitted diagnostics match 'expected-*' lines on the |
618 | /// corresponding line of the source file. |
619 | class SourceMgrDiagnosticVerifierHandler : public SourceMgrDiagnosticHandler { |
620 | public: |
621 | SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, |
622 | raw_ostream &out); |
623 | SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx); |
624 | ~SourceMgrDiagnosticVerifierHandler(); |
625 | |
626 | /// Returns the status of the handler and verifies that all expected |
627 | /// diagnostics were emitted. This return success if all diagnostics were |
628 | /// verified correctly, failure otherwise. |
629 | LogicalResult verify(); |
630 | |
631 | private: |
632 | /// Process a single diagnostic. |
633 | void process(Diagnostic &diag); |
634 | |
635 | /// Process a FileLineColLoc diagnostic. |
636 | void process(FileLineColLoc loc, StringRef msg, DiagnosticSeverity kind); |
637 | |
638 | std::unique_ptr<detail::SourceMgrDiagnosticVerifierHandlerImpl> impl; |
639 | }; |
640 | |
641 | //===----------------------------------------------------------------------===// |
642 | // ParallelDiagnosticHandler |
643 | //===----------------------------------------------------------------------===// |
644 | |
645 | namespace detail { |
646 | struct ParallelDiagnosticHandlerImpl; |
647 | } // namespace detail |
648 | |
649 | /// This class is a utility diagnostic handler for use when multi-threading some |
650 | /// part of the compiler where diagnostics may be emitted. This handler ensures |
651 | /// a deterministic ordering to the emitted diagnostics that mirrors that of a |
652 | /// single-threaded compilation. |
653 | class ParallelDiagnosticHandler { |
654 | public: |
655 | ParallelDiagnosticHandler(MLIRContext *ctx); |
656 | ~ParallelDiagnosticHandler(); |
657 | |
658 | /// Set the order id for the current thread. This is required to be set by |
659 | /// each thread that will be emitting diagnostics to this handler. The orderID |
660 | /// corresponds to the order in which diagnostics would be emitted when |
661 | /// executing synchronously. For example, if we were processing a list |
662 | /// of operations [a, b, c] on a single-thread. Diagnostics emitted while |
663 | /// processing operation 'a' would be emitted before those for 'b' or 'c'. |
664 | /// This corresponds 1-1 with the 'orderID'. The thread that is processing 'a' |
665 | /// should set the orderID to '0'; the thread processing 'b' should set it to |
666 | /// '1'; and so on and so forth. This provides a way for the handler to |
667 | /// deterministically order the diagnostics that it receives given the thread |
668 | /// that it is receiving on. |
669 | void setOrderIDForThread(size_t orderID); |
670 | |
671 | /// Remove the order id for the current thread. This removes the thread from |
672 | /// diagnostics tracking. |
673 | void eraseOrderIDForThread(); |
674 | |
675 | private: |
676 | std::unique_ptr<detail::ParallelDiagnosticHandlerImpl> impl; |
677 | }; |
678 | } // namespace mlir |
679 | |
680 | #endif |
681 | |