Skip to content

Commit

Permalink
Correctly parse and handle annotated statement
Browse files Browse the repository at this point in the history
We currently don't handle the actual annotation, the annotated statement
is processed as a regular statement.
  • Loading branch information
serge-sans-paille committed Oct 17, 2024
1 parent 0bf7f7f commit b266216
Show file tree
Hide file tree
Showing 29 changed files with 247 additions and 73 deletions.
7 changes: 5 additions & 2 deletions pythran/analyses/aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,9 @@ def visit_Assign(self, node):
Everyone points to the formal parameter 'a' \o/
'''
md.visit(self, node)
value_aliases = self.visit(node.value)
for t in node.targets:
value_aliases = self.visit(node.value) if node.value else {}
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for t in targets:
if isinstance(t, ast.Name):
self.aliases[t.id] = set(value_aliases) or {t}
for alias in list(value_aliases):
Expand All @@ -663,6 +664,8 @@ def visit_Assign(self, node):
else:
self.visit(t)

visit_AnnAssign = visit_Assign

def visit_For(self, node):
'''
For loop creates aliasing between the target
Expand Down
5 changes: 4 additions & 1 deletion pythran/analyses/argument_effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,16 @@ def visit_AugAssign(self, node):
self.generic_visit(node)

def visit_Assign(self, node):
for t in node.targets:
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for t in targets:
if isinstance(t, ast.Subscript):
n = self.argument_index(t)
if n >= 0:
self.current_function.update_effects[n] = True
self.generic_visit(node)

visit_AnnAssign = visit_Assign

def visit_Call(self, node):
for i, arg in enumerate(node.args):
n = self.argument_index(arg)
Expand Down
5 changes: 4 additions & 1 deletion pythran/analyses/argument_read_once.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,13 @@ def visit_Return(self, node):

def visit_Assign(self, node):
dep = self.generic_visit(node)
local = [self.local_effect(t, 2) for t in node.targets
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
local = [self.local_effect(t, 2) for t in targets
if isinstance(t, ast.Subscript)]
return lambda ctx: dep(ctx) + sum(l(ctx) for l in local)

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
dep = self.generic_visit(node)
local = self.local_effect(node.target, 2)
Expand Down
2 changes: 1 addition & 1 deletion pythran/analyses/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def visit_Pass(self, node):
return (node,), ()

# All these nodes have the same behavior as pass
visit_Assign = visit_AugAssign = visit_Import = visit_Pass
visit_Assign = visit_AnnAssign = visit_AugAssign = visit_Import = visit_Pass
visit_Expr = visit_Print = visit_ImportFrom = visit_Pass
visit_Yield = visit_Delete = visit_Pass

Expand Down
6 changes: 6 additions & 0 deletions pythran/analyses/constant_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,9 @@ def visit_Slice(self, node):

def visit_Index(self, node):
return self.visit(node.value) and self.add(node)

def visit_AnnAssign(self, node):
self.visit(node.target)
# skip annotation
if node.value:
self.visit(node.value)
7 changes: 6 additions & 1 deletion pythran/analyses/fixed_size_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ def is_safe_use(self, use):

def visit_Assign(self, node):
self.generic_visit(node)
if not node.value:
return
if not self.is_fixed_size_list_def(node.value):
return

for target in node.targets:
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for target in targets:
def_ = self.def_use_chains.chains[target]
if any(not self.is_safe_use(u) for u in def_.users()):
break
Expand All @@ -72,6 +75,8 @@ def visit_Assign(self, node):
else:
self.result.add(node.value)

visit_AnnAssign = visit_Assign

def visit_Call(self, node):
self.generic_visit(node)
for i, arg in enumerate(node.args):
Expand Down
8 changes: 6 additions & 2 deletions pythran/analyses/imported_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,14 @@ def visit_Assign(self, node):
# order matter as an assignation
# is evaluated before being assigned
md.visit(self, node)
self.visit(node.value)
for target in node.targets:
if node.value:
self.visit(node.value)
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for target in targets:
self.visit(target)

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
self.in_augassign = True
self.generic_visit(node)
Expand Down
12 changes: 9 additions & 3 deletions pythran/analyses/lazyness_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,13 @@ def visit_FunctionDef(self, node):

def visit_Assign(self, node):
md.visit(self, node)
self.visit(node.value)
ids = self.gather(Identifiers, node.value)
for target in node.targets:
if node.value:
self.visit(node.value)
ids = self.gather(Identifiers, node.value)
else:
ids = set()
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for target in targets:
if isinstance(target, ast.Name):
self.assign_to(target, ids)
if node.value not in self.pure_expressions:
Expand All @@ -177,6 +181,8 @@ def visit_Assign(self, node):
else:
raise PythranSyntaxError("Assign to unknown node", node)

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
md.visit(self, node)
# augassigned variable can't be lazy
Expand Down
4 changes: 3 additions & 1 deletion pythran/analyses/literals.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def visit_Assign(self, node):
# a constructor which may be costly and they can be updated using
# function call
if isinstance(node.value, (ast.Constant, ast.Lambda)):
targets = [target for target in node.targets
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
targets = [target for target in targets
if isinstance(target, ast.Name)]
self.result.update(targets)
visit_AnnAssign = visit_Assign
12 changes: 9 additions & 3 deletions pythran/analyses/locals_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,18 @@ def visit_Assign(self, node):
self.expr_parent = node
self.result[node] = self.locals.copy()
md.visit(self, node)
self.visit(node.value)
self.locals.update(t.id for t in node.targets
if node.value:
self.visit(node.value)
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
self.locals.update(t.id for t in targets
if isinstance(t, ast.Name))
for target in node.targets:
for target in targets:
self.visit(target)

def visit_AnnAssign(self, node):
self.visit_Assign(node)
self.visit(node.annotation)

def visit_For(self, node):
self.expr_parent = node
self.result[node] = self.locals.copy()
Expand Down
7 changes: 6 additions & 1 deletion pythran/analyses/range_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,14 +579,19 @@ def visit_Assign(self, node):
>>> res['b']
Interval(low=2, high=2)
"""
if not node.value:
return
assigned_range = self.visit(node.value)
for target in node.targets:
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for target in targets:
if isinstance(target, ast.Name):
# Make sure all Interval doesn't alias for multiple variables.
self.add(target.id, assigned_range)
else:
self.visit(target)

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
""" Update range value for augassigned variables.
Expand Down
36 changes: 23 additions & 13 deletions pythran/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,34 +408,37 @@ def visit_Assign(self, node):
Finally, process OpenMP clause like #pragma omp atomic
"""
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
if not all(isinstance(n, (ast.Name, ast.Subscript))
for n in node.targets):
for n in targets):
raise PythranSyntaxError(
"Must assign to an identifier or a subscript",
node)
if not node.value:
return self.visit_Pass(node)
value = self.visit(node.value)
targets = [self.visit(t) for t in node.targets]
alltargets = "= ".join(targets)
islocal = (len(targets) == 1 and
isinstance(node.targets[0], ast.Name) and
node.targets[0].id in self.scope[node] and
node.targets[0].id not in self.openmp_deps)
stargets = [self.visit(t) for t in targets]
alltargets = "= ".join(stargets)
islocal = (len(stargets) == 1 and
isinstance(targets[0], ast.Name) and
targets[0].id in self.scope[node] and
targets[0].id not in self.openmp_deps)
if islocal:
# remove this decls from local decls
self.ldecls.difference_update(t.id for t in node.targets)
self.ldecls.difference_update(t.id for t in targets)
# add a local declaration
if self.types[node.targets[0]].iscombined():
alltargets = '{} {}'.format(self.typeof(node.targets[0]),
if self.types[targets[0]].iscombined():
alltargets = '{} {}'.format(self.typeof(targets[0]),
alltargets)
elif isinstance(self.types[node.targets[0]],
elif isinstance(self.types[targets[0]],
self.types.builder.Assignable):
alltargets = '{} {}'.format(
self.types.builder.AssignableNoEscape(
self.types.builder.NamedType(
'decltype({})'.format(value))).sgenerate(),
alltargets)
else:
assert isinstance(self.types[node.targets[0]],
assert isinstance(self.types[targets[0]],
self.types.builder.Lazy)
alltargets = '{} {}'.format(
self.types.builder.Lazy(
Expand All @@ -445,6 +448,8 @@ def visit_Assign(self, node):
stmt = Assign(alltargets, value)
return self.process_omp_attachements(node, stmt)

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
value = self.visit(node.value)
target = self.visit(node.target)
Expand Down Expand Up @@ -1325,12 +1330,17 @@ def visit_Yield(self, node):
]).generate())

def visit_Assign(self, node):
if not node.value:
return self.visit_Pass(node)
value = self.visit(node.value)
targets = [self.visit(t) for t in node.targets]
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
targets = [self.visit(t) for t in targets]
alltargets = "= ".join(targets)
stmt = Assign(alltargets, value)
return self.process_omp_attachements(node, stmt)

visit_AnnAssign = visit_Assign

def can_use_autofor(self, node):
"""
TODO : Yield should block only if it is use in the for loop, not in the
Expand Down
7 changes: 6 additions & 1 deletion pythran/optimizations/constant_folding.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,20 @@ def visit_Delete(self, node):
self.locals.pop(node.id)

def visit_Assign(self, node):
if not node.value:
return
value = self.visit(node.value)
for target in node.targets:
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
for target in targets:
if isinstance(target, ast.Name):
self.locals[target.id] = value
elif isinstance(target, ast.Subscript):
self.visit(target.value)[self.visit(target.slice)] = value
else:
raise NotImplementedError("assign")

visit_AnnAssign = visit_Assign

def visit_AugAssign(self, node):
value = self.visit(node.value)
ty = type(node.op)
Expand Down
8 changes: 6 additions & 2 deletions pythran/optimizations/copyto.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ def visit_Module(self, node):
return node

def visit_Assign(self, node):
if len(node.targets) != 1:
if not node.value:
return node
target, = node.targets
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
if len(targets) != 1:
return node
target, = targets
if not self.is_fully_sliced(target):
return node
if self.is_fully_sliced(node.value):
Expand All @@ -87,5 +90,6 @@ def visit_Assign(self, node):
[target.value, value],
[])
)
visit_AnnAssign = visit_Assign


13 changes: 12 additions & 1 deletion pythran/optimizations/dead_code_elimination.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,26 @@ def visit_Assign(self, node):
if self.used_target(target)]
if len(targets) == len(node.targets):
return node
node.targets = targets
self.update = True
if targets:
node.targets = targets
return node
if node.value in self.pure_expressions:
return ast.Pass()
else:
return ast.Expr(value=node.value)

