big C++ mergeback
[cython.git] / Cython / Compiler / Symtab.py
index aa9c51d3f1d1c80fffc93e87270ef86281a15f66..cb5f41a7b5a74c4ed6a1c0f761992c733fefa04c 100644 (file)
@@ -8,13 +8,14 @@ from Errors import warning, error, InternalError
 from StringEncoding import EncodedString
 import Options, Naming
 import PyrexTypes
-from PyrexTypes import py_object_type
+from PyrexTypes import py_object_type, unspecified_type
 import TypeSlots
 from TypeSlots import \
     pyfunction_signature, pymethod_signature, \
     get_special_method_signature, get_property_accessor_signature
 import ControlFlow
-import __builtin__
+import Code
+import __builtin__ as builtins
 try:
     set
 except NameError:
@@ -114,9 +115,10 @@ class Entry(object):
     # api              boolean    Generate C API for C class or function
     # utility_code     string     Utility code needed when this entry is used
     #
-    # buffer_aux      BufferAux or None  Extra information needed for buffer variables
+    # buffer_aux       BufferAux or None  Extra information needed for buffer variables
     # inline_func_in_pxd boolean  Hacky special case for inline function in pxd file.
     #                             Ideally this should not be necesarry.
+    # assignments      [ExprNode] List of expressions that get assigned to this entry.
 
     inline_func_in_pxd = False
     borrowed = 0
@@ -173,6 +175,10 @@ class Entry(object):
         self.pos = pos
         self.init = init
         self.overloaded_alternatives = []
+        self.assignments = []
+    
+    def __repr__(self):
+        return "Entry(name=%s, type=%s)" % (self.name, self.type)
         
     def redeclared(self, pos):
         error(pos, "'%s' does not match previous declaration" % self.name)
@@ -193,9 +199,6 @@ class Scope(object):
     # pyfunc_entries    [Entry]            Python function entries
     # cfunc_entries     [Entry]            C function entries
     # c_class_entries   [Entry]            All extension type entries
-    # temp_entries      [Entry]            Temporary variable entries
-    # free_temp_entries [Entry]            Temp variables currently unused
-    # temp_counter      integer            Counter for naming temp vars
     # cname_to_entry    {string : Entry}   Temp cname to entry mapping
     # int_to_entry      {int : Entry}      Temp cname to entry mapping
     # return_type       PyrexType or None  Return type of function owning scope
@@ -218,8 +221,6 @@ class Scope(object):
     in_cinclude = 0
     nogil = 0
     
-    temp_prefix = Naming.pyrex_prefix
-    
     def __init__(self, name, outer_scope, parent_scope):
         # The outer_scope is the next scope in the lookup chain.
         # The parent_scope is used to derive the qualified name of this scope.
@@ -245,10 +246,6 @@ class Scope(object):
         self.c_class_entries = []
         self.defined_c_classes = []
         self.imported_c_classes = {}
-        self.temp_entries = []
-        self.free_temp_entries = []
-        #self.pending_temp_entries = [] # TEMPORARY
-        self.temp_counter = 1
         self.cname_to_entry = {}
         self.string_to_entry = {}
         self.identifier_to_entry = {}
@@ -257,6 +254,7 @@ class Scope(object):
         self.pystring_entries = []
         self.buffer_entries = []
         self.control_flow = ControlFlow.LinearControlFlow()
+        self.return_type = None
         
     def start_branching(self, pos):
         self.control_flow = self.control_flow.start_branch(pos)
@@ -270,9 +268,6 @@ class Scope(object):
     def __str__(self):
         return "<%s %s>" % (self.__class__.__name__, self.qualified_name)
 
-    def intern_identifier(self, name):
-        return self.global_scope().intern_identifier(name)
-
     def qualifying_scope(self):
         return self.parent_scope
     
@@ -326,7 +321,7 @@ class Scope(object):
     
     def qualify_name(self, name):
         return "%s.%s" % (self.qualified_name, name)
-    
+
     def declare_const(self, name, type, value, pos, cname = None, visibility = 'private'):
         # Add an entry for a named constant.
         if not cname:
@@ -336,7 +331,7 @@ class Scope(object):
                 cname = self.mangle(Naming.enum_prefix, name)
         entry = self.declare(name, cname, type, pos, visibility)
         entry.is_const = 1
