From: Tianqi Chen Date: Tue, 14 Jan 2020 04:16:03 +0000 (-0800) Subject: [REFACTOR][IR] Move error.h into ir (#4701) X-Git-Tag: upstream/0.7.0~1387 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=edc3674d89d9b2791be45e59c10a97c3395388c2;p=platform%2Fupstream%2Ftvm.git [REFACTOR][IR] Move error.h into ir (#4701) We will use a single ErrorReporter to report errors during program transformations. --- diff --git a/include/tvm/relay/error.h b/include/tvm/ir/error.h similarity index 66% rename from include/tvm/relay/error.h rename to include/tvm/ir/error.h index 1c91b6e..94064ae 100644 --- a/include/tvm/relay/error.h +++ b/include/tvm/ir/error.h @@ -18,12 +18,13 @@ */ /*! - * \file error.h - * \brief The set of errors raised by Relay. + * \file tvm/ir/error.h + * \brief Utilities for error tracking and reporting. */ -#ifndef TVM_RELAY_ERROR_H_ -#define TVM_RELAY_ERROR_H_ +#ifndef TVM_IR_ERROR_H_ +#define TVM_IR_ERROR_H_ +#include #include #include @@ -31,49 +32,65 @@ #include #include -#include "./base.h" -#include "./expr.h" - - namespace tvm { -namespace relay { - -#define RELAY_ERROR(msg) (RelayErrorStream() << msg) - -// Forward declaratio for RelayErrorStream. -struct Error; - -/*! \brief A wrapper around std::stringstream. +/*! + * \brief A wrapper around std::stringstream to build error. * - * This is designed to avoid platform specific - * issues compiling and using std::stringstream - * for error reporting. + * Can be consumed by Error to construct an error. + * + * \code + * + * void ReportError(const Error& err); + * + * void Test(int number) { + * // Use error reporter to construct an error. + * ReportError(ErrorBuilder() << "This is an error number=" << number); + * } + * + * \endcode */ -struct RelayErrorStream { - std::stringstream ss; - +struct ErrorBuilder { + public: template - RelayErrorStream& operator<<(const T& t) { - ss << t; + ErrorBuilder& operator<<(const T& val) { // NOLINT(*) + stream_ << val; return *this; } - std::string str() const { - return ss.str(); - } - - void Raise() const; + private: + std::stringstream stream_; + friend class Error; }; -struct Error : public dmlc::Error { - Span sp; - explicit Error(const std::string& msg) : dmlc::Error(msg), sp(nullptr) {} - Error(const RelayErrorStream& msg) : dmlc::Error(msg.str()), sp(nullptr) {} // NOLINT(*) - Error(const Error& err) : dmlc::Error(err.what()), sp(nullptr) {} - Error() : dmlc::Error(""), sp(nullptr) {} +/*! + * \brief Custom Error class to be thrown during compilation. + */ +class Error : public dmlc::Error { + public: + /*! \brief Location of the error */ + Span span; + /*! + * \brief construct error from message. + * \param msg The message + */ + explicit Error(const std::string& msg) : dmlc::Error(msg), span(nullptr) {} + /*! + * \brief construct error from error builder. + * \param err The error builder + */ + Error(const ErrorBuilder& err) : dmlc::Error(err.stream_.str()), span(nullptr) {} // NOLINT(*) + /*! + * \brief copy constructor. + * \param other The other ereor. + */ + Error(const Error& other) : dmlc::Error(other.what()), span(other.span) {} // NOLINT(*) + /*! + * \brief default constructor. */ + Error() : dmlc::Error(""), span(nullptr) {} }; -/*! \brief An abstraction around how errors are stored and reported. +/*! + * \brief An abstraction around how errors are stored and reported. * Designed to be opaque to users, so we can support a robust and simpler * error reporting mode, as well as a more complex mode. * @@ -94,23 +111,26 @@ struct Error : public dmlc::Error { */ class ErrorReporter { public: + /*! \brief default constructor. */ ErrorReporter() : errors_(), node_to_error_() {} - /*! \brief Report a tvm::relay::Error. + /*! + * \brief Report a tvm::Error. * * This API is useful for reporting spanned errors. * * \param err The error to report. */ void Report(const Error& err) { - if (!err.sp.defined()) { + if (!err.span.defined()) { throw err; } this->errors_.push_back(err); } - /*! \brief Report an error against a program, using the full program + /*! + * \brief Report an error against a program, using the full program * error reporting strategy. * * This error reporting method requires the global function in which @@ -121,12 +141,13 @@ class ErrorReporter { * \param node The expression or type to report the error at. * \param err The error message to report. */ - inline void ReportAt(const GlobalVar& global, const ObjectRef& node, std::stringstream& err) { + void ReportAt(const GlobalVar& global, const ObjectRef& node, std::stringstream& err) { std::string err_msg = err.str(); this->ReportAt(global, node, Error(err_msg)); } - /*! \brief Report an error against a program, using the full program + /*! + * \brief Report an error against a program, using the full program * error reporting strategy. * * This error reporting method requires the global function in which @@ -139,7 +160,8 @@ class ErrorReporter { */ void ReportAt(const GlobalVar& global, const ObjectRef& node, const Error& err); - /*! \brief Render all reported errors and exit the program. + /*! + * \brief Render all reported errors and exit the program. * * This function should be used after executing a pass to render reported errors. * @@ -161,7 +183,5 @@ class ErrorReporter { std::unordered_map node_to_gv_; }; -} // namespace relay } // namespace tvm - -#endif // TVM_RELAY_ERROR_H_ +#endif // TVM_IR_ERROR_H_ diff --git a/include/tvm/relay/expr_functor.h b/include/tvm/relay/expr_functor.h index f1d7152..68cef94 100644 --- a/include/tvm/relay/expr_functor.h +++ b/include/tvm/relay/expr_functor.h @@ -26,13 +26,16 @@ #define TVM_RELAY_EXPR_FUNCTOR_H_ #include +#include + #include #include #include + #include "./expr.h" #include "./adt.h" #include "./op.h" -#include "./error.h" + namespace tvm { namespace relay { diff --git a/include/tvm/relay/pattern_functor.h b/include/tvm/relay/pattern_functor.h index 71a024f..6e0fb17 100644 --- a/include/tvm/relay/pattern_functor.h +++ b/include/tvm/relay/pattern_functor.h @@ -26,12 +26,14 @@ #define TVM_RELAY_PATTERN_FUNCTOR_H_ #include +#include + #include #include #include + #include "./expr.h" #include "./op.h" -#include "./error.h" #include "./adt.h" namespace tvm { diff --git a/include/tvm/relay/transform.h b/include/tvm/relay/transform.h index d57740c..a740ea4 100644 --- a/include/tvm/relay/transform.h +++ b/include/tvm/relay/transform.h @@ -59,7 +59,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/relay/ir/error.cc b/src/ir/error.cc similarity index 88% rename from src/relay/ir/error.cc rename to src/ir/error.cc index 5c23f33..99db14e 100644 --- a/src/relay/ir/error.cc +++ b/src/ir/error.cc @@ -18,23 +18,24 @@ */ /*! - * \file error_reporter.h - * \brief The set of errors raised by Relay. + * \file ir/error.cc + * \brief Utilities for error tracking and reporting. */ -#include #include -#include +#include +// NOTE on dependencies on relay AsText. +// We calls into relay's printing module for better rendering. +// These dependency does not happen at the interface-level. +// And is only used to enhance developer experiences when relay +// functions are presented. +#include + #include #include #include namespace tvm { -namespace relay { - -void RelayErrorStream::Raise() const { - throw Error(*this); -} template using NodeMap = std::unordered_map; @@ -43,7 +44,7 @@ void ErrorReporter::RenderErrors(const IRModule& module, bool use_color) { // First we pick an error reporting strategy for each error. // TODO(@jroesch): Spanned errors are currently not supported. for (auto err : this->errors_) { - CHECK(!err.sp.defined()) << "attempting to use spanned errors, currently not supported"; + CHECK(!err.span.defined()) << "attempting to use spanned errors, currently not supported"; } NodeMap> error_maps; @@ -110,7 +111,7 @@ void ErrorReporter::RenderErrors(const IRModule& module, bool use_color) { // // The annotation callback will annotate the error messages // contained in the map. - annotated_prog << AsText(func, false, [&err_map](tvm::relay::Expr expr) { + annotated_prog << relay::AsText(func, false, [&err_map](tvm::relay::Expr expr) { auto it = err_map.find(expr); if (it != err_map.end()) { CHECK_NE(it->second.size(), 0); @@ -144,5 +145,4 @@ void ErrorReporter::ReportAt(const GlobalVar& global, const ObjectRef& node, con this->node_to_gv_.insert({ node, global }); } -} // namespace relay } // namespace tvm diff --git a/src/relay/backend/vm/compiler.cc b/src/relay/backend/vm/compiler.cc index ce3972f..be0ce5a 100644 --- a/src/relay/backend/vm/compiler.cc +++ b/src/relay/backend/vm/compiler.cc @@ -23,7 +23,7 @@ */ #include -#include +#include #include #include #include diff --git a/src/relay/backend/vm/compiler.h b/src/relay/backend/vm/compiler.h index 00bde11..07e704f 100644 --- a/src/relay/backend/vm/compiler.h +++ b/src/relay/backend/vm/compiler.h @@ -25,7 +25,7 @@ #ifndef TVM_RELAY_BACKEND_VM_COMPILER_H_ #define TVM_RELAY_BACKEND_VM_COMPILER_H_ -#include +#include #include #include #include diff --git a/src/relay/op/tensor/transform.cc b/src/relay/op/tensor/transform.cc index c9d824d..aa643c4 100644 --- a/src/relay/op/tensor/transform.cc +++ b/src/relay/op/tensor/transform.cc @@ -22,7 +22,7 @@ * \brief Transform operators. */ #include -#include +#include #include #include #include @@ -392,7 +392,7 @@ bool StackRel(const Array& types, for (size_t j = 0; j < first->shape.size(); ++j) { if (j == static_cast(axis)) continue; if (reporter->AssertEQ(first->shape[j], e->shape[j])) continue; - throw relay::Error("relay.stack requires all tensors have the same shape " + throw Error("relay.stack requires all tensors have the same shape " "on non-stacking axes"); } } diff --git a/src/relay/op/tensor/transform.h b/src/relay/op/tensor/transform.h index 74a630c..a1cbf7a 100644 --- a/src/relay/op/tensor/transform.h +++ b/src/relay/op/tensor/transform.h @@ -24,7 +24,7 @@ #ifndef TVM_RELAY_OP_TENSOR_TRANSFORM_H_ #define TVM_RELAY_OP_TENSOR_TRANSFORM_H_ -#include +#include #include #include #include @@ -48,10 +48,10 @@ bool ConcatenateRel(const Array& types, */ const auto* tensor_tuple = types[0].as(); if (tensor_tuple == nullptr) { - throw relay::Error( - RELAY_ERROR( - "concatenate requires a tuple of tensors as the first argument, found " - << PrettyPrint(types[0]))); + throw Error( + ErrorBuilder() + << "concatenate requires a tuple of tensors as the first argument, found " + << PrettyPrint(types[0])); } else if (types[0].as() != nullptr) { return false; } @@ -68,10 +68,10 @@ bool ConcatenateRel(const Array& types, // Sanity check: axis int axis = param->axis; if (!(-ndim <= axis && axis < ndim)) { - throw relay::Error(RELAY_ERROR( + throw Error(ErrorBuilder() << "concatenate only accepts `axis` in [-ndim, ndim)" << ", but got axis = " << axis << - ", and ndim = " << ndim)); + ", and ndim = " << ndim); } axis = axis < 0 ? ndim + axis : axis; @@ -85,16 +85,16 @@ bool ConcatenateRel(const Array& types, int e_ndim = static_cast(e->shape.size()); const DataType& e_dtype = e->dtype; if (e_ndim != ndim) { - throw relay::Error("relay.concatenate requires all tensors have the same ndim"); + throw Error("relay.concatenate requires all tensors have the same ndim"); } if (e_dtype != dtype) { - throw relay::Error("relay.concatenate requires all tensors have the same dtype"); + throw Error("relay.concatenate requires all tensors have the same dtype"); } for (size_t j = 0; j < first->shape.size(); ++j) { if (j == static_cast(axis)) continue; if (reporter->AssertEQ(first->shape[j], e->shape[j])) continue; - throw relay::Error("relay.concatenate requires all tensors have the same shape " - "on non-concatenating axes"); + throw Error("relay.concatenate requires all tensors have the same shape " + "on non-concatenating axes"); } } diff --git a/src/relay/op/type_relations.cc b/src/relay/op/type_relations.cc index 13baefb..fbaf665 100644 --- a/src/relay/op/type_relations.cc +++ b/src/relay/op/type_relations.cc @@ -93,9 +93,9 @@ Type ConcreteBroadcast(const TensorType& t1, } else if (EqualCheck(s1, s2)) { oshape.push_back(s1); } else { - RELAY_ERROR( - "Incompatible broadcast type " - << t1 << " and " << t2).Raise(); + throw Error(ErrorBuilder() + << "Incompatible broadcast type " + << t1 << " and " << t2); } } diff --git a/src/relay/op/type_relations.h b/src/relay/op/type_relations.h index f52bf78..80e555b 100644 --- a/src/relay/op/type_relations.h +++ b/src/relay/op/type_relations.h @@ -25,7 +25,7 @@ #ifndef TVM_RELAY_OP_TYPE_RELATIONS_H_ #define TVM_RELAY_OP_TYPE_RELATIONS_H_ -#include +#include #include #include diff --git a/src/relay/pass/kind_check.cc b/src/relay/pass/kind_check.cc index 2d207b5..55fd78a 100644 --- a/src/relay/pass/kind_check.cc +++ b/src/relay/pass/kind_check.cc @@ -32,7 +32,7 @@ * contains a data type such as `int`, `float`, `uint`. */ #include -#include +#include #include "../ir/type_functor.h" namespace tvm { @@ -55,11 +55,12 @@ struct KindChecker : TypeFunctor { Kind expected, const std::string& description) { Kind k = this->VisitType(t); if (k != expected) { - ReportFatalError(RELAY_ERROR("Incorrect kind for a " << description - << ". Type " << t << " inside " << outer - << " is of kind " << k - << " but was expected to be " - << expected)); + ReportFatalError(ErrorBuilder() + << "Incorrect kind for a " << description + << ". Type " << t << " inside " << outer + << " is of kind " << k + << " but was expected to be " + << expected); } } @@ -127,8 +128,9 @@ struct KindChecker : TypeFunctor { TypeCall tc = GetRef(op); const auto* gtv = op->func.as(); if (gtv == nullptr) { - ReportFatalError(RELAY_ERROR("The callee in " << tc - << " is not a global type var, but is " << op->func)); + ReportFatalError( + ErrorBuilder() <<"The callee in " << tc + << " is not a global type var, but is " << op->func); } CheckKindMatches(op->func, tc, Kind::kAdtHandle, "type call function"); @@ -141,8 +143,9 @@ struct KindChecker : TypeFunctor { auto var = GetRef(gtv); auto data = mod->LookupTypeDef(var); if (data->type_vars.size() != op->args.size()) { - ReportFatalError(RELAY_ERROR("Expected " << data->type_vars.size() << "arguments for " << tc - << "; got " << op->args.size())); + ReportFatalError(ErrorBuilder() + << "Expected " << data->type_vars.size() << "arguments for " << tc + << "; got " << op->args.size()); } return Kind::kType; } @@ -161,8 +164,9 @@ struct KindChecker : TypeFunctor { for (const auto& con : op->constructors) { if (!con->belong_to.same_as(op->header)) { - ReportFatalError(RELAY_ERROR(con << " has header " << con->belong_to - << " but " << op << " has header " << op->header)); + ReportFatalError(ErrorBuilder() + <belong_to + << " but " << op << " has header " << op->header); } for (const Type& t : con->inputs) { diff --git a/src/relay/pass/match_exhaustion.cc b/src/relay/pass/match_exhaustion.cc index 161b682..885c47e 100644 --- a/src/relay/pass/match_exhaustion.cc +++ b/src/relay/pass/match_exhaustion.cc @@ -28,7 +28,7 @@ * dynamic error unless exhaustiveness is checked in advance. */ #include -#include +#include #include #include #include diff --git a/src/relay/pass/type_infer.cc b/src/relay/pass/type_infer.cc index 876cf48..faf42ab 100644 --- a/src/relay/pass/type_infer.cc +++ b/src/relay/pass/type_infer.cc @@ -38,7 +38,7 @@ * constraints we will trigger an error. */ -#include +#include #include #include #include @@ -144,11 +144,12 @@ class TypeInferencer : private ExprFunctor, } catch (const dmlc::Error &e) { this->ReportFatalError( expr, - RELAY_ERROR("Error unifying `" + ErrorBuilder() + << "Error unifying `" << t1 << "` and `" << t2 - << "`: " << e.what())); + << "`: " << e.what()); return Type(); } } @@ -188,9 +189,9 @@ class TypeInferencer : private ExprFunctor, if (!mod_.defined()) { this->ReportFatalError( GetRef(op), - RELAY_ERROR( + ErrorBuilder() << "Cannot do type inference on global variables " \ - "without a module")); + "without a module"); } Expr e = mod_->Lookup(var); return e->checked_type(); @@ -239,16 +240,18 @@ class TypeInferencer : private ExprFunctor, auto* tc = unified.as(); if (!tc) { - this->ReportFatalError(pc, RELAY_ERROR("Expected a type call, got " << unified)); + this->ReportFatalError(pc, ErrorBuilder() << "Expected a type call, got " << unified); } if (td->header != tc->func) { - this->ReportFatalError(pc, RELAY_ERROR("ADT headers must match, but we have " - << td->header << " and " << tc->func)); + this->ReportFatalError(pc, + ErrorBuilder() << "ADT headers must match, but we have " + << td->header << " and " << tc->func); } if (td->type_vars.size() != tc->args.size()) { - this->ReportFatalError(pc, RELAY_ERROR("The number of type args must match" - << "the number of type vars in the type data: " - << td->type_vars.size() << " != " << tc->args.size())); + this->ReportFatalError(pc, + ErrorBuilder() << "The number of type args must match" + << "the number of type vars in the type data: " + << td->type_vars.size() << " != " << tc->args.size()); } std::unordered_map type_var_map_; for (size_t i = 0; i < td->type_vars.size(); ++i) { @@ -256,9 +259,10 @@ class TypeInferencer : private ExprFunctor, } CHECK(con->constructor->inputs.size() == con->patterns.size()) << "not enough pattern"; if (con->constructor->inputs.size() != con->patterns.size()) { - this->ReportFatalError(pc, RELAY_ERROR("Not enough inputs for the constructor; " - << "expected " << con->constructor->inputs.size() - << ", got " << con->patterns.size())); + this->ReportFatalError(pc, + ErrorBuilder() << "Not enough inputs for the constructor; " + << "expected " << con->constructor->inputs.size() + << ", got " << con->patterns.size()); } for (size_t i = 0; i < con->constructor->inputs.size(); ++i) { VisitPattern(con->patterns[i], Bind(con->constructor->inputs[i], type_var_map_)); @@ -278,7 +282,7 @@ class TypeInferencer : private ExprFunctor, auto* tt = unified.as(); if (!tt) { - this->ReportFatalError(pt, RELAY_ERROR("Expected a tuple type, got " << unified)); + this->ReportFatalError(pt, ErrorBuilder() << "Expected a tuple type, got " << unified); } CHECK(tup->patterns.size() == tt->fields.size()) << "not enough pattern"; for (size_t i = 0; i < tup->patterns.size(); ++i) { @@ -310,7 +314,7 @@ class TypeInferencer : private ExprFunctor, Match match = GetRef(op); Array unmatched_cases = UnmatchedCases(match, this->mod_); if (unmatched_cases.size() != 0) { - RelayErrorStream ss; + ErrorBuilder ss; ss << "match expression does not handle the following cases: "; int i = 0; for (auto cs : unmatched_cases) { @@ -454,8 +458,9 @@ class TypeInferencer : private ExprFunctor, if (fn_ty_node == nullptr && inc_ty_node == nullptr) { this->ReportFatalError( GetRef(call), - RELAY_ERROR("only expressions with function types can be called, found " - << ftype)); + ErrorBuilder() + << "only expressions with function types can be called, found " + << ftype); } // incomplete type => it must be a function taking the arg types @@ -470,11 +475,12 @@ class TypeInferencer : private ExprFunctor, Array type_args = call->type_args; if (type_args.size() > fn_ty_node->type_params.size()) { this->ReportFatalError(GetRef(call), - RELAY_ERROR("Incorrect number of type args in " + ErrorBuilder() + << "Incorrect number of type args in " << call->span << ": " << "Expected " << fn_ty_node->type_params.size() - << "but got " << type_args.size())); + << "but got " << type_args.size()); } FuncType fn_ty = InstantiateFuncType(fn_ty_node, type_args); @@ -488,13 +494,15 @@ class TypeInferencer : private ExprFunctor, if (type_arity < number_of_args) { this->ReportFatalError( GetRef(call), - RELAY_ERROR("the function is provided too many arguments " - << "expected " << type_arity << ", found " << number_of_args)); + ErrorBuilder() + << "the function is provided too many arguments " + << "expected " << type_arity << ", found " << number_of_args); } else { this->ReportFatalError( GetRef(call), - RELAY_ERROR("the function is provided too few arguments " - << "expected " << type_arity << ", found " << number_of_args)); + ErrorBuilder() + << "the function is provided too few arguments " + << "expected " << type_arity << ", found " << number_of_args); } } diff --git a/src/relay/pass/type_solver.cc b/src/relay/pass/type_solver.cc index 372b351..d0d8b43 100644 --- a/src/relay/pass/type_solver.cc +++ b/src/relay/pass/type_solver.cc @@ -124,10 +124,11 @@ class TypeSolver::Unifier : public TypeFunctor { } else { Type resolved = this->VisitType(lhs->resolved_type, rhs->resolved_type); if (!resolved.defined()) { - solver_->ReportError(RELAY_ERROR("unable to unify: " - << "`" << PrettyPrint(lhs->resolved_type) << "` and `" - << PrettyPrint(rhs->resolved_type) << "`"), - this->loc); + solver_->ReportError( + ErrorBuilder() << "unable to unify: " + << "`" << PrettyPrint(lhs->resolved_type) << "` and `" + << PrettyPrint(rhs->resolved_type) << "`", + this->loc); return lhs->resolved_type; } else { TypeNode* top = solver_->GetTypeNode(resolved); @@ -225,13 +226,13 @@ class TypeSolver::Unifier : public TypeFunctor { tvm::Array shape; if (tt1->shape.size() != tt2->shape.size()) { this->solver_->ReportError( - RELAY_ERROR( + ErrorBuilder() << "tensor type `" << PrettyPrint(tt1) << "` has " << tt1->shape.size() << " dimensions, while `" << PrettyPrint(tt2) << "` has " << tt2->shape.size() << - " dimensions"), this->loc); + " dimensions", this->loc); return Type(nullptr); } @@ -253,7 +254,7 @@ class TypeSolver::Unifier : public TypeFunctor { } if (mismatches.size() != 0) { - RelayErrorStream err; + ErrorBuilder err; err << "in particular "; for (auto mismatch : mismatches) { err << "dimension " @@ -639,10 +640,11 @@ bool TypeSolver::Solve() { rnode->resolved = false; } catch (const dmlc::Error& err) { rnode->resolved = false; - this->ReportError(RELAY_ERROR("an internal invariant was violated while " - "typechecking your program " - << err.what()), - rnode->location); + this->ReportError( + ErrorBuilder() << "an internal invariant was violated while " + << "typechecking your program " + << err.what(), + rnode->location); } // Mark inqueue as false after the function call diff --git a/src/relay/pass/type_solver.h b/src/relay/pass/type_solver.h index eba1bea..00a43ec 100644 --- a/src/relay/pass/type_solver.h +++ b/src/relay/pass/type_solver.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/relay/qnn/op/concatenate.cc b/src/relay/qnn/op/concatenate.cc index 685fb9f..26093f2 100644 --- a/src/relay/qnn/op/concatenate.cc +++ b/src/relay/qnn/op/concatenate.cc @@ -41,9 +41,10 @@ bool QnnConcatenateRel(const Array& types, int num_inputs, const Attrs& at // Check the scale and zero point types const auto* input_scales_tuple = types[1].as(); if (input_scales_tuple == nullptr) { - throw relay::Error( - RELAY_ERROR("qnn concatenate requires a tuple of scales as the second argument, found " - << PrettyPrint(types[1]))); + throw Error( + ErrorBuilder() + << "qnn concatenate requires a tuple of scales as the second argument, found " + << PrettyPrint(types[1])); } for (const auto& input_scale : input_scales_tuple->fields) { CHECK(IsScalarType(input_scale, DataType::Float(32))); // input_scales[idx] @@ -51,9 +52,10 @@ bool QnnConcatenateRel(const Array& types, int num_inputs, const Attrs& at const auto* input_zero_points_tuple = types[2].as(); if (input_zero_points_tuple == nullptr) { - throw relay::Error( - RELAY_ERROR("qnn concatenate requires a tuple of zero_points as the third argument, found " - << PrettyPrint(types[2]))); + throw Error( + ErrorBuilder() + << "qnn concatenate requires a tuple of zero_points as the third argument, found " + << PrettyPrint(types[2])); } for (const auto& input_zero_point : input_zero_points_tuple->fields) { CHECK(IsScalarType(input_zero_point, DataType::Int(32))); // input_zero_points[idx]