def visit_AnnAssign(self, node):
if not node.value:
return node
if self.used_target(node.target):
return node
self.update = True
if node.value in self.pure_expressions:
return ast.Pass()
else:
return ast.Expr(value=node.value)

def visit_Expr(self, node):
if (node.value in self.pure_expressions and
not isinstance(node.value, ast.Yield)):
Expand Down
9 changes: 7 additions & 2 deletions pythran/optimizations/fast_gexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ def may_alias(self, gexpr, value):


def visit_Assign(self, node):
if len(node.targets) > 1:
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
if len(targets) > 1:
return node

target, = node.targets
if not node.value:
return node

target, = targets
value = node.value
gexpr = self.as_gexpr(target)
if not gexpr:
Expand All @@ -65,4 +69,5 @@ def visit_Assign(self, node):
attr="pythran", ctx=ast.Load()),
attr="restrict_assign", ctx=ast.Load())
return ast.Expr(ast.Call(func, args=[target, value], keywords=[]))
visit_AnnAssign = visit_Assign

9 changes: 9 additions & 0 deletions pythran/optimizations/forward_substitution.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ def visit_Assign(self, node):
return ast.Pass()
return node

def visit_AnnAssign(self, node):
if node in self.nodes:
to_prune = self.nodes[node]
if node.target in to_prune:
return ast.Pass()
else:
return node
return node


class ForwardSubstitution(Transformation):

Expand Down
1 change: 1 addition & 0 deletions pythran/optimizations/inlining.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def visit_Stmt(self, node):

visit_Return = visit_Stmt
visit_Assign = visit_Stmt
visit_AnnAssign = visit_Stmt
visit_AugAssign = visit_Stmt
visit_Print = visit_Stmt
visit_For = visit_Stmt
Expand Down
2 changes: 2 additions & 0 deletions pythran/optimizations/list_to_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ def foo(n):
node.value = self.convert(node.value)
return node

visit_AnnAssign = visit_Assign

def convert(self, node):
self.update = True

Expand Down
Loading

0 comments on commit b266216

Please sign in to comment.