-        entry.value = value
+        entry.value_node = value
         return entry
     
     def declare_type(self, name, type, pos, 
@@ -358,7 +353,11 @@ class Scope(object):
                 cname = name
             else:
                 cname = self.mangle(Naming.type_prefix, name)
-        type = PyrexTypes.CTypedefType(cname, base_type, (visibility == 'extern'))
+        try:
+            type = PyrexTypes.create_typedef_type(cname, base_type, (visibility == 'extern'))
+        except ValueError, e:
+            error(pos, e.message)
+            type = PyrexTypes.error_type
         entry = self.declare_type(name, type, pos, cname, visibility)
         type.qualified_name = entry.qualified_name
         return entry
@@ -379,6 +378,7 @@ class Scope(object):
             entry = self.declare_type(name, type, pos, cname,
                 visibility = visibility, defining = scope is not None)
             self.sue_entries.append(entry)
+            type.entry = entry
         else:
             if not (entry.is_type and entry.type.is_struct_or_union
                     and entry.type.kind == kind):
@@ -519,7 +519,7 @@ class Scope(object):
             if entry.as_module:
                 scope = entry.as_module
             else:
-                error(pos, "'%s' is not a cimported module" % scope.qualified_name)
+                error(pos, "'%s' is not a cimported module" % '.'.join(path))
                 return None
         return scope
         
@@ -554,138 +554,8 @@ class Scope(object):
         if entry and entry.is_type:
             return entry.type
 
-    def add_string_const(self, value, identifier = False):
-        # Add an entry for a string constant.
-        if identifier:
-            cname = self.new_string_const_cname(value)
-        else:
-            cname = self.new_const_cname()
-        if value.is_unicode:
-            c_type = PyrexTypes.c_utf8_char_array_type
-            value = value.utf8encode()
-        else:
-            c_type = PyrexTypes.c_char_array_type
-            value = value.byteencode()
-        entry = Entry("", cname, c_type, init = value)
-        entry.used = 1
-        self.const_entries.append(entry)
-        return entry
-
-    def get_string_const(self, value, identifier = False):
-        # Get entry for string constant. Returns an existing
-        # one if possible, otherwise creates a new one.
-        genv = self.global_scope()
-        if identifier:
-            string_map = genv.identifier_to_entry
-        else:
-            string_map = genv.string_to_entry
-        entry = string_map.get(value)
-        if not entry:
-            entry = self.add_string_const(value, identifier)
-            entry.is_identifier = identifier
-            string_map[value] = entry
-        return entry
-
-    def add_py_string(self, entry, identifier = None):
-        # If not already done, allocate a C name for a Python version of
-        # a string literal, and add it to the list of Python strings to
-        # be created at module init time. If the string resembles a
-        # Python identifier, it will be interned.
-        if entry.pystring_cname:
-            return
-        value = entry.init
-        entry.pystring_cname = Naming.py_const_prefix + entry.cname[len(Naming.const_prefix):]
-        self.pystring_entries.append(entry)
-        self.global_scope().all_pystring_entries.append(entry)
-        if identifier or (identifier is None and possible_identifier(value)):
-            entry.is_interned = 1
-            self.global_scope().new_interned_string_entries.append(entry)
-
-    def add_py_num(self, value):
-        # Add an entry for an int constant.
-        cname = "%s%s" % (Naming.interned_num_prefix, value)
-        cname = cname.replace('-', 'neg_').replace('.','_')
-        entry = Entry("", cname, py_object_type, init = value)
-        entry.used = 1
-        entry.is_interned = 1
-        self.const_entries.append(entry)
-        self.interned_nums.append(entry)
-        return entry
-        
-    def get_py_num(self, value, longness):
-        # Get entry for int constant. Returns an existing
-        # one if possible, otherwise creates a new one.
-        if longness or Utils.long_literal(value):
-            value += "L"
-        genv = self.global_scope()
-        entry = genv.num_to_entry.get(value)
-        if not entry:
-            entry = genv.add_py_num(value)
-            genv.num_to_entry[value] = entry
-            genv.pynum_entries.append(entry)
-        return entry
-        
-    def get_py_obj(self, obj, c_prefix=''):
-        # Get entry for a generic constant. Returns an existing
-        # one if possible, otherwise creates a new one.
-        genv = self.global_scope()
-        entry = genv.obj_to_entry.get(obj)
-        if not entry:
-            entry = genv.add_py_num(obj, c_prefix)
-            genv.obj_to_entry[obj] = entry
-        return entry
-    
-    def new_string_const_cname(self, value):
-        # Create a new globally-unique nice name for a string constant.
-        if len(value) < 20 and nice_identifier(value):
-            return "%s%s" % (Naming.const_prefix, value)
-        else:
-            return self.global_scope().new_const_cname()
-
-    def new_const_cname(self):
-        # Create a new globally-unique name for a constant.
-        return self.global_scope().new_const_cname()
-
-    def allocate_temp(self, type):
-        # Allocate a temporary variable of the given type from the 
-        # free list if available, otherwise create a new one.
-        # Returns the cname of the variable.
-        for entry in self.free_temp_entries:
-            if entry.type == type:
-                self.free_temp_entries.remove(entry)
-                return entry.cname
-        n = self.temp_counter
-        self.temp_counter = n + 1
-        cname = "%s%d" % (self.temp_prefix, n)
-        entry = Entry("", cname, type)
-        entry.used = 1
-        if type.is_pyobject or type == PyrexTypes.c_py_ssize_t_type:
-            entry.init = "0"
-        self.cname_to_entry[entry.cname] = entry
-        self.temp_entries.append(entry)
-        return entry.cname
-    
-    def allocate_temp_pyobject(self):
-        # Allocate a temporary PyObject variable.
-        return self.allocate_temp(py_object_type)
-
-    def release_temp(self, cname):
-        # Release a temporary variable for re-use.
-        if not cname: # can happen when type of an expr is void
-            return
-        entry = self.cname_to_entry[cname]
-        if entry in self.free_temp_entries:
-            raise InternalError("Temporary variable %s released more than once"
-                % cname)
-        self.free_temp_entries.append(entry)
-    
-    def temps_in_use(self):
-        # Return a new list of temp entries currently in use.
-        return [entry for entry in self.temp_entries
-            if entry not in self.free_temp_entries]
-    
-    def use_utility_code(self, new_code, name=None):
-        self.global_scope().use_utility_code(new_code, name)
+    def use_utility_code(self, new_code):
+        self.global_scope().use_utility_code(new_code)
 
     def generate_library_function_declarations(self, code):
         # Generate extern decls for C library funcs used.
@@ -699,6 +569,10 @@ class Scope(object):
                 return 1
         return 0
     
+    def infer_types(self):
+        from TypeInference import get_type_inferer
+        get_type_inferer().infer_types(self)
+    
     def is_cpp(self):
         outer = self.outer_scope
         if outer is None:
@@ -735,7 +609,7 @@ class BuiltinScope(Scope):
             self.declare_var(name, type, None, cname)
         
     def declare_builtin(self, name, pos):
-        if not hasattr(__builtin__, name):
+        if not hasattr(builtins, name):
             if self.outer_scope is not None:
                 return self.outer_scope.declare_builtin(name, pos)
             else:
@@ -773,6 +647,7 @@ class BuiltinScope(Scope):
         var_entry.is_variable = 1
         var_entry.is_cglobal = 1
         var_entry.is_readonly = 1
+        var_entry.is_builtin = 1
         var_entry.utility_code = utility_code
         entry.as_variable = var_entry
 
@@ -818,9 +693,7 @@ class ModuleScope(Scope):
     # method_table_cname   string             C name of method table
     # doc                  string             Module doc string
     # doc_cname            string             C name of module doc string
-    # const_counter        integer            Counter for naming constants (PS: MOVED TO GLOBAL)
-    # utility_code_list    [(UtilityCode, string)] Queuing utility codes for forwarding to Code.py
-    # default_entries      [Entry]            Function argument default entries
+    # utility_code_list    [UtilityCode]      Queuing utility codes for forwarding to Code.py
     # python_include_files [string]           Standard  Python headers to be included
     # include_files        [string]           Other C headers to be included
     # string_to_entry      {string : Entry}   Map string const to entry
@@ -832,9 +705,6 @@ class ModuleScope(Scope):
     # included_files       [string]           Cython sources included with 'include'
     # pxd_file_loaded      boolean            Corresponding .pxd file has been processed
     # cimported_modules    [ModuleScope]      Modules imported with cimport
-    # new_interned_string_entries [Entry]     New interned strings waiting to be declared
-    # interned_nums        [int/long]         Interned numeric constants
-    # all_pystring_entries [Entry]            Python string consts from all scopes
     # types_imported       {PyrexType : 1}    Set of types for which import code generated
     # has_import_star      boolean            Module contains import *
     # cpp                  boolean            Compiling a C++ file
@@ -859,20 +729,14 @@ class ModuleScope(Scope):
         self.doc = ""
         self.doc_cname = Naming.moddoc_cname
         self.utility_code_list = []
-        self.default_entries = []
         self.module_entries = {}
         self.python_include_files = ["Python.h", "structmember.h"]
         self.include_files = []
         self.type_names = dict(outer_scope.type_names)
         self.pxd_file_loaded = 0
         self.cimported_modules = []
-        self.new_interned_string_entries = []
-        self.interned_nums = []
-        self.interned_objs = []
-        self.all_pystring_entries = []
         self.types_imported = {}
         self.included_files = []
-        self.pynum_entries = []
         self.has_extern_class = 0
         self.cached_builtins = []
         self.undeclared_cached_builtins = []
@@ -887,7 +751,7 @@ class ModuleScope(Scope):
         return self
     
     def declare_builtin(self, name, pos):
-        if not hasattr(__builtin__, name):
+        if not hasattr(builtins, name):
             if self.has_import_star:
                 entry = self.declare_var(name, py_object_type, pos)
                 return entry
@@ -911,11 +775,6 @@ class ModuleScope(Scope):
             entry.is_builtin = 1
         return entry
 
-    def intern_identifier(self, name):
-        string_entry = self.get_string_const(name, identifier = True)
-        self.add_py_string(string_entry, identifier = 1)
-        return string_entry.pystring_cname
-
     def find_module(self, module_name, pos):
         # Find a module in the import namespace, interpreting
         # relative imports relative to this module's parent.
@@ -994,6 +853,8 @@ class ModuleScope(Scope):
         if not visibility in ('private', 'public', 'extern'):
             error(pos, "Module-level variable cannot be declared %s" % visibility)
         if not is_cdef:
+            if type is unspecified_type:
+                type = py_object_type
             if not (type.is_pyobject and not type.is_extension_type):
                 raise InternalError(
                     "Non-cdef global variable is not a generic Python object")
@@ -1010,30 +871,25 @@ class ModuleScope(Scope):
         if not entry:
             self.declare_var(name, py_object_type, pos)
     
-    def add_default_value(self, type):
-        # Add an entry for holding a function argument
-        # default value.
-        cname = self.new_const_cname()
-        entry = Entry("", cname, type)
-        self.default_entries.append(entry)
-        return entry
-        
-    def new_const_cname(self):
-        global const_counter
-        # Create a new globally-unique name for a constant.
-        prefix=''
-        n = const_counter
-        const_counter = n + 1
-        return "%s%s%d" % (Naming.const_prefix, prefix, n)
-    
-    def use_utility_code(self, new_code, name=None):
+    def use_utility_code(self, new_code):
         if new_code is not None:
-            self.utility_code_list.append((new_code, name))
+            self.utility_code_list.append(new_code)
 
     def declare_c_class(self, name, pos, defining = 0, implementing = 0,
         module_name = None, base_type = None, objstruct_cname = None,
         typeobj_cname = None, visibility = 'private', typedef_flag = 0, api = 0,
         buffer_defaults = None):
+        # If this is a non-extern typedef class, expose the typedef, but use
+        # the non-typedef struct internally to avoid needing forward
+        # declarations for anonymous structs. 
+        if typedef_flag and visibility != 'extern':
+            if visibility != 'public':
+                warning(pos, "ctypedef only valid for public and extern classes", 2)
+            objtypedef_cname = objstruct_cname
+            objstruct_cname = None
+            typedef_flag = 0
+        else:
+            objtypedef_cname = None
         #
         #  Look for previous declaration as a type
         #
@@ -1055,9 +911,11 @@ class ModuleScope(Scope):
         #  Make a new entry if needed
         #
         if not entry:
-            type = PyrexTypes.PyExtensionType(name, typedef_flag, base_type)
+            type = PyrexTypes.PyExtensionType(name, typedef_flag, base_type, visibility == 'extern')
             type.pos = pos
             type.buffer_defaults = buffer_defaults
+            if objtypedef_cname is not None:
+                type.objtypedef_cname = objtypedef_cname
             if visibility == 'extern':
                 type.module_name = module_name
             else:
@@ -1069,7 +927,7 @@ class ModuleScope(Scope):
             if objstruct_cname:
                 type.objstruct_cname = objstruct_cname
             elif not entry.in_cinclude:
-                type.objstruct_cname = self.mangle(Naming.objstruct_prefix, name)                
+                type.objstruct_cname = self.mangle(Naming.objstruct_prefix, name)
             else:
                 error(entry.pos, 
                     "Object name required for 'public' or 'extern' C class")
@@ -1174,6 +1032,22 @@ class ModuleScope(Scope):
             type.vtabstruct_cname = self.mangle(Naming.vtabstruct_prefix, entry.name)
             type.vtabptr_cname = self.mangle(Naming.vtabptr_prefix, entry.name)
 
+    def check_c_classes_pxd(self):
+        # Performs post-analysis checking and finishing up of extension types
+        # being implemented in this module. This is called only for the .pxd.
+        #
+        # Checks all extension types declared in this scope to
+        # make sure that:
+        #
+        #    * The extension type is fully declared
+        #
+        # Also allocates a name for the vtable if needed.
+        #
+        for entry in self.c_class_entries:
+            # Check defined
+            if not entry.type.scope:
+                error(entry.pos, "C class '%s' is declared but not defined" % entry.name)
+                
     def check_c_classes(self):
         # Performs post-analysis checking and finishing up of extension types
         # being implemented in this module. This is called only for the main
@@ -1250,10 +1124,16 @@ class ModuleScope(Scope):
     def is_cpp(self):
         return self.cpp
         
-class LocalScope(Scope):
+    def infer_types(self):
+        from TypeInference import PyObjectTypeInferer
+        PyObjectTypeInferer().infer_types(self)
+        
+class LocalScope(Scope):    
 
-    def __init__(self, name, outer_scope):
-        Scope.__init__(self, name, outer_scope, outer_scope)
+    def __init__(self, name, outer_scope, parent_scope = None):
+        if parent_scope is None:
+            parent_scope = outer_scope
+        Scope.__init__(self, name, outer_scope, parent_scope)
     
     def mangle(self, prefix, name):
         return prefix + name
@@ -1280,7 +1160,7 @@ class LocalScope(Scope):
             cname, visibility, is_cdef)
         if type.is_pyobject and not Options.init_local_none:
             entry.init = "0"
