Commit 2fd7ea79 authored by Kevin Modzelewski's avatar Kevin Modzelewski Committed by Boxiang Sun

Fix instancemethod comparisons

We had implemented instancemethod.__eq__, but not any of the other comparisons,
and most importantly, not __hash__.
parent 17b548d9
...@@ -1872,25 +1872,35 @@ static Box* instancemethodRepr(Box* b) { ...@@ -1872,25 +1872,35 @@ static Box* instancemethodRepr(Box* b) {
return result; return result;
} }
Box* instancemethodEq(BoxedInstanceMethod* self, Box* rhs) { static int instancemethod_compare(BoxedInstanceMethod* a, BoxedInstanceMethod* b) noexcept {
if (rhs->cls != instancemethod_cls) { int cmp;
return boxBool(false); cmp = PyObject_Compare(a->func, b->func);
} if (cmp)
return cmp;
BoxedInstanceMethod* rhs_im = static_cast<BoxedInstanceMethod*>(rhs); if (a->obj == b->obj)
if (self->func == rhs_im->func) { return 0;
if (self->obj == NULL && rhs_im->obj == NULL) { if (a->obj == NULL || b->obj == NULL)
return boxBool(true); return (a->obj < b->obj) ? -1 : 1;
} else { else
if (self->obj != NULL && rhs_im->obj != NULL) { return PyObject_Compare(a->obj, b->obj);
return compareInternal<NOT_REWRITABLE>(self->obj, rhs_im->obj, AST_TYPE::Eq, NULL); }
} else {
return boxBool(false); Box* instancemethodHash(BoxedInstanceMethod* self) {
} long x, y;
} if (self->obj == NULL)
} else { x = PyObject_Hash(Py_None);
return boxBool(false); else
} x = PyObject_Hash(self->obj);
if (x == -1)
throwCAPIException();
y = PyObject_Hash(self->func);
if (y == -1)
throwCAPIException();
x = x ^ y;
if (x == -1)
x = -2;
return boxInt(x);
} }
Box* sliceRepr(BoxedSlice* self) { Box* sliceRepr(BoxedSlice* self) {
...@@ -4538,8 +4548,8 @@ void setupRuntime() { ...@@ -4538,8 +4548,8 @@ void setupRuntime() {
{ NULL })); { NULL }));
instancemethod_cls->giveAttr( instancemethod_cls->giveAttr(
"__repr__", new BoxedFunction(BoxedCode::create((void*)instancemethodRepr, STR, 1, "instancemethod.__repr__"))); "__repr__", new BoxedFunction(BoxedCode::create((void*)instancemethodRepr, STR, 1, "instancemethod.__repr__")));
instancemethod_cls->giveAttr( instancemethod_cls->giveAttr("__hash__", new BoxedFunction(BoxedCode::create((void*)instancemethodHash, UNKNOWN, 1,
"__eq__", new BoxedFunction(BoxedCode::create((void*)instancemethodEq, UNKNOWN, 2, "instancemethod.__eq__"))); "instancemethod.__hash__")));
instancemethod_cls->giveAttr("__get__", instancemethod_cls->giveAttr("__get__",
new BoxedFunction(BoxedCode::create((void*)instancemethodGet, UNKNOWN, 3, false, false, new BoxedFunction(BoxedCode::create((void*)instancemethodGet, UNKNOWN, 3, false, false,
"instancemethod.__get__"))); "instancemethod.__get__")));
...@@ -4556,6 +4566,9 @@ void setupRuntime() { ...@@ -4556,6 +4566,9 @@ void setupRuntime() {
// TODO: this should be handled via a getattro instead (which proxies to the function): // TODO: this should be handled via a getattro instead (which proxies to the function):
instancemethod_cls->giveAttrDescriptor("__doc__", im_doc, NULL); instancemethod_cls->giveAttrDescriptor("__doc__", im_doc, NULL);
instancemethod_cls->tp_compare = (cmpfunc)instancemethod_compare;
add_operators(instancemethod_cls);
instancemethod_cls->freeze(); instancemethod_cls->freeze();
slice_cls->giveAttr("__new__", slice_cls->giveAttr("__new__",
......
...@@ -45,3 +45,28 @@ f(C.foo) ...@@ -45,3 +45,28 @@ f(C.foo)
f(C().foo) f(C().foo)
C().foo.__call__() C().foo.__call__()
# Check comparisons and hashing:
class C(object):
def foo(self):
pass
assert C.foo is not C.foo # This could be fine, but if it's true then the rest of the checks here don't make sense
assert C.foo == C.foo
assert not (C.foo != C.foo)
assert not (C.foo < C.foo)
assert not (C.foo > C.foo)
assert C.foo >= C.foo
assert C.foo <= C.foo
assert len({C.foo, C.foo}) == 1
c = C()
assert c.foo is not c.foo # This could be fine, but if it's true then the rest of the checks here don't make sense
assert c.foo == c.foo
assert not (c.foo != c.foo)
assert not (c.foo < c.foo)
assert not (c.foo > c.foo)
assert c.foo >= c.foo
assert c.foo <= c.foo
assert len({c.foo, c.foo}) == 1
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