static RawObject constantKey()

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