Commit 774b88c6 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge pull request #200 from nnemkin/module_level_literal_arrays

Allow module-level literal lists (Fixes #113).
parents 16cb9327 71d13da9
...@@ -484,7 +484,7 @@ class FunctionState(object): ...@@ -484,7 +484,7 @@ class FunctionState(object):
self.in_try_finally = 0 self.in_try_finally = 0
self.exc_vars = None 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_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
self.temps_used_type = {} # name -> (type, manage_ref) self.temps_used_type = {} # name -> (type, manage_ref)
self.temp_counter = 0 self.temp_counter = 0
...@@ -563,7 +563,7 @@ class FunctionState(object): ...@@ -563,7 +563,7 @@ class FunctionState(object):
# temp handling # 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 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 allocated and released one of the same type). Type is simply registered
...@@ -578,6 +578,10 @@ class FunctionState(object): ...@@ -578,6 +578,10 @@ class FunctionState(object):
still has to be passed. It is recommended to pass False by convention 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. 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. A C string referring to the variable is returned.
""" """
if type.is_const: if type.is_const:
...@@ -595,7 +599,7 @@ class FunctionState(object): ...@@ -595,7 +599,7 @@ class FunctionState(object):
self.temp_counter += 1 self.temp_counter += 1
result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter) result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
if not result in self.names_taken: break 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) self.temps_used_type[result] = (type, manage_ref)
if DebugFlags.debug_temp_code_comments: if DebugFlags.debug_temp_code_comments:
self.owner.putln("/* %s allocated */" % result) self.owner.putln("/* %s allocated */" % result)
...@@ -626,7 +630,7 @@ class FunctionState(object): ...@@ -626,7 +630,7 @@ class FunctionState(object):
that are currently in use. that are currently in use.
""" """
used = [] 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)) freelist = self.temps_free.get((type, manage_ref))
if freelist is None or name not in freelist: if freelist is None or name not in freelist:
used.append((name, type, manage_ref and type.is_pyobject)) used.append((name, type, manage_ref and type.is_pyobject))
...@@ -645,7 +649,7 @@ class FunctionState(object): ...@@ -645,7 +649,7 @@ class FunctionState(object):
"""Return a list of (cname, type) tuples of refcount-managed Python objects. """Return a list of (cname, type) tuples of refcount-managed Python objects.
""" """
return [(cname, type) 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] if manage_ref]
def all_free_managed_temps(self): def all_free_managed_temps(self):
...@@ -1604,7 +1608,7 @@ class CCodeWriter(object): ...@@ -1604,7 +1608,7 @@ class CCodeWriter(object):
self.putln(";") self.putln(";")
def put_temp_declarations(self, func_context): 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) decl = type.declaration_code(name)
if type.is_pyobject: if type.is_pyobject:
self.putln("%s = NULL;" % decl) self.putln("%s = NULL;" % decl)
...@@ -1612,7 +1616,7 @@ class CCodeWriter(object): ...@@ -1612,7 +1616,7 @@ class CCodeWriter(object):
import MemoryView import MemoryView
self.putln("%s = %s;" % (decl, MemoryView.memslice_entry_init)) self.putln("%s = %s;" % (decl, MemoryView.memslice_entry_init))
else: else:
self.putln("%s;" % decl) self.putln("%s%s;" % (static and "static " or "", decl))
def put_h_guard(self, guard): def put_h_guard(self, guard):
self.putln("#ifndef %s" % guard) self.putln("#ifndef %s" % guard)
......
...@@ -1815,7 +1815,7 @@ class NameNode(AtomicExprNode): ...@@ -1815,7 +1815,7 @@ class NameNode(AtomicExprNode):
return # There was an error earlier return # There was an error earlier
if (self.entry.type.is_ptr and isinstance(rhs, ListNode) 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") 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. # is_pyglobal seems to be True for module level-globals only.
...@@ -5914,6 +5914,7 @@ class ListNode(SequenceNode): ...@@ -5914,6 +5914,7 @@ class ListNode(SequenceNode):
obj_conversion_errors = [] obj_conversion_errors = []
type = list_type type = list_type
in_module_scope = False
gil_message = "Constructing Python list" gil_message = "Constructing Python list"
...@@ -5934,6 +5935,8 @@ class ListNode(SequenceNode): ...@@ -5934,6 +5935,8 @@ class ListNode(SequenceNode):
node = SequenceNode.analyse_types(self, env) node = SequenceNode.analyse_types(self, env)
node.obj_conversion_errors = held_errors() node.obj_conversion_errors = held_errors()
release_errors(ignore=True) release_errors(ignore=True)
if env.is_module_scope:
self.in_module_scope = True
return node return node
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
...@@ -5975,6 +5978,13 @@ class ListNode(SequenceNode): ...@@ -5975,6 +5978,13 @@ class ListNode(SequenceNode):
t.constant_result = tuple(self.constant_result) t.constant_result = tuple(self.constant_result)
return t 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): def release_temp_result(self, env):
if self.type.is_array: if self.type.is_array:
# To be valid C++, we must allocate the memory on the stack # To be valid C++, we must allocate the memory on the stack
......
...@@ -55,6 +55,26 @@ def test_struct(int x, y): ...@@ -55,6 +55,26 @@ def test_struct(int x, y):
print_struct(aa[0]) print_struct(aa[0])
print_struct([1, 2, <double**>1]) 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. # Make sure it's still naturally an object.
[0,1,2,3].append(4) [0,1,2,3].append(4)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment