//     Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file

// This file is included from another C file, help IDEs to still parse it on
// its own.
#ifdef __IDE_ONLY__
#include "nuitka/prelude.h"
#endif

// Our replacement for "PyType_IsSubtype"
bool Nuitka_Type_IsSubtype(PyTypeObject *a, PyTypeObject *b) {
    CHECK_OBJECT(a);
    CHECK_OBJECT(b);

#if PYTHON_VERSION < 0x300
    if (!(a->tp_flags & Py_TPFLAGS_HAVE_CLASS)) {
        return b == a || b == &PyBaseObject_Type;
    }
#endif

    PyObject *mro = a->tp_mro;
    CHECK_OBJECT_X(mro);

    if (likely(mro != NULL)) {
        assert(PyTuple_Check(mro));

        Py_ssize_t n = PyTuple_GET_SIZE(mro);

        for (Py_ssize_t i = 0; i < n; i++) {
            if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) {
                return true;
            }
        }

        return false;
    } else {
        // Fallback for uninitialized classes to base class scan
        do {
            if (a == b) {
                return true;
            }
            a = a->tp_base;
        } while (a != NULL);

        return (b == &PyBaseObject_Type);
    }
}

#if PYTHON_VERSION >= 0x270
static PyObject *lookupSpecialNoError(PyObject *source, PyObject *attr_name) {
    CHECK_OBJECT(source);
    CHECK_OBJECT(attr_name);

    PyTypeObject *type = Py_TYPE(source);

    if (unlikely(type->tp_dict == NULL)) {
        if (unlikely(PyType_Ready(type) < 0)) {
            return NULL;
        }
    }

    PyObject *descr = Nuitka_TypeLookup(type, attr_name);

    if (descr == NULL) {
        return NULL;
    }

    descrgetfunc func = Py_TYPE(descr)->tp_descr_get;

    if (func == NULL) {
        Py_INCREF(descr);
        return descr;
    } else {
        return func(descr, source, (PyObject *)type);
    }
}
#endif

static PyObject *_Nuitka_abstract_get_bases(PyThreadState *tstate, PyObject *cls) {
    PyObject *bases = LOOKUP_ATTRIBUTE(tstate, cls, const_str_plain___bases__);

    if (bases == NULL) {
        CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate);

        return NULL;
    }

    if (unlikely(!PyTuple_Check(bases))) {
        Py_DECREF(bases);
        return NULL;
    }

    return bases;
}

#if PYTHON_VERSION >= 0x390
static int _Nuitka_abstract_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) {
    PyObject *bases = NULL;
    int r = 0;

    while (1) {
        if (derived == cls) {
            Py_XDECREF(bases);
            return 1;
        }

        PyObject *next_bases = _Nuitka_abstract_get_bases(tstate, derived);

        Py_XDECREF(bases);
        bases = next_bases;

        if (bases == NULL) {
            if (HAS_ERROR_OCCURRED(tstate)) {
                return -1;
            }

            return 0;
        }

        Py_ssize_t n = PyTuple_GET_SIZE(bases);

        if (n == 0) {
            Py_DECREF(bases);
            return 0;
        }

        if (n == 1) {
            derived = PyTuple_GET_ITEM(bases, 0);
            continue;
        }

#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __issubclass__")) {
            Py_DECREF(bases);
            return -1;
        }
#endif

        for (Py_ssize_t i = 0; i < n; i++) {
            r = _Nuitka_abstract_issubclass(tstate, PyTuple_GET_ITEM(bases, i), cls);

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif
        Py_DECREF(bases);

        return r;
    }
}
#else
static int _Nuitka_abstract_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) {
    while (1) {
        if (derived == cls) {
            return 1;
        }

        PyObject *bases = _Nuitka_abstract_get_bases(tstate, derived);

        if (bases == NULL) {
            if (HAS_ERROR_OCCURRED(tstate)) {
                return -1;
            }

            return 0;
        }

        Py_ssize_t n = PyTuple_GET_SIZE(bases);

        if (n == 0) {
            Py_DECREF(bases);
            return 0;
        }

        if (n == 1) {
            derived = PyTuple_GET_ITEM(bases, 0);
            Py_DECREF(bases);
            continue;
        }

        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = _Nuitka_abstract_issubclass(tstate, PyTuple_GET_ITEM(bases, i), cls);

            if (r != 0) {
                break;
            }
        }

        Py_DECREF(bases);

        return r;
    }
}
#endif

