summaryrefslogtreecommitdiffstats
path: root/contrib/python/pythran
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-06-07 08:40:18 +0300
committerrobot-piglet <[email protected]>2025-06-07 08:51:32 +0300
commit0d8efcced9baa2783ed347bd277d427f4856f2cd (patch)
tree4c34c507a4f9799b69cb173b614ec557c786cf6c /contrib/python/pythran
parent4090cb78bac6232cb38fb8dde597d315070fca46 (diff)
Intermediate changes
commit_hash:929b636025dc9f709f1ff57bfa609d3504adba19
Diffstat (limited to 'contrib/python/pythran')
-rw-r--r--contrib/python/pythran/.dist-info/METADATA35
-rw-r--r--contrib/python/pythran/README.rst2
-rw-r--r--contrib/python/pythran/pythran/analyses/__init__.py2
-rw-r--r--contrib/python/pythran/pythran/analyses/aliases.py44
-rw-r--r--contrib/python/pythran/pythran/analyses/ancestors.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/argument_effects.py61
-rw-r--r--contrib/python/pythran/pythran/analyses/argument_read_once.py105
-rw-r--r--contrib/python/pythran/pythran/analyses/cfg.py5
-rw-r--r--contrib/python/pythran/pythran/analyses/constant_expressions.py7
-rw-r--r--contrib/python/pythran/pythran/analyses/dependencies.py4
-rw-r--r--contrib/python/pythran/pythran/analyses/extended_syntax_check.py7
-rw-r--r--contrib/python/pythran/pythran/analyses/fixed_size_list.py12
-rw-r--r--contrib/python/pythran/pythran/analyses/global_declarations.py61
-rw-r--r--contrib/python/pythran/pythran/analyses/global_effects.py41
-rw-r--r--contrib/python/pythran/pythran/analyses/globals_analysis.py7
-rw-r--r--contrib/python/pythran/pythran/analyses/has_return.py12
-rw-r--r--contrib/python/pythran/pythran/analyses/identifiers.py4
-rw-r--r--contrib/python/pythran/pythran/analyses/immediates.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/imported_ids.py43
-rw-r--r--contrib/python/pythran/pythran/analyses/inlinable.py8
-rw-r--r--contrib/python/pythran/pythran/analyses/intrinsics.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/is_assigned.py5
-rw-r--r--contrib/python/pythran/pythran/analyses/lazyness_analysis.py25
-rw-r--r--contrib/python/pythran/pythran/analyses/literals.py4
-rw-r--r--contrib/python/pythran/pythran/analyses/local_declarations.py11
-rw-r--r--contrib/python/pythran/pythran/analyses/locals_analysis.py5
-rw-r--r--contrib/python/pythran/pythran/analyses/node_count.py4
-rw-r--r--contrib/python/pythran/pythran/analyses/optimizable_comprehension.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/ordered_global_declarations.py20
-rw-r--r--contrib/python/pythran/pythran/analyses/parallel_maps.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/potential_iterator.py6
-rw-r--r--contrib/python/pythran/pythran/analyses/pure_expressions.py7
-rw-r--r--contrib/python/pythran/pythran/analyses/pure_functions.py7
-rw-r--r--contrib/python/pythran/pythran/analyses/range_values.py8
-rw-r--r--contrib/python/pythran/pythran/analyses/scope.py25
-rw-r--r--contrib/python/pythran/pythran/analyses/static_expressions.py5
-rw-r--r--contrib/python/pythran/pythran/analyses/use_def_chain.py29
-rw-r--r--contrib/python/pythran/pythran/analyses/use_omp.py4
-rw-r--r--contrib/python/pythran/pythran/analyses/yield_points.py4
-rw-r--r--contrib/python/pythran/pythran/backend.py105
-rw-r--r--contrib/python/pythran/pythran/config.py45
-rw-r--r--contrib/python/pythran/pythran/cxxgen.py60
-rw-r--r--contrib/python/pythran/pythran/cxxtypes.py30
-rw-r--r--contrib/python/pythran/pythran/dist.py5
-rw-r--r--contrib/python/pythran/pythran/errors.py4
-rw-r--r--contrib/python/pythran/pythran/graph.py6
-rw-r--r--contrib/python/pythran/pythran/intrinsic.py32
-rw-r--r--contrib/python/pythran/pythran/optimizations/comprehension_patterns.py5
-rw-r--r--contrib/python/pythran/pythran/optimizations/constant_folding.py46
-rw-r--r--contrib/python/pythran/pythran/optimizations/dead_code_elimination.py6
-rw-r--r--contrib/python/pythran/pythran/optimizations/fast_gexpr.py14
-rw-r--r--contrib/python/pythran/pythran/optimizations/forward_substitution.py36
-rw-r--r--contrib/python/pythran/pythran/optimizations/inline_builtins.py5
-rw-r--r--contrib/python/pythran/pythran/optimizations/inlining.py18
-rw-r--r--contrib/python/pythran/pythran/optimizations/iter_transformation.py6
-rw-r--r--contrib/python/pythran/pythran/optimizations/list_comp_to_genexp.py4
-rw-r--r--contrib/python/pythran/pythran/optimizations/list_to_tuple.py5
-rw-r--r--contrib/python/pythran/pythran/optimizations/modindex.py6
-rw-r--r--contrib/python/pythran/pythran/optimizations/pattern_transform.py6
-rw-r--r--contrib/python/pythran/pythran/optimizations/range_based_simplify.py5
-rw-r--r--contrib/python/pythran/pythran/optimizations/remove_dead_functions.py5
-rw-r--r--contrib/python/pythran/pythran/optimizations/square.py3
-rw-r--r--contrib/python/pythran/pythran/optimizations/tuple_to_shape.py6
-rw-r--r--contrib/python/pythran/pythran/passmanager.py46
-rw-r--r--contrib/python/pythran/pythran/pythonic/builtins/oct.hpp8
-rw-r--r--contrib/python/pythran/pythran/pythonic/builtins/str/splitlines.hpp35
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/builtins/str/splitlines.hpp22
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/numpy/array.hpp7
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/numpy/dot.hpp16
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/numpy/float128.hpp6
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/NoneType.hpp195
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/combined.hpp94
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/dict.hpp105
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/list.hpp5
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp6
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/numpy_gexpr.hpp6
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/numpy_iexpr.hpp25
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/set.hpp9
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/types/tuple.hpp7
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/utils/numpy_traits.hpp19
-rw-r--r--contrib/python/pythran/pythran/pythonic/include/utils/seq.hpp23
-rw-r--r--contrib/python/pythran/pythran/pythonic/numpy/dot.hpp10
-rw-r--r--contrib/python/pythran/pythran/pythonic/numpy/float128.hpp5
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/NoneType.hpp182
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/array.hpp4
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/dict.hpp26
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/list.hpp8
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp1
-rw-r--r--contrib/python/pythran/pythran/pythonic/types/set.hpp6
-rw-r--r--contrib/python/pythran/pythran/pythran.cfg13
-rw-r--r--contrib/python/pythran/pythran/spec.py15
-rw-r--r--contrib/python/pythran/pythran/syntax.py9
-rw-r--r--contrib/python/pythran/pythran/tables.py11
-rw-r--r--contrib/python/pythran/pythran/toolchain.py4
-rw-r--r--contrib/python/pythran/pythran/transformations/expand_builtins.py5
-rw-r--r--contrib/python/pythran/pythran/transformations/expand_globals.py2
-rw-r--r--contrib/python/pythran/pythran/transformations/expand_imports.py4
-rw-r--r--contrib/python/pythran/pythran/transformations/false_polymorphism.py13
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_ifelse.py5
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_is_none.py15
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_method_calls.py4
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_return.py17
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_static_if.py29
-rw-r--r--contrib/python/pythran/pythran/transformations/normalize_tuples.py3
-rw-r--r--contrib/python/pythran/pythran/transformations/remove_comprehension.py2
-rw-r--r--contrib/python/pythran/pythran/transformations/remove_lambdas.py5
-rw-r--r--contrib/python/pythran/pythran/transformations/remove_named_arguments.py5
-rw-r--r--contrib/python/pythran/pythran/transformations/remove_nested_functions.py148
-rw-r--r--contrib/python/pythran/pythran/transformations/unshadow_parameters.py5
-rw-r--r--contrib/python/pythran/pythran/types/reorder.py9
-rw-r--r--contrib/python/pythran/pythran/types/tog.py15
-rw-r--r--contrib/python/pythran/pythran/types/type_dependencies.py22
-rw-r--r--contrib/python/pythran/pythran/types/types.py504
-rw-r--r--contrib/python/pythran/pythran/unparse.py4
-rw-r--r--contrib/python/pythran/pythran/version.py2
-rw-r--r--contrib/python/pythran/ya.make2
116 files changed, 1668 insertions, 1202 deletions
diff --git a/contrib/python/pythran/.dist-info/METADATA b/contrib/python/pythran/.dist-info/METADATA
index f68434e090c..977fa455ed0 100644
--- a/contrib/python/pythran/.dist-info/METADATA
+++ b/contrib/python/pythran/.dist-info/METADATA
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: pythran
-Version: 0.17.0
+Version: 0.18.0
Summary: Ahead of Time compiler for numeric kernels
Author-email: Serge Guelton <[email protected]>
License: Copyright (c) 2012, HPC Project and Serge Guelton
@@ -52,24 +52,25 @@ Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE
License-File: AUTHORS
-Requires-Dist: ply >=3.4
+Requires-Dist: ply>=3.4
Requires-Dist: setuptools
-Requires-Dist: gast ~=0.6.0
+Requires-Dist: gast~=0.6.0
Requires-Dist: numpy
-Requires-Dist: beniget ~=0.4.0
+Requires-Dist: beniget~=0.4.0
Provides-Extra: doc
-Requires-Dist: numpy ; extra == 'doc'
-Requires-Dist: nbsphinx ; extra == 'doc'
-Requires-Dist: scipy ; extra == 'doc'
-Requires-Dist: guzzle-sphinx-theme ; extra == 'doc'
+Requires-Dist: numpy; extra == "doc"
+Requires-Dist: nbsphinx; extra == "doc"
+Requires-Dist: scipy; extra == "doc"
+Requires-Dist: guzzle_sphinx_theme; extra == "doc"
Provides-Extra: test
-Requires-Dist: ipython ; extra == 'test'
-Requires-Dist: nbval ; extra == 'test'
-Requires-Dist: cython ; extra == 'test'
-Requires-Dist: wheel ; extra == 'test'
-Requires-Dist: packaging ; extra == 'test'
-Requires-Dist: ninja ; extra == 'test'
-Requires-Dist: meson ; extra == 'test'
+Requires-Dist: ipython; extra == "test"
+Requires-Dist: nbval; extra == "test"
+Requires-Dist: cython; extra == "test"
+Requires-Dist: wheel; extra == "test"
+Requires-Dist: packaging; extra == "test"
+Requires-Dist: ninja; extra == "test"
+Requires-Dist: meson; extra == "test"
+Dynamic: license-file
Pythran
#######
@@ -119,7 +120,7 @@ Using ``pip``
Using ``mamba`` or ``conda``
****************************
-1. Using ``mamba`` (https://github.com/conda-forge/miniforge#mambaforge) or ``conda`` (https://github.com/conda-forge/miniforge)
+1. Using ``mamba`` (https://github.com/conda-forge/miniforge) or ``conda`` (https://github.com/conda-forge/miniforge)
2. Run::
diff --git a/contrib/python/pythran/README.rst b/contrib/python/pythran/README.rst
index a8ac9706212..eeb9384b2e9 100644
--- a/contrib/python/pythran/README.rst
+++ b/contrib/python/pythran/README.rst
@@ -46,7 +46,7 @@ Using ``pip``
Using ``mamba`` or ``conda``
****************************
-1. Using ``mamba`` (https://github.com/conda-forge/miniforge#mambaforge) or ``conda`` (https://github.com/conda-forge/miniforge)
+1. Using ``mamba`` (https://github.com/conda-forge/miniforge) or ``conda`` (https://github.com/conda-forge/miniforge)
2. Run::
diff --git a/contrib/python/pythran/pythran/analyses/__init__.py b/contrib/python/pythran/pythran/analyses/__init__.py
index 3c149f9b0bd..9948c9260c5 100644
--- a/contrib/python/pythran/pythran/analyses/__init__.py
+++ b/contrib/python/pythran/pythran/analyses/__init__.py
@@ -19,7 +19,7 @@ from .constant_expressions import ConstantExpressions
from .dependencies import Dependencies
from .extended_syntax_check import ExtendedSyntaxCheck
from .fixed_size_list import FixedSizeList
-from .global_declarations import GlobalDeclarations
+from .global_declarations import GlobalDeclarations, NonlocalDeclarations
from .global_effects import GlobalEffects
from .globals_analysis import Globals
from .has_return import HasReturn, HasBreak, HasContinue
diff --git a/contrib/python/pythran/pythran/analyses/aliases.py b/contrib/python/pythran/pythran/analyses/aliases.py
index 79f050e7111..ff4f2a937fa 100644
--- a/contrib/python/pythran/pythran/analyses/aliases.py
+++ b/contrib/python/pythran/pythran/analyses/aliases.py
@@ -71,7 +71,7 @@ for module in MODULES.values():
save_intrinsic_alias(module)
-class Aliases(ModuleAnalysis):
+class Aliases(ModuleAnalysis[GlobalDeclarations]):
'''
Gather aliasing informations across nodes
@@ -81,10 +81,11 @@ class Aliases(ModuleAnalysis):
RetId = '@'
+ ResultType = dict
+
def __init__(self):
- self.result = dict()
+ super().__init__()
self.aliases = None
- super(Aliases, self).__init__(GlobalDeclarations)
@staticmethod
def dump(result, filter=None):
@@ -420,6 +421,21 @@ class Aliases(ModuleAnalysis):
>>> Aliases.dump(result, filter=ast.Subscript)
[a, b][c] => ['a', 'b']
+ Also work in case of a dict:
+
+ >>> module = ast.parse('def foo(a, b, c): return {a:b}[c]')
+ >>> result = pm.gather(Aliases, module)
+ >>> Aliases.dump(result, filter=ast.Subscript)
+ {a: b}[c] => ['b']
+
+ Even when built in several statements:
+ >>> module = ast.parse('def foo(a, b, c): d = {} ; d[a] = b; return d[c]')
+ >>> result = pm.gather(Aliases, module)
+ >>> Aliases.dump(result, filter=ast.Subscript)
+ d[a] => ['b']
+ d[c] => ['b']
+
+
Moreover, in case of a tuple indexed by a constant value, we can
further refine the aliasing information:
@@ -473,7 +489,7 @@ class Aliases(ModuleAnalysis):
def visit_Name(self, node):
if node.id not in self.aliases:
- raise UnboundIdentifierError
+ raise UnboundIdentifierError(node.id)
return self.add(node, self.aliases[node.id])
def visit_Tuple(self, node):
@@ -661,10 +677,28 @@ class Aliases(ModuleAnalysis):
a_id = alias.id
self.aliases[a_id] = self.aliases[a_id].union((t,))
self.add(t, self.aliases[t.id])
+ elif isinstance(t, ast.Subscript):
+ def wrap(t, aliases):
+ if isinstance(t, ast.Subscript):
+ alias, wrapped = wrap(t.value, aliases)
+ return alias, {ContainerOf(wrapped)}
+ elif isinstance(t, ast.Name):
+ return t, aliases
+ else:
+ raise NotImplementedError
+ try:
+ alias, wrapped = wrap(t, value_aliases)
+ self.aliases[alias.id] = self.aliases[alias.id].union(wrapped)
+ except NotImplementedError:
+ ...
+ self.visit(t)
+ self.add(t, value_aliases)
else:
self.visit(t)
- visit_AnnAssign = visit_Assign
+ def visit_AnnAssign(self, node):
+ self.visit_Assign(node)
+ self.visit(node.annotation)
def visit_For(self, node):
'''
diff --git a/contrib/python/pythran/pythran/analyses/ancestors.py b/contrib/python/pythran/pythran/analyses/ancestors.py
index 6956378bb34..7364b6b134e 100644
--- a/contrib/python/pythran/pythran/analyses/ancestors.py
+++ b/contrib/python/pythran/pythran/analyses/ancestors.py
@@ -13,10 +13,11 @@ class Ancestors(ModuleAnalysis):
and list of nodes as values.
'''
+ ResultType = dict
+
def __init__(self):
- self.result = dict()
+ super().__init__()
self.current = tuple()
- super(Ancestors, self).__init__()
def generic_visit(self, node):
self.result[node] = current = self.current
@@ -29,6 +30,7 @@ class Ancestors(ModuleAnalysis):
class AncestorsWithBody(Ancestors):
+ # Overload the visit method set from Ancestors
visit = ModuleAnalysis.visit
def visit_metadata(self, node):
diff --git a/contrib/python/pythran/pythran/analyses/argument_effects.py b/contrib/python/pythran/pythran/analyses/argument_effects.py
index 808b86349c5..8b436c98418 100644
--- a/contrib/python/pythran/pythran/analyses/argument_effects.py
+++ b/contrib/python/pythran/pythran/analyses/argument_effects.py
@@ -48,17 +48,17 @@ for module in MODULES.values():
save_function_effect(module)
-class ArgumentEffects(ModuleAnalysis):
+class ArgumentEffectsHelper(ModuleAnalysis[Aliases, GlobalDeclarations, Intrinsics]):
"""Gathers inter-procedural effects on function arguments."""
+ ResultType = DiGraph
+
def __init__(self):
# There's an edge between src and dest if a parameter of dest is
# modified by src
- self.result = DiGraph()
+ super().__init__()
self.node_to_functioneffect = {}
- super(ArgumentEffects, self).__init__(Aliases, GlobalDeclarations,
- Intrinsics)
def prepare(self, node):
"""
@@ -67,7 +67,7 @@ class ArgumentEffects(ModuleAnalysis):
Initialisation done for Pythonic functions and default value set for
user defined functions.
"""
- super(ArgumentEffects, self).prepare(node)
+ super().prepare(node)
for i in self.intrinsics:
fe = IntrinsicArgumentEffects[i]
self.node_to_functioneffect[i] = fe
@@ -78,29 +78,6 @@ class ArgumentEffects(ModuleAnalysis):
self.node_to_functioneffect[n] = fe
self.result.add_node(fe)
- def run(self, node):
- result = super(ArgumentEffects, self).run(node)
- candidates = set(result)
- while candidates:
- function = candidates.pop()
- for ue in enumerate(function.update_effects):
- update_effect_idx, update_effect = ue
- if not update_effect:
- continue
- for pred in result.successors(function):
- edge = result.edges[function, pred]
- for fp in enumerate(edge["formal_parameters"]):
- i, formal_parameter_idx = fp
- # propagate the impurity backward if needed.
- # Afterward we may need another graph iteration
- ith_effectiv = edge["effective_parameters"][i]
- if(formal_parameter_idx == update_effect_idx and
- not pred.update_effects[ith_effectiv]):
- pred.update_effects[ith_effectiv] = True
- candidates.add(pred)
- self.result = {f.func: f.update_effects for f in result}
- return self.result
-
def argument_index(self, node):
while isinstance(node, ast.Subscript):
node = node.value
@@ -199,3 +176,31 @@ class ArgumentEffects(ModuleAnalysis):
edge["effective_parameters"].append(n)
edge["formal_parameters"].append(i)
self.generic_visit(node)
+
+class ArgumentEffects(ModuleAnalysis[ArgumentEffectsHelper]):
+
+ """Gathers inter-procedural effects on function arguments."""
+
+ ResultType = dict
+
+ def visit_Module(self, node):
+ result = self.argument_effects_helper
+ candidates = set(result)
+ while candidates:
+ function = candidates.pop()
+ for ue in enumerate(function.update_effects):
+ update_effect_idx, update_effect = ue
+ if not update_effect:
+ continue
+ for pred in result.successors(function):
+ edge = result.edges[function, pred]
+ for fp in enumerate(edge["formal_parameters"]):
+ i, formal_parameter_idx = fp
+ # propagate the impurity backward if needed.
+ # Afterward we may need another graph iteration
+ ith_effectiv = edge["effective_parameters"][i]
+ if(formal_parameter_idx == update_effect_idx and
+ not pred.update_effects[ith_effectiv]):
+ pred.update_effects[ith_effectiv] = True
+ candidates.add(pred)
+ self.result = {f.func: f.update_effects for f in result}
diff --git a/contrib/python/pythran/pythran/analyses/argument_read_once.py b/contrib/python/pythran/pythran/analyses/argument_read_once.py
index 8210fc70d74..676ce77d98c 100644
--- a/contrib/python/pythran/pythran/analyses/argument_read_once.py
+++ b/contrib/python/pythran/pythran/analyses/argument_read_once.py
@@ -4,24 +4,32 @@ from pythran.analyses.aliases import Aliases
from pythran.analyses.global_declarations import GlobalDeclarations
from pythran.passmanager import ModuleAnalysis
from pythran.tables import MODULES
+from pythran.intrinsic import defaultlist
import pythran.intrinsic as intrinsic
import gast as ast
from functools import reduce
-class ArgumentReadOnce(ModuleAnalysis):
-
- """
- Counts the usages of each argument of each function.
+def recursive_weight(function, index, predecessors):
+ # TODO : Find out why it happens in some cases
+ if len(function.read_effects) <= index:
+ return 0
+ if function.read_effects[index] == -1:
+ # In case of recursive/cyclic calls
+ cycle = function in predecessors
+ predecessors.add(function)
+ if cycle:
+ function.read_effects[index] = 2 * function.dependencies(
+ ArgumentReadOnceHelper.Context(function, index,
+ predecessors, False))
+ else:
+ function.read_effects[index] = function.dependencies(
+ ArgumentReadOnceHelper.Context(function, index,
+ predecessors, True))
+ return function.read_effects[index]
- Attributes
- ----------
- result : {FunctionEffects}
- Number of use for each argument of each function.
- node_to_functioneffect : {???: ???}
- FunctionDef ast node to function effect binding.
- """
+class ArgumentReadOnceHelper(ModuleAnalysis[Aliases, GlobalDeclarations]):
class FunctionEffects(object):
def __init__(self, node):
@@ -30,9 +38,15 @@ class ArgumentReadOnce(ModuleAnalysis):
if isinstance(node, ast.FunctionDef):
self.read_effects = [-1] * len(node.args.args)
elif isinstance(node, intrinsic.Intrinsic):
- self.read_effects = [
- 1 if isinstance(x, intrinsic.ReadOnceEffect)
- else 2 for x in node.argument_effects]
+ def tocode(x):
+ return 1 if isinstance(x, intrinsic.ReadOnceEffect) else 2
+
+ isdefaultlist = isinstance(node.argument_effects, defaultlist)
+ container = node.argument_effects.content if isdefaultlist else node.argument_effects
+ self.read_effects = [tocode(x) for x in container]
+ if isdefaultlist:
+ default = lambda: tocode(node.argument_effects.default())
+ self.read_effects = defaultlist(self.read_effects, default=default)
elif isinstance(node, ast.alias):
self.read_effects = []
else:
@@ -51,11 +65,12 @@ class ArgumentReadOnce(ModuleAnalysis):
self.path = path
self.global_dependencies = global_dependencies
+ ResultType = set
+
def __init__(self):
""" Basic initialiser for class attributes. """
- self.result = set()
+ super().__init__()
self.node_to_functioneffect = dict()
- super(ArgumentReadOnce, self).__init__(Aliases, GlobalDeclarations)
def prepare(self, node):
"""
@@ -64,10 +79,10 @@ class ArgumentReadOnce(ModuleAnalysis):
Initialisation done for Pythonic functions and default values set for
user defined functions.
"""
- super(ArgumentReadOnce, self).prepare(node)
+ super(ArgumentReadOnceHelper, self).prepare(node)
# global functions init
for n in self.global_declarations.values():
- fe = ArgumentReadOnce.FunctionEffects(n)
+ fe = ArgumentReadOnceHelper.FunctionEffects(n)
self.node_to_functioneffect[n] = fe
self.result.add(fe)
@@ -78,7 +93,7 @@ class ArgumentReadOnce(ModuleAnalysis):
if isinstance(intr, dict): # Submodule case
save_effect(intr)
else:
- fe = ArgumentReadOnce.FunctionEffects(intr)
+ fe = ArgumentReadOnceHelper.FunctionEffects(intr)
self.node_to_functioneffect[intr] = fe
self.result.add(fe)
if isinstance(intr, intrinsic.Class): # Class case
@@ -87,32 +102,6 @@ class ArgumentReadOnce(ModuleAnalysis):
for module in MODULES.values():
save_effect(module)
- def run(self, node):
- result = super(ArgumentReadOnce, self).run(node)
- for fun in result:
- for i in range(len(fun.read_effects)):
- self.recursive_weight(fun, i, set())
- self.result = {f.func: f.read_effects for f in result}
- return self.result
-
- def recursive_weight(self, function, index, predecessors):
- # TODO : Find out why it happens in some cases
- if len(function.read_effects) <= index:
- return 0
- if function.read_effects[index] == -1:
- # In case of recursive/cyclic calls
- cycle = function in predecessors
- predecessors.add(function)
- if cycle:
- function.read_effects[index] = 2 * function.dependencies(
- ArgumentReadOnce.Context(function, index,
- predecessors, False))
- else:
- function.read_effects[index] = function.dependencies(
- ArgumentReadOnce.Context(function, index,
- predecessors, True))
- return function.read_effects[index]
-
def argument_index(self, node):
while isinstance(node, ast.Subscript):
node = node.value
@@ -219,8 +208,7 @@ class ArgumentReadOnce(ModuleAnalysis):
def merger(ctx):
base = l0(ctx)
if (ctx.index in index_corres) and ctx.global_dependencies:
- rec = self.recursive_weight(func, index_corres[ctx.index],
- ctx.path)
+ rec = recursive_weight(func, index_corres[ctx.index], ctx.path)
else:
rec = 0
return base + rec
@@ -236,3 +224,26 @@ class ArgumentReadOnce(ModuleAnalysis):
dep = self.generic_visit(node)
local = self.local_effect(node.iter, 1)
return lambda ctx: dep(ctx) + local(ctx)
+
+class ArgumentReadOnce(ModuleAnalysis[ArgumentReadOnceHelper]):
+
+ """
+ Counts the usages of each argument of each function.
+
+ Attributes
+ ----------
+ result : {FunctionEffects}
+ Number of use for each argument of each function.
+ node_to_functioneffect : {???: ???}
+ FunctionDef ast node to function effect binding.
+ """
+
+ ResultType = set
+
+
+ def visit_Module(self, node):
+ result = self.argument_read_once_helper
+ for fun in result:
+ for i in range(len(fun.read_effects)):
+ recursive_weight(fun, i, set())
+ self.result = {f.func: f.read_effects for f in result}
diff --git a/contrib/python/pythran/pythran/analyses/cfg.py b/contrib/python/pythran/pythran/analyses/cfg.py
index b2e9de78f47..59c14b8a723 100644
--- a/contrib/python/pythran/pythran/analyses/cfg.py
+++ b/contrib/python/pythran/pythran/analyses/cfg.py
@@ -35,9 +35,7 @@ class CFG(FunctionAnalysis):
#: control flow without a return statement.
NIL = object()
- def __init__(self):
- self.result = DiGraph()
- super(CFG, self).__init__()
+ ResultType = DiGraph
def visit_FunctionDef(self, node):
"""OUT = node, RAISES = ()"""
@@ -64,6 +62,7 @@ class CFG(FunctionAnalysis):
visit_Assign = visit_AnnAssign = visit_AugAssign = visit_Import = visit_Pass
visit_Expr = visit_Print = visit_ImportFrom = visit_Pass
visit_Yield = visit_Delete = visit_Pass
+ visit_Nonlocal = visit_Pass
def visit_Return(self, node):
"""OUT = (), RAISES = ()"""
diff --git a/contrib/python/pythran/pythran/analyses/constant_expressions.py b/contrib/python/pythran/pythran/analyses/constant_expressions.py
index 7c6e3938f78..4c05d4024fa 100644
--- a/contrib/python/pythran/pythran/analyses/constant_expressions.py
+++ b/contrib/python/pythran/pythran/analyses/constant_expressions.py
@@ -11,14 +11,11 @@ import gast as ast
-class ConstantExpressions(NodeAnalysis):
+class ConstantExpressions(NodeAnalysis[Globals, Locals, Aliases, PureFunctions]):
"""Identify constant expressions."""
- def __init__(self):
- self.result = set()
- super(ConstantExpressions, self).__init__(Globals, Locals, Aliases,
- PureFunctions)
+ ResultType = set
def add(self, node):
self.result.add(node)
diff --git a/contrib/python/pythran/pythran/analyses/dependencies.py b/contrib/python/pythran/pythran/analyses/dependencies.py
index 93efecf0486..f153558f8c1 100644
--- a/contrib/python/pythran/pythran/analyses/dependencies.py
+++ b/contrib/python/pythran/pythran/analyses/dependencies.py
@@ -59,9 +59,7 @@ class Dependencies(ModuleAnalysis):
ast.FloorDiv: ('operator', 'ifloordiv'),
}
- def __init__(self):
- self.result = set()
- super(Dependencies, self).__init__()
+ ResultType = set
def visit_List(self, node):
self.result.add(('builtins', 'list'))
diff --git a/contrib/python/pythran/pythran/analyses/extended_syntax_check.py b/contrib/python/pythran/pythran/analyses/extended_syntax_check.py
index e0673dc75f1..3520ebc14a3 100644
--- a/contrib/python/pythran/pythran/analyses/extended_syntax_check.py
+++ b/contrib/python/pythran/pythran/analyses/extended_syntax_check.py
@@ -26,7 +26,7 @@ def is_global(node):
is_global_constant(node))
-class ExtendedSyntaxCheck(ModuleAnalysis):
+class ExtendedSyntaxCheck(ModuleAnalysis[StrictAliases, ArgumentEffects]):
"""
Perform advanced syntax checking, based on strict aliases analysis:
- is there a function redefinition?
@@ -34,12 +34,11 @@ class ExtendedSyntaxCheck(ModuleAnalysis):
- is there an operation that updates a global variable?
"""
+ ResultType = type(None)
def __init__(self):
- self.result = None
- self.update = False
+ super().__init__()
self.inassert = False
self.functions = set()
- ModuleAnalysis.__init__(self, StrictAliases, ArgumentEffects)
def check_global_with_side_effect(self, node, arg):
if not isinstance(arg, ast.Call):
diff --git a/contrib/python/pythran/pythran/analyses/fixed_size_list.py b/contrib/python/pythran/pythran/analyses/fixed_size_list.py
index 1d05d2dc421..dc6593ab9cb 100644
--- a/contrib/python/pythran/pythran/analyses/fixed_size_list.py
+++ b/contrib/python/pythran/pythran/analyses/fixed_size_list.py
@@ -5,18 +5,16 @@ This could be a type information, but it seems easier to implement it that way
"""
from pythran.passmanager import FunctionAnalysis
from pythran.tables import MODULES
+from pythran.analyses import Aliases, Ancestors
+from pythran.analyses.use_def_chain import DefUseChains
+from pythran.analyses import ArgumentEffects
import gast as ast
-class FixedSizeList(FunctionAnalysis):
+class FixedSizeList(FunctionAnalysis[Aliases, DefUseChains, Ancestors, ArgumentEffects]):
- def __init__(self):
- self.result = set()
- from pythran.analyses import Aliases, DefUseChains, Ancestors
- from pythran.analyses import ArgumentEffects
- super(FixedSizeList, self).__init__(Aliases, DefUseChains, Ancestors,
- ArgumentEffects)
+ ResultType = set
def is_fixed_size_list_def(self, node):
if isinstance(node, ast.List):
diff --git a/contrib/python/pythran/pythran/analyses/global_declarations.py b/contrib/python/pythran/pythran/analyses/global_declarations.py
index aca22d4632b..3a27a6af22a 100644
--- a/contrib/python/pythran/pythran/analyses/global_declarations.py
+++ b/contrib/python/pythran/pythran/analyses/global_declarations.py
@@ -23,10 +23,7 @@ class GlobalDeclarations(ModuleAnalysis):
"""
- def __init__(self):
- """ Result is an identifier with matching definition. """
- self.result = dict()
- super(GlobalDeclarations, self).__init__()
+ ResultType = dict
def visit_FunctionDef(self, node):
""" Import module define a new variable name. """
@@ -43,3 +40,59 @@ class GlobalDeclarations(ModuleAnalysis):
def visit_Name(self, node):
if isinstance(node.ctx, ast.Store):
self.result[node.id] = node
+
+
+class NonlocalDeclarations(ModuleAnalysis):
+
+ """ Transitively gather nonlocal declarations, per function
+
+ >>> import gast as ast
+ >>> from pythran import passmanager
+ >>> from pythran.analyses import NonlocalDeclarations
+ >>> node = ast.parse('''
+ ... def foo(a):
+ ... def t():
+ ... def bar():
+ ... nonlocal a
+ ... a = 1
+ ... bar()
+ ... t()''')
+ >>> pm = passmanager.PassManager("test")
+ >>> [(n.name, l) for n, l in pm.gather(NonlocalDeclarations, node).items()]
+ [('foo', set()), ('t', {'a'}), ('bar', {'a'})]
+
+ """
+
+ ResultType = dict
+
+ def __init__(self):
+ super().__init__()
+ self.context = []
+ self.locals = []
+ self.nested = [set()]
+
+ def visit_FunctionDef(self, node):
+ self.nested[-1].add(node)
+
+ self.nested.append(set())
+ self.locals.append(set())
+
+ self.context.append(node)
+
+ self.result[node] = set()
+ self.generic_visit(node)
+
+ self.context.pop()
+
+ locals_ = self.locals.pop()
+ nested = self.nested.pop()
+
+ for f in nested:
+ self.result[node].update(self.result[f] - locals_)
+
+ def visit_Name(self, node):
+ if isinstance(node.ctx, (ast.Store, ast.Param)):
+ self.locals[-1].add(node.id)
+
+ def visit_Nonlocal(self, node):
+ self.result[self.context[-1]].update(node.names)
diff --git a/contrib/python/pythran/pythran/analyses/global_effects.py b/contrib/python/pythran/pythran/analyses/global_effects.py
index 0f8c3ad8108..dd82a0958e5 100644
--- a/contrib/python/pythran/pythran/analyses/global_effects.py
+++ b/contrib/python/pythran/pythran/analyses/global_effects.py
@@ -46,15 +46,14 @@ def save_global_effects(module):
for module in MODULES.values():
save_global_effects(module)
-class GlobalEffects(ModuleAnalysis):
+class GlobalEffectsHelper(ModuleAnalysis[Aliases, GlobalDeclarations, Intrinsics]):
"""Add a flag on each function that updates a global variable."""
+ ResultType = DiGraph
def __init__(self):
- self.result = DiGraph()
+ super().__init__()
self.node_to_functioneffect = dict()
- super(GlobalEffects, self).__init__(Aliases, GlobalDeclarations,
- Intrinsics)
def prepare(self, node):
"""
@@ -63,7 +62,7 @@ class GlobalEffects(ModuleAnalysis):
Initialisation done for Pythonic functions and default value set for
user defined functions.
"""
- super(GlobalEffects, self).prepare(node)
+ super().prepare(node)
for i in self.intrinsics:
fe = IntrinsicGlobalEffects[i]
@@ -78,19 +77,6 @@ class GlobalEffects(ModuleAnalysis):
self.node_to_functioneffect[intrinsic.UnboundValue] = \
FunctionEffect(intrinsic.UnboundValue)
- def run(self, node):
- result = super(GlobalEffects, self).run(node)
- keep_going = True
- while keep_going:
- keep_going = False
- for function in result:
- if function.global_effect:
- for pred in self.result.predecessors(function):
- if not pred.global_effect:
- keep_going = pred.global_effect = True
- self.result = {f.func for f in result if f.global_effect}
- return self.result
-
def visit_FunctionDef(self, node):
self.current_function = self.node_to_functioneffect[node]
assert self.current_function in self.result
@@ -125,3 +111,22 @@ class GlobalEffects(ModuleAnalysis):
func_alias = self.node_to_functioneffect[func_alias]
self.result.add_edge(self.current_function, func_alias)
self.generic_visit(node)
+
+
+class GlobalEffects(ModuleAnalysis[GlobalEffectsHelper]):
+
+ """Add a flag on each function that updates a global variable."""
+
+ ResultType = dict
+
+ def visit_Module(self, node):
+ result = self.global_effects_helper
+ keep_going = True
+ while keep_going:
+ keep_going = False
+ for function in result:
+ if function.global_effect:
+ for pred in result.predecessors(function):
+ if not pred.global_effect:
+ keep_going = pred.global_effect = True
+ self.result = {f.func for f in result if f.global_effect}
diff --git a/contrib/python/pythran/pythran/analyses/globals_analysis.py b/contrib/python/pythran/pythran/analyses/globals_analysis.py
index 18cadb757c1..e883ade87eb 100644
--- a/contrib/python/pythran/pythran/analyses/globals_analysis.py
+++ b/contrib/python/pythran/pythran/analyses/globals_analysis.py
@@ -4,10 +4,9 @@ from pythran.analyses.global_declarations import GlobalDeclarations
from pythran.passmanager import ModuleAnalysis
-class Globals(ModuleAnalysis):
- def __init__(self):
- self.result = set()
- super(Globals, self).__init__(GlobalDeclarations)
+class Globals(ModuleAnalysis[GlobalDeclarations]):
+
+ ResultType = set
def visit_Module(self, node):
self.result = {'builtins',
diff --git a/contrib/python/pythran/pythran/analyses/has_return.py b/contrib/python/pythran/pythran/analyses/has_return.py
index 7f9a27b50f8..604c68f591f 100644
--- a/contrib/python/pythran/pythran/analyses/has_return.py
+++ b/contrib/python/pythran/pythran/analyses/has_return.py
@@ -9,9 +9,7 @@ from pythran.passmanager import NodeAnalysis
class HasReturn(NodeAnalysis):
- def __init__(self):
- self.result = False
- super(HasReturn, self).__init__()
+ ResultType = bool
def visit_Return(self, _):
self.result = True
@@ -22,9 +20,7 @@ class HasReturn(NodeAnalysis):
class HasBreak(NodeAnalysis):
- def __init__(self):
- self.result = False
- super(HasBreak, self).__init__()
+ ResultType = bool
def visit_For(self, _):
return
@@ -37,9 +33,7 @@ class HasBreak(NodeAnalysis):
class HasContinue(NodeAnalysis):
- def __init__(self):
- self.result = False
- super(HasContinue, self).__init__()
+ ResultType = bool
def visit_For(self, _):
return
diff --git a/contrib/python/pythran/pythran/analyses/identifiers.py b/contrib/python/pythran/pythran/analyses/identifiers.py
index 5dc0e6047b1..b909fa17b98 100644
--- a/contrib/python/pythran/pythran/analyses/identifiers.py
+++ b/contrib/python/pythran/pythran/analyses/identifiers.py
@@ -7,9 +7,7 @@ from pythran.passmanager import NodeAnalysis
class Identifiers(NodeAnalysis):
"""Gather all identifiers used throughout a node."""
- def __init__(self):
- self.result = set()
- super(Identifiers, self).__init__()
+ ResultType = set
def visit_Name(self, node):
self.result.add(node.id)
diff --git a/contrib/python/pythran/pythran/analyses/immediates.py b/contrib/python/pythran/pythran/analyses/immediates.py
index 08c58d59d85..61a0897bfe5 100644
--- a/contrib/python/pythran/pythran/analyses/immediates.py
+++ b/contrib/python/pythran/pythran/analyses/immediates.py
@@ -11,10 +11,8 @@ from pythran.utils import pythran_builtin, isnum, ispowi
_make_shape = pythran_builtin('make_shape')
-class Immediates(NodeAnalysis):
- def __init__(self):
- self.result = set()
- super(Immediates, self).__init__(Aliases)
+class Immediates(NodeAnalysis[Aliases]):
+ ResultType = set
def visit_BinOp(self, node):
self.generic_visit(node)
diff --git a/contrib/python/pythran/pythran/analyses/imported_ids.py b/contrib/python/pythran/pythran/analyses/imported_ids.py
index 4df642bceed..41e59e35a75 100644
--- a/contrib/python/pythran/pythran/analyses/imported_ids.py
+++ b/contrib/python/pythran/pythran/analyses/imported_ids.py
@@ -8,24 +8,45 @@ import pythran.metadata as md
import gast as ast
-class ImportedIds(NodeAnalysis):
-
- """Gather ids referenced by a node and not declared locally."""
+class ImportedIds(NodeAnalysis[Globals, Locals]):
+
+ """
+ Gather ids referenced by a node and not declared locally.
+
+ >>> import gast as ast
+ >>> from pythran import passmanager
+ >>> from pythran.analyses import ImportedIds
+ >>> node = ast.parse('''
+ ... def foo():
+ ... def t():
+ ... nonlocal g
+ ... g = k
+ ... t()''')
+ >>> pm = passmanager.PassManager("test")
+ >>> sorted(pm.gather(ImportedIds, node))
+ ['g', 'k']
+ """
+
+ ResultType = set
def __init__(self):
- self.result = set()
+ super().__init__()
self.current_locals = set()
- self.is_list = False
+ self.current_nonlocals = set()
self.in_augassign = False
- super(ImportedIds, self).__init__(Globals, Locals)
def visit_Name(self, node):
- if isinstance(node.ctx, ast.Store) and not self.in_augassign:
+ if node.id in self.current_nonlocals:
+ self.result.add(node.id)
+ elif isinstance(node.ctx, ast.Store) and not self.in_augassign:
self.current_locals.add(node.id)
elif (node.id not in self.visible_globals and
node.id not in self.current_locals):
self.result.add(node.id)
+ def visit_Nonlocal(self, node):
+ self.current_nonlocals.update(node.names)
+
def visit_FunctionDef(self, node):
self.current_locals.add(node.name)
current_locals = self.current_locals.copy()
@@ -98,12 +119,4 @@ class ImportedIds(NodeAnalysis):
def prepare(self, node):
super(ImportedIds, self).prepare(node)
- if self.is_list: # so that this pass can be called on list
- node = node.body[0]
self.visible_globals = set(self.globals) - self.locals[node]
-
- def run(self, node):
- if isinstance(node, list): # so that this pass can be called on list
- self.is_list = True
- node = ast.If(ast.Constant(1, None), node, [])
- return super(ImportedIds, self).run(node)
diff --git a/contrib/python/pythran/pythran/analyses/inlinable.py b/contrib/python/pythran/pythran/analyses/inlinable.py
index 7de49627904..16c0f4edc63 100644
--- a/contrib/python/pythran/pythran/analyses/inlinable.py
+++ b/contrib/python/pythran/pythran/analyses/inlinable.py
@@ -9,7 +9,7 @@ import gast as ast
import copy
-class Inlinable(ModuleAnalysis):
+class Inlinable(ModuleAnalysis[PureExpressions]):
""" Determine set of inlinable function.
@@ -17,9 +17,7 @@ class Inlinable(ModuleAnalysis):
recurse on itself.
"""
- def __init__(self):
- self.result = dict()
- super(Inlinable, self).__init__(PureExpressions)
+ ResultType = dict
def visit_FunctionDef(self, node):
""" Determine this function definition can be inlined. """
@@ -40,7 +38,7 @@ class Inlinable(ModuleAnalysis):
return
ids = self.gather(Identifiers, sbody)
- # FIXME : It mark "not inlinable" def foo(foo): return foo
+ # FIXME : It marks "not inlinable" def foo(foo): return foo
if node.name not in ids:
self.result[node.name] = copy.deepcopy(node)
self.result[node.name].body = [self.result[node.name].body[sindex]]
diff --git a/contrib/python/pythran/pythran/analyses/intrinsics.py b/contrib/python/pythran/pythran/analyses/intrinsics.py
index 8b558e6d535..87c6f2beece 100644
--- a/contrib/python/pythran/pythran/analyses/intrinsics.py
+++ b/contrib/python/pythran/pythran/analyses/intrinsics.py
@@ -10,10 +10,8 @@ class Intrinsics(ModuleAnalysis):
""" Gather all intrinsics used in the module
"""
- def __init__(self):
- """ Result is a set of intrinsic values. """
- self.result = set()
- super(Intrinsics, self).__init__()
+ """ Result is a set of intrinsic values. """
+ ResultType = set
def visit_Attribute(self, node):
obj, _ = attr_to_path(node)
diff --git a/contrib/python/pythran/pythran/analyses/is_assigned.py b/contrib/python/pythran/pythran/analyses/is_assigned.py
index 64d9db5f13e..b349297d1b8 100644
--- a/contrib/python/pythran/pythran/analyses/is_assigned.py
+++ b/contrib/python/pythran/pythran/analyses/is_assigned.py
@@ -14,10 +14,7 @@ class IsAssigned(NodeAnalysis):
arguments effects as it is use by value.
"""
- def __init__(self):
- """ Basic initialiser. """
- self.result = list()
- super(IsAssigned, self).__init__()
+ ResultType = list
def visit_Name(self, node):
""" Stored variable have new value. """
diff --git a/contrib/python/pythran/pythran/analyses/lazyness_analysis.py b/contrib/python/pythran/pythran/analyses/lazyness_analysis.py
index 5589231080b..398cba7c2c8 100644
--- a/contrib/python/pythran/pythran/analyses/lazyness_analysis.py
+++ b/contrib/python/pythran/pythran/analyses/lazyness_analysis.py
@@ -14,7 +14,7 @@ import gast as ast
import sys
-class LazynessAnalysis(FunctionAnalysis):
+class LazynessAnalysis(FunctionAnalysis[ArgumentEffects, Aliases, PureExpressions]):
"""
Returns number of time a name is used.
@@ -88,9 +88,11 @@ class LazynessAnalysis(FunctionAnalysis):
INF = float('inf')
MANY = sys.maxsize
+ # map variable with maximum count of use in the programm
+ ResultType = dict
+
def __init__(self):
- # map variable with maximum count of use in the programm
- self.result = dict()
+ super().__init__()
# map variable with current count of use
self.name_count = dict()
# map variable to variables needed to compute it
@@ -104,8 +106,6 @@ class LazynessAnalysis(FunctionAnalysis):
# prevent any form of Forward Substitution at omp frontier
self.in_omp = set()
self.name_to_nodes = dict()
- super(LazynessAnalysis, self).__init__(ArgumentEffects, Aliases,
- PureExpressions)
def modify(self, name):
# if we modify a variable, all variables that needed it
@@ -157,6 +157,11 @@ class LazynessAnalysis(FunctionAnalysis):
self.ids = self.gather(Identifiers, node)
self.generic_visit(node)
+ # update result with last name_count values
+ for name, val in self.name_count.items():
+ old_val = self.result.get(name, 0)
+ self.result[name] = max(old_val, val)
+
def visit_Assign(self, node):
md.visit(self, node)
if node.value:
@@ -371,13 +376,3 @@ class LazynessAnalysis(FunctionAnalysis):
self.visit(arg)
self.func_args_lazyness(node.func, node.args, node)
self.visit(node.func)
-
- def run(self, node):
- result = super(LazynessAnalysis, self).run(node)
-
- # update result with last name_count values
- for name, val in self.name_count.items():
- old_val = result.get(name, 0)
- result[name] = max(old_val, val)
- self.result = result
- return self.result
diff --git a/contrib/python/pythran/pythran/analyses/literals.py b/contrib/python/pythran/pythran/analyses/literals.py
index 929e8c4e6a7..bcf152ff98b 100644
--- a/contrib/python/pythran/pythran/analyses/literals.py
+++ b/contrib/python/pythran/pythran/analyses/literals.py
@@ -11,9 +11,7 @@ class Literals(FunctionAnalysis):
"""
Store variable that save only Literals (with no construction cost)
"""
- def __init__(self):
- self.result = set()
- super(Literals, self).__init__()
+ ResultType = set
def visit_Assign(self, node):
# list, dict, set and other are not considered as Literals as they have
diff --git a/contrib/python/pythran/pythran/analyses/local_declarations.py b/contrib/python/pythran/pythran/analyses/local_declarations.py
index e3ac7548832..9a538911866 100644
--- a/contrib/python/pythran/pythran/analyses/local_declarations.py
+++ b/contrib/python/pythran/pythran/analyses/local_declarations.py
@@ -32,10 +32,7 @@ class LocalNodeDeclarations(NodeAnalysis):
['b', 'c']
"""
- def __init__(self):
- """ Initialize empty set as the result. """
- self.result = set()
- super(LocalNodeDeclarations, self).__init__()
+ ResultType = set
def visit_Name(self, node):
""" Any node with Store context is a new declaration. """
@@ -58,10 +55,8 @@ class LocalNameDeclarations(NodeAnalysis):
['a', 'b', 'foo']
"""
- def __init__(self):
- """ Initialize empty set as the result. """
- self.result = set()
- super(LocalNameDeclarations, self).__init__()
+ """ Initialize empty set as the result. """
+ ResultType = set
def visit_Name(self, node):
""" Any node with Store or Param context is a new identifier. """
diff --git a/contrib/python/pythran/pythran/analyses/locals_analysis.py b/contrib/python/pythran/pythran/analyses/locals_analysis.py
index 0d76584521a..1f4fb3054ae 100644
--- a/contrib/python/pythran/pythran/analyses/locals_analysis.py
+++ b/contrib/python/pythran/pythran/analyses/locals_analysis.py
@@ -33,11 +33,12 @@ class Locals(ModuleAnalysis):
['b', 'm', 'n']
"""
+ ResultType = dict
+
def __init__(self):
- self.result = dict()
+ super().__init__()
self.locals = set()
self.nesting = 0
- super(Locals, self).__init__()
def generic_visit(self, node):
super(Locals, self).generic_visit(node)
diff --git a/contrib/python/pythran/pythran/analyses/node_count.py b/contrib/python/pythran/pythran/analyses/node_count.py
index 02a6455b7b3..37615cca1f4 100644
--- a/contrib/python/pythran/pythran/analyses/node_count.py
+++ b/contrib/python/pythran/pythran/analyses/node_count.py
@@ -20,9 +20,7 @@ class NodeCount(NodeAnalysis):
5
"""
- def __init__(self):
- self.result = 0
- super(NodeCount, self).__init__()
+ ResultType = int
def generic_visit(self, node):
self.result += 1
diff --git a/contrib/python/pythran/pythran/analyses/optimizable_comprehension.py b/contrib/python/pythran/pythran/analyses/optimizable_comprehension.py
index 1635fc40005..4803d8f6cf9 100644
--- a/contrib/python/pythran/pythran/analyses/optimizable_comprehension.py
+++ b/contrib/python/pythran/pythran/analyses/optimizable_comprehension.py
@@ -6,11 +6,9 @@ from pythran.analyses.identifiers import Identifiers
from pythran.passmanager import NodeAnalysis
-class OptimizableComprehension(NodeAnalysis):
+class OptimizableComprehension(NodeAnalysis[Identifiers]):
"""Find whether a comprehension can be optimized."""
- def __init__(self):
- self.result = set()
- super(OptimizableComprehension, self).__init__(Identifiers)
+ ResultType = set
def check_comprehension(self, iters):
targets = {gen.target.id for gen in iters}
diff --git a/contrib/python/pythran/pythran/analyses/ordered_global_declarations.py b/contrib/python/pythran/pythran/analyses/ordered_global_declarations.py
index 1c4f30c0484..2e3dcd3dd74 100644
--- a/contrib/python/pythran/pythran/analyses/ordered_global_declarations.py
+++ b/contrib/python/pythran/pythran/analyses/ordered_global_declarations.py
@@ -7,12 +7,9 @@ from pythran.passmanager import ModuleAnalysis
import gast as ast
-class OrderedGlobalDeclarations(ModuleAnalysis):
+class OrderedGlobalDeclarationsHelper(ModuleAnalysis[StrictAliases, GlobalDeclarations]):
'''Order all global functions according to their callgraph depth'''
- def __init__(self):
- self.result = dict()
- super(OrderedGlobalDeclarations, self).__init__(
- StrictAliases, GlobalDeclarations)
+ ResultType = dict
def visit_FunctionDef(self, node):
self.curr = node
@@ -29,10 +26,15 @@ class OrderedGlobalDeclarations(ModuleAnalysis):
if alias in self.global_declarations:
self.result[self.curr].add(alias)
- def run(self, node):
+
+class OrderedGlobalDeclarations(ModuleAnalysis[OrderedGlobalDeclarationsHelper]):
+ '''Order all global functions according to their callgraph depth'''
+ ResultType = list
+
+ def visit_Module(self, node):
# compute the weight of each function
# the weight of a function is the number functions it references
- result = super(OrderedGlobalDeclarations, self).run(node)
+ result = self.ordered_global_declarations_helper
old_count = -1
new_count = 0
# iteratively propagate weights
@@ -42,6 +44,6 @@ class OrderedGlobalDeclarations(ModuleAnalysis):
old_count = new_count
new_count = sum(len(value) for value in result.values())
# return functions, the one with the greatest weight first
- self.result = sorted(self.result.keys(), reverse=True,
- key=lambda s: len(self.result[s]))
+ self.result = sorted(result.keys(), reverse=True,
+ key=lambda s: len(result[s]))
return self.result
diff --git a/contrib/python/pythran/pythran/analyses/parallel_maps.py b/contrib/python/pythran/pythran/analyses/parallel_maps.py
index 42a2761f10d..449630dc8d3 100644
--- a/contrib/python/pythran/pythran/analyses/parallel_maps.py
+++ b/contrib/python/pythran/pythran/analyses/parallel_maps.py
@@ -6,13 +6,11 @@ from pythran.passmanager import ModuleAnalysis
from pythran.tables import MODULES
-class ParallelMaps(ModuleAnalysis):
+class ParallelMaps(ModuleAnalysis[PureExpressions, Aliases]):
"""Yields the est of maps that could be parallel."""
- def __init__(self):
- self.result = set()
- super(ParallelMaps, self).__init__(PureExpressions, Aliases)
+ ResultType = set
def visit_Call(self, node):
if all(alias == MODULES['builtins']['map']
diff --git a/contrib/python/pythran/pythran/analyses/potential_iterator.py b/contrib/python/pythran/pythran/analyses/potential_iterator.py
index 23c6b3ad30f..9cf9dd2ccaf 100644
--- a/contrib/python/pythran/pythran/analyses/potential_iterator.py
+++ b/contrib/python/pythran/pythran/analyses/potential_iterator.py
@@ -9,11 +9,9 @@ from pythran.passmanager import NodeAnalysis
import gast as ast
-class PotentialIterator(NodeAnalysis):
+class PotentialIterator(NodeAnalysis[Aliases, ArgumentReadOnce]):
"""Find whether an expression can be replaced with an iterator."""
- def __init__(self):
- self.result = set()
- NodeAnalysis.__init__(self, Aliases, ArgumentReadOnce)
+ ResultType = set
def visit_For(self, node):
self.result.add(node.iter)
diff --git a/contrib/python/pythran/pythran/analyses/pure_expressions.py b/contrib/python/pythran/pythran/analyses/pure_expressions.py
index 71381f4e843..51bd051ec38 100644
--- a/contrib/python/pythran/pythran/analyses/pure_expressions.py
+++ b/contrib/python/pythran/pythran/analyses/pure_expressions.py
@@ -12,13 +12,10 @@ from pythran.intrinsic import Intrinsic
import gast as ast
-class PureExpressions(ModuleAnalysis):
+class PureExpressions(ModuleAnalysis[ArgumentEffects, GlobalEffects, Aliases, PureFunctions]):
'''Yields the set of pure expressions'''
- def __init__(self):
- self.result = set()
- super(PureExpressions, self).__init__(ArgumentEffects, GlobalEffects,
- Aliases, PureFunctions)
+ ResultType = set
def visit_FunctionDef(self, node):
if node in self.pure_functions:
diff --git a/contrib/python/pythran/pythran/analyses/pure_functions.py b/contrib/python/pythran/pythran/analyses/pure_functions.py
index f8feba86711..9bc27d18103 100644
--- a/contrib/python/pythran/pythran/analyses/pure_functions.py
+++ b/contrib/python/pythran/pythran/analyses/pure_functions.py
@@ -7,13 +7,10 @@ from pythran.analyses.global_effects import GlobalEffects
from pythran.passmanager import ModuleAnalysis
-class PureFunctions(ModuleAnalysis):
+class PureFunctions(ModuleAnalysis[ArgumentEffects, GlobalEffects]):
'''Yields the set of pure functions'''
- def __init__(self):
- self.result = set()
- super(PureFunctions, self).__init__(ArgumentEffects, GlobalEffects)
-
+ ResultType = set
def prepare(self, node):
super(PureFunctions, self).prepare(node)
diff --git a/contrib/python/pythran/pythran/analyses/range_values.py b/contrib/python/pythran/pythran/analyses/range_values.py
index bc11d37b47f..ab1cbe0de8e 100644
--- a/contrib/python/pythran/pythran/analyses/range_values.py
+++ b/contrib/python/pythran/pythran/analyses/range_values.py
@@ -6,6 +6,7 @@ from collections import defaultdict
from functools import reduce
from pythran.analyses import Aliases, CFG
+from pythran.analyses.use_omp import UseOMP
from pythran.intrinsic import Intrinsic
from pythran.passmanager import ModuleAnalysis
from pythran.interval import Interval, IntervalTuple, UNKNOWN_RANGE
@@ -205,15 +206,14 @@ def bound_range(mapping, aliases, node, modified=None):
left = right
-class RangeValuesBase(ModuleAnalysis):
+class RangeValuesBase(ModuleAnalysis[Aliases, CFG, UseOMP]):
ResultHolder = object()
+ ResultType = lambda: defaultdict(lambda: UNKNOWN_RANGE)
def __init__(self):
"""Initialize instance variable and gather globals name information."""
- self.result = defaultdict(lambda: UNKNOWN_RANGE)
- from pythran.analyses import UseOMP
- super(RangeValuesBase, self).__init__(Aliases, CFG, UseOMP)
+ super().__init__()
self.parent = self
def add(self, variable, range_):
diff --git a/contrib/python/pythran/pythran/analyses/scope.py b/contrib/python/pythran/pythran/analyses/scope.py
index f96783a09b9..56a4532533c 100644
--- a/contrib/python/pythran/pythran/analyses/scope.py
+++ b/contrib/python/pythran/pythran/analyses/scope.py
@@ -10,7 +10,24 @@ from collections import defaultdict
import gast as ast
-class Scope(FunctionAnalysis):
+def all_users(d):
+ """
+ Gather users of a definition, including users of an augmented assign.
+ """
+ visited = set()
+ dname = d.name()
+ def all_users_impl(d):
+ if d in visited:
+ return
+ visited.add(d)
+ for u in d.users():
+ if u.name() == dname:
+ yield u
+ yield from all_users_impl(u)
+ return all_users_impl(d)
+
+
+class Scope(FunctionAnalysis[AncestorsWithBody, DefUseChains]):
'''
Associate each variable declaration with the node that defines it
@@ -20,12 +37,12 @@ class Scope(FunctionAnalysis):
The result is a dictionary with nodes as key and set of names as values
'''
+ ResultType = lambda: defaultdict(set)
def __init__(self):
- self.result = defaultdict(set)
+ super().__init__()
self.decl_holders = (ast.FunctionDef, ast.For,
ast.excepthandler,
ast.While, ast.If, tuple)
- super(Scope, self).__init__(AncestorsWithBody, DefUseChains)
def visit_OMPDirective(self, node):
for dep in node.deps:
@@ -50,7 +67,7 @@ class Scope(FunctionAnalysis):
for name, defs in name_to_defs.items():
# get all refs to that name
refs = [d.node for d in defs] + [u.node
- for d in defs for u in d.users()]
+ for d in defs for u in all_users(d)]
# add OpenMP refs (well, the parent of the holding stmt)
refs.extend(self.ancestors[d][-3] # -3 to get the right parent
for d in self.openmp_deps.get(name, []))
diff --git a/contrib/python/pythran/pythran/analyses/static_expressions.py b/contrib/python/pythran/pythran/analyses/static_expressions.py
index c6c7fa52989..4a56c8b8df9 100644
--- a/contrib/python/pythran/pythran/analyses/static_expressions.py
+++ b/contrib/python/pythran/pythran/analyses/static_expressions.py
@@ -6,7 +6,6 @@ from pythran.passmanager import NodeAnalysis
class HasStaticExpression(NodeAnalysis):
def __init__(self):
self.result = False
- super(HasStaticExpression, self).__init__()
def visit_Attribute(self, node):
self.generic_visit(node)
@@ -17,10 +16,10 @@ class StaticExpressions(NodeAnalysis):
"""Identify constant expressions."""
+ ResultType = set
def __init__(self):
- self.result = set()
+ super().__init__()
self.constant_expressions = set()
- super(StaticExpressions, self).__init__()
def add(self, node):
self.result.add(node)
diff --git a/contrib/python/pythran/pythran/analyses/use_def_chain.py b/contrib/python/pythran/pythran/analyses/use_def_chain.py
index b88ce258ad5..beb9a9c82c2 100644
--- a/contrib/python/pythran/pythran/analyses/use_def_chain.py
+++ b/contrib/python/pythran/pythran/analyses/use_def_chain.py
@@ -17,33 +17,30 @@ class ExtendedDefUseChains(beniget.DefUseChains):
md.visit(self, node)
return super(ExtendedDefUseChains, self).visit(node)
-
-class UseDefChains(ModuleAnalysis):
+class DefUseChains(ModuleAnalysis):
"""
- Build use-define chains analysis for each variable.
+ Build define-use-define chains analysis for each variable.
"""
- def __init__(self):
- self.result = None
- super(UseDefChains, self).__init__(DefUseChains)
+ ResultType = type(None)
def visit_Module(self, node):
- udc = beniget.UseDefChains(self.def_use_chains)
- self.result = udc.chains
+ duc = ExtendedDefUseChains()
+ duc.visit(node)
+ self.result = duc
-class DefUseChains(ModuleAnalysis):
+class UseDefChains(ModuleAnalysis[DefUseChains]):
"""
- Build define-use-define chains analysis for each variable.
+ Build use-define chains analysis for each variable.
"""
- def __init__(self):
- self.result = None
- super(DefUseChains, self).__init__()
+ ResultType = type(None)
def visit_Module(self, node):
- duc = ExtendedDefUseChains()
- duc.visit(node)
- self.result = duc
+ udc = beniget.UseDefChains(self.def_use_chains)
+ self.result = udc.chains
+
+
diff --git a/contrib/python/pythran/pythran/analyses/use_omp.py b/contrib/python/pythran/pythran/analyses/use_omp.py
index 68da74307f8..b2d4dafba09 100644
--- a/contrib/python/pythran/pythran/analyses/use_omp.py
+++ b/contrib/python/pythran/pythran/analyses/use_omp.py
@@ -7,9 +7,7 @@ from pythran.passmanager import FunctionAnalysis
class UseOMP(FunctionAnalysis):
"""Detects if a function use openMP"""
- def __init__(self):
- self.result = False
- super(UseOMP, self).__init__()
+ ResultType = bool
def visit_OMPDirective(self, _):
self.result = True
diff --git a/contrib/python/pythran/pythran/analyses/yield_points.py b/contrib/python/pythran/pythran/analyses/yield_points.py
index a1069df3137..0f0f5f32f1b 100644
--- a/contrib/python/pythran/pythran/analyses/yield_points.py
+++ b/contrib/python/pythran/pythran/analyses/yield_points.py
@@ -7,9 +7,7 @@ from pythran.passmanager import FunctionAnalysis
class YieldPoints(FunctionAnalysis):
'''Gathers all yield points of a generator, if any.'''
- def __init__(self):
- self.result = list()
- super(YieldPoints, self).__init__()
+ ResultType = list
def visit_Yield(self, node):
self.result.append(node)
diff --git a/contrib/python/pythran/pythran/backend.py b/contrib/python/pythran/pythran/backend.py
index cb0f77c1ccd..e5728ab3e0e 100644
--- a/contrib/python/pythran/pythran/backend.py
+++ b/contrib/python/pythran/pythran/backend.py
@@ -42,9 +42,7 @@ class Python(Backend):
print('hello world')
'''
- def __init__(self):
- self.result = ''
- super(Python, self).__init__()
+ ResultType = str
def visit(self, node):
output = io.StringIO()
@@ -98,7 +96,7 @@ def cxx_loop(visit):
res = visit(self, node)
return res
- break_handler = "__no_breaking{0}".format(id(node))
+ break_handler = "__no_breaking{0}".format(len(self.break_handlers))
with pushpop(self.break_handlers, break_handler):
res = visit(self, node)
@@ -108,7 +106,9 @@ def cxx_loop(visit):
orelse_label = [Label(break_handler)]
else:
orelse_label = []
- return Block([res] + orelse + orelse_label)
+ skip = [node.target.id] if isinstance(node, ast.For) else []
+ return self.process_locals(node, Block([res] + orelse + orelse_label),
+ *skip)
return loop_visitor
@@ -116,25 +116,31 @@ class CachedTypeVisitor:
def __init__(self, other=None):
if other is None:
- self.rcache = dict()
self.mapping = dict()
+ self.typeid = dict()
+ self.combined = dict()
else:
- self.rcache = other.rcache.copy()
self.mapping = other.mapping.copy()
+ self.typeid = other.typeid.copy()
+ self.combined = other.combined.copy()
def __call__(self, node):
if node not in self.mapping:
t = node.generate(self)
if node not in self.mapping:
- if t in self.rcache:
- self.mapping[node] = self.rcache[t]
+ # Always re-evaluate LType as their evaluation depends on the
+ # callers (due to the recursion clause)
+ if type(node).__name__ == 'LType':
+ return t
else:
- self.rcache[t] = len(self.mapping)
- self.mapping[node] = len(self.mapping)
- return "__type{0}".format(self.mapping[node])
+ if t not in self.typeid:
+ self.typeid[t] = len(self.typeid)
+ self.mapping[node] = (t, self.typeid[t])
+
+ return "__type{0}".format(self.mapping[node][1])
def typedefs(self):
- kv = sorted(self.rcache.items(), key=lambda x: x[1])
+ kv = sorted(set(self.mapping.values()), key=lambda x: x[1])
L = list()
for k, v in kv:
typename = "__type" + str(v)
@@ -187,6 +193,7 @@ class CxxFunction(ast.NodeVisitor):
self.used_break = set()
self.ldecls = None
self.openmp_deps = set()
+ self.unique_counter = 0
if not (cfg.getboolean('backend', 'annotate') and
self.passmanager.code):
self.add_line_info = self.skip_line_info
@@ -196,6 +203,10 @@ class CxxFunction(ast.NodeVisitor):
def __getattr__(self, attr):
return getattr(self.parent, attr)
+ def unique(self):
+ self.unique_counter += 1
+ return self.unique_counter
+
# local declaration processing
def process_locals(self, node, node_visited, *skipped):
"""
@@ -209,7 +220,7 @@ class CxxFunction(ast.NodeVisitor):
return node_visited # no processing
locals_visited = []
- for varname in local_vars:
+ for varname in sorted(local_vars):
vartype = self.typeof(varname)
decl = Statement("{} {}".format(vartype, varname))
locals_visited.append(decl)
@@ -336,13 +347,13 @@ class CxxFunction(ast.NodeVisitor):
"typename {0}result_type".format(ffscope),
"{0}::operator()".format(self.fname),
formal_types, formal_args)
- ctx = CachedTypeVisitor(self.lctx)
+
operator_local_declarations = (
[Statement("{0} {1}".format(
- ctx(self.types[self.local_names[k]]), cxxid(k)))
- for k in self.ldecls]
+ self.lctx(self.types[self.local_names[k]]), cxxid(k)))
+ for k in sorted(self.ldecls)]
)
- dependent_typedefs = ctx.typedefs()
+ dependent_typedefs = self.lctx.typedefs()
operator_definition = FunctionBody(
templatize(operator_signature, formal_types),
Block(dependent_typedefs +
@@ -439,7 +450,9 @@ class CxxFunction(ast.NodeVisitor):
alltargets)
else:
assert isinstance(self.types[targets[0]],
- self.types.builder.Lazy)
+ (self.types.builder.Lazy,
+ self.types.builder.NamedType,
+ self.types.builder.ListType))
alltargets = '{} {}'.format(
self.types.builder.Lazy(
self.types.builder.NamedType(
@@ -502,7 +515,7 @@ class CxxFunction(ast.NodeVisitor):
is removed for iterator in case of yields statement in function.
"""
# Choose target variable for iterator (which is iterator type)
- local_target = "__target{0}".format(id(node))
+ local_target = "__target{0}".format(self.unique())
local_target_decl = self.typeof(
self.types.builder.IteratorOfType(local_iter_decl))
@@ -592,7 +605,7 @@ class CxxFunction(ast.NodeVisitor):
if self.is_in_collapse(node, args[upper_arg]):
upper_bound = upper_value # compatible with collapse
else:
- upper_bound = "__target{0}".format(id(node))
+ upper_bound = "__target{0}".format(self.unique())
islocal = (node.target.id not in self.openmp_deps and
node.target.id in self.scope[node] and
@@ -764,7 +777,8 @@ class CxxFunction(ast.NodeVisitor):
loop_body = Block([self.visit(stmt) for stmt in node.body])
# Declare local variables at the top of the loop body
- loop_body = self.process_locals(node, loop_body, node.target.id)
+ if not node.orelse:
+ loop_body = self.process_locals(node, loop_body, node.target.id)
iterable = self.visit(node.iter)
if self.can_use_c_for(node):
@@ -778,7 +792,7 @@ class CxxFunction(ast.NodeVisitor):
loop = [self.process_omp_attachements(node, autofor)]
else:
# Iterator declaration
- local_iter = "__iter{0}".format(id(node))
+ local_iter = "__iter{0}".format(self.unique())
local_iter_decl = self.types.builder.Assignable(
self.types[node.iter])
@@ -921,19 +935,12 @@ class CxxFunction(ast.NodeVisitor):
if not node.elts: # empty list
return '{}(pythonic::types::empty_list())'.format(self.typeof(node))
else:
-
+ node_type = self.types.builder.Assignable(self.types[node])
elts = [self.visit(n) for n in node.elts]
- node_type = self.types[node]
-
- # constructor disambiguation, clang++ workaround
- if len(elts) == 1:
- return "{0}({1}, pythonic::types::single_value())".format(
- self.typeof(self.types.builder.Assignable(node_type)),
- elts[0])
- else:
- return "{0}({{{1}}})".format(
- self.typeof(self.types.builder.Assignable(node_type)),
- ", ".join(elts))
+ return "{0}({{{1}}})".format(
+ self.typeof(node_type),
+ ", ".join("static_cast<typename {}::value_type>({})"
+ .format(self.typeof(node_type), elt) for elt in elts))
def visit_Set(self, node):
if not node.elts: # empty set
@@ -941,17 +948,10 @@ class CxxFunction(ast.NodeVisitor):
else:
elts = [self.visit(n) for n in node.elts]
node_type = self.types.builder.Assignable(self.types[node])
-
- # constructor disambiguation, clang++ workaround
- if len(elts) == 1:
- return "{0}({1}, pythonic::types::single_value())".format(
- self.typeof(node_type),
- elts[0])
- else:
- return "{0}{{{{{1}}}}}".format(
- self.typeof(node_type),
- ", ".join("static_cast<typename {}::value_type>({})"
- .format(self.typeof(node_type), elt) for elt in elts))
+ return "{0}({{{1}}})".format(
+ self.typeof(node_type),
+ ", ".join("static_cast<typename {}::value_type>({})"
+ .format(self.typeof(node_type), elt) for elt in elts))
def visit_Dict(self, node):
if not node.keys: # empty dict
@@ -1249,7 +1249,7 @@ class CxxGenerator(CxxFunction):
[Statement("{0} {1}".format(
ctx(self.types[self.local_names[k]]),
k))
- for k in self.ldecls] +
+ for k in sorted(self.ldecls)] +
[Statement("{0} {1}".format(v, k))
for k, v in self.extra_declarations] +
[Statement(
@@ -1354,7 +1354,8 @@ class CxxGenerator(CxxFunction):
return super(CxxGenerator, self).make_assign("", local_iter, iterable)
-class Cxx(Backend):
+class Cxx(Backend[Dependencies, GlobalDeclarations, Types, Scope, RangeValues,
+ PureExpressions, Immediates, Ancestors, StrictAliases]):
"""
Produces a C++ representation of the AST.
@@ -1392,13 +1393,7 @@ class Cxx(Backend):
}
}
"""
-
- def __init__(self):
- """ Basic initialiser gathering analysis informations. """
- self.result = None
- super(Cxx, self).__init__(Dependencies, GlobalDeclarations, Types,
- Scope, RangeValues, PureExpressions,
- Immediates, Ancestors, StrictAliases)
+ ResultType = type(None)
# mod
def visit_Module(self, node):
diff --git a/contrib/python/pythran/pythran/config.py b/contrib/python/pythran/pythran/config.py
index 053c26d3d81..d24cc1fa486 100644
--- a/contrib/python/pythran/pythran/config.py
+++ b/contrib/python/pythran/pythran/config.py
@@ -200,8 +200,6 @@ def make_extension(python, **extra):
if python:
extension['define_macros'].append('ENABLE_PYTHON_MODULE')
- extension['define_macros'].append(
- '__PYTHRAN__={}'.format(sys.version_info.major))
pythonic_dir = get_include()
@@ -313,10 +311,7 @@ def run():
Dump on stdout the config flags required to compile pythran-generated code.
'''
import argparse
- import distutils.ccompiler
- import distutils.sysconfig
import pythran
- import numpy
parser = argparse.ArgumentParser(
prog='pythran-config',
@@ -327,8 +322,26 @@ def run():
parser.add_argument('--compiler', action='store_true',
help='print default compiler')
- parser.add_argument('--cflags', action='store_true',
- help='print compilation flags')
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument('--cflags', action='store_true',
+ help='print compilation flags to compile extension '
+ 'modules directly')
+
+ # The with-BLAS variant could be added if there's user demand. Unclear
+ # if there are packages that need this, and also integrate with a build
+ # system for Python and NumPy flags. SciPy and scikit-image need the
+ # no-BLAS variant.
+ group.add_argument('--cflags-pythran-only', action='store_true',
+ help='print compilation flags for usage by a build '
+ 'system (doesn\'t include Python, NumPy or BLAS '
+ 'flags).'
+ )
+
+ parser.add_argument('--include-dir', action='store_true',
+ help=(
+ 'print Pythran include directory '
+ '(matches `pythran.get_include()`).')
+ )
parser.add_argument('--libs', action='store_true',
help='print linker flags')
@@ -349,6 +362,21 @@ def run():
args = parser.parse_args(sys.argv[1:])
+ # This should not rely on distutils/setuptools, or anything else not in the
+ # stdlib. Please don't add imports higher up.
+ if args.cflags_pythran_only:
+ compile_flags = [
+ "-DENABLE_PYTHON_MODULE",
+ "-DPYTHRAN_BLAS_NONE",
+ ]
+ print(" ".join(compile_flags), f"-I{get_include()}")
+ return None
+
+
+ import distutils.ccompiler
+ import distutils.sysconfig
+ import numpy
+
args.python = not args.no_python
output = []
@@ -416,6 +444,9 @@ def run():
if args.libs:
output.extend(ldflags)
+ if args.include_dir:
+ output.append(get_include())
+
if output:
print(' '.join(output))
diff --git a/contrib/python/pythran/pythran/cxxgen.py b/contrib/python/pythran/pythran/cxxgen.py
index 504a9636cfe..83b9432c405 100644
--- a/contrib/python/pythran/pythran/cxxgen.py
+++ b/contrib/python/pythran/pythran/cxxgen.py
@@ -28,6 +28,7 @@ Generator for C/C++.
#
from textwrap import dedent
+from pythran.config import cfg
from pythran.tables import pythran_ward
from pythran.spec import signatures_to_string
from pythran.utils import quote_cxxstring
@@ -580,9 +581,7 @@ class PythonModule(object):
self.functions.setdefault(name, []).append(func_descriptor)
def add_global_var(self, name, init):
- self.global_vars.append(name)
- self.python_implems.append(Assign('static PyObject* ' + name,
- 'to_python({})'.format(init)))
+ self.global_vars.append((name, 'to_python({})'.format(init)))
def __str__(self):
"""Generate (i.e. yield) the source code of the
@@ -591,9 +590,9 @@ class PythonModule(object):
themethods = []
theextraobjects = []
theoverloads = []
- for vname in self.global_vars:
+ for vname, vinit in self.global_vars:
theextraobjects.append(
- 'PyModule_AddObject(theModule, "{0}", {0});'.format(vname))
+ 'PyModule_AddObject(theModule, "{0}", {1});'.format(vname, vinit))
for fname, overloads in self.functions.items():
tryall = []
@@ -695,26 +694,19 @@ class PythonModule(object):
'''.format(methods="".join(m + "," for m in themethods)))
module = dedent('''
- #if PY_MAJOR_VERSION >= 3
- static struct PyModuleDef moduledef = {{
- PyModuleDef_HEAD_INIT,
- "{name}", /* m_name */
- {moduledoc}, /* m_doc */
- -1, /* m_size */
- Methods, /* m_methods */
- NULL, /* m_reload */
- NULL, /* m_traverse */
- NULL, /* m_clear */
- NULL, /* m_free */
- }};
- #define PYTHRAN_RETURN return theModule
- #define PYTHRAN_MODULE_INIT(s) PyInit_##s
- #else
- #define PYTHRAN_RETURN return
- #define PYTHRAN_MODULE_INIT(s) init##s
- #endif
+ static struct PyModuleDef moduledef = {{
+ PyModuleDef_HEAD_INIT,
+ "{name}", /* m_name */
+ {moduledoc}, /* m_doc */
+ -1, /* m_size */
+ Methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+ }};
PyMODINIT_FUNC
- PYTHRAN_MODULE_INIT({name})(void)
+ PyInit_{name}(void)
#ifndef _WIN32
__attribute__ ((visibility("default")))
#if defined(GNUC) && !defined(__clang__)
@@ -723,34 +715,32 @@ class PythonModule(object):
#endif
;
PyMODINIT_FUNC
- PYTHRAN_MODULE_INIT({name})(void) {{
+ PyInit_{name}(void) {{
import_array();
{import_umath}
- #if PY_MAJOR_VERSION >= 3
PyObject* theModule = PyModule_Create(&moduledef);
- #else
- PyObject* theModule = Py_InitModule3("{name}",
- Methods,
- {moduledoc}
- );
- #endif
if(! theModule)
- PYTHRAN_RETURN;
+ return theModule;
+ {freethreading}
PyObject * theDoc = Py_BuildValue("(ss)",
"{version}",
"{hash}");
if(! theDoc)
- PYTHRAN_RETURN;
+ return theModule;
PyModule_AddObject(theModule,
"__pythran__",
theDoc);
{extraobjects}
- PYTHRAN_RETURN;
+ return theModule;
}}
'''.format(name=self.name,
import_umath="import_umath();" if self.ufuncs else "",
extraobjects='\n'.join(theextraobjects),
+ freethreading="""
+ #ifdef Py_GIL_DISABLED
+ PyUnstable_Module_SetGIL(theModule, Py_MOD_GIL_NOT_USED);
+ #endif""" if cfg.getboolean("backend", "freethreading_compatible") else "",
**self.metadata))
body = (self.preamble +
diff --git a/contrib/python/pythran/pythran/cxxtypes.py b/contrib/python/pythran/pythran/cxxtypes.py
index e0d86e47f50..c30cd884dc1 100644
--- a/contrib/python/pythran/pythran/cxxtypes.py
+++ b/contrib/python/pythran/pythran/cxxtypes.py
@@ -89,6 +89,9 @@ std::declval<str>()))>::type>::type
>>> builder.ListType(builder.NamedType('int'))
pythonic::types::list<typename std::remove_reference<int>::type>
+ >>> builder.NDArrayType(builder.NamedType('int'), 1)
+ pythonic::types::ndarray<int, pythonic::types::pshape<long>>
+
>>> builder.SetType(builder.NamedType('int'))
pythonic::types::set<int>
@@ -117,7 +120,7 @@ std::declval<bool>()))
"""
A generic type object to be sub-classed
- The keyword arguments are used to built the internal representation
+ The keyword arguments are used to build the internal representation
one attribute per key with the associated value
"""
@@ -172,14 +175,11 @@ std::declval<bool>()))
"""
prefix = "__ptype{0}"
- count = 0
- def __init__(self, fun, ptype):
+ def __init__(self, fun, ptype, index):
super(PType, self).__init__(fun=fun,
type=ptype,
- name=PType.prefix.format(
- PType.count))
- PType.count += 1
+ name=PType.prefix.format(index))
def generate(self, ctx):
return ctx(self.type)
@@ -202,7 +202,9 @@ std::declval<bool>()))
return ctx(self.orig)
else:
self.isrec = True
- return ctx(self.final_type)
+ res = ctx(self.final_type)
+ self.isrec = False
+ return res
class InstantiatedType(Type):
"""
@@ -473,6 +475,20 @@ std::declval<bool>()))
return 'pythonic::types::dict<{},{}>'.format(ctx(self.of_key),
ctx(self.of_val))
+ class NDArrayType(DependentType):
+ '''
+ Type holding a numpy array without view
+ '''
+ def __init__(self, dtype, nbdims):
+ super(DependentType, self).__init__(of=dtype, nbdims=nbdims)
+
+ def generate(self, ctx):
+ return 'pythonic::types::ndarray<{}, pythonic::types::pshape<{}>>'.format(
+ ctx(self.of),
+ ", ".join((['long'] * self.nbdims))
+ )
+
+
class ContainerType(DependentType):
'''
Type of any container of stuff of the same type
diff --git a/contrib/python/pythran/pythran/dist.py b/contrib/python/pythran/pythran/dist.py
index b254dee7b97..45cf299ca0c 100644
--- a/contrib/python/pythran/pythran/dist.py
+++ b/contrib/python/pythran/pythran/dist.py
@@ -60,6 +60,7 @@ class PythranBuildExtMixIn(object):
'preprocessor': None,
'compiler_cxx': None,
'compiler_so': None,
+ 'compiler_so_cxx': None,
'compiler': None,
'linker_exe': None,
'linker_so': None,
@@ -72,7 +73,6 @@ class PythranBuildExtMixIn(object):
prev[key] = get_value(self.compiler, key)
else:
del prev[key]
-
# try hard to modify the compiler
if getattr(ext, 'cxx', None) is not None:
for comp in prev:
@@ -92,7 +92,7 @@ class PythranBuildExtMixIn(object):
return find_exe(exe, *args, **kwargs)
msvc._find_exe = _find_exe
- except ImportError:
+ except (AttributeError, ImportError):
pass
# In general, distutils uses -Wstrict-prototypes, but this option
@@ -175,7 +175,6 @@ class PythranExtension(Extension):
# Inserting at head so that user-specified config in CLI takes
# precedence.
kwargs['config'] = ['compiler.blas=none'] + kwargs.get('config', [])
-
cfg_ext = cfg.make_extension(python=True, **kwargs)
self.cxx = cfg_ext.pop('cxx', None)
self.cc = cfg_ext.pop('cc', None)
diff --git a/contrib/python/pythran/pythran/errors.py b/contrib/python/pythran/pythran/errors.py
index 02e978da4e7..c3416aa6b1e 100644
--- a/contrib/python/pythran/pythran/errors.py
+++ b/contrib/python/pythran/pythran/errors.py
@@ -16,8 +16,8 @@ class PythranSyntaxError(SyntaxError):
SyntaxError.__init__(self, msg)
if node:
self.filename = getattr(node, 'filename', None)
- self.lineno = node.lineno
- self.offset = node.col_offset
+ self.lineno = getattr(node, 'lineno', None)
+ self.offset = getattr(node, 'col_offset', None)
def __str__(self):
loc_info = self.lineno is not None and self.offset is not None
diff --git a/contrib/python/pythran/pythran/graph.py b/contrib/python/pythran/pythran/graph.py
index 7a29337e27a..9d152c80529 100644
--- a/contrib/python/pythran/pythran/graph.py
+++ b/contrib/python/pythran/pythran/graph.py
@@ -18,11 +18,11 @@ class DiGraph(object):
return (k for k, v in self._adjacency.items() if node in v)
def add_node(self, node):
- self._adjacency.setdefault(node, set())
+ self._adjacency.setdefault(node, {})
def add_edge(self, src, dest, **props):
self.add_node(dest)
- self._adjacency.setdefault(src, set()).add(dest)
+ self._adjacency.setdefault(src, {}).setdefault(dest, None)
self._edges[(src, dest)] = props
@property
@@ -33,7 +33,7 @@ class DiGraph(object):
return dest in self._adjacency[src]
def remove_edge(self, src, dest):
- self._adjacency[src].remove(dest)
+ self._adjacency[src].pop(dest)
del self._edges[(src, dest)]
def __len__(self):
diff --git a/contrib/python/pythran/pythran/intrinsic.py b/contrib/python/pythran/pythran/intrinsic.py
index c214bb419af..f7566e4de3d 100644
--- a/contrib/python/pythran/pythran/intrinsic.py
+++ b/contrib/python/pythran/pythran/intrinsic.py
@@ -5,6 +5,7 @@ from pythran.interval import UNKNOWN_RANGE, bool_values
from pythran.types.signature import extract_combiner
from pythran.typing import Any, Union, Fun, Generator
+from itertools import repeat, chain
import gast as ast
@@ -16,8 +17,21 @@ class UnboundValueType(object):
UnboundValue = UnboundValueType()
-# FIXME: we should find a better way to implement default behavior
-DefaultArgNum = 20
+class defaultlist:
+ DefaultLen = 20
+ def __init__(self, content=None, *, default):
+ self.content = [] if content is None else content
+ self.default = default
+
+ def __iter__(self):
+ return chain(self.content, repeat(self.default(), len(self) -
+ len(self.content)))
+
+ def __getitem__(self, i):
+ return self.content[i] if i < len(self.content) else self.default()
+
+ def __len__(self):
+ return max(len(self.content), defaultlist.DefaultLen)
class UpdateEffect(object):
@@ -51,7 +65,7 @@ class Intrinsic(object):
def __init__(self, **kwargs):
self.argument_effects = kwargs.get('argument_effects',
- (UpdateEffect(),) * DefaultArgNum)
+ defaultlist(default=UpdateEffect))
self.global_effects = kwargs.get('global_effects', False)
self.return_alias = kwargs.get('return_alias',
lambda x: {UnboundValue})
@@ -151,27 +165,27 @@ class UserFunction(FunctionIntr):
class ConstFunctionIntr(FunctionIntr):
def __init__(self, **kwargs):
kwargs.setdefault('argument_effects',
- (ReadEffect(),) * DefaultArgNum)
+ defaultlist(default=ReadEffect))
super(ConstFunctionIntr, self).__init__(**kwargs)
class ConstExceptionIntr(ConstFunctionIntr):
def __init__(self, **kwargs):
kwargs.setdefault('argument_effects',
- (ReadEffect(),) * DefaultArgNum)
+ defaultlist(default=ReadEffect))
super(ConstExceptionIntr, self).__init__(**kwargs)
class ReadOnceFunctionIntr(ConstFunctionIntr):
def __init__(self, **kwargs):
super(ReadOnceFunctionIntr, self).__init__(
- argument_effects=(ReadOnceEffect(),) * DefaultArgNum, **kwargs)
+ argument_effects=defaultlist(default=ReadOnceEffect), **kwargs)
class MethodIntr(FunctionIntr):
def __init__(self, *combiners, **kwargs):
kwargs.setdefault('argument_effects',
- (UpdateEffect(),) + (ReadEffect(),) * DefaultArgNum)
+ defaultlist((UpdateEffect(),), default=ReadEffect))
kwargs['combiners'] = combiners
super(MethodIntr, self).__init__(**kwargs)
@@ -184,14 +198,14 @@ class MethodIntr(FunctionIntr):
class ConstMethodIntr(MethodIntr):
def __init__(self, *combiners, **kwargs):
- kwargs.setdefault('argument_effects', (ReadEffect(),) * DefaultArgNum)
+ kwargs.setdefault('argument_effects', defaultlist(default=ReadEffect))
super(ConstMethodIntr, self).__init__(*combiners, **kwargs)
class ReadOnceMethodIntr(ConstMethodIntr):
def __init__(self, **kwargs):
super(ReadOnceMethodIntr, self).__init__(
- argument_effects=(ReadOnceEffect(),) * DefaultArgNum, **kwargs)
+ argument_effects=defaultlist(default=ReadOnceEffect), **kwargs)
class AttributeIntr(Intrinsic):
diff --git a/contrib/python/pythran/pythran/optimizations/comprehension_patterns.py b/contrib/python/pythran/pythran/optimizations/comprehension_patterns.py
index 1381e6a7ccf..2d85a1d1ddc 100644
--- a/contrib/python/pythran/pythran/optimizations/comprehension_patterns.py
+++ b/contrib/python/pythran/pythran/optimizations/comprehension_patterns.py
@@ -10,7 +10,7 @@ from pythran.utils import attr_to_path, path_to_attr
import gast as ast
-class ComprehensionPatterns(Transformation):
+class ComprehensionPatterns(Transformation[OptimizableComprehension]):
'''
Transforms list comprehension into intrinsics.
>>> import gast as ast
@@ -28,9 +28,6 @@ class ComprehensionPatterns(Transformation):
return ([0] * builtins.len(builtins.range(y)))
'''
- def __init__(self):
- Transformation.__init__(self, OptimizableComprehension)
-
def visit_Module(self, node):
self.use_itertools = False
self.generic_visit(node)
diff --git a/contrib/python/pythran/pythran/optimizations/constant_folding.py b/contrib/python/pythran/pythran/optimizations/constant_folding.py
index 5bf1f3e0233..b89b578b777 100644
--- a/contrib/python/pythran/pythran/optimizations/constant_folding.py
+++ b/contrib/python/pythran/pythran/optimizations/constant_folding.py
@@ -13,6 +13,7 @@ import builtins
import gast as ast
from copy import deepcopy
import logging
+import numpy
import sys
logger = logging.getLogger('pythran')
@@ -57,6 +58,22 @@ class PythranBuiltins(object):
return true_br if cond else false_br
@staticmethod
+ def StaticIfReturn(val):
+ return (None, val, None)
+
+ @staticmethod
+ def StaticIfNoReturn(val):
+ return (None, val, None)
+
+ @staticmethod
+ def StaticIfCont(val):
+ return (None, val, None)
+
+ @staticmethod
+ def StaticIfBreak(val):
+ return (None, val, None)
+
+ @staticmethod
def is_none(val):
return val is None
@@ -64,6 +81,10 @@ class PythranBuiltins(object):
def make_shape(*args):
return args
+ @staticmethod
+ def restrict_assign(expr, value):
+ numpy.copyto(expr, value)
+
class BreakLoop(Exception):
pass
@@ -86,7 +107,8 @@ class ConstEval(ast.NodeVisitor):
# stmt
def visit_Return(self, node):
- self.locals['@'] = node.value and self.visit(node.value)
+ if '@' not in self.locals:
+ self.locals['@'] = node.value and self.visit(node.value)
def visit_Delete(self, node):
if isinstance(node, ast.Name):
@@ -317,15 +339,17 @@ class ConstEval(ast.NodeVisitor):
elif type(op) is ast.LtE:
cond = curr <= right
elif type(op) is ast.Gt:
- cond = curr <= right
+ cond = curr > right
elif type(op) is ast.GtE:
- cond = curr <= right
+ cond = curr >= right
elif type(op) is ast.Is:
- cond = curr <= right
+ cond = curr is right
elif type(op) is ast.IsNot:
- cond = curr <= right
+ cond = curr is not right
elif type(op) is ast.In:
- cond = curr <= right
+ cond = curr in right
+ elif type(op) is ast.NotIn:
+ cond = curr not in right
else:
raise ValueError("invalid compare op")
if not cond:
@@ -387,7 +411,7 @@ class ConstEval(ast.NodeVisitor):
assert not self.locals
-class ConstantFolding(Transformation):
+class ConstantFolding(Transformation[ConstantExpressions]):
"""
Replace constant expression by their evaluation.
@@ -402,9 +426,6 @@ class ConstantFolding(Transformation):
return 4
"""
- def __init__(self):
- Transformation.__init__(self, ConstantExpressions)
-
def prepare(self, node):
assert isinstance(node, ast.Module)
self.env = {'builtins': builtins}
@@ -487,7 +508,7 @@ class ConstantFolding(Transformation):
return Transformation.generic_visit(self, node)
-class PartialConstantFolding(Transformation):
+class PartialConstantFolding(Transformation[ConstantExpressions]):
"""
Replace partially constant expression by their evaluation.
@@ -521,9 +542,6 @@ class PartialConstantFolding(Transformation):
return (n, m, n)
"""
- def __init__(self):
- Transformation.__init__(self, ConstantExpressions)
-
def fold_mult_left(self, node):
if not isinstance(node.left, (ast.List, ast.Tuple)):
return False
diff --git a/contrib/python/pythran/pythran/optimizations/dead_code_elimination.py b/contrib/python/pythran/pythran/optimizations/dead_code_elimination.py
index fd1a6b91dc3..e6d0ff984c0 100644
--- a/contrib/python/pythran/pythran/optimizations/dead_code_elimination.py
+++ b/contrib/python/pythran/pythran/optimizations/dead_code_elimination.py
@@ -20,7 +20,7 @@ class ClumsyOpenMPDependencyHandler(ast.NodeVisitor):
return node
-class DeadCodeElimination(Transformation):
+class DeadCodeElimination(Transformation[PureExpressions, DefUseChains, Ancestors]):
"""
Remove useless statement like:
- assignment to unused variables
@@ -57,9 +57,7 @@ class DeadCodeElimination(Transformation):
return 1
"""
def __init__(self):
- super(DeadCodeElimination, self).__init__(PureExpressions,
- DefUseChains,
- Ancestors)
+ super().__init__()
self.blacklist = set()
def used_target(self, node):
diff --git a/contrib/python/pythran/pythran/optimizations/fast_gexpr.py b/contrib/python/pythran/pythran/optimizations/fast_gexpr.py
index 8fe23b33d08..58b437de5c3 100644
--- a/contrib/python/pythran/pythran/optimizations/fast_gexpr.py
+++ b/contrib/python/pythran/pythran/optimizations/fast_gexpr.py
@@ -8,10 +8,6 @@ import gast as ast
class FastGExpr(Transformation):
- def __init__(self):
- self.update = False
- super(FastGExpr, self).__init__(InterproceduralAliases)
-
def as_gexpr(self, node):
if not isinstance(node, ast.Subscript):
return None
@@ -39,10 +35,20 @@ class FastGExpr(Transformation):
if isinstance(value, ast.Subscript):
if not isinstance(value.value, ast.Name):
return True
+ # Lazy loading of interprocedural_aliases as it's a costly analysis
+ if not self.interprocedural_aliases:
+ self.interprocedural_aliases = self.passmanager.gather(InterproceduralAliases, self.current_module)
return not self.interprocedural_aliases[gexpr[0]].isdisjoint(self.interprocedural_aliases[value.value])
return True
+ def visit_Module(self, node):
+ self.current_module = node
+ self.interprocedural_aliases = None
+ new_node = self.generic_visit(node)
+ self.interprocedural_aliases = None
+ self.current_module = None
+ return new_node
def visit_Assign(self, node):
targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
diff --git a/contrib/python/pythran/pythran/optimizations/forward_substitution.py b/contrib/python/pythran/pythran/optimizations/forward_substitution.py
index 4d34f8c05e5..79d0c8006de 100644
--- a/contrib/python/pythran/pythran/optimizations/forward_substitution.py
+++ b/contrib/python/pythran/pythran/optimizations/forward_substitution.py
@@ -9,6 +9,7 @@ from pythran.passmanager import Transformation
import pythran.graph as graph
from collections import defaultdict
+from copy import deepcopy
import gast as ast
try:
@@ -45,7 +46,8 @@ class Remover(ast.NodeTransformer):
return node
-class ForwardSubstitution(Transformation):
+class ForwardSubstitution(Transformation[LazynessAnalysis, UseDefChains, DefUseChains,
+ Ancestors, CFG, Literals]):
"""
Replace variable that can be computed later.
@@ -80,12 +82,7 @@ class ForwardSubstitution(Transformation):
def __init__(self):
""" Satisfy dependencies on others analyses. """
- super(ForwardSubstitution, self).__init__(LazynessAnalysis,
- UseDefChains,
- DefUseChains,
- Ancestors,
- CFG,
- Literals)
+ super().__init__()
self.to_remove = None
def visit_FunctionDef(self, node):
@@ -98,6 +95,31 @@ class ForwardSubstitution(Transformation):
Remover(self.to_remove).visit(node)
return node
+ def visit_AugAssign(self, node):
+ # Separate case for augassign, where we only handle situation where the
+ # def is a literal (any kind) with a single use (this AugAssign).
+ self.generic_visit(node)
+ if not isinstance(node.target, ast.Name):
+ return node
+
+ defs = self.use_def_chains[node.target]
+ name_defs = [dnode for dnode in defs if isinstance(dnode.node, ast.Name)]
+ if len(name_defs) != 1:
+ return node
+ name_def, = name_defs
+ dnode = name_def.node
+ parent = self.ancestors[dnode][-1]
+ if not isinstance(parent, ast.Assign):
+ return node
+
+ try:
+ ast.literal_eval(parent.value)
+ except ValueError:
+ return node
+
+ self.update = True
+ return ast.Assign([node.target], ast.BinOp(deepcopy(parent.value), node.op, node.value))
+
def visit_Name(self, node):
if not isinstance(node.ctx, ast.Load):
return node
diff --git a/contrib/python/pythran/pythran/optimizations/inline_builtins.py b/contrib/python/pythran/pythran/optimizations/inline_builtins.py
index b8cd26f7a1a..4bddbf480d8 100644
--- a/contrib/python/pythran/pythran/optimizations/inline_builtins.py
+++ b/contrib/python/pythran/pythran/optimizations/inline_builtins.py
@@ -11,7 +11,7 @@ from copy import deepcopy
import gast as ast
-class InlineBuiltins(Transformation):
+class InlineBuiltins(Transformation[Aliases]):
"""
Replace some builtins by their bodies.
@@ -34,9 +34,6 @@ class InlineBuiltins(Transformation):
return [bar(1), bar(2)]
"""
- def __init__(self):
- Transformation.__init__(self, Aliases)
-
def inlineBuiltinsXMap(self, node):
self.update = True
diff --git a/contrib/python/pythran/pythran/optimizations/inlining.py b/contrib/python/pythran/pythran/optimizations/inlining.py
index dac8dc6b2f4..803066028c7 100644
--- a/contrib/python/pythran/pythran/optimizations/inlining.py
+++ b/contrib/python/pythran/pythran/optimizations/inlining.py
@@ -7,7 +7,7 @@ import gast as ast
import copy
-class Inlining(Transformation):
+class Inlining(Transformation[Inlinable, Aliases]):
"""
Inline one line functions.
@@ -36,10 +36,10 @@ __pythran_inlinefooa0)) * (__pythran_inlinefoob1 + \
def __init__(self):
""" fun : Function {name :body} for inlinable functions. """
+ super().__init__()
self.update = False
self.defs = list()
self.call_count = 0
- super(Inlining, self).__init__(Inlinable, Aliases)
def visit_Stmt(self, node):
""" Add new variable definition before the Statement. """
@@ -54,12 +54,21 @@ __pythran_inlinefooa0)) * (__pythran_inlinefoob1 + \
visit_AugAssign = visit_Stmt
visit_Print = visit_Stmt
visit_For = visit_Stmt
- visit_While = visit_Stmt
visit_If = visit_Stmt
visit_With = visit_Stmt
visit_Assert = visit_Stmt
visit_Expr = visit_Stmt
+ def visit_While(self, node):
+ # FIXME: we're only preventing inlining within test because it's
+ # difficult to compute predecessors of the test while also handling
+ # exceptions. We could use the cfg analysis for this but I'm a bit lazy
+ # and it's not a critical optimization.
+ test, node.test = node.test, None
+ self.generic_visit(node)
+ node.test = test
+ return node
+
def visit_Call(self, node):
"""
Replace function call by inlined function's body.
@@ -86,11 +95,12 @@ __pythran_inlinefooa0)) * (__pythran_inlinefoob1 + \
annotation=None, type_comment=None)
self.defs.append(ast.Assign(targets=[new_var],
value=arg_call,
- type_comment=None))
+ type_comment=None))
arg_to_value[arg_fun.id] = ast.Name(id=v_name,
ctx=ast.Load(),
annotation=None,
type_comment=None)
+
self.call_count += 1
return Inliner(arg_to_value).visit(to_inline.body[0])
return node
diff --git a/contrib/python/pythran/pythran/optimizations/iter_transformation.py b/contrib/python/pythran/pythran/optimizations/iter_transformation.py
index 9af88c32cac..42b03f346ca 100644
--- a/contrib/python/pythran/pythran/optimizations/iter_transformation.py
+++ b/contrib/python/pythran/pythran/optimizations/iter_transformation.py
@@ -14,7 +14,7 @@ EQUIVALENT_ITERATORS = {
}
-class IterTransformation(Transformation):
+class IterTransformation(Transformation[PotentialIterator, Aliases]):
"""
Replaces expressions by iterators when possible.
@@ -36,10 +36,6 @@ class IterTransformation(Transformation):
return foo(n)
"""
- def __init__(self):
- """Gather required information."""
- Transformation.__init__(self, PotentialIterator, Aliases)
-
def find_matching_builtin(self, node):
"""
Return matched keyword.
diff --git a/contrib/python/pythran/pythran/optimizations/list_comp_to_genexp.py b/contrib/python/pythran/pythran/optimizations/list_comp_to_genexp.py
index 0fd7587f223..346aa0046f3 100644
--- a/contrib/python/pythran/pythran/optimizations/list_comp_to_genexp.py
+++ b/contrib/python/pythran/pythran/optimizations/list_comp_to_genexp.py
@@ -6,7 +6,7 @@ from pythran.passmanager import Transformation
import gast as ast
-class ListCompToGenexp(Transformation):
+class ListCompToGenexp(Transformation[PotentialIterator]):
'''
Transforms list comprehension into genexp
>>> import gast as ast
@@ -25,8 +25,6 @@ def bar(n): \\n\
def bar(n):
return foo((x for x in builtins.range(n)))
'''
- def __init__(self):
- Transformation.__init__(self, PotentialIterator)
def visit_ListComp(self, node):
self.generic_visit(node)
diff --git a/contrib/python/pythran/pythran/optimizations/list_to_tuple.py b/contrib/python/pythran/pythran/optimizations/list_to_tuple.py
index 87261ab7330..7c49b65b98a 100644
--- a/contrib/python/pythran/pythran/optimizations/list_to_tuple.py
+++ b/contrib/python/pythran/pythran/optimizations/list_to_tuple.py
@@ -24,7 +24,7 @@ def totuple(node):
return ast.Tuple(node.elts, node.ctx)
-class ListToTuple(Transformation):
+class ListToTuple(Transformation[Aliases, FixedSizeList]):
"""
Replace list nodes by tuple nodes when possible
@@ -39,9 +39,6 @@ class ListToTuple(Transformation):
import numpy
return numpy.ones((n, n))
"""
- def __init__(self):
- self.update = False
- super(ListToTuple, self).__init__(Aliases, FixedSizeList)
def visit_AugAssign(self, node):
if not islist(node.value):
diff --git a/contrib/python/pythran/pythran/optimizations/modindex.py b/contrib/python/pythran/pythran/optimizations/modindex.py
index 2a046ea10fe..99917db50ae 100644
--- a/contrib/python/pythran/pythran/optimizations/modindex.py
+++ b/contrib/python/pythran/pythran/optimizations/modindex.py
@@ -9,7 +9,8 @@ import gast as ast
from copy import deepcopy
-class ModIndex(Transformation):
+class ModIndex(Transformation[UseDefChains, Ancestors, Aliases, RangeValues,
+ Identifiers]):
'''
Simplify modulo on loop index
@@ -33,8 +34,7 @@ class ModIndex(Transformation):
'''
def __init__(self):
- Transformation.__init__(self, UseDefChains, Ancestors, Aliases,
- RangeValues, Identifiers)
+ super().__init__()
self.loops_mod = dict()
def single_def(self, node):
diff --git a/contrib/python/pythran/pythran/optimizations/pattern_transform.py b/contrib/python/pythran/pythran/optimizations/pattern_transform.py
index 1638bbd9da3..e18d49c3af7 100644
--- a/contrib/python/pythran/pythran/optimizations/pattern_transform.py
+++ b/contrib/python/pythran/pythran/optimizations/pattern_transform.py
@@ -334,8 +334,8 @@ class PlaceholderReplace(Transformation):
def __init__(self, placeholders):
""" Store placeholders value collected. """
+ super().__init__()
self.placeholders = placeholders
- super(PlaceholderReplace, self).__init__()
def visit(self, node):
""" Replace the placeholder if it is one or continue. """
@@ -352,10 +352,6 @@ class PatternTransform(Transformation):
Based on BaseMatcher to search correct pattern.
"""
- def __init__(self):
- """ Initialize the Basematcher to search for placeholders. """
- super(PatternTransform, self).__init__()
-
def visit_Module(self, node):
self.extra_imports = []
self.generic_visit(node)
diff --git a/contrib/python/pythran/pythran/optimizations/range_based_simplify.py b/contrib/python/pythran/pythran/optimizations/range_based_simplify.py
index 3add95211ea..f1995698eef 100644
--- a/contrib/python/pythran/pythran/optimizations/range_based_simplify.py
+++ b/contrib/python/pythran/pythran/optimizations/range_based_simplify.py
@@ -8,7 +8,7 @@ from math import isinf
from copy import deepcopy
-class RangeBasedSimplify(Transformation):
+class RangeBasedSimplify(Transformation[RangeValues]):
'''
Simplify expressions based on range analysis
@@ -40,9 +40,6 @@ class RangeBasedSimplify(Transformation):
return (2, 1)
'''
- def __init__(self):
- Transformation.__init__(self, RangeValues)
-
def visit_OMPDirective(self, node):
return node
diff --git a/contrib/python/pythran/pythran/optimizations/remove_dead_functions.py b/contrib/python/pythran/pythran/optimizations/remove_dead_functions.py
index 5aff9a28ddb..55df57af0de 100644
--- a/contrib/python/pythran/pythran/optimizations/remove_dead_functions.py
+++ b/contrib/python/pythran/pythran/optimizations/remove_dead_functions.py
@@ -5,7 +5,7 @@ from pythran.passmanager import Transformation
import pythran.metadata as metadata
-class RemoveDeadFunctions(Transformation):
+class RemoveDeadFunctions(Transformation[DefUseChains]):
"""
Remove useless local functions
@@ -24,9 +24,6 @@ class RemoveDeadFunctions(Transformation):
<BLANKLINE>
"""
- def __init__(self):
- super(RemoveDeadFunctions, self).__init__(DefUseChains)
-
def visit_FunctionDef(self, node):
if metadata.get(node, metadata.Local):
if not self.def_use_chains.chains[node].users():
diff --git a/contrib/python/pythran/pythran/optimizations/square.py b/contrib/python/pythran/pythran/optimizations/square.py
index 85d8cc2836f..062045edc20 100644
--- a/contrib/python/pythran/pythran/optimizations/square.py
+++ b/contrib/python/pythran/pythran/optimizations/square.py
@@ -38,9 +38,6 @@ class Square(Transformation):
[AST_any(), ast.Constant(2, None)],
[])
- def __init__(self):
- Transformation.__init__(self)
-
def replace(self, value):
self.update = self.need_import = True
module_name = ast.Name(mangle('numpy'), ast.Load(), None, None)
diff --git a/contrib/python/pythran/pythran/optimizations/tuple_to_shape.py b/contrib/python/pythran/pythran/optimizations/tuple_to_shape.py
index 7a3e5f92590..797c3400b7c 100644
--- a/contrib/python/pythran/pythran/optimizations/tuple_to_shape.py
+++ b/contrib/python/pythran/pythran/optimizations/tuple_to_shape.py
@@ -24,7 +24,7 @@ def toshape(node):
return ast.Call(b, node.elts, [])
-class TupleToShape(Transformation):
+class TupleToShape(Transformation[Aliases]):
"""
Replace tuple nodes by shape when relevant
@@ -39,10 +39,6 @@ class TupleToShape(Transformation):
import numpy
return numpy.ones(builtins.pythran.make_shape(n, 4))
"""
- def __init__(self):
- self.update = False
- super(TupleToShape, self).__init__(Aliases)
-
def visit_Call(self, node):
func_aliases = self.aliases.get(node.func, None)
if func_aliases is not None:
diff --git a/contrib/python/pythran/pythran/passmanager.py b/contrib/python/pythran/pythran/passmanager.py
index e4ea253888f..a4c1191a0d9 100644
--- a/contrib/python/pythran/pythran/passmanager.py
+++ b/contrib/python/pythran/pythran/passmanager.py
@@ -14,6 +14,7 @@ There are two kinds of passes: transformations and analysis.
import gast as ast
import os
import re
+from collections.abc import Iterable
from time import time
@@ -23,7 +24,7 @@ def uncamel(name):
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
-class AnalysisContext(object):
+class AnalysisContext:
"""
Class that stores the hierarchy of node visited.
@@ -38,7 +39,16 @@ class AnalysisContext(object):
self.function = None
-class ContextManager(object):
+class ContextManagerMeta(type):
+ def __getitem__(cls, Dependencies):
+ class CustomContextManager(cls):
+ if isinstance(Dependencies, Iterable):
+ deps = Dependencies
+ else:
+ deps = Dependencies,
+ return CustomContextManager
+
+class ContextManager(metaclass=ContextManagerMeta):
"""
Class to be inherited from to add automatic update of context.
@@ -46,11 +56,11 @@ class ContextManager(object):
The optional analysis dependencies are listed in `dependencies'.
"""
- def __init__(self, *dependencies):
+ deps = ()
+
+ def __init__(self):
""" Create default context and save all dependencies. """
- self.deps = dependencies
self.verify_dependencies()
- super(ContextManager, self).__init__()
def attach(self, pm, ctx=None):
self.passmanager = pm
@@ -101,19 +111,14 @@ class ContextManager(object):
a.attach(self.passmanager, self.ctx)
return a.run(node)
-
class Analysis(ContextManager, ast.NodeVisitor):
"""
A pass that does not change its content but gathers informations about it.
"""
- def __init__(self, *dependencies):
- '''`dependencies' holds the type of all analysis required by this
- analysis. `self.result' must be set prior to calling this
- constructor.'''
- assert hasattr(self, "result"), (
- "An analysis must have a result attribute when initialized")
+ def __init__(self):
+ super().__init__()
+ self.result = type(self).ResultType()
self.update = False
- ContextManager.__init__(self, *dependencies)
def run(self, node):
key = node, type(self)
@@ -131,10 +136,9 @@ class Analysis(ContextManager, ast.NodeVisitor):
self.display(self.run(node))
return False, node
-
class ModuleAnalysis(Analysis):
-
"""An analysis that operates on a whole module."""
+
def run(self, node):
if not isinstance(node, ast.Module):
if self.ctx.module is None:
@@ -143,9 +147,7 @@ class ModuleAnalysis(Analysis):
node = self.ctx.module
return super(ModuleAnalysis, self).run(node)
-
class FunctionAnalysis(Analysis):
-
"""An analysis that operates on a function."""
def run(self, node):
@@ -164,14 +166,10 @@ class FunctionAnalysis(Analysis):
node = self.ctx.function
return super(FunctionAnalysis, self).run(node)
-
class NodeAnalysis(Analysis):
-
"""An analysis that operates on any node."""
-
class Backend(ModuleAnalysis):
-
"""A pass that produces code from an AST."""
@@ -179,9 +177,9 @@ class Transformation(ContextManager, ast.NodeTransformer):
"""A pass that updates its content."""
- def __init__(self, *args, **kwargs):
+ def __init__(self):
""" Initialize the update used to know if update happened. """
- super(Transformation, self).__init__(*args, **kwargs)
+ super(Transformation, self).__init__()
self.update = False
def run(self, node):
@@ -201,7 +199,7 @@ class Transformation(ContextManager, ast.NodeTransformer):
return self.update, new_node
-class PassManager(object):
+class PassManager:
'''
Front end to the pythran pass system.
'''
diff --git a/contrib/python/pythran/pythran/pythonic/builtins/oct.hpp b/contrib/python/pythran/pythran/pythonic/builtins/oct.hpp
index 9150c790633..791d2811563 100644
--- a/contrib/python/pythran/pythran/pythonic/builtins/oct.hpp
+++ b/contrib/python/pythran/pythran/pythonic/builtins/oct.hpp
@@ -17,13 +17,7 @@ namespace builtins
types::str oct(T const &v)
{
std::ostringstream oss;
- oss <<
-#if defined(__PYTHRAN__) && __PYTHRAN__ == 3
- "0o"
-#else
- '0'
-#endif
- << std::oct << v;
+ oss << "0o" << std::oct << v;
return oss.str();
}
} // namespace builtins
diff --git a/contrib/python/pythran/pythran/pythonic/builtins/str/splitlines.hpp b/contrib/python/pythran/pythran/pythonic/builtins/str/splitlines.hpp
new file mode 100644
index 00000000000..91fa344ada5
--- /dev/null
+++ b/contrib/python/pythran/pythran/pythonic/builtins/str/splitlines.hpp
@@ -0,0 +1,35 @@
+#ifndef PYTHONIC_BUILTIN_STR_SPLITLINES_HPP
+#define PYTHONIC_BUILTIN_STR_SPLITLINES_HPP
+
+#include "pythonic/include/builtins/str/splitlines.hpp"
+
+#include "pythonic/types/list.hpp"
+#include "pythonic/types/str.hpp"
+#include "pythonic/utils/functor.hpp"
+
+PYTHONIC_NS_BEGIN
+
+namespace builtins
+{
+
+ namespace str
+ {
+
+ inline types::list<types::str> splitlines(types::str const &in, bool keepends)
+ {
+ auto const& in_ = in.chars();
+ types::list<types::str> res(0);
+ size_t current = 0;
+ const ssize_t adjust = keepends ? 1 : 0;
+ while (current < (size_t)in.size()) {
+ size_t next = in_.find("\n", current);
+ res.push_back(in_.substr(current, next - current + adjust));
+ current = (next == types::str::npos) ? next : (next + 1);
+ }
+ return res;
+ }
+
+ } // namespace str
+} // namespace builtins
+PYTHONIC_NS_END
+#endif
diff --git a/contrib/python/pythran/pythran/pythonic/include/builtins/str/splitlines.hpp b/contrib/python/pythran/pythran/pythonic/include/builtins/str/splitlines.hpp
new file mode 100644
index 00000000000..8bc7a3c45c0
--- /dev/null
+++ b/contrib/python/pythran/pythran/pythonic/include/builtins/str/splitlines.hpp
@@ -0,0 +1,22 @@
+#ifndef PYTHONIC_INCLUDE_BUILTIN_STR_SPLITLINES_HPP
+#define PYTHONIC_INCLUDE_BUILTIN_STR_SPLITLINES_HPP
+
+#include "pythonic/include/types/list.hpp"
+#include "pythonic/include/types/str.hpp"
+#include "pythonic/include/utils/functor.hpp"
+
+PYTHONIC_NS_BEGIN
+
+namespace builtins
+{
+
+ namespace str
+ {
+
+ types::list<types::str> splitlines(types::str const &in, bool keepends = false);
+
+ DEFINE_FUNCTOR(pythonic::builtins::str, splitlines);
+ }
+}
+PYTHONIC_NS_END
+#endif
diff --git a/contrib/python/pythran/pythran/pythonic/include/numpy/array.hpp b/contrib/python/pythran/pythran/pythonic/include/numpy/array.hpp
index fbc6081c7d6..d64ec19f4cc 100644
--- a/contrib/python/pythran/pythran/pythonic/include/numpy/array.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/numpy/array.hpp
@@ -53,6 +53,13 @@ namespace numpy
typename types::array_base<T, N, V>::shape_t>
array(types::array_base<T, N, V> &&, dtype d = dtype());
+ template <class... Ts>
+ auto array(std::tuple<Ts...> t)
+ -> decltype(array(types::to_array<typename __combined<Ts...>::type>(t)))
+ {
+ return array(types::to_array<typename __combined<Ts...>::type>(t));
+ }
+
DEFINE_FUNCTOR(pythonic::numpy, array);
} // namespace numpy
PYTHONIC_NS_END
diff --git a/contrib/python/pythran/pythran/pythonic/include/numpy/dot.hpp b/contrib/python/pythran/pythran/pythonic/include/numpy/dot.hpp
index 1d7b2a2576a..41aab6226c4 100644
--- a/contrib/python/pythran/pythran/pythonic/include/numpy/dot.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/numpy/dot.hpp
@@ -29,15 +29,15 @@ struct is_strided {
template <class E>
struct is_blas_array {
static constexpr bool value =
- pythonic::types::is_array<E>::value &&
+ pythonic::types::has_buffer<E>::value &&
is_blas_type<typename pythonic::types::dtype_of<E>::type>::value &&
!is_strided<E>::value;
};
template <class E>
-struct is_blas_expr {
+struct is_blas_view {
static constexpr bool value =
- pythonic::types::is_array<E>::value &&
+ pythonic::types::has_buffer<E>::value &&
is_blas_type<typename pythonic::types::dtype_of<E>::type>::value;
};
@@ -56,7 +56,7 @@ namespace numpy
typename std::enable_if<
types::is_numexpr_arg<E>::value && types::is_numexpr_arg<F>::value &&
E::value == 1 && F::value == 1 &&
- (!is_blas_expr<E>::value || !is_blas_expr<F>::value ||
+ (!is_blas_view<E>::value || !is_blas_view<F>::value ||
!std::is_same<typename E::dtype, typename F::dtype>::value),
typename __combined<typename E::dtype, typename F::dtype>::type>::type
dot(E const &e, F const &f);
@@ -102,7 +102,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, float>::value &&
std::is_same<typename F::dtype, float>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
float>::type
dot(E const &e, F const &f);
@@ -112,7 +112,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, double>::value &&
std::is_same<typename F::dtype, double>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
double>::type
dot(E const &e, F const &f);
@@ -122,7 +122,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, std::complex<float>>::value &&
std::is_same<typename F::dtype, std::complex<float>>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
std::complex<float>>::type
dot(E const &e, F const &f);
@@ -132,7 +132,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, std::complex<double>>::value &&
std::is_same<typename F::dtype, std::complex<double>>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
std::complex<double>>::type
dot(E const &e, F const &f);
diff --git a/contrib/python/pythran/pythran/pythonic/include/numpy/float128.hpp b/contrib/python/pythran/pythran/pythonic/include/numpy/float128.hpp
index a0051f56bc4..8f4819e2b10 100644
--- a/contrib/python/pythran/pythran/pythonic/include/numpy/float128.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/numpy/float128.hpp
@@ -12,7 +12,11 @@ namespace numpy
namespace details
{
- long double float128();
+ inline long double float128()
+ {
+ return {};
+ }
+
template <class V>
long double float128(V v);
} // namespace details
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/NoneType.hpp b/contrib/python/pythran/pythran/pythonic/include/types/NoneType.hpp
index b60dab4d715..9dfdbd77bc9 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/NoneType.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/NoneType.hpp
@@ -15,6 +15,14 @@ namespace types
struct none_type {
none_type();
intptr_t id() const;
+ bool operator==(none_type) const
+ {
+ return true;
+ }
+ explicit operator bool() const
+ {
+ return false;
+ }
};
inline std::ostream &operator<<(std::ostream &os, none_type const &)
@@ -25,7 +33,7 @@ namespace types
template <class T, bool is_fundamental = std::is_fundamental<T>::value>
struct none;
- /* Type adapator to simulate an option type
+ /* Type adaptor to simulate an option type
*
* see http://en.wikipedia.org/wiki/Option_type
*/
@@ -60,10 +68,13 @@ namespace types
explicit operator bool() const;
- intptr_t id() const;
+ explicit operator T const &() const
+ {
+ assert(!is_none);
+ return *static_cast<T const *>(this);
+ }
- template <class T0>
- friend std::ostream &operator<<(std::ostream &os, none<T0, false> const &);
+ intptr_t id() const;
};
/* specialization of none for integral types we cannot derive from
@@ -75,7 +86,7 @@ namespace types
return !static_cast<P const *>(this)->is_none &&
static_cast<P const *>(this)->data;
}
- operator T() const
+ operator T const &() const
{
return static_cast<P const *>(this)->data;
}
@@ -91,64 +102,6 @@ namespace types
template <class T>
struct none<T, true> : none_data<none<T, true>, T> {
T data;
- template <class T1>
- friend std::ostream &operator<<(std::ostream &, none<T1, true> const &);
- template <class T1>
- friend T1 operator+(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend T1 operator+(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<T1, true> operator+(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend bool operator>(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend bool operator>(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<bool> operator>(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend bool operator>=(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend bool operator>=(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<bool> operator>=(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend bool operator<(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend bool operator<(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<bool> operator<(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend bool operator<=(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend bool operator<=(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<bool> operator<=(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend T1 operator-(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend T1 operator-(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<T1, true> operator-(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend T1 operator*(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend T1 operator*(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<T1, true> operator*(none<T1, true> const &t0,
- none<T1, true> const &t1);
- template <class T1>
- friend T1 operator/(none<T1, true> const &t0, T1 const &t1);
- template <class T1>
- friend T1 operator/(T1 const &t0, none<T1, true> const &t1);
- template <class T1>
- friend none<T1, true> operator/(none<T1, true> const &t0,
- none<T1, true> const &t1);
template <class T1>
none &operator+=(T1 other);
@@ -159,7 +112,6 @@ namespace types
template <class T1>
none &operator/=(T1 other);
- public:
bool is_none;
none();
none(none_type const &);
@@ -181,65 +133,64 @@ namespace types
return {static_cast<T1>(data)};
}
};
- template <class T>
- T operator+(none<T, true> const &t0, T const &t1);
- template <class T>
- T operator+(T const &t0, none<T, true> const &t1);
- template <class T>
- none<T, true> operator+(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- bool operator>(none<T, true> const &t0, T const &t1);
- template <class T>
- bool operator>(T const &t0, none<T, true> const &t1);
- template <class T>
- none<bool> operator>(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- bool operator>=(none<T, true> const &t0, T const &t1);
- template <class T>
- bool operator>=(T const &t0, none<T, true> const &t1);
- template <class T>
- none<bool> operator>=(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- bool operator<(none<T, true> const &t0, T const &t1);
- template <class T>
- bool operator<(T const &t0, none<T, true> const &t1);
- template <class T>
- none<bool> operator<(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- bool operator<=(none<T, true> const &t0, T const &t1);
- template <class T>
- bool operator<=(T const &t0, none<T, true> const &t1);
- template <class T>
- none<bool> operator<=(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- T operator-(none<T, true> const &t0, T const &t1);
- template <class T>
- T operator-(T const &t0, none<T, true> const &t1);
- template <class T>
- none<T, true> operator-(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- T operator*(none<T, true> const &t0, T const &t1);
- template <class T>
- T operator*(T const &t0, none<T, true> const &t1);
- template <class T>
- none<T, true> operator*(none<T, true> const &t0, none<T, true> const &t1);
- template <class T>
- T operator/(none<T, true> const &t0, T const &t1);
- template <class T>
- T operator/(T const &t0, none<T, true> const &t1);
- template <class T>
- none<T, true> operator/(none<T, true> const &t0, none<T, true> const &t1);
+
+#define NONE_OPERATOR_OVERLOAD(op) \
+ template <class T> \
+ auto operator op(none<T> const &t0, T const &t1) \
+ ->decltype(static_cast<T const &>(t0) op t1) \
+ { \
+ return static_cast<T const &>(t0) op t1; \
+ } \
+ \
+ template <class T> \
+ auto operator op(T const &t0, none<T> const &t1) \
+ ->decltype(t0 op static_cast<T const &>(t1)) \
+ { \
+ return t0 op static_cast<T const &>(t1); \
+ } \
+ \
+ template <class T> \
+ auto operator op(none<T> const &t0, none<T> const &t1) \
+ ->none<decltype(static_cast<T const &>(t0) \
+ op static_cast<T const &>(t1))> \
+ { \
+ if (t0.is_none && t1.is_none) \
+ return none_type{}; \
+ else { \
+ return {static_cast<T const &>(t0) op static_cast<T const &>(t1)}; \
+ } \
+ }
+
+ NONE_OPERATOR_OVERLOAD(+)
+ NONE_OPERATOR_OVERLOAD(-)
+ NONE_OPERATOR_OVERLOAD(*)
+ NONE_OPERATOR_OVERLOAD(/)
+
+ NONE_OPERATOR_OVERLOAD(>)
+ NONE_OPERATOR_OVERLOAD(>=)
+ NONE_OPERATOR_OVERLOAD(<)
+ NONE_OPERATOR_OVERLOAD(<=)
+
template <class T0, class T1>
decltype(operator_::mod(std::declval<T0>(), std::declval<T1>()))
- operator%(none<T0, true> const &t0, T1 const &t1);
+ operator%(none<T0> const &t0, T1 const &t1);
+
template <class T0, class T1>
decltype(operator_::mod(std::declval<T0>(), std::declval<T1>()))
- operator%(T0 const &t0, none<T1, true> const &t1);
+ operator%(T0 const &t0, none<T1> const &t1);
+
template <class T0, class T1>
- none<decltype(operator_::mod(std::declval<T0>(), std::declval<T1>())), true>
- operator%(none<T0, true> const &t0, none<T1, true> const &t1);
- template <class T>
- std::ostream &operator<<(std::ostream &os, none<T, true> const &v);
+ none<decltype(operator_::mod(std::declval<T0>(), std::declval<T1>()))>
+ operator%(none<T0> const &t0, none<T1> const &t1);
+
+ template <class T, bool F>
+ std::ostream &operator<<(std::ostream &os, none<T, F> const &v)
+ {
+ if (v.is_none)
+ return os << none_type();
+ else
+ return os << v.data;
+ }
template <class T>
struct is_none {
@@ -269,6 +220,14 @@ namespace std
struct tuple_element<I, pythonic::types::none<T0>> {
using type = typename std::tuple_element<I, T0>::type;
};
+
+ template <>
+ struct hash<pythonic::types::none_type> {
+ size_t operator()(const pythonic::types::none_type &x) const
+ {
+ return 0;
+ }
+ };
} // namespace std
/* type inference stuff { */
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/combined.hpp b/contrib/python/pythran/pythran/pythonic/include/types/combined.hpp
index 6f531e9fb98..4e2179f4848 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/combined.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/combined.hpp
@@ -45,9 +45,9 @@ struct __combined<T0, T1> {
// by our clumsy type inference scheme
// so we sometime endup with __combined<indexable_container<...>, int> which
// only makes sense when broadcasting
- // fortunately, broadcasting is only supported by ndarray, && we already
+ // fortunately, broadcasting is only supported by ndarray, and we already
// ignore __combined for ndarray
- // so the only thing to do in such situations is « ! throw an error »
+ // so the only thing to do in such situations is « not throw an error »
template <class F0, class F1>
static F0 get(...);
@@ -60,116 +60,91 @@ struct __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<const T0, T1> {
- using type = typename std::add_const<typename __combined<T0, T1>::type>::type;
+struct __combined<const T0, T1> : std::add_const<typename __combined<T0, T1>::type> {
};
template <class T0, class T1>
-struct __combined<T0, const T1> {
- using type = typename std::add_const<typename __combined<T0, T1>::type>::type;
+struct __combined<T0, const T1> : std::add_const<typename __combined<T0, T1>::type> {
};
template <class T0, class T1>
-struct __combined<T0 &, T1> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &, T1> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &&, T1> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &&, T1> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 const &, T1> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 const &, T1> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0, T1 &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0, T1 &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0, T1 &&> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0, T1 &&> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0, T1 const &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0, T1 const &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<const T0, T1 const &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<const T0, T1 const &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<const T0, T1 &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<const T0, T1 &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<const T0, T1 &&> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<const T0, T1 &&> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &, T1 const> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &, T1 const> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &&, T1 const> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &&, T1 const> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 const &, T1 const> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 const &, T1 const> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &, T1 const &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &, T1 const &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &&, T1 const &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 &&, T1 const &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 const &, T1 &> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 const &, T1 &> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 const &, T1 &&> {
- using type = typename __combined<T0, T1>::type;
+struct __combined<T0 const &, T1 &&> : __combined<T0, T1> {
};
template <class T0, class T1>
-struct __combined<T0 &, T1 &> {
- using type = typename std::add_lvalue_reference<
- typename __combined<T0, T1>::type>::type;
+struct __combined<T0 &, T1 &> : std::add_lvalue_reference<typename __combined<T0, T1>::type> {
};
template <class T0, class T1>
-struct __combined<T0 &&, T1 &&> {
- using type = typename std::add_rvalue_reference<
- typename __combined<T0, T1>::type>::type;
+struct __combined<T0 &&, T1 &&> : std::add_rvalue_reference<typename __combined<T0, T1>::type> {
};
template <class T0, class T1>
-struct __combined<const T0, const T1> {
- using type = typename std::add_const<typename __combined<T0, T1>::type>::type;
+struct __combined<const T0, const T1> : std::add_const<typename __combined<T0, T1>::type> {
};
template <class T0, class T1>
-struct __combined<const T0 &, const T1 &> {
- using type = typename std::add_lvalue_reference<
- typename std::add_const<typename __combined<T0, T1>::type>::type>::type;
+struct __combined<const T0 &, const T1 &> : std::add_lvalue_reference<typename std::add_const<typename __combined<T0, T1>::type>::type> {
};
template <class T>
@@ -183,6 +158,13 @@ private:
container();
};
+namespace std {
+ template <size_t I, class T>
+ struct tuple_element<I, container<T>> {
+ using type = typename container<T>::value_type;
+ };
+}
+
template <class K, class V>
class indexable_container
{
@@ -196,6 +178,13 @@ private:
indexable_container();
};
+namespace std {
+ template <size_t I, class K, class V>
+ struct tuple_element<I, indexable_container<K, V>> {
+ using type = typename indexable_container<K, V>::value_type;
+ };
+}
+
template <class T>
class dict_container
{
@@ -207,6 +196,13 @@ private:
dict_container();
};
+namespace std {
+ template <size_t I, class T>
+ struct tuple_element<I, dict_container<T>> {
+ using type = typename dict_container<T>::value_type;
+ };
+}
+
template <class T>
class indexable
{
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/dict.hpp b/contrib/python/pythran/pythran/pythonic/include/types/dict.hpp
index 5f6b826bd94..7772a2641e5 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/dict.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/dict.hpp
@@ -28,35 +28,56 @@ namespace types
struct empty_dict;
template <class I>
- struct item_iterator_adaptator : public I {
+ struct item_iterator_adaptator {
+ I base;
+ using difference_type = typename std::iterator_traits<I>::difference_type;
using value_type = make_tuple_t<
- typename std::remove_cv<typename I::value_type::first_type>::type,
- typename I::value_type::second_type>;
+ typename std::remove_cv<typename std::iterator_traits<I>::value_type::first_type>::type,
+ typename std::iterator_traits<I>::value_type::second_type>;
using pointer = value_type *;
using reference = value_type &;
+ using iterator_category = typename std::iterator_traits<I>::iterator_category;
item_iterator_adaptator() = default;
item_iterator_adaptator(I const &i);
value_type operator*() const;
+ bool operator==(item_iterator_adaptator const& other) const { return base == other.base;}
+ bool operator!=(item_iterator_adaptator const& other) const { return base != other.base;}
+ item_iterator_adaptator& operator++() { ++base; return *this;}
+ auto operator->() ->decltype( &*base) { return &*base;}
};
template <class I>
- struct key_iterator_adaptator : public I {
- using value_type = typename I::value_type::first_type;
- using pointer = typename I::value_type::first_type *;
- using reference = typename I::value_type::first_type &;
- key_iterator_adaptator();
+ struct key_iterator_adaptator {
+ I base;
+ using difference_type = typename std::iterator_traits<I>::difference_type;
+ using value_type = typename std::iterator_traits<I>::value_type::first_type;
+ using pointer = typename std::iterator_traits<I>::value_type::first_type *;
+ using reference = typename std::iterator_traits<I>::value_type::first_type &;
+ using iterator_category = typename std::iterator_traits<I>::iterator_category;
+ key_iterator_adaptator() = default;
key_iterator_adaptator(I const &i);
value_type operator*() const;
+ bool operator==(key_iterator_adaptator const& other) const { return base == other.base;}
+ bool operator!=(key_iterator_adaptator const& other) const { return base != other.base;}
+ key_iterator_adaptator& operator++() { ++base; return *this;}
+ auto operator->() ->decltype( &*base) { return &*base;}
};
template <class I>
- struct value_iterator_adaptator : public I {
- using value_type = typename I::value_type::second_type;
- using pointer = typename I::value_type::second_type *;
- using reference = typename I::value_type::second_type &;
- value_iterator_adaptator();
+ struct value_iterator_adaptator {
+ I base;
+ using difference_type = typename std::iterator_traits<I>::difference_type;
+ using value_type = typename std::iterator_traits<I>::value_type::second_type;
+ using pointer = typename std::iterator_traits<I>::value_type::second_type *;
+ using reference = typename std::iterator_traits<I>::value_type::second_type &;
+ using iterator_category = typename std::iterator_traits<I>::iterator_category;
+ value_iterator_adaptator() = default;
value_iterator_adaptator(I const &i);
value_type operator*() const;
+ bool operator==(value_iterator_adaptator const& other) const { return base == other.base;}
+ bool operator!=(value_iterator_adaptator const& other) const { return base != other.base;}
+ value_iterator_adaptator& operator++() { ++base; return *this;}
+ auto operator->() ->decltype( &*base) { return &*base;}
};
template <class D>
@@ -95,6 +116,55 @@ namespace types
long size() const;
};
+ // specialization of a map whose key are none_type
+ template <class V>
+ struct none_type_map {
+ using key_type = none_type;
+ using mapped_type = V;
+ using value_type = std::pair<const key_type, mapped_type>;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using iterator = value_type *;
+ using const_iterator = const value_type *;
+
+ value_type data_[1];
+ bool empty_;
+
+ none_type_map(size_type) : data_(), empty_(true) {}
+ template <class B, class E>
+ none_type_map(B begin, E end) : data_{begin == end ? value_type() : *begin}, empty_(begin == end) {
+ }
+
+ mapped_type& operator[](none_type) {
+ empty_ = false;
+ return data_[0].second;
+ }
+
+ iterator find(none_type) {
+ return data_ + empty_;
+ }
+
+ const_iterator find(none_type) const {
+ return data_ + empty_;
+ }
+
+ iterator begin() { return data_ + empty_; }
+ const_iterator begin() const { return data_ + empty_; }
+ iterator end() { return data_ + 1; }
+ const_iterator end() const { return data_ + 1; }
+
+ bool empty() const { return empty_;}
+ void clear() { empty_ = true; data_[0].second = {}; }
+ const_iterator erase(const_iterator pos) { clear(); return end();}
+ bool erase(none_type) { bool res = empty_; clear(); return !res;}
+
+ size_t size() const { return empty_?0:1;}
+ };
+
template <class K, class V>
class dict
{
@@ -104,9 +174,11 @@ namespace types
typename std::remove_cv<typename std::remove_reference<K>::type>::type;
using _value_type =
typename std::remove_cv<typename std::remove_reference<V>::type>::type;
- using container_type = std::unordered_map<
+ using container_type = typename std::conditional<std::is_same<K, none_type>::value,
+ none_type_map<_value_type>,
+ std::unordered_map<
_key_type, _value_type, std::hash<_key_type>, std::equal_to<_key_type>,
- utils::allocator<std::pair<const _key_type, _value_type>>>;
+ utils::allocator<std::pair<const _key_type, _value_type>>>>::type;
utils::shared_ref<container_type> data;
template <class Kp, class Vp>
@@ -135,7 +207,6 @@ namespace types
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using value_type = typename container_type::value_type;
- using allocator_type = typename container_type::allocator_type;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
@@ -168,7 +239,7 @@ namespace types
value_const_iterator value_end() const;
// dict interface
- operator bool();
+ operator bool() const;
V &operator[](K const &key) &;
template <class OtherKey>
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/list.hpp b/contrib/python/pythran/pythran/pythonic/include/types/list.hpp
index be008c7bcca..5d5d939aaab 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/list.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/list.hpp
@@ -37,10 +37,6 @@ namespace types
template <class... Tys>
struct pshape;
- /* for type disambiguification */
- struct single_value {
- };
-
/* list view */
template <class T, class S = slice>
class sliced_list
@@ -219,7 +215,6 @@ namespace types
list(InputIterator start, InputIterator stop);
list(empty_list const &);
list(size_type sz);
- list(T const &value, single_value, size_type sz = 1);
list(std::initializer_list<T> l);
list(list<T> &&other);
list(list<T> const &other);
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp b/contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp
index 49c82549874..fff23b7522d 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp
@@ -17,10 +17,12 @@
#include "pythonic/include/types/tuple.hpp"
#include "pythonic/include/numpy/bool_.hpp"
+#include "pythonic/include/numpy/complex256.hpp"
#include "pythonic/include/numpy/complex128.hpp"
#include "pythonic/include/numpy/complex64.hpp"
#include "pythonic/include/numpy/float32.hpp"
#include "pythonic/include/numpy/float64.hpp"
+#include "pythonic/include/numpy/float128.hpp"
#include "pythonic/include/numpy/int16.hpp"
#include "pythonic/include/numpy/int32.hpp"
#include "pythonic/include/numpy/int64.hpp"
@@ -800,6 +802,10 @@ namespace types
using type = pythonic::numpy::functor::float64;
};
template <>
+ struct dtype_helper<long double> {
+ using type = pythonic::numpy::functor::float128;
+ };
+ template <>
struct dtype_helper<std::complex<float>> {
using type = pythonic::numpy::functor::complex64;
};
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/numpy_gexpr.hpp b/contrib/python/pythran/pythran/pythonic/include/types/numpy_gexpr.hpp
index 9057e4da1ff..b56aeba3bc1 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/numpy_gexpr.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/numpy_gexpr.hpp
@@ -1026,6 +1026,12 @@ template <class Arg, class... S, class O>
struct __combined<O, pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::numpy_gexpr<Arg, S...>;
};
+
+template <class Arg, class... S, class O>
+struct __combined<O &, pythonic::types::numpy_gexpr<Arg, S...>> {
+ using type = pythonic::types::numpy_gexpr<Arg, S...>;
+};
+
template <class Arg, class... S>
struct __combined<pythonic::types::none_type,
pythonic::types::numpy_gexpr<Arg, S...>> {
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/numpy_iexpr.hpp b/contrib/python/pythran/pythran/pythonic/include/types/numpy_iexpr.hpp
index d0a65262313..99f19d12bdf 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/numpy_iexpr.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/numpy_iexpr.hpp
@@ -72,23 +72,23 @@ namespace types
long size() const;
- template <class E>
- struct is_almost_same : std::false_type {
+ template <class E0, class E1>
+ struct is_almost_same : std::is_same<typename std::decay<E0>::type, typename std::decay<E1>::type> {
};
- template <class Argp>
- struct is_almost_same<numpy_iexpr<Argp>>
- : std::integral_constant<
- bool, !std::is_same<Arg, Argp>::value &&
- std::is_same<typename std::decay<Arg>::type,
- typename std::decay<Argp>::type>::value> {
+
+ template <class A0, class A1>
+ struct is_almost_same<numpy_iexpr<A0>, numpy_iexpr<A1>> : is_almost_same<A0, A1> {
+ };
+ template <class T, class S0, class S1>
+ struct is_almost_same<ndarray<T, S0>, ndarray<T, S1>> : std::integral_constant<bool, (std::tuple_size<S0>::value == std::tuple_size<S1>::value)> {
};
template <class E, class Requires = typename std::enable_if<
- !is_almost_same<E>::value, void>::type>
+ !is_almost_same<numpy_iexpr, E>::value, void>::type>
numpy_iexpr &operator=(E const &expr);
template <class Argp,
class Requires = typename std::enable_if<
- is_almost_same<numpy_iexpr<Argp>>::value, void>::type>
+ is_almost_same<Arg, Argp>::value, void>::type>
numpy_iexpr &operator=(numpy_iexpr<Argp> const &expr);
numpy_iexpr &operator=(numpy_iexpr const &expr);
@@ -461,6 +461,11 @@ template <class E, class K>
struct __combined<pythonic::types::numpy_iexpr<E>, container<K>> {
using type = pythonic::types::numpy_iexpr<E>;
};
+
+template <class E, class Arg, class...S>
+struct __combined<pythonic::types::numpy_iexpr<E>, pythonic::types::numpy_gexpr<Arg, S...>> {
+ using type = pythonic::types::numpy_iexpr<E>;
+};
template <class E0, class E1>
struct __combined<pythonic::types::numpy_iexpr<E0>,
pythonic::types::numpy_iexpr<E1>> {
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/set.hpp b/contrib/python/pythran/pythran/pythonic/include/types/set.hpp
index a15c901f0f1..cb07955def3 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/set.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/set.hpp
@@ -29,6 +29,14 @@ namespace types
} // namespace types
PYTHONIC_NS_END
+namespace std
+{
+ template <size_t I, class T>
+ struct tuple_element<I, pythonic::types::set<T>> {
+ typedef typename pythonic::types::set<T>::value_type type;
+ };
+} // namespace std
+
/* type inference stuff {*/
#include "pythonic/include/types/combined.hpp"
template <class A, class B>
@@ -157,7 +165,6 @@ namespace types
template <class InputIterator>
set(InputIterator start, InputIterator stop);
set(empty_set const &);
- set(T const &value, single_value);
set(std::initializer_list<value_type> l);
set(set<T> const &other);
template <class F>
diff --git a/contrib/python/pythran/pythran/pythonic/include/types/tuple.hpp b/contrib/python/pythran/pythran/pythonic/include/types/tuple.hpp
index a5eed3475e1..a8f072cafba 100644
--- a/contrib/python/pythran/pythran/pythonic/include/types/tuple.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/types/tuple.hpp
@@ -674,6 +674,13 @@ namespace std
}
template <size_t I, class T, size_t N, class V>
+ typename pythonic::types::array_base<T, N, V>::value_type
+ get(pythonic::types::array_base<T, N, V> &&t)
+ {
+ return std::move(t)[I];
+ }
+
+ template <size_t I, class T, size_t N, class V>
struct tuple_element<I, pythonic::types::array_base<T, N, V>> {
using type = typename pythonic::types::array_base<T, N, V>::value_type;
};
diff --git a/contrib/python/pythran/pythran/pythonic/include/utils/numpy_traits.hpp b/contrib/python/pythran/pythran/pythonic/include/utils/numpy_traits.hpp
index 112ff7a9491..2ab8e72f66c 100644
--- a/contrib/python/pythran/pythran/pythonic/include/utils/numpy_traits.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/utils/numpy_traits.hpp
@@ -185,6 +185,25 @@ namespace types
static T get(...);
using type = decltype(get<E>(nullptr));
};
+
+ template <class T>
+ struct has_buffer {
+ static constexpr bool value = false;
+ };
+
+ template <class T, class pS>
+ struct has_buffer<ndarray<T, pS>> {
+ static constexpr bool value = true;
+ };
+
+ template <class A>
+ struct has_buffer<numpy_iexpr<A>> : has_buffer<A>{
+ };
+
+ template <class A, class... S>
+ struct has_buffer<numpy_gexpr<A, S...>> : has_buffer<A> {
+ };
+
} // namespace types
PYTHONIC_NS_END
diff --git a/contrib/python/pythran/pythran/pythonic/include/utils/seq.hpp b/contrib/python/pythran/pythran/pythonic/include/utils/seq.hpp
index abb47fe97be..576141cb7ef 100644
--- a/contrib/python/pythran/pythran/pythonic/include/utils/seq.hpp
+++ b/contrib/python/pythran/pythran/pythonic/include/utils/seq.hpp
@@ -17,15 +17,28 @@ namespace utils
namespace details
{
+ template <class Left, class Right>
+ struct make_integer_sequence_join;
+
+ template <class T, T... Left, T... Right>
+ struct make_integer_sequence_join<integer_sequence<T, Left...>,
+ integer_sequence<T, Right...>> {
+ using type = integer_sequence<T, Left..., (sizeof...(Left) + Right)...>;
+ };
template <class T, std::size_t N, T... S>
struct make_integer_sequence
- : make_integer_sequence<T, N - 1, static_cast<T>(N - 1), S...> {
+ : make_integer_sequence_join<
+ typename make_integer_sequence<T, N / 2>::type,
+ typename make_integer_sequence<T, N - N / 2>::type> {
};
-
- template <class T, T... S>
- struct make_integer_sequence<T, 0, S...> {
- using type = integer_sequence<T, S...>;
+ template <class T>
+ struct make_integer_sequence<T, 0> {
+ using type = integer_sequence<T>;
+ };
+ template <class T>
+ struct make_integer_sequence<T, 1> {
+ using type = integer_sequence<T, 0>;
};
} // namespace details
diff --git a/contrib/python/pythran/pythran/pythonic/numpy/dot.hpp b/contrib/python/pythran/pythran/pythonic/numpy/dot.hpp
index 2c1f65b3d73..5beadb4489a 100644
--- a/contrib/python/pythran/pythran/pythonic/numpy/dot.hpp
+++ b/contrib/python/pythran/pythran/pythonic/numpy/dot.hpp
@@ -1354,7 +1354,7 @@ namespace numpy
types::is_numexpr_arg<E>::value &&
types::is_numexpr_arg<F>::value // Arguments are array_like
&& E::value == 1 && F::value == 1 // It is a two vectors.
- && (!is_blas_expr<E>::value || !is_blas_expr<F>::value ||
+ && (!is_blas_view<E>::value || !is_blas_view<F>::value ||
!std::is_same<typename E::dtype, typename F::dtype>::value),
typename __combined<typename E::dtype, typename F::dtype>::type>::type
dot(E const &e, F const &f)
@@ -1423,7 +1423,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, float>::value &&
std::is_same<typename F::dtype, float>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
float>::type
dot(E const &e, F const &f)
@@ -1442,7 +1442,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, double>::value &&
std::is_same<typename F::dtype, double>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
double>::type
dot(E const &e, F const &f)
@@ -1461,7 +1461,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, std::complex<float>>::value &&
std::is_same<typename F::dtype, std::complex<float>>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
std::complex<float>>::type
dot(E const &e, F const &f)
@@ -1482,7 +1482,7 @@ namespace numpy
E::value == 1 && F::value == 1 &&
std::is_same<typename E::dtype, std::complex<double>>::value &&
std::is_same<typename F::dtype, std::complex<double>>::value &&
- (is_blas_expr<E>::value && is_blas_expr<F>::value &&
+ (is_blas_view<E>::value && is_blas_view<F>::value &&
!(is_blas_array<E>::value && is_blas_array<F>::value)),
std::complex<double>>::type
dot(E const &e, F const &f)
diff --git a/contrib/python/pythran/pythran/pythonic/numpy/float128.hpp b/contrib/python/pythran/pythran/pythonic/numpy/float128.hpp
index d8e678d3fcd..e5c2ea5b552 100644
--- a/contrib/python/pythran/pythran/pythonic/numpy/float128.hpp
+++ b/contrib/python/pythran/pythran/pythonic/numpy/float128.hpp
@@ -15,11 +15,6 @@ namespace numpy
namespace details
{
- inline long double float128()
- {
- return {};
- }
-
template <class V>
long double float128(V v)
{
diff --git a/contrib/python/pythran/pythran/pythonic/types/NoneType.hpp b/contrib/python/pythran/pythran/pythonic/types/NoneType.hpp
index 201d91ce72b..3952b8092a9 100644
--- a/contrib/python/pythran/pythran/pythonic/types/NoneType.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/NoneType.hpp
@@ -135,191 +135,23 @@ namespace types
return is_none ? NONE_ID : reinterpret_cast<intptr_t>(&data);
}
- template <class T>
- T operator+(none<T, true> const &t0, T const &t1)
- {
- return t0.data + t1;
- }
-
- template <class T>
- T operator+(T const &t0, none<T, true> const &t1)
- {
- return t0 + t1.data;
- }
-
- template <class T>
- none<T, true> operator+(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data + t1.data};
- }
-
- template <class T>
- bool operator>(none<T, true> const &t0, T const &t1)
- {
- return t0.data > t1;
- }
-
- template <class T>
- bool operator>(T const &t0, none<T, true> const &t1)
- {
- return t0 > t1.data;
- }
-
- template <class T>
- none<bool> operator>(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data > t1.data};
- }
-
- template <class T>
- bool operator>=(none<T, true> const &t0, T const &t1)
- {
- return t0.data >= t1;
- }
-
- template <class T>
- bool operator>=(T const &t0, none<T, true> const &t1)
- {
- return t0 >= t1.data;
- }
-
- template <class T>
- none<bool> operator>=(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data >= t1.data};
- }
-
- template <class T>
- bool operator<(none<T, true> const &t0, T const &t1)
- {
- return t0.data < t1;
- }
-
- template <class T>
- bool operator<(T const &t0, none<T, true> const &t1)
- {
- return t0 < t1.data;
- }
-
- template <class T>
- none<bool> operator<(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data < t1.data};
- }
-
- template <class T>
- bool operator<=(none<T, true> const &t0, T const &t1)
- {
- return t0.data <= t1;
- }
-
- template <class T>
- bool operator<=(T const &t0, none<T, true> const &t1)
- {
- return t0 <= t1.data;
- }
-
- template <class T>
- none<bool> operator<=(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data <= t1.data};
- }
-
- template <class T>
- T operator-(none<T, true> const &t0, T const &t1)
- {
- return t0.data - t1;
- }
-
- template <class T>
- T operator-(T const &t0, none<T, true> const &t1)
- {
- return t0 - t1.data;
- }
-
- template <class T>
- none<T, true> operator-(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data - t1.data};
- }
-
- template <class T>
- T operator*(none<T, true> const &t0, T const &t1)
- {
- return t0.data * t1;
- }
-
- template <class T>
- T operator*(T const &t0, none<T, true> const &t1)
- {
- return t0 * t1.data;
- }
-
- template <class T>
- none<T, true> operator*(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data * t1.data};
- }
-
- template <class T>
- T operator/(none<T, true> const &t0, T const &t1)
- {
- return t0.data / t1;
- }
-
- template <class T>
- T operator/(T const &t0, none<T, true> const &t1)
- {
- return t0 / t1.data;
- }
-
- template <class T>
- none<T, true> operator/(none<T, true> const &t0, none<T, true> const &t1)
- {
- if (t0.is_none && t1.is_none)
- return none_type{};
- else
- return {t0.data / t1.data};
- }
-
template <class T0, class T1>
decltype(operator_::mod(std::declval<T0>(), std::declval<T1>()))
- operator%(none<T0, true> const &t0, T1 const &t1)
+ operator%(none<T0> const &t0, T1 const &t1)
{
return operator_::mod(t0.data, t1);
}
template <class T0, class T1>
decltype(operator_::mod(std::declval<T0>(), std::declval<T1>()))
- operator%(T0 const &t0, none<T1, true> const &t1)
+ operator%(T0 const &t0, none<T1> const &t1)
{
return operator_::mod(t0, t1.data);
}
template <class T0, class T1>
none<decltype(operator_::mod(std::declval<T0>(), std::declval<T1>())), true>
- operator%(none<T0, true> const &t0, none<T1, true> const &t1)
+ operator%(none<T0> const &t0, none<T1> const &t1)
{
if (t0.is_none && t1.is_none)
return none_type{};
@@ -363,14 +195,6 @@ namespace types
return *this;
}
- template <class T>
- std::ostream &operator<<(std::ostream &os, none<T, true> const &v)
- {
- if (v.is_none)
- return os << none_type();
- else
- return os << v.data;
- }
} // namespace types
PYTHONIC_NS_END
diff --git a/contrib/python/pythran/pythran/pythonic/types/array.hpp b/contrib/python/pythran/pythran/pythonic/types/array.hpp
index e7b3966793d..b10d9405e01 100644
--- a/contrib/python/pythran/pythran/pythonic/types/array.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/array.hpp
@@ -593,7 +593,9 @@ namespace types
array<T> array<T>::operator*(long n) const
{
if (size() == 1) {
- return array<T>(fast(0), single_value{}, n);
+ array<T> r(n);
+ std::fill(begin(), end(), fast(0));
+ return r;
} else {
array<T> r(size() * n);
auto start = r.begin();
diff --git a/contrib/python/pythran/pythran/pythonic/types/dict.hpp b/contrib/python/pythran/pythran/pythonic/types/dict.hpp
index c6065e87ac6..5bc7cbc89fb 100644
--- a/contrib/python/pythran/pythran/pythonic/types/dict.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/dict.hpp
@@ -21,28 +21,21 @@ PYTHONIC_NS_BEGIN
namespace types
{
/// item implementation
-
template <class I>
- item_iterator_adaptator<I>::item_iterator_adaptator(I const &i) : I(i)
+ item_iterator_adaptator<I>::item_iterator_adaptator(I const &i) : base(i)
{
}
-
template <class I>
typename item_iterator_adaptator<I>::value_type
item_iterator_adaptator<I>::operator*() const
{
- auto &&tmp = I::operator*();
+ auto &&tmp = *base;;
return pythonic::types::make_tuple(tmp.first, tmp.second);
}
/// key_iterator_adaptator implementation
template <class I>
- key_iterator_adaptator<I>::key_iterator_adaptator() : I()
- {
- }
-
- template <class I>
- key_iterator_adaptator<I>::key_iterator_adaptator(I const &i) : I(i)
+ key_iterator_adaptator<I>::key_iterator_adaptator(I const &i) : base(i)
{
}
@@ -50,17 +43,12 @@ namespace types
typename key_iterator_adaptator<I>::value_type
key_iterator_adaptator<I>::operator*() const
{
- return (*this)->first;
+ return base->first;
}
/// value_iterator_adaptator implementation
template <class I>
- value_iterator_adaptator<I>::value_iterator_adaptator() : I()
- {
- }
-
- template <class I>
- value_iterator_adaptator<I>::value_iterator_adaptator(I const &i) : I(i)
+ value_iterator_adaptator<I>::value_iterator_adaptator(I const &i) : base(i)
{
}
@@ -68,7 +56,7 @@ namespace types
typename value_iterator_adaptator<I>::value_type
value_iterator_adaptator<I>::operator*() const
{
- return (*this)->second;
+ return base->second;
}
template <class D>
@@ -302,7 +290,7 @@ namespace types
// dict interface
template <class K, class V>
- dict<K, V>::operator bool()
+ dict<K, V>::operator bool() const
{
return !data->empty();
}
diff --git a/contrib/python/pythran/pythran/pythonic/types/list.hpp b/contrib/python/pythran/pythran/pythonic/types/list.hpp
index 9c346b07059..076b5e701de 100644
--- a/contrib/python/pythran/pythran/pythonic/types/list.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/list.hpp
@@ -273,10 +273,6 @@ namespace types
{
}
template <class T>
- list<T>::list(T const &value, single_value, size_type sz) : _data(sz, value)
- {
- }
- template <class T>
list<T>::list(std::initializer_list<T> l) : _data(std::move(l))
{
}
@@ -642,7 +638,9 @@ namespace types
list<T> list<T>::operator*(long n) const
{
if (size() == 1) {
- return list<T>(fast(0), single_value{}, n);
+ list<T> r(size() * n);
+ std::fill(r.begin(), r.end(), fast(0));
+ return r;
} else {
list<T> r(size() * n);
auto start = r.begin();
diff --git a/contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp b/contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp
index d4faef64896..5f44a4ae1bc 100644
--- a/contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp
@@ -83,7 +83,6 @@ namespace types
return *new (this) numpy_iexpr<Arg>(expr);
}
- assert(buffer);
return utils::broadcast_copy < numpy_iexpr &, numpy_iexpr const &, value,
value - utils::dim_of<numpy_iexpr>::value,
is_vectorizable && numpy_iexpr<Arg>::is_vectorizable &&
diff --git a/contrib/python/pythran/pythran/pythonic/types/set.hpp b/contrib/python/pythran/pythran/pythonic/types/set.hpp
index 7bf74bf0fc8..7c31ada0ec0 100644
--- a/contrib/python/pythran/pythran/pythonic/types/set.hpp
+++ b/contrib/python/pythran/pythran/pythonic/types/set.hpp
@@ -44,12 +44,6 @@ namespace types
}
template <class T>
- set<T>::set(T const &value, single_value) : data()
- {
- data->insert(value);
- }
-
- template <class T>
set<T>::set(std::initializer_list<value_type> l) : data(std::move(l))
{
}
diff --git a/contrib/python/pythran/pythran/pythran.cfg b/contrib/python/pythran/pythran/pythran.cfg
index 677677657a6..3665a6e8d56 100644
--- a/contrib/python/pythran/pythran/pythran.cfg
+++ b/contrib/python/pythran/pythran/pythran.cfg
@@ -31,10 +31,15 @@ ignore_fold_error = False
[typing]
-# maximum number of combiner per user function
+# maximum number of inter-procedural combiner per user function
# increasing this value inreases typing accuracy
# but slows down compilation time, to the point of making g++ crash
-max_combiner = 2
+max_interprocedural_combiner = 2
+
+# maximum number of intra-procedural combiner per user function
+# increasing this value inreases typing accuracy
+# but slows down compilation time, to the point of making g++ crash
+max_combiner = 8
# above this number of overloads, pythran specifications are considered invalid
# as it generates ultra-large binaries
@@ -54,3 +59,7 @@ annotate = false
# set to 'lineno' if you want to generate line number instead of python extract
annotation_kind = 'comment'
+
+# make generated module compatible with freethreading, see
+# https://py-free-threading.github.io/
+freethreading_compatible = true
diff --git a/contrib/python/pythran/pythran/spec.py b/contrib/python/pythran/pythran/spec.py
index 694094083cb..838f99ccd0e 100644
--- a/contrib/python/pythran/pythran/spec.py
+++ b/contrib/python/pythran/pythran/spec.py
@@ -454,12 +454,23 @@ class SpecParser(object):
def p_error(self, p):
if p.type == 'IDENTIFIER':
+ alt = {'double': 'float64',
+ 'void': 'None',
+ 'char': 'int8',
+ 'short': 'int16',
+ 'long': 'int64',
+ }.get(p.value)
+ if alt:
+ hint = " Did you mean `{}`?".format(alt)
+ else:
+ hint = ''
raise self.PythranSpecError(
- "Unexpected identifier `{}` at that point".format(p.value),
+ "Unsupported identifier `{}` at that point.{}".format(p.value,
+ hint),
p.lexpos)
else:
raise self.PythranSpecError(
- "Unexpected token `{}` at that point".format(p.value),
+ "Unexpected token `{}` at that point.".format(p.value),
p.lexpos)
def __init__(self):
diff --git a/contrib/python/pythran/pythran/syntax.py b/contrib/python/pythran/pythran/syntax.py
index 2137d6ba33e..13f4d7fa634 100644
--- a/contrib/python/pythran/pythran/syntax.py
+++ b/contrib/python/pythran/pythran/syntax.py
@@ -47,6 +47,7 @@ class SyntaxChecker(ast.NodeVisitor):
def __init__(self):
""" Gather attributes from MODULES content. """
self.attributes = set()
+ self.functions = []
def save_attribute(module):
""" Recursively save Pythonic keywords as possible attributes. """
@@ -131,7 +132,9 @@ class SyntaxChecker(ast.NodeVisitor):
if node.args.kwarg:
raise PythranSyntaxError("Keyword arguments not supported",
node)
+ self.functions.append(node)
self.generic_visit(node)
+ self.functions.pop()
def visit_Raise(self, node):
self.generic_visit(node)
@@ -207,6 +210,12 @@ class SyntaxChecker(ast.NodeVisitor):
def visit_Global(self, node):
raise PythranSyntaxError("'global' statements are not supported", node)
+ def visit_Nonlocal(self, node):
+ if len(self.functions) < 2:
+ raise PythranSyntaxError(
+ "nonlocal keyword is only valid on nested functions",
+ node)
+
def check_syntax(node):
'''Does nothing but raising PythranSyntaxError when needed'''
diff --git a/contrib/python/pythran/pythran/tables.py b/contrib/python/pythran/pythran/tables.py
index d62abe11c05..a61709d198b 100644
--- a/contrib/python/pythran/pythran/tables.py
+++ b/contrib/python/pythran/pythran/tables.py
@@ -4589,6 +4589,17 @@ try:
except ImportError:
pass
+def save_path(module_name, elements):
+ """ Recursively sets path. """
+ for elem, signature in elements.items():
+ if isinstance(signature, dict): # Submodule case
+ save_path(module_name + (elem,), signature)
+ else:
+ signature.path = module_name + (elem,)
+
+save_path((), CLASSES)
+save_path((), MODULES)
+
def looks_like_a_forward_function(spec):
return not spec.args and spec.varargs == 'args' and spec.varkw == 'kwargs'
diff --git a/contrib/python/pythran/pythran/toolchain.py b/contrib/python/pythran/pythran/toolchain.py
index ba6a9aafe91..42dcc072ad2 100644
--- a/contrib/python/pythran/pythran/toolchain.py
+++ b/contrib/python/pythran/pythran/toolchain.py
@@ -52,8 +52,8 @@ def _extract_specs_dependencies(specs):
# for each argument
for t in signature:
deps.update(pytype_to_deps(t))
- # Keep "include" first
- return sorted(deps, key=lambda x: "include" not in x)
+
+ return sorted(deps)
def _parse_optimization(optimization):
diff --git a/contrib/python/pythran/pythran/transformations/expand_builtins.py b/contrib/python/pythran/pythran/transformations/expand_builtins.py
index 8ce5c63c154..4d815c2b8d3 100644
--- a/contrib/python/pythran/pythran/transformations/expand_builtins.py
+++ b/contrib/python/pythran/pythran/transformations/expand_builtins.py
@@ -9,7 +9,7 @@ import builtins
import gast as ast
-class ExpandBuiltins(Transformation):
+class ExpandBuiltins(Transformation[Locals, Globals]):
"""
Expands all builtins into full paths.
@@ -24,9 +24,6 @@ class ExpandBuiltins(Transformation):
return builtins.list()
"""
- def __init__(self):
- Transformation.__init__(self, Locals, Globals)
-
def visit_Name(self, node):
s = node.id
if s in ('None', 'True', 'False'):
diff --git a/contrib/python/pythran/pythran/transformations/expand_globals.py b/contrib/python/pythran/pythran/transformations/expand_globals.py
index 605b786039f..0cb6ef39887 100644
--- a/contrib/python/pythran/pythran/transformations/expand_globals.py
+++ b/contrib/python/pythran/pythran/transformations/expand_globals.py
@@ -51,9 +51,9 @@ class ExpandGlobals(Transformation):
def __init__(self):
""" Initialize local declaration and constant name to expand. """
+ super().__init__()
self.local_decl = set()
self.to_expand = set()
- super(ExpandGlobals, self).__init__()
def visit_Module(self, node):
"""Turn globals assignment to functionDef and visit function defs. """
diff --git a/contrib/python/pythran/pythran/transformations/expand_imports.py b/contrib/python/pythran/pythran/transformations/expand_imports.py
index 718feadbff8..43da1c892d1 100644
--- a/contrib/python/pythran/pythran/transformations/expand_imports.py
+++ b/contrib/python/pythran/pythran/transformations/expand_imports.py
@@ -9,7 +9,7 @@ from pythran.analyses import Ancestors
import gast as ast
-class ExpandImports(Transformation):
+class ExpandImports(Transformation[Ancestors]):
"""
Expands all imports into full paths.
@@ -39,7 +39,7 @@ class ExpandImports(Transformation):
"""
def __init__(self):
- super(ExpandImports, self).__init__(Ancestors)
+ super().__init__()
self.imports = set()
self.symbols = dict()
diff --git a/contrib/python/pythran/pythran/transformations/false_polymorphism.py b/contrib/python/pythran/pythran/transformations/false_polymorphism.py
index 165517b98aa..c1c8a35685e 100644
--- a/contrib/python/pythran/pythran/transformations/false_polymorphism.py
+++ b/contrib/python/pythran/pythran/transformations/false_polymorphism.py
@@ -4,9 +4,10 @@ from pythran.passmanager import Transformation
from pythran.analyses import DefUseChains, UseDefChains, Identifiers
import gast as ast
+import re
-class FalsePolymorphism(Transformation):
+class FalsePolymorphism(Transformation[DefUseChains, UseDefChains]):
"""
Rename variable when possible to avoid false polymorphism.
@@ -22,16 +23,18 @@ class FalsePolymorphism(Transformation):
a_ = 'babar'
"""
- def __init__(self):
- super(FalsePolymorphism, self).__init__(DefUseChains, UseDefChains)
-
def visit_FunctionDef(self, node):
# reset available identifier names
# removing local identifiers from the list so that first occurrence can
# actually use the slot
identifiers = self.gather(Identifiers, node)
+ captured_identifiers = set()
+ captured_identifiers_pattern = re.compile('^__pythran_boxed_(?:args_)?(.*)$')
for def_ in self.def_use_chains.locals[node]:
+ match = captured_identifiers_pattern.match(def_.name())
+ if match:
+ captured_identifiers.add(match.group(1))
try:
identifiers.remove(def_.name())
except KeyError:
@@ -41,6 +44,8 @@ class FalsePolymorphism(Transformation):
# that should have the same name
visited_defs = set()
for def_ in self.def_use_chains.locals[node]:
+ if def_.name() in captured_identifiers:
+ continue
if def_ in visited_defs:
continue
diff --git a/contrib/python/pythran/pythran/transformations/normalize_ifelse.py b/contrib/python/pythran/pythran/transformations/normalize_ifelse.py
index be75ff495a9..d30bc8eb8fb 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_ifelse.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_ifelse.py
@@ -6,7 +6,7 @@ from pythran.passmanager import Transformation
import gast as ast
-class NormalizeIfElse(Transformation):
+class NormalizeIfElse(Transformation[Ancestors]):
'''
>>> import gast as ast
@@ -47,9 +47,6 @@ class NormalizeIfElse(Transformation):
return 2
'''
- def __init__(self):
- super(NormalizeIfElse, self).__init__(Ancestors)
-
def check_lasts(self, node):
if isinstance(node, (ast.Return, ast.Break, ast.Return)):
return True
diff --git a/contrib/python/pythran/pythran/transformations/normalize_is_none.py b/contrib/python/pythran/pythran/transformations/normalize_is_none.py
index 17a31e0e91d..57cecae3210 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_is_none.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_is_none.py
@@ -9,15 +9,7 @@ import gast as ast
def is_none(expr):
- # py3
- if isinstance(expr, ast.Constant) and expr.value is None:
- return True
-
- # py2
- if not isinstance(expr, ast.Attribute):
- return False
-
- return expr.attr == "None"
+ return isinstance(expr, ast.Constant) and expr.value is None
def is_is_none(expr):
@@ -64,13 +56,10 @@ def is_is_not_none(expr):
return None
-class NormalizeIsNone(Transformation):
+class NormalizeIsNone(Transformation[Ancestors]):
table = {ast.And: ast.BitAnd, ast.Or: ast.BitOr}
- def __init__(self):
- super(NormalizeIsNone, self).__init__(Ancestors)
-
@staticmethod
def match_is_none(node):
noned_var = is_is_none(node)
diff --git a/contrib/python/pythran/pythran/transformations/normalize_method_calls.py b/contrib/python/pythran/pythran/transformations/normalize_method_calls.py
index 9aea96e39f6..8cc06b00a2c 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_method_calls.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_method_calls.py
@@ -12,7 +12,7 @@ import gast as ast
from functools import reduce
-class NormalizeMethodCalls(Transformation):
+class NormalizeMethodCalls(Transformation[Globals, Ancestors]):
'''
Turns built in method calls into function calls.
@@ -27,7 +27,7 @@ class NormalizeMethodCalls(Transformation):
'''
def __init__(self):
- Transformation.__init__(self, Globals, Ancestors)
+ super().__init__()
self.imports = {'builtins': 'builtins',
mangle('__dispatch__'): '__dispatch__'}
self.to_import = set()
diff --git a/contrib/python/pythran/pythran/transformations/normalize_return.py b/contrib/python/pythran/pythran/transformations/normalize_return.py
index 5c948ec232b..cb4096780c1 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_return.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_return.py
@@ -6,7 +6,7 @@ from pythran.passmanager import Transformation
import gast as ast
-class NormalizeReturn(Transformation):
+class NormalizeReturn(Transformation[CFG]):
'''
Adds Return statement when they are implicit,
and adds the None return value when not set
@@ -19,12 +19,9 @@ class NormalizeReturn(Transformation):
>>> print(pm.dump(backend.Python, node))
def foo(y):
print(y)
- return builtins.None
+ return None
'''
- def __init__(self):
- super(NormalizeReturn, self).__init__(CFG)
-
def visit_FunctionDef(self, node):
self.yield_points = self.gather(YieldPoints, node)
for stmt in node.body:
@@ -38,19 +35,13 @@ class NormalizeReturn(Transformation):
if self.yield_points:
node.body.append(ast.Return(None))
else:
- none = ast.Attribute(
- ast.Name("builtins", ast.Load(), None, None),
- 'None',
- ast.Load())
- node.body.append(ast.Return(none))
+ node.body.append(ast.Return(ast.Constant(None, None)))
break
return node
def visit_Return(self, node):
if not node.value and not self.yield_points:
- none = ast.Attribute(ast.Name("builtins", ast.Load(), None, None),
- 'None', ast.Load())
- node.value = none
+ node.value = ast.Constant(None, None)
self.update = True
return node
diff --git a/contrib/python/pythran/pythran/transformations/normalize_static_if.py b/contrib/python/pythran/pythran/transformations/normalize_static_if.py
index 0b29d157a10..f21b7bad97c 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_static_if.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_static_if.py
@@ -47,7 +47,7 @@ def outline(name, formal_parameters, out_parameters, stmts,
)
if has_return:
pr = PatchReturn(stmts[-1], has_break or has_cont)
- pr.visit(fdef)
+ pr.generic_visit(fdef)
if has_break or has_cont:
if not has_return:
@@ -55,7 +55,7 @@ def outline(name, formal_parameters, out_parameters, stmts,
stmts[-1].value],
ast.Load())
pbc = PatchBreakContinue(stmts[-1])
- pbc.visit(fdef)
+ pbc.generic_visit(fdef)
return fdef
@@ -66,6 +66,9 @@ class PatchReturn(ast.NodeTransformer):
self.guard = guard
self.has_break_or_cont = has_break_or_cont
+ def visit_FunctionDef(self, node):
+ return node
+
def visit_Return(self, node):
if node is self.guard:
holder = "StaticIfNoReturn"
@@ -92,11 +95,14 @@ class PatchBreakContinue(ast.NodeTransformer):
def __init__(self, guard):
self.guard = guard
- def visit_For(self, _):
- pass
+ def visit_FunctionDef(self, node):
+ return node
+
+ def visit_For(self, node):
+ return node
- def visit_While(self, _):
- pass
+ def visit_While(self, node):
+ return node
def patch_Control(self, node, flag):
new_node = deepcopy(self.guard)
@@ -117,11 +123,7 @@ class PatchBreakContinue(ast.NodeTransformer):
return self.patch_Control(node, LOOP_CONT)
-class NormalizeStaticIf(Transformation):
-
- def __init__(self):
- super(NormalizeStaticIf, self).__init__(StaticExpressions, Ancestors,
- DefUseChains)
+class NormalizeStaticIf(Transformation[StaticExpressions, Ancestors, DefUseChains]):
def visit_Module(self, node):
self.new_functions = []
@@ -329,10 +331,7 @@ class NormalizeStaticIf(Transformation):
return ast.Expr(actual_call)
-class SplitStaticExpression(Transformation):
-
- def __init__(self):
- super(SplitStaticExpression, self).__init__(StaticExpressions)
+class SplitStaticExpression(Transformation[StaticExpressions]):
def visit_Cond(self, node):
'''
diff --git a/contrib/python/pythran/pythran/transformations/normalize_tuples.py b/contrib/python/pythran/pythran/transformations/normalize_tuples.py
index 0cd5ba5c809..3c71b3a6f92 100644
--- a/contrib/python/pythran/pythran/transformations/normalize_tuples.py
+++ b/contrib/python/pythran/pythran/transformations/normalize_tuples.py
@@ -46,9 +46,6 @@ class NormalizeTuples(Transformation):
"""
tuple_name = "__tuple"
- def __init__(self):
- Transformation.__init__(self)
-
def get_new_id(self):
i = 0
while 1:
diff --git a/contrib/python/pythran/pythran/transformations/remove_comprehension.py b/contrib/python/pythran/pythran/transformations/remove_comprehension.py
index be5b979d76e..0f19643e898 100644
--- a/contrib/python/pythran/pythran/transformations/remove_comprehension.py
+++ b/contrib/python/pythran/pythran/transformations/remove_comprehension.py
@@ -29,8 +29,8 @@ class RemoveComprehension(Transformation):
"""
def __init__(self):
+ super().__init__()
self.count = 0
- Transformation.__init__(self)
def visit_Module(self, node):
self.has_dist_comp = False
diff --git a/contrib/python/pythran/pythran/transformations/remove_lambdas.py b/contrib/python/pythran/pythran/transformations/remove_lambdas.py
index db832c39897..4b00c83d89b 100644
--- a/contrib/python/pythran/pythran/transformations/remove_lambdas.py
+++ b/contrib/python/pythran/pythran/transformations/remove_lambdas.py
@@ -141,7 +141,7 @@ class _LambdaRemover(ast.NodeTransformer):
return proxy_call
-class RemoveLambdas(Transformation):
+class RemoveLambdas(Transformation[GlobalDeclarations]):
"""
Turns lambda into top-level functions.
@@ -159,9 +159,6 @@ class RemoveLambdas(Transformation):
return (y + x)
"""
- def __init__(self):
- super(RemoveLambdas, self).__init__(GlobalDeclarations)
-
def visit_Module(self, node):
self.lambda_functions = list()
self.patterns = {}
diff --git a/contrib/python/pythran/pythran/transformations/remove_named_arguments.py b/contrib/python/pythran/pythran/transformations/remove_named_arguments.py
index b63ff70b6ce..b70c278e141 100644
--- a/contrib/python/pythran/pythran/transformations/remove_named_arguments.py
+++ b/contrib/python/pythran/pythran/transformations/remove_named_arguments.py
@@ -15,7 +15,7 @@ def handle_special_calls(func_alias, node):
node.args.insert(0, ast.Constant(0, None))
-class RemoveNamedArguments(Transformation):
+class RemoveNamedArguments(Transformation[Aliases]):
'''
Replace call with named arguments to regular calls
@@ -32,9 +32,6 @@ class RemoveNamedArguments(Transformation):
return foo(0, z)
'''
- def __init__(self):
- super(RemoveNamedArguments, self).__init__(Aliases)
-
def handle_keywords(self, func, node, offset=0):
'''
Gather keywords to positional argument information
diff --git a/contrib/python/pythran/pythran/transformations/remove_nested_functions.py b/contrib/python/pythran/pythran/transformations/remove_nested_functions.py
index cbb82989978..1958179f85b 100644
--- a/contrib/python/pythran/pythran/transformations/remove_nested_functions.py
+++ b/contrib/python/pythran/pythran/transformations/remove_nested_functions.py
@@ -1,6 +1,6 @@
""" RemoveNestedFunctions turns nested function into top-level functions. """
-from pythran.analyses import GlobalDeclarations, ImportedIds
+from pythran.analyses import GlobalDeclarations, NonlocalDeclarations, ImportedIds
from pythran.passmanager import Transformation
from pythran.tables import MODULES
from pythran.conversion import mangle
@@ -15,10 +15,15 @@ class _NestedFunctionRemover(ast.NodeTransformer):
ast.NodeTransformer.__init__(self)
self.parent = parent
self.identifiers = set(self.global_declarations.keys())
+ self.boxes = {}
+ self.nonlocal_boxes = {}
def __getattr__(self, attr):
return getattr(self.parent, attr)
+ def visit_Nonlocal(self, node):
+ return ast.Pass()
+
def visit_FunctionDef(self, node):
self.update = True
if MODULES['functools'] not in self.global_declarations.values():
@@ -40,12 +45,35 @@ class _NestedFunctionRemover(ast.NodeTransformer):
self.identifiers.add(new_name)
ii = self.gather(ImportedIds, node)
- binded_args = [ast.Name(iin, ast.Load(), None, None)
- for iin in sorted(ii)]
+ sii = sorted(ii)
+ binded_args = [ast.Name('__pythran_boxed_' + iin, ast.Load(), None,
+ None)
+ for iin in sii]
node.args.args = ([ast.Name(iin, ast.Param(), None, None)
- for iin in sorted(ii)] +
+ for iin in sii] +
node.args.args)
+ unboxing = []
+ nonlocal_boxes = {}
+ for iin in sii:
+ if iin in self.nonlocal_declarations[node]:
+ nonlocal_boxes.setdefault(iin, None)
+ self.nonlocal_boxes.setdefault(iin, None)
+ else:
+ unboxing.append(ast.Assign([ast.Name(iin, ast.Store(), None, None)],
+ ast.Subscript(ast.Name('__pythran_boxed_args_' + iin, ast.Load(), None,
+ None),
+ ast.Constant(None, None),
+ ast.Load())))
+ self.boxes.setdefault(iin, None)
+ BoxArgsInserter(nonlocal_boxes).visit(node)
+
+ for arg in node.args.args:
+ if arg.id in ii:
+ arg.id = '__pythran_boxed_args_' + arg.id
+
+ node.body = unboxing + node.body
+
metadata.add(node, metadata.Local())
class Renamer(ast.NodeTransformer):
@@ -55,11 +83,12 @@ class _NestedFunctionRemover(ast.NodeTransformer):
node.func.id == former_name):
node.func.id = new_name
node.args = (
- [ast.Name(iin, ast.Load(), None, None)
- for iin in sorted(ii)] +
+ [ast.Name('__pythran_boxed_args_' + iin, ast.Load(), None, None)
+ for iin in sii] +
node.args
)
return node
+
Renamer().visit(node)
node.name = new_name
@@ -79,11 +108,101 @@ class _NestedFunctionRemover(ast.NodeTransformer):
),
None)
- self.generic_visit(node)
- return new_node
+ nfr = _NestedFunctionRemover(self)
+ nfr.remove_nested(node)
+ return new_node
-class RemoveNestedFunctions(Transformation):
+ def remove_nested(self, node):
+ node.body = [self.visit(stmt) for stmt in node.body]
+ if self.update:
+ boxes = []
+ arg_ids = {arg.id for arg in node.args.args}
+ all_boxes = list(self.boxes)
+ all_boxes.extend(b for b in self.nonlocal_boxes if b not in
+ self.boxes)
+ for i in all_boxes:
+ if i in arg_ids:
+ box_value = ast.Dict([ast.Constant(None, None)], [ast.Name(i,
+ ast.Load(),
+ None,
+ None)])
+ else:
+ box_value = ast.Dict([], [])
+ box = ast.Assign([ast.Name('__pythran_boxed_' + i, ast.Store(),
+ None, None)], box_value)
+ boxes.append(box)
+
+ pre_boxes = self.boxes.copy()
+ for k in self.nonlocal_boxes:
+ pre_boxes.pop(k, None)
+
+ BoxPreInserter(pre_boxes).visit(node)
+ BoxInserter(self.nonlocal_boxes).visit(node)
+ node.body = boxes + node.body
+ return self.update
+
+class BoxPreInserter(ast.NodeTransformer):
+
+ def __init__(self, insertion_points):
+ self.insertion_points = insertion_points
+
+ def insert_target(self, target):
+ if getattr(target, 'id', None) not in self.insertion_points:
+ return None
+ return ast.Assign(
+ [ast.Subscript(
+ ast.Name('__pythran_boxed_' + target.id, ast.Load(), None, None),
+ ast.Constant(None, None),
+ ast.Store())],
+ ast.Name(target.id, ast.Load(), None, None))
+
+
+ def visit_Assign(self, node):
+ extras = []
+ for t in node.targets:
+ extra = self.insert_target(t)
+ if extra:
+ extras.append(extra)
+ if extras:
+ return [node] + extras
+ else:
+ return node
+
+ def visit_AugAssign(self, node):
+ extra = self.insert_target(node.target)
+ if extra:
+ return [node, extra]
+ else:
+ return node
+
+
+class BoxInserter(ast.NodeTransformer):
+
+ def __init__(self, insertion_points):
+ self.insertion_points = insertion_points
+ self.prefix = '__pythran_boxed_'
+
+ def visit_Name(self, node):
+ if node.id not in self.insertion_points:
+ return node
+ if isinstance(node.ctx, ast.Param):
+ return node
+ return ast.Subscript(
+ ast.Name(self.prefix + node.id,
+ ast.Load(), None, None),
+ ast.Constant(None, None),
+ node.ctx)
+
+class BoxArgsInserter(BoxInserter):
+
+ def __init__(self, insertion_points):
+ super().__init__(insertion_points)
+ self.prefix += 'args_'
+
+
+class RemoveNestedFunctions(Transformation[GlobalDeclarations,
+ NonlocalDeclarations]):
"""
Replace nested function by top-level functions.
@@ -99,13 +218,13 @@ class RemoveNestedFunctions(Transformation):
>>> print(pm.dump(backend.Python, node))
import functools as __pythran_import_functools
def foo(x):
- bar = __pythran_import_functools.partial(pythran_bar0, x)
+ __pythran_boxed_x = {None: x}
+ bar = __pythran_import_functools.partial(pythran_bar0, __pythran_boxed_x)
bar(12)
- def pythran_bar0(x, y):
+ def pythran_bar0(__pythran_boxed_args_x, y):
+ x = __pythran_boxed_args_x[None]
return (x + y)
"""
- def __init__(self):
- super(RemoveNestedFunctions, self).__init__(GlobalDeclarations)
def visit_Module(self, node):
# keep original node as it's updated by _NestedFunctionRemover
@@ -115,6 +234,5 @@ class RemoveNestedFunctions(Transformation):
def visit_FunctionDef(self, node):
nfr = _NestedFunctionRemover(self)
- node.body = [nfr.visit(stmt) for stmt in node.body]
- self.update |= nfr.update
+ self.update |= nfr.remove_nested(node)
return node
diff --git a/contrib/python/pythran/pythran/transformations/unshadow_parameters.py b/contrib/python/pythran/pythran/transformations/unshadow_parameters.py
index e61e90c695b..ee582c603be 100644
--- a/contrib/python/pythran/pythran/transformations/unshadow_parameters.py
+++ b/contrib/python/pythran/pythran/transformations/unshadow_parameters.py
@@ -8,7 +8,7 @@ from pythran.passmanager import Transformation
import gast as ast
-class UnshadowParameters(Transformation):
+class UnshadowParameters(Transformation[Identifiers]):
'''
Prevents parameter shadowing by creating new variable.
@@ -23,9 +23,6 @@ class UnshadowParameters(Transformation):
a_ = 1
'''
- def __init__(self):
- Transformation.__init__(self, Identifiers)
-
def visit_FunctionDef(self, node):
self.argsid = {arg.id for arg in node.args.args}
self.renaming = {}
diff --git a/contrib/python/pythran/pythran/types/reorder.py b/contrib/python/pythran/pythran/types/reorder.py
index c027ec0cc9f..b3835804150 100644
--- a/contrib/python/pythran/pythran/types/reorder.py
+++ b/contrib/python/pythran/pythran/types/reorder.py
@@ -44,18 +44,13 @@ def topological_sort(G, nbunch):
return list(reversed(order))
-class Reorder(Transformation):
+class Reorder(Transformation[TypeDependencies, OrderedGlobalDeclarations]):
""" Reorder top-level functions to prevent circular type dependencies. """
- def __init__(self):
- """ Trigger others analysis informations. """
- super(Reorder, self).__init__(TypeDependencies,
- OrderedGlobalDeclarations)
-
def prepare(self, node):
""" Format type dependencies information to use if for reordering. """
- super(Reorder, self).prepare(node)
+ super().prepare(node)
candidates = self.type_dependencies.successors(
TypeDependencies.NoDeps)
# We first select function which may have a result without calling any
diff --git a/contrib/python/pythran/pythran/types/tog.py b/contrib/python/pythran/pythran/types/tog.py
index f3a2b0f461c..e115bcf8526 100644
--- a/contrib/python/pythran/pythran/types/tog.py
+++ b/contrib/python/pythran/pythran/types/tog.py
@@ -5,6 +5,7 @@
import gast as ast
from copy import deepcopy
+import numpy
from numpy import floating, integer, complexfloating
from pythran.tables import MODULES, attributes
@@ -410,7 +411,7 @@ def tr(t):
elif isinstance(t, NoneType_):
return NoneType
- elif t is bool:
+ elif t in (bool, getattr(numpy, 'bool', bool)):
return Bool()
elif issubclass(t, slice):
@@ -953,6 +954,18 @@ def analyse(node, env, non_generic=None):
defn_type),
node)
return env
+ elif isinstance(node, ast.AnnAssign):
+ defn_type = analyse(node.value, env, non_generic)
+ target_type = analyse(node.target, env, non_generic)
+ try:
+ unify(target_type, defn_type)
+ except InferenceError:
+ raise PythranTypeError(
+ "Invalid assignment from type `{}` to type `{}`".format(
+ target_type,
+ defn_type),
+ node)
+ return env
elif isinstance(node, ast.AugAssign):
# FIMXE: not optimal: evaluates type of node.value twice
fake_target = deepcopy(node.target)
diff --git a/contrib/python/pythran/pythran/types/type_dependencies.py b/contrib/python/pythran/pythran/types/type_dependencies.py
index a7fe47a9c8d..15674ceecec 100644
--- a/contrib/python/pythran/pythran/types/type_dependencies.py
+++ b/contrib/python/pythran/pythran/types/type_dependencies.py
@@ -51,7 +51,7 @@ def pytype_to_deps(t):
return res
-class TypeDependencies(ModuleAnalysis):
+class TypeDependencies(ModuleAnalysis[GlobalDeclarations]):
"""
Gathers the callees of each function required for type inference.
@@ -224,14 +224,15 @@ class TypeDependencies(ModuleAnalysis):
NoDeps = "None"
+ ResultType = DiGraph
+
def __init__(self):
""" Create empty result graph and gather global declarations. """
- self.result = DiGraph()
+ super().__init__()
self.current_function = None
self.naming = dict() # variable to dependencies for current function.
# variable to dependencies for current conditional statement
self.in_cond = dict()
- ModuleAnalysis.__init__(self, GlobalDeclarations)
def prepare(self, node):
"""
@@ -334,15 +335,20 @@ class TypeDependencies(ModuleAnalysis):
It is valid for subscript, `a[i] = foo()` means `a` type depend on
`foo` return type.
"""
- if not node.value:
- return
value_deps = self.visit(node.value)
- targets = node.targets if isinstance(node, ast.Assign) else (node.target,)
- for target in targets:
+ for target in node.targets:
name = get_variable(target)
if isinstance(name, ast.Name):
self.naming[name.id] = value_deps
- visit_AnnAssign = visit_Assign
+
+ def visit_AnnAssign(self, node):
+ deps = []
+ if node.value:
+ deps.extend(self.visit(node.value))
+ deps.extend(self.visit(node.annotation))
+ name = get_variable(node.target)
+ if isinstance(name, ast.Name):
+ self.naming[name.id] = deps
def visit_AugAssign(self, node):
"""
diff --git a/contrib/python/pythran/pythran/types/types.py b/contrib/python/pythran/pythran/types/types.py
index e2774616e09..40cfcdf0308 100644
--- a/contrib/python/pythran/pythran/types/types.py
+++ b/contrib/python/pythran/pythran/types/types.py
@@ -7,12 +7,15 @@ This module performs the return type inference, according to symbolic types,
from pythran.analyses import LazynessAnalysis, StrictAliases, YieldPoints
from pythran.analyses import LocalNodeDeclarations, Immediates, RangeValues
from pythran.analyses import Ancestors
+from pythran.analyses.aliases import ContainerOf
from pythran.config import cfg
from pythran.cxxtypes import TypeBuilder, ordered_set
from pythran.intrinsic import UserFunction, Class
from pythran.passmanager import ModuleAnalysis
+from pythran.errors import PythranSyntaxError
from pythran.tables import operator_to_lambda, MODULES
-from pythran.types.conversion import pytype_to_ctype
+from pythran.typing import List, Dict, Set, Tuple, NDArray, Union
+from pythran.types.conversion import pytype_to_ctype, PYTYPE_TO_CTYPE_TABLE
from pythran.types.reorder import Reorder
from pythran.utils import attr_to_path, cxxid, isnum, isextslice
@@ -21,40 +24,201 @@ from functools import reduce
import gast as ast
from itertools import islice
from copy import deepcopy
+import numpy as np
+
+alias_to_type = {
+ MODULES['builtins']['int'] : int,
+ MODULES['builtins']['bool'] : bool,
+ MODULES['builtins']['float'] : float,
+ MODULES['builtins']['str'] : str,
+ MODULES['builtins']['complex'] : complex,
+ MODULES['builtins']['dict'] : Dict,
+ MODULES['builtins']['list'] : List,
+ MODULES['builtins']['set'] : Set,
+ MODULES['builtins']['tuple'] : Tuple,
+ MODULES['builtins']['type'] : type,
+ MODULES['builtins']['None'] : type(None),
+ MODULES['numpy']['intc']: np.intc,
+ MODULES['numpy']['intp']: np.intp,
+ MODULES['numpy']['int64']: np.int64,
+ MODULES['numpy']['int32']: np.int32,
+ MODULES['numpy']['int16']: np.int16,
+ MODULES['numpy']['int8']: np.int8,
+ MODULES['numpy']['uintc']: np.uintc,
+ MODULES['numpy']['uintp']: np.uintp,
+ MODULES['numpy']['uint64']: np.uint64,
+ MODULES['numpy']['uint32']: np.uint32,
+ MODULES['numpy']['uint16']: np.uint16,
+ MODULES['numpy']['uint8']: np.uint8,
+ MODULES['numpy']['float32']: np.float32,
+ MODULES['numpy']['float64']: np.float64,
+ MODULES['numpy']['complex64']: np.complex64,
+ MODULES['numpy']['complex128']: np.complex128,
+ MODULES['numpy']['ndarray']: NDArray,
+}
+try:
+ alias_to_type[MODULES['numpy']['float128']] = np.float128
+ alias_to_type[MODULES['numpy']['complex256']] = np.complex256
+except AttributeError:
+ pass
+
+def alias_key(a):
+ if hasattr(a, 'path'):
+ return a.path
+ if hasattr(a, 'name'):
+ return a.name,
+ if hasattr(a, 'id'):
+ return a.id,
+ if isinstance(a, ast.Subscript):
+ return ('subscript:',) + alias_key(a.value) + alias_key(a.slice)
+ if isinstance(a, ast.Attribute):
+ return ('attr:', a.attr) + alias_key(a.value)
+ if isinstance(a, ast.Call):
+ return ('call:',) + alias_key(a.func)
+ if isinstance(a, ContainerOf):
+ return sum((alias_key(c) for c in sorted(a.containees, key=alias_key)), ())
+ if isinstance(a, ast.Constant):
+ return ('cst:', str(a.value))
+ # FIXME: how could we order those?
+ return str(id(a)),
+
+
+
+class TypeAnnotationParser(ast.NodeVisitor):
+
+ class TypeOf:
+ def __init__(self, val):
+ self.val = val
+
+ class UnionOf:
+ def __init__(self, lhs, rhs):
+ self.lhs = lhs
+ self.rhs = rhs
+
+ def __init__(self, type_visitor):
+ self.type_visitor = type_visitor
+ self.aliases = self.type_visitor.strict_aliases
+
+ def extract(self, node):
+ node_aliases = self.aliases[node]
+ if len(node_aliases) > 1:
+ raise PythranSyntaxError("Ambiguous identifier in type annotation",
+ node)
+ if not node_aliases:
+ raise PythranSyntaxError("Unbound identifier in type annotation",
+ node)
+ node_alias, = node_aliases
+ if node_alias not in alias_to_type:
+ raise PythranSyntaxError("Unsupported identifier in type annotation",
+ node)
+
+ return alias_to_type[node_alias]
+
+
+ def visit_Attribute(self, node):
+ return self.extract(node)
+
+ def visit_Name(self, node):
+ return self.extract(node)
+
+ def visit_Constant(self, node):
+ return node.value
+
+ def visit_Call(self, node):
+ func = self.visit(node.func)
+ if func is not type:
+ raise PythranSyntaxError("Expecting a type or a call to `type(...)`",
+ node)
+ if len(node.args) != 1:
+ raise PythranSyntaxError("`type` only supports a single argument",
+ node)
+ self.type_visitor.visit(node.args[0])
+ return self.TypeOf(self.type_visitor.result[node.args[0]])
+
+ def visit_Tuple(self, node):
+ return tuple([self.visit(elt) for elt in node.elts])
+
+ def visit_BinOp(self, node):
+ if not isinstance(node.op, ast.BitOr):
+ raise PythranSyntaxError("Unsupported operation between type operands",
+ node)
+ left = self.visit(node.left)
+ right = self.visit(node.right)
+ return self.UnionOf(left, right)
+
+ def visit_Subscript(self, node):
+ value = self.visit(node.value)
+ slice_ = self.visit(node.slice)
+ if issubclass(value, NDArray):
+ dtype, ndims = slice_
+ return value[tuple([dtype, *([slice(0)] * ndims)])]
+ else:
+ return value[slice_]
+
+
+def build_type(builder, t):
+ """ Python -> pythonic type binding. """
+ if t is None:
+ return builder.NamedType(pytype_to_ctype(type(None)))
+ elif isinstance(t, List):
+ return builder.ListType(build_type(builder, t.__args__[0]))
+ elif isinstance(t, Set):
+ return builder.SetType(build_type(builder, t.__args__[0]))
+ elif isinstance(t, Dict):
+ tkey, tvalue = t.__args__
+ return builder.DictType(build_type(builder, tkey), build_type(builder,
+ tvalue))
+ elif isinstance(t, Tuple):
+ return builder.TupleType(*[build_type(builder, p) for p in t.__args__])
+ elif isinstance(t, NDArray):
+ return builder.NDArrayType(build_type(builder, t.__args__[0]), len(t.__args__) - 1)
+ elif isinstance(t, TypeAnnotationParser.TypeOf):
+ return t.val
+ elif isinstance(t, TypeAnnotationParser.UnionOf):
+ return builder.CombinedTypes(build_type(builder, t.lhs),
+ build_type(builder, t.rhs))
+ elif t in PYTYPE_TO_CTYPE_TABLE:
+ return builder.NamedType(PYTYPE_TO_CTYPE_TABLE[t])
+ else:
+ raise NotImplementedError("build_type on {}".format(type(t)))
+
+
+def parse_type_annotation(type_visitor, ann):
+ tap = TypeAnnotationParser(type_visitor)
+ typ = tap.visit(ann)
+ return build_type(type_visitor.builder, typ)
class UnboundableRValue(Exception):
pass
-class Types(ModuleAnalysis):
+class Types(ModuleAnalysis[Reorder, StrictAliases, LazynessAnalysis,
+ Immediates, RangeValues, Ancestors]):
""" Infer symbolic type for all AST node. """
+ class ResultType(dict):
+ def __init__(self):
+ self.builder = TypeBuilder()
+
+ def copy(self):
+ other = TypeResult()
+ other.update(self.items())
+ other.builder = self.builder
+ return other
- def __init__(self):
+ def __init__(self):
+ super().__init__()
self.max_seq_size = cfg.getint('typing',
'max_heterogeneous_sequence_size')
-
- class TypeResult(dict):
- def __init__(self):
- self.builder = TypeBuilder()
-
- def copy(self):
- other = TypeResult()
- other.update(self.items())
- other.builder = self.builder
- return other
-
- self.result = TypeResult()
self.builder = self.result.builder
self.result["bool"] = self.builder.NamedType("bool")
self.combiners = defaultdict(UserFunction)
self.current_global_declarations = dict()
self.max_recompute = 1 # max number of use to be lazy
- ModuleAnalysis.__init__(self, Reorder, StrictAliases, LazynessAnalysis,
- Immediates, RangeValues, Ancestors)
self.curr_locals_declaration = None
+ self.ptype_count = 0
def combined(self, *types):
all_types = ordered_set()
@@ -69,6 +233,16 @@ class Types(ModuleAnalysis):
elif len(all_types) == 1:
return next(iter(all_types))
else:
+ all_types = all_types[:cfg.getint('typing', 'max_combiner')]
+ if {type(ty) for ty in all_types} == {self.builder.ListType}:
+ return self.builder.ListType(self.combined(*[ty.of for ty in all_types]))
+ if {type(ty) for ty in all_types} == {self.builder.SetType}:
+ return self.builder.SetType(self.combined(*[ty.of for ty in all_types]))
+ if {type(ty) for ty in all_types} == {self.builder.Assignable}:
+ return self.builder.Assignable(self.combined(*[ty.of for ty in all_types]))
+ if {type(ty) for ty in all_types} == {self.builder.Lazy}:
+ return self.builder.Lazy(self.combined(*[ty.of for ty in all_types]))
+
return self.builder.CombinedTypes(*all_types)
@@ -97,37 +271,41 @@ class Types(ModuleAnalysis):
register(mname, module)
super(Types, self).prepare(node)
- def run(self, node):
- super(Types, self).run(node)
+ def visit_Module(self, node):
+ self.generic_visit(node)
for head in self.current_global_declarations.values():
if head not in self.result:
self.result[head] = "pythonic::types::none_type"
- return self.result
def register(self, fname, nid, ptype):
"""register ptype as a local typedef"""
# Too many of them leads to memory burst
- if len(self.typedefs[fname, nid]) < cfg.getint('typing', 'max_combiner'):
+ if len(self.typedefs[fname, nid]) < cfg.getint('typing',
+ 'max_interprocedural_combiner'):
self.typedefs[fname, nid].append(ptype)
return True
return False
def node_to_id(self, n, depth=()):
+ name, depth = self.node_to_name(n, depth)
+ return name.id, depth
+
+ def node_to_name(self, n, depth=()):
if isinstance(n, ast.Name):
- return (n.id, depth)
+ return (n, depth)
elif isinstance(n, ast.Subscript):
if isinstance(n.slice, ast.Slice):
- return self.node_to_id(n.value, depth)
+ return self.node_to_name(n.value, depth)
else:
index = n.slice.value if isnum(n.slice) else None
- return self.node_to_id(n.value, depth + (index,))
+ return self.node_to_name(n.value, depth + (index,))
# use alias information if any
elif isinstance(n, ast.Call):
- for alias in self.strict_aliases[n]:
+ for alias in self.sorted_strict_aliases(n):
if alias is n: # no specific alias info
continue
try:
- return self.node_to_id(alias, depth)
+ return self.node_to_name(alias, depth)
except UnboundableRValue:
continue
raise UnboundableRValue()
@@ -156,98 +334,130 @@ class Types(ModuleAnalysis):
op = lambda x: x
node_aliases = ordered_set([node])
- node_aliases.extend(self.strict_aliases.get(node, ()))
+ if node in self.strict_aliases:
+ node_aliases.extend(self.sorted_strict_aliases(node))
for a in node_aliases:
self.combine_(a, op, othernode)
def combine_(self, node, op, othernode):
+ # This comes from an assignment,so we must check where the value is
+ # assigned
+ name = None
try:
- # This comes from an assignment,so we must check where the value is
- # assigned
- try:
- node_id, depth = self.node_to_id(node)
- if depth:
- node = ast.Name(node_id, ast.Load(), None, None)
- former_op = op
-
- # update the type to reflect container nesting
- def merge_container_type(ty, index):
- # integral index make it possible to correctly
- # update tuple type
- if isinstance(index, int):
- kty = self.builder.NamedType(
- 'std::integral_constant<long,{}>'
- .format(index))
- return self.builder.IndexableContainerType(kty,
- ty)
- elif isinstance(index, float):
- kty = self.builder.NamedType('double')
- return self.builder.IndexableContainerType(kty,
- ty)
+ name, depth = self.node_to_name(node)
+ if depth:
+ former_op = op
+
+ # update the type to reflect container nesting
+ def merge_container_type(ty, index):
+ # integral index make it possible to correctly
+ # update tuple type
+ if isinstance(index, int):
+ kty = self.builder.NamedType(
+ 'std::integral_constant<long,{}>'
+ .format(index))
+ return self.builder.IndexableContainerType(kty,
+ ty)
+ elif isinstance(index, float):
+ kty = self.builder.NamedType('double')
+ return self.builder.IndexableContainerType(kty,
+ ty)
+ else:
+ # FIXME: what about other key types?
+ return self.builder.ContainerType(ty)
+
+ for node_alias in self.sorted_strict_aliases(name,
+ extra=[name]):
+ def traverse_alias(alias, l):
+ if isinstance(alias, ContainerOf):
+ for containee in sorted(alias.containees,
+ key=alias_key):
+ traverse_alias(containee, l + 1)
else:
- # FIXME: what about other key types?
- return self.builder.ContainerType(ty)
-
- def op(*args):
- return reduce(merge_container_type, depth,
- former_op(*args))
-
- self.name_to_nodes[node_id].append(node)
- except UnboundableRValue:
- pass
-
- # perform inter procedural combination
- if self.isargument(node):
- node_id, _ = self.node_to_id(node)
- if node not in self.result:
- self.result[node] = op(self.result[othernode])
- assert self.result[node], "found an alias with a type"
-
- parametric_type = self.builder.PType(self.current,
- self.result[othernode])
-
- if self.register(self.current, node_id, parametric_type):
-
- current_function = self.combiners[self.current]
-
- def translator_generator(args, op):
- ''' capture args for translator generation'''
- def interprocedural_type_translator(s, n):
- translated_othernode = ast.Name(
- '__fake__', ast.Load(), None, None)
- s.result[translated_othernode] = (
- parametric_type.instanciate(
- s.current,
- [s.result[arg] for arg in n.args]))
-
- # look for modified argument
- for p, effective_arg in enumerate(n.args):
- formal_arg = args[p]
- if formal_arg.id == node_id:
- translated_node = effective_arg
- break
- try:
- s.combine(translated_node,
- op,
- translated_othernode)
- except NotImplementedError:
- pass
- # this may fail when the effective
- # parameter is an expression
- except UnboundLocalError:
- pass
- # this may fail when translated_node
- # is a default parameter
- return interprocedural_type_translator
-
- translator = translator_generator(self.current.args.args, op)
- current_function.add_combiner(translator)
- else:
- self.update_type(node, op, self.result[othernode])
-
+ def local_op(*args):
+ return reduce(merge_container_type,
+ depth[:-l] if l else depth,
+ former_op(*args))
+ if len(depth) > l:
+ self.combine_(alias, local_op, othernode)
+
+ traverse_alias(node_alias, 0)
+ return
except UnboundableRValue:
pass
+ if isinstance(node, ContainerOf):
+ def containeeop(*args):
+ container_type = op(*args)
+ if isinstance(container_type, self.builder.IndexableType):
+ raise NotImplementedError
+ if isinstance(container_type, (self.builder.ListType,
+ self.builder.SetType)):
+ return container_type.of
+ return self.builder.ElementType(
+ 0 if np.isnan(node.index) else node.index,
+ container_type)
+
+ for containee in sorted(node.containees, key=alias_key):
+ try:
+ self.combine(containee, containeeop, othernode)
+ except NotImplementedError:
+ pass
+
+ # perform inter procedural combination
+ if self.isargument(node):
+ node_id, _ = self.node_to_id(node)
+ if node not in self.result:
+ self.result[node] = op(self.result[othernode])
+ self.name_to_nodes[name.id].append(node)
+ assert self.result[node], "found an alias with a type"
+
+ parametric_type = self.builder.PType(self.current,
+ self.result[othernode],
+ self.ptype_count)
+ self.ptype_count += 1
+
+ if self.register(self.current, node_id, parametric_type):
+
+ current_function = self.combiners[self.current]
+
+ def translator_generator(args, op):
+ ''' capture args for translator generation'''
+ def interprocedural_type_translator(s, n):
+ translated_othernode = ast.Name(
+ '__fake__', ast.Load(), None, None)
+ s.result[translated_othernode] = (
+ parametric_type.instanciate(
+ s.current,
+ [s.result[arg] for arg in n.args]))
+
+ # look for modified argument
+ for p, effective_arg in enumerate(n.args):
+ formal_arg = args[p]
+ if formal_arg.id == node_id:
+ translated_node = effective_arg
+ break
+ try:
+ s.combine(translated_node,
+ op,
+ translated_othernode)
+ except NotImplementedError:
+ pass
+ # this may fail when the effective
+ # parameter is an expression
+ except UnboundLocalError:
+ pass
+ # this may fail when translated_node
+ # is a default parameter
+ return interprocedural_type_translator
+
+ translator = translator_generator(self.current.args.args, op)
+ current_function.add_combiner(translator)
+ else:
+ self.update_type(node, op, self.result[othernode])
+ if name is not None:
+ self.name_to_nodes[name.id].append(node)
+
def update_type(self, node, ty_builder, *args):
if ty_builder is None:
ty, = args
@@ -280,10 +490,14 @@ class Types(ModuleAnalysis):
for delayed_node in self.delayed_nodes:
delayed_type = self.result[delayed_node]
+ if not isinstance(delayed_type, self.builder.LType):
+ continue
all_types = ordered_set(self.result[ty] for ty in
self.name_to_nodes[delayed_node.id])
final_type = self.combined(*all_types)
delayed_type.final_type = final_type
+ if final_type is delayed_type.orig:
+ self.result[delayed_node] = delayed_type.orig
# propagate type information through all aliases
for name, nodes in self.name_to_nodes.items():
@@ -305,11 +519,25 @@ class Types(ModuleAnalysis):
for k in self.curr_locals_declaration:
self.result[k] = self.get_qualifier(k)(self.result[k])
+ def assignable(self, ty):
+ if isinstance(ty, (self.builder.Assignable, self.builder.ListType,
+ self.builder.NamedType)):
+ return ty
+ else:
+ return self.builder.Assignable(ty)
+
+ def lazy(self, ty):
+ if isinstance(ty, (self.builder.Lazy, self.builder.ListType,
+ self.builder.NamedType)):
+ return ty
+ else:
+ return self.builder.Lazy(ty)
+
def get_qualifier(self, node):
lazy_res = self.lazyness_analysis[node.id]
- return (self.builder.Lazy
+ return (self.lazy
if lazy_res <= self.max_recompute
- else self.builder.Assignable)
+ else self.assignable)
def visit_Return(self, node):
""" Compute return type and merges with others possible return type."""
@@ -331,38 +559,38 @@ class Types(ModuleAnalysis):
if t in self.curr_locals_declaration:
self.result[t] = self.get_qualifier(t)(self.result[t])
if isinstance(t, ast.Subscript):
- if self.visit_AssignedSubscript(t):
- for alias in self.strict_aliases[t.value]:
- fake = ast.Subscript(alias, t.slice, ast.Store())
- self.combine(fake, None, node.value)
+ self.visit_AssignedSubscript(t)
def visit_AnnAssign(self, node):
+ node_type = parse_type_annotation(self, node.annotation)
+ t = node.target
+ if node_type:
+ self.result[t] = node_type
if not node.value:
- # FIXME: replace this by actually setting the node type from the
- # annotation
- self.curr_locals_declaration.remove(node.target)
+ self.curr_locals_declaration.remove(t)
return
self.visit(node.value)
- t = node.target
- self.combine(t, None, node.value)
+ if node_type:
+ # A bit odd, isn't it? :-)
+ self.combine(t, None, t)
+ else:
+ self.combine(t, None, node.value)
if t in self.curr_locals_declaration:
self.result[t] = self.get_qualifier(t)(self.result[t])
+
if isinstance(t, ast.Subscript):
- if self.visit_AssignedSubscript(t):
- for alias in self.strict_aliases[t.value]:
- fake = ast.Subscript(alias, t.slice, ast.Store())
- self.combine(fake, None, node.value)
+ self.visit_AssignedSubscript(t)
def visit_AugAssign(self, node):
- self.visit(node.value)
-
+ # No visit_AssignedSubscript as the container should already have been
+ # populated.
if isinstance(node.target, ast.Subscript):
- if self.visit_AssignedSubscript(node.target):
- for alias in self.strict_aliases[node.target.value]:
- fake = ast.Subscript(alias, node.target.slice, ast.Store())
- self.combine(fake, None, node.value)
+ self.visit(node.target)
+ self.visit(node.value)
else:
- self.combine(node.target, None, node.value)
+ tmp = ast.BinOp(deepcopy(node.target), node.op, node.value)
+ self.visit(tmp)
+ self.combine(node.target, None, tmp)
def visit_For(self, node):
@@ -417,18 +645,21 @@ class Types(ModuleAnalysis):
self.result[left],
self.result[right])
+ def sorted_strict_aliases(self, func, extra=[]):
+ return sorted(self.strict_aliases[func] | set(extra), key=alias_key)
+
def visit_Call(self, node):
self.generic_visit(node)
func = node.func
- for alias in self.strict_aliases[func]:
+ for alias in self.sorted_strict_aliases(func):
# this comes from a bind
if isinstance(alias, ast.Call):
a0 = alias.args[0]
# by construction of the bind construct
assert len(self.strict_aliases[a0]) == 1
- bounded_function = next(iter(self.strict_aliases[a0]))
+ bounded_function = next(iter(self.sorted_strict_aliases(a0)))
fake_name = deepcopy(a0)
fake_node = ast.Call(fake_name, alias.args[1:] + node.args,
[])
@@ -444,7 +675,7 @@ class Types(ModuleAnalysis):
def last_chance():
# maybe we can get saved if we have a hint about
# the called function return type
- for alias in self.strict_aliases[func]:
+ for alias in self.sorted_strict_aliases(func):
if alias is self.current and alias in self.result:
# great we have a (partial) type information
self.result[node] = self.result[alias]
@@ -563,13 +794,12 @@ class Types(ModuleAnalysis):
def visit_AssignedSubscript(self, node):
if isinstance(node.slice, ast.Slice):
- return False
+ return
elif isextslice(node.slice):
- return False
+ return
else:
self.visit(node.slice)
self.combine(node.value, self.builder.IndexableType, node.slice)
- return True
def delayed(self, node):
fallback_type = self.combined(*[self.result[n] for n in
diff --git a/contrib/python/pythran/pythran/unparse.py b/contrib/python/pythran/pythran/unparse.py
index 058aa004ac7..f08fc0630aa 100644
--- a/contrib/python/pythran/pythran/unparse.py
+++ b/contrib/python/pythran/pythran/unparse.py
@@ -218,6 +218,10 @@ class Unparser:
self.fill("global ")
interleave(lambda: self.write(", "), self.write, t.names)
+ def _Nonlocal(self, t):
+ self.fill("nonlocal ")
+ interleave(lambda: self.write(", "), self.write, t.names)
+
def _Yield(self, t):
self.write("(")
self.write("yield")
diff --git a/contrib/python/pythran/pythran/version.py b/contrib/python/pythran/pythran/version.py
index 7aca4f8ffed..f083a1bd668 100644
--- a/contrib/python/pythran/pythran/version.py
+++ b/contrib/python/pythran/pythran/version.py
@@ -1,2 +1,2 @@
-__version__ = '0.17.0'
+__version__ = '0.18.0'
__descr__ = 'Ahead of Time compiler for numeric kernels'
diff --git a/contrib/python/pythran/ya.make b/contrib/python/pythran/ya.make
index 96bb3996205..ace834763b2 100644
--- a/contrib/python/pythran/ya.make
+++ b/contrib/python/pythran/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(0.17.0)
+VERSION(0.18.0)
LICENSE(BSD-3-Clause)