1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*!
21 * \file diagnostic.h
22 * \brief A new diagnostic interface for TVM error reporting.
23 *
24 */
25
26#ifndef TVM_IR_DIAGNOSTIC_H_
27#define TVM_IR_DIAGNOSTIC_H_
28
29#include <tvm/ir/module.h>
30
31#include <sstream>
32#include <string>
33
34namespace tvm {
35
36using tvm::runtime::TypedPackedFunc;
37
38/*! \brief The diagnostic level, controls the printing of the message. */
39enum class DiagnosticLevel : int {
40 kBug = 10,
41 kError = 20,
42 kWarning = 30,
43 kNote = 40,
44 kHelp = 50,
45};
46
47class DiagnosticBuilder;
48
49/*! \brief A compiler diagnostic. */
50class Diagnostic;
51
52/*! \brief A compiler diagnostic message. */
53class DiagnosticNode : public Object {
54 public:
55 /*! \brief The level. */
56 DiagnosticLevel level;
57 /*! \brief The span at which to report an error. */
58 Span span;
59 /*! \brief The diagnostic message. */
60 String message;
61
62 // override attr visitor
63 void VisitAttrs(AttrVisitor* v) {
64 v->Visit("level", &level);
65 v->Visit("span", &span);
66 v->Visit("message", &message);
67 }
68
69 bool SEqualReduce(const DiagnosticNode* other, SEqualReducer equal) const {
70 return equal(this->level, other->level) && equal(this->span, other->span) &&
71 equal(this->message, other->message);
72 }
73
74 static constexpr const char* _type_key = "Diagnostic";
75 TVM_DECLARE_FINAL_OBJECT_INFO(DiagnosticNode, Object);
76};
77
78class Diagnostic : public ObjectRef {
79 public:
80 TVM_DLL Diagnostic(DiagnosticLevel level, Span span, const std::string& message);
81
82 static DiagnosticBuilder Bug(Span span);
83 static DiagnosticBuilder Error(Span span);
84 static DiagnosticBuilder Warning(Span span);
85 static DiagnosticBuilder Note(Span span);
86 static DiagnosticBuilder Help(Span span);
87
88 TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(Diagnostic, ObjectRef, DiagnosticNode);
89};
90
91/*!
92 * \brief A wrapper around std::stringstream to build a diagnostic.
93 */
94class DiagnosticBuilder {
95 public:
96 /*! \brief The level. */
97 DiagnosticLevel level;
98
99 /*! \brief The source name. */
100 SourceName source_name;
101
102 /*! \brief The span of the diagnostic. */
103 Span span;
104
105 template <typename T>
106 DiagnosticBuilder& operator<<(const T& val) { // NOLINT(*)
107 stream_ << val;
108 return *this;
109 }
110
111 DiagnosticBuilder() : level(DiagnosticLevel::kError), source_name(), span(Span()) {}
112
113 DiagnosticBuilder(const DiagnosticBuilder& builder)
114 : level(builder.level), source_name(builder.source_name), span(builder.span) {}
115
116 DiagnosticBuilder(DiagnosticLevel level, Span span) : level(level), span(span) {}
117
118 operator Diagnostic() { return Diagnostic(this->level, this->span, this->stream_.str()); }
119
120 private:
121 std::stringstream stream_;
122 friend class Diagnostic;
123};
124
125/*!
126 * \brief A diagnostic context for recording errors against a source file.
127 */
128class DiagnosticContext;
129
130/*! \brief Display diagnostics in a given display format.
131 *
132 * A diagnostic renderer is responsible for converting the
133 * raw diagnostics into consumable output.
134 *
135 * For example the terminal renderer will render a sequence
136 * of compiler diagnostics to std::out and std::err in
137 * a human readable form.
138 */
139class DiagnosticRendererNode : public Object {
140 public:
141 TypedPackedFunc<void(DiagnosticContext ctx)> renderer;
142
143 // override attr visitor
144 void VisitAttrs(AttrVisitor* v) {}
145
146 static constexpr const char* _type_key = "DiagnosticRenderer";
147 TVM_DECLARE_FINAL_OBJECT_INFO(DiagnosticRendererNode, Object);
148};
149
150class DiagnosticRenderer : public ObjectRef {
151 public:
152 TVM_DLL DiagnosticRenderer(TypedPackedFunc<void(DiagnosticContext ctx)> render);
153 TVM_DLL DiagnosticRenderer()
154 : DiagnosticRenderer(TypedPackedFunc<void(DiagnosticContext ctx)>()) {}
155
156 void Render(const DiagnosticContext& ctx);
157
158 DiagnosticRendererNode* operator->() {
159 ICHECK(get() != nullptr);
160 return static_cast<DiagnosticRendererNode*>(get_mutable());
161 }
162
163 TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(DiagnosticRenderer, ObjectRef, DiagnosticRendererNode);
164};
165
166class DiagnosticContextNode : public Object {
167 public:
168 /*! \brief The Module to report against. */
169 IRModule module;
170
171 /*! \brief The set of diagnostics to report. */
172 Array<Diagnostic> diagnostics;
173
174 /*! \brief The renderer set for the context. */
175 DiagnosticRenderer renderer;
176
177 void VisitAttrs(AttrVisitor* v) {
178 v->Visit("module", &module);
179 v->Visit("diagnostics", &diagnostics);
180 }
181
182 bool SEqualReduce(const DiagnosticContextNode* other, SEqualReducer equal) const {
183 return equal(module, other->module) && equal(diagnostics, other->diagnostics);
184 }
185
186 static constexpr const char* _type_key = "DiagnosticContext";
187 TVM_DECLARE_FINAL_OBJECT_INFO(DiagnosticContextNode, Object);
188};
189
190class DiagnosticContext : public ObjectRef {
191 public:
192 TVM_DLL DiagnosticContext(const IRModule& module, const DiagnosticRenderer& renderer);
193 TVM_DLL static DiagnosticContext Default(const IRModule& source_map);
194
195 /*! \brief Emit a diagnostic.
196 * \param diagnostic The diagnostic to emit.
197 */
198 void Emit(const Diagnostic& diagnostic);
199
200 /*! \brief Emit a diagnostic and then immediately attempt to render all errors.
201 *
202 * \param diagnostic The diagnostic to emit.
203 *
204 * Note: this will raise an exception if you would like to instead continue execution
205 * use the Emit method instead.
206 */
207 void EmitFatal(const Diagnostic& diagnostic);
208
209 /*! \brief Render the errors and raise a DiagnosticError exception. */
210 void Render();
211
212 DiagnosticContextNode* operator->() {
213 ICHECK(get() != nullptr);
214 return static_cast<DiagnosticContextNode*>(get_mutable());
215 }
216
217 TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(DiagnosticContext, ObjectRef, DiagnosticContextNode);
218};
219
220DiagnosticRenderer TerminalRenderer(std::ostream& ostream);
221
222} // namespace tvm
223#endif // TVM_IR_DIAGNOSTIC_H_
224