static bool _Nuitka_check_class(PyThreadState *tstate, PyObject *cls, char const *error) {
    PyObject *bases = _Nuitka_abstract_get_bases(tstate, cls);

    if (bases == NULL) {
        if (!HAS_ERROR_OCCURRED(tstate)) {
            SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError, error);
        }

        return false;
    }

    Py_DECREF(bases);

    return true;
}

#if PYTHON_VERSION < 0x300
static int Nuitka_PyClass_IsSubclass(PyObject *klass, PyObject *base) {
    if (klass == base) {
        return 1;
    }

    if (PyTuple_Check(base)) {
        Py_ssize_t n = PyTuple_GET_SIZE(base);

        for (Py_ssize_t i = 0; i < n; i++) {
            if (Nuitka_PyClass_IsSubclass(klass, PyTuple_GET_ITEM(base, i))) {
                return 1;
            }
        }

        return 0;
    }

    if (klass == NULL || !PyClass_Check(klass)) {
        return 0;
    }

    PyObject *bases = ((PyClassObject *)klass)->cl_bases;

    if (bases == NULL || !PyTuple_Check(bases)) {
        return 0;
    }

    Py_ssize_t n = PyTuple_GET_SIZE(bases);

    for (Py_ssize_t i = 0; i < n; i++) {
        if (Nuitka_PyClass_IsSubclass(PyTuple_GET_ITEM(bases, i), base)) {
            return 1;
        }
    }

    return 0;
}

static int _Nuitka_recursive_isinstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    int retval = 0;

    if (PyClass_Check(cls) && PyInstance_Check(instance)) {
        PyObject *inclass = (PyObject *)((PyInstanceObject *)instance)->in_class;
        retval = Nuitka_PyClass_IsSubclass(inclass, cls);
    } else if (PyType_Check(cls)) {
        retval = Nuitka_PyObject_TypeCheck(instance, (PyTypeObject *)cls);

        if (retval == 0) {
            PyObject *c = LOOKUP_ATTRIBUTE_CLASS_SLOT(tstate, instance);

            if (c == NULL) {
                CLEAR_ERROR_OCCURRED(tstate);
            } else {
                if (c != (PyObject *)Py_TYPE(instance) && PyType_Check(c)) {
                    retval = Nuitka_Type_IsSubtype((PyTypeObject *)c, (PyTypeObject *)cls);
                }

                Py_DECREF(c);
            }
        }
    } else {
        if (!_Nuitka_check_class(tstate, cls,
                                 "isinstance() arg 2 must be a class, type, or tuple of classes and types")) {
            return -1;
        }

        PyObject *icls = LOOKUP_ATTRIBUTE_CLASS_SLOT(tstate, instance);

        if (icls == NULL) {
            CLEAR_ERROR_OCCURRED(tstate);
            retval = 0;
        } else {
            retval = _Nuitka_abstract_issubclass(tstate, icls, cls);
            Py_DECREF(icls);
        }
    }

    return retval;
}

#if PYTHON_VERSION >= 0x270
int Nuitka_Object_IsInstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    CHECK_OBJECT(instance);
    CHECK_OBJECT(cls);

    if (Py_TYPE(instance) == (PyTypeObject *)cls) {
        return 1;
    }

    if (PyTuple_Check(cls)) {
#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            return -1;
        }
#endif

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(cls, i));

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif

        return r;
    }

    if (!(PyClass_Check(cls) || PyInstance_Check(cls))) {
        PyObject *checker = lookupSpecialNoError(cls, const_str_plain___instancecheck__);

        if (checker != NULL) {
            int ok = -1;

#ifdef _NUITKA_FULL_COMPAT
            if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
                Py_DECREF(checker);
                return ok;
            }
#endif

            PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, checker, instance);

#ifdef _NUITKA_FULL_COMPAT
            Py_LeaveRecursiveCall();
#endif
            Py_DECREF(checker);

            if (res != NULL) {
                ok = CHECK_IF_TRUE(res);
                Py_DECREF(res);
            }

            return ok;
        } else if (HAS_ERROR_OCCURRED(tstate)) {
            return -1;
        }
    }

    return _Nuitka_recursive_isinstance(tstate, instance, cls);
}
#else
int Nuitka_Object_IsInstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    CHECK_OBJECT(instance);
    CHECK_OBJECT(cls);

    if (Py_TYPE(instance) == (PyTypeObject *)cls) {
        return 1;
    }

    if (PyTuple_Check(cls)) {
#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            return -1;
        }
#endif

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(cls, i));

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif

        return r;
    }

    PyObject *checker = LOOKUP_ATTRIBUTE(tstate, cls, const_str_plain___instancecheck__);

    if (checker == NULL && HAS_ERROR_OCCURRED(tstate)) {
        CLEAR_ERROR_OCCURRED(tstate);
    }

    if (checker != NULL) {
        int ok = -1;

#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            Py_DECREF(checker);
            return ok;
        }
#endif

        PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, checker, instance);

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif
        Py_DECREF(checker);

        if (res != NULL) {
            ok = CHECK_IF_TRUE(res);
            Py_DECREF(res);
        }

        return ok;
    }

    return _Nuitka_recursive_isinstance(tstate, instance, cls);
}
#endif
#else
static int _Nuitka_recursive_isinstance(PyThreadState *tstate, PyObject *instance, PyObject *cls,
                                        char const *error_message) {
    int retval = 0;

    if (PyType_Check(cls)) {
        retval = Nuitka_PyObject_TypeCheck(instance, (PyTypeObject *)cls);

        if (retval == 0) {
            PyObject *c = LOOKUP_ATTRIBUTE_CLASS_SLOT(tstate, instance);

            if (c == NULL) {
                if (!CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate)) {
                    retval = -1;
                }
            } else {
                if (c != (PyObject *)Py_TYPE(instance) && PyType_Check(c)) {
                    retval = Nuitka_Type_IsSubtype((PyTypeObject *)c, (PyTypeObject *)cls);
                } else {
                    retval = 0;
                }

                Py_DECREF(c);
            }
        }
    } else {
        if (!_Nuitka_check_class(tstate, cls, error_message)) {
            return -1;
        }

        PyObject *icls = LOOKUP_ATTRIBUTE_CLASS_SLOT(tstate, instance);

        if (icls == NULL) {
            if (!CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(tstate)) {
                retval = -1;
            }
        } else {
            retval = _Nuitka_abstract_issubclass(tstate, icls, cls);
            Py_DECREF(icls);
        }
    }

    return retval;
}

#if PYTHON_VERSION == 0x340
int Nuitka_Object_IsInstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    CHECK_OBJECT(instance);
    CHECK_OBJECT(cls);

    if (Py_TYPE(instance) == (PyTypeObject *)cls) {
        return 1;
    }

    if (PyTuple_Check(cls)) {
#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            return -1;
        }
#endif

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(cls, i));

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif

        return r;
    }

    PyObject *checker = lookupSpecialNoError(cls, const_str_plain___instancecheck__);

    if (checker != NULL) {
        int ok = -1;

#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            Py_DECREF(checker);
            return ok;
        }
