in ext/Objects/codeobject.cpp [150:236]
static RawObject constantKey(Thread* thread, const Object& obj) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
if (obj.isNoneType() || obj.isEllipsis() || obj.isSmallInt() ||
obj.isLargeInt() || obj.isStr() || obj.isCode()) {
return *obj;
}
if (obj.isBool() || obj.isBytes()) {
Object type(&scope, runtime->typeOf(*obj));
return runtime->newTupleWith2(type, obj);
}
if (obj.isFloat()) {
double d = Float::cast(*obj).value();
Object type(&scope, runtime->typeOf(*obj));
if (d == 0.0 && std::signbit(d)) {
Object none(&scope, NoneType::object());
return runtime->newTupleWith3(type, obj, none);
}
return runtime->newTupleWith2(type, obj);
}
if (obj.isComplex()) {
Complex c(&scope, *obj);
Py_complex z;
z.real = c.real();
z.imag = c.imag();
// For the complex case we must make complex(x, 0.)
// different from complex(x, -0.) and complex(0., y)
// different from complex(-0., y), for any x and y.
// All four complex zeros must be distinguished.
bool real_negzero = z.real == 0.0 && std::signbit(z.real);
bool imag_negzero = z.imag == 0.0 && std::signbit(z.imag);
// use True, False and None singleton as tags for the real and imag sign,
// to make tuples different
Object type(&scope, runtime->typeOf(*obj));
if (real_negzero && imag_negzero) {
Object tru(&scope, Bool::trueObj());
return runtime->newTupleWith3(type, obj, tru);
}
if (imag_negzero) {
Object fals(&scope, Bool::falseObj());
return runtime->newTupleWith3(type, obj, fals);
}
if (real_negzero) {
Object none(&scope, NoneType::object());
return runtime->newTupleWith3(type, obj, none);
}
return runtime->newTupleWith2(type, obj);
}
if (obj.isTuple()) {
Tuple tuple(&scope, *obj);
Object result_obj(&scope, NoneType::object());
word length = tuple.length();
if (length > 0) {
MutableTuple result(&scope, runtime->newMutableTuple(length));
Object item(&scope, NoneType::object());
Object item_key(&scope, NoneType::object());
for (word i = 0; i < length; i++) {
item = tuple.at(i);
item_key = constantKey(thread, item);
if (item_key.isError()) return *item_key;
result.atPut(i, *item_key);
}
result_obj = result.becomeImmutable();
} else {
result_obj = runtime->emptyTuple();
}
return runtime->newTupleWith2(result_obj, obj);
}
if (obj.isFrozenSet()) {
FrozenSet set(&scope, *obj);
FrozenSet result(&scope, runtime->newFrozenSet());
Object item(&scope, NoneType::object());
Object item_key(&scope, NoneType::object());
Object hash_obj(&scope, NoneType::object());
for (word j = 0, idx = 0; setNextItem(set, &idx, &item); j++) {
item_key = constantKey(thread, item);
if (item_key.isError()) return *item_key;
hash_obj = Interpreter::hash(thread, item_key);
if (hash_obj.isErrorException()) return *hash_obj;
setAdd(thread, result, item_key, SmallInt::cast(*hash_obj).value());
}
return runtime->newTupleWith2(result, obj);
}
PyObject* ptr = ApiHandle::borrowedReference(runtime, *obj);
Object obj_id(&scope, runtime->newInt(reinterpret_cast<word>(ptr)));
return runtime->newTupleWith2(obj_id, obj);
}