fix unprefixed strings with non-UTF8 source code encoding in Py3
[cython.git] / Cython / Compiler / Code.py
index c3ed37186a2ec41fde58ebe094420058b5571d35..dcfad31396cce182fc4a87486d4fc93633a2b5b9 100644 (file)
@@ -1,4 +1,4 @@
-# cython: language_level = 3
+# cython: language_level = 3, py2_import=True
 #
 #   Pyrex - Code output module
 #
@@ -6,7 +6,7 @@
 import cython
 cython.declare(re=object, Naming=object, Options=object, StringEncoding=object,
                Utils=object, SourceDescriptor=object, StringIOTree=object,
-               DebugFlags=object, none_or_sub=object)
+               DebugFlags=object, none_or_sub=object, basestring=object)
 
 import re
 import Naming
@@ -19,9 +19,24 @@ import DebugFlags
 
 from Cython.Utils import none_or_sub
 try:
-    basestring
-except NameError:
-    basestring = str
+    from __builtin__ import basestring
+except ImportError:
+    from builtins import str as basestring
+
+
+non_portable_builtins_map = {
+    # builtins that have different names in different Python versions
+    'bytes'         : ('PY_MAJOR_VERSION < 3',  'str'),
+    'unicode'       : ('PY_MAJOR_VERSION >= 3', 'str'),
+    'xrange'        : ('PY_MAJOR_VERSION >= 3', 'range'),
+    'BaseException' : ('PY_VERSION_HEX < 0x02050000', 'Exception'),
+    }
+
+uncachable_builtins = [
+    # builtin names that cannot be cached because they may or may not
+    # be available at import time
+    'WindowsError',
+    ]
 
 class UtilityCode(object):
     # Stores utility code to add during code generation.
@@ -29,7 +44,7 @@ class UtilityCode(object):
     # See GlobalState.put_utility_code.
     #
     # hashes/equals by instance
-    
+
     def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None,
                  proto_block='utility_code_proto'):
         # proto_block: Which code block to dump prototype in. See GlobalState.
@@ -84,8 +99,8 @@ class UtilityCode(object):
                 writer.put(self.cleanup)
             else:
                 self.cleanup(writer, output.module_pos)
-            
-        
+
+
 
 class FunctionState(object):
     # return_label     string          function return point label
@@ -101,7 +116,7 @@ class FunctionState(object):
     def __init__(self, owner, names_taken=cython.set()):
         self.names_taken = names_taken
         self.owner = owner
-        
+
         self.error_label = None
         self.label_counter = 0
         self.labels_used = cython.set()
@@ -117,6 +132,7 @@ class FunctionState(object):
         self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
         self.temps_used_type = {} # name -> (type, manage_ref)
         self.temp_counter = 0
+        self.closure_temps = None
 
     # labels
 
@@ -127,28 +143,28 @@ class FunctionState(object):
         if name is not None:
             label += '_' + name
         return label
-    
+
     def new_error_label(self):
         old_err_lbl = self.error_label
         self.error_label = self.new_label('error')
         return old_err_lbl
-    
+
     def get_loop_labels(self):
         return (
             self.continue_label,
             self.break_label)
-    
+
     def set_loop_labels(self, labels):
         (self.continue_label,
          self.break_label) = labels
-    
+
     def new_loop_labels(self):
         old_labels = self.get_loop_labels()
         self.set_loop_labels(
-            (self.new_label("continue"), 
+            (self.new_label("continue"),
              self.new_label("break")))
         return old_labels
-    
+
     def get_all_labels(self):
         return (
             self.continue_label,
@@ -172,10 +188,10 @@ class FunctionState(object):
                 new_labels.append(old_label)
         self.set_all_labels(new_labels)
         return old_labels
-    
+
     def use_label(self, lbl):
         self.labels_used.add(lbl)
-        
+
     def label_used(self, lbl):
         return lbl in self.labels_used
 
@@ -270,6 +286,9 @@ class FunctionState(object):
                 if manage_ref
                 for cname in freelist]
 
+    def init_closure_temps(self, scope):
+        self.closure_temps = ClosureTempAllocator(scope)
+
 
 class IntConst(object):
     """Global info about a Python integer constant held by GlobalState.
@@ -313,7 +332,8 @@ class StringConst(object):
         self.escaped_value = StringEncoding.escape_byte_string(byte_string)
         self.py_strings = None
 
-    def get_py_string_const(self, encoding, identifier=None, is_str=False):
+    def get_py_string_const(self, encoding, identifier=None,
+                            is_str=False, py3str_cstring=None):
         py_strings = self.py_strings
         text = self.text
 
@@ -332,47 +352,52 @@ class StringConst(object):
             else:
                 encoding_key = ''.join(find_alphanums(encoding))
 
-        key = (is_str, is_unicode, encoding_key)
-        if py_strings is not None and key in py_strings:
-            py_string = py_strings[key]
+        key = (is_str, is_unicode, encoding_key, py3str_cstring)
+        if py_strings is not None:
+            try:
+                return py_strings[key]
+            except KeyError:
+                pass
         else:
-            if py_strings is None:
-                self.py_strings = {}
-            if identifier:
-                intern = True
-            elif identifier is None:
-                if isinstance(text, unicode):
-                    intern = bool(possible_unicode_identifier(text))
-                else:
-                    intern = bool(possible_bytes_identifier(text))
-            else:
-                intern = False
-            if intern:
-                prefix = Naming.interned_str_prefix
-            else:
-                prefix = Naming.py_const_prefix
-            pystring_cname = "%s%s_%s" % (
-                prefix,
-                (is_str and 's') or (is_unicode and 'u') or 'b',
-                self.cname[len(Naming.const_prefix):])
-
-            py_string = PyStringConst(
-                pystring_cname, encoding, is_unicode, is_str, intern)
-            self.py_strings[key] = py_string
+            self.py_strings = {}
 
+        if identifier:
+            intern = True
+        elif identifier is None:
+            if isinstance(text, unicode):
+                intern = bool(possible_unicode_identifier(text))
+            else:
+                intern = bool(possible_bytes_identifier(text))
+        else:
+            intern = False
+        if intern:
+            prefix = Naming.interned_str_prefix
+        else:
+            prefix = Naming.py_const_prefix
+        pystring_cname = "%s%s_%s" % (
+            prefix,
+            (is_str and 's') or (is_unicode and 'u') or 'b',
+            self.cname[len(Naming.const_prefix):])
+
+        py_string = PyStringConst(
+            pystring_cname, encoding, is_unicode, is_str, py3str_cstring, intern)
+        self.py_strings[key] = py_string
         return py_string
 
 class PyStringConst(object):
     """Global info about a Python string constant held by GlobalState.
     """
     # cname       string
+    # py3str_cstring string
     # encoding    string
     # intern      boolean
     # is_unicode  boolean
     # is_str      boolean
 
-    def __init__(self, cname, encoding, is_unicode, is_str=False, intern=False):
+    def __init__(self, cname, encoding, is_unicode, is_str=False,
+                 py3str_cstring=None, intern=False):
         self.cname = cname
+        self.py3str_cstring = py3str_cstring
         self.encoding = encoding
         self.is_str = is_str
         self.is_unicode = is_unicode
@@ -402,7 +427,7 @@ class GlobalState(object):
 
     # parts            {string:CCodeWriter}
 
-    
+
     # interned_strings
     # consts
     # interned_nums
@@ -438,7 +463,7 @@ class GlobalState(object):
         'utility_code_def',
         'end'
     ]
-    
+
 
     def __init__(self, writer, emit_linenums=False):
         self.filename_table = {}
@@ -475,6 +500,7 @@ class GlobalState(object):
         w.enter_cfunc_scope()
         w.putln("")
         w.putln("static int __Pyx_InitCachedConstants(void) {")
+        w.put_declare_refcount_context()
         w.put_setup_refcount_context("__Pyx_InitCachedConstants")
 
         w = self.parts['init_globals']
@@ -556,7 +582,7 @@ class GlobalState(object):
             w = self.parts['cleanup_module']
             w.putln("}")
             w.exit_cfunc_scope()
-         
+
     def put_pyobject_decl(self, entry):
         self['global_var'].putln("static PyObject *%s;" % entry.cname)
 
@@ -594,10 +620,16 @@ class GlobalState(object):
             c = self.new_string_const(text, byte_string)
         return c
 
-    def get_py_string_const(self, text, identifier=None, is_str=False):
+    def get_py_string_const(self, text, identifier=None,
+                            is_str=False, unicode_value=None):
         # return a Python string constant, creating a new one if necessary
         c_string = self.get_string_const(text)
-        py_string = c_string.get_py_string_const(text.encoding, identifier, is_str)
+        py3str_cstring = None
+        if is_str and unicode_value is not None \
+               and unicode_value.utf8encode() != text.byteencode():
+            py3str_cstring = self.get_string_const(unicode_value)
+        py_string = c_string.get_py_string_const(
+            text.encoding, identifier, is_str, py3str_cstring)
         return py_string
 
     def get_interned_identifier(self, text):
@@ -646,21 +678,22 @@ class GlobalState(object):
         return "%s%s%d" % (Naming.const_prefix, prefix, n)
 
     def add_cached_builtin_decl(self, entry):
-        if Options.cache_builtins:
+        if entry.is_builtin and entry.is_const:
             if self.should_declare(entry.cname, entry):
                 self.put_pyobject_decl(entry)
                 w = self.parts['cached_builtins']
-                if entry.name == 'xrange':
-                    # replaced by range() in Py3
-                    w.putln('#if PY_MAJOR_VERSION >= 3')
+                condition = None
+                if entry.name in non_portable_builtins_map:
+                    condition, replacement = non_portable_builtins_map[entry.name]
+                    w.putln('#if %s' % condition)
                     self.put_cached_builtin_init(
-                        entry.pos, StringEncoding.EncodedString('range'),
+                        entry.pos, StringEncoding.EncodedString(replacement),
                         entry.cname)
                     w.putln('#else')
                 self.put_cached_builtin_init(
                     entry.pos, StringEncoding.EncodedString(entry.name),
                     entry.cname)
-                if entry.name == 'xrange':
+                if condition:
                     w.putln('#endif')
 
     def put_cached_builtin_init(self, pos, name, cname):
@@ -722,6 +755,17 @@ class GlobalState(object):
 
                 decls_writer.putln(
                     "static PyObject *%s;" % py_string.cname)
+                if py_string.py3str_cstring:
+                    w.putln("#if PY_MAJOR_VERSION >= 3")
+                    w.putln(
+                        "{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
+                        py_string.cname,
+                        py_string.py3str_cstring.cname,
+                        py_string.py3str_cstring.cname,
+                        '0', 1, 0,
+                        py_string.intern
+                        ))
+                    w.putln("#else")
                 w.putln(
                     "{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
                     py_string.cname,
@@ -732,6 +776,8 @@ class GlobalState(object):
                     py_string.is_str,
                     py_string.intern
                     ))
+                if py_string.py3str_cstring:
+                    w.putln("#endif")
             w.putln("{0, 0, 0, 0, 0, 0, 0}")
             w.putln("};")
 
@@ -764,7 +810,7 @@ class GlobalState(object):
     # The functions below are there in a transition phase only
     # and will be deprecated. They are called from Nodes.BlockNode.
     # The copy&paste duplication is intentional in order to be able
-    # to see quickly how BlockNode worked, until this is replaced.    
+    # to see quickly how BlockNode worked, until this is replaced.
 
     def should_declare(self, cname, entry):
         if cname in self.declared_cnames:
@@ -813,7 +859,7 @@ class GlobalState(object):
     #
     # Utility code state
     #
-    
+
     def use_utility_code(self, utility_code):
         """
         Adds code to the C file. utility_code should
@@ -859,32 +905,34 @@ class CCodeWriter(object):
     - filename_table, filename_list, input_file_contents: All codewriters
       coming from the same root share the same instances simultaneously.
     """
-    
-    # f                file            output file
-    # buffer           StringIOTree
-    
-    # level            int             indentation level
-    # bol              bool            beginning of line?
-    # marker           string          comment to emit before next line
-    # funcstate        FunctionState   contains state local to a C function used for code
-    #                                  generation (labels and temps state etc.)
-    # globalstate      GlobalState     contains state global for a C file (input file info,
-    #                                  utility code, declared constants etc.)
-    # emit_linenums    boolean         whether or not to write #line pragmas
+
+    # f                   file            output file
+    # buffer              StringIOTree
+
+    # level               int             indentation level
+    # bol                 bool            beginning of line?
+    # marker              string          comment to emit before next line
+    # funcstate           FunctionState   contains state local to a C function used for code
+    #                                     generation (labels and temps state etc.)
+    # globalstate         GlobalState     contains state global for a C file (input file info,
+    #                                     utility code, declared constants etc.)
+    # emit_linenums       boolean         whether or not to write #line pragmas
     #
-    # pyclass_stack    list            used during recursive code generation to pass information
-    #                                  about the current class one is in
+    # c_line_in_traceback boolean         append the c file and line number to the traceback for exceptions
+    #
+    # pyclass_stack       list            used during recursive code generation to pass information
+    #                                     about the current class one is in
 
     globalstate = None
-    
-    def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
+
+    def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None, c_line_in_traceback=True):
         if buffer is None: buffer = StringIOTree()
         self.buffer = buffer
         self.marker = None
         self.last_marker_line = 0
         self.source_desc = ""
         self.pyclass_stack = []
-        
+
         self.funcstate = None
         self.level = 0
         self.call_level = 0
@@ -902,11 +950,12 @@ class CCodeWriter(object):
             self.emit_linenums = self.globalstate.emit_linenums
         else:
             self.emit_linenums = emit_linenums
+        self.c_line_in_traceback = c_line_in_traceback
 
     def create_new(self, create_from, buffer, copy_formatting):
         # polymorphic constructor -- very slightly more versatile
         # than using __class__
-        result = CCodeWriter(create_from, buffer, copy_formatting)
+        result = CCodeWriter(create_from, buffer, copy_formatting, c_line_in_traceback=self.c_line_in_traceback)
         return result
 
     def copyto(self, f):
@@ -916,13 +965,13 @@ class CCodeWriter(object):
         return self.buffer.getvalue()
 
     def write(self, s):
-        # also put invalid markers (lineno 0), to indicate that those lines 
+        # also put invalid markers (lineno 0), to indicate that those lines
         # have no Cython source code correspondence
         if self.marker is None:
             cython_lineno = self.last_marker_line
         else:
             cython_lineno = self.marker[0]
-        
+
         self.buffer.markers.extend([cython_lineno] * s.count('\n'))
         self.buffer.write(s)
 
@@ -935,7 +984,7 @@ class CCodeWriter(object):
         Creates a new CCodeWriter connected to the same global state, which
         can later be inserted using insert.
         """
-        return CCodeWriter(create_from=self)
+        return CCodeWriter(create_from=self, c_line_in_traceback=self.c_line_in_traceback)
 
     def insert(self, writer):
         """
@@ -971,7 +1020,7 @@ class CCodeWriter(object):
 
     def enter_cfunc_scope(self):
         self.funcstate = FunctionState(self)
-    
+
     def exit_cfunc_scope(self):
         self.funcstate = None
 
@@ -986,8 +1035,10 @@ class CCodeWriter(object):
     def get_string_const(self, text):
         return self.globalstate.get_string_const(text).cname
 
-    def get_py_string_const(self, text, identifier=None, is_str=False):
-        return self.globalstate.get_py_string_const(text, identifier, is_str).cname
+    def get_py_string_const(self, text, identifier=None,
+                            is_str=False, unicode_value=None):
+        return self.globalstate.get_py_string_const(
+            text, identifier, is_str, unicode_value).cname
 
     def get_argument_default_const(self, type):
         return self.globalstate.get_py_const(type).cname
@@ -1008,7 +1059,7 @@ class CCodeWriter(object):
             self.emit_marker()
         if self.emit_linenums and self.last_marker_line != 0:
             self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
-        
+
         if code:
             if safe:
                 self.put_safe(code)
@@ -1016,7 +1067,7 @@ class CCodeWriter(object):
                 self.put(code)
         self.write("\n");
         self.bol = 1
-    
+
     def emit_marker(self):
         self.write("\n");
         self.indent()
@@ -1054,18 +1105,18 @@ class CCodeWriter(object):
 
     def increase_indent(self):
         self.level = self.level + 1
-    
+
     def decrease_indent(self):
         self.level = self.level - 1
-    
+
     def begin_block(self):
         self.putln("{")
         self.increase_indent()
-    
+
     def end_block(self):
         self.decrease_indent()
         self.putln("}")
-    
+
     def indent(self):
         self.write("  " * self.level)
 
@@ -1089,21 +1140,21 @@ class CCodeWriter(object):
         self.marker = (line, marker)
         if self.emit_linenums:
             self.source_desc = source_desc.get_escaped_description()
-        
+
     def put_label(self, lbl):
         if lbl in self.funcstate.labels_used:
             self.putln("%s:;" % lbl)
-    
+
     def put_goto(self, lbl):
         self.funcstate.use_label(lbl)
         self.putln("goto %s;" % lbl)
-    
+
     def put_var_declarations(self, entries, static = 0, dll_linkage = None,
             definition = True):
         for entry in entries:
             if not entry.in_cinclude:
                 self.put_var_declaration(entry, static, dll_linkage, definition)
-    
+
     def put_var_declaration(self, entry, static = 0, dll_linkage = None,
             definition = True):
         #print "Code.put_var_declaration:", entry.name, "definition =", definition ###
@@ -1146,7 +1197,7 @@ class CCodeWriter(object):
     def put_h_guard(self, guard):
         self.putln("#ifndef %s" % guard)
         self.putln("#define %s" % guard)
-    
+
     def unlikely(self, cond):
         if Options.gcc_branch_hints:
             return 'unlikely(%s)' % cond
@@ -1162,17 +1213,17 @@ class CCodeWriter(object):
             return "(PyObject *)" + entry.cname
         else:
             return entry.cname
-    
+
     def as_pyobject(self, cname, type):
         from PyrexTypes import py_object_type, typecast
         return typecast(py_object_type, type, cname)
-    
+
     def put_gotref(self, cname):
         self.putln("__Pyx_GOTREF(%s);" % cname)
-    
+
     def put_giveref(self, cname):
         self.putln("__Pyx_GIVEREF(%s);" % cname)
-    
+
     def put_xgiveref(self, cname):
         self.putln("__Pyx_XGIVEREF(%s);" % cname)
 
@@ -1184,7 +1235,7 @@ class CCodeWriter(object):
             self.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname, type))
         else:
             self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
-    
+
     def put_decref(self, cname, type, nanny=True):
         if nanny:
             self.putln("__Pyx_DECREF(%s);" % self.as_pyobject(cname, type))
@@ -1194,7 +1245,7 @@ class CCodeWriter(object):
     def put_var_gotref(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_GOTREF(%s);" % self.entry_as_pyobject(entry))
-        
+
     def put_var_giveref(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_GIVEREF(%s);" % self.entry_as_pyobject(entry))
@@ -1210,7 +1261,7 @@ class CCodeWriter(object):
     def put_var_incref(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
-    
+
     def put_decref_clear(self, cname, type, nanny=True):
         from PyrexTypes import py_object_type, typecast
         if nanny:
@@ -1219,13 +1270,13 @@ class CCodeWriter(object):
         else:
             self.putln("Py_DECREF(%s); %s = 0;" % (
                 typecast(py_object_type, type, cname), cname))
-    
+
     def put_xdecref(self, cname, type, nanny=True):
         if nanny:
             self.putln("__Pyx_XDECREF(%s);" % self.as_pyobject(cname, type))
         else:
             self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
-    
+
     def put_xdecref_clear(self, cname, type, nanny=True):
         if nanny:
             self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
@@ -1240,21 +1291,21 @@ class CCodeWriter(object):
                 self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
             else:
                 self.putln("__Pyx_DECREF(%s);" % self.entry_as_pyobject(entry))
-    
+
     def put_var_decref_clear(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_DECREF(%s); %s = 0;" % (
                 self.entry_as_pyobject(entry), entry.cname))
-    
+
     def put_var_xdecref(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
-    
+
     def put_var_xdecref_clear(self, entry):
         if entry.type.is_pyobject:
             self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
                 self.entry_as_pyobject(entry), entry.cname))
-    
+
     def put_var_decrefs(self, entries, used_only = 0):
         for entry in entries:
             if not used_only or entry.used:
@@ -1262,15 +1313,15 @@ class CCodeWriter(object):
                     self.put_var_xdecref(entry)
                 else:
                     self.put_var_decref(entry)
-    
+
     def put_var_xdecrefs(self, entries):
         for entry in entries:
             self.put_var_xdecref(entry)
-    
+
     def put_var_xdecrefs_clear(self, entries):
         for entry in entries:
             self.put_var_xdecref_clear(entry)
-    
+
     def put_init_to_py_none(self, cname, type, nanny=True):
         from PyrexTypes import py_object_type, typecast
         py_none = typecast(type, py_object_type, "Py_None")
@@ -1278,12 +1329,14 @@ class CCodeWriter(object):
             self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none))
         else:
             self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
-    
+
     def put_init_var_to_py_none(self, entry, template = "%s", nanny=True):
         code = template % entry.cname
         #if entry.type.is_extension_type:
         #    code = "((PyObject*)%s)" % code
         self.put_init_to_py_none(code, entry.type, nanny)
+        if entry.in_closure:
+            self.put_giveref('Py_None')
 
     def put_pymethoddef(self, entry, term, allow_skip=True):
         if entry.is_special or entry.name == '__getattribute__':
@@ -1306,7 +1359,7 @@ class CCodeWriter(object):
                 method_flags += [method_coexist]
             self.putln(
                 '{__Pyx_NAMESTR("%s"), (PyCFunction)%s, %s, __Pyx_DOCSTR(%s)}%s' % (
-                    entry.name, 
+                    entry.name,
                     entry.func_cname,
                     "|".join(method_flags),
                     doc_code,
@@ -1319,7 +1372,7 @@ class CCodeWriter(object):
         return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
 
     def set_error_info(self, pos):
-        if Options.c_line_in_traceback:
+        if self.c_line_in_traceback:
             cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
         else:
             cinfo = ""
@@ -1330,7 +1383,7 @@ class CCodeWriter(object):
             Naming.lineno_cname,
             pos[1],
             cinfo)
-        
+
     def error_goto(self, pos):
         lbl = self.funcstate.error_label
         self.funcstate.use_label(lbl)
@@ -1340,19 +1393,22 @@ class CCodeWriter(object):
 
     def error_goto_if(self, cond, pos):
         return "if (%s) %s" % (self.unlikely(cond), self.error_goto(pos))
-            
+
     def error_goto_if_null(self, cname, pos):
         return self.error_goto_if("!%s" % cname, pos)
-    
+
     def error_goto_if_neg(self, cname, pos):
         return self.error_goto_if("%s < 0" % cname, pos)
-    
+
     def error_goto_if_PyErr(self, pos):
         return self.error_goto_if("PyErr_Occurred()", pos)
-    
+
     def lookup_filename(self, filename):
         return self.globalstate.lookup_filename(filename)
 
+    def put_declare_refcount_context(self):
+        self.putln('__Pyx_RefNannyDeclarations')
+
     def put_setup_refcount_context(self, name):
         self.putln('__Pyx_RefNannySetupContext("%s");' % name)
 
@@ -1361,13 +1417,13 @@ class CCodeWriter(object):
 
     def put_trace_declarations(self):
         self.putln('__Pyx_TraceDeclarations');
-    
+
     def put_trace_call(self, name, pos):
         self.putln('__Pyx_TraceCall("%s", %s[%s], %s);' % (name, Naming.filetable_cname, self.lookup_filename(pos[0]), pos[1]));
-    
+
     def put_trace_exception(self):
         self.putln("__Pyx_TraceException();")
-    
+
     def put_trace_return(self, retvalue_cname):
         self.putln("__Pyx_TraceReturn(%s);" % retvalue_cname)
 
@@ -1379,13 +1435,36 @@ class PyrexCodeWriter(object):
     def __init__(self, outfile_name):
         self.f = Utils.open_new_file(outfile_name)
         self.level = 0
-    
+
     def putln(self, code):
         self.f.write("%s%s\n" % (" " * self.level, code))
-    
+
     def indent(self):
         self.level += 1
-    
+
     def dedent(self):
         self.level -= 1
 
+
+class ClosureTempAllocator(object):
+    def __init__(self, klass):
+        self.klass = klass
+        self.temps_allocated = {}
+        self.temps_free = {}
+        self.temps_count = 0
+
+    def reset(self):
+        for type, cnames in self.temps_allocated.items():
+            self.temps_free[type] = list(cnames)
+
+    def allocate_temp(self, type):
+        if not type in self.temps_allocated:
+            self.temps_allocated[type] = []
+            self.temps_free[type] = []
+        elif self.temps_free[type]:
+            return self.temps_free[type].pop(0)
+        cname = '%s%d' % (Naming.codewriter_temp_prefix, self.temps_count)
+        self.klass.declare_var(pos=None, name=cname, cname=cname, type=type, is_cdef=True)
+        self.temps_allocated[type].append(cname)
+        self.temps_count += 1
+        return cname