#endif

        PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, checker, instance);

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif
        Py_DECREF(checker);

        if (res != NULL) {
            ok = CHECK_IF_TRUE(res);
            Py_DECREF(res);
        }

        return ok;
    } else if (HAS_ERROR_OCCURRED(tstate)) {
        return -1;
    }

    return _Nuitka_recursive_isinstance(tstate, instance, cls, "isinstance() arg 2 must be a type or tuple of types");
}
#elif PYTHON_VERSION < 0x3a0
int Nuitka_Object_IsInstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    CHECK_OBJECT(instance);
    CHECK_OBJECT(cls);

    if (Py_TYPE(instance) == (PyTypeObject *)cls) {
        return 1;
    }

    if (PyType_CheckExact(cls)) {
        return _Nuitka_recursive_isinstance(tstate, instance, cls,
                                            "isinstance() arg 2 must be a type or tuple of types");
    }

    if (PyTuple_Check(cls)) {
#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            return -1;
        }
#endif

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(cls, i));

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif

        return r;
    }

    PyObject *checker = lookupSpecialNoError(cls, const_str_plain___instancecheck__);

    if (checker != NULL) {
        int ok = -1;

#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            Py_DECREF(checker);
            return ok;
        }
#endif

        PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, checker, instance);

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif
        Py_DECREF(checker);

        if (res != NULL) {
            ok = CHECK_IF_TRUE(res);
            Py_DECREF(res);
        }

        return ok;
    } else if (HAS_ERROR_OCCURRED(tstate)) {
        return -1;
    }

    return _Nuitka_recursive_isinstance(tstate, instance, cls, "isinstance() arg 2 must be a type or tuple of types");
}
#else
int Nuitka_Object_IsInstance(PyThreadState *tstate, PyObject *instance, PyObject *cls) {
    CHECK_OBJECT(instance);
    CHECK_OBJECT(cls);

    if (Py_TYPE(instance) == (PyTypeObject *)cls) {
        return 1;
    }

    if (PyType_CheckExact(cls)) {
        return _Nuitka_recursive_isinstance(tstate, instance, cls,
                                            "isinstance() arg 2 must be a type, a tuple of types, or a union");
    }

#if PYTHON_VERSION >= 0x3a0
    if (Py_TYPE(cls) == Nuitka_PyUnion_Type) {
        PyObject *args = LOOKUP_ATTRIBUTE(tstate, cls, const_str_plain___args__);

        if (args == NULL) {
            return -1;
        }

        if (unlikely(!PyTuple_Check(args))) {
            Py_DECREF(args);
            return -1;
        }

        Py_ssize_t n = PyTuple_GET_SIZE(args);

        if (n == 0) {
            Py_DECREF(args);
            SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError,
                                            "isinstance() argument 2 cannot be an empty union");
            return -1;
        }

#if PYTHON_VERSION >= 0x3a0 && PYTHON_VERSION < 0x3b0
        // Validate that no arg is a parameterized generic.
        for (Py_ssize_t i = 0; i < n; i++) {
            PyObject *arg = PyTuple_GET_ITEM(args, i);

            if (Nuitka_Type_IsSubtype(Py_TYPE(arg), &Py_GenericAliasType)) {
                Py_DECREF(args);
                SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError,
                                                "isinstance() argument 2 cannot contain a parameterized generic");
                return -1;
            }
        }
#endif

        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(args, i));

            if (r != 0) {
                break;
            }
        }

        Py_DECREF(args);

        return r;
    }
#endif

    if (PyTuple_Check(cls)) {
#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            return -1;
        }
#endif

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; i++) {
            r = Nuitka_Object_IsInstance(tstate, instance, PyTuple_GET_ITEM(cls, i));

            if (r != 0) {
                break;
            }
        }

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif

        return r;
    }

    PyObject *checker = lookupSpecialNoError(cls, const_str_plain___instancecheck__);

    if (checker != NULL) {
        int ok = -1;

#ifdef _NUITKA_FULL_COMPAT
        if (Py_EnterRecursiveCall((char *)" in __instancecheck__")) {
            Py_DECREF(checker);
            return ok;
        }
#endif

        PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, checker, instance);

