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 tvm/ir/error.h
22  * \brief Utilities for error tracking and reporting.
23  */
24 #ifndef TVM_IR_ERROR_H_
25 #define TVM_IR_ERROR_H_
26 
27 #include <tvm/ir/module.h>
28 #include <tvm/ir/span.h>
29 
30 #include <sstream>
31 #include <string>
32 #include <unordered_map>
33 #include <vector>
34 
35 namespace tvm {
36 /*!
37  * \brief A wrapper around std::stringstream to build error.
38  *
39  * Can be consumed by Error to construct an error.
40  *
41  * \code
42  *
43  * void ReportError(const Error& err);
44  *
45  * void Test(int number) {
46  *   // Use error reporter to construct an error.
47  *   ReportError(ErrorBuilder() << "This is an error number=" << number);
48  * }
49  *
50  * \endcode
51  */
52 struct ErrorBuilder {
53  public:
54   template <typename T>
55   ErrorBuilder& operator<<(const T& val) {  // NOLINT(*)
56     stream_ << val;
57     return *this;
58   }
59 
60  private:
61   std::stringstream stream_;
62   friend class Error;
63 };
64 
65 /*!
66  * \brief Custom Error class to be thrown during compilation.
67  */
68 class Error : public dmlc::Error {
69  public:
70   /*! \brief Location of the error */
71   Span span;
72   /*!
73    * \brief construct error from message.
74    * \param msg The message
75    */
Error(const std::string & msg)76   explicit Error(const std::string& msg) : dmlc::Error(msg), span(nullptr) {}
77   /*!
78    * \brief construct error from error builder.
79    * \param err The error builder
80    */
Error(const ErrorBuilder & err)81   Error(const ErrorBuilder& err) : dmlc::Error(err.stream_.str()), span(nullptr) {}  // NOLINT(*)
82   /*!
83    * \brief copy constructor.
84    * \param other The other ereor.
85    */
Error(const Error & other)86   Error(const Error& other) : dmlc::Error(other.what()), span(other.span) {}  // NOLINT(*)
87   /*!
88    * \brief default constructor. */
Error()89   Error() : dmlc::Error(""), span(nullptr) {}
90 };
91 
92 /*!
93  * \brief An abstraction around how errors are stored and reported.
94  * Designed to be opaque to users, so we can support a robust and simpler
95  * error reporting mode, as well as a more complex mode.
96  *
97  * The first mode is the most accurate: we report a Relay error at a specific
98  * Span, and then render the error message directly against a textual representation
99  * of the program, highlighting the exact lines in which it occurs. This mode is not
100  * implemented in this PR and will not work.
101  *
102  * The second mode is a general-purpose mode, which attempts to annotate the program's
103  * textual format with errors.
104  *
105  * The final mode represents the old mode, if we report an error that has no span or
106  * expression, we will default to throwing an exception with a textual representation
107  * of the error and no indication of where it occurred in the original program.
108  *
109  * The latter mode is not ideal, and the goal of the new error reporting machinery is
110  * to avoid ever reporting errors in this style.
111  */
112 class ErrorReporter {
113  public:
114   /*! \brief default constructor. */
ErrorReporter()115   ErrorReporter() : errors_(), node_to_error_() {}
116 
117   /*!
118    * \brief Report a tvm::Error.
119    *
120    * This API is useful for reporting spanned errors.
121    *
122    * \param err The error to report.
123    */
Report(const Error & err)124   void Report(const Error& err) {
125     if (!err.span.defined()) {
126       throw err;
127     }
128 
129     this->errors_.push_back(err);
130   }
131 
132   /*!
133    * \brief Report an error against a program, using the full program
134    * error reporting strategy.
135    *
136    * This error reporting method requires the global function in which
137    * to report an error, the expression to report the error on,
138    * and the error object.
139    *
140    * \param global The global function in which the expression is contained.
141    * \param node The expression or type to report the error at.
142    * \param err The error message to report.
143    */
ReportAt(const GlobalVar & global,const ObjectRef & node,std::stringstream & err)144   void ReportAt(const GlobalVar& global, const ObjectRef& node, std::stringstream& err) {
145     std::string err_msg = err.str();
146     this->ReportAt(global, node, Error(err_msg));
147   }
148 
149   /*!
150    * \brief Report an error against a program, using the full program
151    * error reporting strategy.
152    *
153    * This error reporting method requires the global function in which
154    * to report an error, the expression to report the error on,
155    * and the error object.
156    *
157    * \param global The global function in which the expression is contained.
158    * \param node The expression or type to report the error at.
159    * \param err The error to report.
160    */
161   void ReportAt(const GlobalVar& global, const ObjectRef& node, const Error& err);
162 
163   /*!
164    * \brief Render all reported errors and exit the program.
165    *
166    * This function should be used after executing a pass to render reported errors.
167    *
168    * It will build an error message from the set of errors, depending on the error
169    * reporting strategy.
170    *
171    * \param module The module to report errors on.
172    * \param use_color Controls whether to colorize the output.
173    */
174   void RenderErrors(const IRModule& module, bool use_color = true);
175 
AnyErrors()176   inline bool AnyErrors() { return errors_.size() != 0; }
177 
178  private:
179   std::vector<Error> errors_;
180   std::unordered_map<ObjectRef, std::vector<size_t>, ObjectPtrHash, ObjectPtrEqual> node_to_error_;
181   std::unordered_map<ObjectRef, GlobalVar, ObjectPtrHash, ObjectPtrEqual> node_to_gv_;
182 };
183 
184 }  // namespace tvm
185 #endif  // TVM_IR_ERROR_H_
186