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 | |
34 | namespace tvm { |
35 | |
36 | using tvm::runtime::TypedPackedFunc; |
37 | |
38 | /*! \brief The diagnostic level, controls the printing of the message. */ |
39 | enum class DiagnosticLevel : int { |
40 | kBug = 10, |
41 | kError = 20, |
42 | kWarning = 30, |
43 | kNote = 40, |
44 | kHelp = 50, |
45 | }; |
46 | |
47 | class DiagnosticBuilder; |
48 | |
49 | /*! \brief A compiler diagnostic. */ |
50 | class Diagnostic; |
51 | |
52 | /*! \brief A compiler diagnostic message. */ |
53 | class 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 | |
78 | class 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 | */ |
94 | class 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 | */ |
128 | class 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 | */ |
139 | class 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 | |
150 | class 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 | |
166 | class 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 | |
190 | class 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 | |
220 | DiagnosticRenderer TerminalRenderer(std::ostream& ostream); |
221 | |
222 | } // namespace tvm |
223 | #endif // TVM_IR_DIAGNOSTIC_H_ |
224 | |