Commit 8caa6c6a authored by Stefan Behnel's avatar Stefan Behnel

refactor scope handling in ScopedExprNode, enable type inference for inlined...

refactor scope handling in ScopedExprNode, enable type inference for inlined genexpr nodes, fix type coercion of sum(genexpr)
parent bb3dfdb4
...@@ -4135,46 +4135,49 @@ class ScopedExprNode(ExprNode): ...@@ -4135,46 +4135,49 @@ class ScopedExprNode(ExprNode):
subexprs = [] subexprs = []
expr_scope = None expr_scope = None
def analyse_types(self, env): # does this node really have a local scope, e.g. does it leak loop
# nothing to do here, the children will be analysed separately # variables or not? non-leaking Py3 behaviour is default, except
# for list comprehensions where the behaviour differs in Py2 and
# Py3 (set in Parsing.py based on parser context)
has_local_scope = True
def init_scope(self, outer_scope, expr_scope=None):
if expr_scope is not None:
self.expr_scope = expr_scope
elif self.has_local_scope:
self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
else:
self.expr_scope = None
def analyse_declarations(self, env):
self.init_scope(env)
def analyse_scoped_declarations(self, env):
# this is called with the expr_scope as env
pass pass
def analyse_expressions(self, env): def analyse_types(self, env):
# nothing to do here, the children will be analysed separately # no recursion here, the children will be analysed separately below
pass pass
def analyse_scoped_expressions(self, env): def analyse_scoped_expressions(self, env):
# this is called with the expr_scope as env # this is called with the expr_scope as env
pass pass
def init_scope(self, outer_scope, expr_scope=None):
self.expr_scope = expr_scope
class ComprehensionNode(ScopedExprNode): class ComprehensionNode(ScopedExprNode):
subexprs = ["target"] subexprs = ["target"]
child_attrs = ["loop", "append"] child_attrs = ["loop", "append"]
# leak loop variables or not? non-leaking Py3 behaviour is
# default, except for list comprehensions where the behaviour
# differs in Py2 and Py3 (see Parsing.py)
has_local_scope = True
def infer_type(self, env): def infer_type(self, env):
return self.target.infer_type(env) return self.target.infer_type(env)
def analyse_declarations(self, env): def analyse_declarations(self, env):
self.append.target = self # this is used in the PyList_Append of the inner loop self.append.target = self # this is used in the PyList_Append of the inner loop
self.init_scope(env) self.init_scope(env)
self.loop.analyse_declarations(self.expr_scope or env)
def init_scope(self, outer_scope, expr_scope=None): def analyse_scoped_declarations(self, env):
if expr_scope is not None: self.loop.analyse_declarations(env)
self.expr_scope = expr_scope
elif self.has_local_scope:
self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
else:
self.expr_scope = None
def analyse_types(self, env): def analyse_types(self, env):
self.target.analyse_expressions(env) self.target.analyse_expressions(env)
...@@ -4182,9 +4185,6 @@ class ComprehensionNode(ScopedExprNode): ...@@ -4182,9 +4185,6 @@ class ComprehensionNode(ScopedExprNode):
if not self.has_local_scope: if not self.has_local_scope:
self.loop.analyse_expressions(env) self.loop.analyse_expressions(env)
def analyse_expressions(self, env):
self.analyse_types(env)
def analyse_scoped_expressions(self, env): def analyse_scoped_expressions(self, env):
if self.has_local_scope: if self.has_local_scope:
self.loop.analyse_expressions(env) self.loop.analyse_expressions(env)
...@@ -4286,21 +4286,17 @@ class GeneratorExpressionNode(ScopedExprNode): ...@@ -4286,21 +4286,17 @@ class GeneratorExpressionNode(ScopedExprNode):
type = py_object_type type = py_object_type
def analyse_declarations(self, env): def analyse_scoped_declarations(self, env):
self.init_scope(env) self.loop.analyse_declarations(env)
self.loop.analyse_declarations(self.expr_scope)
def init_scope(self, outer_scope, expr_scope=None):
if expr_scope is not None:
self.expr_scope = expr_scope
else:
self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
def analyse_types(self, env): def analyse_types(self, env):
if not self.has_local_scope:
self.loop.analyse_expressions(env)
self.is_temp = True self.is_temp = True
def analyse_scoped_expressions(self, env): def analyse_scoped_expressions(self, env):
self.loop.analyse_expressions(env) if self.has_local_scope:
self.loop.analyse_expressions(env)
def may_be_none(self): def may_be_none(self):
return False return False
...@@ -4320,14 +4316,29 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode): ...@@ -4320,14 +4316,29 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
# orig_func String the name of the builtin function this node replaces # orig_func String the name of the builtin function this node replaces
child_attrs = ["loop"] child_attrs = ["loop"]
loop_analysed = False
def infer_type(self, env):
return self.result_node.infer_type(env)
def analyse_types(self, env): def analyse_types(self, env):
if not self.has_local_scope:
self.loop_analysed = True
self.loop.analyse_expressions(env)
self.type = self.result_node.type self.type = self.result_node.type
self.is_temp = True self.is_temp = True
def analyse_scoped_expressions(self, env):
self.loop_analysed = True
GeneratorExpressionNode.analyse_scoped_expressions(self, env)
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if self.orig_func == 'sum' and dst_type.is_numeric: if self.orig_func == 'sum' and dst_type.is_numeric and not self.loop_analysed:
# we can optimise by dropping the aggregation variable into C # We can optimise by dropping the aggregation variable and
# the add operations into C. This can only be done safely
# before analysing the loop body, after that, the result
# reference type will have infected expressions and
# assignments.
self.result_node.type = self.type = dst_type self.result_node.type = self.type = dst_type
return self return self
return GeneratorExpressionNode.coerce_to(self, dst_type, env) return GeneratorExpressionNode.coerce_to(self, dst_type, env)
......
...@@ -1073,15 +1073,18 @@ property NAME: ...@@ -1073,15 +1073,18 @@ property NAME:
return node return node
def visit_ScopedExprNode(self, node): def visit_ScopedExprNode(self, node):
node.analyse_declarations(self.env_stack[-1]) env = self.env_stack[-1]
node.analyse_declarations(env)
# the node may or may not have a local scope # the node may or may not have a local scope
if node.expr_scope: if node.has_local_scope:
self.seen_vars_stack.append(cython.set(self.seen_vars_stack[-1])) self.seen_vars_stack.append(cython.set(self.seen_vars_stack[-1]))
self.env_stack.append(node.expr_scope) self.env_stack.append(node.expr_scope)
node.analyse_scoped_declarations(node.expr_scope)
self.visitchildren(node) self.visitchildren(node)
self.env_stack.pop() self.env_stack.pop()
self.seen_vars_stack.pop() self.seen_vars_stack.pop()
else: else:
node.analyse_scoped_declarations(env)
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -1177,7 +1180,7 @@ class AnalyseExpressionsTransform(CythonTransform): ...@@ -1177,7 +1180,7 @@ class AnalyseExpressionsTransform(CythonTransform):
return node return node
def visit_ScopedExprNode(self, node): def visit_ScopedExprNode(self, node):
if node.expr_scope is not None: if node.has_local_scope:
node.expr_scope.infer_types() node.expr_scope.infer_types()
node.analyse_scoped_expressions(node.expr_scope) node.analyse_scoped_expressions(node.expr_scope)
self.visitchildren(node) self.visitchildren(node)
......
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