#ifdef _NUITKA_FULL_COMPAT
        Py_LeaveRecursiveCall();
#endif
        Py_DECREF(checker);

        if (res != NULL) {
            ok = CHECK_IF_TRUE(res);
            Py_DECREF(res);
        }

        return ok;
    } else if (HAS_ERROR_OCCURRED(tstate)) {
        return -1;
    }

    return _Nuitka_recursive_isinstance(tstate, instance, cls,
                                        "isinstance() arg 2 must be a type, a tuple of types, or a union");
}
#endif
#endif

// TODO: We cannot really do this, until Nuitka_TypeLookup (_PyType_Lookup) is
// not also a call to an API, we just become wasteful here. What will make sense
// is to make specialized variants for not sub class checks, like
// PyExc_GeneratorExit and PyExc_StopIteration by caching the descriptor
// "checker" for them and then calling the "func" behind them more or less
// directly. These could be created during startup and be very fast to use.

#if 0
int Nuitka_Object_IsSubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
{
    // TODO: Checking for a type is nothing the core does, could have a second variant
    if (PyType_CheckExact(cls)) {
        // Only a quick test for an exact match, but then give up.
        if (derived == cls) {
            return 1;
        }

        // Too hard for us.
        return PyObject_IsSubclass(derived, cls);
    }

    // TODO: Checking for a tuple is nothing the core does, could have a second variant
    if (PyTuple_Check(cls)) {
        if (Py_EnterRecursiveCall((char *)" in __subclasscheck__")) {
            return -1;
        }

        Py_ssize_t n = PyTuple_GET_SIZE(cls);
        int r = 0;

        for (Py_ssize_t i = 0; i < n; ++i) {
            PyObject *item = PyTuple_GET_ITEM(cls, i);

            r = Nuitka_Object_IsSubclass(tstate, derived, item);

            if (r != 0) {
                break;
            }
        }

        Py_LeaveRecursiveCall();

        return r;
    }

    // TODO: For many of our uses, we know it.
    PyObject *checker = Nuitka_TypeLookup((PyTypeObject *)cls, const_str_plain___subclasscheck__);

    if (checker != NULL) {
        descrgetfunc f = Py_TYPE(checker)->tp_descr_get;

        if (f == NULL) {
            Py_INCREF(checker);
        } else {
            checker = f(checker, cls, (PyObject *)(Py_TYPE(cls)));
        }
    }

    if (checker != NULL) {
        int ok = -1;

        if (Py_EnterRecursiveCall((char *)" in __subclasscheck__")) {
            Py_DECREF(checker);
            return ok;
        }

        PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(checker, derived);

        Py_LeaveRecursiveCall();

        Py_DECREF(checker);

        if (res != NULL) {
            ok = CHECK_IF_TRUE(res);
            Py_DECREF(res);
        }
        return ok;
    } else if (HAS_ERROR_OCCURRED(tstate)) {
        return -1;
    }

    // Too hard for us.
    return PyObject_IsSubclass(derived, cls);
}
#endif

getattrofunc PyObject_GenericGetAttr_resolved;
setattrofunc PyObject_GenericSetAttr_resolved;

