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;
}