Allow module-level literal lists (Fixes #113).
authorNikita Nemkin <nikita@nemkin.ru>
Thu, 28 Mar 2013 11:11:10 +0000 (17:11 +0600)
committerNikita Nemkin <nikita@nemkin.ru>
Thu, 28 Mar 2013 12:26:04 +0000 (18:26 +0600)
Cython/Compiler/Code.py
Cython/Compiler/ExprNodes.py
tests/run/literal_lists.pyx

index 94b1246..807e6e0 100644 (file)
@@ -484,7 +484,7 @@ class FunctionState(object):
         self.in_try_finally = 0
         self.exc_vars = None
 
-        self.temps_allocated = [] # of (name, type, manage_ref)
+        self.temps_allocated = [] # of (name, type, manage_ref, static)
         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
@@ -563,7 +563,7 @@ class FunctionState(object):
 
     # temp handling
 
-    def allocate_temp(self, type, manage_ref):
+    def allocate_temp(self, type, manage_ref, static=False):
         """
         Allocates a temporary (which may create a new one or get a previously
         allocated and released one of the same type). Type is simply registered
@@ -578,6 +578,10 @@ class FunctionState(object):
         still has to be passed. It is recommended to pass False by convention
         if it is known that type will never be a Python object.
 
+        static=True marks the temporary declaration with "static".
+        This is only used when allocating backing store for a module-level
+        C array literals.
+
         A C string referring to the variable is returned.
         """
         if type.is_const:
@@ -595,7 +599,7 @@ class FunctionState(object):
                 self.temp_counter += 1
                 result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
                 if not result in self.names_taken: break
-            self.temps_allocated.append((result, type, manage_ref))
+            self.temps_allocated.append((result, type, manage_ref, static))
         self.temps_used_type[result] = (type, manage_ref)
         if DebugFlags.debug_temp_code_comments:
             self.owner.putln("/* %s allocated */" % result)
@@ -626,7 +630,7 @@ class FunctionState(object):
         that are currently in use.
         """
         used = []
-        for name, type, manage_ref in self.temps_allocated:
+        for name, type, manage_ref, static in self.temps_allocated:
             freelist = self.temps_free.get((type, manage_ref))
             if freelist is None or name not in freelist:
                 used.append((name, type, manage_ref and type.is_pyobject))
@@ -645,7 +649,7 @@ class FunctionState(object):
         """Return a list of (cname, type) tuples of refcount-managed Python objects.
         """
         return [(cname, type)
-                    for cname, type, manage_ref in self.temps_allocated
+                    for cname, type, manage_ref, static in self.temps_allocated
                         if manage_ref]
 
     def all_free_managed_temps(self):
@@ -1604,7 +1608,7 @@ class CCodeWriter(object):
         self.putln(";")
 
     def put_temp_declarations(self, func_context):
-        for name, type, manage_ref in func_context.temps_allocated:
+        for name, type, manage_ref, static in func_context.temps_allocated:
             decl = type.declaration_code(name)
             if type.is_pyobject:
                 self.putln("%s = NULL;" % decl)
@@ -1612,7 +1616,7 @@ class CCodeWriter(object):
                 import MemoryView
                 self.putln("%s = %s;" % (decl, MemoryView.memslice_entry_init))
             else:
-                self.putln("%s;" % decl)
+                self.putln("%s%s;" % (static and "static " or "", decl))
 
     def put_h_guard(self, guard):
         self.putln("#ifndef %s" % guard)
index ec4d52b..afffb1d 100755 (executable)
@@ -1815,7 +1815,7 @@ class NameNode(AtomicExprNode):
             return # There was an error earlier
 
         if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
-            and not self.lhs_of_first_assignment):
+            and not self.lhs_of_first_assignment and not rhs.in_module_scope):
             error(self.pos, "Literal list must be assigned to pointer at time of declaration")
 
         # is_pyglobal seems to be True for module level-globals only.
@@ -5914,6 +5914,7 @@ class ListNode(SequenceNode):
 
     obj_conversion_errors = []
     type = list_type
+    in_module_scope = False
 
     gil_message = "Constructing Python list"
 
@@ -5934,6 +5935,8 @@ class ListNode(SequenceNode):
         node = SequenceNode.analyse_types(self, env)
         node.obj_conversion_errors = held_errors()
         release_errors(ignore=True)
+        if env.is_module_scope:
+            self.in_module_scope = True
         return node
 
     def coerce_to(self, dst_type, env):
@@ -5975,6 +5978,13 @@ class ListNode(SequenceNode):
             t.constant_result = tuple(self.constant_result)
         return t
 
+    def allocate_temp_result(self, code):
+        if self.type.is_array and self.in_module_scope:
+            self.temp_code = code.funcstate.allocate_temp(
+                self.type, manage_ref=False, static=True)
+        else:
+            SequenceNode.allocate_temp_result(self, code)
+
     def release_temp_result(self, env):
         if self.type.is_array:
             # To be valid C++, we must allocate the memory on the stack
index 35fd74f..57942ef 100644 (file)
@@ -55,6 +55,26 @@ def test_struct(int x, y):
     print_struct(aa[0])
     print_struct([1, 2, <double**>1])
 
+cdef int m_int = -1
+cdef int* m_iarray = [4, m_int]
+cdef int** m_piarray = [m_iarray, &m_int]
+cdef char** m_carray = [b"a", b"bc"]
+cdef MyStruct* m_structarray = [[m_int,0,NULL], [1,m_int+1,NULL]]
+
+def test_module_level():
+    """
+    >>> test_module_level()
+    4 -1
+    4 -1
+    True True
+    1 0 True
+    """
+    print m_iarray[0], m_iarray[1]
+    print m_piarray[0][0], m_piarray[1][0]
+    print m_carray[0] == b"a", m_carray[1] == b"bc"
+    print_struct(m_structarray[1])
+
+
 # Make sure it's still naturally an object.
 
 [0,1,2,3].append(4)