// Our wrapper for "PyType_Ready" that takes care of trying to avoid DLL entry
// points for generic attributes. spell-checker: ignore aiter
void Nuitka_PyType_Ready(PyTypeObject *type, PyTypeObject *base, bool generic_get_attr, bool generic_set_attr,
                         bool self_iter, bool await_self_iter, bool await_self_aiter) {
    assert(type->tp_base == NULL);

    PyObject_GenericGetAttr_resolved = PyBaseObject_Type.tp_getattro;
    PyObject_GenericSetAttr_resolved = PyBaseObject_Type.tp_setattro;

    type->tp_base = base;

#if PYTHON_VERSION >= 0x3d0
    if (base != NULL) {
        // Assert that we don't accidentally miss setting pre-header flags
        // that the base type has, as it causes GC segfaults.
        assert(_PyType_PreHeaderSize(type) == _PyType_PreHeaderSize(base));
    }
#endif

    if (generic_get_attr) {
        assert(type->tp_getattro == NULL);
        type->tp_getattro = PyObject_GenericGetAttr_resolved;
    }

    if (generic_set_attr) {
        assert(type->tp_setattro == NULL);
        type->tp_setattro = PyObject_GenericSetAttr_resolved;
    }

    if (self_iter) {
        assert(type->tp_iter == NULL);
        type->tp_iter = PyObject_SelfIter;
    }

#if PYTHON_VERSION >= 0x350
    if (await_self_iter) {
        assert(type->tp_as_async->am_await == NULL);
        type->tp_as_async->am_await = PyObject_SelfIter;
    }

    if (await_self_aiter) {
        assert(type->tp_as_async->am_aiter == NULL);
        type->tp_as_async->am_aiter = PyObject_SelfIter;
    }
#else
    assert(!await_self_iter);
    assert(!await_self_aiter);
#endif

#if PYTHON_VERSION >= 0x3a0
    type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
#endif

    NUITKA_MAY_BE_UNUSED int res = PyType_Ready(type);
    assert(res >= 0);
}

#if PYTHON_VERSION >= 0x3c0

typedef struct {
    PyObject_HEAD PyObject *name;
    PyObject *type_params;
    PyObject *compute_value;
    PyObject *value;
    PyObject *module;
} typealiasobject;

static PyTypeObject *getTypeAliasType(void) {
    static PyTypeObject *type_alias_type = NULL;

    if (type_alias_type == NULL) {

        PyObject *typing_module = PyImport_ImportModule("_typing");
        CHECK_OBJECT(typing_module);

        type_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "TypeAliasType");
        CHECK_OBJECT(type_alias_type);
    }

    return type_alias_type;
}

PyObject *MAKE_TYPE_ALIAS(PyObject *name, PyObject *type_params, PyObject *compute_value, PyObject *module_name) {
    // TODO: For Python 3.13 we can use the intrinsic.

    typealiasobject *ta = Nuitka_GC_New(getTypeAliasType());

    // TODO: Lets follow Python new inline function in the future, this is 3.12
    // only code, so we can use it here.
    ta->name = Py_NewRef(name);
    ta->type_params = Py_IsNone(type_params) ? NULL : Py_XNewRef(type_params);
    ta->compute_value = Py_NewRef(compute_value);
    ta->value = NULL;
    ta->module = Py_NewRef(module_name);

    Nuitka_GC_Track(ta);

    return (PyObject *)ta;
}

typedef struct {
    PyObject_HEAD PyObject *name;
    PyObject *bound;
    PyObject *evaluate_bound;
    PyObject *constraints;
    PyObject *evaluate_constraints;
#if PYTHON_VERSION >= 0x3d0
    PyObject *default_value;
    PyObject *evaluate_default;
#endif
    bool covariant;
    bool contravariant;
    bool infer_variance;
} typevarobject;

typedef struct {
    PyObject_HEAD PyObject *name;
#if PYTHON_VERSION >= 0x3f0
    PyObject *bound;
#endif
#if PYTHON_VERSION >= 0x3d0
    PyObject *default_value;
    PyObject *evaluate_default;
#endif
#if PYTHON_VERSION >= 0x3f0
    bool covariant;
    bool contravariant;
    bool infer_variance;
#endif
} typevartupleobject; // Following CPython, spell-checker: ignore typevartupleobject

typedef struct {
    PyObject_HEAD PyObject *name;
    PyObject *bound;
#if PYTHON_VERSION >= 0x3d0
    PyObject *default_value;
    PyObject *evaluate_default;
#endif
    bool covariant;
    bool contravariant;
    bool infer_variance;
} paramspecobject;

static typevarobject *_Nuitka_typevar_alloc(PyThreadState *tstate, PyObject *name, PyObject *bound,
                                            PyObject *evaluate_bound, PyObject *constraints,
                                            PyObject *evaluate_constraints, bool covariant, bool contravariant,
                                            bool infer_variance, PyObject *module) {
    PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevar_type);
    typevarobject *result = Nuitka_GC_New(tp);

    result->name = Py_NewRef(name);

    result->bound = Py_XNewRef(bound);
    result->evaluate_bound = Py_XNewRef(evaluate_bound);
    result->constraints = Py_XNewRef(constraints);
    result->evaluate_constraints = Py_XNewRef(evaluate_constraints);
#if PYTHON_VERSION >= 0x3d0
    result->default_value = NULL;
    result->evaluate_default = NULL;
#endif

    result->covariant = covariant;
    result->contravariant = contravariant;
    result->infer_variance = infer_variance;

    Nuitka_GC_Track(result);

    // TODO: Not seen yet.
    if (unlikely(module != NULL)) {
        if (PyObject_SetAttrString((PyObject *)result, "__module__", module) < 0) {
            Py_DECREF(result);
            return NULL;
        }
    }

    return result;
}

static typevartupleobject *_Nuitka_typevartuple_alloc(PyThreadState *tstate, PyObject *name, PyObject *module,
                                                      PyObject *default_value, bool covariant, bool contravariant,
                                                      bool infer_variance) {
    PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevartuple_type);
    typevartupleobject *tvt = Nuitka_GC_New(tp);
    if (tvt == NULL) {
        return NULL;
    }
    tvt->name = Py_NewRef(name);
#if PYTHON_VERSION >= 0x3f0
    tvt->bound = NULL;
#endif
#if PYTHON_VERSION >= 0x3d0
    tvt->default_value = Py_XNewRef(default_value);
    tvt->evaluate_default = NULL;
#endif
#if PYTHON_VERSION >= 0x3f0
    tvt->covariant = covariant;
    tvt->contravariant = contravariant;
    tvt->infer_variance = infer_variance;
#endif
    Nuitka_GC_Track(tvt);
    if (module != NULL) {
        if (PyObject_SetAttrString((PyObject *)tvt, "__module__", module) < 0) {
            Py_DECREF(tvt);
            return NULL;
        }
    }
    return tvt;
}

static paramspecobject *_Nuitka_paramspec_alloc(PyThreadState *tstate, PyObject *name, PyObject *bound,
                                                PyObject *default_value, bool covariant, bool contravariant,
                                                bool infer_variance, PyObject *module) {
    PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, paramspec_type);
    paramspecobject *ps = Nuitka_GC_New(tp);
    if (ps == NULL) {
        return NULL;
    }
    ps->name = Py_NewRef(name);
    ps->bound = Py_XNewRef(bound);
    ps->covariant = covariant;
    ps->contravariant = contravariant;
    ps->infer_variance = infer_variance;
#if PYTHON_VERSION >= 0x3d0
    ps->default_value = Py_XNewRef(default_value);
    ps->evaluate_default = NULL;
#endif
    Nuitka_GC_Track(ps);
    if (module != NULL) {
        if (PyObject_SetAttrString((PyObject *)ps, "__module__", module) < 0) {
            Py_DECREF(ps);
            return NULL;
        }
    }
    return ps;
}

PyObject *MAKE_TYPE_VAR(PyThreadState *tstate, PyObject *name) {
    // TODO: For Python 3.13 this would work.
    // return _PyIntrinsics_UnaryFunctions[INTRINSIC_TYPEVAR].func(tstate, name);

    return (PyObject *)_Nuitka_typevar_alloc(tstate, name, NULL, NULL, NULL, NULL, false, false, true, NULL);
}

PyObject *MAKE_TYPE_VAR_TUPLE(PyThreadState *tstate, PyObject *name) {
    return (PyObject *)_Nuitka_typevartuple_alloc(tstate, name, NULL, NULL, false, false, true);
}

