Repository: numpy/numpy
File: numpy/_core/src/multiarray/item_selection.c (C)

Code before:
                goto fail;
            }
        }
        ret_data += elsize;
        PyArray_MultiIter_NEXT(multi);
    }

    NPY_cast_info_xfree(&cast_info);
    Py_DECREF(multi);
    for (i = 0; i < n; i++) {
        Py_XDECREF(mps[i]);
    }
    Py_DECREF(ap);
    PyDataMem_FREE(mps);
    if (out != NULL && out != obj) {
        Py_INCREF(out);
        int success;
        if (is_newarr) {
            success = PyArray_CopyAnyInto(out, obj);
        }
        else {
            success = PyArray_ResolveWritebackIfCopy(obj);
        }
        if (success < 0) {
            Py_DECREF(obj);
            goto fail;
        }
        Py_DECREF(obj);
        obj = out;
    }
    return (PyObject *)obj;

 fail:
    NPY_cast_info_xfree(&cast_info);
    Py_XDECREF(multi);
    for (i = 0; i < n; i++) {
        Py_XDECREF(mps[i]);
    }
    Py_XDECREF(ap);
    PyDataMem_FREE(mps);
    PyArray_DiscardWritebackIfCopy(obj);
    Py_XDECREF(obj);
    return NULL;
}

/*
 * These algorithms use special sorting.  They are not called unless the
 * underlying sort function for the type is available.  Note that axis is
 * already valid. The sort functions require 1-d contiguous and well-behaved
 * data.  Therefore, a copy will be made of the data if needed before handing
 * it to the sorting routine.  An iterator is constructed and adjusted to walk

Review comment (bug):
Just noticed: this is wrong!  At this point the code already did most/all cleanup and doing that again will cause crashes.  So you just need to do a straight `return NULL`.

Code after:
            char *args[2] = {PyArray_MultiIter_DATA(multi, mi), ret_data};
            if (cast_info.func(&cast_info.context, args, &one,
                                transfer_strides, cast_info.auxdata) < 0) {
                goto fail;
            }
        }
        ret_data += elsize;
        PyArray_MultiIter_NEXT(multi);
    }

    NPY_cast_info_xfree(&cast_info);
    Py_DECREF(multi);
    for (i = 0; i < n; i++) {
        Py_XDECREF(mps[i]);
    }
    Py_DECREF(ap);
    PyDataMem_FREE(mps);
    if (copy_existing_out) {
        int res = PyArray_CopyInto(out, obj);
        Py_DECREF(obj);
        if (res < 0) {
            return NULL;
        }
        return Py_NewRef(out);
    }
    return (PyObject *)obj;

 fail:
    NPY_cast_info_xfree(&cast_info);
    Py_XDECREF(multi);
    for (i = 0; i < n; i++) {
        Py_XDECREF(mps[i]);
    }
    Py_XDECREF(ap);
    PyDataMem_FREE(mps);
    PyArray_DiscardWritebackIfCopy(obj);
    Py_XDECREF(obj);
    return NULL;
}

/*
 * These algorithms use special sorting.  They are not called unless the
 * underlying sort function for the type is available.  Note that axis is
 * already valid. The sort functions require 1-d contiguous and well-behaved
 * data.  Therefore, a copy will be made of the data if needed before handing
 * it to the sorting routine.  An iterator is constructed and adjusted to walk
 * over all but the desired sorting axis.
 */
static int
_new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
              PyArray_PartitionFunc *part, npy_intp const *kth, npy_intp nkth)