Repository: cython/cython

Issue:
[BUG] compiler error
**Describe the bug**
An unexpected compiler error with `infer_types=True`

**To Reproduce**
```
In [1]: %%cython  
   ...: #cython: infer_types=True 
   ...: class A: 
   ...:     def f(self): 
   ...:         x = max(self.a, self.a)
```

Removing `max`, or setting `infer_types=False`, or setting `x = None` first all make it compile.

Error: [cpython_error.txt](https://github.com/cython/cython/files/6469216/cpython_error.txt)

**Expected behavior**
It should compile

**Environment (please complete the following information):**
 - OS: Linux
 - Python version: 3.7.4
 - Cython version: 0.29.21



Discussion:

Resolving patch:
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -6874,7 +6874,6 @@ class AttributeNode(ExprNode):
     is_attribute = 1
     subexprs = ['obj']
 
-    type = PyrexTypes.error_type
     entry = None
     is_called = 0
     needs_none_check = True
@@ -6964,6 +6963,8 @@ def analyse_target_types(self, env):
         return node
 
     def analyse_types(self, env, target = 0):
+        if not self.type:
+            self.type = PyrexTypes.error_type  # default value if it isn't analysed successfully
         self.initialized_check = env.directives['initializedcheck']
         node = self.analyse_as_cimported_attribute_node(env, target)
         if node is None and not target:
@@ -10989,8 +10990,6 @@ class TypeidNode(ExprNode):
     #  arg_type      ExprNode
     #  is_variable   boolean
 
-    type = PyrexTypes.error_type
-
     subexprs = ['operand']
 
     arg_type = None
@@ -11008,19 +11007,25 @@ def get_type_info_type(self, env):
     cpp_message = 'typeid operator'
 
     def analyse_types(self, env):
+        if not self.type:
+            self.type = PyrexTypes.error_type  # default value if it isn't analysed successfully
         self.cpp_check(env)
         type_info = self.get_type_info_type(env)
         if not type_info:
             self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
             return self
+        if self.operand is None:
+            return self  # already analysed, no need to repeat
         self.type = type_info
         as_type = self.operand.analyse_as_specialized_type(env)
         if as_type:
             self.arg_type = as_type
             self.is_type = True
+            self.operand = None  # nothing further uses self.operand - will only cause problems if its used in code generation
         else:
             self.arg_type = self.operand.analyse_types(env)
             self.is_type = False
+            self.operand = None  # nothing further uses self.operand - will only cause problems if its used in code generation
             if self.arg_type.type.is_pyobject:
                 self.error("Cannot use typeid on a Python object")
                 return self


Test patch:
diff --git a/tests/run/type_inference.pyx b/tests/run/type_inference.pyx
--- a/tests/run/type_inference.pyx
+++ b/tests/run/type_inference.pyx
@@ -789,3 +789,16 @@ def test_bound_methods():
   default_arg = o.default_arg
   assert default_arg(2) == 12, default_arg(2)
   assert default_arg(2, 3) == 15, default_arg(2, 2)
+
+def test_builtin_max():
+    """
+    # builtin max is slightly complicated because it gets transformed to EvalWithTempExprNode
+    # See https://github.com/cython/cython/issues/4155
+    >>> test_builtin_max()
+    """
+    class C:
+        a = 2
+        def get_max(self):
+            a = max(self.a, self.a)
+            assert typeof(a) == "Python object", typeof(a)
+    C().get_max()