# Buggy code
def generate_evaluation_code(self, code):
    code.mark_pos(self.pos)
    self.allocate_temp_result(code)

    self.function.generate_evaluation_code(code)
    assert self.arg_tuple.mult_factor is None
    args = self.arg_tuple.args
    for arg in args:
        arg.generate_evaluation_code(code)

    # make sure function is in temp so that we can replace the reference below if it's a method
    reuse_function_temp = self.function.is_temp
    if reuse_function_temp:
        function = self.function.result()
    else:
        function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
        self.function.make_owned_reference(code)
        code.put("%s = %s; " % (function, self.function.py_result()))
        self.function.generate_disposal_code(code)
        self.function.free_temps(code)

    self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
    code.putln("%s = NULL;" % self_arg)
    arg_offset_cname = code.funcstate.allocate_temp(
        PyrexTypes.c_int_type, manage_ref=False
    )
    code.putln("%s = 0;" % arg_offset_cname)

    def attribute_is_likely_method(attr):
        obj = attr.obj
        if obj.is_name and obj.entry.is_pyglobal:
            return False  # more likely to be a function
        return True

    if self.function.is_attribute:
        likely_method = (
            "likely" if attribute_is_likely_method(self.function) else "unlikely"
        )
    elif self.function.is_name and self.function.cf_state:
        # not an attribute itself, but might have been assigned from one (e.g. bound method)
        for assignment in self.function.cf_state:
            value = assignment.rhs
            if value and value.is_attribute and value.obj.type.is_pyobject:
                if attribute_is_likely_method(value):
                    likely_method = "likely"
                    break
        else:
            likely_method = "unlikely"
    else:
        likely_method = "unlikely"

    code.putln(
        "if (CYTHON_UNPACK_METHODS && %s(PyMethod_Check(%s))) {"
        % (likely_method, function)
    )
    code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
    # the following is always true in Py3 (kept only for safety),
    # but is false for unbound methods in Py2
    code.putln("if (likely(%s)) {" % self_arg)
    code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
    code.put_incref(self_arg, py_object_type)
    code.put_incref("function", py_object_type)
    # free method object as early to possible to enable reuse from CPython's freelist
    code.put_decref_set(function, py_object_type, "function")
    code.putln("%s = 1;" % arg_offset_cname)
    code.putln("}")
    code.putln("}")

    # actually call the function
    code.globalstate.use_utility_code(
        UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c")
    )

    code.putln("{")
    code.putln(
        "PyObject *__pyx_callargs[%d] = {%s, %s};"
        % (len(args) + 1, self_arg, ", ".join(arg.py_result() for arg in args))
    )
    code.putln(
        "%s = __Pyx_PyObject_FastCall(%s, __pyx_callargs+1-%s, %d+%s);"
        % (self.result(), function, arg_offset_cname, len(args), arg_offset_cname)
    )

    code.put_xdecref_clear(self_arg, py_object_type)
    code.funcstate.release_temp(self_arg)
    code.funcstate.release_temp(arg_offset_cname)
    for arg in args:
        arg.generate_disposal_code(code)
        arg.free_temps(code)
    code.putln(code.error_goto_if_null(self.result(), self.pos))
    self.generate_gotref(code)

    if reuse_function_temp:
        self.function.generate_disposal_code(code)
        self.function.free_temps(code)
    else:
        code.put_decref_clear(function, py_object_type)
        code.funcstate.release_temp(function)
    code.putln("}")


