This is an automated email from the ASF dual-hosted git repository.

junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 4c712ca  [RFC][Error][ABI] Update Error to enable future compact to 
cause chaining (#396)
4c712ca is described below

commit 4c712ca3ec72ad18c10e42e5ef8b7f91ec23a803
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Jan 11 12:26:05 2026 -0500

    [RFC][Error][ABI] Update Error to enable future compact to cause chaining 
(#396)
    
    This PR brings a backward compatible update to error ABI to enable
    possible future support of cause chaining. Specifically, we add two
    fields:
    
    - cause_chain is an optional field for chaining errors
    - extra_context can be used to optionally attach opaque object (e.g.
    python error) if needed.
    
    The change is backward compatible as we only append to the error field.
    Most of the existing usages will simply ignore the two fields and use a
    single error.
---
 include/tvm/ffi/c_api.h | 24 +++++++++++++++++
 include/tvm/ffi/error.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/ffi/error.cc        | 32 +++++++++++++++++++++++
 tests/cpp/test_error.cc | 11 ++++++++
 4 files changed, 136 insertions(+)

diff --git a/include/tvm/ffi/c_api.h b/include/tvm/ffi/c_api.h
index fda024f..4da0d4d 100644
--- a/include/tvm/ffi/c_api.h
+++ b/include/tvm/ffi/c_api.h
@@ -394,6 +394,16 @@ typedef struct {
    */
   void (*update_backtrace)(TVMFFIObjectHandle self, const TVMFFIByteArray* 
backtrace,
                            int32_t update_mode);
+  /*!
+   * \brief Optional cause error chain that caused this error to be raised.
+   * \note This handle is owned by the ErrorCell.
+   */
+  TVMFFIObjectHandle cause_chain;
+  /*!
+   * \brief Optional extra context that can be used to record additional info 
about the error.
+   * \note This handle is owned by the ErrorCell.
+   */
+  TVMFFIObjectHandle extra_context;
 } TVMFFIErrorCell;
 
 /*!
@@ -631,6 +641,20 @@ TVM_FFI_DLL void TVMFFIErrorSetRaisedFromCStrParts(const 
char* kind, const char*
 TVM_FFI_DLL int TVMFFIErrorCreate(const TVMFFIByteArray* kind, const 
TVMFFIByteArray* message,
                                   const TVMFFIByteArray* backtrace, 
TVMFFIObjectHandle* out);
 
+/*!
+ * \brief Create an initial error object with cause chain and extra context.
+ * \param kind The kind of the error.
+ * \param message The error message.
+ * \param backtrace The backtrace of the error.
+ * \param cause_chain The cause error chain that caused this error to be 
raised.
+ * \param extra_context The extra context that can be used to record 
additional information.
+ * \param out The output error object handle.
+ * \return 0 on success, nonzero on failure.
+ */
+TVM_FFI_DLL int TVMFFIErrorCreateWithCauseAndExtraContext(
+    const TVMFFIByteArray* kind, const TVMFFIByteArray* message, const 
TVMFFIByteArray* backtrace,
+    TVMFFIObjectHandle cause_chain, TVMFFIObjectHandle extra_context, 
TVMFFIObjectHandle* out);
+
 //------------------------------------------------------------
 // Section: DLPack support APIs
 //------------------------------------------------------------
diff --git a/include/tvm/ffi/error.h b/include/tvm/ffi/error.h
index 91d55c6..b3c2cef 100644
--- a/include/tvm/ffi/error.h
+++ b/include/tvm/ffi/error.h
@@ -32,6 +32,7 @@
 #include <cstring>
 #include <iostream>
 #include <memory>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -84,6 +85,20 @@ struct EnvErrorAlreadySet : public std::exception {};
  */
 class ErrorObj : public Object, public TVMFFIErrorCell {
  public:
+  ErrorObj() {
+    this->cause_chain = nullptr;
+    this->extra_context = nullptr;
+  }
+
+  ~ErrorObj() {
+    if (this->cause_chain != nullptr) {
+      details::ObjectUnsafe::DecRefObjectHandle(this->cause_chain);
+    }
+    if (this->extra_context != nullptr) {
+      details::ObjectUnsafe::DecRefObjectHandle(this->extra_context);
+    }
+  }
+
   /// \cond Doxygen_Suppress
   static constexpr const int32_t _type_index = TypeIndex::kTVMFFIError;
   TVM_FFI_DECLARE_OBJECT_INFO_STATIC(StaticTypeKey::kTVMFFIError, ErrorObj, 
Object);
@@ -146,6 +161,29 @@ class Error : public ObjectRef, public std::exception {
                                                   std::move(backtrace));
   }
 
+  /*!
+   * \brief Constructor
+   * \param kind The kind of the error.
+   * \param message The message of the error.
+   * \param backtrace The backtrace of the error.
+   * \param cause_chain The cause chain of the error.
+   * \param extra_context The extra context of the error.
+   */
+  Error(std::string kind, std::string message, std::string backtrace,
+        std::optional<Error> cause_chain, std::optional<ObjectRef> 
extra_context) {
+    ObjectPtr<ErrorObj> error_obj = make_object<details::ErrorObjFromStd>(
+        std::move(kind), std::move(message), std::move(backtrace));
+    if (cause_chain.has_value()) {
+      error_obj->cause_chain =
+          
details::ObjectUnsafe::MoveObjectRefToTVMFFIObjectPtr(*std::move(cause_chain));
+    }
+    if (extra_context.has_value()) {
+      error_obj->extra_context =
+          
details::ObjectUnsafe::MoveObjectRefToTVMFFIObjectPtr(*std::move(extra_context));
+    }
+    data_ = std::move(error_obj);
+  }
+
   /*!
    * \brief Constructor
    * \param kind The kind of the error.
@@ -173,6 +211,37 @@ class Error : public ObjectRef, public std::exception {
     return std::string(obj->message.data, obj->message.size);
   }
 
+  /*!
+
+   * \brief Get the cause chain of the error object.
+   * \return The cause chain of the error object.
+   */
+  std::optional<Error> cause_chain() const {
+    ErrorObj* obj = static_cast<ErrorObj*>(data_.get());
+    if (obj->cause_chain != nullptr) {
+      return details::ObjectUnsafe::ObjectRefFromObjectPtr<Error>(
+          details::ObjectUnsafe::ObjectPtrFromUnowned<ErrorObj>(
+              static_cast<Object*>(obj->cause_chain)));
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  /*!
+   * \brief Get the extra context of the error object.
+   * \return The extra context of the error object.
+   */
+  std::optional<ObjectRef> extra_context() const {
+    ErrorObj* obj = static_cast<ErrorObj*>(data_.get());
+    if (obj->extra_context != nullptr) {
+      return details::ObjectUnsafe::ObjectRefFromObjectPtr<ObjectRef>(
+          details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(
+              static_cast<Object*>(obj->extra_context)));
+    } else {
+      return std::nullopt;
+    }
+  }
+
   /*!
    * \brief Get the backtrace of the error object.
    * \return The backtrace of the error object.
diff --git a/src/ffi/error.cc b/src/ffi/error.cc
index 6358c75..8dd209c 100644
--- a/src/ffi/error.cc
+++ b/src/ffi/error.cc
@@ -111,3 +111,35 @@ int TVMFFIErrorCreate(const TVMFFIByteArray* kind, const 
TVMFFIByteArray* messag
   }
   TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIErrorCreate);
 }
+
+int TVMFFIErrorCreateWithCauseAndExtraContext(
+    const TVMFFIByteArray* kind, const TVMFFIByteArray* message, const 
TVMFFIByteArray* backtrace,
+    TVMFFIObjectHandle cause_chain, TVMFFIObjectHandle extra_context, 
TVMFFIObjectHandle* out) {
+  // log other errors to the logger
+  TVM_FFI_LOG_EXCEPTION_CALL_BEGIN();
+  try {
+    std::optional<tvm::ffi::Error> cause_chain_error;
+    if (cause_chain != nullptr) {
+      cause_chain_error = 
tvm::ffi::details::ObjectUnsafe::ObjectRefFromObjectPtr<tvm::ffi::Error>(
+          
tvm::ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<tvm::ffi::ErrorObj>(
+              static_cast<tvm::ffi::ErrorObj*>(cause_chain)));
+    }
+    std::optional<tvm::ffi::ObjectRef> extra_context_ref;
+    if (extra_context != nullptr) {
+      extra_context_ref =
+          
tvm::ffi::details::ObjectUnsafe::ObjectRefFromObjectPtr<tvm::ffi::ObjectRef>(
+              
tvm::ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<tvm::ffi::Object>(
+                  static_cast<tvm::ffi::Object*>(extra_context)));
+    }
+
+    tvm::ffi::Error error(std::string(kind->data, kind->size),
+                          std::string(message->data, message->size),
+                          std::string(backtrace->data, backtrace->size),
+                          std::move(cause_chain_error), 
std::move(extra_context_ref));
+    *out = 
tvm::ffi::details::ObjectUnsafe::MoveObjectRefToTVMFFIObjectPtr(std::move(error));
+    return 0;
+  } catch (const std::bad_alloc& e) {
+    return -1;
+  }
+  TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIErrorCreateWithCauseAndExtraContext);
+}
diff --git a/tests/cpp/test_error.cc b/tests/cpp/test_error.cc
index 283e4b9..6a520ee 100644
--- a/tests/cpp/test_error.cc
+++ b/tests/cpp/test_error.cc
@@ -118,4 +118,15 @@ TEST(Error, TracebackMostRecentCallLast) {
   Error error("TypeError", "here", "test0\ntest1\ntest2\n");
   EXPECT_EQ(error.TracebackMostRecentCallLast(), "test2\ntest1\ntest0\n");
 }
+
+TEST(Error, CauseChain) {
+  Error original_error("TypeError", "here", "test0");
+  Error cause_chain("ValueError", "cause", "test1", original_error, 
std::nullopt);
+  auto opt_cause = cause_chain.cause_chain();
+  EXPECT_TRUE(opt_cause.has_value());
+  if (opt_cause.has_value()) {
+    EXPECT_EQ(opt_cause->kind(), "TypeError");
+  }
+  EXPECT_TRUE(!cause_chain.extra_context().has_value());
+}
 }  // namespace

Reply via email to