Commit fee6818c authored by Kevin Modzelewski's avatar Kevin Modzelewski

Support multiple inheritance

The core functionality is to calculate and store tp_mro and tp_bases
instead of just tp_base.  This gives the runtime a bit harder of a
time to bootstrap itself since now a fully-built class depends on
a few more classes, so the bootstrapping section got larger:
- object_cls (base of the tp_base hierarchy)
- type_cls (base of the metaclass hierarchy)
- str_cls (for ht_name)
- tuple_cls (for tp_mro)
- list_cls (for calculating the mro)

There were a few places that needed to be updated now that we have
multiple inheritance:
- typeLookup()
- isSubclass()
- typeNew()
- super()

This change doesn't even attempt to add multiple inheritance rules
around old-style classes.
parent 8780a2bb
......@@ -1466,6 +1466,508 @@ static void add_operators(BoxedClass* cls) noexcept {
add_tp_new_wrapper(cls);
}
static void type_mro_modified(PyTypeObject* type, PyObject* bases) {
/*
Check that all base classes or elements of the mro of type are
able to be cached. This function is called after the base
classes or mro of the type are altered.
Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type
inherits from an old-style class, either directly or if it
appears in the MRO of a new-style class. No support either for
custom MROs that include types that are not officially super
types.
Called from mro_internal, which will subsequently be called on
each subclass when their mro is recursively updated.
*/
Py_ssize_t i, n;
int clear = 0;
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
return;
n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) {
PyObject* b = PyTuple_GET_ITEM(bases, i);
PyTypeObject* cls;
if (!PyType_Check(b)) {
clear = 1;
break;
}
cls = (PyTypeObject*)b;
if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || !PyType_IsSubtype(type, cls)) {
clear = 1;
break;
}
}
if (clear)
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG | Py_TPFLAGS_VALID_VERSION_TAG);
}
static int extra_ivars(PyTypeObject* type, PyTypeObject* base) noexcept {
size_t t_size = type->tp_basicsize;
size_t b_size = base->tp_basicsize;
assert(t_size >= b_size); /* Else type smaller than base! */
if (type->tp_itemsize || base->tp_itemsize) {
/* If itemsize is involved, stricter rules */
return t_size != b_size || type->tp_itemsize != base->tp_itemsize;
}
if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 && type->tp_weaklistoffset + sizeof(PyObject*) == t_size
&& type->tp_flags & Py_TPFLAGS_HEAPTYPE)
t_size -= sizeof(PyObject*);
if (type->tp_dictoffset && base->tp_dictoffset == 0 && type->tp_dictoffset + sizeof(PyObject*) == t_size
&& type->tp_flags & Py_TPFLAGS_HEAPTYPE)
t_size -= sizeof(PyObject*);
// Pyston change:
if (type->instancesHaveHCAttrs() && !base->instancesHaveHCAttrs())
t_size -= sizeof(HCAttrs);
return t_size != b_size;
}
static PyTypeObject* solid_base(PyTypeObject* type) noexcept {
PyTypeObject* base;
if (type->tp_base)
base = solid_base(type->tp_base);
else
base = object_cls;
if (extra_ivars(type, base))
return type;
else
return base;
}
PyTypeObject* best_base(PyObject* bases) noexcept {
Py_ssize_t i, n;
PyTypeObject* base, *winner, *candidate, *base_i;
PyObject* base_proto;
assert(PyTuple_Check(bases));
n = PyTuple_GET_SIZE(bases);
assert(n > 0);
base = NULL;
winner = NULL;
for (i = 0; i < n; i++) {
base_proto = PyTuple_GET_ITEM(bases, i);
if (PyClass_Check(base_proto))
continue;
if (!PyType_Check(base_proto)) {
PyErr_SetString(PyExc_TypeError, "bases must be types");
return NULL;
}
base_i = (PyTypeObject*)base_proto;
// Pyston change: we require things are already ready
if (base_i->tp_dict == NULL) {
assert(base_i->is_pyston_class);
#if 0
if (PyType_Ready(base_i) < 0)
return NULL;
#endif
}
candidate = solid_base(base_i);
if (winner == NULL) {
winner = candidate;
base = base_i;
} else if (PyType_IsSubtype(winner, candidate))
;
else if (PyType_IsSubtype(candidate, winner)) {
winner = candidate;
base = base_i;
} else {
PyErr_SetString(PyExc_TypeError, "multiple bases have "
"instance lay-out conflict");
return NULL;
}
}
if (base == NULL)
PyErr_SetString(PyExc_TypeError, "a new-style class can't have only classic bases");
return base;
}
static int fill_classic_mro(PyObject* mro, PyObject* cls) {
PyObject* bases, *base;
Py_ssize_t i, n;
assert(PyList_Check(mro));
assert(PyClass_Check(cls));
i = PySequence_Contains(mro, cls);
if (i < 0)
return -1;
if (!i) {
if (PyList_Append(mro, cls) < 0)
return -1;
}
Py_FatalError("unimplemented");
// We should add multiple inheritance for old-style classes
#if 0
bases = ((PyClassObject*)cls)->cl_bases;
assert(bases && PyTuple_Check(bases));
n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) {
base = PyTuple_GET_ITEM(bases, i);
if (fill_classic_mro(mro, base) < 0)
return -1;
}
return 0;
#endif
}
static PyObject* classic_mro(PyObject* cls) {
PyObject* mro;
assert(PyClass_Check(cls));
mro = PyList_New(0);
if (mro != NULL) {
if (fill_classic_mro(mro, cls) == 0)
return mro;
Py_DECREF(mro);
}
return NULL;
}
/*
Method resolution order algorithm C3 described in
"A Monotonic Superclass Linearization for Dylan",
by Kim Barrett, Bob Cassel, Paul Haahr,
David A. Moon, Keith Playford, and P. Tucker Withington.
(OOPSLA 1996)
Some notes about the rules implied by C3:
No duplicate bases.
It isn't legal to repeat a class in a list of base classes.
The next three properties are the 3 constraints in "C3".
Local precendece order.
If A precedes B in C's MRO, then A will precede B in the MRO of all
subclasses of C.
Monotonicity.
The MRO of a class must be an extension without reordering of the
MRO of each of its superclasses.
Extended Precedence Graph (EPG).
Linearization is consistent if there is a path in the EPG from
each class to all its successors in the linearization. See
the paper for definition of EPG.
*/
static int tail_contains(PyObject* list, int whence, PyObject* o) {
Py_ssize_t j, size;
size = PyList_GET_SIZE(list);
for (j = whence + 1; j < size; j++) {
if (PyList_GET_ITEM(list, j) == o)
return 1;
}
return 0;
}
static PyObject* class_name(PyObject* cls) {
PyObject* name = PyObject_GetAttrString(cls, "__name__");
if (name == NULL) {
PyErr_Clear();
Py_XDECREF(name);
name = PyObject_Repr(cls);
}
if (name == NULL)
return NULL;
if (!PyString_Check(name)) {
Py_DECREF(name);
return NULL;
}
return name;
}
static int check_duplicates(PyObject* list) {
Py_ssize_t i, j, n;
/* Let's use a quadratic time algorithm,
assuming that the bases lists is short.
*/
n = PyList_GET_SIZE(list);
for (i = 0; i < n; i++) {
PyObject* o = PyList_GET_ITEM(list, i);
for (j = i + 1; j < n; j++) {
if (PyList_GET_ITEM(list, j) == o) {
o = class_name(o);
PyErr_Format(PyExc_TypeError, "duplicate base class %s", o ? PyString_AS_STRING(o) : "?");
Py_XDECREF(o);
return -1;
}
}
}
return 0;
}
/* Raise a TypeError for an MRO order disagreement.
It's hard to produce a good error message. In the absence of better
insight into error reporting, report the classes that were candidates
to be put next into the MRO. There is some conflict between the
order in which they should be put in the MRO, but it's hard to
diagnose what constraint can't be satisfied.
*/
static void set_mro_error(PyObject* to_merge, int* remain) noexcept {
Py_ssize_t i, n, off, to_merge_size;
char buf[1000];
PyObject* k, *v;
PyObject* set = PyDict_New();
if (!set)
return;
to_merge_size = PyList_GET_SIZE(to_merge);
for (i = 0; i < to_merge_size; i++) {
PyObject* L = PyList_GET_ITEM(to_merge, i);
if (remain[i] < PyList_GET_SIZE(L)) {
PyObject* c = PyList_GET_ITEM(L, remain[i]);
if (PyDict_SetItem(set, c, Py_None) < 0) {
Py_DECREF(set);
return;
}
}
}
n = PyDict_Size(set);
off = PyOS_snprintf(buf, sizeof(buf), "Cannot create a \
consistent method resolution\norder (MRO) for bases");
i = 0;
while (PyDict_Next(set, &i, &k, &v) && (size_t)off < sizeof(buf)) {
PyObject* name = class_name(k);
off += PyOS_snprintf(buf + off, sizeof(buf) - off, " %s", name ? PyString_AS_STRING(name) : "?");
Py_XDECREF(name);
if (--n && (size_t)(off + 1) < sizeof(buf)) {
buf[off++] = ',';
buf[off] = '\0';
}
}
PyErr_SetString(PyExc_TypeError, buf);
Py_DECREF(set);
}
static int pmerge(PyObject* acc, PyObject* to_merge) noexcept {
Py_ssize_t i, j, to_merge_size, empty_cnt;
int* remain;
int ok;
to_merge_size = PyList_GET_SIZE(to_merge);
/* remain stores an index into each sublist of to_merge.
remain[i] is the index of the next base in to_merge[i]
that is not included in acc.
*/
remain = (int*)PyMem_MALLOC(SIZEOF_INT * to_merge_size);
if (remain == NULL)
return -1;
for (i = 0; i < to_merge_size; i++)
remain[i] = 0;
again:
empty_cnt = 0;
for (i = 0; i < to_merge_size; i++) {
PyObject* candidate;
PyObject* cur_list = PyList_GET_ITEM(to_merge, i);
if (remain[i] >= PyList_GET_SIZE(cur_list)) {
empty_cnt++;
continue;
}
/* Choose next candidate for MRO.
The input sequences alone can determine the choice.
If not, choose the class which appears in the MRO
of the earliest direct superclass of the new class.
*/
candidate = PyList_GET_ITEM(cur_list, remain[i]);
for (j = 0; j < to_merge_size; j++) {
PyObject* j_lst = PyList_GET_ITEM(to_merge, j);
if (tail_contains(j_lst, remain[j], candidate)) {
goto skip; /* continue outer loop */
}
}
ok = PyList_Append(acc, candidate);
if (ok < 0) {
PyMem_Free(remain);
return -1;
}
for (j = 0; j < to_merge_size; j++) {
PyObject* j_lst = PyList_GET_ITEM(to_merge, j);
if (remain[j] < PyList_GET_SIZE(j_lst) && PyList_GET_ITEM(j_lst, remain[j]) == candidate) {
remain[j]++;
}
}
goto again;
skip:
;
}
if (empty_cnt == to_merge_size) {
PyMem_FREE(remain);
return 0;
}
set_mro_error(to_merge, remain);
PyMem_FREE(remain);
return -1;
}
static PyObject* mro_implementation(PyTypeObject* type) noexcept {
Py_ssize_t i, n;
int ok;
PyObject* bases, *result;
PyObject* to_merge, *bases_aslist;
// Pyston change: we require things are already ready
if (type->tp_dict == NULL) {
assert(type->is_pyston_class);
#if 0
if (PyType_Ready(type) < 0)
return NULL;
#endif
}
/* Find a superclass linearization that honors the constraints
of the explicit lists of bases and the constraints implied by
each base class.
to_merge is a list of lists, where each list is a superclass
linearization implied by a base class. The last element of
to_merge is the declared list of bases.
*/
bases = type->tp_bases;
assert(type->tp_bases);
assert(type->tp_bases->cls == tuple_cls);
n = PyTuple_GET_SIZE(bases);
to_merge = PyList_New(n + 1);
if (to_merge == NULL)
return NULL;
for (i = 0; i < n; i++) {
PyObject* base = PyTuple_GET_ITEM(bases, i);
PyObject* parentMRO;
if (PyType_Check(base))
parentMRO = PySequence_List(((PyTypeObject*)base)->tp_mro);
else
parentMRO = classic_mro(base);
if (parentMRO == NULL) {
Py_DECREF(to_merge);
return NULL;
}
PyList_SET_ITEM(to_merge, i, parentMRO);
}
bases_aslist = PySequence_List(bases);
if (bases_aslist == NULL) {
Py_DECREF(to_merge);
return NULL;
}
/* This is just a basic sanity check. */
if (check_duplicates(bases_aslist) < 0) {
Py_DECREF(to_merge);
Py_DECREF(bases_aslist);
return NULL;
}
PyList_SET_ITEM(to_merge, n, bases_aslist);
result = Py_BuildValue("[O]", (PyObject*)type);
if (result == NULL) {
Py_DECREF(to_merge);
return NULL;
}
ok = pmerge(result, to_merge);
Py_DECREF(to_merge);
if (ok < 0) {
Py_DECREF(result);
return NULL;
}
return result;
}
// Pyston change: made this non-static
PyObject* mro_external(PyObject* self) noexcept {
PyTypeObject* type = (PyTypeObject*)self;
return mro_implementation(type);
}
static int mro_internal(PyTypeObject* type) noexcept {
PyObject* mro, *result, *tuple;
int checkit = 0;
if (Py_TYPE(type) == &PyType_Type) {
result = mro_implementation(type);
} else {
static PyObject* mro_str;
checkit = 1;
mro = lookup_method((PyObject*)type, "mro", &mro_str);
if (mro == NULL)
return -1;
result = PyObject_CallObject(mro, NULL);
Py_DECREF(mro);
}
if (result == NULL)
return -1;
tuple = PySequence_Tuple(result);
Py_DECREF(result);
if (tuple == NULL)
return -1;
if (checkit) {
Py_ssize_t i, len;
PyObject* cls;
PyTypeObject* solid;
solid = solid_base(type);
len = PyTuple_GET_SIZE(tuple);
for (i = 0; i < len; i++) {
PyTypeObject* t;
cls = PyTuple_GET_ITEM(tuple, i);
if (PyClass_Check(cls))
continue;
else if (!PyType_Check(cls)) {
PyErr_Format(PyExc_TypeError, "mro() returned a non-class ('%.500s')", Py_TYPE(cls)->tp_name);
Py_DECREF(tuple);
return -1;
}
t = (PyTypeObject*)cls;
if (!PyType_IsSubtype(solid, solid_base(t))) {
PyErr_Format(PyExc_TypeError, "mro() returned base with unsuitable layout ('%.500s')", t->tp_name);
Py_DECREF(tuple);
return -1;
}
}
}
type->tp_mro = tuple;
type_mro_modified(type, type->tp_mro);
/* corner case: the old-style super class might have been hidden
from the custom MRO */
type_mro_modified(type, type->tp_bases);
PyType_Modified(type);
return 0;
}
extern "C" int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) noexcept {
return isSubclass(a, b);
}
......@@ -1773,21 +2275,27 @@ static void inherit_slots(PyTypeObject* type, PyTypeObject* base) noexcept {
// and our internal type-creation endpoints (BoxedClass::BoxedClass()).
// TODO: Move more of the duplicated logic into here.
void commonClassSetup(BoxedClass* cls) {
if (!cls->tp_base) {
assert(cls == object_cls);
return;
if (cls->tp_bases == NULL) {
if (cls->tp_base)
cls->tp_bases = new BoxedTuple({ cls->tp_base });
else
cls->tp_bases = new BoxedTuple({});
}
inherit_special(cls, cls->tp_base);
/* Calculate method resolution order */
if (mro_internal(cls) < 0)
throwCAPIException();
// This is supposed to be over the MRO but we don't support multiple inheritance yet:
BoxedClass* b = cls->tp_base;
while (b) {
// Not sure when this could fail; maybe not in Pyston right now but apparently it can in CPython:
if (PyType_Check(b))
inherit_slots(cls, b);
if (cls->tp_base)
inherit_special(cls, cls->tp_base);
b = b->tp_base;
assert(cls->tp_mro);
assert(cls->tp_mro->cls == tuple_cls);
for (auto b : static_cast<BoxedTuple*>(cls->tp_mro)->elts) {
if (b == cls)
continue;
if (PyType_Check(b))
inherit_slots(cls, static_cast<BoxedClass*>(b));
}
}
......@@ -1796,6 +2304,8 @@ extern "C" void PyType_Modified(PyTypeObject* type) noexcept {
}
extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
ASSERT(!cls->is_pyston_class, "should not call this on Pyston classes");
gc::registerNonheapRootObject(cls);
// unhandled fields:
......
......@@ -24,6 +24,13 @@ bool update_slot(BoxedClass* self, const std::string& attr) noexcept;
void fixup_slot_dispatchers(BoxedClass* self) noexcept;
void commonClassSetup(BoxedClass* cls);
// We need to expose these due to our different file organization (they
// are defined as part of the CPython copied into typeobject.c, but used
// from Pyston code).
// We could probably unify things more but that's for later.
PyTypeObject* best_base(PyObject* bases) noexcept;
PyObject* mro_external(PyObject* self) noexcept;
}
#endif
......@@ -195,10 +195,11 @@ bool isInstance(Box* obj, BoxedClass* cls) {
extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) {
// TODO the class is allowed to override this using __subclasscheck__
while (child) {
if (child == parent)
assert(child->tp_mro);
assert(child->tp_mro->cls == tuple_cls);
for (auto b : static_cast<BoxedTuple*>(child->tp_mro)->elts) {
if (b == parent)
return true;
child = child->tp_base;
}
return false;
}
......@@ -314,7 +315,10 @@ BoxedClass::BoxedClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset
if (cls == NULL) {
assert(type_cls == NULL);
} else {
assert(isSubclass(cls, type_cls));
// The (cls == type_cls) part of the check is important because during bootstrapping
// we might not have set up enough stuff in order to do proper subclass checking,
// but those clases will either have cls == NULL or cls == type_cls
assert(cls == type_cls || isSubclass(cls, type_cls));
}
assert(tp_dealloc == NULL);
......@@ -375,15 +379,22 @@ BoxedHeapClass::BoxedHeapClass(BoxedClass* base, gcvisit_func gc_visit, int attr
BoxedHeapClass* BoxedHeapClass::create(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int instance_size, bool is_user_defined, const std::string& name) {
return create(metaclass, base, gc_visit, attrs_offset, instance_size, is_user_defined, new BoxedString(name));
return create(metaclass, base, gc_visit, attrs_offset, instance_size, is_user_defined, new BoxedString(name), NULL);
}
BoxedHeapClass* BoxedHeapClass::create(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int instance_size, bool is_user_defined, BoxedString* name) {
int instance_size, bool is_user_defined, BoxedString* name, BoxedTuple* bases) {
BoxedHeapClass* made = new (metaclass)
BoxedHeapClass(base, gc_visit, attrs_offset, instance_size, is_user_defined, name);
// While it might be ok if these were set, it'd indicate a difference in
// expectations as to who was going to calculate them:
assert(!made->tp_mro);
assert(!made->tp_bases);
made->tp_bases = bases;
made->finishInitialization();
assert(made->tp_mro);
return made;
}
......@@ -651,19 +662,45 @@ Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* re
RewriterVar* obj_saved = rewrite_args->obj;
val = cls->getattr(attr, rewrite_args);
assert(rewrite_args->out_success);
if (!val and cls->tp_base) {
auto _mro = cls->tp_mro;
assert(_mro->cls == tuple_cls);
BoxedTuple* mro = static_cast<BoxedTuple*>(_mro);
// Guarding approach:
// Guard on the value of the tp_mro slot, which should be a tuple and thus be
// immutable. Then we don't have to figure out the guards to emit that check
// the individual mro entries.
// We can probably move this guard to after we call getattr() on the given cls.
//
// TODO this can fail if we replace the mro with another mro that lives in the same
// address.
obj_saved->addAttrGuard(offsetof(BoxedClass, tp_mro), (intptr_t)mro);
for (auto base : mro->elts) {
rewrite_args->out_success = false;
rewrite_args->obj = obj_saved->getAttr(offsetof(BoxedClass, tp_base));
val = typeLookup(cls->tp_base, attr, rewrite_args);
if (base == cls) {
// Small optimization: don't have to load the class again since it was given to us in
// a register.
assert(rewrite_args->obj == obj_saved);
} else {
rewrite_args->obj = rewrite_args->rewriter->loadConst((intptr_t)base, Location::any());
}
val = base->getattr(attr, rewrite_args);
assert(rewrite_args->out_success);
if (val)
return val;
}
return val;
return NULL;
} else {
val = cls->getattr(attr, NULL);
if (!val and cls->tp_base)
return typeLookup(cls->tp_base, attr, NULL);
return val;
assert(cls->tp_mro);
assert(cls->tp_mro->cls == tuple_cls);
for (auto b : static_cast<BoxedTuple*>(cls->tp_mro)->elts) {
val = b->getattr(attr, NULL);
if (val)
return val;
}
return NULL;
}
}
......@@ -1516,9 +1553,8 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
// This doesn't belong here either:
if (strcmp(attr, "__bases__") == 0 && isSubclass(obj->cls, type_cls)) {
BoxedClass* cls = static_cast<BoxedClass*>(obj);
if (cls->tp_base)
return new BoxedTuple({ static_cast<BoxedClass*>(obj)->tp_base });
return EmptyTuple;
assert(cls->tp_bases);
return cls->tp_bases;
}
}
......@@ -2026,10 +2062,16 @@ extern "C" void dump(void* p) {
auto cls = static_cast<BoxedClass*>(b);
printf("Type name: %s\n", getFullNameOfClass(cls).c_str());
printf("MRO: %s", getFullNameOfClass(cls).c_str());
while (cls->tp_base) {
printf(" -> %s", getFullNameOfClass(cls->tp_base).c_str());
cls = cls->tp_base;
printf("MRO:");
if (cls->tp_mro && cls->tp_mro->cls == tuple_cls) {
bool first = true;
for (auto b : static_cast<BoxedTuple*>(cls->tp_mro)->elts) {
if (!first)
printf(" ->");
first = false;
printf(" %s", getFullNameOfClass(static_cast<BoxedClass*>(b)).c_str());
}
}
printf("\n");
}
......@@ -3567,10 +3609,10 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
if (!isSubclass(_cls->cls, type_cls))
raiseExcHelper(TypeError, "type.__new__(X): X is not a type object (%s)", getTypeName(_cls));
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
if (!isSubclass(cls, type_cls))
raiseExcHelper(TypeError, "type.__new__(%s): %s is not a subtype of type", getNameOfClass(cls),
getNameOfClass(cls));
BoxedClass* metatype = static_cast<BoxedClass*>(_cls);
if (!isSubclass(metatype, type_cls))
raiseExcHelper(TypeError, "type.__new__(%s): %s is not a subtype of type", getNameOfClass(metatype),
getNameOfClass(metatype));
if (arg2 == NULL) {
assert(arg3 == NULL);
......@@ -3587,28 +3629,62 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
RELEASE_ASSERT(arg1->cls == str_cls, "");
BoxedString* name = static_cast<BoxedString*>(arg1);
BoxedClass* base;
if (bases->elts.size() == 0) {
bases = new BoxedTuple({ object_cls });
}
RELEASE_ASSERT(bases->elts.size() == 1, "");
Box* _base = bases->elts[0];
RELEASE_ASSERT(isSubclass(_base->cls, type_cls), "");
base = static_cast<BoxedClass*>(_base);
// Ported from CPython:
int nbases = bases->elts.size();
BoxedClass* winner = metatype;
for (auto tmp : bases->elts) {
auto tmptype = tmp->cls;
if (tmptype == classobj_cls)
continue;
if (isSubclass(winner, tmptype))
continue;
if (isSubclass(tmptype, winner)) {
winner = tmptype;
continue;
}
raiseExcHelper(TypeError, "metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");
}
if ((base->tp_flags & Py_TPFLAGS_BASETYPE) == 0)
if (winner != metatype) {
if (getattr(winner, "__new__") != getattr(type_cls, "__new__")) {
RELEASE_ASSERT(0, "untested");
return callattr(winner, &new_str, CallattrFlags({.cls_only = true, .null_on_nonexistent = false }),
ArgPassSpec(3), arg1, arg2, arg3, _args + 1, NULL);
}
metatype = winner;
}
BoxedClass* base = best_base(bases);
checkAndThrowCAPIException();
assert(base);
if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE))
raiseExcHelper(TypeError, "type '%.100s' is not an acceptable base type", base->tp_name);
assert(isSubclass(base->cls, type_cls));
// TODO I don't think we have to implement the __slots__ memory savings behavior
// (we get almost all of that automatically with hidden classes), but adding a __slots__
// adds other restrictions (ex for multiple inheritance) that we won't end up enforcing.
// I guess it should be ok if we're more permissive?
// auto slots = PyDict_GetItemString(attr_dict, "__slots__");
// RELEASE_ASSERT(!slots, "__slots__ unsupported");
BoxedClass* made;
if (base->instancesHaveDictAttrs() || base->instancesHaveHCAttrs()) {
made = BoxedHeapClass::create(cls, base, NULL, base->attrs_offset, base->tp_basicsize, true, name);
made = BoxedHeapClass::create(metatype, base, NULL, base->attrs_offset, base->tp_basicsize, true, name, bases);
} else {
assert(base->tp_basicsize % sizeof(void*) == 0);
made = BoxedHeapClass::create(cls, base, NULL, base->tp_basicsize, base->tp_basicsize + sizeof(HCAttrs), true,
name);
made = BoxedHeapClass::create(metatype, base, NULL, base->tp_basicsize, base->tp_basicsize + sizeof(HCAttrs),
true, name, bases);
}
// TODO: how much of these should be in BoxedClass::finishInitialization()?
......
......@@ -16,6 +16,7 @@
#include <sstream>
#include "capi/types.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/objmodel.h"
......@@ -66,15 +67,68 @@ Box* superGetattribute(Box* _s, Box* _attr) {
}
if (!skip) {
// We don't support multiple inheritance yet, so the lookup order is simple:
Box* r = typeLookup(s->type->tp_base, attr->s, NULL);
if (r) {
return processDescriptor(r, (s->obj == s->obj_type ? None : s->obj), s->obj_type);
PyObject* mro, *res, *tmp, *dict;
PyTypeObject* starttype;
descrgetfunc f;
Py_ssize_t i, n;
starttype = s->obj_type;
mro = starttype->tp_mro;
if (mro == NULL)
n = 0;
else {
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
}
for (i = 0; i < n; i++) {
if ((PyObject*)(s->type) == PyTuple_GET_ITEM(mro, i))
break;
}
i++;
res = NULL;
for (; i < n; i++) {
tmp = PyTuple_GET_ITEM(mro, i);
// Pyston change:
#if 0
if (PyType_Check(tmp))
dict = ((PyTypeObject *)tmp)->tp_dict;
else if (PyClass_Check(tmp))
dict = ((PyClassObject *)tmp)->cl_dict;
else
continue;
res = PyDict_GetItem(dict, name);
#endif
res = tmp->getattr(attr->s);
if (res != NULL) {
// Pyston change:
#if 0
Py_INCREF(res);
f = Py_TYPE(res)->tp_descr_get;
if (f != NULL) {
tmp = f(res,
/* Only pass 'obj' param if
this is instance-mode sper
(See SF ID #743627)
*/
(s->obj == (PyObject *)
s->obj_type
? (PyObject *)NULL
: s->obj),
(PyObject *)starttype);
Py_DECREF(res);
res = tmp;
}
#endif
return processDescriptor(res, (s->obj == s->obj_type ? None : s->obj), s->obj_type);
}
}
}
Box* r = typeLookup(s->cls, attr->s, NULL);
// TODO implement this
RELEASE_ASSERT(r, "should call the equivalent of objectGetattr here");
return processDescriptor(r, s, s->cls);
}
......
......@@ -97,10 +97,16 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
assert(static_cast<BoxedClass*>(e)->is_pyston_class);
}
#endif
BoxedClass* b = cls;
while (b) {
ASSERT(b->is_pyston_class, "%s (%s)", cls->tp_name, b->tp_name);
b = b->tp_base;
if (!cls->tp_mro) {
assert(!list_cls);
} else {
assert(cls->tp_mro && "maybe we should just skip these checks if !mro");
assert(cls->tp_mro->cls == tuple_cls);
for (auto b : static_cast<BoxedTuple*>(cls->tp_mro)->elts) {
assert(isSubclass(b->cls, type_cls));
ASSERT(static_cast<BoxedClass*>(b)->is_pyston_class, "%s (%s)", cls->tp_name,
static_cast<BoxedClass*>(b)->tp_name);
}
}
#endif
......@@ -903,6 +909,15 @@ Box* typeHash(BoxedClass* self) {
return boxInt(reinterpret_cast<intptr_t>(self) >> 4);
}
Box* typeMro(BoxedClass* self) {
assert(isSubclass(self->cls, type_cls));
Box* r = mro_external(self);
if (!r)
throwCAPIException();
return r;
}
Box* moduleRepr(BoxedModule* m) {
assert(m->cls == module_cls);
......@@ -1229,19 +1244,15 @@ void setupRuntime() {
PyObject_Init(object_cls, type_cls);
PyObject_Init(type_cls, type_cls);
object_cls->finishInitialization();
type_cls->finishInitialization();
none_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, sizeof(Box), false, NULL);
none_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(Box), false, NULL);
None = new (none_cls) Box();
assert(None->cls);
gc::registerPermanentRoot(None);
// You can't actually have an instance of basestring
basestring_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, sizeof(Box), false, NULL);
basestring_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(Box), false, NULL);
// TODO we leak all the string data!
str_cls = BoxedHeapClass::create(type_cls, basestring_cls, NULL, 0, sizeof(BoxedString), false, NULL);
str_cls = new BoxedHeapClass(basestring_cls, NULL, 0, sizeof(BoxedString), false, NULL);
// Hold off on assigning names until str_cls is ready
object_cls->tp_name = "object";
......@@ -1268,9 +1279,26 @@ void setupRuntime() {
object_cls->giveAttr("__base__", None);
tuple_cls = BoxedHeapClass::create(type_cls, object_cls, &tupleGCHandler, 0, sizeof(BoxedTuple), false, "tuple");
tuple_cls = new BoxedHeapClass(object_cls, &tupleGCHandler, 0, sizeof(BoxedTuple), false, boxStrConstant("tuple"));
EmptyTuple = new BoxedTuple({});
gc::registerPermanentRoot(EmptyTuple);
list_cls = new BoxedHeapClass(object_cls, &listGCHandler, 0, sizeof(BoxedList), false, boxStrConstant("list"));
// Kind of hacky, but it's easier to manually construct the mro for a couple key classes
// than try to make the MRO construction code be safe against say, tuple_cls not having
// an mro (since the mro is stored as a tuple).
tuple_cls->tp_mro = new BoxedTuple({ tuple_cls, object_cls });
list_cls->tp_mro = new BoxedTuple({ list_cls, object_cls });
type_cls->tp_mro = new BoxedTuple({ type_cls, object_cls });
object_cls->finishInitialization();
type_cls->finishInitialization();
basestring_cls->finishInitialization();
str_cls->finishInitialization();
none_cls->finishInitialization();
tuple_cls->finishInitialization();
list_cls->finishInitialization();
module_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, offsetof(BoxedModule, attrs), sizeof(BoxedModule),
......@@ -1357,6 +1385,9 @@ void setupRuntime() {
type_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)typeRepr, STR, 1)));
type_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)typeHash, BOXED_INT, 1)));
type_cls->giveAttr("__module__", new (pyston_getset_cls) BoxedGetsetDescriptor(typeModule, typeSetModule, NULL));
type_cls->giveAttr("__mro__",
new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, offsetof(BoxedClass, tp_mro)));
type_cls->giveAttr("mro", new BoxedFunction(boxRTFunction((void*)typeMro, UNKNOWN, 1)));
type_cls->freeze();
none_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)noneRepr, STR, 1)));
......
......@@ -213,9 +213,9 @@ public:
BoxedString* ht_name;
PyObject** ht_slots;
// This is the preferred way to construct new types:
// These functions are the preferred way to construct new types:
static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int instance_size, bool is_user_defined, BoxedString* name);
int instance_size, bool is_user_defined, BoxedString* name, BoxedTuple* bases);
static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int instance_size, bool is_user_defined, const std::string& name);
......@@ -227,6 +227,8 @@ private:
BoxedString* name);
friend void setupRuntime();
DEFAULT_CLASS(type_cls);
};
static_assert(sizeof(pyston::Box) == sizeof(struct _object), "");
......
# expected: fail
# - wip
# Testing the basic multiple-inheritance rules and functionality:
class C(object):
......@@ -92,3 +89,6 @@ s = S()
s.c()
s.d()
s.f()
for cls in [object, tuple, list, type, int, bool]:
print cls.__mro__
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment