Unify IR Node and Reference Class Naming Convention

In many node objects we introduced recently, we use the Node suffix to hold IR/AST nodes and no suffix for reference classes.

class RangeNode : public Object {
  Expr min;
  Expr extent;

class Range : public ObjectRef {
  // constructor
  Range(Expr begin, Expr end);
  // static factory.
  static Range make_by_min_extent(Expr min, extent);

In the above example:

  • RangeNode is the container object.
  • Range is the reference class.
  • New objects are constructed via constructors in the reference class.
  • We also define clear factory function ```make_by_min_extent`` in the reference class.

However, due to legacy reasons, we also have a few different styles in the codebase. In particular, some IR container object does have a Node suffix or a reference class. Because not every IR container object has a reference class, the references are constructed via static make functions in the object container class instead. The code block below shows an example:

class IntImm : public ExprNode {
  Expr make(DataType dtype, int64_t value);

Strawman’s Proposal

This RFC proposes to adopt a single style throughout the codebase. We propose to adopt the following convention as a strawman:

  • Always use Node as the suffix for IR node container objects.
  • Always introduce a reference class(which does not have suffix) to each container object.
  • Because we have a reference class for each object class, we can use a constructor to directly construct a reference class
  • In some instances where we need a clearly named factory function, we can put them as a static function of the corresponding reference class.

Notably, for object containers that are not part of IR, some end in Obj(e.g. ADTObj) and some end in Node(e.g. ModuleNode). This RFC will not seek to consolidate these names, but it would also be great to hear from everyone’s thoughts about this topic as well.

The main advantage of the proposed style is that the C++ code will be more consistent with the python version. It is also more natural to construct reference via constructors.

Once we agree on a convention, we can adopt the convention when creating new nodes and gradually refactor the codebase to the accepted convention.

Please share your thoughts.



What does it mean to “use a constructor to construct a ref class”? Current implementation mostly uses a factory function XXX::make or XXXNode::make like a constructor, in which we call make_object<XXXNode> to allocate space. Does it mean that we move this logic into the constructor?

For example, in the case when constructing SeqStmt(refclass), instead of

void Test() {
  auto seq = SeqStmtNode::make(list_of_stmt);
  // use seq latter

we can directly write

void Test() {
  SeqStmt seq(list_of_stmt);
  // use seq later
1 Like

I thought about it for a bit, but it is not quite clear to me how to deal with objects with multiple constructors. In this case, we are unable to register the those constructors as global packed functions, right?

(I don’t think it is a big problem though. Python doesn’t support multiple dispatch but it lives well)

In that case, we will need to register a PackedFunc that dispatches, or use lambda to rename these functions