PyObject *MAKE_PARAM_SPEC(PyThreadState *tstate, PyObject *name) {
    return (PyObject *)_Nuitka_paramspec_alloc(tstate, name, NULL, NULL, false, false, true, NULL);
}

static PyTypeObject *_getTypeGenericAliasType(void) {
    static PyTypeObject *type_generic_alias_type = NULL;

    if (type_generic_alias_type == NULL) {

        PyObject *typing_module = IMPORT_HARD_TYPING();

        type_generic_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "_GenericAlias");
        CHECK_OBJECT(type_generic_alias_type);
    }

    return type_generic_alias_type;
}

static int _Nuitka_contains_typevartuple(PyTupleObject *params) {
    Py_ssize_t n = PyTuple_GET_SIZE(params);
    PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(PyInterpreterState_Get(), typevartuple_type);
    for (Py_ssize_t i = 0; i < n; i++) {
        PyObject *param = PyTuple_GET_ITEM(params, i);
        if (Py_IS_TYPE(param, tp)) {
            return 1;
        }
    }
    return 0;
}

static PyObject *_Nuitka_unpack_param(PyThreadState *tstate, PyObject *self) {
    static PyObject *typing_unpack = NULL;

    if (typing_unpack == NULL) {
        typing_unpack = LOOKUP_ATTRIBUTE(tstate, IMPORT_HARD_TYPING(), const_str_plain_Unpack);
        CHECK_OBJECT(typing_unpack);
    }

    return LOOKUP_SUBSCRIPT(tstate, typing_unpack, self);
}

// Match CPython, spell-checker: ignore typevartuple,typevartuples
static PyObject *_Nuitka_unpack_typevartuples(PyThreadState *tstate, PyObject *params) {
    assert(PyTuple_Check(params));
    // TypeVarTuple must be unpacked when passed to Generic, so we do that here.
    if (_Nuitka_contains_typevartuple((PyTupleObject *)params)) {
        Py_ssize_t n = PyTuple_GET_SIZE(params);
        PyObject *new_params = MAKE_TUPLE_EMPTY(tstate, n);
        CHECK_OBJECT(new_params);

        PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevartuple_type);
        for (Py_ssize_t i = 0; i < n; i++) {
            PyObject *param = PyTuple_GET_ITEM(params, i);
            if (Py_IS_TYPE(param, tp)) {
                PyObject *unpacked = _Nuitka_unpack_param(tstate, param);
                if (unpacked == NULL) {
                    Py_DECREF(new_params);
                    return NULL;
                }
                PyTuple_SET_ITEM(new_params, i, unpacked);
            } else {
                PyTuple_SET_ITEM(new_params, i, Py_NewRef(param));
            }
        }
        return new_params;
    } else {
        return Py_NewRef(params);
    }
}

PyObject *MAKE_TYPE_GENERIC(PyThreadState *tstate, PyObject *params) {
    CHECK_OBJECT(params);
    PyObject *unpacked_params = _Nuitka_unpack_typevartuples(tstate, params);
    CHECK_OBJECT(unpacked_params);
    assert(PyTuple_CheckExact(unpacked_params));

    PyObject *args[2] = {(PyObject *)_Py_INTERP_CACHED_OBJECT(tstate->interp, generic_type), unpacked_params};

    PyObject *called = (PyObject *)_getTypeGenericAliasType();

    PyObject *result = CALL_FUNCTION_WITH_ARGS2(tstate, called, args);
    Py_DECREF(unpacked_params);

    return MAKE_TUPLE1_0(tstate, result);
}

#endif

//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the GNU Affero General Public License, Version 3 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        https://www.gnu.org/licenses/agpl-3.0.txt
//
//     See also: "Nuitka Runtime Library Exception, Version 1.0" in file
//     "LICENSE-RUNTIME.txt" for additional permissions granted under Section 7.
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