-        entry.init_to_none = type.is_pyobject and Options.init_local_none
+        entry.init_to_none = (type.is_pyobject or type.is_unspecified) and Options.init_local_none
         entry.is_local = 1
         self.var_entries.append(entry)
         return entry
@@ -1311,11 +1191,9 @@ class LocalScope(Scope):
 
 class GeneratorLocalScope(LocalScope):
 
-    temp_prefix = Naming.cur_scope_cname + "->" + LocalScope.temp_prefix
-    
     def mangle_closure_cnames(self, scope_var):
-        for entry in self.entries.values() + self.temp_entries:
-            entry.in_closure = 1
+#        for entry in self.entries.values() + self.temp_entries:
+#            entry.in_closure = 1
         LocalScope.mangle_closure_cnames(self, scope_var)
     
 #    def mangle(self, prefix, name):
@@ -1350,7 +1228,7 @@ class StructOrUnionScope(Scope):
     def declare_cfunction(self, name, type, pos, 
                           cname = None, visibility = 'private', defining = 0,
                           api = 0, in_pxd = 0, modifiers = ()):
-        self.declare_var(name, type, pos, cname, visibility)
+        return self.declare_var(name, type, pos, cname, visibility)
 
 class ClassScope(Scope):
     #  Abstract base class for namespace of
