RawObject typeNew()

in runtime/type-builtins.cpp [826:1040]


RawObject typeNew(Thread* thread, const Type& metaclass, const Str& name,
                  const Tuple& bases, const Dict& dict, word flags,
                  bool inherit_slots, bool add_instance_dict) {
  HandleScope scope(thread);
  Runtime* runtime = thread->runtime();

  LayoutId metaclass_id = Layout::cast(metaclass.instanceLayout()).id();
  Type type(&scope, runtime->newTypeWithMetaclass(metaclass_id));
  type.setName(*name);
  type.setBases(*bases);

  Object fixed_attr_base_obj(&scope, computeFixedAttributeBase(thread, bases));
  if (fixed_attr_base_obj.isErrorException()) return *fixed_attr_base_obj;
  Type fixed_attr_base(&scope, *fixed_attr_base_obj);

  // Determine metaclass.mro method. Set `mro_method` to `None` if it is the
  // default `type.mro`.
  Object mro_method(&scope, Unbound::object());
  if (metaclass_id != LayoutId::kType) {
    mro_method = Interpreter::lookupMethod(thread, type, ID(mro));
    if (mro_method.isErrorException()) return *mro_method;
    if (mro_method.isErrorNotFound()) {
      Object mro_name(&scope, runtime->symbols()->at(ID(mro)));
      return objectRaiseAttributeError(thread, metaclass, mro_name);
    }
    Type builtin_type(&scope, runtime->typeAt(LayoutId::kType));
    if (mro_method == typeAtById(thread, builtin_type, ID(mro))) {
      mro_method = Unbound::object();
    }
  }
  Object mro_obj(&scope, NoneType::object());
  if (mro_method.isUnbound()) {
    mro_obj = computeMro(thread, type);
    if (mro_obj.isErrorException()) return *mro_obj;
  } else {
    flags |= Type::Flag::kHasCustomMro;
    mro_obj = Interpreter::callMethod1(thread, mro_method, type);
    if (mro_obj.isErrorException()) return *mro_obj;
    if (!mro_obj.isTuple()) {
      mro_obj = thread->invokeFunction1(ID(builtins), ID(tuple), mro_obj);
      if (mro_obj.isErrorException()) return *mro_obj;
      CHECK(mro_obj.isTuple(), "Result of builtins.tuple should be tuple");
    }
  }

  Tuple mro(&scope, *mro_obj);
  type.setMro(*mro);

  Object result(&scope, typeAssignFromDict(thread, type, dict));
  if (result.isErrorException()) return *result;

  if (flags & Type::Flag::kIsCPythonHeaptype) {
    if (typeAtById(thread, type, ID(__module__)).isErrorNotFound()) {
      // Use depth=3 to skip over frame of `_type_init`, `type.__new__`
      // and `type.__call__`.
      Object module_name(&scope, getModuleNameAtFrame(thread, /*depth=*/3));
      if (!module_name.isErrorNotFound()) {
        typeAtPutById(thread, type, ID(__module__), module_name);
      }
    }
  } else {
    // Non-heap-types in CPython have no `__module__` unless there is a
    // "." in `tp_name`. Remove the attribute when it equals "builtins".
    Object module_name(&scope, typeAtById(thread, type, ID(__module__)));
    if (module_name.isStr() &&
        Str::cast(*module_name).equals(runtime->symbols()->at(ID(builtins)))) {
      typeRemoveById(thread, type, ID(__module__));
    }
  }

  Object qualname(&scope, typeRemoveById(thread, type, ID(__qualname__)));
  if (qualname.isErrorNotFound()) {
    qualname = *name;
  } else if (!runtime->isInstanceOfStr(*qualname)) {
    return thread->raiseWithFmt(LayoutId::kTypeError,
                                "type __qualname__ must be a str, not %T",
                                &qualname);
  }
  type.setQualname(*qualname);

  // TODO(T53997177): Centralize type initialization
  typeAddDocstring(thread, type);

  // Special-case __new__ to be a staticmethod
  Object dunder_new(&scope, typeAtById(thread, type, ID(__new__)));
  if (dunder_new.isFunction()) {
    StaticMethod dunder_new_method(&scope, runtime->newStaticMethod());
    dunder_new_method.setFunction(*dunder_new);
    typeAtPutById(thread, type, ID(__new__), dunder_new_method);
  }

  // Special-case __init_subclass__ to be a classmethod
  Object init_subclass(&scope, typeAtById(thread, type, ID(__init_subclass__)));
  if (init_subclass.isFunction()) {
    ClassMethod init_subclass_method(&scope, runtime->newClassMethod());
    init_subclass_method.setFunction(*init_subclass);
    typeAtPutById(thread, type, ID(__init_subclass__), init_subclass_method);
  }

  // Special-case __class_getitem__ to be a classmethod
  Object class_getitem(&scope, typeAtById(thread, type, ID(__class_getitem__)));
  if (class_getitem.isFunction()) {
    ClassMethod class_getitem_method(&scope, runtime->newClassMethod());
    class_getitem_method.setFunction(*class_getitem);
    typeAtPutById(thread, type, ID(__class_getitem__), class_getitem_method);
  }

  Object class_cell(&scope, typeAtById(thread, type, ID(__classcell__)));
  if (!class_cell.isErrorNotFound()) {
    DCHECK(class_cell.isCell(), "class cell must be a cell");
    Cell::cast(*class_cell).setValue(*type);
    Object class_cell_name(&scope, runtime->symbols()->at(ID(__classcell__)));
    typeRemove(thread, type, class_cell_name);
  }

  // Analyze bases: Merge flags; add to subclasses lists; check for attribute
  // dictionaries.
  Type base_type(&scope, *type);
  bool bases_have_instance_dict = false;
  bool bases_have_type_slots = false;
  word bases_flags = 0;
  for (word i = 0; i < bases.length(); i++) {
    base_type = bases.at(i);
    bases_flags |= base_type.flags();
    addSubclass(thread, base_type, type);
    bases_have_type_slots |= typeHasSlots(base_type);
    if (base_type.hasCustomDict()) bases_have_instance_dict = true;
    if (!Layout::cast(base_type.instanceLayout()).isSealed()) {
      bases_have_instance_dict = true;
    }
  }
  if (bases_have_instance_dict) add_instance_dict = false;
  flags |= (bases_flags & Type::kInheritableFlags);
  // Attribute flags are set explicitly here since `typeAssignFromDict` cannot
  // compute it properly with a partially created type object:
  // Computing this properly depends if Type::Flag::kHasCustomMro is set or not.
  for (SymbolId id : kAttributesForTypeFlags) {
    flags = computeAttributeTypeFlags(thread, type, id, flags);
  }
  // TODO(T66646764): This is a hack to make `type` look finalized. Remove this.
  type.setFlags(static_cast<Type::Flag>(flags));

  if (bases_have_type_slots) {
    if (inherit_slots) {
      result = typeInheritSlots(thread, type, fixed_attr_base);
      if (result.isErrorException()) return *result;
    }
  }

  Object dunder_slots_obj(&scope, typeAtById(thread, type, ID(__slots__)));
  Object slots_obj(&scope, NoneType::object());
  if (!dunder_slots_obj.isErrorNotFound()) {
    // NOTE: CPython raises an exception when slots are given to a subtype of a
    // type with type.tp_itemsize != 0, which means having a variable length.
    // For example, __slots__ in int's subtype or str's type is disallowed.
    // This behavior is ignored in Pyro since all objects' size in RawObject is
    // fixed in Pyro.
    if (runtime->isInstanceOfStr(*dunder_slots_obj)) {
      dunder_slots_obj = runtime->newTupleWith1(dunder_slots_obj);
    } else if (!runtime->isInstanceOfTuple(*dunder_slots_obj)) {
      Type tuple_type(&scope, runtime->typeAt(LayoutId::kTuple));
      dunder_slots_obj =
          Interpreter::call1(thread, tuple_type, dunder_slots_obj);
      if (dunder_slots_obj.isErrorException()) {
        return *dunder_slots_obj;
      }
      CHECK(dunder_slots_obj.isTuple(), "Converting __slots__ to tuple failed");
    }
    Tuple slots_tuple(&scope, *dunder_slots_obj);
    slots_obj = validateSlots(thread, type, slots_tuple,
                              bases_have_instance_dict, &add_instance_dict);
    if (slots_obj.isErrorException()) {
      return *slots_obj;
    }
    if (!slots_obj.isNoneType()) {
      List slots(&scope, *slots_obj);
      // Add descriptors that mediate access to __slots__ attributes.
      Object slot_descriptor(&scope, NoneType::object());
      Object slot_name(&scope, NoneType::object());
      for (word i = 0, num_items = slots.numItems(); i < num_items; i++) {
        slot_name = slots.at(i);
        slot_descriptor = runtime->newSlotDescriptor(type, slot_name);
        typeAtPut(thread, type, slot_name, slot_descriptor);
        slots.atPut(i, *slot_descriptor);
      }
    }
  }

  if (!slots_obj.isNoneType()) {
    flags |= Type::Flag::kHasSlots;
    flags |= Type::Flag::kIsFixedAttributeBase;
  }

  type.setFlags(static_cast<Type::Flag>(flags));

  // Add a `__dict__` descriptor when we added an instance dict.
  if (add_instance_dict &&
      typeAtById(thread, type, ID(__dict__)).isErrorNotFound()) {
    typeAddInstanceDict(thread, type);
  }

  Function type_dunder_call(&scope,
                            runtime->lookupNameInModule(thread, ID(_builtins),
                                                        ID(_type_dunder_call)));
  type.setCtor(*type_dunder_call);

  result = typeComputeLayout(thread, type, fixed_attr_base, add_instance_dict,
                             slots_obj);
  if (result.isErrorException()) return *result;

  result = typeSetNames(thread, type, dict);
  if (result.isErrorException()) return *result;

  return *type;
}