# Traceback (AttributeError)
$ cythonize -i -3 test_cpdef_func_ptr2.pyx
Compiling /home/leofang/dev/test_cpdef_func_ptr2.pyx because it changed.
[1/1] Cythonizing /home/leofang/dev/test_cpdef_func_ptr2.pyx
Traceback (most recent call last):
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/bin/cythonize", line 11, in <module>
sys.exit(main())
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Build/Cythonize.py", line 223, in main
cython_compile(path, options)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Build/Cythonize.py", line 106, in cython_compile
**options.options)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Build/Dependencies.py", line 1102, in cythonize
cythonize_one(*args)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Build/Dependencies.py", line 1208, in cythonize_one
result = compile_single(pyx_file, options, full_module_name=full_module_name)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Main.py", line 727, in compile_single
return run_pipeline(source, options, full_module_name)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Main.py", line 515, in run_pipeline
err, enddata = Pipeline.run_pipeline(pipeline, source)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Pipeline.py", line 355, in run_pipeline
data = run(phase, data)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Pipeline.py", line 335, in run
return phase(data)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Pipeline.py", line 52, in generate_pyx_code_stage
module_node.process_implementation(options, result)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/ModuleNode.py", line 143, in process_implementation
self.generate_c_code(env, options, result)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/ModuleNode.py", line 385, in generate_c_code
self.body.generate_function_definitions(env, code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 442, in generate_function_definitions
stat.generate_function_definitions(env, code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 442, in generate_function_definitions
stat.generate_function_definitions(env, code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 3174, in generate_function_definitions
FuncDefNode.generate_function_definitions(self, env, code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 1981, in generate_function_definitions
self.generate_function_body(env, code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 1743, in generate_function_body
self.body.generate_execution_code(code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 448, in generate_execution_code
stat.generate_execution_code(code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/Nodes.py", line 5988, in generate_execution_code
value.generate_evaluation_code(code)
File "/home/leofang/miniconda3/envs/cupy_cuda112_dev/lib/python3.7/site-packages/Cython/Compiler/ExprNodes.py", line 6052, in generate_evaluation_code
if value and value.is_attribute and value.obj.type.is_pyobject:
AttributeError: 'NoneType' object has no attribute 'is_pyobject'

# Fixed code
def generate_evaluation_code(self, code):
    code.mark_pos(self.pos)
    self.allocate_temp_result(code)

    self.function.generate_evaluation_code(code)
    assert self.arg_tuple.mult_factor is None
    args = self.arg_tuple.args
    for arg in args:
        arg.generate_evaluation_code(code)

    # make sure function is in temp so that we can replace the reference below if it's a method
    reuse_function_temp = self.function.is_temp
    if reuse_function_temp:
        function = self.function.result()
    else:
        function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
        self.function.make_owned_reference(code)
        code.put("%s = %s; " % (function, self.function.py_result()))
        self.function.generate_disposal_code(code)
        self.function.free_temps(code)

    self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
    code.putln("%s = NULL;" % self_arg)
    arg_offset_cname = code.funcstate.allocate_temp(
        PyrexTypes.c_int_type, manage_ref=False
    )
    code.putln("%s = 0;" % arg_offset_cname)

    def attribute_is_likely_method(attr):
        obj = attr.obj
        if obj.is_name and obj.entry.is_pyglobal:
            return False  # more likely to be a function
        return True

    if self.function.is_attribute:
        likely_method = (
            "likely" if attribute_is_likely_method(self.function) else "unlikely"
        )
    elif self.function.is_name and self.function.cf_state:
        # not an attribute itself, but might have been assigned from one (e.g. bound method)
        for assignment in self.function.cf_state:
            value = assignment.rhs
            if (
                value
                and value.is_attribute
                and value.obj.type
                and value.obj.type.is_pyobject
            ):
                if attribute_is_likely_method(value):
                    likely_method = "likely"
                    break
        else:
            likely_method = "unlikely"
    else:
        likely_method = "unlikely"

    code.putln(
        "if (CYTHON_UNPACK_METHODS && %s(PyMethod_Check(%s))) {"
        % (likely_method, function)
    )
    code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
    # the following is always true in Py3 (kept only for safety),
    # but is false for unbound methods in Py2
    code.putln("if (likely(%s)) {" % self_arg)
    code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
    code.put_incref(self_arg, py_object_type)
    code.put_incref("function", py_object_type)
    # free method object as early to possible to enable reuse from CPython's freelist
    code.put_decref_set(function, py_object_type, "function")
    code.putln("%s = 1;" % arg_offset_cname)
    code.putln("}")
    code.putln("}")

    # actually call the function
    code.globalstate.use_utility_code(
        UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c")
    )

    code.putln("{")
    code.putln(
        "PyObject *__pyx_callargs[%d] = {%s, %s};"
        % (len(args) + 1, self_arg, ", ".join(arg.py_result() for arg in args))
    )
    code.putln(
        "%s = __Pyx_PyObject_FastCall(%s, __pyx_callargs+1-%s, %d+%s);"
        % (self.result(), function, arg_offset_cname, len(args), arg_offset_cname)
    )

    code.put_xdecref_clear(self_arg, py_object_type)
    code.funcstate.release_temp(self_arg)
    code.funcstate.release_temp(arg_offset_cname)
    for arg in args:
        arg.generate_disposal_code(code)
        arg.free_temps(code)
    code.putln(code.error_goto_if_null(self.result(), self.pos))
    self.generate_gotref(code)

    if reuse_function_temp:
        self.function.generate_disposal_code(code)
        self.function.free_temps(code)
    else:
        code.put_decref_clear(function, py_object_type)
        code.funcstate.release_temp(function)
    code.putln("}")