Skip to content

[Bug] Off-by-one error in the type index range check within Object::IsInstance() #17901

@ConvolutedDog

Description

@ConvolutedDog

Expected behavior

The expected output of the following test code should be:

child1node.IsInstance<ParentNode>(): 1
child2node.IsInstance<ParentNode>(): 1

The child1node and child2node both inherits from ParentNode, so they should both be instances of ParentNode.

Test code (main.cpp):

#include "tvm/runtime/object.h"
#include <iostream>

template <typename T> inline T InitObject() {
  static_assert(std::is_base_of<tvm::runtime::Object, T>::value,
                "can only be used to init Object");
  return T::Create();
}

template <typename Derived> class CreateHelper {
public:
  static Derived Create() {
    Derived obj{};
    obj.type_index_ = Derived::RuntimeTypeIndex();
    return obj;
  }
};

class ParentNode : public tvm::runtime::Object,
                   public CreateHelper<ParentNode> {
public:
  friend class CreateHelper<ParentNode>;
  static constexpr const uint32_t _type_index =
      tvm::runtime::TypeIndex::kDynamic;
  static constexpr const char *_type_key = "test.ParentNode";
  static const constexpr int _type_child_slots = 2;
  static const constexpr bool _type_child_slots_can_overflow = 0;
  TVM_DECLARE_BASE_OBJECT_INFO(ParentNode, tvm::runtime::Object);
};

class Child1Node : public ParentNode, public CreateHelper<Child1Node> {
public:
  friend class CreateHelper<Child1Node>;
  using CreateHelper<Child1Node>::Create;
  static constexpr const uint32_t _type_index =
      tvm::runtime::TypeIndex::kDynamic;
  static constexpr const char *_type_key = "test.Child1Node";
  TVM_DECLARE_FINAL_OBJECT_INFO(Child1Node, ParentNode);
};

class Child2Node : public ParentNode, public CreateHelper<Child2Node> {
public:
  friend class CreateHelper<Child2Node>;
  using CreateHelper<Child2Node>::Create;
  static constexpr const uint32_t _type_index =
      tvm::runtime::TypeIndex::kDynamic;
  static constexpr const char *_type_key = "test.Child2Node";
  TVM_DECLARE_FINAL_OBJECT_INFO(Child2Node, ParentNode);
};

/// This is not necessary.
TVM_REGISTER_OBJECT_TYPE(ParentNode);
TVM_REGISTER_OBJECT_TYPE(Child1Node);
TVM_REGISTER_OBJECT_TYPE(Child2Node);

#define LOG_PRINT_VAR(stmt) std::cout << #stmt << ": " << (stmt) << std::endl

int main() {
  ParentNode parentnode = InitObject<ParentNode>();
  Child1Node child1node = InitObject<Child1Node>();
  Child2Node child2node = InitObject<Child2Node>();

  LOG_PRINT_VAR(child1node.IsInstance<ParentNode>());
  LOG_PRINT_VAR(child2node.IsInstance<ParentNode>());

  return 0;
}

Actual behavior

child1node.IsInstance<ParentNode>(): 1
child2node.IsInstance<ParentNode>(): 0

Environment

Any environment details, such as: Operating System, TVM version, etc
Operating System: Linux
TVM version: main branch (commit 2ca6ec8, Mon Apr 21 15:17:16 2025)

Steps to reproduce

  • Save the test code as main.cpp.
  • Compile and run it:
    export TVM_HOME=/path/to/tvm
    clang++ main.cpp -I$TVM_HOME/include -I$TVM_HOME/3rdparty/dlpack/include -I$TVM_HOME/3rdparty/dmlc-core/include -L$TVM_HOME/build -ltvm
    ./a.out

Triage

  • core:object
  • runtime:c++

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triagePRs or issues that need to be investigated by maintainers to find the right assignees to address ittype: bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions