diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2017-10-03 12:11:47 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2017-10-03 13:23:43 +0200 |
commit | ffdc9b88f9b3515d2b33ed5b95d56b7c09529478 (patch) | |
tree | 292a606ee128571e1d81a7301661ece08c563718 | |
parent | 65c2cd5d9bfb92e649a0ed28a560bc7f5abd9502 (diff) | |
download | cython-ffdc9b88f9b3515d2b33ed5b95d56b7c09529478.tar.gz |
Repair compilation error for nested module level comprehensions.
Closes #1906.
-rw-r--r-- | Cython/Compiler/ExprNodes.py | 14 | ||||
-rw-r--r-- | Cython/Compiler/Symtab.py | 18 | ||||
-rw-r--r-- | tests/run/dictcomp.pyx | 32 |
3 files changed, 56 insertions, 8 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6406d7e43..fead0a4a5 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -7949,8 +7949,7 @@ class ScopedExprNode(ExprNode): generate_inner_evaluation_code(code) # normal (non-error) exit - for entry in py_entries: - code.put_var_xdecref_clear(entry) + self._generate_vars_cleanup(code, py_entries) # error/loop body exit points exit_scope = code.new_label('exit_scope') @@ -7959,8 +7958,7 @@ class ScopedExprNode(ExprNode): list(zip(code.get_loop_labels(), old_loop_labels))): if code.label_used(label): code.put_label(label) - for entry in py_entries: - code.put_var_xdecref_clear(entry) + self._generate_vars_cleanup(code, py_entries) code.put_goto(old_label) code.put_label(exit_scope) code.putln('} /* exit inner scope */') @@ -7968,6 +7966,14 @@ class ScopedExprNode(ExprNode): code.set_loop_labels(old_loop_labels) code.error_label = old_error_label + def _generate_vars_cleanup(self, code, py_entries): + for entry in py_entries: + if entry.is_cglobal: + code.put_var_gotref(entry) + code.put_decref_set(entry.cname, "Py_None") + else: + code.put_var_xdecref_clear(entry) + class ComprehensionNode(ScopedExprNode): # A list/set/dict comprehension diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 32e10b554..ab43c7057 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1704,7 +1704,10 @@ class LocalScope(Scope): # Return None if not found. entry = Scope.lookup(self, name) if entry is not None: - if entry.scope is not self and entry.scope.is_closure_scope: + entry_scope = entry.scope + while entry_scope.is_genexpr_scope: + entry_scope = entry_scope.outer_scope + if entry_scope is not self and entry_scope.is_closure_scope: if hasattr(entry.scope, "scope_class"): raise InternalError("lookup() after scope class created.") # The actual c fragment for the different scopes differs @@ -1740,8 +1743,12 @@ class GeneratorExpressionScope(Scope): is_genexpr_scope = True def __init__(self, outer_scope): - name = outer_scope.global_scope().next_id(Naming.genexpr_id_ref) - Scope.__init__(self, name, outer_scope, outer_scope) + parent_scope = outer_scope + # TODO: also ignore class scopes? + while parent_scope.is_genexpr_scope: + parent_scope = parent_scope.parent_scope + name = parent_scope.global_scope().next_id(Naming.genexpr_id_ref) + Scope.__init__(self, name, outer_scope, parent_scope) self.directives = outer_scope.directives self.genexp_prefix = "%s%d%s" % (Naming.pyrex_prefix, len(name), name) @@ -1768,7 +1775,10 @@ class GeneratorExpressionScope(Scope): cname = '%s%s' % (self.genexp_prefix, self.parent_scope.mangle(Naming.var_prefix, name or self.next_id())) entry = self.declare(name, cname, type, pos, visibility) entry.is_variable = True - entry.is_local = True + if self.parent_scope.is_module_scope: + entry.is_cglobal = True + else: + entry.is_local = True entry.in_subscope = True self.var_entries.append(entry) self.entries[name] = entry diff --git a/tests/run/dictcomp.pyx b/tests/run/dictcomp.pyx index 731020f0b..e0137fe6f 100644 --- a/tests/run/dictcomp.pyx +++ b/tests/run/dictcomp.pyx @@ -57,3 +57,35 @@ def sorted(it): l = list(it) l.sort() return l + + +# Copied from sre_compile.py in CPython 3.7. Previously failed to detect variable initialisation. +_equivalences = ( + # LATIN SMALL LETTER I, LATIN SMALL LETTER DOTLESS I + (0x69, 0x131), # iı + # LATIN SMALL LETTER S, LATIN SMALL LETTER LONG S + (0x73, 0x17f), # sſ + # MICRO SIGN, GREEK SMALL LETTER MU + (0xb5, 0x3bc), # µμ + # COMBINING GREEK YPOGEGRAMMENI, GREEK SMALL LETTER IOTA, GREEK PROSGEGRAMMENI + (0x345, 0x3b9, 0x1fbe), # \u0345ιι + # ... +) + +_ignorecase_fixes = { + i: tuple(j for j in t if i != j) + for t in _equivalences for i in t +} + +def nested_tuple(): + """ + >>> modlevel, funclevel = nested_tuple() + >>> modlevel == funclevel or (modlevel, funclevel) + True + """ + inner_ignorecase_fixes = { + i: tuple(j for j in t if i != j) + for t in _equivalences for i in t + } + + return sorted(_ignorecase_fixes.items()), sorted(inner_ignorecase_fixes.items()) |