@@ -1370,6 +1248,9 @@ class ClassScope(Scope):
         return self.outer_scope.add_string_const(value, identifier)
 
     def lookup(self, name):
+        entry = Scope.lookup(self, name)
+        if entry:
+            return entry
         if name == "classmethod":
             # We don't want to use the builtin classmethod here 'cause it won't do the 
             # right thing in this scope (as the class memebers aren't still functions). 
@@ -1383,36 +1264,26 @@ class ClassScope(Scope):
                     py_object_type,
                     [PyrexTypes.CFuncTypeArg("", py_object_type, None)], 0, 0))
             entry.is_cfunction = 1
-            return entry
-        else:
-            return Scope.lookup(self, name)
+        return entry
     
 
 class PyClassScope(ClassScope):
     #  Namespace of a Python class.
     #
-    #  class_dict_cname    string   C variable holding class dict
     #  class_obj_cname     string   C variable holding class object
 
     is_py_class_scope = 1
     
     def declare_var(self, name, type, pos, 
             cname = None, visibility = 'private', is_cdef = 0):
+        if type is unspecified_type:
+            type = py_object_type
         # Add an entry for a class attribute.
         entry = Scope.declare_var(self, name, type, pos, 
             cname, visibility, is_cdef)
         entry.is_pyglobal = 1
         return entry
 
-    def allocate_temp(self, type):
-        return self.outer_scope.allocate_temp(type)
-
-    def release_temp(self, cname):
-        self.outer_scope.release_temp(cname)
-
-    #def recycle_pending_temps(self):
-    #    self.outer_scope.recycle_pending_temps()
-
     def add_default_value(self, type):
         return self.outer_scope.add_default_value(type)
 
@@ -1452,7 +1323,8 @@ class CClassScope(ClassScope):
         # If the type or any of its base types have Python-valued
         # C attributes, then it needs to participate in GC.
         return self.has_pyobject_attrs or \
-            (self.parent_type.base_type and \
+            (self.parent_type.base_type and
+                self.parent_type.base_type.scope is not None and
                 self.parent_type.base_type.scope.needs_gc())
 
     def declare_var(self, name, type, pos, 
@@ -1462,7 +1334,7 @@ class CClassScope(ClassScope):
             if self.defined:
                 error(pos,
                     "C attributes cannot be added in implementation part of"
-                    " extension type")
+                    " extension type defined in a pxd")
             if get_special_method_signature(name):
                 error(pos, 
                     "The name '%s' is reserved for a special method."
@@ -1492,6 +1364,8 @@ class CClassScope(ClassScope):
                     "Non-generic Python attribute cannot be exposed for writing from Python")
             return entry
         else:
+            if type is unspecified_type:
+                type = py_object_type
             # Add an entry for a class attribute.
             entry = Scope.declare_var(self, name, type, pos, 
                 cname, visibility, is_cdef)
@@ -1500,7 +1374,6 @@ class CClassScope(ClassScope):
                                   # I keep it in for now. is_member should be enough
                                   # later on
             self.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname
-            entry.interned_cname = self.intern_identifier(name)
             return entry
 
 
@@ -1608,11 +1481,6 @@ class CClassScope(ClassScope):
                                        base_entry.visibility, base_entry.func_modifiers)
             entry.is_inherited = 1
             
