Commit 1505cc69 authored by Kevin Modzelewski's avatar Kevin Modzelewski

int() continues to be tricky

int(str) and int(float) don't always return ints (cant return longs, doh).
If we call int() on a subclass of int, we should call its __int__ method in
case the subclass overrode it.
parent 069a7014
......@@ -843,7 +843,19 @@ extern "C" Box* intTrunc(BoxedInt* self) {
raiseExcHelper(TypeError, "descriptor '__trunc__' requires a 'int' object but received a '%s'",
getTypeName(self));
return self;
if (self->cls == int_cls)
return self;
return boxInt(self->n);
}
extern "C" Box* intInt(BoxedInt* self) {
if (!isSubclass(self->cls, int_cls))
raiseExcHelper(TypeError, "descriptor '__int__' requires a 'int' object but received a '%s'",
getTypeName(self));
if (self->cls == int_cls)
return self;
return boxInt(self->n);
}
extern "C" Box* intIndex(BoxedInt* v) {
......@@ -853,7 +865,7 @@ extern "C" Box* intIndex(BoxedInt* v) {
}
static Box* _intNew(Box* val, Box* base) {
if (isSubclass(val->cls, int_cls)) {
if (val->cls == int_cls) {
RELEASE_ASSERT(!base, "");
BoxedInt* n = static_cast<BoxedInt*>(val);
if (val->cls == int_cls)
......@@ -890,8 +902,33 @@ static Box* _intNew(Box* val, Box* base) {
return r;
} else if (val->cls == float_cls) {
RELEASE_ASSERT(!base, "");
double d = static_cast<BoxedFloat*>(val)->d;
return new BoxedInt(d);
// This is tricky -- code copied from CPython:
double x = PyFloat_AsDouble(val);
double wholepart; /* integral portion of x, rounded toward 0 */
(void)modf(x, &wholepart);
/* Try to get out cheap if this fits in a Python int. The attempt
* to cast to long must be protected, as C doesn't define what
* happens if the double is too big to fit in a long. Some rare
* systems raise an exception then (RISCOS was mentioned as one,
* and someone using a non-default option on Sun also bumped into
* that). Note that checking for <= LONG_MAX is unsafe: if a long
* has more bits of precision than a double, casting LONG_MAX to
* double may yield an approximation, and if that's rounded up,
* then, e.g., wholepart=LONG_MAX+1 would yield true from the C
* expression wholepart<=LONG_MAX, despite that wholepart is
* actually greater than LONG_MAX. However, assuming a two's complement
* machine with no trap representation, LONG_MIN will be a power of 2 (and
* hence exactly representable as a double), and LONG_MAX = -1-LONG_MIN, so
* the comparisons with (double)LONG_MIN below should be safe.
*/
if ((double)LONG_MIN <= wholepart && wholepart < -(double)LONG_MIN) {
const long aslong = (long)wholepart;
return PyInt_FromLong(aslong);
}
return PyLong_FromDouble(wholepart);
} else {
RELEASE_ASSERT(!base, "");
static BoxedString* int_str = static_cast<BoxedString*>(PyString_InternFromString("__int__"));
......@@ -992,7 +1029,7 @@ static void _addFuncIntUnknown(const char* name, ConcreteCompilerType* rtn_type,
int_cls->giveAttr(name, new BoxedFunction(cl));
}
static Box* intInt(Box* b, void*) {
static Box* intIntGetset(Box* b, void*) {
if (b->cls == int_cls) {
return b;
} else {
......@@ -1081,6 +1118,7 @@ void setupInt() {
int_cls->giveAttr("__trunc__", new BoxedFunction(boxRTFunction((void*)intTrunc, BOXED_INT, 1)));
int_cls->giveAttr("__index__", new BoxedFunction(boxRTFunction((void*)intIndex, BOXED_INT, 1)));
int_cls->giveAttr("__int__", new BoxedFunction(boxRTFunction((void*)intInt, BOXED_INT, 1)));
int_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)intNew, UNKNOWN, 3, 2, false, false,
ParamNames({ "", "x", "base" }, "", "")),
......@@ -1088,10 +1126,10 @@ void setupInt() {
int_cls->giveAttr("bit_length", new BoxedFunction(boxRTFunction((void*)intBitLength, BOXED_INT, 1)));
int_cls->giveAttr("real", new (pyston_getset_cls) BoxedGetsetDescriptor(intInt, NULL, NULL));
int_cls->giveAttr("real", new (pyston_getset_cls) BoxedGetsetDescriptor(intIntGetset, NULL, NULL));
int_cls->giveAttr("imag", new (pyston_getset_cls) BoxedGetsetDescriptor(int0, NULL, NULL));
int_cls->giveAttr("conjugate", new BoxedFunction(boxRTFunction((void*)intInt, BOXED_INT, 1)));
int_cls->giveAttr("numerator", new (pyston_getset_cls) BoxedGetsetDescriptor(intInt, NULL, NULL));
int_cls->giveAttr("conjugate", new BoxedFunction(boxRTFunction((void*)intIntGetset, BOXED_INT, 1)));
int_cls->giveAttr("numerator", new (pyston_getset_cls) BoxedGetsetDescriptor(intIntGetset, NULL, NULL));
int_cls->giveAttr("denominator", new (pyston_getset_cls) BoxedGetsetDescriptor(int1, NULL, NULL));
add_operators(int_cls);
......
......@@ -783,28 +783,30 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
}
bool type_new_special_case;
// For debugging, keep track of why we think we can rewrite this:
enum { NOT_ALLOWED, VERIFIED, NO_INIT, TYPE_NEW_SPECIAL_CASE, } why_rewrite_allowed = NOT_ALLOWED;
if (rewrite_args) {
bool ok = false;
for (auto b : allowable_news) {
if (b == new_attr) {
ok = true;
why_rewrite_allowed = VERIFIED;
break;
}
}
if (!ok && (cls == int_cls || cls == float_cls || cls == long_cls)) {
if (npassed_args == 1)
ok = true;
else if (npassed_args == 2 && (arg2->cls == int_cls || arg2->cls == str_cls || arg2->cls == float_cls)) {
if (cls == int_cls || cls == float_cls || cls == long_cls) {
if (npassed_args == 1) {
why_rewrite_allowed = VERIFIED;
} else if (npassed_args == 2 && (arg2->cls == int_cls || arg2->cls == str_cls || arg2->cls == float_cls)) {
why_rewrite_allowed = NO_INIT;
rewrite_args->arg2->addAttrGuard(offsetof(Box, cls), (intptr_t)arg2->cls);
ok = true;
}
}
type_new_special_case = (cls == type_cls && argspec == ArgPassSpec(2));
if (cls == type_cls && argspec == ArgPassSpec(2))
why_rewrite_allowed = TYPE_NEW_SPECIAL_CASE;
if (!ok && !type_new_special_case) {
if (why_rewrite_allowed == NOT_ALLOWED) {
// Uncomment this to try to find __new__ functions that we could either white- or blacklist:
// ASSERT(cls->is_user_defined || cls == type_cls, "Does '%s' have a well-behaved __new__? if so, add to
// allowable_news, otherwise add to the blacklist in this assert", cls->tp_name);
......@@ -869,7 +871,8 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
}
ASSERT(made->cls == cls || type_new_special_case,
ASSERT(made->cls == cls || why_rewrite_allowed == TYPE_NEW_SPECIAL_CASE
|| (why_rewrite_allowed == NO_INIT && cls->tp_init == object_cls->tp_init),
"We should only have allowed the rewrite to continue if we were guaranteed that made "
"would have class cls!");
} else {
......@@ -893,8 +896,10 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
// If __new__ returns a subclass, supposed to call that subclass's __init__.
// If __new__ returns a non-subclass, not supposed to call __init__.
if (made->cls != cls) {
ASSERT(rewrite_args == NULL, "We should only have allowed the rewrite to continue if we were guaranteed that "
"made would have class cls!");
ASSERT(rewrite_args == NULL || (why_rewrite_allowed == NO_INIT && made->cls->tp_init == object_cls->tp_init
&& cls->tp_init == object_cls->tp_init),
"We should only have allowed the rewrite to continue if we were guaranteed that "
"made would have class cls!");
if (!isSubclass(made->cls, cls)) {
init_attr = NULL;
......
......@@ -10,6 +10,11 @@ for i in xrange(1, 12):
print i & j
print i ^ j
print (2).__int__()
print (True).__int__()
print (2).__trunc__()
print (True).__trunc__()
print 1 ** 0
print 0 ** 0
print -1 ** 0, (-1) ** 0, (-5) ** 0
......@@ -93,3 +98,38 @@ for i1 in [1, I(2), 3, I(4)]:
print int("12345", base=16)
print type(2 ** 48)
class I(int):
def __init__(self, n):
print "I.__init__(%r)" % n
self.n = n
def __int__(self):
return self
def __repr__(self):
return "<I(%r)>" % self.n
def call_int(i):
print "calling int(%r)" % i
i2 = int(i)
print "return type:", type(i2)
print
call_int(1)
print
i = I(1)
call_int(i)
print "i.n is a", type(i.n) # should be 'I' now!
print
del I.__int__
i = I(1)
call_int(i)
print
# These return longs:
print int("12938719238719827398172938712983791827938712987312")
print int(1e100)
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