-    def allocate_temp(self, type):
-        return Scope.allocate_temp(self.global_scope(), type)
-
-    def release_temp(self, cname):
-        return Scope.release_temp(self.global_scope(), cname)
         
 
 class CppClassScope(Scope):
@@ -1695,7 +1563,7 @@ class PropertyScope(Scope):
 # Should this go elsewhere (and then get imported)?
 #------------------------------------------------------------------------------------
 
-classmethod_utility_code = Utils.UtilityCode(
+classmethod_utility_code = Code.UtilityCode(
 proto = """
 #include "descrobject.h"
 static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
@@ -1703,18 +1571,31 @@ static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
 impl = """
 static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
     /* It appears that PyMethodDescr_Type is not anywhere exposed in the Python/C API */
-    /* if (!PyObject_TypeCheck(method, &PyMethodDescr_Type)) { */ 
-    if (__Pyx_StrEq(Py_TYPE(method)->tp_name, "method_descriptor")) { /* cdef classes */
+    static PyTypeObject *methoddescr_type = NULL;
+    if (methoddescr_type == NULL) {
+       PyObject *meth = __Pyx_GetAttrString((PyObject*)&PyList_Type, "append");
+       if (!meth) return NULL;
+       methoddescr_type = Py_TYPE(meth);
+       Py_DECREF(meth);
+    }
+    if (PyObject_TypeCheck(method, methoddescr_type)) { /* cdef classes */
         PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
-        return PyDescr_NewClassMethod(descr->d_type, descr->d_method);
+        #if PY_VERSION_HEX < 0x03020000
+        PyTypeObject *d_type = descr->d_type;
+        #else
+        PyTypeObject *d_type = descr->d_common.d_type;
+        #endif
+        return PyDescr_NewClassMethod(d_type, descr->d_method);
     }
-    else if (PyMethod_Check(method)) {                                /* python classes */
+    else if (PyMethod_Check(method)) { /* python classes */
         return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
     }
     else if (PyCFunction_Check(method)) {
         return PyClassMethod_New(method);
     }
-    PyErr_Format(PyExc_TypeError, "Class-level classmethod() can only be called on a method_descriptor or instance method.");
+    PyErr_Format(PyExc_TypeError,
+                 "Class-level classmethod() can only be called on"
+                 "a method_descriptor or instance method.");
     return NULL;
 }
 """)