aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/cython/Cython/Compiler/ExprNodes.py
diff options
context:
space:
mode:
authorAnton Samokhvalov <pg83@yandex.ru>2022-02-10 16:45:15 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:45:15 +0300
commit72cb13b4aff9bc9cf22e49251bc8fd143f82538f (patch)
treeda2c34829458c7d4e74bdfbdf85dff449e9e7fb8 /contrib/tools/cython/Cython/Compiler/ExprNodes.py
parent778e51ba091dc39e7b7fcab2b9cf4dbedfb6f2b5 (diff)
downloadydb-72cb13b4aff9bc9cf22e49251bc8fd143f82538f.tar.gz
Restoring authorship annotation for Anton Samokhvalov <pg83@yandex.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/cython/Cython/Compiler/ExprNodes.py')
-rw-r--r--contrib/tools/cython/Cython/Compiler/ExprNodes.py19962
1 files changed, 9981 insertions, 9981 deletions
diff --git a/contrib/tools/cython/Cython/Compiler/ExprNodes.py b/contrib/tools/cython/Cython/Compiler/ExprNodes.py
index 4a402f8126..4d711fa128 100644
--- a/contrib/tools/cython/Cython/Compiler/ExprNodes.py
+++ b/contrib/tools/cython/Cython/Compiler/ExprNodes.py
@@ -1,91 +1,91 @@
-#
-# Parse tree nodes for expressions
-#
-
-from __future__ import absolute_import
-
-import cython
-cython.declare(error=object, warning=object, warn_once=object, InternalError=object,
- CompileError=object, UtilityCode=object, TempitaUtilityCode=object,
+#
+# Parse tree nodes for expressions
+#
+
+from __future__ import absolute_import
+
+import cython
+cython.declare(error=object, warning=object, warn_once=object, InternalError=object,
+ CompileError=object, UtilityCode=object, TempitaUtilityCode=object,
StringEncoding=object, operator=object, local_errors=object, report_error=object,
- Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object,
- list_type=object, tuple_type=object, set_type=object, dict_type=object,
- unicode_type=object, str_type=object, bytes_type=object, type_type=object,
- Builtin=object, Symtab=object, Utils=object, find_coercion_error=object,
- debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object,
+ Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object,
+ list_type=object, tuple_type=object, set_type=object, dict_type=object,
+ unicode_type=object, str_type=object, bytes_type=object, type_type=object,
+ Builtin=object, Symtab=object, Utils=object, find_coercion_error=object,
+ debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object,
bytearray_type=object, slice_type=object, _py_int_types=object,
IS_PYTHON3=cython.bint)
-
+
import re
import sys
import copy
-import os.path
-import operator
-
+import os.path
+import operator
+
from .Errors import (
error, warning, InternalError, CompileError, report_error, local_errors)
-from .Code import UtilityCode, TempitaUtilityCode
-from . import StringEncoding
-from . import Naming
-from . import Nodes
+from .Code import UtilityCode, TempitaUtilityCode
+from . import StringEncoding
+from . import Naming
+from . import Nodes
from .Nodes import Node, utility_code_for_imports, analyse_type_annotation
-from . import PyrexTypes
-from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
- unspecified_type
-from . import TypeSlots
-from .Builtin import list_type, tuple_type, set_type, dict_type, type_type, \
- unicode_type, str_type, bytes_type, bytearray_type, basestring_type, slice_type
-from . import Builtin
-from . import Symtab
-from .. import Utils
-from .Annotate import AnnotationItem
-from . import Future
-from ..Debugging import print_call_chain
-from .DebugFlags import debug_disposal_code, debug_temp_alloc, \
- debug_coercion
+from . import PyrexTypes
+from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
+ unspecified_type
+from . import TypeSlots
+from .Builtin import list_type, tuple_type, set_type, dict_type, type_type, \
+ unicode_type, str_type, bytes_type, bytearray_type, basestring_type, slice_type
+from . import Builtin
+from . import Symtab
+from .. import Utils
+from .Annotate import AnnotationItem
+from . import Future
+from ..Debugging import print_call_chain
+from .DebugFlags import debug_disposal_code, debug_temp_alloc, \
+ debug_coercion
from .Pythran import (to_pythran, is_pythran_supported_type, is_pythran_supported_operation_type,
is_pythran_expr, pythran_func_type, pythran_binop_type, pythran_unaryop_type, has_np_pythran,
pythran_indexing_code, pythran_indexing_type, is_pythran_supported_node_or_none, pythran_type,
pythran_is_numpy_func_supported, pythran_get_func_include_file, pythran_functor)
from .PyrexTypes import PythranExpr
-
-try:
- from __builtin__ import basestring
-except ImportError:
+
+try:
+ from __builtin__ import basestring
+except ImportError:
# Python 3
basestring = str
any_string_type = (bytes, str)
else:
# Python 2
any_string_type = (bytes, unicode)
-
-
+
+
if sys.version_info[0] >= 3:
IS_PYTHON3 = True
_py_int_types = int
else:
IS_PYTHON3 = False
_py_int_types = (int, long)
-
-
-class NotConstant(object):
- _obj = None
-
- def __new__(cls):
- if NotConstant._obj is None:
- NotConstant._obj = super(NotConstant, cls).__new__(cls)
-
- return NotConstant._obj
-
- def __repr__(self):
- return "<NOT CONSTANT>"
-
-not_a_constant = NotConstant()
-constant_value_not_set = object()
-
-# error messages when coercing from key[0] to key[1]
-coercion_error_dict = {
- # string related errors
+
+
+class NotConstant(object):
+ _obj = None
+
+ def __new__(cls):
+ if NotConstant._obj is None:
+ NotConstant._obj = super(NotConstant, cls).__new__(cls)
+
+ return NotConstant._obj
+
+ def __repr__(self):
+ return "<NOT CONSTANT>"
+
+not_a_constant = NotConstant()
+constant_value_not_set = object()
+
+# error messages when coercing from key[0] to key[1]
+coercion_error_dict = {
+ # string related errors
(unicode_type, str_type): ("Cannot convert Unicode string to 'str' implicitly."
" This is not portable and requires explicit encoding."),
(unicode_type, bytes_type): "Cannot convert Unicode string to 'bytes' implicitly, encoding required.",
@@ -117,76 +117,76 @@ coercion_error_dict = {
(PyrexTypes.c_uchar_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required",
(PyrexTypes.c_const_uchar_ptr_type, unicode_type): (
"Cannot convert 'char*' to unicode implicitly, decoding required"),
-}
-
-def find_coercion_error(type_tuple, default, env):
- err = coercion_error_dict.get(type_tuple)
- if err is None:
- return default
+}
+
+def find_coercion_error(type_tuple, default, env):
+ err = coercion_error_dict.get(type_tuple)
+ if err is None:
+ return default
elif (env.directives['c_string_encoding'] and
any(t in type_tuple for t in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_uchar_ptr_type,
PyrexTypes.c_const_char_ptr_type, PyrexTypes.c_const_uchar_ptr_type))):
- if type_tuple[1].is_pyobject:
- return default
- elif env.directives['c_string_encoding'] in ('ascii', 'default'):
- return default
- else:
- return "'%s' objects do not support coercion to C types with non-ascii or non-default c_string_encoding" % type_tuple[0].name
- else:
- return err
-
-
-def default_str_type(env):
- return {
- 'bytes': bytes_type,
- 'bytearray': bytearray_type,
- 'str': str_type,
- 'unicode': unicode_type
- }.get(env.directives['c_string_type'])
-
-
-def check_negative_indices(*nodes):
- """
- Raise a warning on nodes that are known to have negative numeric values.
- Used to find (potential) bugs inside of "wraparound=False" sections.
- """
- for node in nodes:
+ if type_tuple[1].is_pyobject:
+ return default
+ elif env.directives['c_string_encoding'] in ('ascii', 'default'):
+ return default
+ else:
+ return "'%s' objects do not support coercion to C types with non-ascii or non-default c_string_encoding" % type_tuple[0].name
+ else:
+ return err
+
+
+def default_str_type(env):
+ return {
+ 'bytes': bytes_type,
+ 'bytearray': bytearray_type,
+ 'str': str_type,
+ 'unicode': unicode_type
+ }.get(env.directives['c_string_type'])
+
+
+def check_negative_indices(*nodes):
+ """
+ Raise a warning on nodes that are known to have negative numeric values.
+ Used to find (potential) bugs inside of "wraparound=False" sections.
+ """
+ for node in nodes:
if node is None or (
not isinstance(node.constant_result, _py_int_types) and
not isinstance(node.constant_result, float)):
- continue
- if node.constant_result < 0:
- warning(node.pos,
- "the result of using negative indices inside of "
- "code sections marked as 'wraparound=False' is "
- "undefined", level=1)
-
-
-def infer_sequence_item_type(env, seq_node, index_node=None, seq_type=None):
- if not seq_node.is_sequence_constructor:
- if seq_type is None:
- seq_type = seq_node.infer_type(env)
- if seq_type is tuple_type:
- # tuples are immutable => we can safely follow assignments
- if seq_node.cf_state and len(seq_node.cf_state) == 1:
- try:
- seq_node = seq_node.cf_state[0].rhs
- except AttributeError:
- pass
- if seq_node is not None and seq_node.is_sequence_constructor:
- if index_node is not None and index_node.has_constant_result():
- try:
- item = seq_node.args[index_node.constant_result]
- except (ValueError, TypeError, IndexError):
- pass
- else:
- return item.infer_type(env)
- # if we're lucky, all items have the same type
- item_types = set([item.infer_type(env) for item in seq_node.args])
- if len(item_types) == 1:
- return item_types.pop()
- return None
-
+ continue
+ if node.constant_result < 0:
+ warning(node.pos,
+ "the result of using negative indices inside of "
+ "code sections marked as 'wraparound=False' is "
+ "undefined", level=1)
+
+
+def infer_sequence_item_type(env, seq_node, index_node=None, seq_type=None):
+ if not seq_node.is_sequence_constructor:
+ if seq_type is None:
+ seq_type = seq_node.infer_type(env)
+ if seq_type is tuple_type:
+ # tuples are immutable => we can safely follow assignments
+ if seq_node.cf_state and len(seq_node.cf_state) == 1:
+ try:
+ seq_node = seq_node.cf_state[0].rhs
+ except AttributeError:
+ pass
+ if seq_node is not None and seq_node.is_sequence_constructor:
+ if index_node is not None and index_node.has_constant_result():
+ try:
+ item = seq_node.args[index_node.constant_result]
+ except (ValueError, TypeError, IndexError):
+ pass
+ else:
+ return item.infer_type(env)
+ # if we're lucky, all items have the same type
+ item_types = set([item.infer_type(env) for item in seq_node.args])
+ if len(item_types) == 1:
+ return item_types.pop()
+ return None
+
def make_dedup_key(outer_type, item_nodes):
"""
@@ -236,7 +236,7 @@ def get_exception_handler(exception_value):
'"Error converting c++ exception.");' % (
exception_value.entry.cname),
False)
-
+
def maybe_check_py_error(code, check_py_exception, pos, nogil):
if check_py_exception:
if nogil:
@@ -292,212 +292,212 @@ def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code,
code.putln('}')
-class ExprNode(Node):
- # subexprs [string] Class var holding names of subexpr node attrs
- # type PyrexType Type of the result
- # result_code string Code fragment
- # result_ctype string C type of result_code if different from type
- # is_temp boolean Result is in a temporary variable
- # is_sequence_constructor
- # boolean Is a list or tuple constructor expression
- # is_starred boolean Is a starred expression (e.g. '*a')
- # saved_subexpr_nodes
- # [ExprNode or [ExprNode or None] or None]
- # Cached result of subexpr_nodes()
- # use_managed_ref boolean use ref-counted temps/assignments/etc.
- # result_is_used boolean indicates that the result will be dropped and the
+class ExprNode(Node):
+ # subexprs [string] Class var holding names of subexpr node attrs
+ # type PyrexType Type of the result
+ # result_code string Code fragment
+ # result_ctype string C type of result_code if different from type
+ # is_temp boolean Result is in a temporary variable
+ # is_sequence_constructor
+ # boolean Is a list or tuple constructor expression
+ # is_starred boolean Is a starred expression (e.g. '*a')
+ # saved_subexpr_nodes
+ # [ExprNode or [ExprNode or None] or None]
+ # Cached result of subexpr_nodes()
+ # use_managed_ref boolean use ref-counted temps/assignments/etc.
+ # result_is_used boolean indicates that the result will be dropped and the
# is_numpy_attribute boolean Is a Numpy module attribute
- # result_code/temp_result can safely be set to None
+ # result_code/temp_result can safely be set to None
# annotation ExprNode or None PEP526 annotation for names or expressions
-
- result_ctype = None
- type = None
+
+ result_ctype = None
+ type = None
annotation = None
- temp_code = None
- old_temp = None # error checker for multiple frees etc.
- use_managed_ref = True # can be set by optimisation transforms
- result_is_used = True
+ temp_code = None
+ old_temp = None # error checker for multiple frees etc.
+ use_managed_ref = True # can be set by optimisation transforms
+ result_is_used = True
is_numpy_attribute = False
-
- # The Analyse Expressions phase for expressions is split
- # into two sub-phases:
- #
- # Analyse Types
- # Determines the result type of the expression based
- # on the types of its sub-expressions, and inserts
- # coercion nodes into the expression tree where needed.
- # Marks nodes which will need to have temporary variables
- # allocated.
- #
- # Allocate Temps
- # Allocates temporary variables where needed, and fills
- # in the result_code field of each node.
- #
- # ExprNode provides some convenience routines which
- # perform both of the above phases. These should only
- # be called from statement nodes, and only when no
- # coercion nodes need to be added around the expression
- # being analysed. In that case, the above two phases
- # should be invoked separately.
- #
- # Framework code in ExprNode provides much of the common
- # processing for the various phases. It makes use of the
- # 'subexprs' class attribute of ExprNodes, which should
- # contain a list of the names of attributes which can
- # hold sub-nodes or sequences of sub-nodes.
- #
- # The framework makes use of a number of abstract methods.
- # Their responsibilities are as follows.
- #
- # Declaration Analysis phase
- #
- # analyse_target_declaration
- # Called during the Analyse Declarations phase to analyse
- # the LHS of an assignment or argument of a del statement.
- # Nodes which cannot be the LHS of an assignment need not
- # implement it.
- #
- # Expression Analysis phase
- #
- # analyse_types
- # - Call analyse_types on all sub-expressions.
- # - Check operand types, and wrap coercion nodes around
- # sub-expressions where needed.
- # - Set the type of this node.
- # - If a temporary variable will be required for the
- # result, set the is_temp flag of this node.
- #
- # analyse_target_types
- # Called during the Analyse Types phase to analyse
- # the LHS of an assignment or argument of a del
- # statement. Similar responsibilities to analyse_types.
- #
- # target_code
- # Called by the default implementation of allocate_target_temps.
- # Should return a C lvalue for assigning to the node. The default
- # implementation calls calculate_result_code.
- #
- # check_const
- # - Check that this node and its subnodes form a
- # legal constant expression. If so, do nothing,
- # otherwise call not_const.
- #
- # The default implementation of check_const
- # assumes that the expression is not constant.
- #
- # check_const_addr
- # - Same as check_const, except check that the
- # expression is a C lvalue whose address is
- # constant. Otherwise, call addr_not_const.
- #
- # The default implementation of calc_const_addr
- # assumes that the expression is not a constant
- # lvalue.
- #
- # Code Generation phase
- #
- # generate_evaluation_code
- # - Call generate_evaluation_code for sub-expressions.
- # - Perform the functions of generate_result_code
- # (see below).
- # - If result is temporary, call generate_disposal_code
- # on all sub-expressions.
- #
- # A default implementation of generate_evaluation_code
- # is provided which uses the following abstract methods:
- #
- # generate_result_code
- # - Generate any C statements necessary to calculate
- # the result of this node from the results of its
- # sub-expressions.
- #
- # calculate_result_code
- # - Should return a C code fragment evaluating to the
- # result. This is only called when the result is not
- # a temporary.
- #
- # generate_assignment_code
- # Called on the LHS of an assignment.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the assignment.
- # - If the assignment absorbed a reference, call
- # generate_post_assignment_code on the RHS,
- # otherwise call generate_disposal_code on it.
- #
- # generate_deletion_code
- # Called on an argument of a del statement.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the deletion.
- # - Call generate_disposal_code on all sub-expressions.
- #
- #
-
- is_sequence_constructor = False
- is_dict_literal = False
+
+ # The Analyse Expressions phase for expressions is split
+ # into two sub-phases:
+ #
+ # Analyse Types
+ # Determines the result type of the expression based
+ # on the types of its sub-expressions, and inserts
+ # coercion nodes into the expression tree where needed.
+ # Marks nodes which will need to have temporary variables
+ # allocated.
+ #
+ # Allocate Temps
+ # Allocates temporary variables where needed, and fills
+ # in the result_code field of each node.
+ #
+ # ExprNode provides some convenience routines which
+ # perform both of the above phases. These should only
+ # be called from statement nodes, and only when no
+ # coercion nodes need to be added around the expression
+ # being analysed. In that case, the above two phases
+ # should be invoked separately.
+ #
+ # Framework code in ExprNode provides much of the common
+ # processing for the various phases. It makes use of the
+ # 'subexprs' class attribute of ExprNodes, which should
+ # contain a list of the names of attributes which can
+ # hold sub-nodes or sequences of sub-nodes.
+ #
+ # The framework makes use of a number of abstract methods.
+ # Their responsibilities are as follows.
+ #
+ # Declaration Analysis phase
+ #
+ # analyse_target_declaration
+ # Called during the Analyse Declarations phase to analyse
+ # the LHS of an assignment or argument of a del statement.
+ # Nodes which cannot be the LHS of an assignment need not
+ # implement it.
+ #
+ # Expression Analysis phase
+ #
+ # analyse_types
+ # - Call analyse_types on all sub-expressions.
+ # - Check operand types, and wrap coercion nodes around
+ # sub-expressions where needed.
+ # - Set the type of this node.
+ # - If a temporary variable will be required for the
+ # result, set the is_temp flag of this node.
+ #
+ # analyse_target_types
+ # Called during the Analyse Types phase to analyse
+ # the LHS of an assignment or argument of a del
+ # statement. Similar responsibilities to analyse_types.
+ #
+ # target_code
+ # Called by the default implementation of allocate_target_temps.
+ # Should return a C lvalue for assigning to the node. The default
+ # implementation calls calculate_result_code.
+ #
+ # check_const
+ # - Check that this node and its subnodes form a
+ # legal constant expression. If so, do nothing,
+ # otherwise call not_const.
+ #
+ # The default implementation of check_const
+ # assumes that the expression is not constant.
+ #
+ # check_const_addr
+ # - Same as check_const, except check that the
+ # expression is a C lvalue whose address is
+ # constant. Otherwise, call addr_not_const.
+ #
+ # The default implementation of calc_const_addr
+ # assumes that the expression is not a constant
+ # lvalue.
+ #
+ # Code Generation phase
+ #
+ # generate_evaluation_code
+ # - Call generate_evaluation_code for sub-expressions.
+ # - Perform the functions of generate_result_code
+ # (see below).
+ # - If result is temporary, call generate_disposal_code
+ # on all sub-expressions.
+ #
+ # A default implementation of generate_evaluation_code
+ # is provided which uses the following abstract methods:
+ #
+ # generate_result_code
+ # - Generate any C statements necessary to calculate
+ # the result of this node from the results of its
+ # sub-expressions.
+ #
+ # calculate_result_code
+ # - Should return a C code fragment evaluating to the
+ # result. This is only called when the result is not
+ # a temporary.
+ #
+ # generate_assignment_code
+ # Called on the LHS of an assignment.
+ # - Call generate_evaluation_code for sub-expressions.
+ # - Generate code to perform the assignment.
+ # - If the assignment absorbed a reference, call
+ # generate_post_assignment_code on the RHS,
+ # otherwise call generate_disposal_code on it.
+ #
+ # generate_deletion_code
+ # Called on an argument of a del statement.
+ # - Call generate_evaluation_code for sub-expressions.
+ # - Generate code to perform the deletion.
+ # - Call generate_disposal_code on all sub-expressions.
+ #
+ #
+
+ is_sequence_constructor = False
+ is_dict_literal = False
is_set_literal = False
- is_string_literal = False
- is_attribute = False
- is_subscript = False
+ is_string_literal = False
+ is_attribute = False
+ is_subscript = False
is_slice = False
-
+
is_buffer_access = False
is_memview_index = False
is_memview_slice = False
is_memview_broadcast = False
is_memview_copy_assignment = False
- saved_subexpr_nodes = None
- is_temp = False
- is_target = False
- is_starred = False
-
- constant_result = constant_value_not_set
-
- child_attrs = property(fget=operator.attrgetter('subexprs'))
-
- def not_implemented(self, method_name):
- print_call_chain(method_name, "not implemented") ###
- raise InternalError(
- "%s.%s not implemented" %
- (self.__class__.__name__, method_name))
-
- def is_lvalue(self):
- return 0
-
- def is_addressable(self):
- return self.is_lvalue() and not self.type.is_memoryviewslice
-
- def is_ephemeral(self):
- # An ephemeral node is one whose result is in
- # a Python temporary and we suspect there are no
- # other references to it. Certain operations are
- # disallowed on such values, since they are
- # likely to result in a dangling pointer.
- return self.type.is_pyobject and self.is_temp
-
- def subexpr_nodes(self):
- # Extract a list of subexpression nodes based
- # on the contents of the subexprs class attribute.
- nodes = []
- for name in self.subexprs:
- item = getattr(self, name)
- if item is not None:
- if type(item) is list:
- nodes.extend(item)
- else:
- nodes.append(item)
- return nodes
-
- def result(self):
- if self.is_temp:
- #if not self.temp_code:
- # pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- # raise RuntimeError("temp result name not set in %s at %r" % (
- # self.__class__.__name__, pos))
- return self.temp_code
- else:
- return self.calculate_result_code()
-
+ saved_subexpr_nodes = None
+ is_temp = False
+ is_target = False
+ is_starred = False
+
+ constant_result = constant_value_not_set
+
+ child_attrs = property(fget=operator.attrgetter('subexprs'))
+
+ def not_implemented(self, method_name):
+ print_call_chain(method_name, "not implemented") ###
+ raise InternalError(
+ "%s.%s not implemented" %
+ (self.__class__.__name__, method_name))
+
+ def is_lvalue(self):
+ return 0
+
+ def is_addressable(self):
+ return self.is_lvalue() and not self.type.is_memoryviewslice
+
+ def is_ephemeral(self):
+ # An ephemeral node is one whose result is in
+ # a Python temporary and we suspect there are no
+ # other references to it. Certain operations are
+ # disallowed on such values, since they are
+ # likely to result in a dangling pointer.
+ return self.type.is_pyobject and self.is_temp
+
+ def subexpr_nodes(self):
+ # Extract a list of subexpression nodes based
+ # on the contents of the subexprs class attribute.
+ nodes = []
+ for name in self.subexprs:
+ item = getattr(self, name)
+ if item is not None:
+ if type(item) is list:
+ nodes.extend(item)
+ else:
+ nodes.append(item)
+ return nodes
+
+ def result(self):
+ if self.is_temp:
+ #if not self.temp_code:
+ # pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
+ # raise RuntimeError("temp result name not set in %s at %r" % (
+ # self.__class__.__name__, pos))
+ return self.temp_code
+ else:
+ return self.calculate_result_code()
+
def pythran_result(self, type_=None):
if is_pythran_supported_node_or_none(self):
return to_pythran(self)
@@ -511,130 +511,130 @@ class ExprNode(Node):
"""
return True
- def result_as(self, type = None):
- # Return the result code cast to the specified C type.
- if (self.is_temp and self.type.is_pyobject and
- type != py_object_type):
- # Allocated temporaries are always PyObject *, which may not
- # reflect the actual type (e.g. an extension type)
- return typecast(type, py_object_type, self.result())
- return typecast(type, self.ctype(), self.result())
-
- def py_result(self):
- # Return the result code cast to PyObject *.
- return self.result_as(py_object_type)
-
- def ctype(self):
- # Return the native C type of the result (i.e. the
- # C type of the result_code expression).
- return self.result_ctype or self.type
-
- def get_constant_c_result_code(self):
- # Return the constant value of this node as a result code
- # string, or None if the node is not constant. This method
- # can be called when the constant result code is required
- # before the code generation phase.
- #
- # The return value is a string that can represent a simple C
- # value, a constant C name or a constant C expression. If the
- # node type depends on Python code, this must return None.
- return None
-
- def calculate_constant_result(self):
- # Calculate the constant compile time result value of this
- # expression and store it in ``self.constant_result``. Does
- # nothing by default, thus leaving ``self.constant_result``
- # unknown. If valid, the result can be an arbitrary Python
- # value.
- #
- # This must only be called when it is assured that all
- # sub-expressions have a valid constant_result value. The
- # ConstantFolding transform will do this.
- pass
-
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
-
- def compile_time_value(self, denv):
- # Return value of compile-time expression, or report error.
- error(self.pos, "Invalid compile-time expression")
-
- def compile_time_value_error(self, e):
- error(self.pos, "Error in compile-time expression: %s: %s" % (
- e.__class__.__name__, e))
-
- # ------------- Declaration Analysis ----------------
-
- def analyse_target_declaration(self, env):
- error(self.pos, "Cannot assign to or delete this")
-
- # ------------- Expression Analysis ----------------
-
- def analyse_const_expression(self, env):
- # Called during the analyse_declarations phase of a
- # constant expression. Analyses the expression's type,
- # checks whether it is a legal const expression,
- # and determines its value.
- node = self.analyse_types(env)
- node.check_const()
- return node
-
- def analyse_expressions(self, env):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for a whole
- # expression.
- return self.analyse_types(env)
-
- def analyse_target_expression(self, env, rhs):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for the LHS of
- # an assignment.
- return self.analyse_target_types(env)
-
- def analyse_boolean_expression(self, env):
- # Analyse expression and coerce to a boolean.
- node = self.analyse_types(env)
- bool = node.coerce_to_boolean(env)
- return bool
-
- def analyse_temp_boolean_expression(self, env):
- # Analyse boolean expression and coerce result into
- # a temporary. This is used when a branch is to be
- # performed on the result and we won't have an
- # opportunity to ensure disposal code is executed
- # afterwards. By forcing the result into a temporary,
- # we ensure that all disposal has been done by the
- # time we get the result.
- node = self.analyse_types(env)
- return node.coerce_to_boolean(env).coerce_to_simple(env)
-
- # --------------- Type Inference -----------------
-
- def type_dependencies(self, env):
- # Returns the list of entries whose types must be determined
- # before the type of self can be inferred.
- if hasattr(self, 'type') and self.type is not None:
- return ()
- return sum([node.type_dependencies(env) for node in self.subexpr_nodes()], ())
-
- def infer_type(self, env):
- # Attempt to deduce the type of self.
- # Differs from analyse_types as it avoids unnecessary
- # analysis of subexpressions, but can assume everything
- # in self.type_dependencies() has been resolved.
- if hasattr(self, 'type') and self.type is not None:
- return self.type
- elif hasattr(self, 'entry') and self.entry is not None:
- return self.entry.type
- else:
- self.not_implemented("infer_type")
-
- def nonlocally_immutable(self):
- # Returns whether this variable is a safe reference, i.e.
- # can't be modified as part of globals or closures.
- return self.is_literal or self.is_temp or self.type.is_array or self.type.is_cfunction
-
+ def result_as(self, type = None):
+ # Return the result code cast to the specified C type.
+ if (self.is_temp and self.type.is_pyobject and
+ type != py_object_type):
+ # Allocated temporaries are always PyObject *, which may not
+ # reflect the actual type (e.g. an extension type)
+ return typecast(type, py_object_type, self.result())
+ return typecast(type, self.ctype(), self.result())
+
+ def py_result(self):
+ # Return the result code cast to PyObject *.
+ return self.result_as(py_object_type)
+
+ def ctype(self):
+ # Return the native C type of the result (i.e. the
+ # C type of the result_code expression).
+ return self.result_ctype or self.type
+
+ def get_constant_c_result_code(self):
+ # Return the constant value of this node as a result code
+ # string, or None if the node is not constant. This method
+ # can be called when the constant result code is required
+ # before the code generation phase.
+ #
+ # The return value is a string that can represent a simple C
+ # value, a constant C name or a constant C expression. If the
+ # node type depends on Python code, this must return None.
+ return None
+
+ def calculate_constant_result(self):
+ # Calculate the constant compile time result value of this
+ # expression and store it in ``self.constant_result``. Does
+ # nothing by default, thus leaving ``self.constant_result``
+ # unknown. If valid, the result can be an arbitrary Python
+ # value.
+ #
+ # This must only be called when it is assured that all
+ # sub-expressions have a valid constant_result value. The
+ # ConstantFolding transform will do this.
+ pass
+
+ def has_constant_result(self):
+ return self.constant_result is not constant_value_not_set and \
+ self.constant_result is not not_a_constant
+
+ def compile_time_value(self, denv):
+ # Return value of compile-time expression, or report error.
+ error(self.pos, "Invalid compile-time expression")
+
+ def compile_time_value_error(self, e):
+ error(self.pos, "Error in compile-time expression: %s: %s" % (
+ e.__class__.__name__, e))
+
+ # ------------- Declaration Analysis ----------------
+
+ def analyse_target_declaration(self, env):
+ error(self.pos, "Cannot assign to or delete this")
+
+ # ------------- Expression Analysis ----------------
+
+ def analyse_const_expression(self, env):
+ # Called during the analyse_declarations phase of a
+ # constant expression. Analyses the expression's type,
+ # checks whether it is a legal const expression,
+ # and determines its value.
+ node = self.analyse_types(env)
+ node.check_const()
+ return node
+
+ def analyse_expressions(self, env):
+ # Convenience routine performing both the Type
+ # Analysis and Temp Allocation phases for a whole
+ # expression.
+ return self.analyse_types(env)
+
+ def analyse_target_expression(self, env, rhs):
+ # Convenience routine performing both the Type
+ # Analysis and Temp Allocation phases for the LHS of
+ # an assignment.
+ return self.analyse_target_types(env)
+
+ def analyse_boolean_expression(self, env):
+ # Analyse expression and coerce to a boolean.
+ node = self.analyse_types(env)
+ bool = node.coerce_to_boolean(env)
+ return bool
+
+ def analyse_temp_boolean_expression(self, env):
+ # Analyse boolean expression and coerce result into
+ # a temporary. This is used when a branch is to be
+ # performed on the result and we won't have an
+ # opportunity to ensure disposal code is executed
+ # afterwards. By forcing the result into a temporary,
+ # we ensure that all disposal has been done by the
+ # time we get the result.
+ node = self.analyse_types(env)
+ return node.coerce_to_boolean(env).coerce_to_simple(env)
+
+ # --------------- Type Inference -----------------
+
+ def type_dependencies(self, env):
+ # Returns the list of entries whose types must be determined
+ # before the type of self can be inferred.
+ if hasattr(self, 'type') and self.type is not None:
+ return ()
+ return sum([node.type_dependencies(env) for node in self.subexpr_nodes()], ())
+
+ def infer_type(self, env):
+ # Attempt to deduce the type of self.
+ # Differs from analyse_types as it avoids unnecessary
+ # analysis of subexpressions, but can assume everything
+ # in self.type_dependencies() has been resolved.
+ if hasattr(self, 'type') and self.type is not None:
+ return self.type
+ elif hasattr(self, 'entry') and self.entry is not None:
+ return self.entry.type
+ else:
+ self.not_implemented("infer_type")
+
+ def nonlocally_immutable(self):
+ # Returns whether this variable is a safe reference, i.e.
+ # can't be modified as part of globals or closures.
+ return self.is_literal or self.is_temp or self.type.is_array or self.type.is_cfunction
+
def inferable_item_node(self, index=0):
"""
Return a node that represents the (type) result of an indexing operation,
@@ -643,289 +643,289 @@ class ExprNode(Node):
return IndexNode(self.pos, base=self, index=IntNode(
self.pos, value=str(index), constant_result=index, type=PyrexTypes.c_py_ssize_t_type))
- # --------------- Type Analysis ------------------
-
- def analyse_as_module(self, env):
- # If this node can be interpreted as a reference to a
- # cimported module, return its scope, else None.
- return None
-
- def analyse_as_type(self, env):
- # If this node can be interpreted as a reference to a
- # type, return that type, else None.
- return None
-
- def analyse_as_extension_type(self, env):
- # If this node can be interpreted as a reference to an
- # extension type or builtin type, return its type, else None.
- return None
-
- def analyse_types(self, env):
- self.not_implemented("analyse_types")
-
- def analyse_target_types(self, env):
- return self.analyse_types(env)
-
- def nogil_check(self, env):
- # By default, any expression based on Python objects is
- # prevented in nogil environments. Subtypes must override
- # this if they can work without the GIL.
- if self.type and self.type.is_pyobject:
- self.gil_error()
-
- def gil_assignment_check(self, env):
- if env.nogil and self.type.is_pyobject:
- error(self.pos, "Assignment of Python object not allowed without gil")
-
- def check_const(self):
- self.not_const()
- return False
-
- def not_const(self):
- error(self.pos, "Not allowed in a constant expression")
-
- def check_const_addr(self):
- self.addr_not_const()
- return False
-
- def addr_not_const(self):
- error(self.pos, "Address is not constant")
-
- # ----------------- Result Allocation -----------------
-
- def result_in_temp(self):
- # Return true if result is in a temporary owned by
- # this node or one of its subexpressions. Overridden
- # by certain nodes which can share the result of
- # a subnode.
- return self.is_temp
-
- def target_code(self):
- # Return code fragment for use as LHS of a C assignment.
- return self.calculate_result_code()
-
- def calculate_result_code(self):
- self.not_implemented("calculate_result_code")
-
-# def release_target_temp(self, env):
-# # Release temporaries used by LHS of an assignment.
-# self.release_subexpr_temps(env)
-
- def allocate_temp_result(self, code):
- if self.temp_code:
- raise RuntimeError("Temp allocated multiple times in %r: %r" % (self.__class__.__name__, self.pos))
- type = self.type
- if not type.is_void:
- if type.is_pyobject:
- type = PyrexTypes.py_object_type
+ # --------------- Type Analysis ------------------
+
+ def analyse_as_module(self, env):
+ # If this node can be interpreted as a reference to a
+ # cimported module, return its scope, else None.
+ return None
+
+ def analyse_as_type(self, env):
+ # If this node can be interpreted as a reference to a
+ # type, return that type, else None.
+ return None
+
+ def analyse_as_extension_type(self, env):
+ # If this node can be interpreted as a reference to an
+ # extension type or builtin type, return its type, else None.
+ return None
+
+ def analyse_types(self, env):
+ self.not_implemented("analyse_types")
+
+ def analyse_target_types(self, env):
+ return self.analyse_types(env)
+
+ def nogil_check(self, env):
+ # By default, any expression based on Python objects is
+ # prevented in nogil environments. Subtypes must override
+ # this if they can work without the GIL.
+ if self.type and self.type.is_pyobject:
+ self.gil_error()
+
+ def gil_assignment_check(self, env):
+ if env.nogil and self.type.is_pyobject:
+ error(self.pos, "Assignment of Python object not allowed without gil")
+
+ def check_const(self):
+ self.not_const()
+ return False
+
+ def not_const(self):
+ error(self.pos, "Not allowed in a constant expression")
+
+ def check_const_addr(self):
+ self.addr_not_const()
+ return False
+
+ def addr_not_const(self):
+ error(self.pos, "Address is not constant")
+
+ # ----------------- Result Allocation -----------------
+
+ def result_in_temp(self):
+ # Return true if result is in a temporary owned by
+ # this node or one of its subexpressions. Overridden
+ # by certain nodes which can share the result of
+ # a subnode.
+ return self.is_temp
+
+ def target_code(self):
+ # Return code fragment for use as LHS of a C assignment.
+ return self.calculate_result_code()
+
+ def calculate_result_code(self):
+ self.not_implemented("calculate_result_code")
+
+# def release_target_temp(self, env):
+# # Release temporaries used by LHS of an assignment.
+# self.release_subexpr_temps(env)
+
+ def allocate_temp_result(self, code):
+ if self.temp_code:
+ raise RuntimeError("Temp allocated multiple times in %r: %r" % (self.__class__.__name__, self.pos))
+ type = self.type
+ if not type.is_void:
+ if type.is_pyobject:
+ type = PyrexTypes.py_object_type
elif not (self.result_is_used or type.is_memoryviewslice or self.is_c_result_required()):
self.temp_code = None
return
- self.temp_code = code.funcstate.allocate_temp(
- type, manage_ref=self.use_managed_ref)
- else:
- self.temp_code = None
-
- def release_temp_result(self, code):
- if not self.temp_code:
- if not self.result_is_used:
- # not used anyway, so ignore if not set up
- return
- pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- if self.old_temp:
- raise RuntimeError("temp %s released multiple times in %s at %r" % (
- self.old_temp, self.__class__.__name__, pos))
- else:
- raise RuntimeError("no temp, but release requested in %s at %r" % (
- self.__class__.__name__, pos))
- code.funcstate.release_temp(self.temp_code)
- self.old_temp = self.temp_code
- self.temp_code = None
-
- # ---------------- Code Generation -----------------
-
- def make_owned_reference(self, code):
- """
- If result is a pyobject, make sure we own a reference to it.
- If the result is in a temp, it is already a new reference.
- """
- if self.type.is_pyobject and not self.result_in_temp():
- code.put_incref(self.result(), self.ctype())
-
- def make_owned_memoryviewslice(self, code):
- """
- Make sure we own the reference to this memoryview slice.
- """
- if not self.result_in_temp():
- code.put_incref_memoryviewslice(self.result(),
- have_gil=self.in_nogil_context)
-
- def generate_evaluation_code(self, code):
- # Generate code to evaluate this node and
- # its sub-expressions, and dispose of any
- # temporary results of its sub-expressions.
- self.generate_subexpr_evaluation_code(code)
-
- code.mark_pos(self.pos)
- if self.is_temp:
- self.allocate_temp_result(code)
-
- self.generate_result_code(code)
- if self.is_temp and not (self.type.is_string or self.type.is_pyunicode_ptr):
- # If we are temp we do not need to wait until this node is disposed
- # before disposing children.
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
-
- def generate_subexpr_evaluation_code(self, code):
- for node in self.subexpr_nodes():
- node.generate_evaluation_code(code)
-
- def generate_result_code(self, code):
- self.not_implemented("generate_result_code")
-
- def generate_disposal_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- if self.result():
- if self.type.is_pyobject:
- code.put_decref_clear(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=not self.in_nogil_context)
+ self.temp_code = code.funcstate.allocate_temp(
+ type, manage_ref=self.use_managed_ref)
+ else:
+ self.temp_code = None
+
+ def release_temp_result(self, code):
+ if not self.temp_code:
+ if not self.result_is_used:
+ # not used anyway, so ignore if not set up
+ return
+ pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
+ if self.old_temp:
+ raise RuntimeError("temp %s released multiple times in %s at %r" % (
+ self.old_temp, self.__class__.__name__, pos))
+ else:
+ raise RuntimeError("no temp, but release requested in %s at %r" % (
+ self.__class__.__name__, pos))
+ code.funcstate.release_temp(self.temp_code)
+ self.old_temp = self.temp_code
+ self.temp_code = None
+
+ # ---------------- Code Generation -----------------
+
+ def make_owned_reference(self, code):
+ """
+ If result is a pyobject, make sure we own a reference to it.
+ If the result is in a temp, it is already a new reference.
+ """
+ if self.type.is_pyobject and not self.result_in_temp():
+ code.put_incref(self.result(), self.ctype())
+
+ def make_owned_memoryviewslice(self, code):
+ """
+ Make sure we own the reference to this memoryview slice.
+ """
+ if not self.result_in_temp():
+ code.put_incref_memoryviewslice(self.result(),
+ have_gil=self.in_nogil_context)
+
+ def generate_evaluation_code(self, code):
+ # Generate code to evaluate this node and
+ # its sub-expressions, and dispose of any
+ # temporary results of its sub-expressions.
+ self.generate_subexpr_evaluation_code(code)
+
+ code.mark_pos(self.pos)
+ if self.is_temp:
+ self.allocate_temp_result(code)
+
+ self.generate_result_code(code)
+ if self.is_temp and not (self.type.is_string or self.type.is_pyunicode_ptr):
+ # If we are temp we do not need to wait until this node is disposed
+ # before disposing children.
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+
+ def generate_subexpr_evaluation_code(self, code):
+ for node in self.subexpr_nodes():
+ node.generate_evaluation_code(code)
+
+ def generate_result_code(self, code):
+ self.not_implemented("generate_result_code")
+
+ def generate_disposal_code(self, code):
+ if self.is_temp:
+ if self.type.is_string or self.type.is_pyunicode_ptr:
+ # postponed from self.generate_evaluation_code()
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+ if self.result():
+ if self.type.is_pyobject:
+ code.put_decref_clear(self.result(), self.ctype())
+ elif self.type.is_memoryviewslice:
+ code.put_xdecref_memoryviewslice(
+ self.result(), have_gil=not self.in_nogil_context)
code.putln("%s.memview = NULL;" % self.result())
code.putln("%s.data = NULL;" % self.result())
- else:
- # Already done if self.is_temp
- self.generate_subexpr_disposal_code(code)
-
- def generate_subexpr_disposal_code(self, code):
- # Generate code to dispose of temporary results
- # of all sub-expressions.
- for node in self.subexpr_nodes():
- node.generate_disposal_code(code)
-
- def generate_post_assignment_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- elif self.type.is_pyobject:
- code.putln("%s = 0;" % self.result())
- elif self.type.is_memoryviewslice:
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- self.generate_subexpr_disposal_code(code)
-
+ else:
+ # Already done if self.is_temp
+ self.generate_subexpr_disposal_code(code)
+
+ def generate_subexpr_disposal_code(self, code):
+ # Generate code to dispose of temporary results
+ # of all sub-expressions.
+ for node in self.subexpr_nodes():
+ node.generate_disposal_code(code)
+
+ def generate_post_assignment_code(self, code):
+ if self.is_temp:
+ if self.type.is_string or self.type.is_pyunicode_ptr:
+ # postponed from self.generate_evaluation_code()
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+ elif self.type.is_pyobject:
+ code.putln("%s = 0;" % self.result())
+ elif self.type.is_memoryviewslice:
+ code.putln("%s.memview = NULL;" % self.result())
+ code.putln("%s.data = NULL;" % self.result())
+ else:
+ self.generate_subexpr_disposal_code(code)
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
- # Stub method for nodes which are not legal as
- # the LHS of an assignment. An error will have
- # been reported earlier.
- pass
-
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- # Stub method for nodes that are not legal as
- # the argument of a del statement. An error
- # will have been reported earlier.
- pass
-
- def free_temps(self, code):
- if self.is_temp:
- if not self.type.is_void:
- self.release_temp_result(code)
- else:
- self.free_subexpr_temps(code)
-
- def free_subexpr_temps(self, code):
- for sub in self.subexpr_nodes():
- sub.free_temps(code)
-
- def generate_function_definitions(self, env, code):
- pass
-
- # ---------------- Annotation ---------------------
-
- def annotate(self, code):
- for node in self.subexpr_nodes():
- node.annotate(code)
-
- # ----------------- Coercion ----------------------
-
- def coerce_to(self, dst_type, env):
- # Coerce the result so that it can be assigned to
- # something of type dst_type. If processing is necessary,
- # wraps this node in a coercion node and returns that.
- # Otherwise, returns this node unchanged.
- #
- # This method is called during the analyse_expressions
- # phase of the src_node's processing.
- #
- # Note that subclasses that override this (especially
- # ConstNodes) must not (re-)set their own .type attribute
- # here. Since expression nodes may turn up in different
- # places in the tree (e.g. inside of CloneNodes in cascaded
- # assignments), this method must return a new node instance
- # if it changes the type.
- #
- src = self
- src_type = self.type
-
- if self.check_for_coercion_error(dst_type, env):
- return self
-
+ # Stub method for nodes which are not legal as
+ # the LHS of an assignment. An error will have
+ # been reported earlier.
+ pass
+
+ def generate_deletion_code(self, code, ignore_nonexisting=False):
+ # Stub method for nodes that are not legal as
+ # the argument of a del statement. An error
+ # will have been reported earlier.
+ pass
+
+ def free_temps(self, code):
+ if self.is_temp:
+ if not self.type.is_void:
+ self.release_temp_result(code)
+ else:
+ self.free_subexpr_temps(code)
+
+ def free_subexpr_temps(self, code):
+ for sub in self.subexpr_nodes():
+ sub.free_temps(code)
+
+ def generate_function_definitions(self, env, code):
+ pass
+
+ # ---------------- Annotation ---------------------
+
+ def annotate(self, code):
+ for node in self.subexpr_nodes():
+ node.annotate(code)
+
+ # ----------------- Coercion ----------------------
+
+ def coerce_to(self, dst_type, env):
+ # Coerce the result so that it can be assigned to
+ # something of type dst_type. If processing is necessary,
+ # wraps this node in a coercion node and returns that.
+ # Otherwise, returns this node unchanged.
+ #
+ # This method is called during the analyse_expressions
+ # phase of the src_node's processing.
+ #
+ # Note that subclasses that override this (especially
+ # ConstNodes) must not (re-)set their own .type attribute
+ # here. Since expression nodes may turn up in different
+ # places in the tree (e.g. inside of CloneNodes in cascaded
+ # assignments), this method must return a new node instance
+ # if it changes the type.
+ #
+ src = self
+ src_type = self.type
+
+ if self.check_for_coercion_error(dst_type, env):
+ return self
+
used_as_reference = dst_type.is_reference
if used_as_reference and not src_type.is_reference:
- dst_type = dst_type.ref_base_type
-
- if src_type.is_const:
- src_type = src_type.const_base_type
-
- if src_type.is_fused or dst_type.is_fused:
- # See if we are coercing a fused function to a pointer to a
- # specialized function
- if (src_type.is_cfunction and not dst_type.is_fused and
- dst_type.is_ptr and dst_type.base_type.is_cfunction):
-
- dst_type = dst_type.base_type
-
- for signature in src_type.get_all_specialized_function_types():
- if signature.same_as(dst_type):
- src.type = signature
- src.entry = src.type.entry
- src.entry.used = True
- return self
-
- if src_type.is_fused:
- error(self.pos, "Type is not specialized")
+ dst_type = dst_type.ref_base_type
+
+ if src_type.is_const:
+ src_type = src_type.const_base_type
+
+ if src_type.is_fused or dst_type.is_fused:
+ # See if we are coercing a fused function to a pointer to a
+ # specialized function
+ if (src_type.is_cfunction and not dst_type.is_fused and
+ dst_type.is_ptr and dst_type.base_type.is_cfunction):
+
+ dst_type = dst_type.base_type
+
+ for signature in src_type.get_all_specialized_function_types():
+ if signature.same_as(dst_type):
+ src.type = signature
+ src.entry = src.type.entry
+ src.entry.used = True
+ return self
+
+ if src_type.is_fused:
+ error(self.pos, "Type is not specialized")
elif src_type.is_null_ptr and dst_type.is_ptr:
# NULL can be implicitly cast to any pointer type
return self
- else:
- error(self.pos, "Cannot coerce to a type that is not specialized")
-
- self.type = error_type
- return self
-
- if self.coercion_type is not None:
- # This is purely for error checking purposes!
- node = NameNode(self.pos, name='', type=self.coercion_type)
- node.coerce_to(dst_type, env)
-
- if dst_type.is_memoryviewslice:
- from . import MemoryView
- if not src.type.is_memoryviewslice:
- if src.type.is_pyobject:
- src = CoerceToMemViewSliceNode(src, dst_type, env)
- elif src.type.is_array:
+ else:
+ error(self.pos, "Cannot coerce to a type that is not specialized")
+
+ self.type = error_type
+ return self
+
+ if self.coercion_type is not None:
+ # This is purely for error checking purposes!
+ node = NameNode(self.pos, name='', type=self.coercion_type)
+ node.coerce_to(dst_type, env)
+
+ if dst_type.is_memoryviewslice:
+ from . import MemoryView
+ if not src.type.is_memoryviewslice:
+ if src.type.is_pyobject:
+ src = CoerceToMemViewSliceNode(src, dst_type, env)
+ elif src.type.is_array:
src = CythonArrayNode.from_carray(src, env).coerce_to(dst_type, env)
- elif not src_type.is_error:
- error(self.pos,
+ elif not src_type.is_error:
+ error(self.pos,
"Cannot convert '%s' to memoryviewslice" % (src_type,))
else:
if src.type.writable_needed:
@@ -938,18 +938,18 @@ class ExprNode(Node):
else:
msg = "Different base types for memoryviews (%s, %s)"
tup = src.type.dtype, dst_type.dtype
-
+
error(self.pos, msg % tup)
-
- elif dst_type.is_pyobject:
- if not src.type.is_pyobject:
- if dst_type is bytes_type and src.type.is_int:
- src = CoerceIntToBytesNode(src, env)
- else:
- src = CoerceToPyTypeNode(src, env, type=dst_type)
- if not src.type.subtype_of(dst_type):
- if src.constant_result is not None:
- src = PyTypeTestNode(src, dst_type, env)
+
+ elif dst_type.is_pyobject:
+ if not src.type.is_pyobject:
+ if dst_type is bytes_type and src.type.is_int:
+ src = CoerceIntToBytesNode(src, env)
+ else:
+ src = CoerceToPyTypeNode(src, env, type=dst_type)
+ if not src.type.subtype_of(dst_type):
+ if src.constant_result is not None:
+ src = PyTypeTestNode(src, dst_type, env)
elif is_pythran_expr(dst_type) and is_pythran_supported_type(src.type):
# We let the compiler decide whether this is valid
return src
@@ -960,58 +960,58 @@ class ExprNode(Node):
return src
# Else, we need to convert the Pythran expression to a Python object
src = CoerceToPyTypeNode(src, env, type=dst_type)
- elif src.type.is_pyobject:
+ elif src.type.is_pyobject:
if used_as_reference and dst_type.is_cpp_class:
warning(
self.pos,
"Cannot pass Python object as C++ data structure reference (%s &), will pass by copy." % dst_type)
- src = CoerceFromPyTypeNode(dst_type, src, env)
- elif (dst_type.is_complex
- and src_type != dst_type
- and dst_type.assignable_from(src_type)):
- src = CoerceToComplexNode(src, dst_type, env)
- else: # neither src nor dst are py types
- # Added the string comparison, since for c types that
- # is enough, but Cython gets confused when the types are
- # in different pxi files.
+ src = CoerceFromPyTypeNode(dst_type, src, env)
+ elif (dst_type.is_complex
+ and src_type != dst_type
+ and dst_type.assignable_from(src_type)):
+ src = CoerceToComplexNode(src, dst_type, env)
+ else: # neither src nor dst are py types
+ # Added the string comparison, since for c types that
+ # is enough, but Cython gets confused when the types are
+ # in different pxi files.
# TODO: Remove this hack and require shared declarations.
if not (src.type == dst_type or str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
- self.fail_assignment(dst_type)
- return src
-
- def fail_assignment(self, dst_type):
- error(self.pos, "Cannot assign type '%s' to '%s'" % (self.type, dst_type))
-
- def check_for_coercion_error(self, dst_type, env, fail=False, default=None):
- if fail and not default:
- default = "Cannot assign type '%(FROM)s' to '%(TO)s'"
- message = find_coercion_error((self.type, dst_type), default, env)
- if message is not None:
- error(self.pos, message % {'FROM': self.type, 'TO': dst_type})
- return True
- if fail:
- self.fail_assignment(dst_type)
- return True
- return False
-
- def coerce_to_pyobject(self, env):
- return self.coerce_to(PyrexTypes.py_object_type, env)
-
- def coerce_to_boolean(self, env):
- # Coerce result to something acceptable as
- # a boolean value.
-
- # if it's constant, calculate the result now
- if self.has_constant_result():
- bool_value = bool(self.constant_result)
- return BoolNode(self.pos, value=bool_value,
- constant_result=bool_value)
-
- type = self.type
- if type.is_enum or type.is_error:
- return self
- elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
- return CoerceToBooleanNode(self, env)
+ self.fail_assignment(dst_type)
+ return src
+
+ def fail_assignment(self, dst_type):
+ error(self.pos, "Cannot assign type '%s' to '%s'" % (self.type, dst_type))
+
+ def check_for_coercion_error(self, dst_type, env, fail=False, default=None):
+ if fail and not default:
+ default = "Cannot assign type '%(FROM)s' to '%(TO)s'"
+ message = find_coercion_error((self.type, dst_type), default, env)
+ if message is not None:
+ error(self.pos, message % {'FROM': self.type, 'TO': dst_type})
+ return True
+ if fail:
+ self.fail_assignment(dst_type)
+ return True
+ return False
+
+ def coerce_to_pyobject(self, env):
+ return self.coerce_to(PyrexTypes.py_object_type, env)
+
+ def coerce_to_boolean(self, env):
+ # Coerce result to something acceptable as
+ # a boolean value.
+
+ # if it's constant, calculate the result now
+ if self.has_constant_result():
+ bool_value = bool(self.constant_result)
+ return BoolNode(self.pos, value=bool_value,
+ constant_result=bool_value)
+
+ type = self.type
+ if type.is_enum or type.is_error:
+ return self
+ elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
+ return CoerceToBooleanNode(self, env)
elif type.is_cpp_class and type.scope and type.scope.lookup("operator bool"):
return SimpleCallNode(
self.pos,
@@ -1022,319 +1022,319 @@ class ExprNode(Node):
bool_value = len(type.components) == 0
return BoolNode(self.pos, value=bool_value,
constant_result=bool_value)
- else:
- error(self.pos, "Type '%s' not acceptable as a boolean" % type)
- return self
-
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.type.is_int:
- return self
- else:
- return self.coerce_to(PyrexTypes.c_long_type, env)
-
- def coerce_to_temp(self, env):
- # Ensure that the result is in a temporary.
- if self.result_in_temp():
- return self
- else:
- return CoerceToTempNode(self, env)
-
- def coerce_to_simple(self, env):
- # Ensure that the result is simple (see is_simple).
- if self.is_simple():
- return self
- else:
- return self.coerce_to_temp(env)
-
- def is_simple(self):
- # A node is simple if its result is something that can
- # be referred to without performing any operations, e.g.
- # a constant, local var, C global var, struct member
- # reference, or temporary.
- return self.result_in_temp()
-
- def may_be_none(self):
- if self.type and not (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- return False
- if self.has_constant_result():
- return self.constant_result is not None
- return True
-
- def as_cython_attribute(self):
- return None
-
- def as_none_safe_node(self, message, error="PyExc_TypeError", format_args=()):
- # Wraps the node in a NoneCheckNode if it is not known to be
- # not-None (e.g. because it is a Python literal).
- if self.may_be_none():
- return NoneCheckNode(self, error, message, format_args)
- else:
- return self
-
- @classmethod
- def from_node(cls, node, **kwargs):
- """Instantiate this node class from another node, properly
- copying over all attributes that one would forget otherwise.
- """
- attributes = "cf_state cf_maybe_null cf_is_null constant_result".split()
- for attr_name in attributes:
- if attr_name in kwargs:
- continue
- try:
- value = getattr(node, attr_name)
- except AttributeError:
- pass
- else:
- kwargs[attr_name] = value
- return cls(node.pos, **kwargs)
-
-
-class AtomicExprNode(ExprNode):
- # Abstract base class for expression nodes which have
- # no sub-expressions.
-
- subexprs = []
-
- # Override to optimize -- we know we have no children
- def generate_subexpr_evaluation_code(self, code):
- pass
- def generate_subexpr_disposal_code(self, code):
- pass
-
-class PyConstNode(AtomicExprNode):
- # Abstract base class for constant Python values.
-
- is_literal = 1
- type = py_object_type
-
- def is_simple(self):
- return 1
-
- def may_be_none(self):
- return False
-
- def analyse_types(self, env):
- return self
-
- def calculate_result_code(self):
- return self.value
-
- def generate_result_code(self, code):
- pass
-
-
-class NoneNode(PyConstNode):
- # The constant value None
-
- is_none = 1
- value = "Py_None"
-
- constant_result = None
-
- nogil_check = None
-
- def compile_time_value(self, denv):
- return None
-
- def may_be_none(self):
- return True
-
+ else:
+ error(self.pos, "Type '%s' not acceptable as a boolean" % type)
+ return self
+
+ def coerce_to_integer(self, env):
+ # If not already some C integer type, coerce to longint.
+ if self.type.is_int:
+ return self
+ else:
+ return self.coerce_to(PyrexTypes.c_long_type, env)
+
+ def coerce_to_temp(self, env):
+ # Ensure that the result is in a temporary.
+ if self.result_in_temp():
+ return self
+ else:
+ return CoerceToTempNode(self, env)
+
+ def coerce_to_simple(self, env):
+ # Ensure that the result is simple (see is_simple).
+ if self.is_simple():
+ return self
+ else:
+ return self.coerce_to_temp(env)
+
+ def is_simple(self):
+ # A node is simple if its result is something that can
+ # be referred to without performing any operations, e.g.
+ # a constant, local var, C global var, struct member
+ # reference, or temporary.
+ return self.result_in_temp()
+
+ def may_be_none(self):
+ if self.type and not (self.type.is_pyobject or
+ self.type.is_memoryviewslice):
+ return False
+ if self.has_constant_result():
+ return self.constant_result is not None
+ return True
+
+ def as_cython_attribute(self):
+ return None
+
+ def as_none_safe_node(self, message, error="PyExc_TypeError", format_args=()):
+ # Wraps the node in a NoneCheckNode if it is not known to be
+ # not-None (e.g. because it is a Python literal).
+ if self.may_be_none():
+ return NoneCheckNode(self, error, message, format_args)
+ else:
+ return self
+
+ @classmethod
+ def from_node(cls, node, **kwargs):
+ """Instantiate this node class from another node, properly
+ copying over all attributes that one would forget otherwise.
+ """
+ attributes = "cf_state cf_maybe_null cf_is_null constant_result".split()
+ for attr_name in attributes:
+ if attr_name in kwargs:
+ continue
+ try:
+ value = getattr(node, attr_name)
+ except AttributeError:
+ pass
+ else:
+ kwargs[attr_name] = value
+ return cls(node.pos, **kwargs)
+
+
+class AtomicExprNode(ExprNode):
+ # Abstract base class for expression nodes which have
+ # no sub-expressions.
+
+ subexprs = []
+
+ # Override to optimize -- we know we have no children
+ def generate_subexpr_evaluation_code(self, code):
+ pass
+ def generate_subexpr_disposal_code(self, code):
+ pass
+
+class PyConstNode(AtomicExprNode):
+ # Abstract base class for constant Python values.
+
+ is_literal = 1
+ type = py_object_type
+
+ def is_simple(self):
+ return 1
+
+ def may_be_none(self):
+ return False
+
+ def analyse_types(self, env):
+ return self
+
+ def calculate_result_code(self):
+ return self.value
+
+ def generate_result_code(self, code):
+ pass
+
+
+class NoneNode(PyConstNode):
+ # The constant value None
+
+ is_none = 1
+ value = "Py_None"
+
+ constant_result = None
+
+ nogil_check = None
+
+ def compile_time_value(self, denv):
+ return None
+
+ def may_be_none(self):
+ return True
+
def coerce_to(self, dst_type, env):
if not (dst_type.is_pyobject or dst_type.is_memoryviewslice or dst_type.is_error):
# Catch this error early and loudly.
error(self.pos, "Cannot assign None to %s" % dst_type)
return super(NoneNode, self).coerce_to(dst_type, env)
-
-
-class EllipsisNode(PyConstNode):
- # '...' in a subscript list.
-
- value = "Py_Ellipsis"
-
- constant_result = Ellipsis
-
- def compile_time_value(self, denv):
- return Ellipsis
-
-
-class ConstNode(AtomicExprNode):
- # Abstract base type for literal constant nodes.
- #
- # value string C code fragment
-
- is_literal = 1
- nogil_check = None
-
- def is_simple(self):
- return 1
-
- def nonlocally_immutable(self):
- return 1
-
- def may_be_none(self):
- return False
-
- def analyse_types(self, env):
- return self # Types are held in class variables
-
- def check_const(self):
- return True
-
- def get_constant_c_result_code(self):
- return self.calculate_result_code()
-
- def calculate_result_code(self):
- return str(self.value)
-
- def generate_result_code(self, code):
- pass
-
-
-class BoolNode(ConstNode):
- type = PyrexTypes.c_bint_type
- # The constant value True or False
-
- def calculate_constant_result(self):
- self.constant_result = self.value
-
- def compile_time_value(self, denv):
- return self.value
-
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.value and 'Py_True' or 'Py_False'
- else:
- return str(int(self.value))
-
- def coerce_to(self, dst_type, env):
+
+
+class EllipsisNode(PyConstNode):
+ # '...' in a subscript list.
+
+ value = "Py_Ellipsis"
+
+ constant_result = Ellipsis
+
+ def compile_time_value(self, denv):
+ return Ellipsis
+
+
+class ConstNode(AtomicExprNode):
+ # Abstract base type for literal constant nodes.
+ #
+ # value string C code fragment
+
+ is_literal = 1
+ nogil_check = None
+
+ def is_simple(self):
+ return 1
+
+ def nonlocally_immutable(self):
+ return 1
+
+ def may_be_none(self):
+ return False
+
+ def analyse_types(self, env):
+ return self # Types are held in class variables
+
+ def check_const(self):
+ return True
+
+ def get_constant_c_result_code(self):
+ return self.calculate_result_code()
+
+ def calculate_result_code(self):
+ return str(self.value)
+
+ def generate_result_code(self, code):
+ pass
+
+
+class BoolNode(ConstNode):
+ type = PyrexTypes.c_bint_type
+ # The constant value True or False
+
+ def calculate_constant_result(self):
+ self.constant_result = self.value
+
+ def compile_time_value(self, denv):
+ return self.value
+
+ def calculate_result_code(self):
+ if self.type.is_pyobject:
+ return self.value and 'Py_True' or 'Py_False'
+ else:
+ return str(int(self.value))
+
+ def coerce_to(self, dst_type, env):
if dst_type == self.type:
return self
if dst_type is py_object_type and self.type is Builtin.bool_type:
return self
- if dst_type.is_pyobject and self.type.is_int:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.bool_type)
- if dst_type.is_int and self.type.is_pyobject:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type)
- return ConstNode.coerce_to(self, dst_type, env)
-
-
-class NullNode(ConstNode):
- type = PyrexTypes.c_null_ptr_type
- value = "NULL"
- constant_result = 0
-
- def get_constant_c_result_code(self):
- return self.value
-
-
-class CharNode(ConstNode):
- type = PyrexTypes.c_char_type
-
- def calculate_constant_result(self):
- self.constant_result = ord(self.value)
-
- def compile_time_value(self, denv):
- return ord(self.value)
-
- def calculate_result_code(self):
- return "'%s'" % StringEncoding.escape_char(self.value)
-
-
-class IntNode(ConstNode):
-
- # unsigned "" or "U"
- # longness "" or "L" or "LL"
- # is_c_literal True/False/None creator considers this a C integer literal
-
- unsigned = ""
- longness = ""
- is_c_literal = None # unknown
-
- def __init__(self, pos, **kwds):
- ExprNode.__init__(self, pos, **kwds)
- if 'type' not in kwds:
- self.type = self.find_suitable_type_for_value()
-
- def find_suitable_type_for_value(self):
- if self.constant_result is constant_value_not_set:
- try:
- self.calculate_constant_result()
- except ValueError:
- pass
- # we ignore 'is_c_literal = True' and instead map signed 32bit
- # integers as C long values
- if self.is_c_literal or \
+ if dst_type.is_pyobject and self.type.is_int:
+ return BoolNode(
+ self.pos, value=self.value,
+ constant_result=self.constant_result,
+ type=Builtin.bool_type)
+ if dst_type.is_int and self.type.is_pyobject:
+ return BoolNode(
+ self.pos, value=self.value,
+ constant_result=self.constant_result,
+ type=PyrexTypes.c_bint_type)
+ return ConstNode.coerce_to(self, dst_type, env)
+
+
+class NullNode(ConstNode):
+ type = PyrexTypes.c_null_ptr_type
+ value = "NULL"
+ constant_result = 0
+
+ def get_constant_c_result_code(self):
+ return self.value
+
+
+class CharNode(ConstNode):
+ type = PyrexTypes.c_char_type
+
+ def calculate_constant_result(self):
+ self.constant_result = ord(self.value)
+
+ def compile_time_value(self, denv):
+ return ord(self.value)
+
+ def calculate_result_code(self):
+ return "'%s'" % StringEncoding.escape_char(self.value)
+
+
+class IntNode(ConstNode):
+
+ # unsigned "" or "U"
+ # longness "" or "L" or "LL"
+ # is_c_literal True/False/None creator considers this a C integer literal
+
+ unsigned = ""
+ longness = ""
+ is_c_literal = None # unknown
+
+ def __init__(self, pos, **kwds):
+ ExprNode.__init__(self, pos, **kwds)
+ if 'type' not in kwds:
+ self.type = self.find_suitable_type_for_value()
+
+ def find_suitable_type_for_value(self):
+ if self.constant_result is constant_value_not_set:
+ try:
+ self.calculate_constant_result()
+ except ValueError:
+ pass
+ # we ignore 'is_c_literal = True' and instead map signed 32bit
+ # integers as C long values
+ if self.is_c_literal or \
not self.has_constant_result() or \
- self.unsigned or self.longness == 'LL':
- # clearly a C literal
- rank = (self.longness == 'LL') and 2 or 1
- suitable_type = PyrexTypes.modifiers_and_name_to_type[not self.unsigned, rank, "int"]
- if self.type:
- suitable_type = PyrexTypes.widest_numeric_type(suitable_type, self.type)
- else:
- # C literal or Python literal - split at 32bit boundary
- if -2**31 <= self.constant_result < 2**31:
- if self.type and self.type.is_int:
- suitable_type = self.type
- else:
- suitable_type = PyrexTypes.c_long_type
- else:
- suitable_type = PyrexTypes.py_object_type
- return suitable_type
-
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- elif dst_type.is_float:
- if self.has_constant_result():
- return FloatNode(self.pos, value='%d.0' % int(self.constant_result), type=dst_type,
- constant_result=float(self.constant_result))
- else:
- return FloatNode(self.pos, value=self.value, type=dst_type,
- constant_result=not_a_constant)
- if dst_type.is_numeric and not dst_type.is_complex:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
+ self.unsigned or self.longness == 'LL':
+ # clearly a C literal
+ rank = (self.longness == 'LL') and 2 or 1
+ suitable_type = PyrexTypes.modifiers_and_name_to_type[not self.unsigned, rank, "int"]
+ if self.type:
+ suitable_type = PyrexTypes.widest_numeric_type(suitable_type, self.type)
+ else:
+ # C literal or Python literal - split at 32bit boundary
+ if -2**31 <= self.constant_result < 2**31:
+ if self.type and self.type.is_int:
+ suitable_type = self.type
+ else:
+ suitable_type = PyrexTypes.c_long_type
+ else:
+ suitable_type = PyrexTypes.py_object_type
+ return suitable_type
+
+ def coerce_to(self, dst_type, env):
+ if self.type is dst_type:
+ return self
+ elif dst_type.is_float:
+ if self.has_constant_result():
+ return FloatNode(self.pos, value='%d.0' % int(self.constant_result), type=dst_type,
+ constant_result=float(self.constant_result))
+ else:
+ return FloatNode(self.pos, value=self.value, type=dst_type,
+ constant_result=not_a_constant)
+ if dst_type.is_numeric and not dst_type.is_complex:
+ node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
type=dst_type, is_c_literal=True,
- unsigned=self.unsigned, longness=self.longness)
- return node
- elif dst_type.is_pyobject:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
+ unsigned=self.unsigned, longness=self.longness)
+ return node
+ elif dst_type.is_pyobject:
+ node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
type=PyrexTypes.py_object_type, is_c_literal=False,
- unsigned=self.unsigned, longness=self.longness)
- else:
- # FIXME: not setting the type here to keep it working with
- # complex numbers. Should they be special cased?
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- unsigned=self.unsigned, longness=self.longness)
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
-
- def coerce_to_boolean(self, env):
- return IntNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type,
- unsigned=self.unsigned, longness=self.longness)
-
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- # pre-allocate a Python version of the number
- plain_integer_string = str(Utils.str_to_number(self.value))
- self.result_code = code.get_py_int(plain_integer_string, self.longness)
- else:
- self.result_code = self.get_constant_c_result_code()
-
- def get_constant_c_result_code(self):
+ unsigned=self.unsigned, longness=self.longness)
+ else:
+ # FIXME: not setting the type here to keep it working with
+ # complex numbers. Should they be special cased?
+ node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
+ unsigned=self.unsigned, longness=self.longness)
+ # We still need to perform normal coerce_to processing on the
+ # result, because we might be coercing to an extension type,
+ # in which case a type test node will be needed.
+ return ConstNode.coerce_to(node, dst_type, env)
+
+ def coerce_to_boolean(self, env):
+ return IntNode(
+ self.pos, value=self.value,
+ constant_result=self.constant_result,
+ type=PyrexTypes.c_bint_type,
+ unsigned=self.unsigned, longness=self.longness)
+
+ def generate_evaluation_code(self, code):
+ if self.type.is_pyobject:
+ # pre-allocate a Python version of the number
+ plain_integer_string = str(Utils.str_to_number(self.value))
+ self.result_code = code.get_py_int(plain_integer_string, self.longness)
+ else:
+ self.result_code = self.get_constant_c_result_code()
+
+ def get_constant_c_result_code(self):
unsigned, longness = self.unsigned, self.longness
literal = self.value_as_c_integer_string()
if not (unsigned or longness) and self.type.is_int and literal[0] == '-' and literal[1] != '0':
@@ -1344,9 +1344,9 @@ class IntNode(ConstNode):
elif self.type.rank >= PyrexTypes.c_long_type.rank:
longness = 'L'
return literal + unsigned + longness
-
- def value_as_c_integer_string(self):
- value = self.value
+
+ def value_as_c_integer_string(self):
+ value = self.value
if len(value) <= 2:
# too short to go wrong (and simplifies code below)
return value
@@ -1372,66 +1372,66 @@ class IntNode(ConstNode):
# but they do for hex (see C standard 6.4.4.1)
value = '0x%X' % int(value)
return neg_sign + value
-
- def calculate_result_code(self):
- return self.result_code
-
- def calculate_constant_result(self):
- self.constant_result = Utils.str_to_number(self.value)
-
- def compile_time_value(self, denv):
- return Utils.str_to_number(self.value)
-
-class FloatNode(ConstNode):
- type = PyrexTypes.c_double_type
-
- def calculate_constant_result(self):
- self.constant_result = float(self.value)
-
- def compile_time_value(self, denv):
- return float(self.value)
-
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject and self.type.is_float:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.float_type)
- if dst_type.is_float and self.type.is_pyobject:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=dst_type)
- return ConstNode.coerce_to(self, dst_type, env)
-
- def calculate_result_code(self):
- return self.result_code
-
- def get_constant_c_result_code(self):
- strval = self.value
+
+ def calculate_result_code(self):
+ return self.result_code
+
+ def calculate_constant_result(self):
+ self.constant_result = Utils.str_to_number(self.value)
+
+ def compile_time_value(self, denv):
+ return Utils.str_to_number(self.value)
+
+class FloatNode(ConstNode):
+ type = PyrexTypes.c_double_type
+
+ def calculate_constant_result(self):
+ self.constant_result = float(self.value)
+
+ def compile_time_value(self, denv):
+ return float(self.value)
+
+ def coerce_to(self, dst_type, env):
+ if dst_type.is_pyobject and self.type.is_float:
+ return FloatNode(
+ self.pos, value=self.value,
+ constant_result=self.constant_result,
+ type=Builtin.float_type)
+ if dst_type.is_float and self.type.is_pyobject:
+ return FloatNode(
+ self.pos, value=self.value,
+ constant_result=self.constant_result,
+ type=dst_type)
+ return ConstNode.coerce_to(self, dst_type, env)
+
+ def calculate_result_code(self):
+ return self.result_code
+
+ def get_constant_c_result_code(self):
+ strval = self.value
assert isinstance(strval, basestring)
- cmpval = repr(float(strval))
- if cmpval == 'nan':
- return "(Py_HUGE_VAL * 0)"
- elif cmpval == 'inf':
- return "Py_HUGE_VAL"
- elif cmpval == '-inf':
- return "(-Py_HUGE_VAL)"
- else:
- return strval
-
- def generate_evaluation_code(self, code):
- c_value = self.get_constant_c_result_code()
- if self.type.is_pyobject:
- self.result_code = code.get_py_float(self.value, c_value)
- else:
- self.result_code = c_value
-
-
-def _analyse_name_as_type(name, pos, env):
- type = PyrexTypes.parse_basic_type(name)
- if type is not None:
- return type
+ cmpval = repr(float(strval))
+ if cmpval == 'nan':
+ return "(Py_HUGE_VAL * 0)"
+ elif cmpval == 'inf':
+ return "Py_HUGE_VAL"
+ elif cmpval == '-inf':
+ return "(-Py_HUGE_VAL)"
+ else:
+ return strval
+
+ def generate_evaluation_code(self, code):
+ c_value = self.get_constant_c_result_code()
+ if self.type.is_pyobject:
+ self.result_code = code.get_py_float(self.value, c_value)
+ else:
+ self.result_code = c_value
+
+
+def _analyse_name_as_type(name, pos, env):
+ type = PyrexTypes.parse_basic_type(name)
+ if type is not None:
+ return type
global_entry = env.global_scope().lookup(name)
if global_entry and global_entry.type and (
@@ -1441,7 +1441,7 @@ def _analyse_name_as_type(name, pos, env):
or global_entry.type.is_cpp_class):
return global_entry.type
- from .TreeFragment import TreeFragment
+ from .TreeFragment import TreeFragment
with local_errors(ignore=True):
pos = (pos[0], pos[1], pos[2]-7)
try:
@@ -1454,175 +1454,175 @@ def _analyse_name_as_type(name, pos, env):
sizeof_node = sizeof_node.analyse_types(env)
if isinstance(sizeof_node, SizeofTypeNode):
return sizeof_node.arg_type
- return None
-
-
-class BytesNode(ConstNode):
- # A char* or bytes literal
- #
- # value BytesLiteral
-
- is_string_literal = True
- # start off as Python 'bytes' to support len() in O(1)
- type = bytes_type
-
- def calculate_constant_result(self):
- self.constant_result = self.value
-
- def as_sliced_node(self, start, stop, step=None):
+ return None
+
+
+class BytesNode(ConstNode):
+ # A char* or bytes literal
+ #
+ # value BytesLiteral
+
+ is_string_literal = True
+ # start off as Python 'bytes' to support len() in O(1)
+ type = bytes_type
+
+ def calculate_constant_result(self):
+ self.constant_result = self.value
+
+ def as_sliced_node(self, start, stop, step=None):
value = StringEncoding.bytes_literal(self.value[start:stop:step], self.value.encoding)
return BytesNode(self.pos, value=value, constant_result=value)
-
- def compile_time_value(self, denv):
+
+ def compile_time_value(self, denv):
return self.value.byteencode()
-
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value.decode('ISO8859-1'), self.pos, env)
-
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
-
- def coerce_to_boolean(self, env):
- # This is special because testing a C char* for truth directly
- # would yield the wrong result.
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
-
- def coerce_to(self, dst_type, env):
- if self.type == dst_type:
- return self
- if dst_type.is_int:
- if not self.can_coerce_to_char_literal():
- error(self.pos, "Only single-character string literals can be coerced into ints.")
- return self
- if dst_type.is_unicode_char:
- error(self.pos, "Bytes literals cannot coerce to Py_UNICODE/Py_UCS4, use a unicode literal instead.")
- return self
- return CharNode(self.pos, value=self.value,
- constant_result=ord(self.value))
-
+
+ def analyse_as_type(self, env):
+ return _analyse_name_as_type(self.value.decode('ISO8859-1'), self.pos, env)
+
+ def can_coerce_to_char_literal(self):
+ return len(self.value) == 1
+
+ def coerce_to_boolean(self, env):
+ # This is special because testing a C char* for truth directly
+ # would yield the wrong result.
+ bool_value = bool(self.value)
+ return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
+
+ def coerce_to(self, dst_type, env):
+ if self.type == dst_type:
+ return self
+ if dst_type.is_int:
+ if not self.can_coerce_to_char_literal():
+ error(self.pos, "Only single-character string literals can be coerced into ints.")
+ return self
+ if dst_type.is_unicode_char:
+ error(self.pos, "Bytes literals cannot coerce to Py_UNICODE/Py_UCS4, use a unicode literal instead.")
+ return self
+ return CharNode(self.pos, value=self.value,
+ constant_result=ord(self.value))
+
node = BytesNode(self.pos, value=self.value, constant_result=self.constant_result)
- if dst_type.is_pyobject:
- if dst_type in (py_object_type, Builtin.bytes_type):
- node.type = Builtin.bytes_type
- else:
- self.check_for_coercion_error(dst_type, env, fail=True)
+ if dst_type.is_pyobject:
+ if dst_type in (py_object_type, Builtin.bytes_type):
+ node.type = Builtin.bytes_type
+ else:
+ self.check_for_coercion_error(dst_type, env, fail=True)
return node
elif dst_type in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- node.type = dst_type
- return node
+ node.type = dst_type
+ return node
elif dst_type in (PyrexTypes.c_uchar_ptr_type, PyrexTypes.c_const_uchar_ptr_type, PyrexTypes.c_void_ptr_type):
node.type = (PyrexTypes.c_const_char_ptr_type if dst_type == PyrexTypes.c_const_uchar_ptr_type
else PyrexTypes.c_char_ptr_type)
return CastNode(node, dst_type)
- elif dst_type.assignable_from(PyrexTypes.c_char_ptr_type):
+ elif dst_type.assignable_from(PyrexTypes.c_char_ptr_type):
# Exclude the case of passing a C string literal into a non-const C++ string.
if not dst_type.is_cpp_class or dst_type.is_const:
node.type = dst_type
return node
-
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
-
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
+
+ # We still need to perform normal coerce_to processing on the
+ # result, because we might be coercing to an extension type,
+ # in which case a type test node will be needed.
+ return ConstNode.coerce_to(node, dst_type, env)
+
+ def generate_evaluation_code(self, code):
+ if self.type.is_pyobject:
result = code.get_py_string_const(self.value)
elif self.type.is_const:
result = code.get_string_const(self.value)
- else:
+ else:
# not const => use plain C string literal and cast to mutable type
literal = self.value.as_c_string_literal()
# C++ may require a cast
result = typecast(self.type, PyrexTypes.c_void_ptr_type, literal)
self.result_code = result
-
- def get_constant_c_result_code(self):
- return None # FIXME
-
- def calculate_result_code(self):
- return self.result_code
-
-
-class UnicodeNode(ConstNode):
- # A Py_UNICODE* or unicode literal
- #
- # value EncodedString
- # bytes_value BytesLiteral the literal parsed as bytes string
- # ('-3' unicode literals only)
-
- is_string_literal = True
- bytes_value = None
- type = unicode_type
-
- def calculate_constant_result(self):
- self.constant_result = self.value
-
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value, self.pos, env)
-
- def as_sliced_node(self, start, stop, step=None):
- if StringEncoding.string_contains_surrogates(self.value[:stop]):
- # this is unsafe as it may give different results
- # in different runtimes
- return None
- value = StringEncoding.EncodedString(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.bytes_value is not None:
+
+ def get_constant_c_result_code(self):
+ return None # FIXME
+
+ def calculate_result_code(self):
+ return self.result_code
+
+
+class UnicodeNode(ConstNode):
+ # A Py_UNICODE* or unicode literal
+ #
+ # value EncodedString
+ # bytes_value BytesLiteral the literal parsed as bytes string
+ # ('-3' unicode literals only)
+
+ is_string_literal = True
+ bytes_value = None
+ type = unicode_type
+
+ def calculate_constant_result(self):
+ self.constant_result = self.value
+
+ def analyse_as_type(self, env):
+ return _analyse_name_as_type(self.value, self.pos, env)
+
+ def as_sliced_node(self, start, stop, step=None):
+ if StringEncoding.string_contains_surrogates(self.value[:stop]):
+ # this is unsafe as it may give different results
+ # in different runtimes
+ return None
+ value = StringEncoding.EncodedString(self.value[start:stop:step])
+ value.encoding = self.value.encoding
+ if self.bytes_value is not None:
bytes_value = StringEncoding.bytes_literal(
self.bytes_value[start:stop:step], self.bytes_value.encoding)
- else:
- bytes_value = None
- return UnicodeNode(
- self.pos, value=value, bytes_value=bytes_value,
- constant_result=value)
-
- def coerce_to(self, dst_type, env):
- if dst_type is self.type:
- pass
- elif dst_type.is_unicode_char:
- if not self.can_coerce_to_char_literal():
- error(self.pos,
- "Only single-character Unicode string literals or "
- "surrogate pairs can be coerced into Py_UCS4/Py_UNICODE.")
- return self
- int_value = ord(self.value)
- return IntNode(self.pos, type=dst_type, value=str(int_value),
- constant_result=int_value)
- elif not dst_type.is_pyobject:
- if dst_type.is_string and self.bytes_value is not None:
- # special case: '-3' enforced unicode literal used in a
- # C char* context
- return BytesNode(self.pos, value=self.bytes_value
- ).coerce_to(dst_type, env)
- if dst_type.is_pyunicode_ptr:
- node = UnicodeNode(self.pos, value=self.value)
- node.type = dst_type
- return node
- error(self.pos,
- "Unicode literals do not support coercion to C types other "
- "than Py_UNICODE/Py_UCS4 (for characters) or Py_UNICODE* "
- "(for strings).")
- elif dst_type not in (py_object_type, Builtin.basestring_type):
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
-
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
- ## or (len(self.value) == 2
- ## and (0xD800 <= self.value[0] <= 0xDBFF)
- ## and (0xDC00 <= self.value[1] <= 0xDFFF))
-
- def coerce_to_boolean(self, env):
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
-
- def contains_surrogates(self):
- return StringEncoding.string_contains_surrogates(self.value)
-
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
+ else:
+ bytes_value = None
+ return UnicodeNode(
+ self.pos, value=value, bytes_value=bytes_value,
+ constant_result=value)
+
+ def coerce_to(self, dst_type, env):
+ if dst_type is self.type:
+ pass
+ elif dst_type.is_unicode_char:
+ if not self.can_coerce_to_char_literal():
+ error(self.pos,
+ "Only single-character Unicode string literals or "
+ "surrogate pairs can be coerced into Py_UCS4/Py_UNICODE.")
+ return self
+ int_value = ord(self.value)
+ return IntNode(self.pos, type=dst_type, value=str(int_value),
+ constant_result=int_value)
+ elif not dst_type.is_pyobject:
+ if dst_type.is_string and self.bytes_value is not None:
+ # special case: '-3' enforced unicode literal used in a
+ # C char* context
+ return BytesNode(self.pos, value=self.bytes_value
+ ).coerce_to(dst_type, env)
+ if dst_type.is_pyunicode_ptr:
+ node = UnicodeNode(self.pos, value=self.value)
+ node.type = dst_type
+ return node
+ error(self.pos,
+ "Unicode literals do not support coercion to C types other "
+ "than Py_UNICODE/Py_UCS4 (for characters) or Py_UNICODE* "
+ "(for strings).")
+ elif dst_type not in (py_object_type, Builtin.basestring_type):
+ self.check_for_coercion_error(dst_type, env, fail=True)
+ return self
+
+ def can_coerce_to_char_literal(self):
+ return len(self.value) == 1
+ ## or (len(self.value) == 2
+ ## and (0xD800 <= self.value[0] <= 0xDBFF)
+ ## and (0xDC00 <= self.value[1] <= 0xDFFF))
+
+ def coerce_to_boolean(self, env):
+ bool_value = bool(self.value)
+ return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
+
+ def contains_surrogates(self):
+ return StringEncoding.string_contains_surrogates(self.value)
+
+ def generate_evaluation_code(self, code):
+ if self.type.is_pyobject:
# FIXME: this should go away entirely!
# Since string_contains_lone_surrogates() returns False for surrogate pairs in Py2/UCS2,
# Py2 can generate different code from Py3 here. Let's hope we get away with claiming that
@@ -1630,8 +1630,8 @@ class UnicodeNode(ConstNode):
# on P16/32bit Unicode platforms.
if StringEncoding.string_contains_lone_surrogates(self.value):
# lone (unpaired) surrogates are not really portable and cannot be
- # decoded by the UTF-8 codec in Py3.3
- self.result_code = code.get_py_const(py_object_type, 'ustring')
+ # decoded by the UTF-8 codec in Py3.3
+ self.result_code = code.get_py_const(py_object_type, 'ustring')
data_cname = code.get_string_const(
StringEncoding.BytesLiteral(self.value.encode('unicode_escape')))
const_code = code.get_cached_constants_writer(self.result_code)
@@ -1640,86 +1640,86 @@ class UnicodeNode(ConstNode):
const_code.mark_pos(self.pos)
const_code.putln(
"%s = PyUnicode_DecodeUnicodeEscape(%s, sizeof(%s) - 1, NULL); %s" % (
- self.result_code,
- data_cname,
- data_cname,
+ self.result_code,
+ data_cname,
+ data_cname,
const_code.error_goto_if_null(self.result_code, self.pos)))
const_code.put_error_if_neg(
- self.pos, "__Pyx_PyUnicode_READY(%s)" % self.result_code)
- else:
- self.result_code = code.get_py_string_const(self.value)
- else:
- self.result_code = code.get_pyunicode_ptr_const(self.value)
-
- def calculate_result_code(self):
- return self.result_code
-
- def compile_time_value(self, env):
- return self.value
-
-
-class StringNode(PyConstNode):
- # A Python str object, i.e. a byte string in Python 2.x and a
- # unicode string in Python 3.x
- #
- # value BytesLiteral (or EncodedString with ASCII content)
- # unicode_value EncodedString or None
- # is_identifier boolean
-
- type = str_type
- is_string_literal = True
- is_identifier = None
- unicode_value = None
-
- def calculate_constant_result(self):
- if self.unicode_value is not None:
- # only the Unicode value is portable across Py2/3
- self.constant_result = self.unicode_value
-
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.unicode_value or self.value.decode('ISO8859-1'), self.pos, env)
-
- def as_sliced_node(self, start, stop, step=None):
- value = type(self.value)(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.unicode_value is not None:
- if StringEncoding.string_contains_surrogates(self.unicode_value[:stop]):
- # this is unsafe as it may give different results in different runtimes
- return None
- unicode_value = StringEncoding.EncodedString(
- self.unicode_value[start:stop:step])
- else:
- unicode_value = None
- return StringNode(
- self.pos, value=value, unicode_value=unicode_value,
- constant_result=value, is_identifier=self.is_identifier)
-
- def coerce_to(self, dst_type, env):
- if dst_type is not py_object_type and not str_type.subtype_of(dst_type):
-# if dst_type is Builtin.bytes_type:
-# # special case: bytes = 'str literal'
-# return BytesNode(self.pos, value=self.value)
- if not dst_type.is_pyobject:
- return BytesNode(self.pos, value=self.value).coerce_to(dst_type, env)
- if dst_type is not Builtin.basestring_type:
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
-
- def can_coerce_to_char_literal(self):
- return not self.is_identifier and len(self.value) == 1
-
- def generate_evaluation_code(self, code):
- self.result_code = code.get_py_string_const(
- self.value, identifier=self.is_identifier, is_str=True,
- unicode_value=self.unicode_value)
-
- def get_constant_c_result_code(self):
- return None
-
- def calculate_result_code(self):
- return self.result_code
-
- def compile_time_value(self, env):
+ self.pos, "__Pyx_PyUnicode_READY(%s)" % self.result_code)
+ else:
+ self.result_code = code.get_py_string_const(self.value)
+ else:
+ self.result_code = code.get_pyunicode_ptr_const(self.value)
+
+ def calculate_result_code(self):
+ return self.result_code
+
+ def compile_time_value(self, env):
+ return self.value
+
+
+class StringNode(PyConstNode):
+ # A Python str object, i.e. a byte string in Python 2.x and a
+ # unicode string in Python 3.x
+ #
+ # value BytesLiteral (or EncodedString with ASCII content)
+ # unicode_value EncodedString or None
+ # is_identifier boolean
+
+ type = str_type
+ is_string_literal = True
+ is_identifier = None
+ unicode_value = None
+
+ def calculate_constant_result(self):
+ if self.unicode_value is not None:
+ # only the Unicode value is portable across Py2/3
+ self.constant_result = self.unicode_value
+
+ def analyse_as_type(self, env):
+ return _analyse_name_as_type(self.unicode_value or self.value.decode('ISO8859-1'), self.pos, env)
+
+ def as_sliced_node(self, start, stop, step=None):
+ value = type(self.value)(self.value[start:stop:step])
+ value.encoding = self.value.encoding
+ if self.unicode_value is not None:
+ if StringEncoding.string_contains_surrogates(self.unicode_value[:stop]):
+ # this is unsafe as it may give different results in different runtimes
+ return None
+ unicode_value = StringEncoding.EncodedString(
+ self.unicode_value[start:stop:step])
+ else:
+ unicode_value = None
+ return StringNode(
+ self.pos, value=value, unicode_value=unicode_value,
+ constant_result=value, is_identifier=self.is_identifier)
+
+ def coerce_to(self, dst_type, env):
+ if dst_type is not py_object_type and not str_type.subtype_of(dst_type):
+# if dst_type is Builtin.bytes_type:
+# # special case: bytes = 'str literal'
+# return BytesNode(self.pos, value=self.value)
+ if not dst_type.is_pyobject:
+ return BytesNode(self.pos, value=self.value).coerce_to(dst_type, env)
+ if dst_type is not Builtin.basestring_type:
+ self.check_for_coercion_error(dst_type, env, fail=True)
+ return self
+
+ def can_coerce_to_char_literal(self):
+ return not self.is_identifier and len(self.value) == 1
+
+ def generate_evaluation_code(self, code):
+ self.result_code = code.get_py_string_const(
+ self.value, identifier=self.is_identifier, is_str=True,
+ unicode_value=self.unicode_value)
+
+ def get_constant_c_result_code(self):
+ return None
+
+ def calculate_result_code(self):
+ return self.result_code
+
+ def compile_time_value(self, env):
if self.value.is_unicode:
return self.value
if not IS_PYTHON3:
@@ -1729,193 +1729,193 @@ class StringNode(PyConstNode):
if self.unicode_value is not None:
return self.unicode_value
return self.value.decode('iso8859-1')
-
-
-class IdentifierStringNode(StringNode):
- # A special str value that represents an identifier (bytes in Py2,
- # unicode in Py3).
- is_identifier = True
-
-
-class ImagNode(AtomicExprNode):
- # Imaginary number literal
- #
+
+
+class IdentifierStringNode(StringNode):
+ # A special str value that represents an identifier (bytes in Py2,
+ # unicode in Py3).
+ is_identifier = True
+
+
+class ImagNode(AtomicExprNode):
+ # Imaginary number literal
+ #
# value string imaginary part (float value)
-
- type = PyrexTypes.c_double_complex_type
-
- def calculate_constant_result(self):
+
+ type = PyrexTypes.c_double_complex_type
+
+ def calculate_constant_result(self):
self.constant_result = complex(0.0, float(self.value))
-
- def compile_time_value(self, denv):
+
+ def compile_time_value(self, denv):
return complex(0.0, float(self.value))
-
- def analyse_types(self, env):
- self.type.create_declaration_utility_code(env)
- return self
-
- def may_be_none(self):
- return False
-
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- node = ImagNode(self.pos, value=self.value)
- if dst_type.is_pyobject:
- node.is_temp = 1
+
+ def analyse_types(self, env):
+ self.type.create_declaration_utility_code(env)
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def coerce_to(self, dst_type, env):
+ if self.type is dst_type:
+ return self
+ node = ImagNode(self.pos, value=self.value)
+ if dst_type.is_pyobject:
+ node.is_temp = 1
node.type = Builtin.complex_type
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return AtomicExprNode.coerce_to(node, dst_type, env)
-
- gil_message = "Constructing complex number"
-
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.result()
- else:
- return "%s(0, %r)" % (self.type.from_parts, float(self.value))
-
- def generate_result_code(self, code):
- if self.type.is_pyobject:
- code.putln(
- "%s = PyComplex_FromDoubles(0.0, %r); %s" % (
- self.result(),
- float(self.value),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class NewExprNode(AtomicExprNode):
-
- # C++ new statement
- #
- # cppclass node c++ class to create
-
- type = None
-
- def infer_type(self, env):
- type = self.cppclass.analyse_as_type(env)
- if type is None or not type.is_cpp_class:
- error(self.pos, "new operator can only be applied to a C++ class")
- self.type = error_type
- return
- self.cpp_check(env)
+ # We still need to perform normal coerce_to processing on the
+ # result, because we might be coercing to an extension type,
+ # in which case a type test node will be needed.
+ return AtomicExprNode.coerce_to(node, dst_type, env)
+
+ gil_message = "Constructing complex number"
+
+ def calculate_result_code(self):
+ if self.type.is_pyobject:
+ return self.result()
+ else:
+ return "%s(0, %r)" % (self.type.from_parts, float(self.value))
+
+ def generate_result_code(self, code):
+ if self.type.is_pyobject:
+ code.putln(
+ "%s = PyComplex_FromDoubles(0.0, %r); %s" % (
+ self.result(),
+ float(self.value),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class NewExprNode(AtomicExprNode):
+
+ # C++ new statement
+ #
+ # cppclass node c++ class to create
+
+ type = None
+
+ def infer_type(self, env):
+ type = self.cppclass.analyse_as_type(env)
+ if type is None or not type.is_cpp_class:
+ error(self.pos, "new operator can only be applied to a C++ class")
+ self.type = error_type
+ return
+ self.cpp_check(env)
constructor = type.get_constructor(self.pos)
- self.class_type = type
- self.entry = constructor
- self.type = constructor.type
- return self.type
-
- def analyse_types(self, env):
- if self.type is None:
- self.infer_type(env)
- return self
-
- def may_be_none(self):
- return False
-
- def generate_result_code(self, code):
- pass
-
- def calculate_result_code(self):
+ self.class_type = type
+ self.entry = constructor
+ self.type = constructor.type
+ return self.type
+
+ def analyse_types(self, env):
+ if self.type is None:
+ self.infer_type(env)
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def generate_result_code(self, code):
+ pass
+
+ def calculate_result_code(self):
return "new " + self.class_type.empty_declaration_code()
-
-
-class NameNode(AtomicExprNode):
- # Reference to a local or global variable name.
- #
- # name string Python name of the variable
- # entry Entry Symbol table entry
- # type_entry Entry For extension type names, the original type entry
- # cf_is_null boolean Is uninitialized before this node
- # cf_maybe_null boolean Maybe uninitialized before this node
- # allow_null boolean Don't raise UnboundLocalError
- # nogil boolean Whether it is used in a nogil context
-
- is_name = True
- is_cython_module = False
- cython_attribute = None
- lhs_of_first_assignment = False # TODO: remove me
- is_used_as_rvalue = 0
- entry = None
- type_entry = None
- cf_maybe_null = True
- cf_is_null = False
- allow_null = False
- nogil = False
- inferred_type = None
-
- def as_cython_attribute(self):
- return self.cython_attribute
-
- def type_dependencies(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is not None and self.entry.type.is_unspecified:
- return (self,)
- else:
- return ()
-
- def infer_type(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is None or self.entry.type is unspecified_type:
- if self.inferred_type is not None:
- return self.inferred_type
- return py_object_type
- elif (self.entry.type.is_extension_type or self.entry.type.is_builtin_type) and \
- self.name == self.entry.type.name:
- # Unfortunately the type attribute of type objects
- # is used for the pointer to the type they represent.
- return type_type
- elif self.entry.type.is_cfunction:
- if self.entry.scope.is_builtin_scope:
- # special case: optimised builtin functions must be treated as Python objects
- return py_object_type
- else:
- # special case: referring to a C function must return its pointer
- return PyrexTypes.CPtrType(self.entry.type)
- else:
- # If entry is inferred as pyobject it's safe to use local
- # NameNode's inferred_type.
- if self.entry.type.is_pyobject and self.inferred_type:
- # Overflow may happen if integer
- if not (self.inferred_type.is_int and self.entry.might_overflow):
- return self.inferred_type
- return self.entry.type
-
- def compile_time_value(self, denv):
- try:
- return denv.lookup(self.name)
- except KeyError:
- error(self.pos, "Compile-time name '%s' not defined" % self.name)
-
- def get_constant_c_result_code(self):
- if not self.entry or self.entry.type.is_pyobject:
- return None
- return self.entry.cname
-
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a builtin
- # C function with a Python equivalent, manufacture a NameNode
- # referring to the Python builtin.
- #print "NameNode.coerce_to:", self.name, dst_type ###
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction:
- var_entry = entry.as_variable
- if var_entry:
- if var_entry.is_builtin and var_entry.is_const:
- var_entry = env.declare_builtin(var_entry.name, self.pos)
- node = NameNode(self.pos, name = self.name)
- node.entry = var_entry
- node.analyse_rvalue_entry(env)
- return node
-
- return super(NameNode, self).coerce_to(dst_type, env)
-
+
+
+class NameNode(AtomicExprNode):
+ # Reference to a local or global variable name.
+ #
+ # name string Python name of the variable
+ # entry Entry Symbol table entry
+ # type_entry Entry For extension type names, the original type entry
+ # cf_is_null boolean Is uninitialized before this node
+ # cf_maybe_null boolean Maybe uninitialized before this node
+ # allow_null boolean Don't raise UnboundLocalError
+ # nogil boolean Whether it is used in a nogil context
+
+ is_name = True
+ is_cython_module = False
+ cython_attribute = None
+ lhs_of_first_assignment = False # TODO: remove me
+ is_used_as_rvalue = 0
+ entry = None
+ type_entry = None
+ cf_maybe_null = True
+ cf_is_null = False
+ allow_null = False
+ nogil = False
+ inferred_type = None
+
+ def as_cython_attribute(self):
+ return self.cython_attribute
+
+ def type_dependencies(self, env):
+ if self.entry is None:
+ self.entry = env.lookup(self.name)
+ if self.entry is not None and self.entry.type.is_unspecified:
+ return (self,)
+ else:
+ return ()
+
+ def infer_type(self, env):
+ if self.entry is None:
+ self.entry = env.lookup(self.name)
+ if self.entry is None or self.entry.type is unspecified_type:
+ if self.inferred_type is not None:
+ return self.inferred_type
+ return py_object_type
+ elif (self.entry.type.is_extension_type or self.entry.type.is_builtin_type) and \
+ self.name == self.entry.type.name:
+ # Unfortunately the type attribute of type objects
+ # is used for the pointer to the type they represent.
+ return type_type
+ elif self.entry.type.is_cfunction:
+ if self.entry.scope.is_builtin_scope:
+ # special case: optimised builtin functions must be treated as Python objects
+ return py_object_type
+ else:
+ # special case: referring to a C function must return its pointer
+ return PyrexTypes.CPtrType(self.entry.type)
+ else:
+ # If entry is inferred as pyobject it's safe to use local
+ # NameNode's inferred_type.
+ if self.entry.type.is_pyobject and self.inferred_type:
+ # Overflow may happen if integer
+ if not (self.inferred_type.is_int and self.entry.might_overflow):
+ return self.inferred_type
+ return self.entry.type
+
+ def compile_time_value(self, denv):
+ try:
+ return denv.lookup(self.name)
+ except KeyError:
+ error(self.pos, "Compile-time name '%s' not defined" % self.name)
+
+ def get_constant_c_result_code(self):
+ if not self.entry or self.entry.type.is_pyobject:
+ return None
+ return self.entry.cname
+
+ def coerce_to(self, dst_type, env):
+ # If coercing to a generic pyobject and this is a builtin
+ # C function with a Python equivalent, manufacture a NameNode
+ # referring to the Python builtin.
+ #print "NameNode.coerce_to:", self.name, dst_type ###
+ if dst_type is py_object_type:
+ entry = self.entry
+ if entry and entry.is_cfunction:
+ var_entry = entry.as_variable
+ if var_entry:
+ if var_entry.is_builtin and var_entry.is_const:
+ var_entry = env.declare_builtin(var_entry.name, self.pos)
+ node = NameNode(self.pos, name = self.name)
+ node.entry = var_entry
+ node.analyse_rvalue_entry(env)
+ return node
+
+ return super(NameNode, self).coerce_to(dst_type, env)
+
def declare_from_annotation(self, env, as_target=False):
"""Implements PEP 526 annotation typing in a fairly relaxed way.
@@ -1944,65 +1944,65 @@ class NameNode(AtomicExprNode):
self.entry = env.declare_var(name, atype, self.pos, is_cdef=not as_target)
self.entry.annotation = annotation
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module.
- # Returns the module scope, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.as_module:
- return entry.as_module
- return None
-
- def analyse_as_type(self, env):
- if self.cython_attribute:
- type = PyrexTypes.parse_basic_type(self.cython_attribute)
- else:
- type = PyrexTypes.parse_basic_type(self.name)
- if type:
- return type
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- return entry.type
- else:
- return None
-
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type.
- # Returns the extension type, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
-
- def analyse_target_declaration(self, env):
- if not self.entry:
- self.entry = env.lookup_here(self.name)
+ def analyse_as_module(self, env):
+ # Try to interpret this as a reference to a cimported module.
+ # Returns the module scope, or None.
+ entry = self.entry
+ if not entry:
+ entry = env.lookup(self.name)
+ if entry and entry.as_module:
+ return entry.as_module
+ return None
+
+ def analyse_as_type(self, env):
+ if self.cython_attribute:
+ type = PyrexTypes.parse_basic_type(self.cython_attribute)
+ else:
+ type = PyrexTypes.parse_basic_type(self.name)
+ if type:
+ return type
+ entry = self.entry
+ if not entry:
+ entry = env.lookup(self.name)
+ if entry and entry.is_type:
+ return entry.type
+ else:
+ return None
+
+ def analyse_as_extension_type(self, env):
+ # Try to interpret this as a reference to an extension type.
+ # Returns the extension type, or None.
+ entry = self.entry
+ if not entry:
+ entry = env.lookup(self.name)
+ if entry and entry.is_type:
+ if entry.type.is_extension_type or entry.type.is_builtin_type:
+ return entry.type
+ return None
+
+ def analyse_target_declaration(self, env):
+ if not self.entry:
+ self.entry = env.lookup_here(self.name)
if not self.entry and self.annotation is not None:
# name : type = ...
self.declare_from_annotation(env, as_target=True)
- if not self.entry:
- if env.directives['warn.undeclared']:
- warning(self.pos, "implicit declaration of '%s'" % self.name, 1)
- if env.directives['infer_types'] != False:
- type = unspecified_type
- else:
- type = py_object_type
- self.entry = env.declare_var(self.name, type, self.pos)
- if self.entry.is_declared_generic:
- self.result_ctype = py_object_type
+ if not self.entry:
+ if env.directives['warn.undeclared']:
+ warning(self.pos, "implicit declaration of '%s'" % self.name, 1)
+ if env.directives['infer_types'] != False:
+ type = unspecified_type
+ else:
+ type = py_object_type
+ self.entry = env.declare_var(self.name, type, self.pos)
+ if self.entry.is_declared_generic:
+ self.result_ctype = py_object_type
if self.entry.as_module:
# cimported modules namespace can shadow actual variables
self.entry.is_variable = 1
-
- def analyse_types(self, env):
- self.initialized_check = env.directives['initializedcheck']
- entry = self.entry
+
+ def analyse_types(self, env):
+ self.initialized_check = env.directives['initializedcheck']
+ entry = self.entry
if entry is None:
entry = env.lookup(self.name)
if not entry:
@@ -2017,12 +2017,12 @@ class NameNode(AtomicExprNode):
if entry.type.is_buffer:
from . import Buffer
Buffer.used_buffer_aux_vars(entry)
- self.analyse_rvalue_entry(env)
- return self
-
- def analyse_target_types(self, env):
- self.analyse_entry(env, is_target=True)
-
+ self.analyse_rvalue_entry(env)
+ return self
+
+ def analyse_target_types(self, env):
+ self.analyse_entry(env, is_target=True)
+
entry = self.entry
if entry.is_cfunction and entry.as_variable:
# FIXME: unify "is_overridable" flags below
@@ -2030,72 +2030,72 @@ class NameNode(AtomicExprNode):
# We need this for assigning to cpdef names and for the fused 'def' TreeFragment
entry = self.entry = entry.as_variable
self.type = entry.type
-
- if self.type.is_const:
- error(self.pos, "Assignment to const '%s'" % self.name)
- if self.type.is_reference:
- error(self.pos, "Assignment to reference '%s'" % self.name)
- if not self.is_lvalue():
+
+ if self.type.is_const:
+ error(self.pos, "Assignment to const '%s'" % self.name)
+ if self.type.is_reference:
+ error(self.pos, "Assignment to reference '%s'" % self.name)
+ if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
- self.type = PyrexTypes.error_type
+ self.type = PyrexTypes.error_type
entry.used = 1
if entry.type.is_buffer:
- from . import Buffer
+ from . import Buffer
Buffer.used_buffer_aux_vars(entry)
- return self
-
- def analyse_rvalue_entry(self, env):
- #print "NameNode.analyse_rvalue_entry:", self.name ###
- #print "Entry:", self.entry.__dict__ ###
- self.analyse_entry(env)
- entry = self.entry
-
- if entry.is_declared_generic:
- self.result_ctype = py_object_type
-
- if entry.is_pyglobal or entry.is_builtin:
- if entry.is_builtin and entry.is_const:
- self.is_temp = 0
- else:
- self.is_temp = 1
-
- self.is_used_as_rvalue = 1
- elif entry.type.is_memoryviewslice:
- self.is_temp = False
- self.is_used_as_rvalue = True
- self.use_managed_ref = True
- return self
-
- def nogil_check(self, env):
- self.nogil = True
- if self.is_used_as_rvalue:
- entry = self.entry
- if entry.is_builtin:
- if not entry.is_const: # cached builtins are ok
- self.gil_error()
- elif entry.is_pyglobal:
- self.gil_error()
-
- gil_message = "Accessing Python global or builtin"
-
- def analyse_entry(self, env, is_target=False):
- #print "NameNode.analyse_entry:", self.name ###
- self.check_identifier_kind()
- entry = self.entry
- type = entry.type
- if (not is_target and type.is_pyobject and self.inferred_type and
- self.inferred_type.is_builtin_type):
- # assume that type inference is smarter than the static entry
- type = self.inferred_type
- self.type = type
-
- def check_identifier_kind(self):
- # Check that this is an appropriate kind of name for use in an
- # expression. Also finds the variable entry associated with
- # an extension type.
- entry = self.entry
- if entry.is_type and entry.type.is_extension_type:
- self.type_entry = entry
+ return self
+
+ def analyse_rvalue_entry(self, env):
+ #print "NameNode.analyse_rvalue_entry:", self.name ###
+ #print "Entry:", self.entry.__dict__ ###
+ self.analyse_entry(env)
+ entry = self.entry
+
+ if entry.is_declared_generic:
+ self.result_ctype = py_object_type
+
+ if entry.is_pyglobal or entry.is_builtin:
+ if entry.is_builtin and entry.is_const:
+ self.is_temp = 0
+ else:
+ self.is_temp = 1
+
+ self.is_used_as_rvalue = 1
+ elif entry.type.is_memoryviewslice:
+ self.is_temp = False
+ self.is_used_as_rvalue = True
+ self.use_managed_ref = True
+ return self
+
+ def nogil_check(self, env):
+ self.nogil = True
+ if self.is_used_as_rvalue:
+ entry = self.entry
+ if entry.is_builtin:
+ if not entry.is_const: # cached builtins are ok
+ self.gil_error()
+ elif entry.is_pyglobal:
+ self.gil_error()
+
+ gil_message = "Accessing Python global or builtin"
+
+ def analyse_entry(self, env, is_target=False):
+ #print "NameNode.analyse_entry:", self.name ###
+ self.check_identifier_kind()
+ entry = self.entry
+ type = entry.type
+ if (not is_target and type.is_pyobject and self.inferred_type and
+ self.inferred_type.is_builtin_type):
+ # assume that type inference is smarter than the static entry
+ type = self.inferred_type
+ self.type = type
+
+ def check_identifier_kind(self):
+ # Check that this is an appropriate kind of name for use in an
+ # expression. Also finds the variable entry associated with
+ # an extension type.
+ entry = self.entry
+ if entry.is_type and entry.type.is_extension_type:
+ self.type_entry = entry
if entry.is_type and entry.type.is_enum:
py_entry = Symtab.Entry(self.name, None, py_object_type)
py_entry.is_pyglobal = True
@@ -2108,265 +2108,265 @@ class NameNode(AtomicExprNode):
self.entry = self.entry.as_variable
elif not self.is_cython_module:
error(self.pos, "'%s' is not a constant, variable or function identifier" % self.name)
-
+
def is_cimported_module_without_shadow(self, env):
if self.is_cython_module or self.cython_attribute:
return False
entry = self.entry or env.lookup(self.name)
return entry.as_module and not entry.is_variable
- def is_simple(self):
- # If it's not a C variable, it'll be in a temp.
- return 1
-
- def may_be_none(self):
- if self.cf_state and self.type and (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- # gard against infinite recursion on self-dependencies
- if getattr(self, '_none_checking', False):
- # self-dependency - either this node receives a None
- # value from *another* node, or it can not reference
- # None at this point => safe to assume "not None"
- return False
- self._none_checking = True
- # evaluate control flow state to see if there were any
- # potential None values assigned to the node so far
- may_be_none = False
- for assignment in self.cf_state:
- if assignment.rhs.may_be_none():
- may_be_none = True
- break
- del self._none_checking
- return may_be_none
- return super(NameNode, self).may_be_none()
-
- def nonlocally_immutable(self):
- if ExprNode.nonlocally_immutable(self):
- return True
- entry = self.entry
- if not entry or entry.in_closure:
- return False
- return entry.is_local or entry.is_arg or entry.is_builtin or entry.is_readonly
-
- def calculate_target_results(self, env):
- pass
-
- def check_const(self):
- entry = self.entry
+ def is_simple(self):
+ # If it's not a C variable, it'll be in a temp.
+ return 1
+
+ def may_be_none(self):
+ if self.cf_state and self.type and (self.type.is_pyobject or
+ self.type.is_memoryviewslice):
+ # gard against infinite recursion on self-dependencies
+ if getattr(self, '_none_checking', False):
+ # self-dependency - either this node receives a None
+ # value from *another* node, or it can not reference
+ # None at this point => safe to assume "not None"
+ return False
+ self._none_checking = True
+ # evaluate control flow state to see if there were any
+ # potential None values assigned to the node so far
+ may_be_none = False
+ for assignment in self.cf_state:
+ if assignment.rhs.may_be_none():
+ may_be_none = True
+ break
+ del self._none_checking
+ return may_be_none
+ return super(NameNode, self).may_be_none()
+
+ def nonlocally_immutable(self):
+ if ExprNode.nonlocally_immutable(self):
+ return True
+ entry = self.entry
+ if not entry or entry.in_closure:
+ return False
+ return entry.is_local or entry.is_arg or entry.is_builtin or entry.is_readonly
+
+ def calculate_target_results(self, env):
+ pass
+
+ def check_const(self):
+ entry = self.entry
if entry is not None and not (
entry.is_const or
entry.is_cfunction or
entry.is_builtin or
entry.type.is_const):
- self.not_const()
- return False
- return True
-
- def check_const_addr(self):
- entry = self.entry
- if not (entry.is_cglobal or entry.is_cfunction or entry.is_builtin):
- self.addr_not_const()
- return False
- return True
-
- def is_lvalue(self):
+ self.not_const()
+ return False
+ return True
+
+ def check_const_addr(self):
+ entry = self.entry
+ if not (entry.is_cglobal or entry.is_cfunction or entry.is_builtin):
+ self.addr_not_const()
+ return False
+ return True
+
+ def is_lvalue(self):
return (
self.entry.is_variable and
- not self.entry.is_readonly
+ not self.entry.is_readonly
) or (
self.entry.is_cfunction and
self.entry.is_overridable
)
-
- def is_addressable(self):
- return self.entry.is_variable and not self.type.is_memoryviewslice
-
- def is_ephemeral(self):
- # Name nodes are never ephemeral, even if the
- # result is in a temporary.
- return 0
-
- def calculate_result_code(self):
- entry = self.entry
- if not entry:
- return "<error>" # There was an error earlier
- return entry.cname
-
- def generate_result_code(self, code):
- assert hasattr(self, 'entry')
- entry = self.entry
- if entry is None:
- return # There was an error earlier
+
+ def is_addressable(self):
+ return self.entry.is_variable and not self.type.is_memoryviewslice
+
+ def is_ephemeral(self):
+ # Name nodes are never ephemeral, even if the
+ # result is in a temporary.
+ return 0
+
+ def calculate_result_code(self):
+ entry = self.entry
+ if not entry:
+ return "<error>" # There was an error earlier
+ return entry.cname
+
+ def generate_result_code(self, code):
+ assert hasattr(self, 'entry')
+ entry = self.entry
+ if entry is None:
+ return # There was an error earlier
if entry.utility_code:
code.globalstate.use_utility_code(entry.utility_code)
- if entry.is_builtin and entry.is_const:
- return # Lookup already cached
- elif entry.is_pyclass_attr:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.is_builtin:
- namespace = Naming.builtins_cname
- else: # entry.is_pyglobal
- namespace = entry.scope.namespace_cname
- if not self.cf_is_null:
- code.putln(
- '%s = PyObject_GetItem(%s, %s);' % (
- self.result(),
- namespace,
- interned_cname))
- code.putln('if (unlikely(!%s)) {' % self.result())
- code.putln('PyErr_Clear();')
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
+ if entry.is_builtin and entry.is_const:
+ return # Lookup already cached
+ elif entry.is_pyclass_attr:
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
+ if entry.is_builtin:
+ namespace = Naming.builtins_cname
+ else: # entry.is_pyglobal
+ namespace = entry.scope.namespace_cname
+ if not self.cf_is_null:
+ code.putln(
+ '%s = PyObject_GetItem(%s, %s);' % (
+ self.result(),
+ namespace,
+ interned_cname))
+ code.putln('if (unlikely(!%s)) {' % self.result())
+ code.putln('PyErr_Clear();')
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
+ code.putln(
'__Pyx_GetModuleGlobalName(%s, %s);' % (
- self.result(),
- interned_cname))
- if not self.cf_is_null:
- code.putln("}")
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
-
- elif entry.is_builtin and not entry.scope.is_module_scope:
- # known builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c"))
- code.putln(
- '%s = __Pyx_GetBuiltinName(%s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope):
- # name in class body, global name or unknown builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.scope.is_module_scope:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
+ self.result(),
+ interned_cname))
+ if not self.cf_is_null:
+ code.putln("}")
+ code.putln(code.error_goto_if_null(self.result(), self.pos))
+ code.put_gotref(self.py_result())
+
+ elif entry.is_builtin and not entry.scope.is_module_scope:
+ # known builtin
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c"))
+ code.putln(
+ '%s = __Pyx_GetBuiltinName(%s); %s' % (
+ self.result(),
+ interned_cname,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+ elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope):
+ # name in class body, global name or unknown builtin
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
+ if entry.scope.is_module_scope:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
+ code.putln(
'__Pyx_GetModuleGlobalName(%s, %s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- # FIXME: is_pyglobal is also used for class namespace
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetNameInClass", "ObjectHandling.c"))
- code.putln(
+ self.result(),
+ interned_cname,
+ code.error_goto_if_null(self.result(), self.pos)))
+ else:
+ # FIXME: is_pyglobal is also used for class namespace
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("GetNameInClass", "ObjectHandling.c"))
+ code.putln(
'__Pyx_GetNameInClass(%s, %s, %s); %s' % (
- self.result(),
- entry.scope.namespace_cname,
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
- # Raise UnboundLocalError for objects and memoryviewslices
- raise_unbound = (
- (self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
- null_code = entry.type.check_for_null_code(entry.cname)
-
- memslice_check = entry.type.is_memoryviewslice and self.initialized_check
-
- if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
- code.put_error_if_unbound(self.pos, entry, self.in_nogil_context)
-
+ self.result(),
+ entry.scope.namespace_cname,
+ interned_cname,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+ elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
+ # Raise UnboundLocalError for objects and memoryviewslices
+ raise_unbound = (
+ (self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
+ null_code = entry.type.check_for_null_code(entry.cname)
+
+ memslice_check = entry.type.is_memoryviewslice and self.initialized_check
+
+ if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
+ code.put_error_if_unbound(self.pos, entry, self.in_nogil_context)
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
- #print "NameNode.generate_assignment_code:", self.name ###
- entry = self.entry
- if entry is None:
- return # There was an error earlier
-
- if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
+ #print "NameNode.generate_assignment_code:", self.name ###
+ entry = self.entry
+ if entry is None:
+ return # There was an error earlier
+
+ if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
and not self.lhs_of_first_assignment and not rhs.in_module_scope):
- error(self.pos, "Literal list must be assigned to pointer at time of declaration")
-
- # is_pyglobal seems to be True for module level-globals only.
- # We use this to access class->tp_dict if necessary.
- if entry.is_pyglobal:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- namespace = self.entry.scope.namespace_cname
- if entry.is_member:
- # if the entry is a member we have to cheat: SetAttr does not work
- # on types, so we create a descriptor which is then added to tp_dict
- setter = 'PyDict_SetItem'
- namespace = '%s->tp_dict' % namespace
- elif entry.scope.is_module_scope:
- setter = 'PyDict_SetItem'
- namespace = Naming.moddict_cname
- elif entry.is_pyclass_attr:
+ error(self.pos, "Literal list must be assigned to pointer at time of declaration")
+
+ # is_pyglobal seems to be True for module level-globals only.
+ # We use this to access class->tp_dict if necessary.
+ if entry.is_pyglobal:
+ assert entry.type.is_pyobject, "Python global or builtin not a Python object"
+ interned_cname = code.intern_identifier(self.entry.name)
+ namespace = self.entry.scope.namespace_cname
+ if entry.is_member:
+ # if the entry is a member we have to cheat: SetAttr does not work
+ # on types, so we create a descriptor which is then added to tp_dict
+ setter = 'PyDict_SetItem'
+ namespace = '%s->tp_dict' % namespace
+ elif entry.scope.is_module_scope:
+ setter = 'PyDict_SetItem'
+ namespace = Naming.moddict_cname
+ elif entry.is_pyclass_attr:
code.globalstate.use_utility_code(UtilityCode.load_cached("SetNameInClass", "ObjectHandling.c"))
setter = '__Pyx_SetNameInClass'
- else:
- assert False, repr(entry)
- code.put_error_if_neg(
- self.pos,
- '%s(%s, %s, %s)' % (
- setter,
- namespace,
- interned_cname,
- rhs.py_result()))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating disposal code for %s" % rhs)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- if entry.is_member:
- # in Py2.6+, we need to invalidate the method cache
- code.putln("PyType_Modified(%s);" %
- entry.scope.parent_type.typeptr_cname)
- else:
- if self.type.is_memoryviewslice:
- self.generate_acquire_memoryviewslice(rhs, code)
-
- elif self.type.is_buffer:
- # Generate code for doing the buffer release/acquisition.
- # This might raise an exception in which case the assignment (done
- # below) will not happen.
- #
- # The reason this is not in a typetest-like node is because the
- # variables that the acquired buffer info is stored to is allocated
- # per entry and coupled with it.
- self.generate_acquire_buffer(rhs, code)
- assigned = False
- if self.type.is_pyobject:
- #print "NameNode.generate_assignment_code: to", self.name ###
- #print "...from", rhs ###
- #print "...LHS type", self.type, "ctype", self.ctype() ###
- #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
- if self.use_managed_ref:
- rhs.make_owned_reference(code)
- is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
- if is_external_ref:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xgotref(self.py_result())
- else:
- code.put_gotref(self.py_result())
- assigned = True
- if entry.is_cglobal:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xdecref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- assigned = False
- if is_external_ref:
- code.put_giveref(rhs.py_result())
- if not self.type.is_memoryviewslice:
- if not assigned:
+ else:
+ assert False, repr(entry)
+ code.put_error_if_neg(
+ self.pos,
+ '%s(%s, %s, %s)' % (
+ setter,
+ namespace,
+ interned_cname,
+ rhs.py_result()))
+ if debug_disposal_code:
+ print("NameNode.generate_assignment_code:")
+ print("...generating disposal code for %s" % rhs)
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
+ if entry.is_member:
+ # in Py2.6+, we need to invalidate the method cache
+ code.putln("PyType_Modified(%s);" %
+ entry.scope.parent_type.typeptr_cname)
+ else:
+ if self.type.is_memoryviewslice:
+ self.generate_acquire_memoryviewslice(rhs, code)
+
+ elif self.type.is_buffer:
+ # Generate code for doing the buffer release/acquisition.
+ # This might raise an exception in which case the assignment (done
+ # below) will not happen.
+ #
+ # The reason this is not in a typetest-like node is because the
+ # variables that the acquired buffer info is stored to is allocated
+ # per entry and coupled with it.
+ self.generate_acquire_buffer(rhs, code)
+ assigned = False
+ if self.type.is_pyobject:
+ #print "NameNode.generate_assignment_code: to", self.name ###
+ #print "...from", rhs ###
+ #print "...LHS type", self.type, "ctype", self.ctype() ###
+ #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
+ if self.use_managed_ref:
+ rhs.make_owned_reference(code)
+ is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
+ if is_external_ref:
+ if not self.cf_is_null:
+ if self.cf_maybe_null:
+ code.put_xgotref(self.py_result())
+ else:
+ code.put_gotref(self.py_result())
+ assigned = True
+ if entry.is_cglobal:
+ code.put_decref_set(
+ self.result(), rhs.result_as(self.ctype()))
+ else:
+ if not self.cf_is_null:
+ if self.cf_maybe_null:
+ code.put_xdecref_set(
+ self.result(), rhs.result_as(self.ctype()))
+ else:
+ code.put_decref_set(
+ self.result(), rhs.result_as(self.ctype()))
+ else:
+ assigned = False
+ if is_external_ref:
+ code.put_giveref(rhs.py_result())
+ if not self.type.is_memoryviewslice:
+ if not assigned:
if overloaded_assignment:
result = rhs.result()
if exception_check == '+':
@@ -2384,188 +2384,188 @@ class NameNode(AtomicExprNode):
code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result))
elif result != self.result():
code.putln('%s = %s;' % (self.result(), result))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating post-assignment code for %s" % rhs)
- rhs.generate_post_assignment_code(code)
- elif rhs.result_in_temp():
- rhs.generate_post_assignment_code(code)
-
- rhs.free_temps(code)
-
- def generate_acquire_memoryviewslice(self, rhs, code):
- """
- Slices, coercions from objects, return values etc are new references.
- We have a borrowed reference in case of dst = src
- """
- from . import MemoryView
-
- MemoryView.put_acquire_memoryviewslice(
- lhs_cname=self.result(),
- lhs_type=self.type,
- lhs_pos=self.pos,
- rhs=rhs,
- code=code,
- have_gil=not self.in_nogil_context,
- first_assignment=self.cf_is_null)
-
- def generate_acquire_buffer(self, rhs, code):
- # rhstmp is only used in case the rhs is a complicated expression leading to
- # the object, to avoid repeating the same C expression for every reference
- # to the rhs. It does NOT hold a reference.
- pretty_rhs = isinstance(rhs, NameNode) or rhs.is_temp
- if pretty_rhs:
- rhstmp = rhs.result_as(self.ctype())
- else:
- rhstmp = code.funcstate.allocate_temp(self.entry.type, manage_ref=False)
- code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
-
- from . import Buffer
- Buffer.put_assign_to_buffer(self.result(), rhstmp, self.entry,
- is_initialized=not self.lhs_of_first_assignment,
- pos=self.pos, code=code)
-
- if not pretty_rhs:
- code.putln("%s = 0;" % rhstmp)
- code.funcstate.release_temp(rhstmp)
-
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if self.entry is None:
- return # There was an error earlier
- elif self.entry.is_pyclass_attr:
- namespace = self.entry.scope.namespace_cname
- interned_cname = code.intern_identifier(self.entry.name)
- if ignore_nonexisting:
- key_error_code = 'PyErr_Clear(); else'
- else:
- # minor hack: fake a NameError on KeyError
- key_error_code = (
- '{ PyErr_Clear(); PyErr_Format(PyExc_NameError, "name \'%%s\' is not defined", "%s"); }' %
- self.entry.name)
- code.putln(
- 'if (unlikely(PyObject_DelItem(%s, %s) < 0)) {'
- ' if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) %s'
- ' %s '
- '}' % (namespace, interned_cname,
- key_error_code,
- code.error_goto(self.pos)))
- elif self.entry.is_pyglobal:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- interned_cname = code.intern_identifier(self.entry.name)
- del_code = '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- Naming.module_cname, interned_cname)
- if ignore_nonexisting:
+ if debug_disposal_code:
+ print("NameNode.generate_assignment_code:")
+ print("...generating post-assignment code for %s" % rhs)
+ rhs.generate_post_assignment_code(code)
+ elif rhs.result_in_temp():
+ rhs.generate_post_assignment_code(code)
+
+ rhs.free_temps(code)
+
+ def generate_acquire_memoryviewslice(self, rhs, code):
+ """
+ Slices, coercions from objects, return values etc are new references.
+ We have a borrowed reference in case of dst = src
+ """
+ from . import MemoryView
+
+ MemoryView.put_acquire_memoryviewslice(
+ lhs_cname=self.result(),
+ lhs_type=self.type,
+ lhs_pos=self.pos,
+ rhs=rhs,
+ code=code,
+ have_gil=not self.in_nogil_context,
+ first_assignment=self.cf_is_null)
+
+ def generate_acquire_buffer(self, rhs, code):
+ # rhstmp is only used in case the rhs is a complicated expression leading to
+ # the object, to avoid repeating the same C expression for every reference
+ # to the rhs. It does NOT hold a reference.
+ pretty_rhs = isinstance(rhs, NameNode) or rhs.is_temp
+ if pretty_rhs:
+ rhstmp = rhs.result_as(self.ctype())
+ else:
+ rhstmp = code.funcstate.allocate_temp(self.entry.type, manage_ref=False)
+ code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
+
+ from . import Buffer
+ Buffer.put_assign_to_buffer(self.result(), rhstmp, self.entry,
+ is_initialized=not self.lhs_of_first_assignment,
+ pos=self.pos, code=code)
+
+ if not pretty_rhs:
+ code.putln("%s = 0;" % rhstmp)
+ code.funcstate.release_temp(rhstmp)
+
+ def generate_deletion_code(self, code, ignore_nonexisting=False):
+ if self.entry is None:
+ return # There was an error earlier
+ elif self.entry.is_pyclass_attr:
+ namespace = self.entry.scope.namespace_cname
+ interned_cname = code.intern_identifier(self.entry.name)
+ if ignore_nonexisting:
+ key_error_code = 'PyErr_Clear(); else'
+ else:
+ # minor hack: fake a NameError on KeyError
+ key_error_code = (
+ '{ PyErr_Clear(); PyErr_Format(PyExc_NameError, "name \'%%s\' is not defined", "%s"); }' %
+ self.entry.name)
+ code.putln(
+ 'if (unlikely(PyObject_DelItem(%s, %s) < 0)) {'
+ ' if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) %s'
+ ' %s '
+ '}' % (namespace, interned_cname,
+ key_error_code,
+ code.error_goto(self.pos)))
+ elif self.entry.is_pyglobal:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
+ interned_cname = code.intern_identifier(self.entry.name)
+ del_code = '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
+ Naming.module_cname, interned_cname)
+ if ignore_nonexisting:
code.putln(
'if (unlikely(%s < 0)) {'
' if (likely(PyErr_ExceptionMatches(PyExc_AttributeError))) PyErr_Clear(); else %s '
'}' % (del_code, code.error_goto(self.pos)))
- else:
- code.put_error_if_neg(self.pos, del_code)
- elif self.entry.type.is_pyobject or self.entry.type.is_memoryviewslice:
- if not self.cf_is_null:
- if self.cf_maybe_null and not ignore_nonexisting:
- code.put_error_if_unbound(self.pos, self.entry)
-
- if self.entry.type.is_pyobject:
- if self.entry.in_closure:
- # generator
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xgotref(self.result())
- else:
- code.put_gotref(self.result())
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xdecref(self.result(), self.ctype())
- else:
- code.put_decref(self.result(), self.ctype())
- code.putln('%s = NULL;' % self.result())
- else:
- code.put_xdecref_memoryviewslice(self.entry.cname,
- have_gil=not self.nogil)
- else:
- error(self.pos, "Deletion of C names not supported")
-
- def annotate(self, code):
- if hasattr(self, 'is_called') and self.is_called:
- pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
- if self.type.is_pyobject:
- style, text = 'py_call', 'python function (%s)'
- else:
- style, text = 'c_call', 'c function (%s)'
- code.annotate(pos, AnnotationItem(style, text % self.type, size=len(self.name)))
-
-class BackquoteNode(ExprNode):
- # `expr`
- #
- # arg ExprNode
-
- type = py_object_type
-
- subexprs = ['arg']
-
- def analyse_types(self, env):
- self.arg = self.arg.analyse_types(env)
- self.arg = self.arg.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
-
- gil_message = "Backquote expression"
-
- def calculate_constant_result(self):
- self.constant_result = repr(self.arg.constant_result)
-
- def generate_result_code(self, code):
- code.putln(
- "%s = PyObject_Repr(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class ImportNode(ExprNode):
- # Used as part of import statement implementation.
- # Implements result =
- # __import__(module_name, globals(), None, name_list, level)
- #
- # module_name StringNode dotted name of module. Empty module
- # name means importing the parent package according
- # to level
- # name_list ListNode or None list of names to be imported
- # level int relative import level:
- # -1: attempt both relative import and absolute import;
- # 0: absolute import;
- # >0: the number of parent directories to search
- # relative to the current module.
- # None: decide the level according to language level and
- # directives
-
- type = py_object_type
-
- subexprs = ['module_name', 'name_list']
-
- def analyse_types(self, env):
- if self.level is None:
- if (env.directives['py2_import'] or
- Future.absolute_import not in env.global_scope().context.future_directives):
- self.level = -1
- else:
- self.level = 0
- module_name = self.module_name.analyse_types(env)
- self.module_name = module_name.coerce_to_pyobject(env)
- if self.name_list:
- name_list = self.name_list.analyse_types(env)
- self.name_list = name_list.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
-
- gil_message = "Python import"
-
- def generate_result_code(self, code):
- if self.name_list:
- name_list_code = self.name_list.py_result()
- else:
- name_list_code = "0"
+ else:
+ code.put_error_if_neg(self.pos, del_code)
+ elif self.entry.type.is_pyobject or self.entry.type.is_memoryviewslice:
+ if not self.cf_is_null:
+ if self.cf_maybe_null and not ignore_nonexisting:
+ code.put_error_if_unbound(self.pos, self.entry)
+
+ if self.entry.type.is_pyobject:
+ if self.entry.in_closure:
+ # generator
+ if ignore_nonexisting and self.cf_maybe_null:
+ code.put_xgotref(self.result())
+ else:
+ code.put_gotref(self.result())
+ if ignore_nonexisting and self.cf_maybe_null:
+ code.put_xdecref(self.result(), self.ctype())
+ else:
+ code.put_decref(self.result(), self.ctype())
+ code.putln('%s = NULL;' % self.result())
+ else:
+ code.put_xdecref_memoryviewslice(self.entry.cname,
+ have_gil=not self.nogil)
+ else:
+ error(self.pos, "Deletion of C names not supported")
+
+ def annotate(self, code):
+ if hasattr(self, 'is_called') and self.is_called:
+ pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
+ if self.type.is_pyobject:
+ style, text = 'py_call', 'python function (%s)'
+ else:
+ style, text = 'c_call', 'c function (%s)'
+ code.annotate(pos, AnnotationItem(style, text % self.type, size=len(self.name)))
+
+class BackquoteNode(ExprNode):
+ # `expr`
+ #
+ # arg ExprNode
+
+ type = py_object_type
+
+ subexprs = ['arg']
+
+ def analyse_types(self, env):
+ self.arg = self.arg.analyse_types(env)
+ self.arg = self.arg.coerce_to_pyobject(env)
+ self.is_temp = 1
+ return self
+
+ gil_message = "Backquote expression"
+
+ def calculate_constant_result(self):
+ self.constant_result = repr(self.arg.constant_result)
+
+ def generate_result_code(self, code):
+ code.putln(
+ "%s = PyObject_Repr(%s); %s" % (
+ self.result(),
+ self.arg.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class ImportNode(ExprNode):
+ # Used as part of import statement implementation.
+ # Implements result =
+ # __import__(module_name, globals(), None, name_list, level)
+ #
+ # module_name StringNode dotted name of module. Empty module
+ # name means importing the parent package according
+ # to level
+ # name_list ListNode or None list of names to be imported
+ # level int relative import level:
+ # -1: attempt both relative import and absolute import;
+ # 0: absolute import;
+ # >0: the number of parent directories to search
+ # relative to the current module.
+ # None: decide the level according to language level and
+ # directives
+
+ type = py_object_type
+
+ subexprs = ['module_name', 'name_list']
+
+ def analyse_types(self, env):
+ if self.level is None:
+ if (env.directives['py2_import'] or
+ Future.absolute_import not in env.global_scope().context.future_directives):
+ self.level = -1
+ else:
+ self.level = 0
+ module_name = self.module_name.analyse_types(env)
+ self.module_name = module_name.coerce_to_pyobject(env)
+ if self.name_list:
+ name_list = self.name_list.analyse_types(env)
+ self.name_list = name_list.coerce_to_pyobject(env)
+ self.is_temp = 1
+ return self
+
+ gil_message = "Python import"
+
+ def generate_result_code(self, code):
+ if self.name_list:
+ name_list_code = self.name_list.py_result()
+ else:
+ name_list_code = "0"
code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c"))
import_code = "__Pyx_Import(%s, %s, %d)" % (
@@ -2584,323 +2584,323 @@ class ImportNode(ExprNode):
self.result(),
import_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class IteratorNode(ExprNode):
- # Used as part of for statement implementation.
- #
- # Implements result = iter(sequence)
- #
- # sequence ExprNode
-
- type = py_object_type
- iter_func_ptr = None
- counter_cname = None
- cpp_iterator_cname = None
- reversed = False # currently only used for list/tuple types (see Optimize.py)
+ code.put_gotref(self.py_result())
+
+
+class IteratorNode(ExprNode):
+ # Used as part of for statement implementation.
+ #
+ # Implements result = iter(sequence)
+ #
+ # sequence ExprNode
+
+ type = py_object_type
+ iter_func_ptr = None
+ counter_cname = None
+ cpp_iterator_cname = None
+ reversed = False # currently only used for list/tuple types (see Optimize.py)
is_async = False
-
- subexprs = ['sequence']
-
- def analyse_types(self, env):
- self.sequence = self.sequence.analyse_types(env)
- if (self.sequence.type.is_array or self.sequence.type.is_ptr) and \
- not self.sequence.type.is_string:
- # C array iteration will be transformed later on
- self.type = self.sequence.type
- elif self.sequence.type.is_cpp_class:
- self.analyse_cpp_types(env)
- else:
- self.sequence = self.sequence.coerce_to_pyobject(env)
+
+ subexprs = ['sequence']
+
+ def analyse_types(self, env):
+ self.sequence = self.sequence.analyse_types(env)
+ if (self.sequence.type.is_array or self.sequence.type.is_ptr) and \
+ not self.sequence.type.is_string:
+ # C array iteration will be transformed later on
+ self.type = self.sequence.type
+ elif self.sequence.type.is_cpp_class:
+ self.analyse_cpp_types(env)
+ else:
+ self.sequence = self.sequence.coerce_to_pyobject(env)
if self.sequence.type in (list_type, tuple_type):
- self.sequence = self.sequence.as_none_safe_node("'NoneType' object is not iterable")
- self.is_temp = 1
- return self
-
- gil_message = "Iterating over Python object"
-
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
-
- def type_dependencies(self, env):
- return self.sequence.type_dependencies(env)
-
- def infer_type(self, env):
- sequence_type = self.sequence.infer_type(env)
- if sequence_type.is_array or sequence_type.is_ptr:
- return sequence_type
- elif sequence_type.is_cpp_class:
- begin = sequence_type.scope.lookup("begin")
- if begin is not None:
- return begin.type.return_type
- elif sequence_type.is_pyobject:
- return sequence_type
- return py_object_type
-
- def analyse_cpp_types(self, env):
- sequence_type = self.sequence.type
- if sequence_type.is_ptr:
- sequence_type = sequence_type.base_type
- begin = sequence_type.scope.lookup("begin")
- end = sequence_type.scope.lookup("end")
- if (begin is None
- or not begin.type.is_cfunction
- or begin.type.args):
- error(self.pos, "missing begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if (end is None
- or not end.type.is_cfunction
- or end.type.args):
- error(self.pos, "missing end() on %s" % self.sequence.type)
- self.type = error_type
- return
- iter_type = begin.type.return_type
- if iter_type.is_cpp_class:
- if env.lookup_operator_for_types(
- self.pos,
- "!=",
- [iter_type, end.type.return_type]) is None:
- error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
- error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
- error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- self.type = iter_type
- elif iter_type.is_ptr:
- if not (iter_type == end.type.return_type):
- error(self.pos, "incompatible types for begin() and end()")
- self.type = iter_type
- else:
- error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
- self.type = error_type
- return
-
- def generate_result_code(self, code):
- sequence_type = self.sequence.type
- if sequence_type.is_cpp_class:
- if self.sequence.is_name:
- # safe: C++ won't allow you to reassign to class references
- begin_func = "%s.begin" % self.sequence.result()
- else:
- sequence_type = PyrexTypes.c_ptr_type(sequence_type)
- self.cpp_iterator_cname = code.funcstate.allocate_temp(sequence_type, manage_ref=False)
- code.putln("%s = &%s;" % (self.cpp_iterator_cname, self.sequence.result()))
- begin_func = "%s->begin" % self.cpp_iterator_cname
- # TODO: Limit scope.
- code.putln("%s = %s();" % (self.result(), begin_func))
- return
- if sequence_type.is_array or sequence_type.is_ptr:
- raise InternalError("for in carray slice not transformed")
+ self.sequence = self.sequence.as_none_safe_node("'NoneType' object is not iterable")
+ self.is_temp = 1
+ return self
+
+ gil_message = "Iterating over Python object"
+
+ _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
+ PyrexTypes.py_object_type, [
+ PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
+ ]))
+
+ def type_dependencies(self, env):
+ return self.sequence.type_dependencies(env)
+
+ def infer_type(self, env):
+ sequence_type = self.sequence.infer_type(env)
+ if sequence_type.is_array or sequence_type.is_ptr:
+ return sequence_type
+ elif sequence_type.is_cpp_class:
+ begin = sequence_type.scope.lookup("begin")
+ if begin is not None:
+ return begin.type.return_type
+ elif sequence_type.is_pyobject:
+ return sequence_type
+ return py_object_type
+
+ def analyse_cpp_types(self, env):
+ sequence_type = self.sequence.type
+ if sequence_type.is_ptr:
+ sequence_type = sequence_type.base_type
+ begin = sequence_type.scope.lookup("begin")
+ end = sequence_type.scope.lookup("end")
+ if (begin is None
+ or not begin.type.is_cfunction
+ or begin.type.args):
+ error(self.pos, "missing begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return
+ if (end is None
+ or not end.type.is_cfunction
+ or end.type.args):
+ error(self.pos, "missing end() on %s" % self.sequence.type)
+ self.type = error_type
+ return
+ iter_type = begin.type.return_type
+ if iter_type.is_cpp_class:
+ if env.lookup_operator_for_types(
+ self.pos,
+ "!=",
+ [iter_type, end.type.return_type]) is None:
+ error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return
+ if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
+ error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return
+ if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
+ error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
+ self.type = error_type
+ return
+ self.type = iter_type
+ elif iter_type.is_ptr:
+ if not (iter_type == end.type.return_type):
+ error(self.pos, "incompatible types for begin() and end()")
+ self.type = iter_type
+ else:
+ error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
+ self.type = error_type
+ return
+
+ def generate_result_code(self, code):
+ sequence_type = self.sequence.type
+ if sequence_type.is_cpp_class:
+ if self.sequence.is_name:
+ # safe: C++ won't allow you to reassign to class references
+ begin_func = "%s.begin" % self.sequence.result()
+ else:
+ sequence_type = PyrexTypes.c_ptr_type(sequence_type)
+ self.cpp_iterator_cname = code.funcstate.allocate_temp(sequence_type, manage_ref=False)
+ code.putln("%s = &%s;" % (self.cpp_iterator_cname, self.sequence.result()))
+ begin_func = "%s->begin" % self.cpp_iterator_cname
+ # TODO: Limit scope.
+ code.putln("%s = %s();" % (self.result(), begin_func))
+ return
+ if sequence_type.is_array or sequence_type.is_ptr:
+ raise InternalError("for in carray slice not transformed")
is_builtin_sequence = sequence_type in (list_type, tuple_type)
- if not is_builtin_sequence:
- # reversed() not currently optimised (see Optimize.py)
- assert not self.reversed, "internal error: reversed() only implemented for list/tuple objects"
- self.may_be_a_sequence = not sequence_type.is_builtin_type
- if self.may_be_a_sequence:
- code.putln(
- "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % (
- self.sequence.py_result(),
- self.sequence.py_result()))
-
- if is_builtin_sequence or self.may_be_a_sequence:
- self.counter_cname = code.funcstate.allocate_temp(
- PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- if self.reversed:
- if sequence_type is list_type:
- init_value = 'PyList_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = 'PyTuple_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = '0'
+ if not is_builtin_sequence:
+ # reversed() not currently optimised (see Optimize.py)
+ assert not self.reversed, "internal error: reversed() only implemented for list/tuple objects"
+ self.may_be_a_sequence = not sequence_type.is_builtin_type
+ if self.may_be_a_sequence:
+ code.putln(
+ "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % (
+ self.sequence.py_result(),
+ self.sequence.py_result()))
+
+ if is_builtin_sequence or self.may_be_a_sequence:
+ self.counter_cname = code.funcstate.allocate_temp(
+ PyrexTypes.c_py_ssize_t_type, manage_ref=False)
+ if self.reversed:
+ if sequence_type is list_type:
+ init_value = 'PyList_GET_SIZE(%s) - 1' % self.result()
+ else:
+ init_value = 'PyTuple_GET_SIZE(%s) - 1' % self.result()
+ else:
+ init_value = '0'
code.putln("%s = %s; __Pyx_INCREF(%s); %s = %s;" % (
self.result(),
self.sequence.py_result(),
self.result(),
self.counter_cname,
init_value))
- if not is_builtin_sequence:
- self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- if self.may_be_a_sequence:
- code.putln("%s = NULL;" % self.iter_func_ptr)
- code.putln("} else {")
- code.put("%s = -1; " % self.counter_cname)
-
- code.putln("%s = PyObject_GetIter(%s); %s" % (
+ if not is_builtin_sequence:
+ self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
+ if self.may_be_a_sequence:
+ code.putln("%s = NULL;" % self.iter_func_ptr)
+ code.putln("} else {")
+ code.put("%s = -1; " % self.counter_cname)
+
+ code.putln("%s = PyObject_GetIter(%s); %s" % (
self.result(),
self.sequence.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below
- # makes it visible to the C compiler that the pointer really isn't NULL, so that
- # it can distinguish between the special cases and the generic case
- code.putln("%s = Py_TYPE(%s)->tp_iternext; %s" % (
- self.iter_func_ptr, self.py_result(),
- code.error_goto_if_null(self.iter_func_ptr, self.pos)))
- if self.may_be_a_sequence:
- code.putln("}")
-
- def generate_next_sequence_item(self, test_name, result_name, code):
- assert self.counter_cname, "internal error: counter_cname temp not prepared"
- final_size = 'Py%s_GET_SIZE(%s)' % (test_name, self.py_result())
- if self.sequence.is_sequence_constructor:
- item_count = len(self.sequence.args)
- if self.sequence.mult_factor is None:
- final_size = item_count
+ code.put_gotref(self.py_result())
+
+ # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below
+ # makes it visible to the C compiler that the pointer really isn't NULL, so that
+ # it can distinguish between the special cases and the generic case
+ code.putln("%s = Py_TYPE(%s)->tp_iternext; %s" % (
+ self.iter_func_ptr, self.py_result(),
+ code.error_goto_if_null(self.iter_func_ptr, self.pos)))
+ if self.may_be_a_sequence:
+ code.putln("}")
+
+ def generate_next_sequence_item(self, test_name, result_name, code):
+ assert self.counter_cname, "internal error: counter_cname temp not prepared"
+ final_size = 'Py%s_GET_SIZE(%s)' % (test_name, self.py_result())
+ if self.sequence.is_sequence_constructor:
+ item_count = len(self.sequence.args)
+ if self.sequence.mult_factor is None:
+ final_size = item_count
elif isinstance(self.sequence.mult_factor.constant_result, _py_int_types):
- final_size = item_count * self.sequence.mult_factor.constant_result
- code.putln("if (%s >= %s) break;" % (self.counter_cname, final_size))
- if self.reversed:
- inc_dec = '--'
- else:
- inc_dec = '++'
+ final_size = item_count * self.sequence.mult_factor.constant_result
+ code.putln("if (%s >= %s) break;" % (self.counter_cname, final_size))
+ if self.reversed:
+ inc_dec = '--'
+ else:
+ inc_dec = '++'
code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- code.putln(
- "%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s%s; %s" % (
- result_name,
- test_name,
- self.py_result(),
- self.counter_cname,
- result_name,
- self.counter_cname,
- inc_dec,
- # use the error label to avoid C compiler warnings if we only use it below
- code.error_goto_if_neg('0', self.pos)
- ))
- code.putln("#else")
- code.putln(
- "%s = PySequence_ITEM(%s, %s); %s%s; %s" % (
- result_name,
- self.py_result(),
- self.counter_cname,
- self.counter_cname,
- inc_dec,
- code.error_goto_if_null(result_name, self.pos)))
+ code.putln(
+ "%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s%s; %s" % (
+ result_name,
+ test_name,
+ self.py_result(),
+ self.counter_cname,
+ result_name,
+ self.counter_cname,
+ inc_dec,
+ # use the error label to avoid C compiler warnings if we only use it below
+ code.error_goto_if_neg('0', self.pos)
+ ))
+ code.putln("#else")
+ code.putln(
+ "%s = PySequence_ITEM(%s, %s); %s%s; %s" % (
+ result_name,
+ self.py_result(),
+ self.counter_cname,
+ self.counter_cname,
+ inc_dec,
+ code.error_goto_if_null(result_name, self.pos)))
code.put_gotref(result_name)
- code.putln("#endif")
-
- def generate_iter_next_result_code(self, result_name, code):
- sequence_type = self.sequence.type
- if self.reversed:
- code.putln("if (%s < 0) break;" % self.counter_cname)
- if sequence_type.is_cpp_class:
- if self.cpp_iterator_cname:
- end_func = "%s->end" % self.cpp_iterator_cname
- else:
- end_func = "%s.end" % self.sequence.result()
- # TODO: Cache end() call?
- code.putln("if (!(%s != %s())) break;" % (
- self.result(),
- end_func))
- code.putln("%s = *%s;" % (
- result_name,
- self.result()))
- code.putln("++%s;" % self.result())
- return
- elif sequence_type is list_type:
- self.generate_next_sequence_item('List', result_name, code)
- return
- elif sequence_type is tuple_type:
- self.generate_next_sequence_item('Tuple', result_name, code)
- return
-
- if self.may_be_a_sequence:
- code.putln("if (likely(!%s)) {" % self.iter_func_ptr)
- code.putln("if (likely(PyList_CheckExact(%s))) {" % self.py_result())
- self.generate_next_sequence_item('List', result_name, code)
- code.putln("} else {")
- self.generate_next_sequence_item('Tuple', result_name, code)
- code.putln("}")
- code.put("} else ")
-
- code.putln("{")
- code.putln(
- "%s = %s(%s);" % (
- result_name,
- self.iter_func_ptr,
- self.py_result()))
- code.putln("if (unlikely(!%s)) {" % result_name)
- code.putln("PyObject* exc_type = PyErr_Occurred();")
- code.putln("if (exc_type) {")
+ code.putln("#endif")
+
+ def generate_iter_next_result_code(self, result_name, code):
+ sequence_type = self.sequence.type
+ if self.reversed:
+ code.putln("if (%s < 0) break;" % self.counter_cname)
+ if sequence_type.is_cpp_class:
+ if self.cpp_iterator_cname:
+ end_func = "%s->end" % self.cpp_iterator_cname
+ else:
+ end_func = "%s.end" % self.sequence.result()
+ # TODO: Cache end() call?
+ code.putln("if (!(%s != %s())) break;" % (
+ self.result(),
+ end_func))
+ code.putln("%s = *%s;" % (
+ result_name,
+ self.result()))
+ code.putln("++%s;" % self.result())
+ return
+ elif sequence_type is list_type:
+ self.generate_next_sequence_item('List', result_name, code)
+ return
+ elif sequence_type is tuple_type:
+ self.generate_next_sequence_item('Tuple', result_name, code)
+ return
+
+ if self.may_be_a_sequence:
+ code.putln("if (likely(!%s)) {" % self.iter_func_ptr)
+ code.putln("if (likely(PyList_CheckExact(%s))) {" % self.py_result())
+ self.generate_next_sequence_item('List', result_name, code)
+ code.putln("} else {")
+ self.generate_next_sequence_item('Tuple', result_name, code)
+ code.putln("}")
+ code.put("} else ")
+
+ code.putln("{")
+ code.putln(
+ "%s = %s(%s);" % (
+ result_name,
+ self.iter_func_ptr,
+ self.py_result()))
+ code.putln("if (unlikely(!%s)) {" % result_name)
+ code.putln("PyObject* exc_type = PyErr_Occurred();")
+ code.putln("if (exc_type) {")
code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
- code.putln("else %s" % code.error_goto(self.pos))
- code.putln("}")
- code.putln("break;")
- code.putln("}")
- code.put_gotref(result_name)
- code.putln("}")
-
- def free_temps(self, code):
- if self.counter_cname:
- code.funcstate.release_temp(self.counter_cname)
- if self.iter_func_ptr:
- code.funcstate.release_temp(self.iter_func_ptr)
- self.iter_func_ptr = None
- if self.cpp_iterator_cname:
- code.funcstate.release_temp(self.cpp_iterator_cname)
- ExprNode.free_temps(self, code)
-
-
-class NextNode(AtomicExprNode):
- # Used as part of for statement implementation.
+ code.putln("else %s" % code.error_goto(self.pos))
+ code.putln("}")
+ code.putln("break;")
+ code.putln("}")
+ code.put_gotref(result_name)
+ code.putln("}")
+
+ def free_temps(self, code):
+ if self.counter_cname:
+ code.funcstate.release_temp(self.counter_cname)
+ if self.iter_func_ptr:
+ code.funcstate.release_temp(self.iter_func_ptr)
+ self.iter_func_ptr = None
+ if self.cpp_iterator_cname:
+ code.funcstate.release_temp(self.cpp_iterator_cname)
+ ExprNode.free_temps(self, code)
+
+
+class NextNode(AtomicExprNode):
+ # Used as part of for statement implementation.
# Implements result = next(iterator)
- # Created during analyse_types phase.
- # The iterator is not owned by this node.
- #
- # iterator IteratorNode
-
- def __init__(self, iterator):
- AtomicExprNode.__init__(self, iterator.pos)
- self.iterator = iterator
-
+ # Created during analyse_types phase.
+ # The iterator is not owned by this node.
+ #
+ # iterator IteratorNode
+
+ def __init__(self, iterator):
+ AtomicExprNode.__init__(self, iterator.pos)
+ self.iterator = iterator
+
def nogil_check(self, env):
# ignore - errors (if any) are already handled by IteratorNode
pass
- def type_dependencies(self, env):
- return self.iterator.type_dependencies(env)
-
+ def type_dependencies(self, env):
+ return self.iterator.type_dependencies(env)
+
def infer_type(self, env, iterator_type=None):
- if iterator_type is None:
- iterator_type = self.iterator.infer_type(env)
- if iterator_type.is_ptr or iterator_type.is_array:
- return iterator_type.base_type
- elif iterator_type.is_cpp_class:
- item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
- if item_type.is_reference:
- item_type = item_type.ref_base_type
- if item_type.is_const:
- item_type = item_type.const_base_type
- return item_type
- else:
- # Avoid duplication of complicated logic.
- fake_index_node = IndexNode(
- self.pos,
- base=self.iterator.sequence,
- index=IntNode(self.pos, value='PY_SSIZE_T_MAX',
- type=PyrexTypes.c_py_ssize_t_type))
- return fake_index_node.infer_type(env)
-
- def analyse_types(self, env):
- self.type = self.infer_type(env, self.iterator.type)
- self.is_temp = 1
- return self
-
- def generate_result_code(self, code):
- self.iterator.generate_iter_next_result_code(self.result(), code)
-
-
+ if iterator_type is None:
+ iterator_type = self.iterator.infer_type(env)
+ if iterator_type.is_ptr or iterator_type.is_array:
+ return iterator_type.base_type
+ elif iterator_type.is_cpp_class:
+ item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
+ if item_type.is_reference:
+ item_type = item_type.ref_base_type
+ if item_type.is_const:
+ item_type = item_type.const_base_type
+ return item_type
+ else:
+ # Avoid duplication of complicated logic.
+ fake_index_node = IndexNode(
+ self.pos,
+ base=self.iterator.sequence,
+ index=IntNode(self.pos, value='PY_SSIZE_T_MAX',
+ type=PyrexTypes.c_py_ssize_t_type))
+ return fake_index_node.infer_type(env)
+
+ def analyse_types(self, env):
+ self.type = self.infer_type(env, self.iterator.type)
+ self.is_temp = 1
+ return self
+
+ def generate_result_code(self, code):
+ self.iterator.generate_iter_next_result_code(self.result(), code)
+
+
class AsyncIteratorNode(ExprNode):
# Used as part of 'async for' statement implementation.
#
@@ -2963,47 +2963,47 @@ class AsyncNextNode(AtomicExprNode):
code.put_gotref(self.result())
-class WithExitCallNode(ExprNode):
- # The __exit__() call of a 'with' statement. Used in both the
- # except and finally clauses.
-
+class WithExitCallNode(ExprNode):
+ # The __exit__() call of a 'with' statement. Used in both the
+ # except and finally clauses.
+
# with_stat WithStatNode the surrounding 'with' statement
# args TupleNode or ResultStatNode the exception info tuple
# await_expr AwaitExprNode the await expression of an 'async with' statement
-
+
subexprs = ['args', 'await_expr']
- test_if_run = True
+ test_if_run = True
await_expr = None
-
- def analyse_types(self, env):
- self.args = self.args.analyse_types(env)
+
+ def analyse_types(self, env):
+ self.args = self.args.analyse_types(env)
if self.await_expr:
self.await_expr = self.await_expr.analyse_types(env)
- self.type = PyrexTypes.c_bint_type
- self.is_temp = True
- return self
-
- def generate_evaluation_code(self, code):
- if self.test_if_run:
- # call only if it was not already called (and decref-cleared)
- code.putln("if (%s) {" % self.with_stat.exit_var)
-
- self.args.generate_evaluation_code(code)
- result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
-
- code.mark_pos(self.pos)
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln("%s = __Pyx_PyObject_Call(%s, %s, NULL);" % (
- result_var,
- self.with_stat.exit_var,
- self.args.result()))
- code.put_decref_clear(self.with_stat.exit_var, type=py_object_type)
- self.args.generate_disposal_code(code)
- self.args.free_temps(code)
-
- code.putln(code.error_goto_if_null(result_var, self.pos))
- code.put_gotref(result_var)
+ self.type = PyrexTypes.c_bint_type
+ self.is_temp = True
+ return self
+
+ def generate_evaluation_code(self, code):
+ if self.test_if_run:
+ # call only if it was not already called (and decref-cleared)
+ code.putln("if (%s) {" % self.with_stat.exit_var)
+
+ self.args.generate_evaluation_code(code)
+ result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
+
+ code.mark_pos(self.pos)
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCall", "ObjectHandling.c"))
+ code.putln("%s = __Pyx_PyObject_Call(%s, %s, NULL);" % (
+ result_var,
+ self.with_stat.exit_var,
+ self.args.result()))
+ code.put_decref_clear(self.with_stat.exit_var, type=py_object_type)
+ self.args.generate_disposal_code(code)
+ self.args.free_temps(code)
+
+ code.putln(code.error_goto_if_null(result_var, self.pos))
+ code.put_gotref(result_var)
if self.await_expr:
# FIXME: result_var temp currently leaks into the closure
@@ -3012,117 +3012,117 @@ class WithExitCallNode(ExprNode):
self.await_expr.generate_post_assignment_code(code)
self.await_expr.free_temps(code)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var))
- code.put_decref_clear(result_var, type=py_object_type)
- if self.result_is_used:
- code.put_error_if_neg(self.pos, self.result())
- code.funcstate.release_temp(result_var)
- if self.test_if_run:
- code.putln("}")
-
-
-class ExcValueNode(AtomicExprNode):
- # Node created during analyse_types phase
- # of an ExceptClauseNode to fetch the current
- # exception value.
-
- type = py_object_type
-
- def __init__(self, pos):
- ExprNode.__init__(self, pos)
-
- def set_var(self, var):
- self.var = var
-
- def calculate_result_code(self):
- return self.var
-
- def generate_result_code(self, code):
- pass
-
- def analyse_types(self, env):
- return self
-
-
-class TempNode(ExprNode):
- # Node created during analyse_types phase
- # of some nodes to hold a temporary value.
- #
- # Note: One must call "allocate" and "release" on
- # the node during code generation to get/release the temp.
- # This is because the temp result is often used outside of
- # the regular cycle.
-
- subexprs = []
-
- def __init__(self, pos, type, env=None):
- ExprNode.__init__(self, pos)
- self.type = type
- if type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
-
- def analyse_types(self, env):
- return self
-
- def analyse_target_declaration(self, env):
- pass
-
- def generate_result_code(self, code):
- pass
-
- def allocate(self, code):
- self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True)
-
- def release(self, code):
- code.funcstate.release_temp(self.temp_cname)
- self.temp_cname = None
-
- def result(self):
- try:
- return self.temp_cname
- except:
- assert False, "Remember to call allocate/release on TempNode"
- raise
-
- # Do not participate in normal temp alloc/dealloc:
- def allocate_temp_result(self, code):
- pass
-
- def release_temp_result(self, code):
- pass
-
-class PyTempNode(TempNode):
- # TempNode holding a Python value.
-
- def __init__(self, pos, env):
- TempNode.__init__(self, pos, PyrexTypes.py_object_type, env)
-
-class RawCNameExprNode(ExprNode):
- subexprs = []
-
- def __init__(self, pos, type=None, cname=None):
- ExprNode.__init__(self, pos, type=type)
- if cname is not None:
- self.cname = cname
-
- def analyse_types(self, env):
- return self
-
- def set_cname(self, cname):
- self.cname = cname
-
- def result(self):
- return self.cname
-
- def generate_result_code(self, code):
- pass
-
-
-#-------------------------------------------------------------------
-#
+ if self.result_is_used:
+ self.allocate_temp_result(code)
+ code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var))
+ code.put_decref_clear(result_var, type=py_object_type)
+ if self.result_is_used:
+ code.put_error_if_neg(self.pos, self.result())
+ code.funcstate.release_temp(result_var)
+ if self.test_if_run:
+ code.putln("}")
+
+
+class ExcValueNode(AtomicExprNode):
+ # Node created during analyse_types phase
+ # of an ExceptClauseNode to fetch the current
+ # exception value.
+
+ type = py_object_type
+
+ def __init__(self, pos):
+ ExprNode.__init__(self, pos)
+
+ def set_var(self, var):
+ self.var = var
+
+ def calculate_result_code(self):
+ return self.var
+
+ def generate_result_code(self, code):
+ pass
+
+ def analyse_types(self, env):
+ return self
+
+
+class TempNode(ExprNode):
+ # Node created during analyse_types phase
+ # of some nodes to hold a temporary value.
+ #
+ # Note: One must call "allocate" and "release" on
+ # the node during code generation to get/release the temp.
+ # This is because the temp result is often used outside of
+ # the regular cycle.
+
+ subexprs = []
+
+ def __init__(self, pos, type, env=None):
+ ExprNode.__init__(self, pos)
+ self.type = type
+ if type.is_pyobject:
+ self.result_ctype = py_object_type
+ self.is_temp = 1
+
+ def analyse_types(self, env):
+ return self
+
+ def analyse_target_declaration(self, env):
+ pass
+
+ def generate_result_code(self, code):
+ pass
+
+ def allocate(self, code):
+ self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True)
+
+ def release(self, code):
+ code.funcstate.release_temp(self.temp_cname)
+ self.temp_cname = None
+
+ def result(self):
+ try:
+ return self.temp_cname
+ except:
+ assert False, "Remember to call allocate/release on TempNode"
+ raise
+
+ # Do not participate in normal temp alloc/dealloc:
+ def allocate_temp_result(self, code):
+ pass
+
+ def release_temp_result(self, code):
+ pass
+
+class PyTempNode(TempNode):
+ # TempNode holding a Python value.
+
+ def __init__(self, pos, env):
+ TempNode.__init__(self, pos, PyrexTypes.py_object_type, env)
+
+class RawCNameExprNode(ExprNode):
+ subexprs = []
+
+ def __init__(self, pos, type=None, cname=None):
+ ExprNode.__init__(self, pos, type=type)
+ if cname is not None:
+ self.cname = cname
+
+ def analyse_types(self, env):
+ return self
+
+ def set_cname(self, cname):
+ self.cname = cname
+
+ def result(self):
+ return self.cname
+
+ def generate_result_code(self, code):
+ pass
+
+
+#-------------------------------------------------------------------
+#
# F-strings
#
#-------------------------------------------------------------------
@@ -3318,68 +3318,68 @@ class FormattedValueNode(ExprNode):
#-------------------------------------------------------------------
#
-# Parallel nodes (cython.parallel.thread(savailable|id))
-#
-#-------------------------------------------------------------------
-
-class ParallelThreadsAvailableNode(AtomicExprNode):
- """
- Note: this is disabled and not a valid directive at this moment
-
- Implements cython.parallel.threadsavailable(). If we are called from the
- sequential part of the application, we need to call omp_get_max_threads(),
- and in the parallel part we can just call omp_get_num_threads()
- """
-
- type = PyrexTypes.c_int_type
-
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
-
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("if (omp_in_parallel()) %s = omp_get_max_threads();" %
- self.temp_code)
- code.putln("else %s = omp_get_num_threads();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 1;" % self.temp_code)
- code.putln("#endif")
-
- def result(self):
- return self.temp_code
-
-
-class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
- """
- Implements cython.parallel.threadid()
- """
-
- type = PyrexTypes.c_int_type
-
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
-
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("%s = omp_get_thread_num();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 0;" % self.temp_code)
- code.putln("#endif")
-
- def result(self):
- return self.temp_code
-
-
-#-------------------------------------------------------------------
-#
-# Trailer nodes
-#
-#-------------------------------------------------------------------
-
+# Parallel nodes (cython.parallel.thread(savailable|id))
+#
+#-------------------------------------------------------------------
+
+class ParallelThreadsAvailableNode(AtomicExprNode):
+ """
+ Note: this is disabled and not a valid directive at this moment
+
+ Implements cython.parallel.threadsavailable(). If we are called from the
+ sequential part of the application, we need to call omp_get_max_threads(),
+ and in the parallel part we can just call omp_get_num_threads()
+ """
+
+ type = PyrexTypes.c_int_type
+
+ def analyse_types(self, env):
+ self.is_temp = True
+ # env.add_include_file("omp.h")
+ return self
+
+ def generate_result_code(self, code):
+ code.putln("#ifdef _OPENMP")
+ code.putln("if (omp_in_parallel()) %s = omp_get_max_threads();" %
+ self.temp_code)
+ code.putln("else %s = omp_get_num_threads();" % self.temp_code)
+ code.putln("#else")
+ code.putln("%s = 1;" % self.temp_code)
+ code.putln("#endif")
+
+ def result(self):
+ return self.temp_code
+
+
+class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
+ """
+ Implements cython.parallel.threadid()
+ """
+
+ type = PyrexTypes.c_int_type
+
+ def analyse_types(self, env):
+ self.is_temp = True
+ # env.add_include_file("omp.h")
+ return self
+
+ def generate_result_code(self, code):
+ code.putln("#ifdef _OPENMP")
+ code.putln("%s = omp_get_thread_num();" % self.temp_code)
+ code.putln("#else")
+ code.putln("%s = 0;" % self.temp_code)
+ code.putln("#endif")
+
+ def result(self):
+ return self.temp_code
+
+
+#-------------------------------------------------------------------
+#
+# Trailer nodes
+#
+#-------------------------------------------------------------------
+
class _IndexingBaseNode(ExprNode):
# Base class for indexing nodes.
@@ -3412,61 +3412,61 @@ class _IndexingBaseNode(ExprNode):
class IndexNode(_IndexingBaseNode):
- # Sequence indexing.
- #
- # base ExprNode
- # index ExprNode
- # type_indices [PyrexType]
- #
- # is_fused_index boolean Whether the index is used to specialize a
- # c(p)def function
-
+ # Sequence indexing.
+ #
+ # base ExprNode
+ # index ExprNode
+ # type_indices [PyrexType]
+ #
+ # is_fused_index boolean Whether the index is used to specialize a
+ # c(p)def function
+
subexprs = ['base', 'index']
- type_indices = None
-
- is_subscript = True
- is_fused_index = False
-
- def calculate_constant_result(self):
+ type_indices = None
+
+ is_subscript = True
+ is_fused_index = False
+
+ def calculate_constant_result(self):
self.constant_result = self.base.constant_result[self.index.constant_result]
-
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- index = self.index.compile_time_value(denv)
- try:
- return base[index]
+
+ def compile_time_value(self, denv):
+ base = self.base.compile_time_value(denv)
+ index = self.index.compile_time_value(denv)
+ try:
+ return base[index]
except Exception as e:
- self.compile_time_value_error(e)
-
- def is_simple(self):
- base = self.base
- return (base.is_simple() and self.index.is_simple()
- and base.type and (base.type.is_ptr or base.type.is_array))
-
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if isinstance(self.index, SliceNode):
- # slicing!
+ self.compile_time_value_error(e)
+
+ def is_simple(self):
+ base = self.base
+ return (base.is_simple() and self.index.is_simple()
+ and base.type and (base.type.is_ptr or base.type.is_array))
+
+ def may_be_none(self):
+ base_type = self.base.type
+ if base_type:
+ if base_type.is_string:
+ return False
+ if isinstance(self.index, SliceNode):
+ # slicing!
if base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
-
- def analyse_target_declaration(self, env):
- pass
-
- def analyse_as_type(self, env):
- base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if base_type.is_cpp_class:
- if isinstance(self.index, TupleNode):
- template_values = self.index.args
- else:
- template_values = [self.index]
- type_node = Nodes.TemplatedTypeNode(
+ basestring_type, list_type, tuple_type):
+ return False
+ return ExprNode.may_be_none(self)
+
+ def analyse_target_declaration(self, env):
+ pass
+
+ def analyse_as_type(self, env):
+ base_type = self.base.analyse_as_type(env)
+ if base_type and not base_type.is_pyobject:
+ if base_type.is_cpp_class:
+ if isinstance(self.index, TupleNode):
+ template_values = self.index.args
+ else:
+ template_values = [self.index]
+ type_node = Nodes.TemplatedTypeNode(
pos=self.pos,
positional_args=template_values,
keyword_args=None)
@@ -3477,71 +3477,71 @@ class IndexNode(_IndexingBaseNode):
env.use_utility_code(MemoryView.view_utility_code)
axes = [self.index] if self.index.is_slice else list(self.index.args)
return PyrexTypes.MemoryViewSliceType(base_type, MemoryView.get_axes_specs(env, axes))
- else:
+ else:
# C array
- index = self.index.compile_time_value(env)
- if index is not None:
+ index = self.index.compile_time_value(env)
+ if index is not None:
try:
index = int(index)
except (ValueError, TypeError):
pass
else:
return PyrexTypes.CArrayType(base_type, index)
- error(self.pos, "Array size must be a compile time constant")
- return None
-
- def type_dependencies(self, env):
- return self.base.type_dependencies(env) + self.index.type_dependencies(env)
-
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
+ error(self.pos, "Array size must be a compile time constant")
+ return None
+
+ def type_dependencies(self, env):
+ return self.base.type_dependencies(env) + self.index.type_dependencies(env)
+
+ def infer_type(self, env):
+ base_type = self.base.infer_type(env)
if self.index.is_slice:
- # slicing!
- if base_type.is_string:
- # sliced C strings must coerce to Python
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- # sliced Py_UNICODE* strings must coerce to Python
- return unicode_type
- elif base_type in (unicode_type, bytes_type, str_type,
- bytearray_type, list_type, tuple_type):
- # slicing these returns the same type
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
-
- index_type = self.index.infer_type(env)
- if index_type and index_type.is_int or isinstance(self.index, IntNode):
- # indexing!
- if base_type is unicode_type:
- # Py_UCS4 will automatically coerce to a unicode string
- # if required, so this is safe. We only infer Py_UCS4
- # when the index is a C integer type. Otherwise, we may
- # need to use normal Python item access, in which case
- # it's faster to return the one-char unicode string than
- # to receive it, throw it away, and potentially rebuild it
- # on a subsequent PyObject coercion.
- return PyrexTypes.c_py_ucs4_type
- elif base_type is str_type:
- # always returns str - Py2: bytes, Py3: unicode
- return base_type
- elif base_type is bytearray_type:
- return PyrexTypes.c_uchar_type
- elif isinstance(self.base, BytesNode):
- #if env.global_scope().context.language_level >= 3:
- # # inferring 'char' can be made to work in Python 3 mode
- # return PyrexTypes.c_char_type
- # Py2/3 return different types on indexing bytes objects
- return py_object_type
- elif base_type in (tuple_type, list_type):
- # if base is a literal, take a look at its values
- item_type = infer_sequence_item_type(
- env, self.base, self.index, seq_type=base_type)
- if item_type is not None:
- return item_type
- elif base_type.is_ptr or base_type.is_array:
- return base_type.base_type
+ # slicing!
+ if base_type.is_string:
+ # sliced C strings must coerce to Python
+ return bytes_type
+ elif base_type.is_pyunicode_ptr:
+ # sliced Py_UNICODE* strings must coerce to Python
+ return unicode_type
+ elif base_type in (unicode_type, bytes_type, str_type,
+ bytearray_type, list_type, tuple_type):
+ # slicing these returns the same type
+ return base_type
+ else:
+ # TODO: Handle buffers (hopefully without too much redundancy).
+ return py_object_type
+
+ index_type = self.index.infer_type(env)
+ if index_type and index_type.is_int or isinstance(self.index, IntNode):
+ # indexing!
+ if base_type is unicode_type:
+ # Py_UCS4 will automatically coerce to a unicode string
+ # if required, so this is safe. We only infer Py_UCS4
+ # when the index is a C integer type. Otherwise, we may
+ # need to use normal Python item access, in which case
+ # it's faster to return the one-char unicode string than
+ # to receive it, throw it away, and potentially rebuild it
+ # on a subsequent PyObject coercion.
+ return PyrexTypes.c_py_ucs4_type
+ elif base_type is str_type:
+ # always returns str - Py2: bytes, Py3: unicode
+ return base_type
+ elif base_type is bytearray_type:
+ return PyrexTypes.c_uchar_type
+ elif isinstance(self.base, BytesNode):
+ #if env.global_scope().context.language_level >= 3:
+ # # inferring 'char' can be made to work in Python 3 mode
+ # return PyrexTypes.c_char_type
+ # Py2/3 return different types on indexing bytes objects
+ return py_object_type
+ elif base_type in (tuple_type, list_type):
+ # if base is a literal, take a look at its values
+ item_type = infer_sequence_item_type(
+ env, self.base, self.index, seq_type=base_type)
+ if item_type is not None:
+ return item_type
+ elif base_type.is_ptr or base_type.is_array:
+ return base_type.base_type
elif base_type.is_ctuple and isinstance(self.index, IntNode):
if self.index.has_constant_result():
index = self.index.constant_result
@@ -3549,94 +3549,94 @@ class IndexNode(_IndexingBaseNode):
index += base_type.size
if 0 <= index < base_type.size:
return base_type.components[index]
-
- if base_type.is_cpp_class:
- class FakeOperand:
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
- operands = [
- FakeOperand(pos=self.pos, type=base_type),
- FakeOperand(pos=self.pos, type=index_type),
- ]
- index_func = env.lookup_operator('[]', operands)
- if index_func is not None:
- return index_func.type.return_type
-
+
+ if base_type.is_cpp_class:
+ class FakeOperand:
+ def __init__(self, **kwds):
+ self.__dict__.update(kwds)
+ operands = [
+ FakeOperand(pos=self.pos, type=base_type),
+ FakeOperand(pos=self.pos, type=index_type),
+ ]
+ index_func = env.lookup_operator('[]', operands)
+ if index_func is not None:
+ return index_func.type.return_type
+
if is_pythran_expr(base_type) and is_pythran_expr(index_type):
index_with_type = (self.index, index_type)
return PythranExpr(pythran_indexing_type(base_type, [index_with_type]))
- # may be slicing or indexing, we don't know
- if base_type in (unicode_type, str_type):
- # these types always returns their own type on Python indexing/slicing
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
-
- def analyse_types(self, env):
- return self.analyse_base_and_index_types(env, getting=True)
-
- def analyse_target_types(self, env):
- node = self.analyse_base_and_index_types(env, setting=True)
- if node.type.is_const:
- error(self.pos, "Assignment to const dereference")
+ # may be slicing or indexing, we don't know
+ if base_type in (unicode_type, str_type):
+ # these types always returns their own type on Python indexing/slicing
+ return base_type
+ else:
+ # TODO: Handle buffers (hopefully without too much redundancy).
+ return py_object_type
+
+ def analyse_types(self, env):
+ return self.analyse_base_and_index_types(env, getting=True)
+
+ def analyse_target_types(self, env):
+ node = self.analyse_base_and_index_types(env, setting=True)
+ if node.type.is_const:
+ error(self.pos, "Assignment to const dereference")
if node is self and not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % node.type)
- return node
-
- def analyse_base_and_index_types(self, env, getting=False, setting=False,
- analyse_base=True):
- # Note: This might be cleaned up by having IndexNode
- # parsed in a saner way and only construct the tuple if
- # needed.
- if analyse_base:
- self.base = self.base.analyse_types(env)
-
- if self.base.type.is_error:
- # Do not visit child tree if base is undeclared to avoid confusing
- # error messages
- self.type = PyrexTypes.error_type
- return self
-
+ error(self.pos, "Assignment to non-lvalue of type '%s'" % node.type)
+ return node
+
+ def analyse_base_and_index_types(self, env, getting=False, setting=False,
+ analyse_base=True):
+ # Note: This might be cleaned up by having IndexNode
+ # parsed in a saner way and only construct the tuple if
+ # needed.
+ if analyse_base:
+ self.base = self.base.analyse_types(env)
+
+ if self.base.type.is_error:
+ # Do not visit child tree if base is undeclared to avoid confusing
+ # error messages
+ self.type = PyrexTypes.error_type
+ return self
+
is_slice = self.index.is_slice
- if not env.directives['wraparound']:
- if is_slice:
- check_negative_indices(self.index.start, self.index.stop)
- else:
- check_negative_indices(self.index)
-
- # Potentially overflowing index value.
- if not is_slice and isinstance(self.index, IntNode) and Utils.long_literal(self.index.value):
- self.index = self.index.coerce_to_pyobject(env)
-
- is_memslice = self.base.type.is_memoryviewslice
- # Handle the case where base is a literal char* (and we expect a string, not an int)
- if not is_memslice and (isinstance(self.base, BytesNode) or is_slice):
- if self.base.type.is_string or not (self.base.type.is_ptr or self.base.type.is_array):
- self.base = self.base.coerce_to_pyobject(env)
-
+ if not env.directives['wraparound']:
+ if is_slice:
+ check_negative_indices(self.index.start, self.index.stop)
+ else:
+ check_negative_indices(self.index)
+
+ # Potentially overflowing index value.
+ if not is_slice and isinstance(self.index, IntNode) and Utils.long_literal(self.index.value):
+ self.index = self.index.coerce_to_pyobject(env)
+
+ is_memslice = self.base.type.is_memoryviewslice
+ # Handle the case where base is a literal char* (and we expect a string, not an int)
+ if not is_memslice and (isinstance(self.base, BytesNode) or is_slice):
+ if self.base.type.is_string or not (self.base.type.is_ptr or self.base.type.is_array):
+ self.base = self.base.coerce_to_pyobject(env)
+
replacement_node = self.analyse_as_buffer_operation(env, getting)
if replacement_node is not None:
return replacement_node
-
- self.nogil = env.nogil
+
+ self.nogil = env.nogil
base_type = self.base.type
-
+
if not base_type.is_cfunction:
self.index = self.index.analyse_types(env)
self.original_index_type = self.index.type
-
- if base_type.is_unicode_char:
- # we infer Py_UNICODE/Py_UCS4 for unicode strings in some
- # cases, but indexing must still work for them
- if setting:
- warning(self.pos, "cannot assign to Unicode string index", level=1)
- elif self.index.constant_result in (0, -1):
- # uchar[0] => uchar
- return self.base
- self.base = self.base.coerce_to_pyobject(env)
- base_type = self.base.type
+
+ if base_type.is_unicode_char:
+ # we infer Py_UNICODE/Py_UCS4 for unicode strings in some
+ # cases, but indexing must still work for them
+ if setting:
+ warning(self.pos, "cannot assign to Unicode string index", level=1)
+ elif self.index.constant_result in (0, -1):
+ # uchar[0] => uchar
+ return self.base
+ self.base = self.base.coerce_to_pyobject(env)
+ base_type = self.base.type
if base_type.is_pyobject:
return self.analyse_as_pyobject(env, is_slice, getting, setting)
@@ -3673,14 +3673,14 @@ class IndexNode(_IndexingBaseNode):
self.index.has_constant_result() and self.index.constant_result >= 0))
and not env.directives['boundscheck']):
self.is_temp = 0
- else:
+ else:
self.is_temp = 1
self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env)
self.original_index_type.create_to_py_utility_code(env)
else:
self.index = self.index.coerce_to_pyobject(env)
self.is_temp = 1
-
+
if self.index.type.is_int and base_type is unicode_type:
# Py_UNICODE/Py_UCS4 will automatically coerce to a unicode string
# if required, so this is fast and safe
@@ -3705,9 +3705,9 @@ class IndexNode(_IndexingBaseNode):
# do the None check explicitly (not in a helper) to allow optimising it away
self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- self.wrap_in_nonecheck_node(env, getting)
- return self
-
+ self.wrap_in_nonecheck_node(env, getting)
+ return self
+
def analyse_as_c_array(self, env, is_slice):
base_type = self.base.type
self.type = base_type.base_type
@@ -3718,7 +3718,7 @@ class IndexNode(_IndexingBaseNode):
elif not self.index.type.is_int:
error(self.pos, "Invalid index type '%s'" % self.index.type)
return self
-
+
def analyse_as_cpp(self, env, setting):
base_type = self.base.type
function = env.lookup_operator("[]", [self.base, self.index])
@@ -3760,7 +3760,7 @@ class IndexNode(_IndexingBaseNode):
error(self.pos, "Wrong number of template arguments: expected %s, got %s" % (
(len(base_type.templates), len(self.type_indices))))
self.type = error_type
- else:
+ else:
self.type = base_type.specialize(dict(zip(base_type.templates, self.type_indices)))
# FIXME: use a dedicated Node class instead of generic IndexNode
return self
@@ -3779,10 +3779,10 @@ class IndexNode(_IndexingBaseNode):
(index, base_type))
self.type = PyrexTypes.error_type
return self
- else:
+ else:
self.base = self.base.coerce_to_pyobject(env)
return self.analyse_base_and_index_types(env, getting=getting, setting=setting, analyse_base=False)
-
+
def analyse_as_buffer_operation(self, env, getting):
"""
Analyse buffer indexing and memoryview indexing/slicing
@@ -3791,7 +3791,7 @@ class IndexNode(_IndexingBaseNode):
indices = self.index.args
else:
indices = [self.index]
-
+
base = self.base
base_type = base.type
replacement_node = None
@@ -3841,157 +3841,157 @@ class IndexNode(_IndexingBaseNode):
return
self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- def parse_index_as_types(self, env, required=True):
- if isinstance(self.index, TupleNode):
- indices = self.index.args
- else:
- indices = [self.index]
- type_indices = []
- for index in indices:
- type_indices.append(index.analyse_as_type(env))
- if type_indices[-1] is None:
- if required:
- error(index.pos, "not parsable as a type")
- return None
- return type_indices
-
- def parse_indexed_fused_cdef(self, env):
- """
- Interpret fused_cdef_func[specific_type1, ...]
-
- Note that if this method is called, we are an indexed cdef function
- with fused argument types, and this IndexNode will be replaced by the
- NameNode with specific entry just after analysis of expressions by
- AnalyseExpressionsTransform.
- """
- self.type = PyrexTypes.error_type
-
- self.is_fused_index = True
-
- base_type = self.base.type
- positions = []
-
- if self.index.is_name or self.index.is_attribute:
- positions.append(self.index.pos)
- elif isinstance(self.index, TupleNode):
- for arg in self.index.args:
- positions.append(arg.pos)
- specific_types = self.parse_index_as_types(env, required=False)
-
- if specific_types is None:
- self.index = self.index.analyse_types(env)
-
- if not self.base.entry.as_variable:
- error(self.pos, "Can only index fused functions with types")
- else:
- # A cpdef function indexed with Python objects
- self.base.entry = self.entry = self.base.entry.as_variable
- self.base.type = self.type = self.entry.type
-
- self.base.is_temp = True
- self.is_temp = True
-
- self.entry.used = True
-
- self.is_fused_index = False
- return
-
- for i, type in enumerate(specific_types):
- specific_types[i] = type.specialize_fused(env)
-
- fused_types = base_type.get_fused_types()
- if len(specific_types) > len(fused_types):
- return error(self.pos, "Too many types specified")
- elif len(specific_types) < len(fused_types):
- t = fused_types[len(specific_types)]
- return error(self.pos, "Not enough types specified to specialize "
- "the function, %s is still fused" % t)
-
- # See if our index types form valid specializations
- for pos, specific_type, fused_type in zip(positions,
- specific_types,
- fused_types):
- if not any([specific_type.same_as(t) for t in fused_type.types]):
- return error(pos, "Type not in fused type")
-
- if specific_type is None or specific_type.is_error:
- return
-
- fused_to_specific = dict(zip(fused_types, specific_types))
- type = base_type.specialize(fused_to_specific)
-
- if type.is_fused:
- # Only partially specific, this is invalid
- error(self.pos,
- "Index operation makes function only partially specific")
- else:
- # Fully specific, find the signature with the specialized entry
- for signature in self.base.type.get_all_specialized_function_types():
- if type.same_as(signature):
- self.type = signature
-
- if self.base.is_attribute:
- # Pretend to be a normal attribute, for cdef extension
- # methods
- self.entry = signature.entry
- self.is_attribute = True
- self.obj = self.base.obj
-
- self.type.entry.used = True
- self.base.type = signature
- self.base.entry = signature.entry
-
- break
- else:
- # This is a bug
- raise InternalError("Couldn't find the right signature")
-
- gil_message = "Indexing Python object"
-
- def calculate_result_code(self):
+ def parse_index_as_types(self, env, required=True):
+ if isinstance(self.index, TupleNode):
+ indices = self.index.args
+ else:
+ indices = [self.index]
+ type_indices = []
+ for index in indices:
+ type_indices.append(index.analyse_as_type(env))
+ if type_indices[-1] is None:
+ if required:
+ error(index.pos, "not parsable as a type")
+ return None
+ return type_indices
+
+ def parse_indexed_fused_cdef(self, env):
+ """
+ Interpret fused_cdef_func[specific_type1, ...]
+
+ Note that if this method is called, we are an indexed cdef function
+ with fused argument types, and this IndexNode will be replaced by the
+ NameNode with specific entry just after analysis of expressions by
+ AnalyseExpressionsTransform.
+ """
+ self.type = PyrexTypes.error_type
+
+ self.is_fused_index = True
+
+ base_type = self.base.type
+ positions = []
+
+ if self.index.is_name or self.index.is_attribute:
+ positions.append(self.index.pos)
+ elif isinstance(self.index, TupleNode):
+ for arg in self.index.args:
+ positions.append(arg.pos)
+ specific_types = self.parse_index_as_types(env, required=False)
+
+ if specific_types is None:
+ self.index = self.index.analyse_types(env)
+
+ if not self.base.entry.as_variable:
+ error(self.pos, "Can only index fused functions with types")
+ else:
+ # A cpdef function indexed with Python objects
+ self.base.entry = self.entry = self.base.entry.as_variable
+ self.base.type = self.type = self.entry.type
+
+ self.base.is_temp = True
+ self.is_temp = True
+
+ self.entry.used = True
+
+ self.is_fused_index = False
+ return
+
+ for i, type in enumerate(specific_types):
+ specific_types[i] = type.specialize_fused(env)
+
+ fused_types = base_type.get_fused_types()
+ if len(specific_types) > len(fused_types):
+ return error(self.pos, "Too many types specified")
+ elif len(specific_types) < len(fused_types):
+ t = fused_types[len(specific_types)]
+ return error(self.pos, "Not enough types specified to specialize "
+ "the function, %s is still fused" % t)
+
+ # See if our index types form valid specializations
+ for pos, specific_type, fused_type in zip(positions,
+ specific_types,
+ fused_types):
+ if not any([specific_type.same_as(t) for t in fused_type.types]):
+ return error(pos, "Type not in fused type")
+
+ if specific_type is None or specific_type.is_error:
+ return
+
+ fused_to_specific = dict(zip(fused_types, specific_types))
+ type = base_type.specialize(fused_to_specific)
+
+ if type.is_fused:
+ # Only partially specific, this is invalid
+ error(self.pos,
+ "Index operation makes function only partially specific")
+ else:
+ # Fully specific, find the signature with the specialized entry
+ for signature in self.base.type.get_all_specialized_function_types():
+ if type.same_as(signature):
+ self.type = signature
+
+ if self.base.is_attribute:
+ # Pretend to be a normal attribute, for cdef extension
+ # methods
+ self.entry = signature.entry
+ self.is_attribute = True
+ self.obj = self.base.obj
+
+ self.type.entry.used = True
+ self.base.type = signature
+ self.base.entry = signature.entry
+
+ break
+ else:
+ # This is a bug
+ raise InternalError("Couldn't find the right signature")
+
+ gil_message = "Indexing Python object"
+
+ def calculate_result_code(self):
if self.base.type in (list_type, tuple_type, bytearray_type):
- if self.base.type is list_type:
- index_code = "PyList_GET_ITEM(%s, %s)"
- elif self.base.type is tuple_type:
- index_code = "PyTuple_GET_ITEM(%s, %s)"
- elif self.base.type is bytearray_type:
- index_code = "((unsigned char)(PyByteArray_AS_STRING(%s)[%s]))"
- else:
- assert False, "unexpected base type in indexing: %s" % self.base.type
- elif self.base.type.is_cfunction:
- return "%s<%s>" % (
- self.base.result(),
+ if self.base.type is list_type:
+ index_code = "PyList_GET_ITEM(%s, %s)"
+ elif self.base.type is tuple_type:
+ index_code = "PyTuple_GET_ITEM(%s, %s)"
+ elif self.base.type is bytearray_type:
+ index_code = "((unsigned char)(PyByteArray_AS_STRING(%s)[%s]))"
+ else:
+ assert False, "unexpected base type in indexing: %s" % self.base.type
+ elif self.base.type.is_cfunction:
+ return "%s<%s>" % (
+ self.base.result(),
",".join([param.empty_declaration_code() for param in self.type_indices]))
elif self.base.type.is_ctuple:
index = self.index.constant_result
if index < 0:
index += self.base.type.size
return "%s.f%s" % (self.base.result(), index)
- else:
- if (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
- error(self.pos, "Invalid use of pointer slice")
- return
- index_code = "(%s[%s])"
- return index_code % (self.base.result(), self.index.result())
-
- def extra_index_params(self, code):
- if self.index.type.is_int:
- is_list = self.base.type is list_type
- wraparound = (
- bool(code.globalstate.directives['wraparound']) and
- self.original_index_type.signed and
+ else:
+ if (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
+ error(self.pos, "Invalid use of pointer slice")
+ return
+ index_code = "(%s[%s])"
+ return index_code % (self.base.result(), self.index.result())
+
+ def extra_index_params(self, code):
+ if self.index.type.is_int:
+ is_list = self.base.type is list_type
+ wraparound = (
+ bool(code.globalstate.directives['wraparound']) and
+ self.original_index_type.signed and
not (isinstance(self.index.constant_result, _py_int_types)
- and self.index.constant_result >= 0))
- boundscheck = bool(code.globalstate.directives['boundscheck'])
- return ", %s, %d, %s, %d, %d, %d" % (
+ and self.index.constant_result >= 0))
+ boundscheck = bool(code.globalstate.directives['boundscheck'])
+ return ", %s, %d, %s, %d, %d, %d" % (
self.original_index_type.empty_declaration_code(),
- self.original_index_type.signed and 1 or 0,
- self.original_index_type.to_py_function,
- is_list, wraparound, boundscheck)
- else:
- return ""
-
- def generate_result_code(self, code):
+ self.original_index_type.signed and 1 or 0,
+ self.original_index_type.to_py_function,
+ is_list, wraparound, boundscheck)
+ else:
+ return ""
+
+ def generate_result_code(self, code):
if not self.is_temp:
# all handled in self.calculate_result_code()
return
@@ -4004,10 +4004,10 @@ class IndexNode(_IndexingBaseNode):
function = "__Pyx_GetItemInt_List"
elif self.base.type is tuple_type:
function = "__Pyx_GetItemInt_Tuple"
- else:
+ else:
function = "__Pyx_GetItemInt"
utility_code = TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c")
- else:
+ else:
if self.base.type is dict_type:
function = "__Pyx_PyDict_GetItem"
utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
@@ -4034,7 +4034,7 @@ class IndexNode(_IndexingBaseNode):
elif not (self.base.type.is_cpp_class and self.exception_check):
assert False, "unexpected type %s and base type %s for indexing" % (
self.type, self.base.type)
-
+
if utility_code is not None:
code.globalstate.use_utility_code(utility_code)
@@ -4042,7 +4042,7 @@ class IndexNode(_IndexingBaseNode):
index_code = self.index.result()
else:
index_code = self.index.py_result()
-
+
if self.base.type.is_cpp_class and self.exception_check:
translate_cpp_exception(code, self.pos,
"%s = %s[%s];" % (self.result(), self.base.result(),
@@ -4051,59 +4051,59 @@ class IndexNode(_IndexingBaseNode):
self.exception_value, self.in_nogil_context)
else:
error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
- code.putln(
+ code.putln(
"%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.base.py_result(),
- index_code,
- self.extra_index_params(code),
+ self.result(),
+ function,
+ self.base.py_result(),
+ index_code,
+ self.extra_index_params(code),
code.error_goto_if(error_check % self.result(), self.pos)))
if self.type.is_pyobject:
code.put_gotref(self.py_result())
-
- def generate_setitem_code(self, value_code, code):
- if self.index.type.is_int:
- if self.base.type is bytearray_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemIntByteArray", "StringTools.c"))
- function = "__Pyx_SetItemInt_ByteArray"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemInt", "ObjectHandling.c"))
- function = "__Pyx_SetItemInt"
- index_code = self.index.result()
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_SetItem"
- # It would seem that we could specialized lists/tuples, but that
- # shouldn't happen here.
- # Both PyList_SetItem() and PyTuple_SetItem() take a Py_ssize_t as
- # index instead of an object, and bad conversion here would give
- # the wrong exception. Also, tuples are supposed to be immutable,
- # and raise a TypeError when trying to set their entries
- # (PyTuple_SetItem() is for creating new tuples from scratch).
- else:
- function = "PyObject_SetItem"
+
+ def generate_setitem_code(self, value_code, code):
+ if self.index.type.is_int:
+ if self.base.type is bytearray_type:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("SetItemIntByteArray", "StringTools.c"))
+ function = "__Pyx_SetItemInt_ByteArray"
+ else:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("SetItemInt", "ObjectHandling.c"))
+ function = "__Pyx_SetItemInt"
+ index_code = self.index.result()
+ else:
+ index_code = self.index.py_result()
+ if self.base.type is dict_type:
+ function = "PyDict_SetItem"
+ # It would seem that we could specialized lists/tuples, but that
+ # shouldn't happen here.
+ # Both PyList_SetItem() and PyTuple_SetItem() take a Py_ssize_t as
+ # index instead of an object, and bad conversion here would give
+ # the wrong exception. Also, tuples are supposed to be immutable,
+ # and raise a TypeError when trying to set their entries
+ # (PyTuple_SetItem() is for creating new tuples from scratch).
+ else:
+ function = "PyObject_SetItem"
code.putln(code.error_goto_if_neg(
"%s(%s, %s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
- value_code,
+ function,
+ self.base.py_result(),
+ index_code,
+ value_code,
self.extra_index_params(code)),
self.pos))
-
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
self.generate_subexpr_evaluation_code(code)
-
+
if self.type.is_pyobject:
- self.generate_setitem_code(rhs.py_result(), code)
- elif self.base.type is bytearray_type:
- value_code = self._check_byte_value(code, rhs)
- self.generate_setitem_code(value_code, code)
+ self.generate_setitem_code(rhs.py_result(), code)
+ elif self.base.type is bytearray_type:
+ value_code = self._check_byte_value(code, rhs)
+ self.generate_setitem_code(value_code, code)
elif self.base.type.is_cpp_class and self.exception_check and self.exception_check == '+':
if overloaded_assignment and exception_check and \
self.exception_value != exception_value:
@@ -4120,75 +4120,75 @@ class IndexNode(_IndexingBaseNode):
"%s = %s;" % (self.result(), rhs.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
- else:
- code.putln(
+ else:
+ code.putln(
"%s = %s;" % (self.result(), rhs.result()))
-
+
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
-
- def _check_byte_value(self, code, rhs):
- # TODO: should we do this generally on downcasts, or just here?
- assert rhs.type.is_int, repr(rhs.type)
- value_code = rhs.result()
- if rhs.has_constant_result():
- if 0 <= rhs.constant_result < 256:
- return value_code
- needs_cast = True # make at least the C compiler happy
- warning(rhs.pos,
- "value outside of range(0, 256)"
- " when assigning to byte: %s" % rhs.constant_result,
- level=1)
- else:
- needs_cast = rhs.type != PyrexTypes.c_uchar_type
-
- if not self.nogil:
- conditions = []
- if rhs.is_literal or rhs.type.signed:
- conditions.append('%s < 0' % value_code)
- if (rhs.is_literal or not
- (rhs.is_temp and rhs.type in (
- PyrexTypes.c_uchar_type, PyrexTypes.c_char_type,
- PyrexTypes.c_schar_type))):
- conditions.append('%s > 255' % value_code)
- if conditions:
- code.putln("if (unlikely(%s)) {" % ' || '.join(conditions))
- code.putln(
- 'PyErr_SetString(PyExc_ValueError,'
- ' "byte must be in range(0, 256)"); %s' %
- code.error_goto(self.pos))
- code.putln("}")
-
- if needs_cast:
- value_code = '((unsigned char)%s)' % value_code
- return value_code
-
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.generate_subexpr_evaluation_code(code)
- #if self.type.is_pyobject:
- if self.index.type.is_int:
- function = "__Pyx_DelItemInt"
- index_code = self.index.result()
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DelItemInt", "ObjectHandling.c"))
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_DelItem"
- else:
- function = "PyObject_DelItem"
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
+
+ def _check_byte_value(self, code, rhs):
+ # TODO: should we do this generally on downcasts, or just here?
+ assert rhs.type.is_int, repr(rhs.type)
+ value_code = rhs.result()
+ if rhs.has_constant_result():
+ if 0 <= rhs.constant_result < 256:
+ return value_code
+ needs_cast = True # make at least the C compiler happy
+ warning(rhs.pos,
+ "value outside of range(0, 256)"
+ " when assigning to byte: %s" % rhs.constant_result,
+ level=1)
+ else:
+ needs_cast = rhs.type != PyrexTypes.c_uchar_type
+
+ if not self.nogil:
+ conditions = []
+ if rhs.is_literal or rhs.type.signed:
+ conditions.append('%s < 0' % value_code)
+ if (rhs.is_literal or not
+ (rhs.is_temp and rhs.type in (
+ PyrexTypes.c_uchar_type, PyrexTypes.c_char_type,
+ PyrexTypes.c_schar_type))):
+ conditions.append('%s > 255' % value_code)
+ if conditions:
+ code.putln("if (unlikely(%s)) {" % ' || '.join(conditions))
+ code.putln(
+ 'PyErr_SetString(PyExc_ValueError,'
+ ' "byte must be in range(0, 256)"); %s' %
+ code.error_goto(self.pos))
+ code.putln("}")
+
+ if needs_cast:
+ value_code = '((unsigned char)%s)' % value_code
+ return value_code
+
+ def generate_deletion_code(self, code, ignore_nonexisting=False):
+ self.generate_subexpr_evaluation_code(code)
+ #if self.type.is_pyobject:
+ if self.index.type.is_int:
+ function = "__Pyx_DelItemInt"
+ index_code = self.index.result()
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("DelItemInt", "ObjectHandling.c"))
+ else:
+ index_code = self.index.py_result()
+ if self.base.type is dict_type:
+ function = "PyDict_DelItem"
+ else:
+ function = "PyObject_DelItem"
code.putln(code.error_goto_if_neg(
"%s(%s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
+ function,
+ self.base.py_result(),
+ index_code,
self.extra_index_params(code)),
self.pos))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
-
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+
class BufferIndexNode(_IndexingBaseNode):
"""
@@ -4269,12 +4269,12 @@ class BufferIndexNode(_IndexingBaseNode):
def calculate_result_code(self):
return "(*%s)" % self.buffer_ptr_code
- def buffer_entry(self):
- base = self.base
- if self.base.is_nonecheck:
- base = base.arg
+ def buffer_entry(self):
+ base = self.base
+ if self.base.is_nonecheck:
+ base = base.arg
return base.type.get_entry(base)
-
+
def get_index_in_temp(self, code, ivar):
ret = code.funcstate.allocate_temp(
PyrexTypes.widest_numeric_type(
@@ -4284,7 +4284,7 @@ class BufferIndexNode(_IndexingBaseNode):
code.putln("%s = %s;" % (ret, ivar.result()))
return ret
- def buffer_lookup_code(self, code):
+ def buffer_lookup_code(self, code):
"""
ndarray[1, 2, 3] and memslice[1, 2, 3]
"""
@@ -4295,16 +4295,16 @@ class BufferIndexNode(_IndexingBaseNode):
# Assign indices to temps of at least (s)size_t to allow further index calculations.
self.index_temps = index_temps = [self.get_index_in_temp(code,ivar) for ivar in self.indices]
-
- # Generate buffer access code using these temps
- from . import Buffer
- buffer_entry = self.buffer_entry()
- if buffer_entry.type.is_buffer:
- negative_indices = buffer_entry.type.negative_indices
- else:
- negative_indices = Buffer.buffer_defaults['negative_indices']
-
- return buffer_entry, Buffer.put_buffer_lookup_code(
+
+ # Generate buffer access code using these temps
+ from . import Buffer
+ buffer_entry = self.buffer_entry()
+ if buffer_entry.type.is_buffer:
+ negative_indices = buffer_entry.type.negative_indices
+ else:
+ negative_indices = Buffer.buffer_defaults['negative_indices']
+
+ return buffer_entry, Buffer.put_buffer_lookup_code(
entry=buffer_entry,
index_signeds=[ivar.type.signed for ivar in self.indices],
index_cnames=index_temps,
@@ -4312,7 +4312,7 @@ class BufferIndexNode(_IndexingBaseNode):
pos=self.pos, code=code,
negative_indices=negative_indices,
in_nogil_context=self.in_nogil_context)
-
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
self.generate_subexpr_evaluation_code(code)
self.generate_buffer_setitem_code(rhs, code)
@@ -4599,37 +4599,37 @@ class MemoryViewSliceNode(MemoryViewIndexNode):
def generate_result_code(self, code):
if self.is_ellipsis_noop:
return ### FIXME: remove
- buffer_entry = self.buffer_entry()
- have_gil = not self.in_nogil_context
-
+ buffer_entry = self.buffer_entry()
+ have_gil = not self.in_nogil_context
+
# TODO Mark: this is insane, do it better
- have_slices = False
- it = iter(self.indices)
- for index in self.original_indices:
+ have_slices = False
+ it = iter(self.indices)
+ for index in self.original_indices:
if index.is_slice:
have_slices = True
- if not index.start.is_none:
- index.start = next(it)
- if not index.stop.is_none:
- index.stop = next(it)
- if not index.step.is_none:
- index.step = next(it)
- else:
- next(it)
-
- assert not list(it)
-
+ if not index.start.is_none:
+ index.start = next(it)
+ if not index.stop.is_none:
+ index.stop = next(it)
+ if not index.step.is_none:
+ index.step = next(it)
+ else:
+ next(it)
+
+ assert not list(it)
+
buffer_entry.generate_buffer_slice_code(
code, self.original_indices, self.result(),
have_gil=have_gil, have_slices=have_slices,
directives=code.globalstate.directives)
-
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
if self.is_ellipsis_noop:
self.generate_subexpr_evaluation_code(code)
else:
self.generate_evaluation_code(code)
-
+
if self.is_memview_scalar_assignment:
self.generate_memoryviewslice_assign_scalar_code(rhs, code)
else:
@@ -4707,10 +4707,10 @@ class MemoryCopyScalar(MemoryCopyNode):
self.type = dst.type.dtype
def _generate_assignment_code(self, scalar, code):
- from . import MemoryView
-
+ from . import MemoryView
+
self.dst.type.assert_direct_dims(self.dst.pos)
-
+
dtype = self.dst.type.dtype
type_decl = dtype.declaration_code("")
slice_decl = self.dst.type.declaration_code("")
@@ -4739,31 +4739,31 @@ class MemoryCopyScalar(MemoryCopyNode):
code.end_block()
-class SliceIndexNode(ExprNode):
- # 2-element slice indexing
- #
- # base ExprNode
- # start ExprNode or None
- # stop ExprNode or None
- # slice ExprNode or None constant slice object
-
- subexprs = ['base', 'start', 'stop', 'slice']
-
- slice = None
-
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
- if base_type.is_string or base_type.is_cpp_class:
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- return unicode_type
+class SliceIndexNode(ExprNode):
+ # 2-element slice indexing
+ #
+ # base ExprNode
+ # start ExprNode or None
+ # stop ExprNode or None
+ # slice ExprNode or None constant slice object
+
+ subexprs = ['base', 'start', 'stop', 'slice']
+
+ slice = None
+
+ def infer_type(self, env):
+ base_type = self.base.infer_type(env)
+ if base_type.is_string or base_type.is_cpp_class:
+ return bytes_type
+ elif base_type.is_pyunicode_ptr:
+ return unicode_type
elif base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return base_type
- elif base_type.is_ptr or base_type.is_array:
- return PyrexTypes.c_array_type(base_type.base_type, None)
- return py_object_type
-
+ basestring_type, list_type, tuple_type):
+ return base_type
+ elif base_type.is_ptr or base_type.is_array:
+ return PyrexTypes.c_array_type(base_type.base_type, None)
+ return py_object_type
+
def inferable_item_node(self, index=0):
# slicing shouldn't change the result type of the base, but the index might
if index is not not_a_constant and self.start:
@@ -4773,75 +4773,75 @@ class SliceIndexNode(ExprNode):
index = not_a_constant
return self.base.inferable_item_node(index)
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if base_type in (bytes_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
-
- def calculate_constant_result(self):
- if self.start is None:
- start = None
- else:
- start = self.start.constant_result
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.constant_result
- self.constant_result = self.base.constant_result[start:stop]
-
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- if self.start is None:
- start = 0
- else:
- start = self.start.compile_time_value(denv)
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.compile_time_value(denv)
- try:
- return base[start:stop]
+ def may_be_none(self):
+ base_type = self.base.type
+ if base_type:
+ if base_type.is_string:
+ return False
+ if base_type in (bytes_type, str_type, unicode_type,
+ basestring_type, list_type, tuple_type):
+ return False
+ return ExprNode.may_be_none(self)
+
+ def calculate_constant_result(self):
+ if self.start is None:
+ start = None
+ else:
+ start = self.start.constant_result
+ if self.stop is None:
+ stop = None
+ else:
+ stop = self.stop.constant_result
+ self.constant_result = self.base.constant_result[start:stop]
+
+ def compile_time_value(self, denv):
+ base = self.base.compile_time_value(denv)
+ if self.start is None:
+ start = 0
+ else:
+ start = self.start.compile_time_value(denv)
+ if self.stop is None:
+ stop = None
+ else:
+ stop = self.stop.compile_time_value(denv)
+ try:
+ return base[start:stop]
except Exception as e:
- self.compile_time_value_error(e)
-
- def analyse_target_declaration(self, env):
- pass
-
- def analyse_target_types(self, env):
- node = self.analyse_types(env, getting=False)
- # when assigning, we must accept any Python type
- if node.type.is_pyobject:
- node.type = py_object_type
- return node
-
- def analyse_types(self, env, getting=True):
- self.base = self.base.analyse_types(env)
-
+ self.compile_time_value_error(e)
+
+ def analyse_target_declaration(self, env):
+ pass
+
+ def analyse_target_types(self, env):
+ node = self.analyse_types(env, getting=False)
+ # when assigning, we must accept any Python type
+ if node.type.is_pyobject:
+ node.type = py_object_type
+ return node
+
+ def analyse_types(self, env, getting=True):
+ self.base = self.base.analyse_types(env)
+
if self.base.type.is_buffer or self.base.type.is_pythran_expr or self.base.type.is_memoryviewslice:
- none_node = NoneNode(self.pos)
- index = SliceNode(self.pos,
- start=self.start or none_node,
- stop=self.stop or none_node,
- step=none_node)
+ none_node = NoneNode(self.pos)
+ index = SliceNode(self.pos,
+ start=self.start or none_node,
+ stop=self.stop or none_node,
+ step=none_node)
index_node = IndexNode(self.pos, index=index, base=self.base)
- return index_node.analyse_base_and_index_types(
- env, getting=getting, setting=not getting,
- analyse_base=False)
-
- if self.start:
- self.start = self.start.analyse_types(env)
- if self.stop:
- self.stop = self.stop.analyse_types(env)
-
- if not env.directives['wraparound']:
- check_negative_indices(self.start, self.stop)
-
- base_type = self.base.type
+ return index_node.analyse_base_and_index_types(
+ env, getting=getting, setting=not getting,
+ analyse_base=False)
+
+ if self.start:
+ self.start = self.start.analyse_types(env)
+ if self.stop:
+ self.stop = self.stop.analyse_types(env)
+
+ if not env.directives['wraparound']:
+ check_negative_indices(self.start, self.stop)
+
+ base_type = self.base.type
if base_type.is_array and not getting:
# cannot assign directly to C array => try to assign by making a copy
if not self.start and not self.stop:
@@ -4849,37 +4849,37 @@ class SliceIndexNode(ExprNode):
else:
self.type = PyrexTypes.CPtrType(base_type.base_type)
elif base_type.is_string or base_type.is_cpp_string:
- self.type = default_str_type(env)
- elif base_type.is_pyunicode_ptr:
- self.type = unicode_type
- elif base_type.is_ptr:
- self.type = base_type
- elif base_type.is_array:
- # we need a ptr type here instead of an array type, as
- # array types can result in invalid type casts in the C
- # code
- self.type = PyrexTypes.CPtrType(base_type.base_type)
- else:
- self.base = self.base.coerce_to_pyobject(env)
- self.type = py_object_type
- if base_type.is_builtin_type:
- # slicing builtin types returns something of the same type
- self.type = base_type
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
-
- if self.type is py_object_type:
- if (not self.start or self.start.is_literal) and \
- (not self.stop or self.stop.is_literal):
- # cache the constant slice object, in case we need it
- none_node = NoneNode(self.pos)
- self.slice = SliceNode(
- self.pos,
- start=copy.deepcopy(self.start or none_node),
- stop=copy.deepcopy(self.stop or none_node),
- step=none_node
- ).analyse_types(env)
- else:
- c_int = PyrexTypes.c_py_ssize_t_type
+ self.type = default_str_type(env)
+ elif base_type.is_pyunicode_ptr:
+ self.type = unicode_type
+ elif base_type.is_ptr:
+ self.type = base_type
+ elif base_type.is_array:
+ # we need a ptr type here instead of an array type, as
+ # array types can result in invalid type casts in the C
+ # code
+ self.type = PyrexTypes.CPtrType(base_type.base_type)
+ else:
+ self.base = self.base.coerce_to_pyobject(env)
+ self.type = py_object_type
+ if base_type.is_builtin_type:
+ # slicing builtin types returns something of the same type
+ self.type = base_type
+ self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
+
+ if self.type is py_object_type:
+ if (not self.start or self.start.is_literal) and \
+ (not self.stop or self.stop.is_literal):
+ # cache the constant slice object, in case we need it
+ none_node = NoneNode(self.pos)
+ self.slice = SliceNode(
+ self.pos,
+ start=copy.deepcopy(self.start or none_node),
+ stop=copy.deepcopy(self.stop or none_node),
+ step=none_node
+ ).analyse_types(env)
+ else:
+ c_int = PyrexTypes.c_py_ssize_t_type
def allow_none(node, default_value, env):
# Coerce to Py_ssize_t, but allow None as meaning the default slice bound.
@@ -4904,17 +4904,17 @@ class SliceIndexNode(ExprNode):
).analyse_result_type(env)
return EvalWithTempExprNode(node_ref, new_expr)
- if self.start:
+ if self.start:
if self.start.type.is_pyobject:
self.start = allow_none(self.start, '0', env)
- self.start = self.start.coerce_to(c_int, env)
- if self.stop:
+ self.start = self.start.coerce_to(c_int, env)
+ if self.stop:
if self.stop.type.is_pyobject:
self.stop = allow_none(self.stop, 'PY_SSIZE_T_MAX', env)
- self.stop = self.stop.coerce_to(c_int, env)
- self.is_temp = 1
- return self
-
+ self.stop = self.stop.coerce_to(c_int, env)
+ self.is_temp = 1
+ return self
+
def analyse_as_type(self, env):
base_type = self.base.analyse_as_type(env)
if base_type and not base_type.is_pyobject:
@@ -4933,153 +4933,153 @@ class SliceIndexNode(ExprNode):
base_type, MemoryView.get_axes_specs(env, [slice_node]))
return None
- nogil_check = Node.gil_error
- gil_message = "Slicing Python object"
-
- get_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Get'})
-
- set_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Set'})
-
- def coerce_to(self, dst_type, env):
- if ((self.base.type.is_string or self.base.type.is_cpp_string)
- and dst_type in (bytes_type, bytearray_type, str_type, unicode_type)):
- if (dst_type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(self.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (self.base.type, dst_type))
- self.type = dst_type
+ nogil_check = Node.gil_error
+ gil_message = "Slicing Python object"
+
+ get_slice_utility_code = TempitaUtilityCode.load(
+ "SliceObject", "ObjectHandling.c", context={'access': 'Get'})
+
+ set_slice_utility_code = TempitaUtilityCode.load(
+ "SliceObject", "ObjectHandling.c", context={'access': 'Set'})
+
+ def coerce_to(self, dst_type, env):
+ if ((self.base.type.is_string or self.base.type.is_cpp_string)
+ and dst_type in (bytes_type, bytearray_type, str_type, unicode_type)):
+ if (dst_type not in (bytes_type, bytearray_type)
+ and not env.directives['c_string_encoding']):
+ error(self.pos,
+ "default encoding required for conversion from '%s' to '%s'" %
+ (self.base.type, dst_type))
+ self.type = dst_type
if dst_type.is_array and self.base.type.is_array:
if not self.start and not self.stop:
# redundant slice building, copy C arrays directly
return self.base.coerce_to(dst_type, env)
# else: check array size if possible
- return super(SliceIndexNode, self).coerce_to(dst_type, env)
-
- def generate_result_code(self, code):
- if not self.type.is_pyobject:
- error(self.pos,
- "Slicing is not currently supported for '%s'." % self.type)
- return
-
- base_result = self.base.result()
- result = self.result()
- start_code = self.start_code()
- stop_code = self.stop_code()
- if self.base.type.is_string:
- base_result = self.base.result()
+ return super(SliceIndexNode, self).coerce_to(dst_type, env)
+
+ def generate_result_code(self, code):
+ if not self.type.is_pyobject:
+ error(self.pos,
+ "Slicing is not currently supported for '%s'." % self.type)
+ return
+
+ base_result = self.base.result()
+ result = self.result()
+ start_code = self.start_code()
+ stop_code = self.stop_code()
+ if self.base.type.is_string:
+ base_result = self.base.result()
if self.base.type not in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- base_result = '((const char*)%s)' % base_result
- if self.type is bytearray_type:
- type_name = 'ByteArray'
- else:
- type_name = self.type.name.title()
- if self.stop is None:
- code.putln(
- "%s = __Pyx_Py%s_FromString(%s + %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_Py%s_FromStringAndSize(%s + %s, %s - %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.base.type.is_pyunicode_ptr:
- base_result = self.base.result()
- if self.base.type != PyrexTypes.c_py_unicode_ptr_type:
- base_result = '((const Py_UNICODE*)%s)' % base_result
- if self.stop is None:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicode(%s + %s); %s" % (
- result,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicodeAndLength(%s + %s, %s - %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
-
- elif self.base.type is unicode_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyUnicode_Substring", "StringTools.c"))
- code.putln(
- "%s = __Pyx_PyUnicode_Substring(%s, %s, %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.type is py_object_type:
- code.globalstate.use_utility_code(self.get_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.putln(
- "%s = __Pyx_PyObject_GetSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % (
- result,
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound']),
- code.error_goto_if_null(result, self.pos)))
- else:
- if self.base.type is list_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyList_GetSlice'
- elif self.base.type is tuple_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyTuple_GetSlice'
- else:
- cfunc = 'PySequence_GetSlice'
- code.putln(
- "%s = %s(%s, %s, %s); %s" % (
- result,
- cfunc,
- self.base.py_result(),
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- code.put_gotref(self.py_result())
-
+ base_result = '((const char*)%s)' % base_result
+ if self.type is bytearray_type:
+ type_name = 'ByteArray'
+ else:
+ type_name = self.type.name.title()
+ if self.stop is None:
+ code.putln(
+ "%s = __Pyx_Py%s_FromString(%s + %s); %s" % (
+ result,
+ type_name,
+ base_result,
+ start_code,
+ code.error_goto_if_null(result, self.pos)))
+ else:
+ code.putln(
+ "%s = __Pyx_Py%s_FromStringAndSize(%s + %s, %s - %s); %s" % (
+ result,
+ type_name,
+ base_result,
+ start_code,
+ stop_code,
+ start_code,
+ code.error_goto_if_null(result, self.pos)))
+ elif self.base.type.is_pyunicode_ptr:
+ base_result = self.base.result()
+ if self.base.type != PyrexTypes.c_py_unicode_ptr_type:
+ base_result = '((const Py_UNICODE*)%s)' % base_result
+ if self.stop is None:
+ code.putln(
+ "%s = __Pyx_PyUnicode_FromUnicode(%s + %s); %s" % (
+ result,
+ base_result,
+ start_code,
+ code.error_goto_if_null(result, self.pos)))
+ else:
+ code.putln(
+ "%s = __Pyx_PyUnicode_FromUnicodeAndLength(%s + %s, %s - %s); %s" % (
+ result,
+ base_result,
+ start_code,
+ stop_code,
+ start_code,
+ code.error_goto_if_null(result, self.pos)))
+
+ elif self.base.type is unicode_type:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyUnicode_Substring", "StringTools.c"))
+ code.putln(
+ "%s = __Pyx_PyUnicode_Substring(%s, %s, %s); %s" % (
+ result,
+ base_result,
+ start_code,
+ stop_code,
+ code.error_goto_if_null(result, self.pos)))
+ elif self.type is py_object_type:
+ code.globalstate.use_utility_code(self.get_slice_utility_code)
+ (has_c_start, has_c_stop, c_start, c_stop,
+ py_start, py_stop, py_slice) = self.get_slice_config()
+ code.putln(
+ "%s = __Pyx_PyObject_GetSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % (
+ result,
+ self.base.py_result(),
+ c_start, c_stop,
+ py_start, py_stop, py_slice,
+ has_c_start, has_c_stop,
+ bool(code.globalstate.directives['wraparound']),
+ code.error_goto_if_null(result, self.pos)))
+ else:
+ if self.base.type is list_type:
+ code.globalstate.use_utility_code(
+ TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
+ cfunc = '__Pyx_PyList_GetSlice'
+ elif self.base.type is tuple_type:
+ code.globalstate.use_utility_code(
+ TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
+ cfunc = '__Pyx_PyTuple_GetSlice'
+ else:
+ cfunc = 'PySequence_GetSlice'
+ code.putln(
+ "%s = %s(%s, %s, %s); %s" % (
+ result,
+ cfunc,
+ self.base.py_result(),
+ start_code,
+ stop_code,
+ code.error_goto_if_null(result, self.pos)))
+ code.put_gotref(self.py_result())
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
- self.generate_subexpr_evaluation_code(code)
- if self.type.is_pyobject:
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- rhs.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- else:
+ self.generate_subexpr_evaluation_code(code)
+ if self.type.is_pyobject:
+ code.globalstate.use_utility_code(self.set_slice_utility_code)
+ (has_c_start, has_c_stop, c_start, c_stop,
+ py_start, py_stop, py_slice) = self.get_slice_config()
+ code.put_error_if_neg(self.pos,
+ "__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
+ self.base.py_result(),
+ rhs.py_result(),
+ c_start, c_stop,
+ py_start, py_stop, py_slice,
+ has_c_start, has_c_stop,
+ bool(code.globalstate.directives['wraparound'])))
+ else:
start_offset = self.start_code() if self.start else '0'
- if rhs.type.is_array:
- array_length = rhs.type.size
- self.generate_slice_guard_code(code, array_length)
- else:
+ if rhs.type.is_array:
+ array_length = rhs.type.size
+ self.generate_slice_guard_code(code, array_length)
+ else:
array_length = '%s - %s' % (self.stop_code(), start_offset)
code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
@@ -5089,79 +5089,79 @@ class SliceIndexNode(ExprNode):
self.base.result(), array_length
))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
-
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if not self.base.type.is_pyobject:
- error(self.pos,
- "Deleting slices is only supported for Python types, not '%s'." % self.type)
- return
- self.generate_subexpr_evaluation_code(code)
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_DelSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
-
- def get_slice_config(self):
- has_c_start, c_start, py_start = False, '0', 'NULL'
- if self.start:
- has_c_start = not self.start.type.is_pyobject
- if has_c_start:
- c_start = self.start.result()
- else:
- py_start = '&%s' % self.start.py_result()
- has_c_stop, c_stop, py_stop = False, '0', 'NULL'
- if self.stop:
- has_c_stop = not self.stop.type.is_pyobject
- if has_c_stop:
- c_stop = self.stop.result()
- else:
- py_stop = '&%s' % self.stop.py_result()
- py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
- return (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice)
-
- def generate_slice_guard_code(self, code, target_size):
- if not self.base.type.is_array:
- return
- slice_size = self.base.type.size
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
+
+ def generate_deletion_code(self, code, ignore_nonexisting=False):
+ if not self.base.type.is_pyobject:
+ error(self.pos,
+ "Deleting slices is only supported for Python types, not '%s'." % self.type)
+ return
+ self.generate_subexpr_evaluation_code(code)
+ code.globalstate.use_utility_code(self.set_slice_utility_code)
+ (has_c_start, has_c_stop, c_start, c_stop,
+ py_start, py_stop, py_slice) = self.get_slice_config()
+ code.put_error_if_neg(self.pos,
+ "__Pyx_PyObject_DelSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
+ self.base.py_result(),
+ c_start, c_stop,
+ py_start, py_stop, py_slice,
+ has_c_start, has_c_stop,
+ bool(code.globalstate.directives['wraparound'])))
+ self.generate_subexpr_disposal_code(code)
+ self.free_subexpr_temps(code)
+
+ def get_slice_config(self):
+ has_c_start, c_start, py_start = False, '0', 'NULL'
+ if self.start:
+ has_c_start = not self.start.type.is_pyobject
+ if has_c_start:
+ c_start = self.start.result()
+ else:
+ py_start = '&%s' % self.start.py_result()
+ has_c_stop, c_stop, py_stop = False, '0', 'NULL'
+ if self.stop:
+ has_c_stop = not self.stop.type.is_pyobject
+ if has_c_stop:
+ c_stop = self.stop.result()
+ else:
+ py_stop = '&%s' % self.stop.py_result()
+ py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
+ return (has_c_start, has_c_stop, c_start, c_stop,
+ py_start, py_stop, py_slice)
+
+ def generate_slice_guard_code(self, code, target_size):
+ if not self.base.type.is_array:
+ return
+ slice_size = self.base.type.size
try:
total_length = slice_size = int(slice_size)
except ValueError:
total_length = None
- start = stop = None
- if self.stop:
- stop = self.stop.result()
- try:
- stop = int(stop)
- if stop < 0:
+ start = stop = None
+ if self.stop:
+ stop = self.stop.result()
+ try:
+ stop = int(stop)
+ if stop < 0:
if total_length is None:
slice_size = '%s + %d' % (slice_size, stop)
else:
slice_size += stop
- else:
- slice_size = stop
- stop = None
- except ValueError:
- pass
-
- if self.start:
- start = self.start.result()
- try:
- start = int(start)
- if start < 0:
+ else:
+ slice_size = stop
+ stop = None
+ except ValueError:
+ pass
+
+ if self.start:
+ start = self.start.result()
+ try:
+ start = int(start)
+ if start < 0:
if total_length is None:
start = '%s + %d' % (self.base.type.size, start)
else:
@@ -5170,9 +5170,9 @@ class SliceIndexNode(ExprNode):
slice_size -= start
else:
slice_size = '%s - (%s)' % (slice_size, start)
- start = None
- except ValueError:
- pass
+ start = None
+ except ValueError:
+ pass
runtime_check = None
compile_time_check = False
@@ -5185,15 +5185,15 @@ class SliceIndexNode(ExprNode):
if compile_time_check and slice_size < 0:
if int_target_size > 0:
- error(self.pos, "Assignment to empty slice.")
+ error(self.pos, "Assignment to empty slice.")
elif compile_time_check and start is None and stop is None:
- # we know the exact slice length
+ # we know the exact slice length
if int_target_size != slice_size:
error(self.pos, "Assignment to slice of wrong length, expected %s, got %s" % (
slice_size, target_size))
- elif start is not None:
- if stop is None:
- stop = slice_size
+ elif start is not None:
+ if stop is None:
+ stop = slice_size
runtime_check = "(%s)-(%s)" % (stop, start)
elif stop is not None:
runtime_check = stop
@@ -5207,95 +5207,95 @@ class SliceIndexNode(ExprNode):
' expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d",'
' (Py_ssize_t)(%s), (Py_ssize_t)(%s));' % (
target_size, runtime_check))
- code.putln(code.error_goto(self.pos))
- code.putln("}")
-
- def start_code(self):
- if self.start:
- return self.start.result()
- else:
- return "0"
-
- def stop_code(self):
- if self.stop:
- return self.stop.result()
- elif self.base.type.is_array:
- return self.base.type.size
- else:
- return "PY_SSIZE_T_MAX"
-
- def calculate_result_code(self):
- # self.result() is not used, but this method must exist
- return "<unused>"
-
-
-class SliceNode(ExprNode):
- # start:stop:step in subscript list
- #
- # start ExprNode
- # stop ExprNode
- # step ExprNode
-
- subexprs = ['start', 'stop', 'step']
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+
+ def start_code(self):
+ if self.start:
+ return self.start.result()
+ else:
+ return "0"
+
+ def stop_code(self):
+ if self.stop:
+ return self.stop.result()
+ elif self.base.type.is_array:
+ return self.base.type.size
+ else:
+ return "PY_SSIZE_T_MAX"
+
+ def calculate_result_code(self):
+ # self.result() is not used, but this method must exist
+ return "<unused>"
+
+
+class SliceNode(ExprNode):
+ # start:stop:step in subscript list
+ #
+ # start ExprNode
+ # stop ExprNode
+ # step ExprNode
+
+ subexprs = ['start', 'stop', 'step']
is_slice = True
- type = slice_type
- is_temp = 1
-
- def calculate_constant_result(self):
- self.constant_result = slice(
- self.start.constant_result,
- self.stop.constant_result,
- self.step.constant_result)
-
- def compile_time_value(self, denv):
- start = self.start.compile_time_value(denv)
- stop = self.stop.compile_time_value(denv)
- step = self.step.compile_time_value(denv)
- try:
- return slice(start, stop, step)
+ type = slice_type
+ is_temp = 1
+
+ def calculate_constant_result(self):
+ self.constant_result = slice(
+ self.start.constant_result,
+ self.stop.constant_result,
+ self.step.constant_result)
+
+ def compile_time_value(self, denv):
+ start = self.start.compile_time_value(denv)
+ stop = self.stop.compile_time_value(denv)
+ step = self.step.compile_time_value(denv)
+ try:
+ return slice(start, stop, step)
except Exception as e:
- self.compile_time_value_error(e)
-
- def may_be_none(self):
- return False
-
- def analyse_types(self, env):
- start = self.start.analyse_types(env)
- stop = self.stop.analyse_types(env)
- step = self.step.analyse_types(env)
- self.start = start.coerce_to_pyobject(env)
- self.stop = stop.coerce_to_pyobject(env)
- self.step = step.coerce_to_pyobject(env)
- if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
- self.is_literal = True
- self.is_temp = False
- return self
-
- gil_message = "Constructing Python slice object"
-
- def calculate_result_code(self):
- return self.result_code
-
- def generate_result_code(self, code):
- if self.is_literal:
+ self.compile_time_value_error(e)
+
+ def may_be_none(self):
+ return False
+
+ def analyse_types(self, env):
+ start = self.start.analyse_types(env)
+ stop = self.stop.analyse_types(env)
+ step = self.step.analyse_types(env)
+ self.start = start.coerce_to_pyobject(env)
+ self.stop = stop.coerce_to_pyobject(env)
+ self.step = step.coerce_to_pyobject(env)
+ if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
+ self.is_literal = True
+ self.is_temp = False
+ return self
+
+ gil_message = "Constructing Python slice object"
+
+ def calculate_result_code(self):
+ return self.result_code
+
+ def generate_result_code(self, code):
+ if self.is_literal:
dedup_key = make_dedup_key(self.type, (self,))
self.result_code = code.get_py_const(py_object_type, 'slice', cleanup_level=2, dedup_key=dedup_key)
code = code.get_cached_constants_writer(self.result_code)
if code is None:
return # already initialised
- code.mark_pos(self.pos)
-
- code.putln(
- "%s = PySlice_New(%s, %s, %s); %s" % (
- self.result(),
- self.start.py_result(),
- self.stop.py_result(),
- self.step.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- if self.is_literal:
- code.put_giveref(self.py_result())
-
+ code.mark_pos(self.pos)
+
+ code.putln(
+ "%s = PySlice_New(%s, %s, %s); %s" % (
+ self.result(),
+ self.start.py_result(),
+ self.stop.py_result(),
+ self.step.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ if self.is_literal:
+ code.put_giveref(self.py_result())
+
class SliceIntNode(SliceNode):
# start:stop:step in subscript list
# This is just a node to hold start,stop and step nodes that can be
@@ -5304,7 +5304,7 @@ class SliceIntNode(SliceNode):
# start ExprNode
# stop ExprNode
# step ExprNode
-
+
is_temp = 0
def calculate_constant_result(self):
@@ -5351,26 +5351,26 @@ class SliceIntNode(SliceNode):
a.arg.result()
-class CallNode(ExprNode):
-
- # allow overriding the default 'may_be_none' behaviour
- may_return_none = None
-
- def infer_type(self, env):
+class CallNode(ExprNode):
+
+ # allow overriding the default 'may_be_none' behaviour
+ may_return_none = None
+
+ def infer_type(self, env):
# TODO(robertwb): Reduce redundancy with analyse_types.
- function = self.function
- func_type = function.infer_type(env)
- if isinstance(function, NewExprNode):
- # note: needs call to infer_type() above
- return PyrexTypes.CPtrType(function.class_type)
- if func_type is py_object_type:
- # function might have lied for safety => try to find better type
- entry = getattr(function, 'entry', None)
- if entry is not None:
- func_type = entry.type or func_type
- if func_type.is_ptr:
- func_type = func_type.base_type
- if func_type.is_cfunction:
+ function = self.function
+ func_type = function.infer_type(env)
+ if isinstance(function, NewExprNode):
+ # note: needs call to infer_type() above
+ return PyrexTypes.CPtrType(function.class_type)
+ if func_type is py_object_type:
+ # function might have lied for safety => try to find better type
+ entry = getattr(function, 'entry', None)
+ if entry is not None:
+ func_type = entry.type or func_type
+ if func_type.is_ptr:
+ func_type = func_type.base_type
+ if func_type.is_cfunction:
if getattr(self.function, 'entry', None) and hasattr(self, 'args'):
alternatives = self.function.entry.all_alternatives()
arg_types = [arg.infer_type(env) for arg in self.args]
@@ -5380,44 +5380,44 @@ class CallNode(ExprNode):
if func_type.is_ptr:
func_type = func_type.base_type
return func_type.return_type
- return func_type.return_type
- elif func_type is type_type:
- if function.is_name and function.entry and function.entry.type:
- result_type = function.entry.type
- if result_type.is_extension_type:
- return result_type
- elif result_type.is_builtin_type:
- if function.entry.name == 'float':
- return PyrexTypes.c_double_type
- elif function.entry.name in Builtin.types_that_construct_their_instance:
- return result_type
- return py_object_type
-
- def type_dependencies(self, env):
- # TODO: Update when Danilo's C++ code merged in to handle the
- # the case of function overloading.
- return self.function.type_dependencies(env)
-
- def is_simple(self):
- # C function calls could be considered simple, but they may
- # have side-effects that may hit when multiple operations must
- # be effected in order, e.g. when constructing the argument
- # sequence for a function call or comparing values.
- return False
-
- def may_be_none(self):
- if self.may_return_none is not None:
- return self.may_return_none
- func_type = self.function.type
- if func_type is type_type and self.function.is_name:
- entry = self.function.entry
- if entry.type.is_extension_type:
- return False
- if (entry.type.is_builtin_type and
- entry.name in Builtin.types_that_construct_their_instance):
- return False
- return ExprNode.may_be_none(self)
-
+ return func_type.return_type
+ elif func_type is type_type:
+ if function.is_name and function.entry and function.entry.type:
+ result_type = function.entry.type
+ if result_type.is_extension_type:
+ return result_type
+ elif result_type.is_builtin_type:
+ if function.entry.name == 'float':
+ return PyrexTypes.c_double_type
+ elif function.entry.name in Builtin.types_that_construct_their_instance:
+ return result_type
+ return py_object_type
+
+ def type_dependencies(self, env):
+ # TODO: Update when Danilo's C++ code merged in to handle the
+ # the case of function overloading.
+ return self.function.type_dependencies(env)
+
+ def is_simple(self):
+ # C function calls could be considered simple, but they may
+ # have side-effects that may hit when multiple operations must
+ # be effected in order, e.g. when constructing the argument
+ # sequence for a function call or comparing values.
+ return False
+
+ def may_be_none(self):
+ if self.may_return_none is not None:
+ return self.may_return_none
+ func_type = self.function.type
+ if func_type is type_type and self.function.is_name:
+ entry = self.function.entry
+ if entry.type.is_extension_type:
+ return False
+ if (entry.type.is_builtin_type and
+ entry.name in Builtin.types_that_construct_their_instance):
+ return False
+ return ExprNode.may_be_none(self)
+
def set_py_result_type(self, function, func_type=None):
if func_type is None:
func_type = function.type
@@ -5444,120 +5444,120 @@ class CallNode(ExprNode):
else:
self.type = py_object_type
- def analyse_as_type_constructor(self, env):
- type = self.function.analyse_as_type(env)
- if type and type.is_struct_or_union:
- args, kwds = self.explicit_args_kwds()
- items = []
- for arg, member in zip(args, type.scope.var_entries):
- items.append(DictItemNode(pos=arg.pos, key=StringNode(pos=arg.pos, value=member.name), value=arg))
- if kwds:
- items += kwds.key_value_pairs
- self.key_value_pairs = items
- self.__class__ = DictNode
- self.analyse_types(env) # FIXME
- self.coerce_to(type, env)
- return True
- elif type and type.is_cpp_class:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- constructor = type.scope.lookup("<init>")
+ def analyse_as_type_constructor(self, env):
+ type = self.function.analyse_as_type(env)
+ if type and type.is_struct_or_union:
+ args, kwds = self.explicit_args_kwds()
+ items = []
+ for arg, member in zip(args, type.scope.var_entries):
+ items.append(DictItemNode(pos=arg.pos, key=StringNode(pos=arg.pos, value=member.name), value=arg))
+ if kwds:
+ items += kwds.key_value_pairs
+ self.key_value_pairs = items
+ self.__class__ = DictNode
+ self.analyse_types(env) # FIXME
+ self.coerce_to(type, env)
+ return True
+ elif type and type.is_cpp_class:
+ self.args = [ arg.analyse_types(env) for arg in self.args ]
+ constructor = type.scope.lookup("<init>")
if not constructor:
error(self.function.pos, "no constructor found for C++ type '%s'" % self.function.name)
self.type = error_type
return self
- self.function = RawCNameExprNode(self.function.pos, constructor.type)
- self.function.entry = constructor
+ self.function = RawCNameExprNode(self.function.pos, constructor.type)
+ self.function.entry = constructor
self.function.set_cname(type.empty_declaration_code())
- self.analyse_c_function_call(env)
- self.type = type
- return True
-
- def is_lvalue(self):
- return self.type.is_reference
-
- def nogil_check(self, env):
- func_type = self.function_type()
- if func_type.is_pyobject:
- self.gil_error()
+ self.analyse_c_function_call(env)
+ self.type = type
+ return True
+
+ def is_lvalue(self):
+ return self.type.is_reference
+
+ def nogil_check(self, env):
+ func_type = self.function_type()
+ if func_type.is_pyobject:
+ self.gil_error()
elif not func_type.is_error and not getattr(func_type, 'nogil', False):
- self.gil_error()
-
- gil_message = "Calling gil-requiring function"
-
-
-class SimpleCallNode(CallNode):
- # Function call without keyword, * or ** args.
- #
- # function ExprNode
- # args [ExprNode]
- # arg_tuple ExprNode or None used internally
- # self ExprNode or None used internally
- # coerced_self ExprNode or None used internally
- # wrapper_call bool used internally
- # has_optional_args bool used internally
- # nogil bool used internally
-
- subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
-
- self = None
- coerced_self = None
- arg_tuple = None
- wrapper_call = False
- has_optional_args = False
- nogil = False
- analysed = False
+ self.gil_error()
+
+ gil_message = "Calling gil-requiring function"
+
+
+class SimpleCallNode(CallNode):
+ # Function call without keyword, * or ** args.
+ #
+ # function ExprNode
+ # args [ExprNode]
+ # arg_tuple ExprNode or None used internally
+ # self ExprNode or None used internally
+ # coerced_self ExprNode or None used internally
+ # wrapper_call bool used internally
+ # has_optional_args bool used internally
+ # nogil bool used internally
+
+ subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
+
+ self = None
+ coerced_self = None
+ arg_tuple = None
+ wrapper_call = False
+ has_optional_args = False
+ nogil = False
+ analysed = False
overflowcheck = False
-
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- args = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return function(*args)
+
+ def compile_time_value(self, denv):
+ function = self.function.compile_time_value(denv)
+ args = [arg.compile_time_value(denv) for arg in self.args]
+ try:
+ return function(*args)
except Exception as e:
- self.compile_time_value_error(e)
-
- def analyse_as_type(self, env):
- attr = self.function.as_cython_attribute()
- if attr == 'pointer':
- if len(self.args) != 1:
- error(self.args.pos, "only one type allowed.")
- else:
- type = self.args[0].analyse_as_type(env)
- if not type:
- error(self.args[0].pos, "Unknown type")
- else:
- return PyrexTypes.CPtrType(type)
+ self.compile_time_value_error(e)
+
+ def analyse_as_type(self, env):
+ attr = self.function.as_cython_attribute()
+ if attr == 'pointer':
+ if len(self.args) != 1:
+ error(self.args.pos, "only one type allowed.")
+ else:
+ type = self.args[0].analyse_as_type(env)
+ if not type:
+ error(self.args[0].pos, "Unknown type")
+ else:
+ return PyrexTypes.CPtrType(type)
elif attr == 'typeof':
if len(self.args) != 1:
error(self.args.pos, "only one type allowed.")
operand = self.args[0].analyse_types(env)
return operand.type
-
- def explicit_args_kwds(self):
- return self.args, None
-
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- if self.analysed:
- return self
- self.analysed = True
- self.function.is_called = 1
- self.function = self.function.analyse_types(env)
- function = self.function
-
- if function.is_attribute and function.entry and function.entry.is_cmethod:
- # Take ownership of the object from which the attribute
- # was obtained, because we need to pass it as 'self'.
- self.self = function.obj
- function.obj = CloneNode(self.self)
-
- func_type = self.function_type()
+
+ def explicit_args_kwds(self):
+ return self.args, None
+
+ def analyse_types(self, env):
+ if self.analyse_as_type_constructor(env):
+ return self
+ if self.analysed:
+ return self
+ self.analysed = True
+ self.function.is_called = 1
+ self.function = self.function.analyse_types(env)
+ function = self.function
+
+ if function.is_attribute and function.entry and function.entry.is_cmethod:
+ # Take ownership of the object from which the attribute
+ # was obtained, because we need to pass it as 'self'.
+ self.self = function.obj
+ function.obj = CloneNode(self.self)
+
+ func_type = self.function_type()
self.is_numpy_call_with_exprs = False
if (has_np_pythran(env) and function.is_numpy_attribute and
pythran_is_numpy_func_supported(function)):
has_pythran_args = True
- self.arg_tuple = TupleNode(self.pos, args = self.args)
+ self.arg_tuple = TupleNode(self.pos, args = self.args)
self.arg_tuple = self.arg_tuple.analyse_types(env)
for arg in self.arg_tuple.args:
has_pythran_args &= is_pythran_supported_node_or_none(arg)
@@ -5573,269 +5573,269 @@ class SimpleCallNode(CallNode):
elif func_type.is_pyobject:
self.arg_tuple = TupleNode(self.pos, args = self.args)
self.arg_tuple = self.arg_tuple.analyse_types(env).coerce_to_pyobject(env)
- self.args = None
+ self.args = None
self.set_py_result_type(function, func_type)
- self.is_temp = 1
- else:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- self.analyse_c_function_call(env)
+ self.is_temp = 1
+ else:
+ self.args = [ arg.analyse_types(env) for arg in self.args ]
+ self.analyse_c_function_call(env)
if func_type.exception_check == '+':
self.is_temp = True
- return self
-
- def function_type(self):
- # Return the type of the function being called, coercing a function
- # pointer to a function if necessary. If the function has fused
- # arguments, return the specific type.
- func_type = self.function.type
-
- if func_type.is_ptr:
- func_type = func_type.base_type
-
- return func_type
-
- def analyse_c_function_call(self, env):
- func_type = self.function.type
- if func_type is error_type:
- self.type = error_type
- return
-
- if func_type.is_cfunction and func_type.is_static_method:
- if self.self and self.self.type.is_extension_type:
- # To support this we'd need to pass self to determine whether
- # it was overloaded in Python space (possibly via a Cython
- # superclass turning a cdef method into a cpdef one).
- error(self.pos, "Cannot call a static method on an instance variable.")
- args = self.args
- elif self.self:
- args = [self.self] + self.args
- else:
- args = self.args
-
- if func_type.is_cpp_class:
- overloaded_entry = self.function.type.scope.lookup("operator()")
- if overloaded_entry is None:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- elif hasattr(self.function, 'entry'):
- overloaded_entry = self.function.entry
+ return self
+
+ def function_type(self):
+ # Return the type of the function being called, coercing a function
+ # pointer to a function if necessary. If the function has fused
+ # arguments, return the specific type.
+ func_type = self.function.type
+
+ if func_type.is_ptr:
+ func_type = func_type.base_type
+
+ return func_type
+
+ def analyse_c_function_call(self, env):
+ func_type = self.function.type
+ if func_type is error_type:
+ self.type = error_type
+ return
+
+ if func_type.is_cfunction and func_type.is_static_method:
+ if self.self and self.self.type.is_extension_type:
+ # To support this we'd need to pass self to determine whether
+ # it was overloaded in Python space (possibly via a Cython
+ # superclass turning a cdef method into a cpdef one).
+ error(self.pos, "Cannot call a static method on an instance variable.")
+ args = self.args
+ elif self.self:
+ args = [self.self] + self.args
+ else:
+ args = self.args
+
+ if func_type.is_cpp_class:
+ overloaded_entry = self.function.type.scope.lookup("operator()")
+ if overloaded_entry is None:
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
+ return
+ elif hasattr(self.function, 'entry'):
+ overloaded_entry = self.function.entry
elif self.function.is_subscript and self.function.is_fused_index:
- overloaded_entry = self.function.type.entry
- else:
- overloaded_entry = None
-
- if overloaded_entry:
- if self.function.type.is_fused:
- functypes = self.function.type.get_all_specialized_function_types()
- alternatives = [f.entry for f in functypes]
- else:
- alternatives = overloaded_entry.all_alternatives()
-
+ overloaded_entry = self.function.type.entry
+ else:
+ overloaded_entry = None
+
+ if overloaded_entry:
+ if self.function.type.is_fused:
+ functypes = self.function.type.get_all_specialized_function_types()
+ alternatives = [f.entry for f in functypes]
+ else:
+ alternatives = overloaded_entry.all_alternatives()
+
entry = PyrexTypes.best_match(
[arg.type for arg in args], alternatives, self.pos, env, args)
-
- if not entry:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
-
- entry.used = True
+
+ if not entry:
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
+ return
+
+ entry.used = True
if not func_type.is_cpp_class:
self.function.entry = entry
- self.function.type = entry.type
- func_type = self.function_type()
- else:
- entry = None
- func_type = self.function_type()
- if not func_type.is_cfunction:
- error(self.pos, "Calling non-function type '%s'" % func_type)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
-
- # Check no. of args
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(args)
- if func_type.optional_arg_count and expected_nargs != actual_nargs:
- self.has_optional_args = 1
- self.is_temp = 1
-
- # check 'self' argument
- if entry and entry.is_cmethod and func_type.args and not func_type.is_static_method:
- formal_arg = func_type.args[0]
- arg = args[0]
- if formal_arg.not_none:
- if self.self:
- self.self = self.self.as_none_safe_node(
+ self.function.type = entry.type
+ func_type = self.function_type()
+ else:
+ entry = None
+ func_type = self.function_type()
+ if not func_type.is_cfunction:
+ error(self.pos, "Calling non-function type '%s'" % func_type)
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
+ return
+
+ # Check no. of args
+ max_nargs = len(func_type.args)
+ expected_nargs = max_nargs - func_type.optional_arg_count
+ actual_nargs = len(args)
+ if func_type.optional_arg_count and expected_nargs != actual_nargs:
+ self.has_optional_args = 1
+ self.is_temp = 1
+
+ # check 'self' argument
+ if entry and entry.is_cmethod and func_type.args and not func_type.is_static_method:
+ formal_arg = func_type.args[0]
+ arg = args[0]
+ if formal_arg.not_none:
+ if self.self:
+ self.self = self.self.as_none_safe_node(
"'NoneType' object has no attribute '%{0}s'".format('.30' if len(entry.name) <= 30 else ''),
- error='PyExc_AttributeError',
- format_args=[entry.name])
- else:
- # unbound method
- arg = arg.as_none_safe_node(
- "descriptor '%s' requires a '%s' object but received a 'NoneType'",
- format_args=[entry.name, formal_arg.type.name])
- if self.self:
- if formal_arg.accept_builtin_subtypes:
- arg = CMethodSelfCloneNode(self.self)
- else:
- arg = CloneNode(self.self)
- arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
- elif formal_arg.type.is_builtin_type:
- # special case: unbound methods of builtins accept subtypes
- arg = arg.coerce_to(formal_arg.type, env)
- if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
- arg.exact_builtin_type = False
- args[0] = arg
-
- # Coerce arguments
- some_args_in_temps = False
+ error='PyExc_AttributeError',
+ format_args=[entry.name])
+ else:
+ # unbound method
+ arg = arg.as_none_safe_node(
+ "descriptor '%s' requires a '%s' object but received a 'NoneType'",
+ format_args=[entry.name, formal_arg.type.name])
+ if self.self:
+ if formal_arg.accept_builtin_subtypes:
+ arg = CMethodSelfCloneNode(self.self)
+ else:
+ arg = CloneNode(self.self)
+ arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
+ elif formal_arg.type.is_builtin_type:
+ # special case: unbound methods of builtins accept subtypes
+ arg = arg.coerce_to(formal_arg.type, env)
+ if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
+ arg.exact_builtin_type = False
+ args[0] = arg
+
+ # Coerce arguments
+ some_args_in_temps = False
for i in range(min(max_nargs, actual_nargs)):
- formal_arg = func_type.args[i]
- formal_type = formal_arg.type
- arg = args[i].coerce_to(formal_type, env)
- if formal_arg.not_none:
- # C methods must do the None checks at *call* time
- arg = arg.as_none_safe_node(
- "cannot pass None into a C function argument that is declared 'not None'")
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if i == 0 and self.self is not None:
- # a method's cloned "self" argument is ok
- pass
- elif arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- args[i] = arg
-
- # handle additional varargs parameters
+ formal_arg = func_type.args[i]
+ formal_type = formal_arg.type
+ arg = args[i].coerce_to(formal_type, env)
+ if formal_arg.not_none:
+ # C methods must do the None checks at *call* time
+ arg = arg.as_none_safe_node(
+ "cannot pass None into a C function argument that is declared 'not None'")
+ if arg.is_temp:
+ if i > 0:
+ # first argument in temp doesn't impact subsequent arguments
+ some_args_in_temps = True
+ elif arg.type.is_pyobject and not env.nogil:
+ if i == 0 and self.self is not None:
+ # a method's cloned "self" argument is ok
+ pass
+ elif arg.nonlocally_immutable():
+ # plain local variables are ok
+ pass
+ else:
+ # we do not safely own the argument's reference,
+ # but we must make sure it cannot be collected
+ # before we return from the function, so we create
+ # an owned temp reference to it
+ if i > 0: # first argument doesn't matter
+ some_args_in_temps = True
+ arg = arg.coerce_to_temp(env)
+ args[i] = arg
+
+ # handle additional varargs parameters
for i in range(max_nargs, actual_nargs):
- arg = args[i]
- if arg.type.is_pyobject:
+ arg = args[i]
+ if arg.type.is_pyobject:
if arg.type is str_type:
arg_ctype = PyrexTypes.c_char_ptr_type
else:
arg_ctype = arg.type.default_coerced_ctype()
- if arg_ctype is None:
- error(self.args[i].pos,
- "Python object cannot be passed as a varargs parameter")
- else:
- args[i] = arg = arg.coerce_to(arg_ctype, env)
- if arg.is_temp and i > 0:
- some_args_in_temps = True
-
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
+ if arg_ctype is None:
+ error(self.args[i].pos,
+ "Python object cannot be passed as a varargs parameter")
+ else:
+ args[i] = arg = arg.coerce_to(arg_ctype, env)
+ if arg.is_temp and i > 0:
+ some_args_in_temps = True
+
+ if some_args_in_temps:
+ # if some args are temps and others are not, they may get
+ # constructed in the wrong order (temps first) => make
+ # sure they are either all temps or all not temps (except
+ # for the last argument, which is evaluated last in any
+ # case)
for i in range(actual_nargs-1):
- if i == 0 and self.self is not None:
- continue # self is ok
- arg = args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0 or i == 1 and self.self is not None: # skip first arg
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
-
- self.args[:] = args
-
- # Calc result type and code fragment
- if isinstance(self.function, NewExprNode):
- self.type = PyrexTypes.CPtrType(self.function.class_type)
- else:
- self.type = func_type.return_type
-
- if self.function.is_name or self.function.is_attribute:
+ if i == 0 and self.self is not None:
+ continue # self is ok
+ arg = args[i]
+ if arg.nonlocally_immutable():
+ # locals, C functions, unassignable types are safe.
+ pass
+ elif arg.type.is_cpp_class:
+ # Assignment has side effects, avoid.
+ pass
+ elif env.nogil and arg.type.is_pyobject:
+ # can't copy a Python reference into a temp in nogil
+ # env (this is safe: a construction would fail in
+ # nogil anyway)
+ pass
+ else:
+ #self.args[i] = arg.coerce_to_temp(env)
+ # instead: issue a warning
+ if i > 0 or i == 1 and self.self is not None: # skip first arg
+ warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
+ break
+
+ self.args[:] = args
+
+ # Calc result type and code fragment
+ if isinstance(self.function, NewExprNode):
+ self.type = PyrexTypes.CPtrType(self.function.class_type)
+ else:
+ self.type = func_type.return_type
+
+ if self.function.is_name or self.function.is_attribute:
func_entry = self.function.entry
if func_entry and (func_entry.utility_code or func_entry.utility_code_definition):
self.is_temp = 1 # currently doesn't work for self.calculate_result_code()
-
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
+
+ if self.type.is_pyobject:
+ self.result_ctype = py_object_type
+ self.is_temp = 1
elif func_type.exception_value is not None or func_type.exception_check:
- self.is_temp = 1
- elif self.type.is_memoryviewslice:
- self.is_temp = 1
- # func_type.exception_check = True
-
+ self.is_temp = 1
+ elif self.type.is_memoryviewslice:
+ self.is_temp = 1
+ # func_type.exception_check = True
+
if self.is_temp and self.type.is_reference:
self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- # Called in 'nogil' context?
- self.nogil = env.nogil
- if (self.nogil and
- func_type.exception_check and
- func_type.exception_check != '+'):
- env.use_utility_code(pyerr_occurred_withgil_utility_code)
- # C++ exception handler
- if func_type.exception_check == '+':
- if func_type.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
-
+ # Called in 'nogil' context?
+ self.nogil = env.nogil
+ if (self.nogil and
+ func_type.exception_check and
+ func_type.exception_check != '+'):
+ env.use_utility_code(pyerr_occurred_withgil_utility_code)
+ # C++ exception handler
+ if func_type.exception_check == '+':
+ if func_type.exception_value is None:
+ env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
+
self.overflowcheck = env.directives['overflowcheck']
- def calculate_result_code(self):
- return self.c_call_code()
-
- def c_call_code(self):
- func_type = self.function_type()
- if self.type is PyrexTypes.error_type or not func_type.is_cfunction:
- return "<error>"
- formal_args = func_type.args
- arg_list_code = []
- args = list(zip(formal_args, self.args))
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(self.args)
- for formal_arg, actual_arg in args[:expected_nargs]:
- arg_code = actual_arg.result_as(formal_arg.type)
- arg_list_code.append(arg_code)
-
- if func_type.is_overridable:
- arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
-
- if func_type.optional_arg_count:
- if expected_nargs == actual_nargs:
- optional_args = 'NULL'
- else:
- optional_args = "&%s" % self.opt_arg_struct
- arg_list_code.append(optional_args)
-
- for actual_arg in self.args[len(formal_args):]:
- arg_list_code.append(actual_arg.result())
-
- result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code))
- return result
-
+ def calculate_result_code(self):
+ return self.c_call_code()
+
+ def c_call_code(self):
+ func_type = self.function_type()
+ if self.type is PyrexTypes.error_type or not func_type.is_cfunction:
+ return "<error>"
+ formal_args = func_type.args
+ arg_list_code = []
+ args = list(zip(formal_args, self.args))
+ max_nargs = len(func_type.args)
+ expected_nargs = max_nargs - func_type.optional_arg_count
+ actual_nargs = len(self.args)
+ for formal_arg, actual_arg in args[:expected_nargs]:
+ arg_code = actual_arg.result_as(formal_arg.type)
+ arg_list_code.append(arg_code)
+
+ if func_type.is_overridable:
+ arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
+
+ if func_type.optional_arg_count:
+ if expected_nargs == actual_nargs:
+ optional_args = 'NULL'
+ else:
+ optional_args = "&%s" % self.opt_arg_struct
+ arg_list_code.append(optional_args)
+
+ for actual_arg in self.args[len(formal_args):]:
+ arg_list_code.append(actual_arg.result())
+
+ result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code))
+ return result
+
def is_c_result_required(self):
func_type = self.function_type()
if not func_type.exception_value or func_type.exception_check == '+':
@@ -5899,9 +5899,9 @@ class SimpleCallNode(CallNode):
subexpr.generate_disposal_code(code)
subexpr.free_temps(code)
- def generate_result_code(self, code):
- func_type = self.function_type()
- if func_type.is_pyobject:
+ def generate_result_code(self, code):
+ func_type = self.function_type()
+ if func_type.is_pyobject:
arg_code = self.arg_tuple.py_result()
code.globalstate.use_utility_code(UtilityCode.load_cached(
"PyObjectCall", "ObjectHandling.c"))
@@ -5911,66 +5911,66 @@ class SimpleCallNode(CallNode):
self.function.py_result(),
arg_code,
code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif func_type.is_cfunction:
- if self.has_optional_args:
- actual_nargs = len(self.args)
- expected_nargs = len(func_type.args) - func_type.optional_arg_count
- self.opt_arg_struct = code.funcstate.allocate_temp(
- func_type.op_arg_struct.base_type, manage_ref=True)
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- Naming.pyrex_prefix + "n",
- len(self.args) - expected_nargs))
- args = list(zip(func_type.args, self.args))
- for formal_arg, actual_arg in args[expected_nargs:actual_nargs]:
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- func_type.opt_arg_cname(formal_arg.name),
- actual_arg.result_as(formal_arg.type)))
- exc_checks = []
- if self.type.is_pyobject and self.is_temp:
- exc_checks.append("!%s" % self.result())
- elif self.type.is_memoryviewslice:
- assert self.is_temp
- exc_checks.append(self.type.error_condition(self.result()))
+ code.put_gotref(self.py_result())
+ elif func_type.is_cfunction:
+ if self.has_optional_args:
+ actual_nargs = len(self.args)
+ expected_nargs = len(func_type.args) - func_type.optional_arg_count
+ self.opt_arg_struct = code.funcstate.allocate_temp(
+ func_type.op_arg_struct.base_type, manage_ref=True)
+ code.putln("%s.%s = %s;" % (
+ self.opt_arg_struct,
+ Naming.pyrex_prefix + "n",
+ len(self.args) - expected_nargs))
+ args = list(zip(func_type.args, self.args))
+ for formal_arg, actual_arg in args[expected_nargs:actual_nargs]:
+ code.putln("%s.%s = %s;" % (
+ self.opt_arg_struct,
+ func_type.opt_arg_cname(formal_arg.name),
+ actual_arg.result_as(formal_arg.type)))
+ exc_checks = []
+ if self.type.is_pyobject and self.is_temp:
+ exc_checks.append("!%s" % self.result())
+ elif self.type.is_memoryviewslice:
+ assert self.is_temp
+ exc_checks.append(self.type.error_condition(self.result()))
elif func_type.exception_check != '+':
- exc_val = func_type.exception_value
- exc_check = func_type.exception_check
- if exc_val is not None:
+ exc_val = func_type.exception_value
+ exc_check = func_type.exception_check
+ if exc_val is not None:
exc_checks.append("%s == %s" % (self.result(), func_type.return_type.cast_code(exc_val)))
- if exc_check:
- if self.nogil:
- exc_checks.append("__Pyx_ErrOccurredWithGIL()")
- else:
- exc_checks.append("PyErr_Occurred()")
- if self.is_temp or exc_checks:
- rhs = self.c_call_code()
- if self.result():
- lhs = "%s = " % self.result()
- if self.is_temp and self.type.is_pyobject:
- #return_type = self.type # func_type.return_type
- #print "SimpleCallNode.generate_result_code: casting", rhs, \
- # "from", return_type, "to pyobject" ###
- rhs = typecast(py_object_type, self.type, rhs)
- else:
- lhs = ""
- if func_type.exception_check == '+':
+ if exc_check:
+ if self.nogil:
+ exc_checks.append("__Pyx_ErrOccurredWithGIL()")
+ else:
+ exc_checks.append("PyErr_Occurred()")
+ if self.is_temp or exc_checks:
+ rhs = self.c_call_code()
+ if self.result():
+ lhs = "%s = " % self.result()
+ if self.is_temp and self.type.is_pyobject:
+ #return_type = self.type # func_type.return_type
+ #print "SimpleCallNode.generate_result_code: casting", rhs, \
+ # "from", return_type, "to pyobject" ###
+ rhs = typecast(py_object_type, self.type, rhs)
+ else:
+ lhs = ""
+ if func_type.exception_check == '+':
translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
self.result() if self.type.is_pyobject else None,
func_type.exception_value, self.nogil)
- else:
+ else:
if exc_checks:
- goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
- else:
- goto_error = ""
- code.putln("%s%s; %s" % (lhs, rhs, goto_error))
- if self.type.is_pyobject and self.result():
- code.put_gotref(self.py_result())
- if self.has_optional_args:
- code.funcstate.release_temp(self.opt_arg_struct)
-
-
+ goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
+ else:
+ goto_error = ""
+ code.putln("%s%s; %s" % (lhs, rhs, goto_error))
+ if self.type.is_pyobject and self.result():
+ code.put_gotref(self.py_result())
+ if self.has_optional_args:
+ code.funcstate.release_temp(self.opt_arg_struct)
+
+
class NumPyMethodCallNode(ExprNode):
# Pythran call to a NumPy function or method.
#
@@ -5999,103 +5999,103 @@ class NumPyMethodCallNode(ExprNode):
", ".join(a.pythran_result() for a in args)))
-class PyMethodCallNode(SimpleCallNode):
- # Specialised call to a (potential) PyMethodObject with non-constant argument tuple.
- # Allows the self argument to be injected directly instead of repacking a tuple for it.
- #
- # function ExprNode the function/method object to call
- # arg_tuple TupleNode the arguments for the args tuple
-
- subexprs = ['function', 'arg_tuple']
- is_temp = True
-
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
-
- self.function.generate_evaluation_code(code)
- assert self.arg_tuple.mult_factor is None
- args = self.arg_tuple.args
- for arg in args:
- arg.generate_evaluation_code(code)
-
- # make sure function is in temp so that we can replace the reference below if it's a method
- reuse_function_temp = self.function.is_temp
- if reuse_function_temp:
- function = self.function.result()
- else:
- function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- self.function.make_owned_reference(code)
- code.put("%s = %s; " % (function, self.function.py_result()))
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
-
- self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = NULL;" % self_arg)
- arg_offset_cname = None
- if len(args) > 1:
+class PyMethodCallNode(SimpleCallNode):
+ # Specialised call to a (potential) PyMethodObject with non-constant argument tuple.
+ # Allows the self argument to be injected directly instead of repacking a tuple for it.
+ #
+ # function ExprNode the function/method object to call
+ # arg_tuple TupleNode the arguments for the args tuple
+
+ subexprs = ['function', 'arg_tuple']
+ is_temp = True
+
+ def generate_evaluation_code(self, code):
+ code.mark_pos(self.pos)
+ self.allocate_temp_result(code)
+
+ self.function.generate_evaluation_code(code)
+ assert self.arg_tuple.mult_factor is None
+ args = self.arg_tuple.args
+ for arg in args:
+ arg.generate_evaluation_code(code)
+
+ # make sure function is in temp so that we can replace the reference below if it's a method
+ reuse_function_temp = self.function.is_temp
+ if reuse_function_temp:
+ function = self.function.result()
+ else:
+ function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
+ self.function.make_owned_reference(code)
+ code.put("%s = %s; " % (function, self.function.py_result()))
+ self.function.generate_disposal_code(code)
+ self.function.free_temps(code)
+
+ self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
+ code.putln("%s = NULL;" % self_arg)
+ arg_offset_cname = None
+ if len(args) > 1:
arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % arg_offset_cname)
-
- def attribute_is_likely_method(attr):
- obj = attr.obj
- if obj.is_name and obj.entry.is_pyglobal:
- return False # more likely to be a function
- return True
-
- if self.function.is_attribute:
- likely_method = 'likely' if attribute_is_likely_method(self.function) else 'unlikely'
- elif self.function.is_name and self.function.cf_state:
- # not an attribute itself, but might have been assigned from one (e.g. bound method)
- for assignment in self.function.cf_state:
- value = assignment.rhs
+ code.putln("%s = 0;" % arg_offset_cname)
+
+ def attribute_is_likely_method(attr):
+ obj = attr.obj
+ if obj.is_name and obj.entry.is_pyglobal:
+ return False # more likely to be a function
+ return True
+
+ if self.function.is_attribute:
+ likely_method = 'likely' if attribute_is_likely_method(self.function) else 'unlikely'
+ elif self.function.is_name and self.function.cf_state:
+ # not an attribute itself, but might have been assigned from one (e.g. bound method)
+ for assignment in self.function.cf_state:
+ value = assignment.rhs
if value and value.is_attribute and value.obj.type and value.obj.type.is_pyobject:
- if attribute_is_likely_method(value):
- likely_method = 'likely'
- break
- else:
- likely_method = 'unlikely'
- else:
- likely_method = 'unlikely'
-
+ if attribute_is_likely_method(value):
+ likely_method = 'likely'
+ break
+ else:
+ likely_method = 'unlikely'
+ else:
+ likely_method = 'unlikely'
+
code.putln("if (CYTHON_UNPACK_METHODS && %s(PyMethod_Check(%s))) {" % (likely_method, function))
- code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
- # the following is always true in Py3 (kept only for safety),
- # but is false for unbound methods in Py2
- code.putln("if (likely(%s)) {" % self_arg)
- code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
- code.put_incref(self_arg, py_object_type)
- code.put_incref("function", py_object_type)
- # free method object as early to possible to enable reuse from CPython's freelist
- code.put_decref_set(function, "function")
- if len(args) > 1:
- code.putln("%s = 1;" % arg_offset_cname)
- code.putln("}")
- code.putln("}")
-
- if not args:
- # fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
+ code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
+ # the following is always true in Py3 (kept only for safety),
+ # but is false for unbound methods in Py2
+ code.putln("if (likely(%s)) {" % self_arg)
+ code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
+ code.put_incref(self_arg, py_object_type)
+ code.put_incref("function", py_object_type)
+ # free method object as early to possible to enable reuse from CPython's freelist
+ code.put_decref_set(function, "function")
+ if len(args) > 1:
+ code.putln("%s = 1;" % arg_offset_cname)
+ code.putln("}")
+ code.putln("}")
+
+ if not args:
+ # fastest special case: try to avoid tuple creation
+ code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectCallNoArg", "ObjectHandling.c"))
code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
+ UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
+ code.putln(
"%s = (%s) ? __Pyx_PyObject_CallOneArg(%s, %s) : __Pyx_PyObject_CallNoArg(%s);" % (
self.result(), self_arg,
- function, self_arg,
+ function, self_arg,
function))
code.put_xdecref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
+ code.funcstate.release_temp(self_arg)
code.putln(code.error_goto_if_null(self.result(), self.pos))
code.put_gotref(self.py_result())
elif len(args) == 1:
# fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
+ code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectCall2Args", "ObjectHandling.c"))
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
arg = args[0]
- code.putln(
+ code.putln(
"%s = (%s) ? __Pyx_PyObject_Call2Args(%s, %s, %s) : __Pyx_PyObject_CallOneArg(%s, %s);" % (
self.result(), self_arg,
function, self_arg, arg.py_result(),
@@ -6105,8 +6105,8 @@ class PyMethodCallNode(SimpleCallNode):
arg.generate_disposal_code(code)
arg.free_temps(code)
code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- else:
+ code.put_gotref(self.py_result())
+ else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyFunctionFastCall", "ObjectHandling.c"))
code.globalstate.use_utility_code(
@@ -6136,193 +6136,193 @@ class PyMethodCallNode(SimpleCallNode):
code.putln("#endif")
code.putln("{")
- args_tuple = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = PyTuple_New(%d+%s); %s" % (
+ args_tuple = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
+ code.putln("%s = PyTuple_New(%d+%s); %s" % (
args_tuple, len(args), arg_offset_cname,
- code.error_goto_if_null(args_tuple, self.pos)))
- code.put_gotref(args_tuple)
-
- if len(args) > 1:
- code.putln("if (%s) {" % self_arg)
+ code.error_goto_if_null(args_tuple, self.pos)))
+ code.put_gotref(args_tuple)
+
+ if len(args) > 1:
+ code.putln("if (%s) {" % self_arg)
code.putln("__Pyx_GIVEREF(%s); PyTuple_SET_ITEM(%s, 0, %s); %s = NULL;" % (
self_arg, args_tuple, self_arg, self_arg)) # stealing owned ref in this case
- code.funcstate.release_temp(self_arg)
- if len(args) > 1:
- code.putln("}")
-
- for i, arg in enumerate(args):
- arg.make_owned_reference(code)
+ code.funcstate.release_temp(self_arg)
+ if len(args) > 1:
+ code.putln("}")
+
+ for i, arg in enumerate(args):
+ arg.make_owned_reference(code)
code.put_giveref(arg.py_result())
- code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % (
+ code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % (
args_tuple, i, arg_offset_cname, arg.py_result()))
- if len(args) > 1:
- code.funcstate.release_temp(arg_offset_cname)
-
- for arg in args:
- arg.generate_post_assignment_code(code)
- arg.free_temps(code)
-
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- function, args_tuple,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- code.put_decref_clear(args_tuple, py_object_type)
- code.funcstate.release_temp(args_tuple)
-
- if len(args) == 1:
- code.putln("}")
+ if len(args) > 1:
+ code.funcstate.release_temp(arg_offset_cname)
+
+ for arg in args:
+ arg.generate_post_assignment_code(code)
+ arg.free_temps(code)
+
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectCall", "ObjectHandling.c"))
+ code.putln(
+ "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
+ self.result(),
+ function, args_tuple,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+ code.put_decref_clear(args_tuple, py_object_type)
+ code.funcstate.release_temp(args_tuple)
+
+ if len(args) == 1:
+ code.putln("}")
code.putln("}") # !CYTHON_FAST_PYCALL
-
- if reuse_function_temp:
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
- else:
- code.put_decref_clear(function, py_object_type)
- code.funcstate.release_temp(function)
-
-
-class InlinedDefNodeCallNode(CallNode):
- # Inline call to defnode
- #
- # function PyCFunctionNode
- # function_name NameNode
- # args [ExprNode]
-
- subexprs = ['args', 'function_name']
- is_temp = 1
- type = py_object_type
- function = None
- function_name = None
-
- def can_be_inlined(self):
- func_type= self.function.def_node
- if func_type.star_arg or func_type.starstar_arg:
- return False
- if len(func_type.args) != len(self.args):
- return False
+
+ if reuse_function_temp:
+ self.function.generate_disposal_code(code)
+ self.function.free_temps(code)
+ else:
+ code.put_decref_clear(function, py_object_type)
+ code.funcstate.release_temp(function)
+
+
+class InlinedDefNodeCallNode(CallNode):
+ # Inline call to defnode
+ #
+ # function PyCFunctionNode
+ # function_name NameNode
+ # args [ExprNode]
+
+ subexprs = ['args', 'function_name']
+ is_temp = 1
+ type = py_object_type
+ function = None
+ function_name = None
+
+ def can_be_inlined(self):
+ func_type= self.function.def_node
+ if func_type.star_arg or func_type.starstar_arg:
+ return False
+ if len(func_type.args) != len(self.args):
+ return False
if func_type.num_kwonly_args:
return False # actually wrong number of arguments
- return True
-
- def analyse_types(self, env):
- self.function_name = self.function_name.analyse_types(env)
-
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- func_type = self.function.def_node
- actual_nargs = len(self.args)
-
- # Coerce arguments
- some_args_in_temps = False
+ return True
+
+ def analyse_types(self, env):
+ self.function_name = self.function_name.analyse_types(env)
+
+ self.args = [ arg.analyse_types(env) for arg in self.args ]
+ func_type = self.function.def_node
+ actual_nargs = len(self.args)
+
+ # Coerce arguments
+ some_args_in_temps = False
for i in range(actual_nargs):
- formal_type = func_type.args[i].type
- arg = self.args[i].coerce_to(formal_type, env)
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- self.args[i] = arg
-
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
+ formal_type = func_type.args[i].type
+ arg = self.args[i].coerce_to(formal_type, env)
+ if arg.is_temp:
+ if i > 0:
+ # first argument in temp doesn't impact subsequent arguments
+ some_args_in_temps = True
+ elif arg.type.is_pyobject and not env.nogil:
+ if arg.nonlocally_immutable():
+ # plain local variables are ok
+ pass
+ else:
+ # we do not safely own the argument's reference,
+ # but we must make sure it cannot be collected
+ # before we return from the function, so we create
+ # an owned temp reference to it
+ if i > 0: # first argument doesn't matter
+ some_args_in_temps = True
+ arg = arg.coerce_to_temp(env)
+ self.args[i] = arg
+
+ if some_args_in_temps:
+ # if some args are temps and others are not, they may get
+ # constructed in the wrong order (temps first) => make
+ # sure they are either all temps or all not temps (except
+ # for the last argument, which is evaluated last in any
+ # case)
for i in range(actual_nargs-1):
- arg = self.args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0:
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
- return self
-
- def generate_result_code(self, code):
- arg_code = [self.function_name.py_result()]
- func_type = self.function.def_node
- for arg, proto_arg in zip(self.args, func_type.args):
- if arg.type.is_pyobject:
- arg_code.append(arg.result_as(proto_arg.type))
- else:
- arg_code.append(arg.result())
- arg_code = ', '.join(arg_code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- self.function.def_node.entry.pyfunc_cname,
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class PythonCapiFunctionNode(ExprNode):
- subexprs = []
-
- def __init__(self, pos, py_name, cname, func_type, utility_code = None):
- ExprNode.__init__(self, pos, name=py_name, cname=cname,
- type=func_type, utility_code=utility_code)
-
- def analyse_types(self, env):
- return self
-
- def generate_result_code(self, code):
- if self.utility_code:
- code.globalstate.use_utility_code(self.utility_code)
-
- def calculate_result_code(self):
- return self.cname
-
-
-class PythonCapiCallNode(SimpleCallNode):
- # Python C-API Function call (only created in transforms)
-
- # By default, we assume that the call never returns None, as this
- # is true for most C-API functions in CPython. If this does not
- # apply to a call, set the following to True (or None to inherit
- # the default behaviour).
- may_return_none = False
-
- def __init__(self, pos, function_name, func_type,
- utility_code = None, py_name=None, **kwargs):
- self.type = func_type.return_type
- self.result_ctype = self.type
- self.function = PythonCapiFunctionNode(
- pos, py_name, function_name, func_type,
- utility_code = utility_code)
- # call this last so that we can override the constructed
- # attributes above with explicit keyword arguments if required
- SimpleCallNode.__init__(self, pos, **kwargs)
-
-
+ arg = self.args[i]
+ if arg.nonlocally_immutable():
+ # locals, C functions, unassignable types are safe.
+ pass
+ elif arg.type.is_cpp_class:
+ # Assignment has side effects, avoid.
+ pass
+ elif env.nogil and arg.type.is_pyobject:
+ # can't copy a Python reference into a temp in nogil
+ # env (this is safe: a construction would fail in
+ # nogil anyway)
+ pass
+ else:
+ #self.args[i] = arg.coerce_to_temp(env)
+ # instead: issue a warning
+ if i > 0:
+ warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
+ break
+ return self
+
+ def generate_result_code(self, code):
+ arg_code = [self.function_name.py_result()]
+ func_type = self.function.def_node
+ for arg, proto_arg in zip(self.args, func_type.args):
+ if arg.type.is_pyobject:
+ arg_code.append(arg.result_as(proto_arg.type))
+ else:
+ arg_code.append(arg.result())
+ arg_code = ', '.join(arg_code)
+ code.putln(
+ "%s = %s(%s); %s" % (
+ self.result(),
+ self.function.def_node.entry.pyfunc_cname,
+ arg_code,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class PythonCapiFunctionNode(ExprNode):
+ subexprs = []
+
+ def __init__(self, pos, py_name, cname, func_type, utility_code = None):
+ ExprNode.__init__(self, pos, name=py_name, cname=cname,
+ type=func_type, utility_code=utility_code)
+
+ def analyse_types(self, env):
+ return self
+
+ def generate_result_code(self, code):
+ if self.utility_code:
+ code.globalstate.use_utility_code(self.utility_code)
+
+ def calculate_result_code(self):
+ return self.cname
+
+
+class PythonCapiCallNode(SimpleCallNode):
+ # Python C-API Function call (only created in transforms)
+
+ # By default, we assume that the call never returns None, as this
+ # is true for most C-API functions in CPython. If this does not
+ # apply to a call, set the following to True (or None to inherit
+ # the default behaviour).
+ may_return_none = False
+
+ def __init__(self, pos, function_name, func_type,
+ utility_code = None, py_name=None, **kwargs):
+ self.type = func_type.return_type
+ self.result_ctype = self.type
+ self.function = PythonCapiFunctionNode(
+ pos, py_name, function_name, func_type,
+ utility_code = utility_code)
+ # call this last so that we can override the constructed
+ # attributes above with explicit keyword arguments if required
+ SimpleCallNode.__init__(self, pos, **kwargs)
+
+
class CachedBuiltinMethodCallNode(CallNode):
# Python call to a method of a known Python builtin (only created in transforms)
@@ -6354,266 +6354,266 @@ class CachedBuiltinMethodCallNode(CallNode):
code.put_gotref(self.result())
-class GeneralCallNode(CallNode):
- # General Python function call, including keyword,
- # * and ** arguments.
- #
- # function ExprNode
- # positional_args ExprNode Tuple of positional arguments
- # keyword_args ExprNode or None Dict of keyword arguments
-
- type = py_object_type
-
- subexprs = ['function', 'positional_args', 'keyword_args']
-
- nogil_check = Node.gil_error
-
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- positional_args = self.positional_args.compile_time_value(denv)
- keyword_args = self.keyword_args.compile_time_value(denv)
- try:
- return function(*positional_args, **keyword_args)
+class GeneralCallNode(CallNode):
+ # General Python function call, including keyword,
+ # * and ** arguments.
+ #
+ # function ExprNode
+ # positional_args ExprNode Tuple of positional arguments
+ # keyword_args ExprNode or None Dict of keyword arguments
+
+ type = py_object_type
+
+ subexprs = ['function', 'positional_args', 'keyword_args']
+
+ nogil_check = Node.gil_error
+
+ def compile_time_value(self, denv):
+ function = self.function.compile_time_value(denv)
+ positional_args = self.positional_args.compile_time_value(denv)
+ keyword_args = self.keyword_args.compile_time_value(denv)
+ try:
+ return function(*positional_args, **keyword_args)
except Exception as e:
- self.compile_time_value_error(e)
-
- def explicit_args_kwds(self):
+ self.compile_time_value_error(e)
+
+ def explicit_args_kwds(self):
if (self.keyword_args and not self.keyword_args.is_dict_literal or
not self.positional_args.is_sequence_constructor):
- raise CompileError(self.pos,
- 'Compile-time keyword arguments must be explicit.')
- return self.positional_args.args, self.keyword_args
-
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- self.function = self.function.analyse_types(env)
- if not self.function.type.is_pyobject:
- if self.function.type.is_error:
- self.type = error_type
- return self
- if hasattr(self.function, 'entry'):
- node = self.map_to_simple_call_node()
- if node is not None and node is not self:
- return node.analyse_types(env)
- elif self.function.entry.as_variable:
- self.function = self.function.coerce_to_pyobject(env)
- elif node is self:
- error(self.pos,
- "Non-trivial keyword arguments and starred "
- "arguments not allowed in cdef functions.")
- else:
- # error was already reported
- pass
- else:
- self.function = self.function.coerce_to_pyobject(env)
- if self.keyword_args:
- self.keyword_args = self.keyword_args.analyse_types(env)
- self.positional_args = self.positional_args.analyse_types(env)
- self.positional_args = \
- self.positional_args.coerce_to_pyobject(env)
+ raise CompileError(self.pos,
+ 'Compile-time keyword arguments must be explicit.')
+ return self.positional_args.args, self.keyword_args
+
+ def analyse_types(self, env):
+ if self.analyse_as_type_constructor(env):
+ return self
+ self.function = self.function.analyse_types(env)
+ if not self.function.type.is_pyobject:
+ if self.function.type.is_error:
+ self.type = error_type
+ return self
+ if hasattr(self.function, 'entry'):
+ node = self.map_to_simple_call_node()
+ if node is not None and node is not self:
+ return node.analyse_types(env)
+ elif self.function.entry.as_variable:
+ self.function = self.function.coerce_to_pyobject(env)
+ elif node is self:
+ error(self.pos,
+ "Non-trivial keyword arguments and starred "
+ "arguments not allowed in cdef functions.")
+ else:
+ # error was already reported
+ pass
+ else:
+ self.function = self.function.coerce_to_pyobject(env)
+ if self.keyword_args:
+ self.keyword_args = self.keyword_args.analyse_types(env)
+ self.positional_args = self.positional_args.analyse_types(env)
+ self.positional_args = \
+ self.positional_args.coerce_to_pyobject(env)
self.set_py_result_type(self.function)
- self.is_temp = 1
- return self
-
- def map_to_simple_call_node(self):
- """
- Tries to map keyword arguments to declared positional arguments.
- Returns self to try a Python call, None to report an error
- or a SimpleCallNode if the mapping succeeds.
- """
- if not isinstance(self.positional_args, TupleNode):
- # has starred argument
- return self
+ self.is_temp = 1
+ return self
+
+ def map_to_simple_call_node(self):
+ """
+ Tries to map keyword arguments to declared positional arguments.
+ Returns self to try a Python call, None to report an error
+ or a SimpleCallNode if the mapping succeeds.
+ """
+ if not isinstance(self.positional_args, TupleNode):
+ # has starred argument
+ return self
if not self.keyword_args.is_dict_literal:
- # keywords come from arbitrary expression => nothing to do here
- return self
- function = self.function
- entry = getattr(function, 'entry', None)
- if not entry:
- return self
- function_type = entry.type
- if function_type.is_ptr:
- function_type = function_type.base_type
- if not function_type.is_cfunction:
- return self
-
- pos_args = self.positional_args.args
- kwargs = self.keyword_args
- declared_args = function_type.args
- if entry.is_cmethod:
- declared_args = declared_args[1:] # skip 'self'
-
- if len(pos_args) > len(declared_args):
- error(self.pos, "function call got too many positional arguments, "
- "expected %d, got %s" % (len(declared_args),
- len(pos_args)))
- return None
-
- matched_args = set([ arg.name for arg in declared_args[:len(pos_args)]
- if arg.name ])
- unmatched_args = declared_args[len(pos_args):]
- matched_kwargs_count = 0
- args = list(pos_args)
-
- # check for duplicate keywords
- seen = set(matched_args)
- has_errors = False
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name in seen:
- error(arg.pos, "argument '%s' passed twice" % name)
- has_errors = True
- # continue to report more errors if there are any
- seen.add(name)
-
- # match keywords that are passed in order
- for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
- name = arg.key.value
- if decl_arg.name == name:
- matched_args.add(name)
- matched_kwargs_count += 1
- args.append(arg.value)
- else:
- break
-
- # match keyword arguments that are passed out-of-order, but keep
- # the evaluation of non-simple arguments in order by moving them
- # into temps
- from .UtilNodes import EvalWithTempExprNode, LetRefNode
- temps = []
- if len(kwargs.key_value_pairs) > matched_kwargs_count:
- unmatched_args = declared_args[len(args):]
- keywords = dict([ (arg.key.value, (i+len(pos_args), arg))
- for i, arg in enumerate(kwargs.key_value_pairs) ])
- first_missing_keyword = None
- for decl_arg in unmatched_args:
- name = decl_arg.name
- if name not in keywords:
- # missing keyword argument => either done or error
- if not first_missing_keyword:
- first_missing_keyword = name
- continue
- elif first_missing_keyword:
- if entry.as_variable:
- # we might be able to convert the function to a Python
- # object, which then allows full calling semantics
- # with default values in gaps - currently, we only
- # support optional arguments at the end
- return self
- # wasn't the last keyword => gaps are not supported
- error(self.pos, "C function call is missing "
- "argument '%s'" % first_missing_keyword)
- return None
- pos, arg = keywords[name]
- matched_args.add(name)
- matched_kwargs_count += 1
- if arg.value.is_simple():
- args.append(arg.value)
- else:
- temp = LetRefNode(arg.value)
- assert temp.is_simple()
- args.append(temp)
- temps.append((pos, temp))
-
- if temps:
- # may have to move preceding non-simple args into temps
- final_args = []
- new_temps = []
- first_temp_arg = temps[0][-1]
- for arg_value in args:
- if arg_value is first_temp_arg:
- break # done
- if arg_value.is_simple():
- final_args.append(arg_value)
- else:
- temp = LetRefNode(arg_value)
- new_temps.append(temp)
- final_args.append(temp)
- if new_temps:
- args = final_args
- temps = new_temps + [ arg for i,arg in sorted(temps) ]
-
- # check for unexpected keywords
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name not in matched_args:
- has_errors = True
- error(arg.pos,
- "C function got unexpected keyword argument '%s'" %
- name)
-
- if has_errors:
- # error was reported already
- return None
-
- # all keywords mapped to positional arguments
- # if we are missing arguments, SimpleCallNode will figure it out
- node = SimpleCallNode(self.pos, function=function, args=args)
- for temp in temps[::-1]:
- node = EvalWithTempExprNode(temp, node)
- return node
-
- def generate_result_code(self, code):
- if self.type.is_error: return
- if self.keyword_args:
- kwargs = self.keyword_args.py_result()
- else:
- kwargs = 'NULL'
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % (
- self.result(),
- self.function.py_result(),
- self.positional_args.py_result(),
- kwargs,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class AsTupleNode(ExprNode):
- # Convert argument to tuple. Used for normalising
- # the * argument of a function call.
- #
- # arg ExprNode
-
- subexprs = ['arg']
+ # keywords come from arbitrary expression => nothing to do here
+ return self
+ function = self.function
+ entry = getattr(function, 'entry', None)
+ if not entry:
+ return self
+ function_type = entry.type
+ if function_type.is_ptr:
+ function_type = function_type.base_type
+ if not function_type.is_cfunction:
+ return self
+
+ pos_args = self.positional_args.args
+ kwargs = self.keyword_args
+ declared_args = function_type.args
+ if entry.is_cmethod:
+ declared_args = declared_args[1:] # skip 'self'
+
+ if len(pos_args) > len(declared_args):
+ error(self.pos, "function call got too many positional arguments, "
+ "expected %d, got %s" % (len(declared_args),
+ len(pos_args)))
+ return None
+
+ matched_args = set([ arg.name for arg in declared_args[:len(pos_args)]
+ if arg.name ])
+ unmatched_args = declared_args[len(pos_args):]
+ matched_kwargs_count = 0
+ args = list(pos_args)
+
+ # check for duplicate keywords
+ seen = set(matched_args)
+ has_errors = False
+ for arg in kwargs.key_value_pairs:
+ name = arg.key.value
+ if name in seen:
+ error(arg.pos, "argument '%s' passed twice" % name)
+ has_errors = True
+ # continue to report more errors if there are any
+ seen.add(name)
+
+ # match keywords that are passed in order
+ for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
+ name = arg.key.value
+ if decl_arg.name == name:
+ matched_args.add(name)
+ matched_kwargs_count += 1
+ args.append(arg.value)
+ else:
+ break
+
+ # match keyword arguments that are passed out-of-order, but keep
+ # the evaluation of non-simple arguments in order by moving them
+ # into temps
+ from .UtilNodes import EvalWithTempExprNode, LetRefNode
+ temps = []
+ if len(kwargs.key_value_pairs) > matched_kwargs_count:
+ unmatched_args = declared_args[len(args):]
+ keywords = dict([ (arg.key.value, (i+len(pos_args), arg))
+ for i, arg in enumerate(kwargs.key_value_pairs) ])
+ first_missing_keyword = None
+ for decl_arg in unmatched_args:
+ name = decl_arg.name
+ if name not in keywords:
+ # missing keyword argument => either done or error
+ if not first_missing_keyword:
+ first_missing_keyword = name
+ continue
+ elif first_missing_keyword:
+ if entry.as_variable:
+ # we might be able to convert the function to a Python
+ # object, which then allows full calling semantics
+ # with default values in gaps - currently, we only
+ # support optional arguments at the end
+ return self
+ # wasn't the last keyword => gaps are not supported
+ error(self.pos, "C function call is missing "
+ "argument '%s'" % first_missing_keyword)
+ return None
+ pos, arg = keywords[name]
+ matched_args.add(name)
+ matched_kwargs_count += 1
+ if arg.value.is_simple():
+ args.append(arg.value)
+ else:
+ temp = LetRefNode(arg.value)
+ assert temp.is_simple()
+ args.append(temp)
+ temps.append((pos, temp))
+
+ if temps:
+ # may have to move preceding non-simple args into temps
+ final_args = []
+ new_temps = []
+ first_temp_arg = temps[0][-1]
+ for arg_value in args:
+ if arg_value is first_temp_arg:
+ break # done
+ if arg_value.is_simple():
+ final_args.append(arg_value)
+ else:
+ temp = LetRefNode(arg_value)
+ new_temps.append(temp)
+ final_args.append(temp)
+ if new_temps:
+ args = final_args
+ temps = new_temps + [ arg for i,arg in sorted(temps) ]
+
+ # check for unexpected keywords
+ for arg in kwargs.key_value_pairs:
+ name = arg.key.value
+ if name not in matched_args:
+ has_errors = True
+ error(arg.pos,
+ "C function got unexpected keyword argument '%s'" %
+ name)
+
+ if has_errors:
+ # error was reported already
+ return None
+
+ # all keywords mapped to positional arguments
+ # if we are missing arguments, SimpleCallNode will figure it out
+ node = SimpleCallNode(self.pos, function=function, args=args)
+ for temp in temps[::-1]:
+ node = EvalWithTempExprNode(temp, node)
+ return node
+
+ def generate_result_code(self, code):
+ if self.type.is_error: return
+ if self.keyword_args:
+ kwargs = self.keyword_args.py_result()
+ else:
+ kwargs = 'NULL'
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ "PyObjectCall", "ObjectHandling.c"))
+ code.putln(
+ "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % (
+ self.result(),
+ self.function.py_result(),
+ self.positional_args.py_result(),
+ kwargs,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class AsTupleNode(ExprNode):
+ # Convert argument to tuple. Used for normalising
+ # the * argument of a function call.
+ #
+ # arg ExprNode
+
+ subexprs = ['arg']
is_temp = 1
-
- def calculate_constant_result(self):
- self.constant_result = tuple(self.arg.constant_result)
-
- def compile_time_value(self, denv):
- arg = self.arg.compile_time_value(denv)
- try:
- return tuple(arg)
+
+ def calculate_constant_result(self):
+ self.constant_result = tuple(self.arg.constant_result)
+
+ def compile_time_value(self, denv):
+ arg = self.arg.compile_time_value(denv)
+ try:
+ return tuple(arg)
except Exception as e:
- self.compile_time_value_error(e)
-
- def analyse_types(self, env):
+ self.compile_time_value_error(e)
+
+ def analyse_types(self, env):
self.arg = self.arg.analyse_types(env).coerce_to_pyobject(env)
if self.arg.type is tuple_type:
return self.arg.as_none_safe_node("'NoneType' object is not iterable")
- self.type = tuple_type
- return self
-
- def may_be_none(self):
- return False
-
- nogil_check = Node.gil_error
- gil_message = "Constructing Python tuple"
-
- def generate_result_code(self, code):
+ self.type = tuple_type
+ return self
+
+ def may_be_none(self):
+ return False
+
+ nogil_check = Node.gil_error
+ gil_message = "Constructing Python tuple"
+
+ def generate_result_code(self, code):
cfunc = "__Pyx_PySequence_Tuple" if self.arg.type in (py_object_type, tuple_type) else "PySequence_Tuple"
- code.putln(
+ code.putln(
"%s = %s(%s); %s" % (
- self.result(),
+ self.result(),
cfunc, self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
class MergedDictNode(ExprNode):
# Helper class for keyword arguments and other merged dicts.
#
@@ -6767,152 +6767,152 @@ class MergedDictNode(ExprNode):
item.annotate(code)
-class AttributeNode(ExprNode):
- # obj.attribute
- #
- # obj ExprNode
- # attribute string
- # needs_none_check boolean Used if obj is an extension type.
- # If set to True, it is known that the type is not None.
- #
- # Used internally:
- #
- # is_py_attr boolean Is a Python getattr operation
- # member string C name of struct member
- # is_called boolean Function call is being done on result
- # entry Entry Symbol table entry of attribute
-
- is_attribute = 1
- subexprs = ['obj']
-
- type = PyrexTypes.error_type
- entry = None
- is_called = 0
- needs_none_check = True
- is_memslice_transpose = False
- is_special_lookup = False
+class AttributeNode(ExprNode):
+ # obj.attribute
+ #
+ # obj ExprNode
+ # attribute string
+ # needs_none_check boolean Used if obj is an extension type.
+ # If set to True, it is known that the type is not None.
+ #
+ # Used internally:
+ #
+ # is_py_attr boolean Is a Python getattr operation
+ # member string C name of struct member
+ # is_called boolean Function call is being done on result
+ # entry Entry Symbol table entry of attribute
+
+ is_attribute = 1
+ subexprs = ['obj']
+
+ type = PyrexTypes.error_type
+ entry = None
+ is_called = 0
+ needs_none_check = True
+ is_memslice_transpose = False
+ is_special_lookup = False
is_py_attr = 0
-
- def as_cython_attribute(self):
- if (isinstance(self.obj, NameNode) and
- self.obj.is_cython_module and not
- self.attribute == u"parallel"):
- return self.attribute
-
- cy = self.obj.as_cython_attribute()
- if cy:
- return "%s.%s" % (cy, self.attribute)
- return None
-
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a cpdef function
- # we can create the corresponding attribute
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction and entry.as_variable:
- # must be a cpdef function
- self.is_temp = 1
- self.entry = entry.as_variable
- self.analyse_as_python_attribute(env)
- return self
- return ExprNode.coerce_to(self, dst_type, env)
-
- def calculate_constant_result(self):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- return
- self.constant_result = getattr(self.obj.constant_result, attr)
-
- def compile_time_value(self, denv):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- error(self.pos,
- "Invalid attribute name '%s' in compile-time expression" % attr)
- return None
- obj = self.obj.compile_time_value(denv)
- try:
- return getattr(obj, attr)
+
+ def as_cython_attribute(self):
+ if (isinstance(self.obj, NameNode) and
+ self.obj.is_cython_module and not
+ self.attribute == u"parallel"):
+ return self.attribute
+
+ cy = self.obj.as_cython_attribute()
+ if cy:
+ return "%s.%s" % (cy, self.attribute)
+ return None
+
+ def coerce_to(self, dst_type, env):
+ # If coercing to a generic pyobject and this is a cpdef function
+ # we can create the corresponding attribute
+ if dst_type is py_object_type:
+ entry = self.entry
+ if entry and entry.is_cfunction and entry.as_variable:
+ # must be a cpdef function
+ self.is_temp = 1
+ self.entry = entry.as_variable
+ self.analyse_as_python_attribute(env)
+ return self
+ return ExprNode.coerce_to(self, dst_type, env)
+
+ def calculate_constant_result(self):
+ attr = self.attribute
+ if attr.startswith("__") and attr.endswith("__"):
+ return
+ self.constant_result = getattr(self.obj.constant_result, attr)
+
+ def compile_time_value(self, denv):
+ attr = self.attribute
+ if attr.startswith("__") and attr.endswith("__"):
+ error(self.pos,
+ "Invalid attribute name '%s' in compile-time expression" % attr)
+ return None
+ obj = self.obj.compile_time_value(denv)
+ try:
+ return getattr(obj, attr)
except Exception as e:
- self.compile_time_value_error(e)
-
- def type_dependencies(self, env):
- return self.obj.type_dependencies(env)
-
- def infer_type(self, env):
- # FIXME: this is way too redundant with analyse_types()
- node = self.analyse_as_cimported_attribute_node(env, target=False)
- if node is not None:
+ self.compile_time_value_error(e)
+
+ def type_dependencies(self, env):
+ return self.obj.type_dependencies(env)
+
+ def infer_type(self, env):
+ # FIXME: this is way too redundant with analyse_types()
+ node = self.analyse_as_cimported_attribute_node(env, target=False)
+ if node is not None:
if node.entry.type and node.entry.type.is_cfunction:
# special-case - function converted to pointer
return PyrexTypes.CPtrType(node.entry.type)
else:
return node.entry.type
node = self.analyse_as_type_attribute(env)
- if node is not None:
- return node.entry.type
- obj_type = self.obj.infer_type(env)
- self.analyse_attribute(env, obj_type=obj_type)
- if obj_type.is_builtin_type and self.type.is_cfunction:
- # special case: C-API replacements for C methods of
- # builtin types cannot be inferred as C functions as
- # that would prevent their use as bound methods
- return py_object_type
+ if node is not None:
+ return node.entry.type
+ obj_type = self.obj.infer_type(env)
+ self.analyse_attribute(env, obj_type=obj_type)
+ if obj_type.is_builtin_type and self.type.is_cfunction:
+ # special case: C-API replacements for C methods of
+ # builtin types cannot be inferred as C functions as
+ # that would prevent their use as bound methods
+ return py_object_type
elif self.entry and self.entry.is_cmethod:
# special case: bound methods should not be inferred
# as their unbound method types
return py_object_type
- return self.type
-
- def analyse_target_declaration(self, env):
- pass
-
- def analyse_target_types(self, env):
- node = self.analyse_types(env, target = 1)
- if node.type.is_const:
- error(self.pos, "Assignment to const attribute '%s'" % self.attribute)
- if not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
- return node
-
- def analyse_types(self, env, target = 0):
- self.initialized_check = env.directives['initializedcheck']
- node = self.analyse_as_cimported_attribute_node(env, target)
- if node is None and not target:
+ return self.type
+
+ def analyse_target_declaration(self, env):
+ pass
+
+ def analyse_target_types(self, env):
+ node = self.analyse_types(env, target = 1)
+ if node.type.is_const:
+ error(self.pos, "Assignment to const attribute '%s'" % self.attribute)
+ if not node.is_lvalue():
+ error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
+ return node
+
+ def analyse_types(self, env, target = 0):
+ self.initialized_check = env.directives['initializedcheck']
+ node = self.analyse_as_cimported_attribute_node(env, target)
+ if node is None and not target:
node = self.analyse_as_type_attribute(env)
- if node is None:
- node = self.analyse_as_ordinary_attribute_node(env, target)
- assert node is not None
- if node.entry:
- node.entry.used = True
- if node.is_attribute:
- node.wrap_obj_in_nonecheck(env)
- return node
-
- def analyse_as_cimported_attribute_node(self, env, target):
- # Try to interpret this as a reference to an imported
- # C const, type, var or function. If successful, mutates
- # this node into a NameNode and returns 1, otherwise
- # returns 0.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and (
- entry.is_cglobal or entry.is_cfunction
- or entry.is_type or entry.is_const):
- return self.as_name_node(env, entry, target)
+ if node is None:
+ node = self.analyse_as_ordinary_attribute_node(env, target)
+ assert node is not None
+ if node.entry:
+ node.entry.used = True
+ if node.is_attribute:
+ node.wrap_obj_in_nonecheck(env)
+ return node
+
+ def analyse_as_cimported_attribute_node(self, env, target):
+ # Try to interpret this as a reference to an imported
+ # C const, type, var or function. If successful, mutates
+ # this node into a NameNode and returns 1, otherwise
+ # returns 0.
+ module_scope = self.obj.analyse_as_module(env)
+ if module_scope:
+ entry = module_scope.lookup_here(self.attribute)
+ if entry and (
+ entry.is_cglobal or entry.is_cfunction
+ or entry.is_type or entry.is_const):
+ return self.as_name_node(env, entry, target)
if self.is_cimported_module_without_shadow(env):
error(self.pos, "cimported module has no attribute '%s'" % self.attribute)
return self
- return None
-
+ return None
+
def analyse_as_type_attribute(self, env):
- # Try to interpret this as a reference to an unbound
- # C method of an extension type or builtin type. If successful,
- # creates a corresponding NameNode and returns it, otherwise
- # returns None.
- if self.obj.is_string_literal:
- return
- type = self.obj.analyse_as_type(env)
+ # Try to interpret this as a reference to an unbound
+ # C method of an extension type or builtin type. If successful,
+ # creates a corresponding NameNode and returns it, otherwise
+ # returns None.
+ if self.obj.is_string_literal:
+ return
+ type = self.obj.analyse_as_type(env)
if type:
if type.is_extension_type or type.is_builtin_type or type.is_cpp_class:
entry = type.scope.lookup_here(self.attribute)
@@ -6940,7 +6940,7 @@ class AttributeNode(ExprNode):
ctype.args[0] = PyrexTypes.CFuncTypeArg('self', type, 'self', None)
else:
cname = "%s->%s" % (type.vtabptr_cname, entry.cname)
- ctype = entry.type
+ ctype = entry.type
ubcm_entry = Symtab.Entry(entry.name, cname, ctype)
ubcm_entry.is_cfunction = 1
ubcm_entry.func_cname = entry.func_cname
@@ -6952,489 +6952,489 @@ class AttributeNode(ExprNode):
for entry in type.entry.enum_values:
if entry.name == self.attribute:
return self.as_name_node(env, entry, target=False)
- else:
+ else:
error(self.pos, "%s not a known value of %s" % (self.attribute, type))
else:
error(self.pos, "%s not a known value of %s" % (self.attribute, type))
- return None
-
- def analyse_as_type(self, env):
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- return module_scope.lookup_type(self.attribute)
- if not self.obj.is_string_literal:
- base_type = self.obj.analyse_as_type(env)
- if base_type and hasattr(base_type, 'scope') and base_type.scope is not None:
- return base_type.scope.lookup_type(self.attribute)
- return None
-
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type
- # in a cimported module. Returns the extension type, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
-
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module
- # in another cimported module. Returns the module scope, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.as_module:
- return entry.as_module
- return None
-
- def as_name_node(self, env, entry, target):
- # Create a corresponding NameNode from this node and complete the
- # analyse_types phase.
- node = NameNode.from_node(self, name=self.attribute, entry=entry)
- if target:
- node = node.analyse_target_types(env)
- else:
- node = node.analyse_rvalue_entry(env)
- node.entry.used = 1
- return node
-
- def analyse_as_ordinary_attribute_node(self, env, target):
- self.obj = self.obj.analyse_types(env)
- self.analyse_attribute(env)
- if self.entry and self.entry.is_cmethod and not self.is_called:
-# error(self.pos, "C method can only be called")
- pass
- ## Reference to C array turns into pointer to first element.
- #while self.type.is_array:
- # self.type = self.type.element_ptr_type()
- if self.is_py_attr:
- if not target:
- self.is_temp = 1
- self.result_ctype = py_object_type
- elif target and self.obj.type.is_builtin_type:
- error(self.pos, "Assignment to an immutable object field")
- #elif self.type.is_memoryviewslice and not target:
- # self.is_temp = True
- return self
-
- def analyse_attribute(self, env, obj_type = None):
- # Look up attribute and set self.type and self.member.
- immutable_obj = obj_type is not None # used during type inference
- self.is_py_attr = 0
- self.member = self.attribute
- if obj_type is None:
- if self.obj.type.is_string or self.obj.type.is_pyunicode_ptr:
- self.obj = self.obj.coerce_to_pyobject(env)
- obj_type = self.obj.type
- else:
- if obj_type.is_string or obj_type.is_pyunicode_ptr:
- obj_type = py_object_type
- if obj_type.is_ptr or obj_type.is_array:
- obj_type = obj_type.base_type
- self.op = "->"
- elif obj_type.is_extension_type or obj_type.is_builtin_type:
- self.op = "->"
+ return None
+
+ def analyse_as_type(self, env):
+ module_scope = self.obj.analyse_as_module(env)
+ if module_scope:
+ return module_scope.lookup_type(self.attribute)
+ if not self.obj.is_string_literal:
+ base_type = self.obj.analyse_as_type(env)
+ if base_type and hasattr(base_type, 'scope') and base_type.scope is not None:
+ return base_type.scope.lookup_type(self.attribute)
+ return None
+
+ def analyse_as_extension_type(self, env):
+ # Try to interpret this as a reference to an extension type
+ # in a cimported module. Returns the extension type, or None.
+ module_scope = self.obj.analyse_as_module(env)
+ if module_scope:
+ entry = module_scope.lookup_here(self.attribute)
+ if entry and entry.is_type:
+ if entry.type.is_extension_type or entry.type.is_builtin_type:
+ return entry.type
+ return None
+
+ def analyse_as_module(self, env):
+ # Try to interpret this as a reference to a cimported module
+ # in another cimported module. Returns the module scope, or None.
+ module_scope = self.obj.analyse_as_module(env)
+ if module_scope:
+ entry = module_scope.lookup_here(self.attribute)
+ if entry and entry.as_module:
+ return entry.as_module
+ return None
+
+ def as_name_node(self, env, entry, target):
+ # Create a corresponding NameNode from this node and complete the
+ # analyse_types phase.
+ node = NameNode.from_node(self, name=self.attribute, entry=entry)
+ if target:
+ node = node.analyse_target_types(env)
+ else:
+ node = node.analyse_rvalue_entry(env)
+ node.entry.used = 1
+ return node
+
+ def analyse_as_ordinary_attribute_node(self, env, target):
+ self.obj = self.obj.analyse_types(env)
+ self.analyse_attribute(env)
+ if self.entry and self.entry.is_cmethod and not self.is_called:
+# error(self.pos, "C method can only be called")
+ pass
+ ## Reference to C array turns into pointer to first element.
+ #while self.type.is_array:
+ # self.type = self.type.element_ptr_type()
+ if self.is_py_attr:
+ if not target:
+ self.is_temp = 1
+ self.result_ctype = py_object_type
+ elif target and self.obj.type.is_builtin_type:
+ error(self.pos, "Assignment to an immutable object field")
+ #elif self.type.is_memoryviewslice and not target:
+ # self.is_temp = True
+ return self
+
+ def analyse_attribute(self, env, obj_type = None):
+ # Look up attribute and set self.type and self.member.
+ immutable_obj = obj_type is not None # used during type inference
+ self.is_py_attr = 0
+ self.member = self.attribute
+ if obj_type is None:
+ if self.obj.type.is_string or self.obj.type.is_pyunicode_ptr:
+ self.obj = self.obj.coerce_to_pyobject(env)
+ obj_type = self.obj.type
+ else:
+ if obj_type.is_string or obj_type.is_pyunicode_ptr:
+ obj_type = py_object_type
+ if obj_type.is_ptr or obj_type.is_array:
+ obj_type = obj_type.base_type
+ self.op = "->"
+ elif obj_type.is_extension_type or obj_type.is_builtin_type:
+ self.op = "->"
elif obj_type.is_reference and obj_type.is_fake_reference:
self.op = "->"
- else:
- self.op = "."
- if obj_type.has_attributes:
- if obj_type.attributes_known():
+ else:
+ self.op = "."
+ if obj_type.has_attributes:
+ if obj_type.attributes_known():
entry = obj_type.scope.lookup_here(self.attribute)
if obj_type.is_memoryviewslice and not entry:
- if self.attribute == 'T':
- self.is_memslice_transpose = True
- self.is_temp = True
- self.use_managed_ref = True
+ if self.attribute == 'T':
+ self.is_memslice_transpose = True
+ self.is_temp = True
+ self.use_managed_ref = True
self.type = self.obj.type.transpose(self.pos)
- return
- else:
- obj_type.declare_attribute(self.attribute, env, self.pos)
+ return
+ else:
+ obj_type.declare_attribute(self.attribute, env, self.pos)
entry = obj_type.scope.lookup_here(self.attribute)
- if entry and entry.is_member:
- entry = None
- else:
- error(self.pos,
- "Cannot select attribute of incomplete type '%s'"
- % obj_type)
- self.type = PyrexTypes.error_type
- return
- self.entry = entry
- if entry:
- if obj_type.is_extension_type and entry.name == "__weakref__":
- error(self.pos, "Illegal use of special attribute __weakref__")
-
- # def methods need the normal attribute lookup
- # because they do not have struct entries
- # fused function go through assignment synthesis
- # (foo = pycfunction(foo_func_obj)) and need to go through
- # regular Python lookup as well
- if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
- self.type = entry.type
- self.member = entry.cname
- return
- else:
- # If it's not a variable or C method, it must be a Python
- # method of an extension type, so we treat it like a Python
- # attribute.
- pass
- # If we get here, the base object is not a struct/union/extension
- # type, or it is an extension type and the attribute is either not
- # declared or is declared as a Python method. Treat it as a Python
- # attribute reference.
- self.analyse_as_python_attribute(env, obj_type, immutable_obj)
-
- def analyse_as_python_attribute(self, env, obj_type=None, immutable_obj=False):
- if obj_type is None:
- obj_type = self.obj.type
- # mangle private '__*' Python attributes used inside of a class
- self.attribute = env.mangle_class_private_name(self.attribute)
- self.member = self.attribute
- self.type = py_object_type
- self.is_py_attr = 1
-
- if not obj_type.is_pyobject and not obj_type.is_error:
+ if entry and entry.is_member:
+ entry = None
+ else:
+ error(self.pos,
+ "Cannot select attribute of incomplete type '%s'"
+ % obj_type)
+ self.type = PyrexTypes.error_type
+ return
+ self.entry = entry
+ if entry:
+ if obj_type.is_extension_type and entry.name == "__weakref__":
+ error(self.pos, "Illegal use of special attribute __weakref__")
+
+ # def methods need the normal attribute lookup
+ # because they do not have struct entries
+ # fused function go through assignment synthesis
+ # (foo = pycfunction(foo_func_obj)) and need to go through
+ # regular Python lookup as well
+ if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
+ self.type = entry.type
+ self.member = entry.cname
+ return
+ else:
+ # If it's not a variable or C method, it must be a Python
+ # method of an extension type, so we treat it like a Python
+ # attribute.
+ pass
+ # If we get here, the base object is not a struct/union/extension
+ # type, or it is an extension type and the attribute is either not
+ # declared or is declared as a Python method. Treat it as a Python
+ # attribute reference.
+ self.analyse_as_python_attribute(env, obj_type, immutable_obj)
+
+ def analyse_as_python_attribute(self, env, obj_type=None, immutable_obj=False):
+ if obj_type is None:
+ obj_type = self.obj.type
+ # mangle private '__*' Python attributes used inside of a class
+ self.attribute = env.mangle_class_private_name(self.attribute)
+ self.member = self.attribute
+ self.type = py_object_type
+ self.is_py_attr = 1
+
+ if not obj_type.is_pyobject and not obj_type.is_error:
# Expose python methods for immutable objects.
if (obj_type.is_string or obj_type.is_cpp_string
or obj_type.is_buffer or obj_type.is_memoryviewslice
or obj_type.is_numeric
or (obj_type.is_ctuple and obj_type.can_coerce_to_pyobject(env))
or (obj_type.is_struct and obj_type.can_coerce_to_pyobject(env))):
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- elif (obj_type.is_cfunction and (self.obj.is_name or self.obj.is_attribute)
- and self.obj.entry.as_variable
- and self.obj.entry.as_variable.type.is_pyobject):
- # might be an optimised builtin function => unpack it
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- else:
- error(self.pos,
- "Object of type '%s' has no attribute '%s'" %
- (obj_type, self.attribute))
-
- def wrap_obj_in_nonecheck(self, env):
- if not env.directives['nonecheck']:
- return
-
- msg = None
- format_args = ()
- if (self.obj.type.is_extension_type and self.needs_none_check and not
- self.is_py_attr):
+ if not immutable_obj:
+ self.obj = self.obj.coerce_to_pyobject(env)
+ elif (obj_type.is_cfunction and (self.obj.is_name or self.obj.is_attribute)
+ and self.obj.entry.as_variable
+ and self.obj.entry.as_variable.type.is_pyobject):
+ # might be an optimised builtin function => unpack it
+ if not immutable_obj:
+ self.obj = self.obj.coerce_to_pyobject(env)
+ else:
+ error(self.pos,
+ "Object of type '%s' has no attribute '%s'" %
+ (obj_type, self.attribute))
+
+ def wrap_obj_in_nonecheck(self, env):
+ if not env.directives['nonecheck']:
+ return
+
+ msg = None
+ format_args = ()
+ if (self.obj.type.is_extension_type and self.needs_none_check and not
+ self.is_py_attr):
msg = "'NoneType' object has no attribute '%{0}s'".format('.30' if len(self.attribute) <= 30 else '')
- format_args = (self.attribute,)
- elif self.obj.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- msg = "Cannot transpose None memoryview slice"
- else:
- entry = self.obj.type.scope.lookup_here(self.attribute)
- if entry:
- # copy/is_c_contig/shape/strides etc
- msg = "Cannot access '%s' attribute of None memoryview slice"
- format_args = (entry.name,)
-
- if msg:
- self.obj = self.obj.as_none_safe_node(msg, 'PyExc_AttributeError',
- format_args=format_args)
-
- def nogil_check(self, env):
- if self.is_py_attr:
- self.gil_error()
-
- gil_message = "Accessing Python attribute"
-
+ format_args = (self.attribute,)
+ elif self.obj.type.is_memoryviewslice:
+ if self.is_memslice_transpose:
+ msg = "Cannot transpose None memoryview slice"
+ else:
+ entry = self.obj.type.scope.lookup_here(self.attribute)
+ if entry:
+ # copy/is_c_contig/shape/strides etc
+ msg = "Cannot access '%s' attribute of None memoryview slice"
+ format_args = (entry.name,)
+
+ if msg:
+ self.obj = self.obj.as_none_safe_node(msg, 'PyExc_AttributeError',
+ format_args=format_args)
+
+ def nogil_check(self, env):
+ if self.is_py_attr:
+ self.gil_error()
+
+ gil_message = "Accessing Python attribute"
+
def is_cimported_module_without_shadow(self, env):
return self.obj.is_cimported_module_without_shadow(env)
- def is_simple(self):
- if self.obj:
- return self.result_in_temp() or self.obj.is_simple()
- else:
- return NameNode.is_simple(self)
-
- def is_lvalue(self):
- if self.obj:
+ def is_simple(self):
+ if self.obj:
+ return self.result_in_temp() or self.obj.is_simple()
+ else:
+ return NameNode.is_simple(self)
+
+ def is_lvalue(self):
+ if self.obj:
return True
- else:
- return NameNode.is_lvalue(self)
-
- def is_ephemeral(self):
- if self.obj:
- return self.obj.is_ephemeral()
- else:
- return NameNode.is_ephemeral(self)
-
- def calculate_result_code(self):
- #print "AttributeNode.calculate_result_code:", self.member ###
- #print "...obj node =", self.obj, "code", self.obj.result() ###
- #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
- obj = self.obj
- obj_code = obj.result_as(obj.type)
- #print "...obj_code =", obj_code ###
- if self.entry and self.entry.is_cmethod:
- if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
- if self.entry.final_func_cname:
- return self.entry.final_func_cname
-
- if self.type.from_fused:
- # If the attribute was specialized through indexing, make
- # sure to get the right fused name, as our entry was
- # replaced by our parent index node
- # (AnalyseExpressionsTransform)
- self.member = self.entry.cname
-
- return "((struct %s *)%s%s%s)->%s" % (
- obj.type.vtabstruct_cname, obj_code, self.op,
- obj.type.vtabslot_cname, self.member)
- elif self.result_is_used:
- return self.member
- # Generating no code at all for unused access to optimised builtin
- # methods fixes the problem that some optimisations only exist as
- # macros, i.e. there is no function pointer to them, so we would
- # generate invalid C code here.
- return
- elif obj.type.is_complex:
- return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
- else:
- if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
- # accessing a field of a builtin type, need to cast better than result_as() does
- obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
- return "%s%s%s" % (obj_code, self.op, self.member)
-
- def generate_result_code(self, code):
- if self.is_py_attr:
- if self.is_special_lookup:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_LookupSpecial'
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_GetAttrStr'
- code.putln(
- '%s = %s(%s, %s); %s' % (
- self.result(),
- lookup_func_name,
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- # transpose the slice
- for access, packing in self.type.axes:
- if access == 'ptr':
- error(self.pos, "Transposing not supported for slices "
- "with indirect dimensions")
- return
-
- code.putln("%s = %s;" % (self.result(), self.obj.result()))
+ else:
+ return NameNode.is_lvalue(self)
+
+ def is_ephemeral(self):
+ if self.obj:
+ return self.obj.is_ephemeral()
+ else:
+ return NameNode.is_ephemeral(self)
+
+ def calculate_result_code(self):
+ #print "AttributeNode.calculate_result_code:", self.member ###
+ #print "...obj node =", self.obj, "code", self.obj.result() ###
+ #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
+ obj = self.obj
+ obj_code = obj.result_as(obj.type)
+ #print "...obj_code =", obj_code ###
+ if self.entry and self.entry.is_cmethod:
+ if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
+ if self.entry.final_func_cname:
+ return self.entry.final_func_cname
+
+ if self.type.from_fused:
+ # If the attribute was specialized through indexing, make
+ # sure to get the right fused name, as our entry was
+ # replaced by our parent index node
+ # (AnalyseExpressionsTransform)
+ self.member = self.entry.cname
+
+ return "((struct %s *)%s%s%s)->%s" % (
+ obj.type.vtabstruct_cname, obj_code, self.op,
+ obj.type.vtabslot_cname, self.member)
+ elif self.result_is_used:
+ return self.member
+ # Generating no code at all for unused access to optimised builtin
+ # methods fixes the problem that some optimisations only exist as
+ # macros, i.e. there is no function pointer to them, so we would
+ # generate invalid C code here.
+ return
+ elif obj.type.is_complex:
+ return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
+ else:
+ if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
+ # accessing a field of a builtin type, need to cast better than result_as() does
+ obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
+ return "%s%s%s" % (obj_code, self.op, self.member)
+
+ def generate_result_code(self, code):
+ if self.is_py_attr:
+ if self.is_special_lookup:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
+ lookup_func_name = '__Pyx_PyObject_LookupSpecial'
+ else:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
+ lookup_func_name = '__Pyx_PyObject_GetAttrStr'
+ code.putln(
+ '%s = %s(%s, %s); %s' % (
+ self.result(),
+ lookup_func_name,
+ self.obj.py_result(),
+ code.intern_identifier(self.attribute),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ elif self.type.is_memoryviewslice:
+ if self.is_memslice_transpose:
+ # transpose the slice
+ for access, packing in self.type.axes:
+ if access == 'ptr':
+ error(self.pos, "Transposing not supported for slices "
+ "with indirect dimensions")
+ return
+
+ code.putln("%s = %s;" % (self.result(), self.obj.result()))
code.put_incref_memoryviewslice(self.result(), have_gil=True)
-
- T = "__pyx_memslice_transpose(&%s) == 0"
- code.putln(code.error_goto_if(T % self.result(), self.pos))
- elif self.initialized_check:
- code.putln(
- 'if (unlikely(!%s.memview)) {'
- 'PyErr_SetString(PyExc_AttributeError,'
- '"Memoryview is not initialized");'
- '%s'
- '}' % (self.result(), code.error_goto(self.pos)))
- else:
- # result_code contains what is needed, but we may need to insert
- # a check and raise an exception
+
+ T = "__pyx_memslice_transpose(&%s) == 0"
+ code.putln(code.error_goto_if(T % self.result(), self.pos))
+ elif self.initialized_check:
+ code.putln(
+ 'if (unlikely(!%s.memview)) {'
+ 'PyErr_SetString(PyExc_AttributeError,'
+ '"Memoryview is not initialized");'
+ '%s'
+ '}' % (self.result(), code.error_goto(self.pos)))
+ else:
+ # result_code contains what is needed, but we may need to insert
+ # a check and raise an exception
if self.obj.type and self.obj.type.is_extension_type:
- pass
+ pass
elif self.entry and self.entry.is_cmethod:
- # C method implemented as function call with utility code
+ # C method implemented as function call with utility code
code.globalstate.use_entry_utility_code(self.entry)
-
- def generate_disposal_code(self, code):
- if self.is_temp and self.type.is_memoryviewslice and self.is_memslice_transpose:
- # mirror condition for putting the memview incref here:
+
+ def generate_disposal_code(self, code):
+ if self.is_temp and self.type.is_memoryviewslice and self.is_memslice_transpose:
+ # mirror condition for putting the memview incref here:
code.put_xdecref_memoryviewslice(
self.result(), have_gil=True)
code.putln("%s.memview = NULL;" % self.result())
code.putln("%s.data = NULL;" % self.result())
- else:
- ExprNode.generate_disposal_code(self, code)
-
+ else:
+ ExprNode.generate_disposal_code(self, code)
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_SetAttrStr(%s, %s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- rhs.py_result()))
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- elif self.obj.type.is_complex:
- code.putln("__Pyx_SET_C%s(%s, %s);" % (
- self.member.upper(),
- self.obj.result_as(self.obj.type),
- rhs.result_as(self.ctype())))
+ self.obj.generate_evaluation_code(code)
+ if self.is_py_attr:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
+ code.put_error_if_neg(self.pos,
+ '__Pyx_PyObject_SetAttrStr(%s, %s, %s)' % (
+ self.obj.py_result(),
+ code.intern_identifier(self.attribute),
+ rhs.py_result()))
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
+ elif self.obj.type.is_complex:
+ code.putln("__Pyx_SET_C%s(%s, %s);" % (
+ self.member.upper(),
+ self.obj.result_as(self.obj.type),
+ rhs.result_as(self.ctype())))
rhs.generate_disposal_code(code)
rhs.free_temps(code)
- else:
- select_code = self.result()
- if self.type.is_pyobject and self.use_managed_ref:
- rhs.make_owned_reference(code)
- code.put_giveref(rhs.py_result())
- code.put_gotref(select_code)
- code.put_decref(select_code, self.ctype())
- elif self.type.is_memoryviewslice:
- from . import MemoryView
- MemoryView.put_assign_to_memviewslice(
- select_code, rhs, rhs.result(), self.type, code)
-
- if not self.type.is_memoryviewslice:
- code.putln(
- "%s = %s;" % (
- select_code,
- rhs.result_as(self.ctype())))
- #rhs.result()))
- rhs.generate_post_assignment_code(code)
- rhs.free_temps(code)
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
-
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr or (self.entry.scope.is_property_scope
- and u'__del__' in self.entry.scope.entries):
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute)))
- else:
- error(self.pos, "Cannot delete C attribute of extension type")
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
-
- def annotate(self, code):
- if self.is_py_attr:
- style, text = 'py_attr', 'python attribute (%s)'
- else:
- style, text = 'c_attr', 'c attribute (%s)'
- code.annotate(self.pos, AnnotationItem(style, text % self.type, size=len(self.attribute)))
-
-
-#-------------------------------------------------------------------
-#
-# Constructor nodes
-#
-#-------------------------------------------------------------------
-
+ else:
+ select_code = self.result()
+ if self.type.is_pyobject and self.use_managed_ref:
+ rhs.make_owned_reference(code)
+ code.put_giveref(rhs.py_result())
+ code.put_gotref(select_code)
+ code.put_decref(select_code, self.ctype())
+ elif self.type.is_memoryviewslice:
+ from . import MemoryView
+ MemoryView.put_assign_to_memviewslice(
+ select_code, rhs, rhs.result(), self.type, code)
+
+ if not self.type.is_memoryviewslice:
+ code.putln(
+ "%s = %s;" % (
+ select_code,
+ rhs.result_as(self.ctype())))
+ #rhs.result()))
+ rhs.generate_post_assignment_code(code)
+ rhs.free_temps(code)
+ self.obj.generate_disposal_code(code)
+ self.obj.free_temps(code)
+
+ def generate_deletion_code(self, code, ignore_nonexisting=False):
+ self.obj.generate_evaluation_code(code)
+ if self.is_py_attr or (self.entry.scope.is_property_scope
+ and u'__del__' in self.entry.scope.entries):
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
+ code.put_error_if_neg(self.pos,
+ '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
+ self.obj.py_result(),
+ code.intern_identifier(self.attribute)))
+ else:
+ error(self.pos, "Cannot delete C attribute of extension type")
+ self.obj.generate_disposal_code(code)
+ self.obj.free_temps(code)
+
+ def annotate(self, code):
+ if self.is_py_attr:
+ style, text = 'py_attr', 'python attribute (%s)'
+ else:
+ style, text = 'c_attr', 'c attribute (%s)'
+ code.annotate(self.pos, AnnotationItem(style, text % self.type, size=len(self.attribute)))
+
+
+#-------------------------------------------------------------------
+#
+# Constructor nodes
+#
+#-------------------------------------------------------------------
+
class StarredUnpackingNode(ExprNode):
- # A starred expression like "*a"
- #
+ # A starred expression like "*a"
+ #
# This is only allowed in sequence assignment or construction such as
- #
- # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
- #
+ #
+ # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
+ #
# and will be special cased during type analysis (or generate an error
- # if it's found at unexpected places).
- #
- # target ExprNode
-
- subexprs = ['target']
- is_starred = 1
- type = py_object_type
- is_temp = 1
+ # if it's found at unexpected places).
+ #
+ # target ExprNode
+
+ subexprs = ['target']
+ is_starred = 1
+ type = py_object_type
+ is_temp = 1
starred_expr_allowed_here = False
-
- def __init__(self, pos, target):
+
+ def __init__(self, pos, target):
ExprNode.__init__(self, pos, target=target)
-
- def analyse_declarations(self, env):
+
+ def analyse_declarations(self, env):
if not self.starred_expr_allowed_here:
error(self.pos, "starred expression is not allowed here")
- self.target.analyse_declarations(env)
-
+ self.target.analyse_declarations(env)
+
def infer_type(self, env):
return self.target.infer_type(env)
- def analyse_types(self, env):
+ def analyse_types(self, env):
if not self.starred_expr_allowed_here:
error(self.pos, "starred expression is not allowed here")
- self.target = self.target.analyse_types(env)
- self.type = self.target.type
- return self
-
- def analyse_target_declaration(self, env):
- self.target.analyse_target_declaration(env)
-
- def analyse_target_types(self, env):
- self.target = self.target.analyse_target_types(env)
- self.type = self.target.type
- return self
-
- def calculate_result_code(self):
- return ""
-
- def generate_result_code(self, code):
- pass
-
-
-class SequenceNode(ExprNode):
- # Base class for list and tuple constructor nodes.
- # Contains common code for performing sequence unpacking.
- #
- # args [ExprNode]
- # unpacked_items [ExprNode] or None
- # coerced_unpacked_items [ExprNode] or None
- # mult_factor ExprNode the integer number of content repetitions ([1,2]*3)
-
- subexprs = ['args', 'mult_factor']
-
- is_sequence_constructor = 1
- unpacked_items = None
- mult_factor = None
- slow = False # trade speed for code size (e.g. use PyTuple_Pack())
-
- def compile_time_value_list(self, denv):
- return [arg.compile_time_value(denv) for arg in self.args]
-
- def replace_starred_target_node(self):
- # replace a starred node in the targets by the contained expression
- self.starred_assignment = False
- args = []
- for arg in self.args:
- if arg.is_starred:
- if self.starred_assignment:
- error(arg.pos, "more than 1 starred expression in assignment")
- self.starred_assignment = True
- arg = arg.target
- arg.is_starred = True
- args.append(arg)
- self.args = args
-
- def analyse_target_declaration(self, env):
- self.replace_starred_target_node()
- for arg in self.args:
- arg.analyse_target_declaration(env)
-
- def analyse_types(self, env, skip_children=False):
+ self.target = self.target.analyse_types(env)
+ self.type = self.target.type
+ return self
+
+ def analyse_target_declaration(self, env):
+ self.target.analyse_target_declaration(env)
+
+ def analyse_target_types(self, env):
+ self.target = self.target.analyse_target_types(env)
+ self.type = self.target.type
+ return self
+
+ def calculate_result_code(self):
+ return ""
+
+ def generate_result_code(self, code):
+ pass
+
+
+class SequenceNode(ExprNode):
+ # Base class for list and tuple constructor nodes.
+ # Contains common code for performing sequence unpacking.
+ #
+ # args [ExprNode]
+ # unpacked_items [ExprNode] or None
+ # coerced_unpacked_items [ExprNode] or None
+ # mult_factor ExprNode the integer number of content repetitions ([1,2]*3)
+
+ subexprs = ['args', 'mult_factor']
+
+ is_sequence_constructor = 1
+ unpacked_items = None
+ mult_factor = None
+ slow = False # trade speed for code size (e.g. use PyTuple_Pack())
+
+ def compile_time_value_list(self, denv):
+ return [arg.compile_time_value(denv) for arg in self.args]
+
+ def replace_starred_target_node(self):
+ # replace a starred node in the targets by the contained expression
+ self.starred_assignment = False
+ args = []
+ for arg in self.args:
+ if arg.is_starred:
+ if self.starred_assignment:
+ error(arg.pos, "more than 1 starred expression in assignment")
+ self.starred_assignment = True
+ arg = arg.target
+ arg.is_starred = True
+ args.append(arg)
+ self.args = args
+
+ def analyse_target_declaration(self, env):
+ self.replace_starred_target_node()
+ for arg in self.args:
+ arg.analyse_target_declaration(env)
+
+ def analyse_types(self, env, skip_children=False):
for i, arg in enumerate(self.args):
if not skip_children:
arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- if self.mult_factor:
- self.mult_factor = self.mult_factor.analyse_types(env)
- if not self.mult_factor.type.is_int:
- self.mult_factor = self.mult_factor.coerce_to_pyobject(env)
- self.is_temp = 1
- # not setting self.type here, subtypes do this
- return self
-
+ self.args[i] = arg.coerce_to_pyobject(env)
+ if self.mult_factor:
+ self.mult_factor = self.mult_factor.analyse_types(env)
+ if not self.mult_factor.type.is_int:
+ self.mult_factor = self.mult_factor.coerce_to_pyobject(env)
+ self.is_temp = 1
+ # not setting self.type here, subtypes do this
+ return self
+
def coerce_to_ctuple(self, dst_type, env):
if self.type == dst_type:
return self
@@ -7478,436 +7478,436 @@ class SequenceNode(ExprNode):
args.append(arg)
self.args[:] = args
- def may_be_none(self):
- return False
-
- def analyse_target_types(self, env):
- if self.mult_factor:
- error(self.pos, "can't assign to multiplied sequence")
- self.unpacked_items = []
- self.coerced_unpacked_items = []
- self.any_coerced_items = False
- for i, arg in enumerate(self.args):
- arg = self.args[i] = arg.analyse_target_types(env)
- if arg.is_starred:
+ def may_be_none(self):
+ return False
+
+ def analyse_target_types(self, env):
+ if self.mult_factor:
+ error(self.pos, "can't assign to multiplied sequence")
+ self.unpacked_items = []
+ self.coerced_unpacked_items = []
+ self.any_coerced_items = False
+ for i, arg in enumerate(self.args):
+ arg = self.args[i] = arg.analyse_target_types(env)
+ if arg.is_starred:
if not arg.type.assignable_from(list_type):
- error(arg.pos,
- "starred target must have Python object (list) type")
- if arg.type is py_object_type:
+ error(arg.pos,
+ "starred target must have Python object (list) type")
+ if arg.type is py_object_type:
arg.type = list_type
- unpacked_item = PyTempNode(self.pos, env)
- coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
- if unpacked_item is not coerced_unpacked_item:
- self.any_coerced_items = True
- self.unpacked_items.append(unpacked_item)
- self.coerced_unpacked_items.append(coerced_unpacked_item)
- self.type = py_object_type
- return self
-
- def generate_result_code(self, code):
- self.generate_operation_code(code)
-
- def generate_sequence_packing_code(self, code, target=None, plain=False):
- if target is None:
- target = self.result()
- size_factor = c_mult = ''
- mult_factor = None
-
- if self.mult_factor and not plain:
- mult_factor = self.mult_factor
- if mult_factor.type.is_int:
- c_mult = mult_factor.result()
+ unpacked_item = PyTempNode(self.pos, env)
+ coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
+ if unpacked_item is not coerced_unpacked_item:
+ self.any_coerced_items = True
+ self.unpacked_items.append(unpacked_item)
+ self.coerced_unpacked_items.append(coerced_unpacked_item)
+ self.type = py_object_type
+ return self
+
+ def generate_result_code(self, code):
+ self.generate_operation_code(code)
+
+ def generate_sequence_packing_code(self, code, target=None, plain=False):
+ if target is None:
+ target = self.result()
+ size_factor = c_mult = ''
+ mult_factor = None
+
+ if self.mult_factor and not plain:
+ mult_factor = self.mult_factor
+ if mult_factor.type.is_int:
+ c_mult = mult_factor.result()
if (isinstance(mult_factor.constant_result, _py_int_types) and
mult_factor.constant_result > 0):
- size_factor = ' * %s' % mult_factor.constant_result
+ size_factor = ' * %s' % mult_factor.constant_result
elif mult_factor.type.signed:
size_factor = ' * ((%s<0) ? 0:%s)' % (c_mult, c_mult)
- else:
+ else:
size_factor = ' * (%s)' % (c_mult,)
-
+
if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult:
- # use PyTuple_Pack() to avoid generating huge amounts of one-time code
- code.putln('%s = PyTuple_Pack(%d, %s); %s' % (
- target,
- len(self.args),
+ # use PyTuple_Pack() to avoid generating huge amounts of one-time code
+ code.putln('%s = PyTuple_Pack(%d, %s); %s' % (
+ target,
+ len(self.args),
', '.join(arg.py_result() for arg in self.args),
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
+ code.error_goto_if_null(target, self.pos)))
+ code.put_gotref(target)
elif self.type.is_ctuple:
for i, arg in enumerate(self.args):
code.putln("%s.f%s = %s;" % (
target, i, arg.result()))
- else:
- # build the tuple/list step by step, potentially multiplying it as we go
+ else:
+ # build the tuple/list step by step, potentially multiplying it as we go
if self.type is list_type:
- create_func, set_item_func = 'PyList_New', 'PyList_SET_ITEM'
+ create_func, set_item_func = 'PyList_New', 'PyList_SET_ITEM'
elif self.type is tuple_type:
- create_func, set_item_func = 'PyTuple_New', 'PyTuple_SET_ITEM'
- else:
- raise InternalError("sequence packing for unexpected type %s" % self.type)
- arg_count = len(self.args)
- code.putln("%s = %s(%s%s); %s" % (
- target, create_func, arg_count, size_factor,
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
-
- if c_mult:
- # FIXME: can't use a temp variable here as the code may
- # end up in the constant building function. Temps
- # currently don't work there.
-
- #counter = code.funcstate.allocate_temp(mult_factor.type, manage_ref=False)
- counter = Naming.quick_temp_cname
- code.putln('{ Py_ssize_t %s;' % counter)
- if arg_count == 1:
- offset = counter
- else:
- offset = '%s * %s' % (counter, arg_count)
- code.putln('for (%s=0; %s < %s; %s++) {' % (
- counter, counter, c_mult, counter
- ))
- else:
- offset = ''
-
+ create_func, set_item_func = 'PyTuple_New', 'PyTuple_SET_ITEM'
+ else:
+ raise InternalError("sequence packing for unexpected type %s" % self.type)
+ arg_count = len(self.args)
+ code.putln("%s = %s(%s%s); %s" % (
+ target, create_func, arg_count, size_factor,
+ code.error_goto_if_null(target, self.pos)))
+ code.put_gotref(target)
+
+ if c_mult:
+ # FIXME: can't use a temp variable here as the code may
+ # end up in the constant building function. Temps
+ # currently don't work there.
+
+ #counter = code.funcstate.allocate_temp(mult_factor.type, manage_ref=False)
+ counter = Naming.quick_temp_cname
+ code.putln('{ Py_ssize_t %s;' % counter)
+ if arg_count == 1:
+ offset = counter
+ else:
+ offset = '%s * %s' % (counter, arg_count)
+ code.putln('for (%s=0; %s < %s; %s++) {' % (
+ counter, counter, c_mult, counter
+ ))
+ else:
+ offset = ''
+
for i in range(arg_count):
- arg = self.args[i]
- if c_mult or not arg.result_in_temp():
- code.put_incref(arg.result(), arg.ctype())
+ arg = self.args[i]
+ if c_mult or not arg.result_in_temp():
+ code.put_incref(arg.result(), arg.ctype())
code.put_giveref(arg.py_result())
- code.putln("%s(%s, %s, %s);" % (
- set_item_func,
- target,
- (offset and i) and ('%s + %s' % (offset, i)) or (offset or i),
- arg.py_result()))
-
- if c_mult:
- code.putln('}')
- #code.funcstate.release_temp(counter)
- code.putln('}')
-
- if mult_factor is not None and mult_factor.type.is_pyobject:
- code.putln('{ PyObject* %s = PyNumber_InPlaceMultiply(%s, %s); %s' % (
- Naming.quick_temp_cname, target, mult_factor.py_result(),
- code.error_goto_if_null(Naming.quick_temp_cname, self.pos)
- ))
- code.put_gotref(Naming.quick_temp_cname)
- code.put_decref(target, py_object_type)
- code.putln('%s = %s;' % (target, Naming.quick_temp_cname))
- code.putln('}')
-
- def generate_subexpr_disposal_code(self, code):
- if self.mult_factor and self.mult_factor.type.is_int:
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
+ code.putln("%s(%s, %s, %s);" % (
+ set_item_func,
+ target,
+ (offset and i) and ('%s + %s' % (offset, i)) or (offset or i),
+ arg.py_result()))
+
+ if c_mult:
+ code.putln('}')
+ #code.funcstate.release_temp(counter)
+ code.putln('}')
+
+ if mult_factor is not None and mult_factor.type.is_pyobject:
+ code.putln('{ PyObject* %s = PyNumber_InPlaceMultiply(%s, %s); %s' % (
+ Naming.quick_temp_cname, target, mult_factor.py_result(),
+ code.error_goto_if_null(Naming.quick_temp_cname, self.pos)
+ ))
+ code.put_gotref(Naming.quick_temp_cname)
+ code.put_decref(target, py_object_type)
+ code.putln('%s = %s;' % (target, Naming.quick_temp_cname))
+ code.putln('}')
+
+ def generate_subexpr_disposal_code(self, code):
+ if self.mult_factor and self.mult_factor.type.is_int:
+ super(SequenceNode, self).generate_subexpr_disposal_code(code)
elif self.type is tuple_type and (self.is_literal or self.slow):
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
- else:
- # We call generate_post_assignment_code here instead
- # of generate_disposal_code, because values were stored
- # in the tuple using a reference-stealing operation.
- for arg in self.args:
- arg.generate_post_assignment_code(code)
- # Should NOT call free_temps -- this is invoked by the default
- # generate_evaluation_code which will do that.
- if self.mult_factor:
- self.mult_factor.generate_disposal_code(code)
-
+ super(SequenceNode, self).generate_subexpr_disposal_code(code)
+ else:
+ # We call generate_post_assignment_code here instead
+ # of generate_disposal_code, because values were stored
+ # in the tuple using a reference-stealing operation.
+ for arg in self.args:
+ arg.generate_post_assignment_code(code)
+ # Should NOT call free_temps -- this is invoked by the default
+ # generate_evaluation_code which will do that.
+ if self.mult_factor:
+ self.mult_factor.generate_disposal_code(code)
+
def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
exception_check=None, exception_value=None):
- if self.starred_assignment:
- self.generate_starred_assignment_code(rhs, code)
- else:
- self.generate_parallel_assignment_code(rhs, code)
-
- for item in self.unpacked_items:
- item.release(code)
- rhs.free_temps(code)
-
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
-
- def generate_parallel_assignment_code(self, rhs, code):
- # Need to work around the fact that generate_evaluation_code
- # allocates the temps in a rather hacky way -- the assignment
- # is evaluated twice, within each if-block.
- for item in self.unpacked_items:
- item.allocate(code)
- special_unpack = (rhs.type is py_object_type
- or rhs.type in (tuple_type, list_type)
- or not rhs.type.is_builtin_type)
- long_enough_for_a_loop = len(self.unpacked_items) > 3
-
- if special_unpack:
- self.generate_special_parallel_unpacking_code(
- code, rhs, use_loop=long_enough_for_a_loop)
- else:
- code.putln("{")
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=long_enough_for_a_loop)
- code.putln("}")
-
- for value_node in self.coerced_unpacked_items:
- value_node.generate_evaluation_code(code)
- for i in range(len(self.args)):
- self.args[i].generate_assignment_code(
- self.coerced_unpacked_items[i], code)
-
- def generate_special_parallel_unpacking_code(self, code, rhs, use_loop):
- sequence_type_test = '1'
- none_check = "likely(%s != Py_None)" % rhs.py_result()
- if rhs.type is list_type:
- sequence_types = ['List']
- if rhs.may_be_none():
- sequence_type_test = none_check
- elif rhs.type is tuple_type:
- sequence_types = ['Tuple']
- if rhs.may_be_none():
- sequence_type_test = none_check
- else:
- sequence_types = ['Tuple', 'List']
- tuple_check = 'likely(PyTuple_CheckExact(%s))' % rhs.py_result()
- list_check = 'PyList_CheckExact(%s)' % rhs.py_result()
- sequence_type_test = "(%s) || (%s)" % (tuple_check, list_check)
-
- code.putln("if (%s) {" % sequence_type_test)
- code.putln("PyObject* sequence = %s;" % rhs.py_result())
-
- # list/tuple => check size
+ if self.starred_assignment:
+ self.generate_starred_assignment_code(rhs, code)
+ else:
+ self.generate_parallel_assignment_code(rhs, code)
+
+ for item in self.unpacked_items:
+ item.release(code)
+ rhs.free_temps(code)
+
+ _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
+ PyrexTypes.py_object_type, [
+ PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
+ ]))
+
+ def generate_parallel_assignment_code(self, rhs, code):
+ # Need to work around the fact that generate_evaluation_code
+ # allocates the temps in a rather hacky way -- the assignment
+ # is evaluated twice, within each if-block.
+ for item in self.unpacked_items:
+ item.allocate(code)
+ special_unpack = (rhs.type is py_object_type
+ or rhs.type in (tuple_type, list_type)
+ or not rhs.type.is_builtin_type)
+ long_enough_for_a_loop = len(self.unpacked_items) > 3
+
+ if special_unpack:
+ self.generate_special_parallel_unpacking_code(
+ code, rhs, use_loop=long_enough_for_a_loop)
+ else:
+ code.putln("{")
+ self.generate_generic_parallel_unpacking_code(
+ code, rhs, self.unpacked_items, use_loop=long_enough_for_a_loop)
+ code.putln("}")
+
+ for value_node in self.coerced_unpacked_items:
+ value_node.generate_evaluation_code(code)
+ for i in range(len(self.args)):
+ self.args[i].generate_assignment_code(
+ self.coerced_unpacked_items[i], code)
+
+ def generate_special_parallel_unpacking_code(self, code, rhs, use_loop):
+ sequence_type_test = '1'
+ none_check = "likely(%s != Py_None)" % rhs.py_result()
+ if rhs.type is list_type:
+ sequence_types = ['List']
+ if rhs.may_be_none():
+ sequence_type_test = none_check
+ elif rhs.type is tuple_type:
+ sequence_types = ['Tuple']
+ if rhs.may_be_none():
+ sequence_type_test = none_check
+ else:
+ sequence_types = ['Tuple', 'List']
+ tuple_check = 'likely(PyTuple_CheckExact(%s))' % rhs.py_result()
+ list_check = 'PyList_CheckExact(%s)' % rhs.py_result()
+ sequence_type_test = "(%s) || (%s)" % (tuple_check, list_check)
+
+ code.putln("if (%s) {" % sequence_type_test)
+ code.putln("PyObject* sequence = %s;" % rhs.py_result())
+
+ # list/tuple => check size
code.putln("Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);")
- code.putln("if (unlikely(size != %d)) {" % len(self.args))
- code.globalstate.use_utility_code(raise_too_many_values_to_unpack)
- code.putln("if (size > %d) __Pyx_RaiseTooManyValuesError(%d);" % (
- len(self.args), len(self.args)))
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.putln("else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);")
+ code.putln("if (unlikely(size != %d)) {" % len(self.args))
+ code.globalstate.use_utility_code(raise_too_many_values_to_unpack)
+ code.putln("if (size > %d) __Pyx_RaiseTooManyValuesError(%d);" % (
+ len(self.args), len(self.args)))
+ code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
+ code.putln("else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);")
# < 0 => exception
- code.putln(code.error_goto(self.pos))
- code.putln("}")
-
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+
code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- # unpack items from list/tuple in unrolled loop (can't fail)
- if len(sequence_types) == 2:
- code.putln("if (likely(Py%s_CheckExact(sequence))) {" % sequence_types[0])
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[0], i))
- if len(sequence_types) == 2:
- code.putln("} else {")
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[1], i))
- code.putln("}")
- for item in self.unpacked_items:
- code.put_incref(item.result(), item.ctype())
-
- code.putln("#else")
- # in non-CPython, use the PySequence protocol (which can fail)
- if not use_loop:
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = PySequence_ITEM(sequence, %d); %s" % (
- item.result(), i,
- code.error_goto_if_null(item.result(), self.pos)))
- code.put_gotref(item.result())
- else:
- code.putln("{")
- code.putln("Py_ssize_t i;")
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in self.unpacked_items])))
- code.putln("for (i=0; i < %s; i++) {" % len(self.unpacked_items))
- code.putln("PyObject* item = PySequence_ITEM(sequence, i); %s" % (
- code.error_goto_if_null('item', self.pos)))
- code.put_gotref('item')
- code.putln("*(temps[i]) = item;")
- code.putln("}")
- code.putln("}")
-
- code.putln("#endif")
- rhs.generate_disposal_code(code)
-
- if sequence_type_test == '1':
- code.putln("}") # all done
- elif sequence_type_test == none_check:
- # either tuple/list or None => save some code by generating the error directly
- code.putln("} else {")
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("RaiseNoneIterError", "ObjectHandling.c"))
- code.putln("__Pyx_RaiseNoneNotIterableError(); %s" % code.error_goto(self.pos))
- code.putln("}") # all done
- else:
- code.putln("} else {") # needs iteration fallback code
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=use_loop)
- code.putln("}")
-
- def generate_generic_parallel_unpacking_code(self, code, rhs, unpacked_items, use_loop, terminate=True):
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
- code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
-
- if use_loop:
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in unpacked_items])))
-
- iterator_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln(
- "%s = PyObject_GetIter(%s); %s" % (
- iterator_temp,
- rhs.py_result(),
- code.error_goto_if_null(iterator_temp, self.pos)))
- code.put_gotref(iterator_temp)
- rhs.generate_disposal_code(code)
-
- iternext_func = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- code.putln("%s = Py_TYPE(%s)->tp_iternext;" % (
- iternext_func, iterator_temp))
-
- unpacking_error_label = code.new_label('unpacking_failed')
- unpack_code = "%s(%s)" % (iternext_func, iterator_temp)
- if use_loop:
- code.putln("for (index=0; index < %s; index++) {" % len(unpacked_items))
- code.put("PyObject* item = %s; if (unlikely(!item)) " % unpack_code)
- code.put_goto(unpacking_error_label)
- code.put_gotref("item")
- code.putln("*(temps[index]) = item;")
- code.putln("}")
- else:
- for i, item in enumerate(unpacked_items):
- code.put(
- "index = %d; %s = %s; if (unlikely(!%s)) " % (
- i,
- item.result(),
- unpack_code,
- item.result()))
- code.put_goto(unpacking_error_label)
- code.put_gotref(item.py_result())
-
- if terminate:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("UnpackItemEndCheck", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos, "__Pyx_IternextUnpackEndCheck(%s, %d)" % (
- unpack_code,
- len(unpacked_items)))
- code.putln("%s = NULL;" % iternext_func)
- code.put_decref_clear(iterator_temp, py_object_type)
-
- unpacking_done_label = code.new_label('unpacking_done')
- code.put_goto(unpacking_done_label)
-
- code.put_label(unpacking_error_label)
- code.put_decref_clear(iterator_temp, py_object_type)
- code.putln("%s = NULL;" % iternext_func)
- code.putln("if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);")
- code.putln(code.error_goto(self.pos))
- code.put_label(unpacking_done_label)
-
- code.funcstate.release_temp(iternext_func)
- if terminate:
- code.funcstate.release_temp(iterator_temp)
- iterator_temp = None
-
- return iterator_temp
-
- def generate_starred_assignment_code(self, rhs, code):
- for i, arg in enumerate(self.args):
- if arg.is_starred:
- starred_target = self.unpacked_items[i]
- unpacked_fixed_items_left = self.unpacked_items[:i]
- unpacked_fixed_items_right = self.unpacked_items[i+1:]
- break
- else:
- assert False
-
- iterator_temp = None
- if unpacked_fixed_items_left:
- for item in unpacked_fixed_items_left:
- item.allocate(code)
- code.putln('{')
- iterator_temp = self.generate_generic_parallel_unpacking_code(
- code, rhs, unpacked_fixed_items_left,
- use_loop=True, terminate=False)
- for i, item in enumerate(unpacked_fixed_items_left):
- value_node = self.coerced_unpacked_items[i]
- value_node.generate_evaluation_code(code)
- code.putln('}')
-
- starred_target.allocate(code)
- target_list = starred_target.result()
- code.putln("%s = PySequence_List(%s); %s" % (
- target_list,
- iterator_temp or rhs.py_result(),
- code.error_goto_if_null(target_list, self.pos)))
- code.put_gotref(target_list)
-
- if iterator_temp:
- code.put_decref_clear(iterator_temp, py_object_type)
- code.funcstate.release_temp(iterator_temp)
- else:
- rhs.generate_disposal_code(code)
-
- if unpacked_fixed_items_right:
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list))
- code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right)))
- code.putln("__Pyx_RaiseNeedMoreValuesError(%d+%s); %s" % (
- len(unpacked_fixed_items_left), length_temp,
- code.error_goto(self.pos)))
- code.putln('}')
-
- for item in unpacked_fixed_items_right[::-1]:
- item.allocate(code)
- for i, (item, coerced_arg) in enumerate(zip(unpacked_fixed_items_right[::-1],
- self.coerced_unpacked_items[::-1])):
- code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
- code.putln("%s = PyList_GET_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- # resize the list the hard way
- code.putln("((PyVarObject*)%s)->ob_size--;" % target_list)
- code.putln('#else')
- code.putln("%s = PySequence_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- code.putln('#endif')
- code.put_gotref(item.py_result())
- coerced_arg.generate_evaluation_code(code)
-
- code.putln('#if !CYTHON_COMPILING_IN_CPYTHON')
- sublist_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln('%s = PySequence_GetSlice(%s, 0, %s-%d); %s' % (
- sublist_temp, target_list, length_temp, len(unpacked_fixed_items_right),
- code.error_goto_if_null(sublist_temp, self.pos)))
- code.put_gotref(sublist_temp)
- code.funcstate.release_temp(length_temp)
- code.put_decref(target_list, py_object_type)
- code.putln('%s = %s; %s = NULL;' % (target_list, sublist_temp, sublist_temp))
- code.putln('#else')
+ # unpack items from list/tuple in unrolled loop (can't fail)
+ if len(sequence_types) == 2:
+ code.putln("if (likely(Py%s_CheckExact(sequence))) {" % sequence_types[0])
+ for i, item in enumerate(self.unpacked_items):
+ code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
+ item.result(), sequence_types[0], i))
+ if len(sequence_types) == 2:
+ code.putln("} else {")
+ for i, item in enumerate(self.unpacked_items):
+ code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
+ item.result(), sequence_types[1], i))
+ code.putln("}")
+ for item in self.unpacked_items:
+ code.put_incref(item.result(), item.ctype())
+
+ code.putln("#else")
+ # in non-CPython, use the PySequence protocol (which can fail)
+ if not use_loop:
+ for i, item in enumerate(self.unpacked_items):
+ code.putln("%s = PySequence_ITEM(sequence, %d); %s" % (
+ item.result(), i,
+ code.error_goto_if_null(item.result(), self.pos)))
+ code.put_gotref(item.result())
+ else:
+ code.putln("{")
+ code.putln("Py_ssize_t i;")
+ code.putln("PyObject** temps[%s] = {%s};" % (
+ len(self.unpacked_items),
+ ','.join(['&%s' % item.result() for item in self.unpacked_items])))
+ code.putln("for (i=0; i < %s; i++) {" % len(self.unpacked_items))
+ code.putln("PyObject* item = PySequence_ITEM(sequence, i); %s" % (
+ code.error_goto_if_null('item', self.pos)))
+ code.put_gotref('item')
+ code.putln("*(temps[i]) = item;")
+ code.putln("}")
+ code.putln("}")
+
+ code.putln("#endif")
+ rhs.generate_disposal_code(code)
+
+ if sequence_type_test == '1':
+ code.putln("}") # all done
+ elif sequence_type_test == none_check:
+ # either tuple/list or None => save some code by generating the error directly
+ code.putln("} else {")
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("RaiseNoneIterError", "ObjectHandling.c"))
+ code.putln("__Pyx_RaiseNoneNotIterableError(); %s" % code.error_goto(self.pos))
+ code.putln("}") # all done
+ else:
+ code.putln("} else {") # needs iteration fallback code
+ self.generate_generic_parallel_unpacking_code(
+ code, rhs, self.unpacked_items, use_loop=use_loop)
+ code.putln("}")
+
+ def generate_generic_parallel_unpacking_code(self, code, rhs, unpacked_items, use_loop, terminate=True):
+ code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
+ code.globalstate.use_utility_code(UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
+ code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
+
+ if use_loop:
+ code.putln("PyObject** temps[%s] = {%s};" % (
+ len(self.unpacked_items),
+ ','.join(['&%s' % item.result() for item in unpacked_items])))
+
+ iterator_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
+ code.putln(
+ "%s = PyObject_GetIter(%s); %s" % (
+ iterator_temp,
+ rhs.py_result(),
+ code.error_goto_if_null(iterator_temp, self.pos)))
+ code.put_gotref(iterator_temp)
+ rhs.generate_disposal_code(code)
+
+ iternext_func = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
+ code.putln("%s = Py_TYPE(%s)->tp_iternext;" % (
+ iternext_func, iterator_temp))
+
+ unpacking_error_label = code.new_label('unpacking_failed')
+ unpack_code = "%s(%s)" % (iternext_func, iterator_temp)
+ if use_loop:
+ code.putln("for (index=0; index < %s; index++) {" % len(unpacked_items))
+ code.put("PyObject* item = %s; if (unlikely(!item)) " % unpack_code)
+ code.put_goto(unpacking_error_label)
+ code.put_gotref("item")
+ code.putln("*(temps[index]) = item;")
+ code.putln("}")
+ else:
+ for i, item in enumerate(unpacked_items):
+ code.put(
+ "index = %d; %s = %s; if (unlikely(!%s)) " % (
+ i,
+ item.result(),
+ unpack_code,
+ item.result()))
+ code.put_goto(unpacking_error_label)
+ code.put_gotref(item.py_result())
+
+ if terminate:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("UnpackItemEndCheck", "ObjectHandling.c"))
+ code.put_error_if_neg(self.pos, "__Pyx_IternextUnpackEndCheck(%s, %d)" % (
+ unpack_code,
+ len(unpacked_items)))
+ code.putln("%s = NULL;" % iternext_func)
+ code.put_decref_clear(iterator_temp, py_object_type)
+
+ unpacking_done_label = code.new_label('unpacking_done')
+ code.put_goto(unpacking_done_label)
+
+ code.put_label(unpacking_error_label)
+ code.put_decref_clear(iterator_temp, py_object_type)
+ code.putln("%s = NULL;" % iternext_func)
+ code.putln("if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);")
+ code.putln(code.error_goto(self.pos))
+ code.put_label(unpacking_done_label)
+
+ code.funcstate.release_temp(iternext_func)
+ if terminate:
+ code.funcstate.release_temp(iterator_temp)
+ iterator_temp = None
+
+ return iterator_temp
+
+ def generate_starred_assignment_code(self, rhs, code):
+ for i, arg in enumerate(self.args):
+ if arg.is_starred:
+ starred_target = self.unpacked_items[i]
+ unpacked_fixed_items_left = self.unpacked_items[:i]
+ unpacked_fixed_items_right = self.unpacked_items[i+1:]
+ break
+ else:
+ assert False
+
+ iterator_temp = None
+ if unpacked_fixed_items_left:
+ for item in unpacked_fixed_items_left:
+ item.allocate(code)
+ code.putln('{')
+ iterator_temp = self.generate_generic_parallel_unpacking_code(
+ code, rhs, unpacked_fixed_items_left,
+ use_loop=True, terminate=False)
+ for i, item in enumerate(unpacked_fixed_items_left):
+ value_node = self.coerced_unpacked_items[i]
+ value_node.generate_evaluation_code(code)
+ code.putln('}')
+
+ starred_target.allocate(code)
+ target_list = starred_target.result()
+ code.putln("%s = PySequence_List(%s); %s" % (
+ target_list,
+ iterator_temp or rhs.py_result(),
+ code.error_goto_if_null(target_list, self.pos)))
+ code.put_gotref(target_list)
+
+ if iterator_temp:
+ code.put_decref_clear(iterator_temp, py_object_type)
+ code.funcstate.release_temp(iterator_temp)
+ else:
+ rhs.generate_disposal_code(code)
+
+ if unpacked_fixed_items_right:
+ code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
+ length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
+ code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list))
+ code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right)))
+ code.putln("__Pyx_RaiseNeedMoreValuesError(%d+%s); %s" % (
+ len(unpacked_fixed_items_left), length_temp,
+ code.error_goto(self.pos)))
+ code.putln('}')
+
+ for item in unpacked_fixed_items_right[::-1]:
+ item.allocate(code)
+ for i, (item, coerced_arg) in enumerate(zip(unpacked_fixed_items_right[::-1],
+ self.coerced_unpacked_items[::-1])):
+ code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
+ code.putln("%s = PyList_GET_ITEM(%s, %s-%d); " % (
+ item.py_result(), target_list, length_temp, i+1))
+ # resize the list the hard way
+ code.putln("((PyVarObject*)%s)->ob_size--;" % target_list)
+ code.putln('#else')
+ code.putln("%s = PySequence_ITEM(%s, %s-%d); " % (
+ item.py_result(), target_list, length_temp, i+1))
+ code.putln('#endif')
+ code.put_gotref(item.py_result())
+ coerced_arg.generate_evaluation_code(code)
+
+ code.putln('#if !CYTHON_COMPILING_IN_CPYTHON')
+ sublist_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
+ code.putln('%s = PySequence_GetSlice(%s, 0, %s-%d); %s' % (
+ sublist_temp, target_list, length_temp, len(unpacked_fixed_items_right),
+ code.error_goto_if_null(sublist_temp, self.pos)))
+ code.put_gotref(sublist_temp)
+ code.funcstate.release_temp(length_temp)
+ code.put_decref(target_list, py_object_type)
+ code.putln('%s = %s; %s = NULL;' % (target_list, sublist_temp, sublist_temp))
+ code.putln('#else')
code.putln('(void)%s;' % sublist_temp) # avoid warning about unused variable
- code.funcstate.release_temp(sublist_temp)
- code.putln('#endif')
-
- for i, arg in enumerate(self.args):
- arg.generate_assignment_code(self.coerced_unpacked_items[i], code)
-
- def annotate(self, code):
- for arg in self.args:
- arg.annotate(code)
- if self.unpacked_items:
- for arg in self.unpacked_items:
- arg.annotate(code)
- for arg in self.coerced_unpacked_items:
- arg.annotate(code)
-
-
-class TupleNode(SequenceNode):
- # Tuple constructor.
-
- type = tuple_type
- is_partly_literal = False
-
- gil_message = "Constructing Python tuple"
-
+ code.funcstate.release_temp(sublist_temp)
+ code.putln('#endif')
+
+ for i, arg in enumerate(self.args):
+ arg.generate_assignment_code(self.coerced_unpacked_items[i], code)
+
+ def annotate(self, code):
+ for arg in self.args:
+ arg.annotate(code)
+ if self.unpacked_items:
+ for arg in self.unpacked_items:
+ arg.annotate(code)
+ for arg in self.coerced_unpacked_items:
+ arg.annotate(code)
+
+
+class TupleNode(SequenceNode):
+ # Tuple constructor.
+
+ type = tuple_type
+ is_partly_literal = False
+
+ gil_message = "Constructing Python tuple"
+
def infer_type(self, env):
if self.mult_factor or not self.args:
return tuple_type
@@ -7917,8 +7917,8 @@ class TupleNode(SequenceNode):
return tuple_type
return env.declare_tuple_type(self.pos, arg_types).type
- def analyse_types(self, env, skip_children=False):
- if len(self.args) == 0:
+ def analyse_types(self, env, skip_children=False):
+ if len(self.args) == 0:
self.is_temp = False
self.is_literal = True
return self
@@ -7945,15 +7945,15 @@ class TupleNode(SequenceNode):
if not node.mult_factor or (
node.mult_factor.is_literal and
isinstance(node.mult_factor.constant_result, _py_int_types)):
- node.is_temp = False
- node.is_literal = True
- else:
+ node.is_temp = False
+ node.is_literal = True
+ else:
if not node.mult_factor.type.is_pyobject:
node.mult_factor = node.mult_factor.coerce_to_pyobject(env)
node.is_temp = True
node.is_partly_literal = True
- return node
-
+ return node
+
def analyse_as_type(self, env):
# ctuple type
if not self.args:
@@ -7984,35 +7984,35 @@ class TupleNode(SequenceNode):
t.constant_result = list(self.constant_result)
return t
- def is_simple(self):
- # either temp or constant => always simple
- return True
-
- def nonlocally_immutable(self):
- # either temp or constant => always safe
- return True
-
- def calculate_result_code(self):
- if len(self.args) > 0:
- return self.result_code
- else:
- return Naming.empty_tuple
-
- def calculate_constant_result(self):
- self.constant_result = tuple([
- arg.constant_result for arg in self.args])
-
- def compile_time_value(self, denv):
- values = self.compile_time_value_list(denv)
- try:
- return tuple(values)
+ def is_simple(self):
+ # either temp or constant => always simple
+ return True
+
+ def nonlocally_immutable(self):
+ # either temp or constant => always safe
+ return True
+
+ def calculate_result_code(self):
+ if len(self.args) > 0:
+ return self.result_code
+ else:
+ return Naming.empty_tuple
+
+ def calculate_constant_result(self):
+ self.constant_result = tuple([
+ arg.constant_result for arg in self.args])
+
+ def compile_time_value(self, denv):
+ values = self.compile_time_value_list(denv)
+ try:
+ return tuple(values)
except Exception as e:
- self.compile_time_value_error(e)
-
- def generate_operation_code(self, code):
- if len(self.args) == 0:
- # result_code is Naming.empty_tuple
- return
+ self.compile_time_value_error(e)
+
+ def generate_operation_code(self, code):
+ if len(self.args) == 0:
+ # result_code is Naming.empty_tuple
+ return
if self.is_literal or self.is_partly_literal:
# The "mult_factor" is part of the deduplication if it is also constant, i.e. when
@@ -8031,56 +8031,56 @@ class TupleNode(SequenceNode):
code.putln('%s = PyNumber_Multiply(%s, %s); %s' % (
self.result(), tuple_target, self.mult_factor.py_result(),
code.error_goto_if_null(self.result(), self.pos)
- ))
+ ))
code.put_gotref(self.py_result())
- else:
+ else:
self.type.entry.used = True
- self.generate_sequence_packing_code(code)
-
-
-class ListNode(SequenceNode):
- # List constructor.
-
- # obj_conversion_errors [PyrexError] used internally
- # orignial_args [ExprNode] used internally
-
- obj_conversion_errors = []
- type = list_type
- in_module_scope = False
-
- gil_message = "Constructing Python list"
-
- def type_dependencies(self, env):
- return ()
-
- def infer_type(self, env):
+ self.generate_sequence_packing_code(code)
+
+
+class ListNode(SequenceNode):
+ # List constructor.
+
+ # obj_conversion_errors [PyrexError] used internally
+ # orignial_args [ExprNode] used internally
+
+ obj_conversion_errors = []
+ type = list_type
+ in_module_scope = False
+
+ gil_message = "Constructing Python list"
+
+ def type_dependencies(self, env):
+ return ()
+
+ def infer_type(self, env):
# TODO: Infer non-object list arrays.
- return list_type
-
- def analyse_expressions(self, env):
+ return list_type
+
+ def analyse_expressions(self, env):
for arg in self.args:
if arg.is_starred:
arg.starred_expr_allowed_here = True
- node = SequenceNode.analyse_expressions(self, env)
- return node.coerce_to_pyobject(env)
-
- def analyse_types(self, env):
+ node = SequenceNode.analyse_expressions(self, env)
+ return node.coerce_to_pyobject(env)
+
+ def analyse_types(self, env):
with local_errors(ignore=True) as errors:
self.original_args = list(self.args)
node = SequenceNode.analyse_types(self, env)
node.obj_conversion_errors = errors
- if env.is_module_scope:
- self.in_module_scope = True
+ if env.is_module_scope:
+ self.in_module_scope = True
node = node._create_merge_node_if_necessary(env)
- return node
-
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
+ return node
+
+ def coerce_to(self, dst_type, env):
+ if dst_type.is_pyobject:
+ for err in self.obj_conversion_errors:
+ report_error(err)
+ self.obj_conversion_errors = []
+ if not self.type.subtype_of(dst_type):
+ error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
elif (dst_type.is_array or dst_type.is_ptr) and dst_type.base_type is not PyrexTypes.c_void_type:
array_length = len(self.args)
if self.mult_factor:
@@ -8091,46 +8091,46 @@ class ListNode(SequenceNode):
array_length *= self.mult_factor.constant_result
else:
error(self.pos, "Cannot coerce dynamically multiplied list to '%s'" % dst_type)
- base_type = dst_type.base_type
+ base_type = dst_type.base_type
self.type = PyrexTypes.CArrayType(base_type, array_length)
- for i in range(len(self.original_args)):
- arg = self.args[i]
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(base_type, env)
+ for i in range(len(self.original_args)):
+ arg = self.args[i]
+ if isinstance(arg, CoerceToPyTypeNode):
+ arg = arg.arg
+ self.args[i] = arg.coerce_to(base_type, env)
elif dst_type.is_cpp_class:
# TODO(robertwb): Avoid object conversion for vector/list/set.
return TypecastNode(self.pos, operand=self, type=PyrexTypes.py_object_type).coerce_to(dst_type, env)
elif self.mult_factor:
error(self.pos, "Cannot coerce multiplied list to '%s'" % dst_type)
- elif dst_type.is_struct:
- if len(self.args) > len(dst_type.scope.var_entries):
+ elif dst_type.is_struct:
+ if len(self.args) > len(dst_type.scope.var_entries):
error(self.pos, "Too many members for '%s'" % dst_type)
- else:
- if len(self.args) < len(dst_type.scope.var_entries):
- warning(self.pos, "Too few members for '%s'" % dst_type, 1)
- for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(member.type, env)
- self.type = dst_type
+ else:
+ if len(self.args) < len(dst_type.scope.var_entries):
+ warning(self.pos, "Too few members for '%s'" % dst_type, 1)
+ for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
+ if isinstance(arg, CoerceToPyTypeNode):
+ arg = arg.arg
+ self.args[i] = arg.coerce_to(member.type, env)
+ self.type = dst_type
elif dst_type.is_ctuple:
return self.coerce_to_ctuple(dst_type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
- return self
-
+ else:
+ self.type = error_type
+ error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
+ return self
+
def as_list(self): # dummy for compatibility with TupleNode
return self
- def as_tuple(self):
- t = TupleNode(self.pos, args=self.args, mult_factor=self.mult_factor)
- if isinstance(self.constant_result, list):
- t.constant_result = tuple(self.constant_result)
- return t
-
- def allocate_temp_result(self, code):
+ def as_tuple(self):
+ t = TupleNode(self.pos, args=self.args, mult_factor=self.mult_factor)
+ if isinstance(self.constant_result, list):
+ t.constant_result = tuple(self.constant_result)
+ return t
+
+ def allocate_temp_result(self, code):
if self.type.is_array:
if self.in_module_scope:
self.temp_code = code.funcstate.allocate_temp(
@@ -8141,27 +8141,27 @@ class ListNode(SequenceNode):
# Yes, this means that we leak a temp array variable.
self.temp_code = code.funcstate.allocate_temp(
self.type, manage_ref=False, reusable=False)
- else:
- SequenceNode.allocate_temp_result(self, code)
-
- def calculate_constant_result(self):
- if self.mult_factor:
+ else:
+ SequenceNode.allocate_temp_result(self, code)
+
+ def calculate_constant_result(self):
+ if self.mult_factor:
raise ValueError() # may exceed the compile time memory
- self.constant_result = [
- arg.constant_result for arg in self.args]
-
- def compile_time_value(self, denv):
- l = self.compile_time_value_list(denv)
- if self.mult_factor:
- l *= self.mult_factor.compile_time_value(denv)
- return l
-
- def generate_operation_code(self, code):
- if self.type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.generate_sequence_packing_code(code)
- elif self.type.is_array:
+ self.constant_result = [
+ arg.constant_result for arg in self.args]
+
+ def compile_time_value(self, denv):
+ l = self.compile_time_value_list(denv)
+ if self.mult_factor:
+ l *= self.mult_factor.compile_time_value(denv)
+ return l
+
+ def generate_operation_code(self, code):
+ if self.type.is_pyobject:
+ for err in self.obj_conversion_errors:
+ report_error(err)
+ self.generate_sequence_packing_code(code)
+ elif self.type.is_array:
if self.mult_factor:
code.putln("{")
code.putln("Py_ssize_t %s;" % Naming.quick_temp_cname)
@@ -8170,7 +8170,7 @@ class ListNode(SequenceNode):
offset = '+ (%d * %s)' % (len(self.args), Naming.quick_temp_cname)
else:
offset = ''
- for i, arg in enumerate(self.args):
+ for i, arg in enumerate(self.args):
if arg.type.is_array:
code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
code.putln("memcpy(&(%s[%s%s]), %s, sizeof(%s[0]));" % (
@@ -8186,98 +8186,98 @@ class ListNode(SequenceNode):
if self.mult_factor:
code.putln("}")
code.putln("}")
- elif self.type.is_struct:
- for arg, member in zip(self.args, self.type.scope.var_entries):
- code.putln("%s.%s = %s;" % (
+ elif self.type.is_struct:
+ for arg, member in zip(self.args, self.type.scope.var_entries):
+ code.putln("%s.%s = %s;" % (
self.result(),
member.cname,
arg.result()))
- else:
- raise InternalError("List type never specified")
-
-
-class ScopedExprNode(ExprNode):
- # Abstract base class for ExprNodes that have their own local
- # scope, such as generator expressions.
- #
- # expr_scope Scope the inner scope of the expression
-
- subexprs = []
- expr_scope = None
-
- # does this node really have a local scope, e.g. does it leak loop
- # variables or not? non-leaking Py3 behaviour is default, except
- # for list comprehensions where the behaviour differs in Py2 and
- # Py3 (set in Parsing.py based on parser context)
- has_local_scope = True
-
- def init_scope(self, outer_scope, expr_scope=None):
- if expr_scope is not None:
- self.expr_scope = expr_scope
- elif self.has_local_scope:
- self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
- else:
- self.expr_scope = None
-
- def analyse_declarations(self, env):
- self.init_scope(env)
-
- def analyse_scoped_declarations(self, env):
- # this is called with the expr_scope as env
- pass
-
- def analyse_types(self, env):
- # no recursion here, the children will be analysed separately below
- return self
-
- def analyse_scoped_expressions(self, env):
- # this is called with the expr_scope as env
- return self
-
- def generate_evaluation_code(self, code):
- # set up local variables and free their references on exit
- generate_inner_evaluation_code = super(ScopedExprNode, self).generate_evaluation_code
- if not self.has_local_scope or not self.expr_scope.var_entries:
- # no local variables => delegate, done
- generate_inner_evaluation_code(code)
- return
-
- code.putln('{ /* enter inner scope */')
- py_entries = []
+ else:
+ raise InternalError("List type never specified")
+
+
+class ScopedExprNode(ExprNode):
+ # Abstract base class for ExprNodes that have their own local
+ # scope, such as generator expressions.
+ #
+ # expr_scope Scope the inner scope of the expression
+
+ subexprs = []
+ expr_scope = None
+
+ # does this node really have a local scope, e.g. does it leak loop
+ # variables or not? non-leaking Py3 behaviour is default, except
+ # for list comprehensions where the behaviour differs in Py2 and
+ # Py3 (set in Parsing.py based on parser context)
+ has_local_scope = True
+
+ def init_scope(self, outer_scope, expr_scope=None):
+ if expr_scope is not None:
+ self.expr_scope = expr_scope
+ elif self.has_local_scope:
+ self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
+ else:
+ self.expr_scope = None
+
+ def analyse_declarations(self, env):
+ self.init_scope(env)
+
+ def analyse_scoped_declarations(self, env):
+ # this is called with the expr_scope as env
+ pass
+
+ def analyse_types(self, env):
+ # no recursion here, the children will be analysed separately below
+ return self
+
+ def analyse_scoped_expressions(self, env):
+ # this is called with the expr_scope as env
+ return self
+
+ def generate_evaluation_code(self, code):
+ # set up local variables and free their references on exit
+ generate_inner_evaluation_code = super(ScopedExprNode, self).generate_evaluation_code
+ if not self.has_local_scope or not self.expr_scope.var_entries:
+ # no local variables => delegate, done
+ generate_inner_evaluation_code(code)
+ return
+
+ code.putln('{ /* enter inner scope */')
+ py_entries = []
for _, entry in sorted(item for item in self.expr_scope.entries.items() if item[0]):
- if not entry.in_closure:
- if entry.type.is_pyobject and entry.used:
- py_entries.append(entry)
- if not py_entries:
- # no local Python references => no cleanup required
- generate_inner_evaluation_code(code)
- code.putln('} /* exit inner scope */')
- return
-
- # must free all local Python references at each exit point
+ if not entry.in_closure:
+ if entry.type.is_pyobject and entry.used:
+ py_entries.append(entry)
+ if not py_entries:
+ # no local Python references => no cleanup required
+ generate_inner_evaluation_code(code)
+ code.putln('} /* exit inner scope */')
+ return
+
+ # must free all local Python references at each exit point
old_loop_labels = code.new_loop_labels()
- old_error_label = code.new_error_label()
-
- generate_inner_evaluation_code(code)
-
- # normal (non-error) exit
+ old_error_label = code.new_error_label()
+
+ generate_inner_evaluation_code(code)
+
+ # normal (non-error) exit
self._generate_vars_cleanup(code, py_entries)
-
- # error/loop body exit points
- exit_scope = code.new_label('exit_scope')
- code.put_goto(exit_scope)
- for label, old_label in ([(code.error_label, old_error_label)] +
- list(zip(code.get_loop_labels(), old_loop_labels))):
- if code.label_used(label):
- code.put_label(label)
+
+ # error/loop body exit points
+ exit_scope = code.new_label('exit_scope')
+ code.put_goto(exit_scope)
+ for label, old_label in ([(code.error_label, old_error_label)] +
+ list(zip(code.get_loop_labels(), old_loop_labels))):
+ if code.label_used(label):
+ code.put_label(label)
self._generate_vars_cleanup(code, py_entries)
- code.put_goto(old_label)
- code.put_label(exit_scope)
- code.putln('} /* exit inner scope */')
-
- code.set_loop_labels(old_loop_labels)
- code.error_label = old_error_label
-
+ code.put_goto(old_label)
+ code.put_label(exit_scope)
+ code.putln('} /* exit inner scope */')
+
+ code.set_loop_labels(old_loop_labels)
+ code.error_label = old_error_label
+
def _generate_vars_cleanup(self, code, py_entries):
for entry in py_entries:
if entry.is_cglobal:
@@ -8285,154 +8285,154 @@ class ScopedExprNode(ExprNode):
code.put_decref_set(entry.cname, "Py_None")
else:
code.put_var_xdecref_clear(entry)
-
-
-class ComprehensionNode(ScopedExprNode):
- # A list/set/dict comprehension
-
- child_attrs = ["loop"]
-
- is_temp = True
+
+
+class ComprehensionNode(ScopedExprNode):
+ # A list/set/dict comprehension
+
+ child_attrs = ["loop"]
+
+ is_temp = True
constant_result = not_a_constant
-
- def infer_type(self, env):
- return self.type
-
- def analyse_declarations(self, env):
- self.append.target = self # this is used in the PyList_Append of the inner loop
- self.init_scope(env)
-
- def analyse_scoped_declarations(self, env):
- self.loop.analyse_declarations(env)
-
- def analyse_types(self, env):
- if not self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
-
- def analyse_scoped_expressions(self, env):
- if self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
-
- def may_be_none(self):
- return False
-
- def generate_result_code(self, code):
- self.generate_operation_code(code)
-
- def generate_operation_code(self, code):
- if self.type is Builtin.list_type:
- create_code = 'PyList_New(0)'
- elif self.type is Builtin.set_type:
- create_code = 'PySet_New(NULL)'
- elif self.type is Builtin.dict_type:
- create_code = 'PyDict_New()'
- else:
- raise InternalError("illegal type for comprehension: %s" % self.type)
- code.putln('%s = %s; %s' % (
- self.result(), create_code,
- code.error_goto_if_null(self.result(), self.pos)))
-
- code.put_gotref(self.result())
- self.loop.generate_execution_code(code)
-
- def annotate(self, code):
- self.loop.annotate(code)
-
-
-class ComprehensionAppendNode(Node):
- # Need to be careful to avoid infinite recursion:
- # target must not be in child_attrs/subexprs
-
- child_attrs = ['expr']
- target = None
-
- type = PyrexTypes.c_int_type
-
- def analyse_expressions(self, env):
- self.expr = self.expr.analyse_expressions(env)
- if not self.expr.type.is_pyobject:
- self.expr = self.expr.coerce_to_pyobject(env)
- return self
-
- def generate_execution_code(self, code):
- if self.target.type is list_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ListCompAppend", "Optimize.c"))
- function = "__Pyx_ListComp_Append"
- elif self.target.type is set_type:
- function = "PySet_Add"
- else:
- raise InternalError(
- "Invalid type for comprehension node: %s" % self.target.type)
-
- self.expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("%s(%s, (PyObject*)%s)" % (
- function,
- self.target.result(),
- self.expr.result()
- ), self.pos))
- self.expr.generate_disposal_code(code)
- self.expr.free_temps(code)
-
- def generate_function_definitions(self, env, code):
- self.expr.generate_function_definitions(env, code)
-
- def annotate(self, code):
- self.expr.annotate(code)
-
-class DictComprehensionAppendNode(ComprehensionAppendNode):
- child_attrs = ['key_expr', 'value_expr']
-
- def analyse_expressions(self, env):
- self.key_expr = self.key_expr.analyse_expressions(env)
- if not self.key_expr.type.is_pyobject:
- self.key_expr = self.key_expr.coerce_to_pyobject(env)
- self.value_expr = self.value_expr.analyse_expressions(env)
- if not self.value_expr.type.is_pyobject:
- self.value_expr = self.value_expr.coerce_to_pyobject(env)
- return self
-
- def generate_execution_code(self, code):
- self.key_expr.generate_evaluation_code(code)
- self.value_expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("PyDict_SetItem(%s, (PyObject*)%s, (PyObject*)%s)" % (
- self.target.result(),
- self.key_expr.result(),
- self.value_expr.result()
- ), self.pos))
- self.key_expr.generate_disposal_code(code)
- self.key_expr.free_temps(code)
- self.value_expr.generate_disposal_code(code)
- self.value_expr.free_temps(code)
-
- def generate_function_definitions(self, env, code):
- self.key_expr.generate_function_definitions(env, code)
- self.value_expr.generate_function_definitions(env, code)
-
- def annotate(self, code):
- self.key_expr.annotate(code)
- self.value_expr.annotate(code)
-
-
+
+ def infer_type(self, env):
+ return self.type
+
+ def analyse_declarations(self, env):
+ self.append.target = self # this is used in the PyList_Append of the inner loop
+ self.init_scope(env)
+
+ def analyse_scoped_declarations(self, env):
+ self.loop.analyse_declarations(env)
+
+ def analyse_types(self, env):
+ if not self.has_local_scope:
+ self.loop = self.loop.analyse_expressions(env)
+ return self
+
+ def analyse_scoped_expressions(self, env):
+ if self.has_local_scope:
+ self.loop = self.loop.analyse_expressions(env)
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def generate_result_code(self, code):
+ self.generate_operation_code(code)
+
+ def generate_operation_code(self, code):
+ if self.type is Builtin.list_type:
+ create_code = 'PyList_New(0)'
+ elif self.type is Builtin.set_type:
+ create_code = 'PySet_New(NULL)'
+ elif self.type is Builtin.dict_type:
+ create_code = 'PyDict_New()'
+ else:
+ raise InternalError("illegal type for comprehension: %s" % self.type)
+ code.putln('%s = %s; %s' % (
+ self.result(), create_code,
+ code.error_goto_if_null(self.result(), self.pos)))
+
+ code.put_gotref(self.result())
+ self.loop.generate_execution_code(code)
+
+ def annotate(self, code):
+ self.loop.annotate(code)
+
+
+class ComprehensionAppendNode(Node):
+ # Need to be careful to avoid infinite recursion:
+ # target must not be in child_attrs/subexprs
+
+ child_attrs = ['expr']
+ target = None
+
+ type = PyrexTypes.c_int_type
+
+ def analyse_expressions(self, env):
+ self.expr = self.expr.analyse_expressions(env)
+ if not self.expr.type.is_pyobject:
+ self.expr = self.expr.coerce_to_pyobject(env)
+ return self
+
+ def generate_execution_code(self, code):
+ if self.target.type is list_type:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("ListCompAppend", "Optimize.c"))
+ function = "__Pyx_ListComp_Append"
+ elif self.target.type is set_type:
+ function = "PySet_Add"
+ else:
+ raise InternalError(
+ "Invalid type for comprehension node: %s" % self.target.type)
+
+ self.expr.generate_evaluation_code(code)
+ code.putln(code.error_goto_if("%s(%s, (PyObject*)%s)" % (
+ function,
+ self.target.result(),
+ self.expr.result()
+ ), self.pos))
+ self.expr.generate_disposal_code(code)
+ self.expr.free_temps(code)
+
+ def generate_function_definitions(self, env, code):
+ self.expr.generate_function_definitions(env, code)
+
+ def annotate(self, code):
+ self.expr.annotate(code)
+
+class DictComprehensionAppendNode(ComprehensionAppendNode):
+ child_attrs = ['key_expr', 'value_expr']
+
+ def analyse_expressions(self, env):
+ self.key_expr = self.key_expr.analyse_expressions(env)
+ if not self.key_expr.type.is_pyobject:
+ self.key_expr = self.key_expr.coerce_to_pyobject(env)
+ self.value_expr = self.value_expr.analyse_expressions(env)
+ if not self.value_expr.type.is_pyobject:
+ self.value_expr = self.value_expr.coerce_to_pyobject(env)
+ return self
+
+ def generate_execution_code(self, code):
+ self.key_expr.generate_evaluation_code(code)
+ self.value_expr.generate_evaluation_code(code)
+ code.putln(code.error_goto_if("PyDict_SetItem(%s, (PyObject*)%s, (PyObject*)%s)" % (
+ self.target.result(),
+ self.key_expr.result(),
+ self.value_expr.result()
+ ), self.pos))
+ self.key_expr.generate_disposal_code(code)
+ self.key_expr.free_temps(code)
+ self.value_expr.generate_disposal_code(code)
+ self.value_expr.free_temps(code)
+
+ def generate_function_definitions(self, env, code):
+ self.key_expr.generate_function_definitions(env, code)
+ self.value_expr.generate_function_definitions(env, code)
+
+ def annotate(self, code):
+ self.key_expr.annotate(code)
+ self.value_expr.annotate(code)
+
+
class InlinedGeneratorExpressionNode(ExprNode):
# An inlined generator expression for which the result is calculated
# inside of the loop and returned as a single, first and only Generator
# return value.
# This will only be created by transforms when replacing safe builtin
# calls on generator expressions.
- #
+ #
# gen GeneratorExpressionNode the generator, not containing any YieldExprNodes
# orig_func String the name of the builtin function this node replaces
# target ExprNode or None a 'target' for a ComprehensionAppend node
-
+
subexprs = ["gen"]
orig_func = None
target = None
is_temp = True
- type = py_object_type
-
+ type = py_object_type
+
def __init__(self, pos, gen, comprehension_type=None, **kwargs):
gbody = gen.def_node.gbody
gbody.is_inlined = True
@@ -8444,17 +8444,17 @@ class InlinedGeneratorExpressionNode(ExprNode):
type=comprehension_type,
)
super(InlinedGeneratorExpressionNode, self).__init__(pos, gen=gen, **kwargs)
-
- def may_be_none(self):
+
+ def may_be_none(self):
return self.orig_func not in ('any', 'all', 'sorted')
-
- def infer_type(self, env):
+
+ def infer_type(self, env):
return self.type
-
- def analyse_types(self, env):
+
+ def analyse_types(self, env):
self.gen = self.gen.analyse_expressions(env)
- return self
-
+ return self
+
def generate_result_code(self, code):
code.putln("%s = __Pyx_Generator_Next(%s); %s" % (
self.result(), self.gen.result(),
@@ -8546,17 +8546,17 @@ class MergedSequenceNode(ExprNode):
assert self.type in (set_type, list_type, tuple_type)
self.args = args
- return self
-
+ return self
+
def may_be_none(self):
return False
-
+
def generate_evaluation_code(self, code):
code.mark_pos(self.pos)
self.allocate_temp_result(code)
-
+
is_set = self.type is set_type
-
+
args = iter(self.args)
item = next(args)
item.generate_evaluation_code(code)
@@ -8573,7 +8573,7 @@ class MergedSequenceNode(ExprNode):
code.put_gotref(self.py_result())
item.generate_disposal_code(code)
item.free_temps(code)
-
+
helpers = set()
if is_set:
add_func = "PySet_Add"
@@ -8581,7 +8581,7 @@ class MergedSequenceNode(ExprNode):
else:
add_func = "__Pyx_ListComp_Append"
extend_func = "__Pyx_PyList_Extend"
-
+
for item in args:
if (is_set and (item.is_set_literal or item.is_sequence_constructor) or
(item.is_sequence_constructor and not item.mult_factor)):
@@ -8635,106 +8635,106 @@ class SetNode(ExprNode):
"""
Set constructor.
"""
- subexprs = ['args']
+ subexprs = ['args']
type = set_type
is_set_literal = True
- gil_message = "Constructing Python set"
-
- def analyse_types(self, env):
- for i in range(len(self.args)):
- arg = self.args[i]
- arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- self.type = set_type
- self.is_temp = 1
- return self
-
- def may_be_none(self):
- return False
-
- def calculate_constant_result(self):
- self.constant_result = set([arg.constant_result for arg in self.args])
-
- def compile_time_value(self, denv):
- values = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return set(values)
+ gil_message = "Constructing Python set"
+
+ def analyse_types(self, env):
+ for i in range(len(self.args)):
+ arg = self.args[i]
+ arg = arg.analyse_types(env)
+ self.args[i] = arg.coerce_to_pyobject(env)
+ self.type = set_type
+ self.is_temp = 1
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def calculate_constant_result(self):
+ self.constant_result = set([arg.constant_result for arg in self.args])
+
+ def compile_time_value(self, denv):
+ values = [arg.compile_time_value(denv) for arg in self.args]
+ try:
+ return set(values)
except Exception as e:
- self.compile_time_value_error(e)
-
- def generate_evaluation_code(self, code):
- for arg in self.args:
- arg.generate_evaluation_code(code)
- self.allocate_temp_result(code)
- code.putln(
- "%s = PySet_New(0); %s" % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- for arg in self.args:
- code.put_error_if_neg(
- self.pos,
- "PySet_Add(%s, %s)" % (self.result(), arg.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
-
-
-class DictNode(ExprNode):
- # Dictionary constructor.
- #
- # key_value_pairs [DictItemNode]
- # exclude_null_values [boolean] Do not add NULL values to dict
- #
- # obj_conversion_errors [PyrexError] used internally
-
- subexprs = ['key_value_pairs']
- is_temp = 1
- exclude_null_values = False
- type = dict_type
- is_dict_literal = True
+ self.compile_time_value_error(e)
+
+ def generate_evaluation_code(self, code):
+ for arg in self.args:
+ arg.generate_evaluation_code(code)
+ self.allocate_temp_result(code)
+ code.putln(
+ "%s = PySet_New(0); %s" % (
+ self.result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ for arg in self.args:
+ code.put_error_if_neg(
+ self.pos,
+ "PySet_Add(%s, %s)" % (self.result(), arg.py_result()))
+ arg.generate_disposal_code(code)
+ arg.free_temps(code)
+
+
+class DictNode(ExprNode):
+ # Dictionary constructor.
+ #
+ # key_value_pairs [DictItemNode]
+ # exclude_null_values [boolean] Do not add NULL values to dict
+ #
+ # obj_conversion_errors [PyrexError] used internally
+
+ subexprs = ['key_value_pairs']
+ is_temp = 1
+ exclude_null_values = False
+ type = dict_type
+ is_dict_literal = True
reject_duplicates = False
-
- obj_conversion_errors = []
-
- @classmethod
- def from_pairs(cls, pos, pairs):
- return cls(pos, key_value_pairs=[
- DictItemNode(pos, key=k, value=v) for k, v in pairs])
-
- def calculate_constant_result(self):
- self.constant_result = dict([
- item.constant_result for item in self.key_value_pairs])
-
- def compile_time_value(self, denv):
- pairs = [(item.key.compile_time_value(denv), item.value.compile_time_value(denv))
- for item in self.key_value_pairs]
- try:
- return dict(pairs)
+
+ obj_conversion_errors = []
+
+ @classmethod
+ def from_pairs(cls, pos, pairs):
+ return cls(pos, key_value_pairs=[
+ DictItemNode(pos, key=k, value=v) for k, v in pairs])
+
+ def calculate_constant_result(self):
+ self.constant_result = dict([
+ item.constant_result for item in self.key_value_pairs])
+
+ def compile_time_value(self, denv):
+ pairs = [(item.key.compile_time_value(denv), item.value.compile_time_value(denv))
+ for item in self.key_value_pairs]
+ try:
+ return dict(pairs)
except Exception as e:
- self.compile_time_value_error(e)
-
- def type_dependencies(self, env):
- return ()
-
- def infer_type(self, env):
+ self.compile_time_value_error(e)
+
+ def type_dependencies(self, env):
+ return ()
+
+ def infer_type(self, env):
# TODO: Infer struct constructors.
- return dict_type
-
- def analyse_types(self, env):
+ return dict_type
+
+ def analyse_types(self, env):
with local_errors(ignore=True) as errors:
self.key_value_pairs = [
item.analyse_types(env)
for item in self.key_value_pairs
]
self.obj_conversion_errors = errors
- return self
-
- def may_be_none(self):
- return False
-
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- self.release_errors()
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def coerce_to(self, dst_type, env):
+ if dst_type.is_pyobject:
+ self.release_errors()
if self.type.is_struct_or_union:
if not dict_type.subtype_of(dst_type):
error(self.pos, "Cannot interpret struct as non-dict type '%s'" % dst_type)
@@ -8742,67 +8742,67 @@ class DictNode(ExprNode):
DictItemNode(item.pos, key=item.key.coerce_to_pyobject(env),
value=item.value.coerce_to_pyobject(env))
for item in self.key_value_pairs])
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- elif dst_type.is_struct_or_union:
- self.type = dst_type
- if not dst_type.is_struct and len(self.key_value_pairs) != 1:
- error(self.pos, "Exactly one field must be specified to convert to union '%s'" % dst_type)
- elif dst_type.is_struct and len(self.key_value_pairs) < len(dst_type.scope.var_entries):
- warning(self.pos, "Not all members given for struct '%s'" % dst_type, 1)
- for item in self.key_value_pairs:
- if isinstance(item.key, CoerceToPyTypeNode):
- item.key = item.key.arg
- if not item.key.is_string_literal:
- error(item.key.pos, "Invalid struct field identifier")
- item.key = StringNode(item.key.pos, value="<error>")
- else:
- key = str(item.key.value) # converts string literals to unicode in Py3
- member = dst_type.scope.lookup_here(key)
- if not member:
- error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key))
- else:
- value = item.value
- if isinstance(value, CoerceToPyTypeNode):
- value = value.arg
- item.value = value.coerce_to(member.type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- return self
-
- def release_errors(self):
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
-
- gil_message = "Constructing Python dict"
-
- def generate_evaluation_code(self, code):
- # Custom method used here because key-value
- # pairs are evaluated and used one at a time.
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
+ if not self.type.subtype_of(dst_type):
+ error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
+ elif dst_type.is_struct_or_union:
+ self.type = dst_type
+ if not dst_type.is_struct and len(self.key_value_pairs) != 1:
+ error(self.pos, "Exactly one field must be specified to convert to union '%s'" % dst_type)
+ elif dst_type.is_struct and len(self.key_value_pairs) < len(dst_type.scope.var_entries):
+ warning(self.pos, "Not all members given for struct '%s'" % dst_type, 1)
+ for item in self.key_value_pairs:
+ if isinstance(item.key, CoerceToPyTypeNode):
+ item.key = item.key.arg
+ if not item.key.is_string_literal:
+ error(item.key.pos, "Invalid struct field identifier")
+ item.key = StringNode(item.key.pos, value="<error>")
+ else:
+ key = str(item.key.value) # converts string literals to unicode in Py3
+ member = dst_type.scope.lookup_here(key)
+ if not member:
+ error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key))
+ else:
+ value = item.value
+ if isinstance(value, CoerceToPyTypeNode):
+ value = value.arg
+ item.value = value.coerce_to(member.type, env)
+ else:
+ self.type = error_type
+ error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
+ return self
+
+ def release_errors(self):
+ for err in self.obj_conversion_errors:
+ report_error(err)
+ self.obj_conversion_errors = []
+
+ gil_message = "Constructing Python dict"
+
+ def generate_evaluation_code(self, code):
+ # Custom method used here because key-value
+ # pairs are evaluated and used one at a time.
+ code.mark_pos(self.pos)
+ self.allocate_temp_result(code)
is_dict = self.type.is_pyobject
if is_dict:
- self.release_errors()
- code.putln(
+ self.release_errors()
+ code.putln(
"%s = __Pyx_PyDict_NewPresized(%d); %s" % (
- self.result(),
+ self.result(),
len(self.key_value_pairs),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
keys_seen = set()
key_type = None
needs_error_helper = False
- for item in self.key_value_pairs:
- item.generate_evaluation_code(code)
+ for item in self.key_value_pairs:
+ item.generate_evaluation_code(code)
if is_dict:
- if self.exclude_null_values:
- code.putln('if (%s) {' % item.value.py_result())
+ if self.exclude_null_values:
+ code.putln('if (%s) {' % item.value.py_result())
key = item.key
if self.reject_duplicates:
if keys_seen is not None:
@@ -8838,310 +8838,310 @@ class DictNode(ExprNode):
item.value.py_result()))
if self.reject_duplicates and keys_seen is None:
code.putln('}')
- if self.exclude_null_values:
- code.putln('}')
- else:
- code.putln("%s.%s = %s;" % (
- self.result(),
- item.key.value,
- item.value.result()))
- item.generate_disposal_code(code)
- item.free_temps(code)
-
+ if self.exclude_null_values:
+ code.putln('}')
+ else:
+ code.putln("%s.%s = %s;" % (
+ self.result(),
+ item.key.value,
+ item.value.result()))
+ item.generate_disposal_code(code)
+ item.free_temps(code)
+
if needs_error_helper:
code.globalstate.use_utility_code(
UtilityCode.load_cached("RaiseDoubleKeywords", "FunctionArguments.c"))
- def annotate(self, code):
- for item in self.key_value_pairs:
- item.annotate(code)
-
-
-class DictItemNode(ExprNode):
- # Represents a single item in a DictNode
- #
- # key ExprNode
- # value ExprNode
- subexprs = ['key', 'value']
-
- nogil_check = None # Parent DictNode takes care of it
-
- def calculate_constant_result(self):
- self.constant_result = (
- self.key.constant_result, self.value.constant_result)
-
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- self.value = self.value.coerce_to_pyobject(env)
- return self
-
- def generate_evaluation_code(self, code):
- self.key.generate_evaluation_code(code)
- self.value.generate_evaluation_code(code)
-
- def generate_disposal_code(self, code):
- self.key.generate_disposal_code(code)
- self.value.generate_disposal_code(code)
-
- def free_temps(self, code):
- self.key.free_temps(code)
- self.value.free_temps(code)
-
- def __iter__(self):
- return iter([self.key, self.value])
-
-
-class SortedDictKeysNode(ExprNode):
- # build sorted list of dict keys, e.g. for dir()
- subexprs = ['arg']
-
- is_temp = True
-
- def __init__(self, arg):
- ExprNode.__init__(self, arg.pos, arg=arg)
- self.type = Builtin.list_type
-
- def analyse_types(self, env):
- arg = self.arg.analyse_types(env)
- if arg.type is Builtin.dict_type:
- arg = arg.as_none_safe_node(
- "'NoneType' object is not iterable")
- self.arg = arg
- return self
-
- def may_be_none(self):
- return False
-
- def generate_result_code(self, code):
- dict_result = self.arg.py_result()
- if self.arg.type is Builtin.dict_type:
- code.putln('%s = PyDict_Keys(%s); %s' % (
- self.result(), dict_result,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- else:
- # originally used PyMapping_Keys() here, but that may return a tuple
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- 'PyObjectCallMethod0', 'ObjectHandling.c'))
- keys_cname = code.intern_identifier(StringEncoding.EncodedString("keys"))
- code.putln('%s = __Pyx_PyObject_CallMethod0(%s, %s); %s' % (
- self.result(), dict_result, keys_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- code.putln("if (unlikely(!PyList_Check(%s))) {" % self.result())
- code.put_decref_set(self.result(), "PySequence_List(%s)" % self.result())
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- code.putln("}")
- code.put_error_if_neg(
- self.pos, 'PyList_Sort(%s)' % self.py_result())
-
-
-class ModuleNameMixin(object):
- def get_py_mod_name(self, code):
- return code.get_py_string_const(
- self.module_name, identifier=True)
-
- def get_py_qualified_name(self, code):
- return code.get_py_string_const(
- self.qualname, identifier=True)
-
-
-class ClassNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
+ def annotate(self, code):
+ for item in self.key_value_pairs:
+ item.annotate(code)
+
+
+class DictItemNode(ExprNode):
+ # Represents a single item in a DictNode
+ #
+ # key ExprNode
+ # value ExprNode
+ subexprs = ['key', 'value']
+
+ nogil_check = None # Parent DictNode takes care of it
+
+ def calculate_constant_result(self):
+ self.constant_result = (
+ self.key.constant_result, self.value.constant_result)
+
+ def analyse_types(self, env):
+ self.key = self.key.analyse_types(env)
+ self.value = self.value.analyse_types(env)
+ self.key = self.key.coerce_to_pyobject(env)
+ self.value = self.value.coerce_to_pyobject(env)
+ return self
+
+ def generate_evaluation_code(self, code):
+ self.key.generate_evaluation_code(code)
+ self.value.generate_evaluation_code(code)
+
+ def generate_disposal_code(self, code):
+ self.key.generate_disposal_code(code)
+ self.value.generate_disposal_code(code)
+
+ def free_temps(self, code):
+ self.key.free_temps(code)
+ self.value.free_temps(code)
+
+ def __iter__(self):
+ return iter([self.key, self.value])
+
+
+class SortedDictKeysNode(ExprNode):
+ # build sorted list of dict keys, e.g. for dir()
+ subexprs = ['arg']
+
+ is_temp = True
+
+ def __init__(self, arg):
+ ExprNode.__init__(self, arg.pos, arg=arg)
+ self.type = Builtin.list_type
+
+ def analyse_types(self, env):
+ arg = self.arg.analyse_types(env)
+ if arg.type is Builtin.dict_type:
+ arg = arg.as_none_safe_node(
+ "'NoneType' object is not iterable")
+ self.arg = arg
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def generate_result_code(self, code):
+ dict_result = self.arg.py_result()
+ if self.arg.type is Builtin.dict_type:
+ code.putln('%s = PyDict_Keys(%s); %s' % (
+ self.result(), dict_result,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ else:
+ # originally used PyMapping_Keys() here, but that may return a tuple
+ code.globalstate.use_utility_code(UtilityCode.load_cached(
+ 'PyObjectCallMethod0', 'ObjectHandling.c'))
+ keys_cname = code.intern_identifier(StringEncoding.EncodedString("keys"))
+ code.putln('%s = __Pyx_PyObject_CallMethod0(%s, %s); %s' % (
+ self.result(), dict_result, keys_cname,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ code.putln("if (unlikely(!PyList_Check(%s))) {" % self.result())
+ code.put_decref_set(self.result(), "PySequence_List(%s)" % self.result())
+ code.putln(code.error_goto_if_null(self.result(), self.pos))
+ code.put_gotref(self.py_result())
+ code.putln("}")
+ code.put_error_if_neg(
+ self.pos, 'PyList_Sort(%s)' % self.py_result())
+
+
+class ModuleNameMixin(object):
+ def get_py_mod_name(self, code):
+ return code.get_py_string_const(
+ self.module_name, identifier=True)
+
+ def get_py_qualified_name(self, code):
+ return code.get_py_string_const(
+ self.qualname, identifier=True)
+
+
+class ClassNode(ExprNode, ModuleNameMixin):
+ # Helper class used in the implementation of Python
+ # class definitions. Constructs a class object given
+ # a name, tuple of bases and class dictionary.
+ #
+ # name EncodedString Name of the class
# class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string
- # module_name EncodedString Name of defining module
-
+ # doc ExprNode or None Doc string
+ # module_name EncodedString Name of defining module
+
subexprs = ['doc']
- type = py_object_type
- is_temp = True
-
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
-
- def analyse_types(self, env):
- if self.doc:
- self.doc = self.doc.analyse_types(env)
- self.doc = self.doc.coerce_to_pyobject(env)
- env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
- return self
-
- def may_be_none(self):
- return True
-
- gil_message = "Constructing Python class"
-
- def generate_result_code(self, code):
+ type = py_object_type
+ is_temp = True
+
+ def infer_type(self, env):
+ # TODO: could return 'type' in some cases
+ return py_object_type
+
+ def analyse_types(self, env):
+ if self.doc:
+ self.doc = self.doc.analyse_types(env)
+ self.doc = self.doc.coerce_to_pyobject(env)
+ env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
+ return self
+
+ def may_be_none(self):
+ return True
+
+ gil_message = "Constructing Python class"
+
+ def generate_result_code(self, code):
class_def_node = self.class_def_node
- cname = code.intern_identifier(self.name)
-
- if self.doc:
- code.put_error_if_neg(self.pos,
- 'PyDict_SetItem(%s, %s, %s)' % (
+ cname = code.intern_identifier(self.name)
+
+ if self.doc:
+ code.put_error_if_neg(self.pos,
+ 'PyDict_SetItem(%s, %s, %s)' % (
class_def_node.dict.py_result(),
- code.intern_identifier(
- StringEncoding.EncodedString("__doc__")),
- self.doc.py_result()))
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
- code.putln(
- '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
- self.result(),
+ code.intern_identifier(
+ StringEncoding.EncodedString("__doc__")),
+ self.doc.py_result()))
+ py_mod_name = self.get_py_mod_name(code)
+ qualname = self.get_py_qualified_name(code)
+ code.putln(
+ '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
+ self.result(),
class_def_node.bases.py_result(),
class_def_node.dict.py_result(),
- cname,
- qualname,
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class Py3ClassNode(ExprNode):
- # Helper class used in the implementation of Python3+
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
- # module_name EncodedString Name of defining module
+ cname,
+ qualname,
+ py_mod_name,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class Py3ClassNode(ExprNode):
+ # Helper class used in the implementation of Python3+
+ # class definitions. Constructs a class object given
+ # a name, tuple of bases and class dictionary.
+ #
+ # name EncodedString Name of the class
+ # module_name EncodedString Name of defining module
# class_def_node PyClassDefNode PyClassDefNode defining this class
- # calculate_metaclass bool should call CalculateMetaclass()
- # allow_py2_metaclass bool should look for Py2 metaclass
-
- subexprs = []
- type = py_object_type
- is_temp = True
-
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
-
- def analyse_types(self, env):
- return self
-
- def may_be_none(self):
- return True
-
- gil_message = "Constructing Python class"
-
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
- cname = code.intern_identifier(self.name)
+ # calculate_metaclass bool should call CalculateMetaclass()
+ # allow_py2_metaclass bool should look for Py2 metaclass
+
+ subexprs = []
+ type = py_object_type
+ is_temp = True
+
+ def infer_type(self, env):
+ # TODO: could return 'type' in some cases
+ return py_object_type
+
+ def analyse_types(self, env):
+ return self
+
+ def may_be_none(self):
+ return True
+
+ gil_message = "Constructing Python class"
+
+ def generate_result_code(self, code):
+ code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
+ cname = code.intern_identifier(self.name)
class_def_node = self.class_def_node
mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL'
if class_def_node.metaclass:
metaclass = class_def_node.metaclass.py_result()
- else:
- metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
- code.putln(
- '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % (
- self.result(),
- metaclass,
- cname,
+ else:
+ metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
+ code.putln(
+ '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % (
+ self.result(),
+ metaclass,
+ cname,
class_def_node.bases.py_result(),
class_def_node.dict.py_result(),
- mkw,
- self.calculate_metaclass,
- self.allow_py2_metaclass,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class PyClassMetaclassNode(ExprNode):
- # Helper class holds Python3 metaclass object
- #
+ mkw,
+ self.calculate_metaclass,
+ self.allow_py2_metaclass,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class PyClassMetaclassNode(ExprNode):
+ # Helper class holds Python3 metaclass object
+ #
# class_def_node PyClassDefNode PyClassDefNode defining this class
-
- subexprs = []
-
- def analyse_types(self, env):
- self.type = py_object_type
- self.is_temp = True
- return self
-
- def may_be_none(self):
- return True
-
- def generate_result_code(self, code):
+
+ subexprs = []
+
+ def analyse_types(self, env):
+ self.type = py_object_type
+ self.is_temp = True
+ return self
+
+ def may_be_none(self):
+ return True
+
+ def generate_result_code(self, code):
bases = self.class_def_node.bases
mkw = self.class_def_node.mkw
if mkw:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
- call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
+ call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
bases.result(),
mkw.result())
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
- call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
+ else:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
+ call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
bases.result())
- code.putln(
- "%s = %s; %s" % (
- self.result(), call,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
- # Helper class holds Python3 namespace object
- #
- # All this are not owned by this node
+ code.putln(
+ "%s = %s; %s" % (
+ self.result(), call,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
+ # Helper class holds Python3 namespace object
+ #
+ # All this are not owned by this node
# class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string (owned)
-
- subexprs = ['doc']
-
- def analyse_types(self, env):
- if self.doc:
+ # doc ExprNode or None Doc string (owned)
+
+ subexprs = ['doc']
+
+ def analyse_types(self, env):
+ if self.doc:
self.doc = self.doc.analyse_types(env).coerce_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- return self
-
- def may_be_none(self):
- return True
-
- def generate_result_code(self, code):
- cname = code.intern_identifier(self.name)
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
+ self.type = py_object_type
+ self.is_temp = 1
+ return self
+
+ def may_be_none(self):
+ return True
+
+ def generate_result_code(self, code):
+ cname = code.intern_identifier(self.name)
+ py_mod_name = self.get_py_mod_name(code)
+ qualname = self.get_py_qualified_name(code)
class_def_node = self.class_def_node
null = "(PyObject *) NULL"
doc_code = self.doc.result() if self.doc else null
mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null
metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null
- code.putln(
- "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
- self.result(),
- metaclass,
+ code.putln(
+ "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
+ self.result(),
+ metaclass,
class_def_node.bases.result(),
- cname,
- qualname,
- mkw,
- py_mod_name,
- doc_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class ClassCellInjectorNode(ExprNode):
- # Initialize CyFunction.func_classobj
- is_temp = True
- type = py_object_type
- subexprs = []
- is_active = False
-
- def analyse_expressions(self, env):
- return self
-
+ cname,
+ qualname,
+ mkw,
+ py_mod_name,
+ doc_code,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class ClassCellInjectorNode(ExprNode):
+ # Initialize CyFunction.func_classobj
+ is_temp = True
+ type = py_object_type
+ subexprs = []
+ is_active = False
+
+ def analyse_expressions(self, env):
+ return self
+
def generate_result_code(self, code):
assert self.is_active
code.putln(
@@ -9149,96 +9149,96 @@ class ClassCellInjectorNode(ExprNode):
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.result())
-
- def generate_injection_code(self, code, classobj_cname):
+
+ def generate_injection_code(self, code, classobj_cname):
assert self.is_active
code.globalstate.use_utility_code(
UtilityCode.load_cached("CyFunctionClassCell", "CythonFunction.c"))
code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % (
self.result(), classobj_cname))
-
-
-class ClassCellNode(ExprNode):
- # Class Cell for noargs super()
- subexprs = []
- is_temp = True
- is_generator = False
- type = py_object_type
-
- def analyse_types(self, env):
- return self
-
- def generate_result_code(self, code):
- if not self.is_generator:
- code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
- self.result(),
- Naming.self_cname))
- else:
- code.putln('%s = %s->classobj;' % (
- self.result(), Naming.generator_cname))
- code.putln(
- 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
- '"super(): empty __class__ cell"); %s }' % (
- self.result(),
- code.error_goto(self.pos)))
- code.put_incref(self.result(), py_object_type)
-
-
-class PyCFunctionNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # functions. Constructs a PyCFunction object
- # from a PyMethodDef struct.
- #
- # pymethdef_cname string PyMethodDef structure
- # self_object ExprNode or None
- # binding bool
- # def_node DefNode the Python function node
- # module_name EncodedString Name of defining module
- # code_object CodeObjectNode the PyCodeObject creator node
-
- subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict',
- 'annotations_dict']
-
- self_object = None
- code_object = None
- binding = False
- def_node = None
- defaults = None
- defaults_struct = None
- defaults_pyobjects = 0
- defaults_tuple = None
- defaults_kwdict = None
- annotations_dict = None
-
- type = py_object_type
- is_temp = 1
-
- specialized_cpdefs = None
- is_specialization = False
-
- @classmethod
- def from_defnode(cls, node, binding):
- return cls(node.pos,
- def_node=node,
- pymethdef_cname=node.entry.pymethdef_cname,
- binding=binding or node.specialized_cpdefs,
- specialized_cpdefs=node.specialized_cpdefs,
- code_object=CodeObjectNode(node))
-
- def analyse_types(self, env):
- if self.binding:
- self.analyse_default_args(env)
- return self
-
- def analyse_default_args(self, env):
- """
- Handle non-literal function's default arguments.
- """
- nonliteral_objects = []
- nonliteral_other = []
- default_args = []
- default_kwargs = []
- annotations = []
+
+
+class ClassCellNode(ExprNode):
+ # Class Cell for noargs super()
+ subexprs = []
+ is_temp = True
+ is_generator = False
+ type = py_object_type
+
+ def analyse_types(self, env):
+ return self
+
+ def generate_result_code(self, code):
+ if not self.is_generator:
+ code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
+ self.result(),
+ Naming.self_cname))
+ else:
+ code.putln('%s = %s->classobj;' % (
+ self.result(), Naming.generator_cname))
+ code.putln(
+ 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
+ '"super(): empty __class__ cell"); %s }' % (
+ self.result(),
+ code.error_goto(self.pos)))
+ code.put_incref(self.result(), py_object_type)
+
+
+class PyCFunctionNode(ExprNode, ModuleNameMixin):
+ # Helper class used in the implementation of Python
+ # functions. Constructs a PyCFunction object
+ # from a PyMethodDef struct.
+ #
+ # pymethdef_cname string PyMethodDef structure
+ # self_object ExprNode or None
+ # binding bool
+ # def_node DefNode the Python function node
+ # module_name EncodedString Name of defining module
+ # code_object CodeObjectNode the PyCodeObject creator node
+
+ subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict',
+ 'annotations_dict']
+
+ self_object = None
+ code_object = None
+ binding = False
+ def_node = None
+ defaults = None
+ defaults_struct = None
+ defaults_pyobjects = 0
+ defaults_tuple = None
+ defaults_kwdict = None
+ annotations_dict = None
+
+ type = py_object_type
+ is_temp = 1
+
+ specialized_cpdefs = None
+ is_specialization = False
+
+ @classmethod
+ def from_defnode(cls, node, binding):
+ return cls(node.pos,
+ def_node=node,
+ pymethdef_cname=node.entry.pymethdef_cname,
+ binding=binding or node.specialized_cpdefs,
+ specialized_cpdefs=node.specialized_cpdefs,
+ code_object=CodeObjectNode(node))
+
+ def analyse_types(self, env):
+ if self.binding:
+ self.analyse_default_args(env)
+ return self
+
+ def analyse_default_args(self, env):
+ """
+ Handle non-literal function's default arguments.
+ """
+ nonliteral_objects = []
+ nonliteral_other = []
+ default_args = []
+ default_kwargs = []
+ annotations = []
# For global cpdef functions and def/cpdef methods in cdef classes, we must use global constants
# for default arguments to avoid the dependency on the CyFunction object as 'self' argument
@@ -9247,23 +9247,23 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
# TODO: change CyFunction implementation to pass both function object and owning object for method calls
must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
- for arg in self.def_node.args:
+ for arg in self.def_node.args:
if arg.default and not must_use_constants:
- if not arg.default.is_literal:
- arg.is_dynamic = True
- if arg.type.is_pyobject:
- nonliteral_objects.append(arg)
- else:
- nonliteral_other.append(arg)
- else:
- arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
- if arg.kw_only:
- default_kwargs.append(arg)
- else:
- default_args.append(arg)
- if arg.annotation:
+ if not arg.default.is_literal:
+ arg.is_dynamic = True
+ if arg.type.is_pyobject:
+ nonliteral_objects.append(arg)
+ else:
+ nonliteral_other.append(arg)
+ else:
+ arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
+ if arg.kw_only:
+ default_kwargs.append(arg)
+ else:
+ default_args.append(arg)
+ if arg.annotation:
arg.annotation = self.analyse_annotation(env, arg.annotation)
- annotations.append((arg.pos, arg.name, arg.annotation))
+ annotations.append((arg.pos, arg.name, arg.annotation))
for arg in (self.def_node.star_arg, self.def_node.starstar_arg):
if arg and arg.annotation:
@@ -9275,82 +9275,82 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
annotation = self.analyse_annotation(env, annotation)
self.def_node.return_type_annotation = annotation
annotations.append((annotation.pos, StringEncoding.EncodedString("return"), annotation))
-
- if nonliteral_objects or nonliteral_other:
- module_scope = env.global_scope()
- cname = module_scope.next_id(Naming.defaults_struct_prefix)
- scope = Symtab.StructOrUnionScope(cname)
- self.defaults = []
- for arg in nonliteral_objects:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
- allow_pyobject=True)
- self.defaults.append((arg, entry))
- for arg in nonliteral_other:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
+
+ if nonliteral_objects or nonliteral_other:
+ module_scope = env.global_scope()
+ cname = module_scope.next_id(Naming.defaults_struct_prefix)
+ scope = Symtab.StructOrUnionScope(cname)
+ self.defaults = []
+ for arg in nonliteral_objects:
+ entry = scope.declare_var(arg.name, arg.type, None,
+ Naming.arg_prefix + arg.name,
+ allow_pyobject=True)
+ self.defaults.append((arg, entry))
+ for arg in nonliteral_other:
+ entry = scope.declare_var(arg.name, arg.type, None,
+ Naming.arg_prefix + arg.name,
allow_pyobject=False, allow_memoryview=True)
- self.defaults.append((arg, entry))
- entry = module_scope.declare_struct_or_union(
- None, 'struct', scope, 1, None, cname=cname)
- self.defaults_struct = scope
- self.defaults_pyobjects = len(nonliteral_objects)
- for arg, entry in self.defaults:
- arg.default_value = '%s->%s' % (
- Naming.dynamic_args_cname, entry.cname)
- self.def_node.defaults_struct = self.defaults_struct.name
-
- if default_args or default_kwargs:
- if self.defaults_struct is None:
- if default_args:
- defaults_tuple = TupleNode(self.pos, args=[
- arg.default for arg in default_args])
+ self.defaults.append((arg, entry))
+ entry = module_scope.declare_struct_or_union(
+ None, 'struct', scope, 1, None, cname=cname)
+ self.defaults_struct = scope
+ self.defaults_pyobjects = len(nonliteral_objects)
+ for arg, entry in self.defaults:
+ arg.default_value = '%s->%s' % (
+ Naming.dynamic_args_cname, entry.cname)
+ self.def_node.defaults_struct = self.defaults_struct.name
+
+ if default_args or default_kwargs:
+ if self.defaults_struct is None:
+ if default_args:
+ defaults_tuple = TupleNode(self.pos, args=[
+ arg.default for arg in default_args])
self.defaults_tuple = defaults_tuple.analyse_types(env).coerce_to_pyobject(env)
- if default_kwargs:
- defaults_kwdict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- arg.pos,
- key=IdentifierStringNode(arg.pos, value=arg.name),
- value=arg.default)
- for arg in default_kwargs])
- self.defaults_kwdict = defaults_kwdict.analyse_types(env)
- else:
- if default_args:
- defaults_tuple = DefaultsTupleNode(
- self.pos, default_args, self.defaults_struct)
- else:
- defaults_tuple = NoneNode(self.pos)
- if default_kwargs:
- defaults_kwdict = DefaultsKwDictNode(
- self.pos, default_kwargs, self.defaults_struct)
- else:
- defaults_kwdict = NoneNode(self.pos)
-
- defaults_getter = Nodes.DefNode(
- self.pos, args=[], star_arg=None, starstar_arg=None,
- body=Nodes.ReturnStatNode(
- self.pos, return_type=py_object_type,
- value=TupleNode(
- self.pos, args=[defaults_tuple, defaults_kwdict])),
- decorators=None,
- name=StringEncoding.EncodedString("__defaults__"))
+ if default_kwargs:
+ defaults_kwdict = DictNode(self.pos, key_value_pairs=[
+ DictItemNode(
+ arg.pos,
+ key=IdentifierStringNode(arg.pos, value=arg.name),
+ value=arg.default)
+ for arg in default_kwargs])
+ self.defaults_kwdict = defaults_kwdict.analyse_types(env)
+ else:
+ if default_args:
+ defaults_tuple = DefaultsTupleNode(
+ self.pos, default_args, self.defaults_struct)
+ else:
+ defaults_tuple = NoneNode(self.pos)
+ if default_kwargs:
+ defaults_kwdict = DefaultsKwDictNode(
+ self.pos, default_kwargs, self.defaults_struct)
+ else:
+ defaults_kwdict = NoneNode(self.pos)
+
+ defaults_getter = Nodes.DefNode(
+ self.pos, args=[], star_arg=None, starstar_arg=None,
+ body=Nodes.ReturnStatNode(
+ self.pos, return_type=py_object_type,
+ value=TupleNode(
+ self.pos, args=[defaults_tuple, defaults_kwdict])),
+ decorators=None,
+ name=StringEncoding.EncodedString("__defaults__"))
# defaults getter must never live in class scopes, it's always a module function
module_scope = env.global_scope()
defaults_getter.analyse_declarations(module_scope)
defaults_getter = defaults_getter.analyse_expressions(module_scope)
- defaults_getter.body = defaults_getter.body.analyse_expressions(
- defaults_getter.local_scope)
- defaults_getter.py_wrapper_required = False
- defaults_getter.pymethdef_required = False
- self.def_node.defaults_getter = defaults_getter
- if annotations:
- annotations_dict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- pos, key=IdentifierStringNode(pos, value=name),
- value=value)
- for pos, name, value in annotations])
- self.annotations_dict = annotations_dict.analyse_types(env)
-
+ defaults_getter.body = defaults_getter.body.analyse_expressions(
+ defaults_getter.local_scope)
+ defaults_getter.py_wrapper_required = False
+ defaults_getter.pymethdef_required = False
+ self.def_node.defaults_getter = defaults_getter
+ if annotations:
+ annotations_dict = DictNode(self.pos, key_value_pairs=[
+ DictItemNode(
+ pos, key=IdentifierStringNode(pos, value=name),
+ value=value)
+ for pos, name, value in annotations])
+ self.annotations_dict = annotations_dict.analyse_types(env)
+
def analyse_annotation(self, env, annotation):
if annotation is None:
return None
@@ -9365,425 +9365,425 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
annotation = annotation.coerce_to_pyobject(env)
return annotation
- def may_be_none(self):
- return False
-
- gil_message = "Constructing Python function"
-
- def self_result_code(self):
- if self.self_object is None:
- self_result = "NULL"
- else:
- self_result = self.self_object.py_result()
- return self_result
-
- def generate_result_code(self, code):
- if self.binding:
- self.generate_cyfunction_code(code)
- else:
- self.generate_pycfunction_code(code)
-
- def generate_pycfunction_code(self, code):
- py_mod_name = self.get_py_mod_name(code)
- code.putln(
- '%s = PyCFunction_NewEx(&%s, %s, %s); %s' % (
- self.result(),
- self.pymethdef_cname,
- self.self_result_code(),
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
-
- code.put_gotref(self.py_result())
-
- def generate_cyfunction_code(self, code):
- if self.specialized_cpdefs:
- def_node = self.specialized_cpdefs[0]
- else:
- def_node = self.def_node
-
- if self.specialized_cpdefs or self.is_specialization:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
+ def may_be_none(self):
+ return False
+
+ gil_message = "Constructing Python function"
+
+ def self_result_code(self):
+ if self.self_object is None:
+ self_result = "NULL"
+ else:
+ self_result = self.self_object.py_result()
+ return self_result
+
+ def generate_result_code(self, code):
+ if self.binding:
+ self.generate_cyfunction_code(code)
+ else:
+ self.generate_pycfunction_code(code)
+
+ def generate_pycfunction_code(self, code):
+ py_mod_name = self.get_py_mod_name(code)
+ code.putln(
+ '%s = PyCFunction_NewEx(&%s, %s, %s); %s' % (
+ self.result(),
+ self.pymethdef_cname,
+ self.self_result_code(),
+ py_mod_name,
+ code.error_goto_if_null(self.result(), self.pos)))
+
+ code.put_gotref(self.py_result())
+
+ def generate_cyfunction_code(self, code):
+ if self.specialized_cpdefs:
+ def_node = self.specialized_cpdefs[0]
+ else:
+ def_node = self.def_node
+
+ if self.specialized_cpdefs or self.is_specialization:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
constructor = "__pyx_FusedFunction_New"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
+ else:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
constructor = "__Pyx_CyFunction_New"
-
- if self.code_object:
- code_object_result = self.code_object.py_result()
- else:
- code_object_result = 'NULL'
-
- flags = []
- if def_node.is_staticmethod:
- flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
- elif def_node.is_classmethod:
- flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
-
+
+ if self.code_object:
+ code_object_result = self.code_object.py_result()
+ else:
+ code_object_result = 'NULL'
+
+ flags = []
+ if def_node.is_staticmethod:
+ flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
+ elif def_node.is_classmethod:
+ flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
+
if def_node.local_scope.parent_scope.is_c_class_scope and not def_node.entry.is_anonymous:
- flags.append('__Pyx_CYFUNCTION_CCLASS')
-
- if flags:
- flags = ' | '.join(flags)
- else:
- flags = '0'
-
- code.putln(
- '%s = %s(&%s, %s, %s, %s, %s, %s, %s); %s' % (
- self.result(),
- constructor,
- self.pymethdef_cname,
- flags,
- self.get_py_qualified_name(code),
- self.self_result_code(),
- self.get_py_mod_name(code),
- Naming.moddict_cname,
- code_object_result,
- code.error_goto_if_null(self.result(), self.pos)))
-
- code.put_gotref(self.py_result())
-
- if def_node.requires_classobj:
- assert code.pyclass_stack, "pyclass_stack is empty"
- class_node = code.pyclass_stack[-1]
- code.put_incref(self.py_result(), py_object_type)
- code.putln(
- 'PyList_Append(%s, %s);' % (
- class_node.class_cell.result(),
- self.result()))
- code.put_giveref(self.py_result())
-
- if self.defaults:
- code.putln(
- 'if (!__Pyx_CyFunction_InitDefaults(%s, sizeof(%s), %d)) %s' % (
- self.result(), self.defaults_struct.name,
- self.defaults_pyobjects, code.error_goto(self.pos)))
- defaults = '__Pyx_CyFunction_Defaults(%s, %s)' % (
- self.defaults_struct.name, self.result())
- for arg, entry in self.defaults:
- arg.generate_assignment_code(code, target='%s->%s' % (
- defaults, entry.cname))
-
- if self.defaults_tuple:
- code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
- self.result(), self.defaults_tuple.py_result()))
- if self.defaults_kwdict:
- code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
- self.result(), self.defaults_kwdict.py_result()))
+ flags.append('__Pyx_CYFUNCTION_CCLASS')
+
+ if flags:
+ flags = ' | '.join(flags)
+ else:
+ flags = '0'
+
+ code.putln(
+ '%s = %s(&%s, %s, %s, %s, %s, %s, %s); %s' % (
+ self.result(),
+ constructor,
+ self.pymethdef_cname,
+ flags,
+ self.get_py_qualified_name(code),
+ self.self_result_code(),
+ self.get_py_mod_name(code),
+ Naming.moddict_cname,
+ code_object_result,
+ code.error_goto_if_null(self.result(), self.pos)))
+
+ code.put_gotref(self.py_result())
+
+ if def_node.requires_classobj:
+ assert code.pyclass_stack, "pyclass_stack is empty"
+ class_node = code.pyclass_stack[-1]
+ code.put_incref(self.py_result(), py_object_type)
+ code.putln(
+ 'PyList_Append(%s, %s);' % (
+ class_node.class_cell.result(),
+ self.result()))
+ code.put_giveref(self.py_result())
+
+ if self.defaults:
+ code.putln(
+ 'if (!__Pyx_CyFunction_InitDefaults(%s, sizeof(%s), %d)) %s' % (
+ self.result(), self.defaults_struct.name,
+ self.defaults_pyobjects, code.error_goto(self.pos)))
+ defaults = '__Pyx_CyFunction_Defaults(%s, %s)' % (
+ self.defaults_struct.name, self.result())
+ for arg, entry in self.defaults:
+ arg.generate_assignment_code(code, target='%s->%s' % (
+ defaults, entry.cname))
+
+ if self.defaults_tuple:
+ code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
+ self.result(), self.defaults_tuple.py_result()))
+ if self.defaults_kwdict:
+ code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
+ self.result(), self.defaults_kwdict.py_result()))
if def_node.defaults_getter and not self.specialized_cpdefs:
# Fused functions do not support dynamic defaults, only their specialisations can have them for now.
- code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
- self.result(), def_node.defaults_getter.entry.pyfunc_cname))
- if self.annotations_dict:
- code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
- self.result(), self.annotations_dict.py_result()))
-
-
-class InnerFunctionNode(PyCFunctionNode):
- # Special PyCFunctionNode that depends on a closure class
- #
-
- binding = True
- needs_self_code = True
-
- def self_result_code(self):
- if self.needs_self_code:
- return "((PyObject*)%s)" % Naming.cur_scope_cname
- return "NULL"
-
-
-class CodeObjectNode(ExprNode):
- # Create a PyCodeObject for a CyFunction instance.
- #
- # def_node DefNode the Python function node
- # varnames TupleNode a tuple with all local variable names
-
- subexprs = ['varnames']
- is_temp = False
+ code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
+ self.result(), def_node.defaults_getter.entry.pyfunc_cname))
+ if self.annotations_dict:
+ code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
+ self.result(), self.annotations_dict.py_result()))
+
+
+class InnerFunctionNode(PyCFunctionNode):
+ # Special PyCFunctionNode that depends on a closure class
+ #
+
+ binding = True
+ needs_self_code = True
+
+ def self_result_code(self):
+ if self.needs_self_code:
+ return "((PyObject*)%s)" % Naming.cur_scope_cname
+ return "NULL"
+
+
+class CodeObjectNode(ExprNode):
+ # Create a PyCodeObject for a CyFunction instance.
+ #
+ # def_node DefNode the Python function node
+ # varnames TupleNode a tuple with all local variable names
+
+ subexprs = ['varnames']
+ is_temp = False
result_code = None
-
- def __init__(self, def_node):
- ExprNode.__init__(self, def_node.pos, def_node=def_node)
- args = list(def_node.args)
- # if we have args/kwargs, then the first two in var_entries are those
- local_vars = [arg for arg in def_node.local_scope.var_entries if arg.name]
- self.varnames = TupleNode(
- def_node.pos,
- args=[IdentifierStringNode(arg.pos, value=arg.name)
- for arg in args + local_vars],
- is_temp=0,
- is_literal=1)
-
- def may_be_none(self):
- return False
-
+
+ def __init__(self, def_node):
+ ExprNode.__init__(self, def_node.pos, def_node=def_node)
+ args = list(def_node.args)
+ # if we have args/kwargs, then the first two in var_entries are those
+ local_vars = [arg for arg in def_node.local_scope.var_entries if arg.name]
+ self.varnames = TupleNode(
+ def_node.pos,
+ args=[IdentifierStringNode(arg.pos, value=arg.name)
+ for arg in args + local_vars],
+ is_temp=0,
+ is_literal=1)
+
+ def may_be_none(self):
+ return False
+
def calculate_result_code(self, code=None):
if self.result_code is None:
self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- return self.result_code
-
- def generate_result_code(self, code):
+ return self.result_code
+
+ def generate_result_code(self, code):
if self.result_code is None:
self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
-
+
code = code.get_cached_constants_writer(self.result_code)
if code is None:
return # already initialised
- code.mark_pos(self.pos)
- func = self.def_node
- func_name = code.get_py_string_const(
- func.name, identifier=True, is_str=False, unicode_value=func.name)
- # FIXME: better way to get the module file path at module init time? Encoding to use?
+ code.mark_pos(self.pos)
+ func = self.def_node
+ func_name = code.get_py_string_const(
+ func.name, identifier=True, is_str=False, unicode_value=func.name)
+ # FIXME: better way to get the module file path at module init time? Encoding to use?
file_path = StringEncoding.bytes_literal(func.pos[0].get_filenametable_entry().encode('utf8'), 'utf8')
# XXX Use get_description() to set arcadia root relative filename
file_path = StringEncoding.bytes_literal(func.pos[0].get_description().encode('utf8'), 'utf8')
- file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
-
+ file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
+
# This combination makes CPython create a new dict for "frame.f_locals" (see GH #1836).
flags = ['CO_OPTIMIZED', 'CO_NEWLOCALS']
- if self.def_node.star_arg:
- flags.append('CO_VARARGS')
- if self.def_node.starstar_arg:
- flags.append('CO_VARKEYWORDS')
-
- code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
- self.result_code,
- len(func.args) - func.num_kwonly_args, # argcount
- func.num_kwonly_args, # kwonlyargcount (Py3 only)
- len(self.varnames.args), # nlocals
- '|'.join(flags) or '0', # flags
- Naming.empty_bytes, # code
- Naming.empty_tuple, # consts
- Naming.empty_tuple, # names (FIXME)
- self.varnames.result(), # varnames
- Naming.empty_tuple, # freevars (FIXME)
- Naming.empty_tuple, # cellvars (FIXME)
- file_path_const, # filename
- func_name, # name
- self.pos[1], # firstlineno
- Naming.empty_bytes, # lnotab
- code.error_goto_if_null(self.result_code, self.pos),
- ))
-
-
-class DefaultLiteralArgNode(ExprNode):
- # CyFunction's literal argument default value
- #
- # Evaluate literal only once.
-
- subexprs = []
- is_literal = True
- is_temp = False
-
- def __init__(self, pos, arg):
- super(DefaultLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.type = self.arg.type
- self.evaluated = False
-
- def analyse_types(self, env):
- return self
-
- def generate_result_code(self, code):
- pass
-
- def generate_evaluation_code(self, code):
- if not self.evaluated:
- self.arg.generate_evaluation_code(code)
- self.evaluated = True
-
- def result(self):
- return self.type.cast_code(self.arg.result())
-
-
-class DefaultNonLiteralArgNode(ExprNode):
- # CyFunction's non-literal argument default value
-
- subexprs = []
-
- def __init__(self, pos, arg, defaults_struct):
- super(DefaultNonLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.defaults_struct = defaults_struct
-
- def analyse_types(self, env):
- self.type = self.arg.type
- self.is_temp = False
- return self
-
- def generate_result_code(self, code):
- pass
-
- def result(self):
- return '__Pyx_CyFunction_Defaults(%s, %s)->%s' % (
- self.defaults_struct.name, Naming.self_cname,
- self.defaults_struct.lookup(self.arg.name).cname)
-
-
-class DefaultsTupleNode(TupleNode):
- # CyFunction's __defaults__ tuple
-
- def __init__(self, pos, defaults, defaults_struct):
- args = []
- for arg in defaults:
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- args.append(arg)
- super(DefaultsTupleNode, self).__init__(pos, args=args)
-
+ if self.def_node.star_arg:
+ flags.append('CO_VARARGS')
+ if self.def_node.starstar_arg:
+ flags.append('CO_VARKEYWORDS')
+
+ code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
+ self.result_code,
+ len(func.args) - func.num_kwonly_args, # argcount
+ func.num_kwonly_args, # kwonlyargcount (Py3 only)
+ len(self.varnames.args), # nlocals
+ '|'.join(flags) or '0', # flags
+ Naming.empty_bytes, # code
+ Naming.empty_tuple, # consts
+ Naming.empty_tuple, # names (FIXME)
+ self.varnames.result(), # varnames
+ Naming.empty_tuple, # freevars (FIXME)
+ Naming.empty_tuple, # cellvars (FIXME)
+ file_path_const, # filename
+ func_name, # name
+ self.pos[1], # firstlineno
+ Naming.empty_bytes, # lnotab
+ code.error_goto_if_null(self.result_code, self.pos),
+ ))
+
+
+class DefaultLiteralArgNode(ExprNode):
+ # CyFunction's literal argument default value
+ #
+ # Evaluate literal only once.
+
+ subexprs = []
+ is_literal = True
+ is_temp = False
+
+ def __init__(self, pos, arg):
+ super(DefaultLiteralArgNode, self).__init__(pos)
+ self.arg = arg
+ self.type = self.arg.type
+ self.evaluated = False
+
+ def analyse_types(self, env):
+ return self
+
+ def generate_result_code(self, code):
+ pass
+
+ def generate_evaluation_code(self, code):
+ if not self.evaluated:
+ self.arg.generate_evaluation_code(code)
+ self.evaluated = True
+
+ def result(self):
+ return self.type.cast_code(self.arg.result())
+
+
+class DefaultNonLiteralArgNode(ExprNode):
+ # CyFunction's non-literal argument default value
+
+ subexprs = []
+
+ def __init__(self, pos, arg, defaults_struct):
+ super(DefaultNonLiteralArgNode, self).__init__(pos)
+ self.arg = arg
+ self.defaults_struct = defaults_struct
+
+ def analyse_types(self, env):
+ self.type = self.arg.type
+ self.is_temp = False
+ return self
+
+ def generate_result_code(self, code):
+ pass
+
+ def result(self):
+ return '__Pyx_CyFunction_Defaults(%s, %s)->%s' % (
+ self.defaults_struct.name, Naming.self_cname,
+ self.defaults_struct.lookup(self.arg.name).cname)
+
+
+class DefaultsTupleNode(TupleNode):
+ # CyFunction's __defaults__ tuple
+
+ def __init__(self, pos, defaults, defaults_struct):
+ args = []
+ for arg in defaults:
+ if not arg.default.is_literal:
+ arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
+ else:
+ arg = arg.default
+ args.append(arg)
+ super(DefaultsTupleNode, self).__init__(pos, args=args)
+
def analyse_types(self, env, skip_children=False):
return super(DefaultsTupleNode, self).analyse_types(env, skip_children).coerce_to_pyobject(env)
-
-
-class DefaultsKwDictNode(DictNode):
- # CyFunction's __kwdefaults__ dict
-
- def __init__(self, pos, defaults, defaults_struct):
- items = []
- for arg in defaults:
- name = IdentifierStringNode(arg.pos, value=arg.name)
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- items.append(DictItemNode(arg.pos, key=name, value=arg))
- super(DefaultsKwDictNode, self).__init__(pos, key_value_pairs=items)
-
-
-class LambdaNode(InnerFunctionNode):
- # Lambda expression node (only used as a function reference)
- #
- # args [CArgDeclNode] formal arguments
- # star_arg PyArgDeclNode or None * argument
- # starstar_arg PyArgDeclNode or None ** argument
- # lambda_name string a module-globally unique lambda name
- # result_expr ExprNode
- # def_node DefNode the underlying function 'def' node
-
- child_attrs = ['def_node']
-
- name = StringEncoding.EncodedString('<lambda>')
-
- def analyse_declarations(self, env):
+
+
+class DefaultsKwDictNode(DictNode):
+ # CyFunction's __kwdefaults__ dict
+
+ def __init__(self, pos, defaults, defaults_struct):
+ items = []
+ for arg in defaults:
+ name = IdentifierStringNode(arg.pos, value=arg.name)
+ if not arg.default.is_literal:
+ arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
+ else:
+ arg = arg.default
+ items.append(DictItemNode(arg.pos, key=name, value=arg))
+ super(DefaultsKwDictNode, self).__init__(pos, key_value_pairs=items)
+
+
+class LambdaNode(InnerFunctionNode):
+ # Lambda expression node (only used as a function reference)
+ #
+ # args [CArgDeclNode] formal arguments
+ # star_arg PyArgDeclNode or None * argument
+ # starstar_arg PyArgDeclNode or None ** argument
+ # lambda_name string a module-globally unique lambda name
+ # result_expr ExprNode
+ # def_node DefNode the underlying function 'def' node
+
+ child_attrs = ['def_node']
+
+ name = StringEncoding.EncodedString('<lambda>')
+
+ def analyse_declarations(self, env):
self.lambda_name = self.def_node.lambda_name = env.next_id('lambda')
- self.def_node.no_assignment_synthesis = True
- self.def_node.pymethdef_required = True
- self.def_node.analyse_declarations(env)
- self.def_node.is_cyfunction = True
- self.pymethdef_cname = self.def_node.entry.pymethdef_cname
- env.add_lambda_def(self.def_node)
-
- def analyse_types(self, env):
- self.def_node = self.def_node.analyse_expressions(env)
- return super(LambdaNode, self).analyse_types(env)
-
- def generate_result_code(self, code):
- self.def_node.generate_execution_code(code)
- super(LambdaNode, self).generate_result_code(code)
-
-
-class GeneratorExpressionNode(LambdaNode):
- # A generator expression, e.g. (i for i in range(10))
- #
- # Result is a generator.
- #
- # loop ForStatNode the for-loop, containing a YieldExprNode
- # def_node DefNode the underlying generator 'def' node
-
- name = StringEncoding.EncodedString('genexpr')
- binding = False
-
- def analyse_declarations(self, env):
+ self.def_node.no_assignment_synthesis = True
+ self.def_node.pymethdef_required = True
+ self.def_node.analyse_declarations(env)
+ self.def_node.is_cyfunction = True
+ self.pymethdef_cname = self.def_node.entry.pymethdef_cname
+ env.add_lambda_def(self.def_node)
+
+ def analyse_types(self, env):
+ self.def_node = self.def_node.analyse_expressions(env)
+ return super(LambdaNode, self).analyse_types(env)
+
+ def generate_result_code(self, code):
+ self.def_node.generate_execution_code(code)
+ super(LambdaNode, self).generate_result_code(code)
+
+
+class GeneratorExpressionNode(LambdaNode):
+ # A generator expression, e.g. (i for i in range(10))
+ #
+ # Result is a generator.
+ #
+ # loop ForStatNode the for-loop, containing a YieldExprNode
+ # def_node DefNode the underlying generator 'def' node
+
+ name = StringEncoding.EncodedString('genexpr')
+ binding = False
+
+ def analyse_declarations(self, env):
self.genexpr_name = env.next_id('genexpr')
- super(GeneratorExpressionNode, self).analyse_declarations(env)
- # No pymethdef required
- self.def_node.pymethdef_required = False
- self.def_node.py_wrapper_required = False
- self.def_node.is_cyfunction = False
- # Force genexpr signature
- self.def_node.entry.signature = TypeSlots.pyfunction_noargs
-
- def generate_result_code(self, code):
- code.putln(
- '%s = %s(%s); %s' % (
- self.result(),
- self.def_node.entry.pyfunc_cname,
- self.self_result_code(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
-
-class YieldExprNode(ExprNode):
- # Yield expression node
- #
- # arg ExprNode the value to return from the generator
- # label_num integer yield label number
- # is_yield_from boolean is a YieldFromExprNode to delegate to another generator
-
- subexprs = ['arg']
- type = py_object_type
- label_num = 0
- is_yield_from = False
+ super(GeneratorExpressionNode, self).analyse_declarations(env)
+ # No pymethdef required
+ self.def_node.pymethdef_required = False
+ self.def_node.py_wrapper_required = False
+ self.def_node.is_cyfunction = False
+ # Force genexpr signature
+ self.def_node.entry.signature = TypeSlots.pyfunction_noargs
+
+ def generate_result_code(self, code):
+ code.putln(
+ '%s = %s(%s); %s' % (
+ self.result(),
+ self.def_node.entry.pyfunc_cname,
+ self.self_result_code(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
+class YieldExprNode(ExprNode):
+ # Yield expression node
+ #
+ # arg ExprNode the value to return from the generator
+ # label_num integer yield label number
+ # is_yield_from boolean is a YieldFromExprNode to delegate to another generator
+
+ subexprs = ['arg']
+ type = py_object_type
+ label_num = 0
+ is_yield_from = False
is_await = False
in_async_gen = False
expr_keyword = 'yield'
-
- def analyse_types(self, env):
+
+ def analyse_types(self, env):
if not self.label_num or (self.is_yield_from and self.in_async_gen):
error(self.pos, "'%s' not supported here" % self.expr_keyword)
- self.is_temp = 1
- if self.arg is not None:
- self.arg = self.arg.analyse_types(env)
- if not self.arg.type.is_pyobject:
- self.coerce_yield_argument(env)
- return self
-
- def coerce_yield_argument(self, env):
- self.arg = self.arg.coerce_to_pyobject(env)
-
- def generate_evaluation_code(self, code):
- if self.arg:
- self.arg.generate_evaluation_code(code)
- self.arg.make_owned_reference(code)
- code.putln(
- "%s = %s;" % (
- Naming.retval_cname,
- self.arg.result_as(py_object_type)))
- self.arg.generate_post_assignment_code(code)
- self.arg.free_temps(code)
- else:
- code.put_init_to_py_none(Naming.retval_cname, py_object_type)
- self.generate_yield_code(code)
-
- def generate_yield_code(self, code):
- """
- Generate the code to return the argument in 'Naming.retval_cname'
- and to continue at the yield label.
- """
+ self.is_temp = 1
+ if self.arg is not None:
+ self.arg = self.arg.analyse_types(env)
+ if not self.arg.type.is_pyobject:
+ self.coerce_yield_argument(env)
+ return self
+
+ def coerce_yield_argument(self, env):
+ self.arg = self.arg.coerce_to_pyobject(env)
+
+ def generate_evaluation_code(self, code):
+ if self.arg:
+ self.arg.generate_evaluation_code(code)
+ self.arg.make_owned_reference(code)
+ code.putln(
+ "%s = %s;" % (
+ Naming.retval_cname,
+ self.arg.result_as(py_object_type)))
+ self.arg.generate_post_assignment_code(code)
+ self.arg.free_temps(code)
+ else:
+ code.put_init_to_py_none(Naming.retval_cname, py_object_type)
+ self.generate_yield_code(code)
+
+ def generate_yield_code(self, code):
+ """
+ Generate the code to return the argument in 'Naming.retval_cname'
+ and to continue at the yield label.
+ """
label_num, label_name = code.new_yield_label(
self.expr_keyword.replace(' ', '_'))
- code.use_label(label_name)
-
- saved = []
- code.funcstate.closure_temps.reset()
- for cname, type, manage_ref in code.funcstate.temps_in_use():
- save_cname = code.funcstate.closure_temps.allocate_temp(type)
- saved.append((cname, save_cname, type))
- if type.is_pyobject:
- code.put_xgiveref(cname)
- code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
-
- code.put_xgiveref(Naming.retval_cname)
+ code.use_label(label_name)
+
+ saved = []
+ code.funcstate.closure_temps.reset()
+ for cname, type, manage_ref in code.funcstate.temps_in_use():
+ save_cname = code.funcstate.closure_temps.allocate_temp(type)
+ saved.append((cname, save_cname, type))
+ if type.is_pyobject:
+ code.put_xgiveref(cname)
+ code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
+
+ code.put_xgiveref(Naming.retval_cname)
profile = code.globalstate.directives['profile']
linetrace = code.globalstate.directives['linetrace']
if profile or linetrace:
code.put_trace_return(Naming.retval_cname,
nogil=not code.funcstate.gil_owned)
- code.put_finish_refcount_context()
+ code.put_finish_refcount_context()
if code.funcstate.current_except is not None:
# inside of an except block => save away currently handled exception
@@ -9795,59 +9795,59 @@ class YieldExprNode(ExprNode):
code.putln("/* return from %sgenerator, %sing value */" % (
'async ' if self.in_async_gen else '',
'await' if self.is_await else 'yield'))
- code.putln("%s->resume_label = %d;" % (
- Naming.generator_cname, label_num))
+ code.putln("%s->resume_label = %d;" % (
+ Naming.generator_cname, label_num))
if self.in_async_gen and not self.is_await:
# __Pyx__PyAsyncGenValueWrapperNew() steals a reference to the return value
code.putln("return __Pyx__PyAsyncGenValueWrapperNew(%s);" % Naming.retval_cname)
else:
code.putln("return %s;" % Naming.retval_cname)
-
- code.put_label(label_name)
- for cname, save_cname, type in saved:
- code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
- if type.is_pyobject:
- code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
- code.put_xgotref(cname)
+
+ code.put_label(label_name)
+ for cname, save_cname, type in saved:
+ code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
+ if type.is_pyobject:
+ code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
+ code.put_xgotref(cname)
self.generate_sent_value_handling_code(code, Naming.sent_value_cname)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname))
- code.put_incref(self.result(), py_object_type)
-
+ if self.result_is_used:
+ self.allocate_temp_result(code)
+ code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname))
+ code.put_incref(self.result(), py_object_type)
+
def generate_sent_value_handling_code(self, code, value_cname):
code.putln(code.error_goto_if_null(value_cname, self.pos))
-
-
+
+
class _YieldDelegationExprNode(YieldExprNode):
def yield_from_func(self, code):
raise NotImplementedError()
-
+
def generate_evaluation_code(self, code, source_cname=None, decref_source=False):
if source_cname is None:
self.arg.generate_evaluation_code(code)
code.putln("%s = %s(%s, %s);" % (
- Naming.retval_cname,
+ Naming.retval_cname,
self.yield_from_func(code),
- Naming.generator_cname,
+ Naming.generator_cname,
self.arg.py_result() if source_cname is None else source_cname))
if source_cname is None:
self.arg.generate_disposal_code(code)
self.arg.free_temps(code)
elif decref_source:
code.put_decref_clear(source_cname, py_object_type)
- code.put_xgotref(Naming.retval_cname)
-
- code.putln("if (likely(%s)) {" % Naming.retval_cname)
- self.generate_yield_code(code)
- code.putln("} else {")
- # either error or sub-generator has normally terminated: return value => node result
- if self.result_is_used:
+ code.put_xgotref(Naming.retval_cname)
+
+ code.putln("if (likely(%s)) {" % Naming.retval_cname)
+ self.generate_yield_code(code)
+ code.putln("} else {")
+ # either error or sub-generator has normally terminated: return value => node result
+ if self.result_is_used:
self.fetch_iteration_result(code)
- else:
+ else:
self.handle_iteration_exception(code)
- code.putln("}")
-
+ code.putln("}")
+
def fetch_iteration_result(self, code):
# YieldExprNode has allocated the result temp for us
code.putln("%s = NULL;" % self.result())
@@ -9927,186 +9927,186 @@ class AwaitIterNextExprNode(AwaitExprNode):
code.putln("}")
-class GlobalsExprNode(AtomicExprNode):
- type = dict_type
- is_temp = 1
-
- def analyse_types(self, env):
- env.use_utility_code(Builtin.globals_utility_code)
- return self
-
- gil_message = "Constructing globals dict"
-
- def may_be_none(self):
- return False
-
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_Globals(); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
-
-
-class LocalsDictItemNode(DictItemNode):
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- if self.value.type.can_coerce_to_pyobject(env):
- self.value = self.value.coerce_to_pyobject(env)
- else:
- self.value = None
- return self
-
-
-class FuncLocalsExprNode(DictNode):
- def __init__(self, pos, env):
- local_vars = sorted([
- entry.name for entry in env.entries.values() if entry.name])
- items = [LocalsDictItemNode(
- pos, key=IdentifierStringNode(pos, value=var),
- value=NameNode(pos, name=var, allow_null=True))
- for var in local_vars]
- DictNode.__init__(self, pos, key_value_pairs=items,
- exclude_null_values=True)
-
- def analyse_types(self, env):
- node = super(FuncLocalsExprNode, self).analyse_types(env)
- node.key_value_pairs = [ i for i in node.key_value_pairs
- if i.value is not None ]
- return node
-
-
-class PyClassLocalsExprNode(AtomicExprNode):
- def __init__(self, pos, pyclass_dict):
- AtomicExprNode.__init__(self, pos)
- self.pyclass_dict = pyclass_dict
-
- def analyse_types(self, env):
- self.type = self.pyclass_dict.type
- self.is_temp = False
- return self
-
- def may_be_none(self):
- return False
-
- def result(self):
- return self.pyclass_dict.result()
-
- def generate_result_code(self, code):
- pass
-
-
-def LocalsExprNode(pos, scope_node, env):
- if env.is_module_scope:
- return GlobalsExprNode(pos)
- if env.is_py_class_scope:
- return PyClassLocalsExprNode(pos, scope_node.dict)
- return FuncLocalsExprNode(pos, env)
-
-
-#-------------------------------------------------------------------
-#
-# Unary operator nodes
-#
-#-------------------------------------------------------------------
-
-compile_time_unary_operators = {
- 'not': operator.not_,
- '~': operator.inv,
- '-': operator.neg,
- '+': operator.pos,
-}
-
-class UnopNode(ExprNode):
- # operator string
- # operand ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when the operand is not a pyobject.
- # - Check operand type and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
-
- subexprs = ['operand']
- infix = True
-
- def calculate_constant_result(self):
- func = compile_time_unary_operators[self.operator]
- self.constant_result = func(self.operand.constant_result)
-
- def compile_time_value(self, denv):
- func = compile_time_unary_operators.get(self.operator)
- if not func:
- error(self.pos,
- "Unary '%s' not supported in compile-time expression"
- % self.operator)
- operand = self.operand.compile_time_value(denv)
- try:
- return func(operand)
+class GlobalsExprNode(AtomicExprNode):
+ type = dict_type
+ is_temp = 1
+
+ def analyse_types(self, env):
+ env.use_utility_code(Builtin.globals_utility_code)
+ return self
+
+ gil_message = "Constructing globals dict"
+
+ def may_be_none(self):
+ return False
+
+ def generate_result_code(self, code):
+ code.putln('%s = __Pyx_Globals(); %s' % (
+ self.result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.result())
+
+
+class LocalsDictItemNode(DictItemNode):
+ def analyse_types(self, env):
+ self.key = self.key.analyse_types(env)
+ self.value = self.value.analyse_types(env)
+ self.key = self.key.coerce_to_pyobject(env)
+ if self.value.type.can_coerce_to_pyobject(env):
+ self.value = self.value.coerce_to_pyobject(env)
+ else:
+ self.value = None
+ return self
+
+
+class FuncLocalsExprNode(DictNode):
+ def __init__(self, pos, env):
+ local_vars = sorted([
+ entry.name for entry in env.entries.values() if entry.name])
+ items = [LocalsDictItemNode(
+ pos, key=IdentifierStringNode(pos, value=var),
+ value=NameNode(pos, name=var, allow_null=True))
+ for var in local_vars]
+ DictNode.__init__(self, pos, key_value_pairs=items,
+ exclude_null_values=True)
+
+ def analyse_types(self, env):
+ node = super(FuncLocalsExprNode, self).analyse_types(env)
+ node.key_value_pairs = [ i for i in node.key_value_pairs
+ if i.value is not None ]
+ return node
+
+
+class PyClassLocalsExprNode(AtomicExprNode):
+ def __init__(self, pos, pyclass_dict):
+ AtomicExprNode.__init__(self, pos)
+ self.pyclass_dict = pyclass_dict
+
+ def analyse_types(self, env):
+ self.type = self.pyclass_dict.type
+ self.is_temp = False
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def result(self):
+ return self.pyclass_dict.result()
+
+ def generate_result_code(self, code):
+ pass
+
+
+def LocalsExprNode(pos, scope_node, env):
+ if env.is_module_scope:
+ return GlobalsExprNode(pos)
+ if env.is_py_class_scope:
+ return PyClassLocalsExprNode(pos, scope_node.dict)
+ return FuncLocalsExprNode(pos, env)
+
+
+#-------------------------------------------------------------------
+#
+# Unary operator nodes
+#
+#-------------------------------------------------------------------
+
+compile_time_unary_operators = {
+ 'not': operator.not_,
+ '~': operator.inv,
+ '-': operator.neg,
+ '+': operator.pos,
+}
+
+class UnopNode(ExprNode):
+ # operator string
+ # operand ExprNode
+ #
+ # Processing during analyse_expressions phase:
+ #
+ # analyse_c_operation
+ # Called when the operand is not a pyobject.
+ # - Check operand type and coerce if needed.
+ # - Determine result type and result code fragment.
+ # - Allocate temporary for result if needed.
+
+ subexprs = ['operand']
+ infix = True
+
+ def calculate_constant_result(self):
+ func = compile_time_unary_operators[self.operator]
+ self.constant_result = func(self.operand.constant_result)
+
+ def compile_time_value(self, denv):
+ func = compile_time_unary_operators.get(self.operator)
+ if not func:
+ error(self.pos,
+ "Unary '%s' not supported in compile-time expression"
+ % self.operator)
+ operand = self.operand.compile_time_value(denv)
+ try:
+ return func(operand)
except Exception as e:
- self.compile_time_value_error(e)
-
- def infer_type(self, env):
- operand_type = self.operand.infer_type(env)
- if operand_type.is_cpp_class or operand_type.is_ptr:
- cpp_type = operand_type.find_cpp_operation_type(self.operator)
- if cpp_type is not None:
- return cpp_type
- return self.infer_unop_type(env, operand_type)
-
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_pyobject:
- return py_object_type
- else:
- return operand_type
-
- def may_be_none(self):
- if self.operand.type and self.operand.type.is_builtin_type:
- if self.operand.type is not type_type:
- return False
- return ExprNode.may_be_none(self)
-
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
+ self.compile_time_value_error(e)
+
+ def infer_type(self, env):
+ operand_type = self.operand.infer_type(env)
+ if operand_type.is_cpp_class or operand_type.is_ptr:
+ cpp_type = operand_type.find_cpp_operation_type(self.operator)
+ if cpp_type is not None:
+ return cpp_type
+ return self.infer_unop_type(env, operand_type)
+
+ def infer_unop_type(self, env, operand_type):
+ if operand_type.is_pyobject:
+ return py_object_type
+ else:
+ return operand_type
+
+ def may_be_none(self):
+ if self.operand.type and self.operand.type.is_builtin_type:
+ if self.operand.type is not type_type:
+ return False
+ return ExprNode.may_be_none(self)
+
+ def analyse_types(self, env):
+ self.operand = self.operand.analyse_types(env)
if self.is_pythran_operation(env):
self.type = PythranExpr(pythran_unaryop_type(self.operator, self.operand.type))
self.is_temp = 1
elif self.is_py_operation():
- self.coerce_operand_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
- return self
-
- def check_const(self):
- return self.operand.check_const()
-
- def is_py_operation(self):
+ self.coerce_operand_to_pyobject(env)
+ self.type = py_object_type
+ self.is_temp = 1
+ elif self.is_cpp_operation():
+ self.analyse_cpp_operation(env)
+ else:
+ self.analyse_c_operation(env)
+ return self
+
+ def check_const(self):
+ return self.operand.check_const()
+
+ def is_py_operation(self):
return self.operand.type.is_pyobject or self.operand.type.is_ctuple
-
+
def is_pythran_operation(self, env):
np_pythran = has_np_pythran(env)
op_type = self.operand.type
return np_pythran and (op_type.is_buffer or op_type.is_pythran_expr)
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
-
- def is_cpp_operation(self):
- type = self.operand.type
- return type.is_cpp_class
-
- def coerce_operand_to_pyobject(self, env):
- self.operand = self.operand.coerce_to_pyobject(env)
-
- def generate_result_code(self, code):
+ def nogil_check(self, env):
+ if self.is_py_operation():
+ self.gil_error()
+
+ def is_cpp_operation(self):
+ type = self.operand.type
+ return type.is_cpp_class
+
+ def coerce_operand_to_pyobject(self, env):
+ self.operand = self.operand.coerce_to_pyobject(env)
+
+ def generate_result_code(self, code):
if self.type.is_pythran_expr:
code.putln("// Pythran unaryop")
code.putln("__Pyx_call_destructor(%s);" % self.result())
@@ -10116,7 +10116,7 @@ class UnopNode(ExprNode):
self.operator,
self.operand.pythran_result()))
elif self.operand.type.is_pyobject:
- self.generate_py_operation_code(code)
+ self.generate_py_operation_code(code)
elif self.is_temp:
if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos,
@@ -10125,23 +10125,23 @@ class UnopNode(ExprNode):
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
-
- def generate_py_operation_code(self, code):
- function = self.py_operation_function(code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- function,
- self.operand.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
-
- def type_error(self):
- if not self.operand.type.is_error:
- error(self.pos, "Invalid operand type for '%s' (%s)" %
- (self.operator, self.operand.type))
- self.type = PyrexTypes.error_type
-
+
+ def generate_py_operation_code(self, code):
+ function = self.py_operation_function(code)
+ code.putln(
+ "%s = %s(%s); %s" % (
+ self.result(),
+ function,
+ self.operand.py_result(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+ def type_error(self):
+ if not self.operand.type.is_error:
+ error(self.pos, "Invalid operand type for '%s' (%s)" %
+ (self.operator, self.operand.type))
+ self.type = PyrexTypes.error_type
+
def analyse_cpp_operation(self, env, overload_check=True):
entry = env.lookup_operator(self.operator, [self.operand])
if overload_check and not entry:
@@ -10157,311 +10157,311 @@ class UnopNode(ExprNode):
else:
self.exception_check = ''
self.exception_value = ''
- cpp_type = self.operand.type.find_cpp_operation_type(self.operator)
+ cpp_type = self.operand.type.find_cpp_operation_type(self.operator)
if overload_check and cpp_type is None:
- error(self.pos, "'%s' operator not defined for %s" % (
- self.operator, type))
- self.type_error()
- return
- self.type = cpp_type
-
-
-class NotNode(UnopNode):
- # 'not' operator
- #
- # operand ExprNode
- operator = '!'
-
- type = PyrexTypes.c_bint_type
-
- def calculate_constant_result(self):
- self.constant_result = not self.operand.constant_result
-
- def compile_time_value(self, denv):
- operand = self.operand.compile_time_value(denv)
- try:
- return not operand
+ error(self.pos, "'%s' operator not defined for %s" % (
+ self.operator, type))
+ self.type_error()
+ return
+ self.type = cpp_type
+
+
+class NotNode(UnopNode):
+ # 'not' operator
+ #
+ # operand ExprNode
+ operator = '!'
+
+ type = PyrexTypes.c_bint_type
+
+ def calculate_constant_result(self):
+ self.constant_result = not self.operand.constant_result
+
+ def compile_time_value(self, denv):
+ operand = self.operand.compile_time_value(denv)
+ try:
+ return not operand
except Exception as e:
- self.compile_time_value_error(e)
-
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_bint_type
-
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- operand_type = self.operand.type
- if operand_type.is_cpp_class:
+ self.compile_time_value_error(e)
+
+ def infer_unop_type(self, env, operand_type):
+ return PyrexTypes.c_bint_type
+
+ def analyse_types(self, env):
+ self.operand = self.operand.analyse_types(env)
+ operand_type = self.operand.type
+ if operand_type.is_cpp_class:
self.analyse_cpp_operation(env)
- else:
- self.operand = self.operand.coerce_to_boolean(env)
- return self
-
- def calculate_result_code(self):
- return "(!%s)" % self.operand.result()
-
-
-class UnaryPlusNode(UnopNode):
- # unary '+' operator
-
- operator = '+'
-
- def analyse_c_operation(self, env):
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
-
- def py_operation_function(self, code):
- return "PyNumber_Positive"
-
- def calculate_result_code(self):
- if self.is_cpp_operation():
- return "(+%s)" % self.operand.result()
- else:
- return self.operand.result()
-
-
-class UnaryMinusNode(UnopNode):
- # unary '-' operator
-
- operator = '-'
-
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
- if self.type.is_complex:
- self.infix = False
-
- def py_operation_function(self, code):
- return "PyNumber_Negative"
-
- def calculate_result_code(self):
- if self.infix:
- return "(-%s)" % self.operand.result()
- else:
- return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
-
- def get_constant_c_result_code(self):
- value = self.operand.get_constant_c_result_code()
- if value:
- return "(-%s)" % value
-
-class TildeNode(UnopNode):
- # unary '~' operator
-
- def analyse_c_operation(self, env):
- if self.operand.type.is_int:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
-
- def py_operation_function(self, code):
- return "PyNumber_Invert"
-
- def calculate_result_code(self):
- return "(~%s)" % self.operand.result()
-
-
-class CUnopNode(UnopNode):
-
- def is_py_operation(self):
- return False
-
-class DereferenceNode(CUnopNode):
- # unary * operator
-
- operator = '*'
-
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_ptr:
- return operand_type.base_type
- else:
- return PyrexTypes.error_type
-
- def analyse_c_operation(self, env):
- if self.operand.type.is_ptr:
- self.type = self.operand.type.base_type
- else:
- self.type_error()
-
- def calculate_result_code(self):
- return "(*%s)" % self.operand.result()
-
-
-class DecrementIncrementNode(CUnopNode):
- # unary ++/-- operator
-
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_ptr:
- self.type = self.operand.type
- else:
- self.type_error()
-
- def calculate_result_code(self):
- if self.is_prefix:
- return "(%s%s)" % (self.operator, self.operand.result())
- else:
- return "(%s%s)" % (self.operand.result(), self.operator)
-
-def inc_dec_constructor(is_prefix, operator):
- return lambda pos, **kwds: DecrementIncrementNode(pos, is_prefix=is_prefix, operator=operator, **kwds)
-
-
-class AmpersandNode(CUnopNode):
- # The C address-of operator.
- #
- # operand ExprNode
- operator = '&'
-
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_ptr_type(operand_type)
-
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- argtype = self.operand.type
- if argtype.is_cpp_class:
+ else:
+ self.operand = self.operand.coerce_to_boolean(env)
+ return self
+
+ def calculate_result_code(self):
+ return "(!%s)" % self.operand.result()
+
+
+class UnaryPlusNode(UnopNode):
+ # unary '+' operator
+
+ operator = '+'
+
+ def analyse_c_operation(self, env):
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
+
+ def py_operation_function(self, code):
+ return "PyNumber_Positive"
+
+ def calculate_result_code(self):
+ if self.is_cpp_operation():
+ return "(+%s)" % self.operand.result()
+ else:
+ return self.operand.result()
+
+
+class UnaryMinusNode(UnopNode):
+ # unary '-' operator
+
+ operator = '-'
+
+ def analyse_c_operation(self, env):
+ if self.operand.type.is_numeric:
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
+ elif self.operand.type.is_enum:
+ self.type = PyrexTypes.c_int_type
+ else:
+ self.type_error()
+ if self.type.is_complex:
+ self.infix = False
+
+ def py_operation_function(self, code):
+ return "PyNumber_Negative"
+
+ def calculate_result_code(self):
+ if self.infix:
+ return "(-%s)" % self.operand.result()
+ else:
+ return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
+
+ def get_constant_c_result_code(self):
+ value = self.operand.get_constant_c_result_code()
+ if value:
+ return "(-%s)" % value
+
+class TildeNode(UnopNode):
+ # unary '~' operator
+
+ def analyse_c_operation(self, env):
+ if self.operand.type.is_int:
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
+ elif self.operand.type.is_enum:
+ self.type = PyrexTypes.c_int_type
+ else:
+ self.type_error()
+
+ def py_operation_function(self, code):
+ return "PyNumber_Invert"
+
+ def calculate_result_code(self):
+ return "(~%s)" % self.operand.result()
+
+
+class CUnopNode(UnopNode):
+
+ def is_py_operation(self):
+ return False
+
+class DereferenceNode(CUnopNode):
+ # unary * operator
+
+ operator = '*'
+
+ def infer_unop_type(self, env, operand_type):
+ if operand_type.is_ptr:
+ return operand_type.base_type
+ else:
+ return PyrexTypes.error_type
+
+ def analyse_c_operation(self, env):
+ if self.operand.type.is_ptr:
+ self.type = self.operand.type.base_type
+ else:
+ self.type_error()
+
+ def calculate_result_code(self):
+ return "(*%s)" % self.operand.result()
+
+
+class DecrementIncrementNode(CUnopNode):
+ # unary ++/-- operator
+
+ def analyse_c_operation(self, env):
+ if self.operand.type.is_numeric:
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
+ elif self.operand.type.is_ptr:
+ self.type = self.operand.type
+ else:
+ self.type_error()
+
+ def calculate_result_code(self):
+ if self.is_prefix:
+ return "(%s%s)" % (self.operator, self.operand.result())
+ else:
+ return "(%s%s)" % (self.operand.result(), self.operator)
+
+def inc_dec_constructor(is_prefix, operator):
+ return lambda pos, **kwds: DecrementIncrementNode(pos, is_prefix=is_prefix, operator=operator, **kwds)
+
+
+class AmpersandNode(CUnopNode):
+ # The C address-of operator.
+ #
+ # operand ExprNode
+ operator = '&'
+
+ def infer_unop_type(self, env, operand_type):
+ return PyrexTypes.c_ptr_type(operand_type)
+
+ def analyse_types(self, env):
+ self.operand = self.operand.analyse_types(env)
+ argtype = self.operand.type
+ if argtype.is_cpp_class:
self.analyse_cpp_operation(env, overload_check=False)
- if not (argtype.is_cfunction or argtype.is_reference or self.operand.is_addressable()):
- if argtype.is_memoryviewslice:
- self.error("Cannot take address of memoryview slice")
- else:
+ if not (argtype.is_cfunction or argtype.is_reference or self.operand.is_addressable()):
+ if argtype.is_memoryviewslice:
+ self.error("Cannot take address of memoryview slice")
+ else:
self.error("Taking address of non-lvalue (type %s)" % argtype)
- return self
- if argtype.is_pyobject:
+ return self
+ if argtype.is_pyobject:
self.error("Cannot take address of Python %s" % (
"variable '%s'" % self.operand.name if self.operand.is_name else
"object attribute '%s'" % self.operand.attribute if self.operand.is_attribute else
"object"))
- return self
+ return self
if not argtype.is_cpp_class or not self.type:
self.type = PyrexTypes.c_ptr_type(argtype)
- return self
-
- def check_const(self):
- return self.operand.check_const_addr()
-
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
-
- def calculate_result_code(self):
- return "(&%s)" % self.operand.result()
-
- def generate_result_code(self, code):
+ return self
+
+ def check_const(self):
+ return self.operand.check_const_addr()
+
+ def error(self, mess):
+ error(self.pos, mess)
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
+
+ def calculate_result_code(self):
+ return "(&%s)" % self.operand.result()
+
+ def generate_result_code(self, code):
if (self.operand.type.is_cpp_class and self.exception_check == '+'):
translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context)
-
-
-unop_node_classes = {
- "+": UnaryPlusNode,
- "-": UnaryMinusNode,
- "~": TildeNode,
-}
-
-def unop_node(pos, operator, operand):
- # Construct unnop node of appropriate class for
- # given operator.
- if isinstance(operand, IntNode) and operator == '-':
- return IntNode(pos = operand.pos, value = str(-Utils.str_to_number(operand.value)),
- longness=operand.longness, unsigned=operand.unsigned)
- elif isinstance(operand, UnopNode) and operand.operator == operator in '+-':
- warning(pos, "Python has no increment/decrement operator: %s%sx == %s(%sx) == x" % ((operator,)*4), 5)
- return unop_node_classes[operator](pos,
- operator = operator,
- operand = operand)
-
-
-class TypecastNode(ExprNode):
- # C type cast
- #
- # operand ExprNode
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
- # typecheck boolean
- #
- # If used from a transform, one can if wanted specify the attribute
- # "type" directly and leave base_type and declarator to None
-
- subexprs = ['operand']
- base_type = declarator = type = None
-
- def type_dependencies(self, env):
- return ()
-
- def infer_type(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- return self.type
-
- def analyse_types(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- if self.operand.has_constant_result():
- # Must be done after self.type is resolved.
- self.calculate_constant_result()
- if self.type.is_cfunction:
- error(self.pos,
- "Cannot cast to a function type")
- self.type = PyrexTypes.error_type
- self.operand = self.operand.analyse_types(env)
- if self.type is PyrexTypes.c_bint_type:
- # short circuit this to a coercion
- return self.operand.coerce_to_boolean(env)
- to_py = self.type.is_pyobject
- from_py = self.operand.type.is_pyobject
- if from_py and not to_py and self.operand.is_ephemeral():
- if not self.type.is_numeric and not self.type.is_cpp_class:
- error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
- if to_py and not from_py:
- if self.type is bytes_type and self.operand.type.is_int:
- return CoerceIntToBytesNode(self.operand, env)
- elif self.operand.type.can_coerce_to_pyobject(env):
- self.result_ctype = py_object_type
+
+
+unop_node_classes = {
+ "+": UnaryPlusNode,
+ "-": UnaryMinusNode,
+ "~": TildeNode,
+}
+
+def unop_node(pos, operator, operand):
+ # Construct unnop node of appropriate class for
+ # given operator.
+ if isinstance(operand, IntNode) and operator == '-':
+ return IntNode(pos = operand.pos, value = str(-Utils.str_to_number(operand.value)),
+ longness=operand.longness, unsigned=operand.unsigned)
+ elif isinstance(operand, UnopNode) and operand.operator == operator in '+-':
+ warning(pos, "Python has no increment/decrement operator: %s%sx == %s(%sx) == x" % ((operator,)*4), 5)
+ return unop_node_classes[operator](pos,
+ operator = operator,
+ operand = operand)
+
+
+class TypecastNode(ExprNode):
+ # C type cast
+ #
+ # operand ExprNode
+ # base_type CBaseTypeNode
+ # declarator CDeclaratorNode
+ # typecheck boolean
+ #
+ # If used from a transform, one can if wanted specify the attribute
+ # "type" directly and leave base_type and declarator to None
+
+ subexprs = ['operand']
+ base_type = declarator = type = None
+
+ def type_dependencies(self, env):
+ return ()
+
+ def infer_type(self, env):
+ if self.type is None:
+ base_type = self.base_type.analyse(env)
+ _, self.type = self.declarator.analyse(base_type, env)
+ return self.type
+
+ def analyse_types(self, env):
+ if self.type is None:
+ base_type = self.base_type.analyse(env)
+ _, self.type = self.declarator.analyse(base_type, env)
+ if self.operand.has_constant_result():
+ # Must be done after self.type is resolved.
+ self.calculate_constant_result()
+ if self.type.is_cfunction:
+ error(self.pos,
+ "Cannot cast to a function type")
+ self.type = PyrexTypes.error_type
+ self.operand = self.operand.analyse_types(env)
+ if self.type is PyrexTypes.c_bint_type:
+ # short circuit this to a coercion
+ return self.operand.coerce_to_boolean(env)
+ to_py = self.type.is_pyobject
+ from_py = self.operand.type.is_pyobject
+ if from_py and not to_py and self.operand.is_ephemeral():
+ if not self.type.is_numeric and not self.type.is_cpp_class:
+ error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
+ if to_py and not from_py:
+ if self.type is bytes_type and self.operand.type.is_int:
+ return CoerceIntToBytesNode(self.operand, env)
+ elif self.operand.type.can_coerce_to_pyobject(env):
+ self.result_ctype = py_object_type
self.operand = self.operand.coerce_to(self.type, env)
- else:
- if self.operand.type.is_ptr:
- if not (self.operand.type.base_type.is_void or self.operand.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast from pointers of primitive types")
- else:
- # Should this be an error?
+ else:
+ if self.operand.type.is_ptr:
+ if not (self.operand.type.base_type.is_void or self.operand.type.base_type.is_struct):
+ error(self.pos, "Python objects cannot be cast from pointers of primitive types")
+ else:
+ # Should this be an error?
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
self.operand.type, self.type))
- self.operand = self.operand.coerce_to_simple(env)
- elif from_py and not to_py:
- if self.type.create_from_py_utility_code(env):
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_ptr:
- if not (self.type.base_type.is_void or self.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast to pointers of primitive types")
- else:
+ self.operand = self.operand.coerce_to_simple(env)
+ elif from_py and not to_py:
+ if self.type.create_from_py_utility_code(env):
+ self.operand = self.operand.coerce_to(self.type, env)
+ elif self.type.is_ptr:
+ if not (self.type.base_type.is_void or self.type.base_type.is_struct):
+ error(self.pos, "Python objects cannot be cast to pointers of primitive types")
+ else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
self.type, self.operand.type))
- elif from_py and to_py:
- if self.typecheck:
- self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
- elif isinstance(self.operand, SliceIndexNode):
- # This cast can influence the created type of string slices.
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_complex and self.operand.type.is_complex:
- self.operand = self.operand.coerce_to_simple(env)
- elif self.operand.type.is_fused:
- self.operand = self.operand.coerce_to(self.type, env)
- #self.type = self.operand.type
+ elif from_py and to_py:
+ if self.typecheck:
+ self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
+ elif isinstance(self.operand, SliceIndexNode):
+ # This cast can influence the created type of string slices.
+ self.operand = self.operand.coerce_to(self.type, env)
+ elif self.type.is_complex and self.operand.type.is_complex:
+ self.operand = self.operand.coerce_to_simple(env)
+ elif self.operand.type.is_fused:
+ self.operand = self.operand.coerce_to(self.type, env)
+ #self.type = self.operand.type
if self.type.is_ptr and self.type.base_type.is_cfunction and self.type.base_type.nogil:
op_type = self.operand.type
if op_type.is_ptr:
@@ -10469,244 +10469,244 @@ class TypecastNode(ExprNode):
if op_type.is_cfunction and not op_type.nogil:
warning(self.pos,
"Casting a GIL-requiring function into a nogil function circumvents GIL validation", 1)
- return self
-
- def is_simple(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_simple()
-
- def is_ephemeral(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_ephemeral()
-
- def nonlocally_immutable(self):
- return self.is_temp or self.operand.nonlocally_immutable()
-
- def nogil_check(self, env):
- if self.type and self.type.is_pyobject and self.is_temp:
- self.gil_error()
-
- def check_const(self):
- return self.operand.check_const()
-
- def calculate_constant_result(self):
- self.constant_result = self.calculate_result_code(self.operand.constant_result)
-
- def calculate_result_code(self, operand_result = None):
- if operand_result is None:
- operand_result = self.operand.result()
- if self.type.is_complex:
- operand_result = self.operand.result()
- if self.operand.type.is_complex:
- real_part = self.type.real_type.cast_code("__Pyx_CREAL(%s)" % operand_result)
- imag_part = self.type.real_type.cast_code("__Pyx_CIMAG(%s)" % operand_result)
- else:
- real_part = self.type.real_type.cast_code(operand_result)
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
- else:
- return self.type.cast_code(operand_result)
-
- def get_constant_c_result_code(self):
- operand_result = self.operand.get_constant_c_result_code()
- if operand_result:
- return self.type.cast_code(operand_result)
-
- def result_as(self, type):
- if self.type.is_pyobject and not self.is_temp:
- # Optimise away some unnecessary casting
- return self.operand.result_as(type)
- else:
- return ExprNode.result_as(self, type)
-
- def generate_result_code(self, code):
- if self.is_temp:
- code.putln(
- "%s = (PyObject *)%s;" % (
- self.result(),
- self.operand.result()))
- code.put_incref(self.result(), self.ctype())
-
-
-ERR_START = "Start may not be given"
-ERR_NOT_STOP = "Stop must be provided to indicate shape"
-ERR_STEPS = ("Strides may only be given to indicate contiguity. "
- "Consider slicing it after conversion")
-ERR_NOT_POINTER = "Can only create cython.array from pointer or array"
-ERR_BASE_TYPE = "Pointer base type does not match cython.array base type"
-
-
-class CythonArrayNode(ExprNode):
- """
- Used when a pointer of base_type is cast to a memoryviewslice with that
- base type. i.e.
-
- <int[:M:1, :N]> p
-
- creates a fortran-contiguous cython.array.
-
- We leave the type set to object so coercions to object are more efficient
- and less work. Acquiring a memoryviewslice from this will be just as
- efficient. ExprNode.coerce_to() will do the additional typecheck on
- self.compile_time_type
-
- This also handles <int[:, :]> my_c_array
-
-
- operand ExprNode the thing we're casting
- base_type_node MemoryViewSliceTypeNode the cast expression node
- """
-
- subexprs = ['operand', 'shapes']
-
- shapes = None
- is_temp = True
- mode = "c"
- array_dtype = None
-
- shape_type = PyrexTypes.c_py_ssize_t_type
-
- def analyse_types(self, env):
- from . import MemoryView
-
- self.operand = self.operand.analyse_types(env)
- if self.array_dtype:
- array_dtype = self.array_dtype
- else:
- array_dtype = self.base_type_node.base_type_node.analyse(env)
- axes = self.base_type_node.axes
-
- self.type = error_type
- self.shapes = []
- ndim = len(axes)
-
- # Base type of the pointer or C array we are converting
- base_type = self.operand.type
-
- if not self.operand.type.is_ptr and not self.operand.type.is_array:
- error(self.operand.pos, ERR_NOT_POINTER)
- return self
-
- # Dimension sizes of C array
- array_dimension_sizes = []
- if base_type.is_array:
- while base_type.is_array:
- array_dimension_sizes.append(base_type.size)
- base_type = base_type.base_type
- elif base_type.is_ptr:
- base_type = base_type.base_type
- else:
- error(self.pos, "unexpected base type %s found" % base_type)
- return self
-
- if not (base_type.same_as(array_dtype) or base_type.is_void):
- error(self.operand.pos, ERR_BASE_TYPE)
- return self
- elif self.operand.type.is_array and len(array_dimension_sizes) != ndim:
- error(self.operand.pos,
- "Expected %d dimensions, array has %d dimensions" %
- (ndim, len(array_dimension_sizes)))
- return self
-
- # Verify the start, stop and step values
- # In case of a C array, use the size of C array in each dimension to
- # get an automatic cast
- for axis_no, axis in enumerate(axes):
- if not axis.start.is_none:
- error(axis.start.pos, ERR_START)
- return self
-
- if axis.stop.is_none:
- if array_dimension_sizes:
- dimsize = array_dimension_sizes[axis_no]
- axis.stop = IntNode(self.pos, value=str(dimsize),
- constant_result=dimsize,
- type=PyrexTypes.c_int_type)
- else:
- error(axis.pos, ERR_NOT_STOP)
- return self
-
- axis.stop = axis.stop.analyse_types(env)
- shape = axis.stop.coerce_to(self.shape_type, env)
- if not shape.is_literal:
- shape.coerce_to_temp(env)
-
- self.shapes.append(shape)
-
- first_or_last = axis_no in (0, ndim - 1)
- if not axis.step.is_none and first_or_last:
- # '1' in the first or last dimension denotes F or C contiguity
- axis.step = axis.step.analyse_types(env)
- if (not axis.step.type.is_int and axis.step.is_literal and not
- axis.step.type.is_error):
- error(axis.step.pos, "Expected an integer literal")
- return self
-
- if axis.step.compile_time_value(env) != 1:
- error(axis.step.pos, ERR_STEPS)
- return self
-
- if axis_no == 0:
- self.mode = "fortran"
-
- elif not axis.step.is_none and not first_or_last:
- # step provided in some other dimension
- error(axis.step.pos, ERR_STEPS)
- return self
-
- if not self.operand.is_name:
- self.operand = self.operand.coerce_to_temp(env)
-
- axes = [('direct', 'follow')] * len(axes)
- if self.mode == "fortran":
- axes[0] = ('direct', 'contig')
- else:
- axes[-1] = ('direct', 'contig')
-
- self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
+ return self
+
+ def is_simple(self):
+ # either temp or a C cast => no side effects other than the operand's
+ return self.operand.is_simple()
+
+ def is_ephemeral(self):
+ # either temp or a C cast => no side effects other than the operand's
+ return self.operand.is_ephemeral()
+
+ def nonlocally_immutable(self):
+ return self.is_temp or self.operand.nonlocally_immutable()
+
+ def nogil_check(self, env):
+ if self.type and self.type.is_pyobject and self.is_temp:
+ self.gil_error()
+
+ def check_const(self):
+ return self.operand.check_const()
+
+ def calculate_constant_result(self):
+ self.constant_result = self.calculate_result_code(self.operand.constant_result)
+
+ def calculate_result_code(self, operand_result = None):
+ if operand_result is None:
+ operand_result = self.operand.result()
+ if self.type.is_complex:
+ operand_result = self.operand.result()
+ if self.operand.type.is_complex:
+ real_part = self.type.real_type.cast_code("__Pyx_CREAL(%s)" % operand_result)
+ imag_part = self.type.real_type.cast_code("__Pyx_CIMAG(%s)" % operand_result)
+ else:
+ real_part = self.type.real_type.cast_code(operand_result)
+ imag_part = "0"
+ return "%s(%s, %s)" % (
+ self.type.from_parts,
+ real_part,
+ imag_part)
+ else:
+ return self.type.cast_code(operand_result)
+
+ def get_constant_c_result_code(self):
+ operand_result = self.operand.get_constant_c_result_code()
+ if operand_result:
+ return self.type.cast_code(operand_result)
+
+ def result_as(self, type):
+ if self.type.is_pyobject and not self.is_temp:
+ # Optimise away some unnecessary casting
+ return self.operand.result_as(type)
+ else:
+ return ExprNode.result_as(self, type)
+
+ def generate_result_code(self, code):
+ if self.is_temp:
+ code.putln(
+ "%s = (PyObject *)%s;" % (
+ self.result(),
+ self.operand.result()))
+ code.put_incref(self.result(), self.ctype())
+
+
+ERR_START = "Start may not be given"
+ERR_NOT_STOP = "Stop must be provided to indicate shape"
+ERR_STEPS = ("Strides may only be given to indicate contiguity. "
+ "Consider slicing it after conversion")
+ERR_NOT_POINTER = "Can only create cython.array from pointer or array"
+ERR_BASE_TYPE = "Pointer base type does not match cython.array base type"
+
+
+class CythonArrayNode(ExprNode):
+ """
+ Used when a pointer of base_type is cast to a memoryviewslice with that
+ base type. i.e.
+
+ <int[:M:1, :N]> p
+
+ creates a fortran-contiguous cython.array.
+
+ We leave the type set to object so coercions to object are more efficient
+ and less work. Acquiring a memoryviewslice from this will be just as
+ efficient. ExprNode.coerce_to() will do the additional typecheck on
+ self.compile_time_type
+
+ This also handles <int[:, :]> my_c_array
+
+
+ operand ExprNode the thing we're casting
+ base_type_node MemoryViewSliceTypeNode the cast expression node
+ """
+
+ subexprs = ['operand', 'shapes']
+
+ shapes = None
+ is_temp = True
+ mode = "c"
+ array_dtype = None
+
+ shape_type = PyrexTypes.c_py_ssize_t_type
+
+ def analyse_types(self, env):
+ from . import MemoryView
+
+ self.operand = self.operand.analyse_types(env)
+ if self.array_dtype:
+ array_dtype = self.array_dtype
+ else:
+ array_dtype = self.base_type_node.base_type_node.analyse(env)
+ axes = self.base_type_node.axes
+
+ self.type = error_type
+ self.shapes = []
+ ndim = len(axes)
+
+ # Base type of the pointer or C array we are converting
+ base_type = self.operand.type
+
+ if not self.operand.type.is_ptr and not self.operand.type.is_array:
+ error(self.operand.pos, ERR_NOT_POINTER)
+ return self
+
+ # Dimension sizes of C array
+ array_dimension_sizes = []
+ if base_type.is_array:
+ while base_type.is_array:
+ array_dimension_sizes.append(base_type.size)
+ base_type = base_type.base_type
+ elif base_type.is_ptr:
+ base_type = base_type.base_type
+ else:
+ error(self.pos, "unexpected base type %s found" % base_type)
+ return self
+
+ if not (base_type.same_as(array_dtype) or base_type.is_void):
+ error(self.operand.pos, ERR_BASE_TYPE)
+ return self
+ elif self.operand.type.is_array and len(array_dimension_sizes) != ndim:
+ error(self.operand.pos,
+ "Expected %d dimensions, array has %d dimensions" %
+ (ndim, len(array_dimension_sizes)))
+ return self
+
+ # Verify the start, stop and step values
+ # In case of a C array, use the size of C array in each dimension to
+ # get an automatic cast
+ for axis_no, axis in enumerate(axes):
+ if not axis.start.is_none:
+ error(axis.start.pos, ERR_START)
+ return self
+
+ if axis.stop.is_none:
+ if array_dimension_sizes:
+ dimsize = array_dimension_sizes[axis_no]
+ axis.stop = IntNode(self.pos, value=str(dimsize),
+ constant_result=dimsize,
+ type=PyrexTypes.c_int_type)
+ else:
+ error(axis.pos, ERR_NOT_STOP)
+ return self
+
+ axis.stop = axis.stop.analyse_types(env)
+ shape = axis.stop.coerce_to(self.shape_type, env)
+ if not shape.is_literal:
+ shape.coerce_to_temp(env)
+
+ self.shapes.append(shape)
+
+ first_or_last = axis_no in (0, ndim - 1)
+ if not axis.step.is_none and first_or_last:
+ # '1' in the first or last dimension denotes F or C contiguity
+ axis.step = axis.step.analyse_types(env)
+ if (not axis.step.type.is_int and axis.step.is_literal and not
+ axis.step.type.is_error):
+ error(axis.step.pos, "Expected an integer literal")
+ return self
+
+ if axis.step.compile_time_value(env) != 1:
+ error(axis.step.pos, ERR_STEPS)
+ return self
+
+ if axis_no == 0:
+ self.mode = "fortran"
+
+ elif not axis.step.is_none and not first_or_last:
+ # step provided in some other dimension
+ error(axis.step.pos, ERR_STEPS)
+ return self
+
+ if not self.operand.is_name:
+ self.operand = self.operand.coerce_to_temp(env)
+
+ axes = [('direct', 'follow')] * len(axes)
+ if self.mode == "fortran":
+ axes[0] = ('direct', 'contig')
+ else:
+ axes[-1] = ('direct', 'contig')
+
+ self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
self.coercion_type.validate_memslice_dtype(self.pos)
- self.type = self.get_cython_array_type(env)
- MemoryView.use_cython_array_utility_code(env)
- env.use_utility_code(MemoryView.typeinfo_to_format_code)
- return self
-
- def allocate_temp_result(self, code):
- if self.temp_code:
+ self.type = self.get_cython_array_type(env)
+ MemoryView.use_cython_array_utility_code(env)
+ env.use_utility_code(MemoryView.typeinfo_to_format_code)
+ return self
+
+ def allocate_temp_result(self, code):
+ if self.temp_code:
raise RuntimeError("temp allocated multiple times")
-
- self.temp_code = code.funcstate.allocate_temp(self.type, True)
-
- def infer_type(self, env):
- return self.get_cython_array_type(env)
-
- def get_cython_array_type(self, env):
+
+ self.temp_code = code.funcstate.allocate_temp(self.type, True)
+
+ def infer_type(self, env):
+ return self.get_cython_array_type(env)
+
+ def get_cython_array_type(self, env):
cython_scope = env.global_scope().context.cython_scope
cython_scope.load_cythonscope()
return cython_scope.viewscope.lookup("array").type
-
- def generate_result_code(self, code):
- from . import Buffer
-
- shapes = [self.shape_type.cast_code(shape.result())
- for shape in self.shapes]
- dtype = self.coercion_type.dtype
-
- shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
- format_temp = code.funcstate.allocate_temp(py_object_type, True)
-
+
+ def generate_result_code(self, code):
+ from . import Buffer
+
+ shapes = [self.shape_type.cast_code(shape.result())
+ for shape in self.shapes]
+ dtype = self.coercion_type.dtype
+
+ shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
+ format_temp = code.funcstate.allocate_temp(py_object_type, True)
+
itemsize = "sizeof(%s)" % dtype.empty_declaration_code()
- type_info = Buffer.get_type_information_cname(code, dtype)
-
- if self.operand.type.is_ptr:
- code.putln("if (!%s) {" % self.operand.result())
- code.putln( 'PyErr_SetString(PyExc_ValueError,'
- '"Cannot create cython.array from NULL pointer");')
- code.putln(code.error_goto(self.operand.pos))
- code.putln("}")
-
+ type_info = Buffer.get_type_information_cname(code, dtype)
+
+ if self.operand.type.is_ptr:
+ code.putln("if (!%s) {" % self.operand.result())
+ code.putln( 'PyErr_SetString(PyExc_ValueError,'
+ '"Cannot create cython.array from NULL pointer");')
+ code.putln(code.error_goto(self.operand.pos))
+ code.putln("}")
+
code.putln("%s = __pyx_format_from_typeinfo(&%s); %s" % (
format_temp,
type_info,
@@ -10714,143 +10714,143 @@ class CythonArrayNode(ExprNode):
))
code.put_gotref(format_temp)
- buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
+ buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
code.putln('%s = Py_BuildValue((char*) "(" %s ")", %s); %s' % (
shapes_temp,
buildvalue_fmt,
", ".join(shapes),
code.error_goto_if_null(shapes_temp, self.pos),
))
- code.put_gotref(shapes_temp)
-
- tup = (self.result(), shapes_temp, itemsize, format_temp,
- self.mode, self.operand.result())
- code.putln('%s = __pyx_array_new('
- '%s, %s, PyBytes_AS_STRING(%s), '
- '(char *) "%s", (char *) %s);' % tup)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.result())
-
- def dispose(temp):
- code.put_decref_clear(temp, py_object_type)
- code.funcstate.release_temp(temp)
-
- dispose(shapes_temp)
- dispose(format_temp)
-
- @classmethod
- def from_carray(cls, src_node, env):
- """
- Given a C array type, return a CythonArrayNode
- """
- pos = src_node.pos
- base_type = src_node.type
-
- none_node = NoneNode(pos)
- axes = []
-
- while base_type.is_array:
- axes.append(SliceNode(pos, start=none_node, stop=none_node,
- step=none_node))
- base_type = base_type.base_type
- axes[-1].step = IntNode(pos, value="1", is_c_literal=True)
-
- memslicenode = Nodes.MemoryViewSliceTypeNode(pos, axes=axes,
- base_type_node=base_type)
- result = CythonArrayNode(pos, base_type_node=memslicenode,
- operand=src_node, array_dtype=base_type)
- result = result.analyse_types(env)
- return result
-
-class SizeofNode(ExprNode):
- # Abstract base class for sizeof(x) expression nodes.
-
- type = PyrexTypes.c_size_t_type
-
- def check_const(self):
- return True
-
- def generate_result_code(self, code):
- pass
-
-
-class SizeofTypeNode(SizeofNode):
- # C sizeof function applied to a type
- #
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
-
- subexprs = []
- arg_type = None
-
- def analyse_types(self, env):
- # we may have incorrectly interpreted a dotted name as a type rather than an attribute
- # this could be better handled by more uniformly treating types as runtime-available objects
- if 0 and self.base_type.module_path:
- path = self.base_type.module_path
- obj = env.lookup(path[0])
- if obj.as_module is None:
- operand = NameNode(pos=self.pos, name=path[0])
- for attr in path[1:]:
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
+ code.put_gotref(shapes_temp)
+
+ tup = (self.result(), shapes_temp, itemsize, format_temp,
+ self.mode, self.operand.result())
+ code.putln('%s = __pyx_array_new('
+ '%s, %s, PyBytes_AS_STRING(%s), '
+ '(char *) "%s", (char *) %s);' % tup)
+ code.putln(code.error_goto_if_null(self.result(), self.pos))
+ code.put_gotref(self.result())
+
+ def dispose(temp):
+ code.put_decref_clear(temp, py_object_type)
+ code.funcstate.release_temp(temp)
+
+ dispose(shapes_temp)
+ dispose(format_temp)
+
+ @classmethod
+ def from_carray(cls, src_node, env):
+ """
+ Given a C array type, return a CythonArrayNode
+ """
+ pos = src_node.pos
+ base_type = src_node.type
+
+ none_node = NoneNode(pos)
+ axes = []
+
+ while base_type.is_array:
+ axes.append(SliceNode(pos, start=none_node, stop=none_node,
+ step=none_node))
+ base_type = base_type.base_type
+ axes[-1].step = IntNode(pos, value="1", is_c_literal=True)
+
+ memslicenode = Nodes.MemoryViewSliceTypeNode(pos, axes=axes,
+ base_type_node=base_type)
+ result = CythonArrayNode(pos, base_type_node=memslicenode,
+ operand=src_node, array_dtype=base_type)
+ result = result.analyse_types(env)
+ return result
+
+class SizeofNode(ExprNode):
+ # Abstract base class for sizeof(x) expression nodes.
+
+ type = PyrexTypes.c_size_t_type
+
+ def check_const(self):
+ return True
+
+ def generate_result_code(self, code):
+ pass
+
+
+class SizeofTypeNode(SizeofNode):
+ # C sizeof function applied to a type
+ #
+ # base_type CBaseTypeNode
+ # declarator CDeclaratorNode
+
+ subexprs = []
+ arg_type = None
+
+ def analyse_types(self, env):
+ # we may have incorrectly interpreted a dotted name as a type rather than an attribute
+ # this could be better handled by more uniformly treating types as runtime-available objects
+ if 0 and self.base_type.module_path:
+ path = self.base_type.module_path
+ obj = env.lookup(path[0])
+ if obj.as_module is None:
+ operand = NameNode(pos=self.pos, name=path[0])
+ for attr in path[1:]:
+ operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
+ operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
node = SizeofVarNode(self.pos, operand=operand).analyse_types(env)
- return node
- if self.arg_type is None:
- base_type = self.base_type.analyse(env)
- _, arg_type = self.declarator.analyse(base_type, env)
- self.arg_type = arg_type
- self.check_type()
- return self
-
- def check_type(self):
- arg_type = self.arg_type
+ return node
+ if self.arg_type is None:
+ base_type = self.base_type.analyse(env)
+ _, arg_type = self.declarator.analyse(base_type, env)
+ self.arg_type = arg_type
+ self.check_type()
+ return self
+
+ def check_type(self):
+ arg_type = self.arg_type
if not arg_type:
return
- if arg_type.is_pyobject and not arg_type.is_extension_type:
- error(self.pos, "Cannot take sizeof Python object")
- elif arg_type.is_void:
- error(self.pos, "Cannot take sizeof void")
- elif not arg_type.is_complete():
- error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
-
- def calculate_result_code(self):
- if self.arg_type.is_extension_type:
- # the size of the pointer is boring
- # we want the size of the actual struct
- arg_code = self.arg_type.declaration_code("", deref=1)
- else:
+ if arg_type.is_pyobject and not arg_type.is_extension_type:
+ error(self.pos, "Cannot take sizeof Python object")
+ elif arg_type.is_void:
+ error(self.pos, "Cannot take sizeof void")
+ elif not arg_type.is_complete():
+ error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
+
+ def calculate_result_code(self):
+ if self.arg_type.is_extension_type:
+ # the size of the pointer is boring
+ # we want the size of the actual struct
+ arg_code = self.arg_type.declaration_code("", deref=1)
+ else:
arg_code = self.arg_type.empty_declaration_code()
- return "(sizeof(%s))" % arg_code
-
-
-class SizeofVarNode(SizeofNode):
- # C sizeof function applied to a variable
- #
- # operand ExprNode
-
- subexprs = ['operand']
-
- def analyse_types(self, env):
- # We may actually be looking at a type rather than a variable...
- # If we are, traditional analysis would fail...
- operand_as_type = self.operand.analyse_as_type(env)
- if operand_as_type:
- self.arg_type = operand_as_type
- if self.arg_type.is_fused:
- self.arg_type = self.arg_type.specialize(env.fused_to_specific)
- self.__class__ = SizeofTypeNode
- self.check_type()
- else:
- self.operand = self.operand.analyse_types(env)
- return self
-
- def calculate_result_code(self):
- return "(sizeof(%s))" % self.operand.result()
-
- def generate_result_code(self, code):
- pass
-
+ return "(sizeof(%s))" % arg_code
+
+
+class SizeofVarNode(SizeofNode):
+ # C sizeof function applied to a variable
+ #
+ # operand ExprNode
+
+ subexprs = ['operand']
+
+ def analyse_types(self, env):
+ # We may actually be looking at a type rather than a variable...
+ # If we are, traditional analysis would fail...
+ operand_as_type = self.operand.analyse_as_type(env)
+ if operand_as_type:
+ self.arg_type = operand_as_type
+ if self.arg_type.is_fused:
+ self.arg_type = self.arg_type.specialize(env.fused_to_specific)
+ self.__class__ = SizeofTypeNode
+ self.check_type()
+ else:
+ self.operand = self.operand.analyse_types(env)
+ return self
+
+ def calculate_result_code(self):
+ return "(sizeof(%s))" % self.operand.result()
+
+ def generate_result_code(self, code):
+ pass
+
class TypeidNode(ExprNode):
# C++ typeid operator applied to a type or variable
@@ -10923,153 +10923,153 @@ class TypeidNode(ExprNode):
"%s = typeid(%s);" % (self.temp_code, arg_code),
None, None, self.in_nogil_context)
-class TypeofNode(ExprNode):
- # Compile-time type of an expression, as a string.
- #
- # operand ExprNode
- # literal StringNode # internal
-
- literal = None
- type = py_object_type
-
- subexprs = ['literal'] # 'operand' will be ignored after type analysis!
-
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
- literal = StringNode(self.pos, value=value)
- literal = literal.analyse_types(env)
- self.literal = literal.coerce_to_pyobject(env)
- return self
-
+class TypeofNode(ExprNode):
+ # Compile-time type of an expression, as a string.
+ #
+ # operand ExprNode
+ # literal StringNode # internal
+
+ literal = None
+ type = py_object_type
+
+ subexprs = ['literal'] # 'operand' will be ignored after type analysis!
+
+ def analyse_types(self, env):
+ self.operand = self.operand.analyse_types(env)
+ value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
+ literal = StringNode(self.pos, value=value)
+ literal = literal.analyse_types(env)
+ self.literal = literal.coerce_to_pyobject(env)
+ return self
+
def analyse_as_type(self, env):
self.operand = self.operand.analyse_types(env)
return self.operand.type
- def may_be_none(self):
- return False
-
- def generate_evaluation_code(self, code):
- self.literal.generate_evaluation_code(code)
-
- def calculate_result_code(self):
- return self.literal.calculate_result_code()
-
-#-------------------------------------------------------------------
-#
-# Binary operator nodes
-#
-#-------------------------------------------------------------------
-
-try:
- matmul_operator = operator.matmul
-except AttributeError:
- def matmul_operator(a, b):
- try:
- func = a.__matmul__
- except AttributeError:
- func = b.__rmatmul__
- return func(a, b)
-
-compile_time_binary_operators = {
- '<': operator.lt,
- '<=': operator.le,
- '==': operator.eq,
- '!=': operator.ne,
- '>=': operator.ge,
- '>': operator.gt,
- 'is': operator.is_,
- 'is_not': operator.is_not,
- '+': operator.add,
- '&': operator.and_,
- '/': operator.truediv,
- '//': operator.floordiv,
- '<<': operator.lshift,
- '%': operator.mod,
- '*': operator.mul,
- '|': operator.or_,
- '**': operator.pow,
- '>>': operator.rshift,
- '-': operator.sub,
- '^': operator.xor,
- '@': matmul_operator,
- 'in': lambda x, seq: x in seq,
- 'not_in': lambda x, seq: x not in seq,
-}
-
-def get_compile_time_binop(node):
- func = compile_time_binary_operators.get(node.operator)
- if not func:
- error(node.pos,
- "Binary '%s' not supported in compile-time expression"
- % node.operator)
- return func
-
-
-class BinopNode(ExprNode):
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when neither operand is a pyobject.
- # - Check operand types and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
-
- subexprs = ['operand1', 'operand2']
- inplace = False
-
- def calculate_constant_result(self):
- func = compile_time_binary_operators[self.operator]
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
-
- def compile_time_value(self, denv):
- func = get_compile_time_binop(self)
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- return func(operand1, operand2)
+ def may_be_none(self):
+ return False
+
+ def generate_evaluation_code(self, code):
+ self.literal.generate_evaluation_code(code)
+
+ def calculate_result_code(self):
+ return self.literal.calculate_result_code()
+
+#-------------------------------------------------------------------
+#
+# Binary operator nodes
+#
+#-------------------------------------------------------------------
+
+try:
+ matmul_operator = operator.matmul
+except AttributeError:
+ def matmul_operator(a, b):
+ try:
+ func = a.__matmul__
+ except AttributeError:
+ func = b.__rmatmul__
+ return func(a, b)
+
+compile_time_binary_operators = {
+ '<': operator.lt,
+ '<=': operator.le,
+ '==': operator.eq,
+ '!=': operator.ne,
+ '>=': operator.ge,
+ '>': operator.gt,
+ 'is': operator.is_,
+ 'is_not': operator.is_not,
+ '+': operator.add,
+ '&': operator.and_,
+ '/': operator.truediv,
+ '//': operator.floordiv,
+ '<<': operator.lshift,
+ '%': operator.mod,
+ '*': operator.mul,
+ '|': operator.or_,
+ '**': operator.pow,
+ '>>': operator.rshift,
+ '-': operator.sub,
+ '^': operator.xor,
+ '@': matmul_operator,
+ 'in': lambda x, seq: x in seq,
+ 'not_in': lambda x, seq: x not in seq,
+}
+
+def get_compile_time_binop(node):
+ func = compile_time_binary_operators.get(node.operator)
+ if not func:
+ error(node.pos,
+ "Binary '%s' not supported in compile-time expression"
+ % node.operator)
+ return func
+
+
+class BinopNode(ExprNode):
+ # operator string
+ # operand1 ExprNode
+ # operand2 ExprNode
+ #
+ # Processing during analyse_expressions phase:
+ #
+ # analyse_c_operation
+ # Called when neither operand is a pyobject.
+ # - Check operand types and coerce if needed.
+ # - Determine result type and result code fragment.
+ # - Allocate temporary for result if needed.
+
+ subexprs = ['operand1', 'operand2']
+ inplace = False
+
+ def calculate_constant_result(self):
+ func = compile_time_binary_operators[self.operator]
+ self.constant_result = func(
+ self.operand1.constant_result,
+ self.operand2.constant_result)
+
+ def compile_time_value(self, denv):
+ func = get_compile_time_binop(self)
+ operand1 = self.operand1.compile_time_value(denv)
+ operand2 = self.operand2.compile_time_value(denv)
+ try:
+ return func(operand1, operand2)
except Exception as e:
- self.compile_time_value_error(e)
-
- def infer_type(self, env):
- return self.result_type(self.operand1.infer_type(env),
+ self.compile_time_value_error(e)
+
+ def infer_type(self, env):
+ return self.result_type(self.operand1.infer_type(env),
self.operand2.infer_type(env), env)
-
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- self.analyse_operation(env)
- return self
-
- def analyse_operation(self, env):
+
+ def analyse_types(self, env):
+ self.operand1 = self.operand1.analyse_types(env)
+ self.operand2 = self.operand2.analyse_types(env)
+ self.analyse_operation(env)
+ return self
+
+ def analyse_operation(self, env):
if self.is_pythran_operation(env):
self.type = self.result_type(self.operand1.type,
self.operand2.type, env)
assert self.type.is_pythran_expr
self.is_temp = 1
elif self.is_py_operation():
- self.coerce_operands_to_pyobjects(env)
- self.type = self.result_type(self.operand1.type,
+ self.coerce_operands_to_pyobjects(env)
+ self.type = self.result_type(self.operand1.type,
self.operand2.type, env)
- assert self.type.is_pyobject
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
-
- def is_py_operation(self):
- return self.is_py_operation_types(self.operand1.type, self.operand2.type)
-
- def is_py_operation_types(self, type1, type2):
+ assert self.type.is_pyobject
+ self.is_temp = 1
+ elif self.is_cpp_operation():
+ self.analyse_cpp_operation(env)
+ else:
+ self.analyse_c_operation(env)
+
+ def is_py_operation(self):
+ return self.is_py_operation_types(self.operand1.type, self.operand2.type)
+
+ def is_py_operation_types(self, type1, type2):
return type1.is_pyobject or type2.is_pyobject or type1.is_ctuple or type2.is_ctuple
-
+
def is_pythran_operation(self, env):
return self.is_pythran_operation_types(self.operand1.type, self.operand2.type, env)
@@ -11079,16 +11079,16 @@ class BinopNode(ExprNode):
(is_pythran_supported_operation_type(type1) and is_pythran_supported_operation_type(type2)) and \
(is_pythran_expr(type1) or is_pythran_expr(type2))
- def is_cpp_operation(self):
- return (self.operand1.type.is_cpp_class
- or self.operand2.type.is_cpp_class)
-
- def analyse_cpp_operation(self, env):
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if not entry:
- self.type_error()
- return
- func_type = entry.type
+ def is_cpp_operation(self):
+ return (self.operand1.type.is_cpp_class
+ or self.operand2.type.is_cpp_class)
+
+ def analyse_cpp_operation(self, env):
+ entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
+ if not entry:
+ self.type_error()
+ return
+ func_type = entry.type
self.exception_check = func_type.exception_check
self.exception_value = func_type.exception_value
if self.exception_check == '+':
@@ -11097,59 +11097,59 @@ class BinopNode(ExprNode):
self.is_temp = 1
if self.exception_value is None:
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if func_type.is_ptr:
- func_type = func_type.base_type
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
-
+ if func_type.is_ptr:
+ func_type = func_type.base_type
+ if len(func_type.args) == 1:
+ self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
+ else:
+ self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
+ self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
+ self.type = func_type.return_type
+
def result_type(self, type1, type2, env):
if self.is_pythran_operation_types(type1, type2, env):
return PythranExpr(pythran_binop_type(self.operator, type1, type2))
- if self.is_py_operation_types(type1, type2):
- if type2.is_string:
- type2 = Builtin.bytes_type
- elif type2.is_pyunicode_ptr:
- type2 = Builtin.unicode_type
- if type1.is_string:
- type1 = Builtin.bytes_type
- elif type1.is_pyunicode_ptr:
- type1 = Builtin.unicode_type
- if type1.is_builtin_type or type2.is_builtin_type:
- if type1 is type2 and self.operator in '**%+|&^':
- # FIXME: at least these operators should be safe - others?
- return type1
- result_type = self.infer_builtin_types_operation(type1, type2)
- if result_type is not None:
- return result_type
- return py_object_type
+ if self.is_py_operation_types(type1, type2):
+ if type2.is_string:
+ type2 = Builtin.bytes_type
+ elif type2.is_pyunicode_ptr:
+ type2 = Builtin.unicode_type
+ if type1.is_string:
+ type1 = Builtin.bytes_type
+ elif type1.is_pyunicode_ptr:
+ type1 = Builtin.unicode_type
+ if type1.is_builtin_type or type2.is_builtin_type:
+ if type1 is type2 and self.operator in '**%+|&^':
+ # FIXME: at least these operators should be safe - others?
+ return type1
+ result_type = self.infer_builtin_types_operation(type1, type2)
+ if result_type is not None:
+ return result_type
+ return py_object_type
elif type1.is_error or type2.is_error:
return PyrexTypes.error_type
- else:
- return self.compute_c_result_type(type1, type2)
-
- def infer_builtin_types_operation(self, type1, type2):
- return None
-
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
-
- def coerce_operands_to_pyobjects(self, env):
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- self.operand2 = self.operand2.coerce_to_pyobject(env)
-
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
-
- def is_ephemeral(self):
- return (super(BinopNode, self).is_ephemeral() or
- self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
-
- def generate_result_code(self, code):
+ else:
+ return self.compute_c_result_type(type1, type2)
+
+ def infer_builtin_types_operation(self, type1, type2):
+ return None
+
+ def nogil_check(self, env):
+ if self.is_py_operation():
+ self.gil_error()
+
+ def coerce_operands_to_pyobjects(self, env):
+ self.operand1 = self.operand1.coerce_to_pyobject(env)
+ self.operand2 = self.operand2.coerce_to_pyobject(env)
+
+ def check_const(self):
+ return self.operand1.check_const() and self.operand2.check_const()
+
+ def is_ephemeral(self):
+ return (super(BinopNode, self).is_ephemeral() or
+ self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
+
+ def generate_result_code(self, code):
if self.type.is_pythran_expr:
code.putln("// Pythran binop")
code.putln("__Pyx_call_destructor(%s);" % self.result())
@@ -11167,21 +11167,21 @@ class BinopNode(ExprNode):
self.operator,
self.operand2.pythran_result()))
elif self.operand1.type.is_pyobject:
- function = self.py_operation_function(code)
- if self.operator == '**':
- extra_args = ", Py_None"
- else:
- extra_args = ""
- code.putln(
- "%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.operand1.py_result(),
- self.operand2.py_result(),
- extra_args,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.is_temp:
+ function = self.py_operation_function(code)
+ if self.operator == '**':
+ extra_args = ", Py_None"
+ else:
+ extra_args = ""
+ code.putln(
+ "%s = %s(%s, %s%s); %s" % (
+ self.result(),
+ function,
+ self.operand1.py_result(),
+ self.operand2.py_result(),
+ extra_args,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+ elif self.is_temp:
# C++ overloaded operators with exception values are currently all
# handled through temporaries.
if self.is_cpp_operation() and self.exception_check == '+':
@@ -11191,230 +11191,230 @@ class BinopNode(ExprNode):
self.exception_value, self.in_nogil_context)
else:
code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
-
- def type_error(self):
- if not (self.operand1.type.is_error
- or self.operand2.type.is_error):
- error(self.pos, "Invalid operand types for '%s' (%s; %s)" %
- (self.operator, self.operand1.type,
- self.operand2.type))
- self.type = PyrexTypes.error_type
-
-
-class CBinopNode(BinopNode):
-
- def analyse_types(self, env):
- node = BinopNode.analyse_types(self, env)
- if node.is_py_operation():
- node.type = PyrexTypes.error_type
- return node
-
- def py_operation_function(self, code):
- return ""
-
- def calculate_result_code(self):
- return "(%s %s %s)" % (
- self.operand1.result(),
- self.operator,
- self.operand2.result())
-
- def compute_c_result_type(self, type1, type2):
- cpp_type = None
- if type1.is_cpp_class or type1.is_ptr:
- cpp_type = type1.find_cpp_operation_type(self.operator, type2)
+
+ def type_error(self):
+ if not (self.operand1.type.is_error
+ or self.operand2.type.is_error):
+ error(self.pos, "Invalid operand types for '%s' (%s; %s)" %
+ (self.operator, self.operand1.type,
+ self.operand2.type))
+ self.type = PyrexTypes.error_type
+
+
+class CBinopNode(BinopNode):
+
+ def analyse_types(self, env):
+ node = BinopNode.analyse_types(self, env)
+ if node.is_py_operation():
+ node.type = PyrexTypes.error_type
+ return node
+
+ def py_operation_function(self, code):
+ return ""
+
+ def calculate_result_code(self):
+ return "(%s %s %s)" % (
+ self.operand1.result(),
+ self.operator,
+ self.operand2.result())
+
+ def compute_c_result_type(self, type1, type2):
+ cpp_type = None
+ if type1.is_cpp_class or type1.is_ptr:
+ cpp_type = type1.find_cpp_operation_type(self.operator, type2)
if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
cpp_type = type2.find_cpp_operation_type(self.operator, type1)
- # FIXME: do we need to handle other cases here?
- return cpp_type
-
-
-def c_binop_constructor(operator):
- def make_binop_node(pos, **operands):
- return CBinopNode(pos, operator=operator, **operands)
- return make_binop_node
-
-class NumBinopNode(BinopNode):
- # Binary operation taking numeric arguments.
-
- infix = True
- overflow_check = False
- overflow_bit_node = None
-
- def analyse_c_operation(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
- self.type = self.compute_c_result_type(type1, type2)
- if not self.type:
- self.type_error()
- return
- if self.type.is_complex:
- self.infix = False
- if (self.type.is_int
- and env.directives['overflowcheck']
- and self.operator in self.overflow_op_names):
- if (self.operator in ('+', '*')
- and self.operand1.has_constant_result()
- and not self.operand2.has_constant_result()):
- self.operand1, self.operand2 = self.operand2, self.operand1
- self.overflow_check = True
- self.overflow_fold = env.directives['overflowcheck.fold']
- self.func = self.type.overflow_check_binop(
- self.overflow_op_names[self.operator],
- env,
- const_rhs = self.operand2.has_constant_result())
- self.is_temp = True
- if not self.infix or (type1.is_numeric and type2.is_numeric):
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
-
- def compute_c_result_type(self, type1, type2):
- if self.c_types_okay(type1, type2):
- widest_type = PyrexTypes.widest_numeric_type(type1, type2)
- if widest_type is PyrexTypes.c_bint_type:
- if self.operator not in '|^&':
- # False + False == 0 # not False!
- widest_type = PyrexTypes.c_int_type
- else:
- widest_type = PyrexTypes.widest_numeric_type(
- widest_type, PyrexTypes.c_int_type)
- return widest_type
- else:
- return None
-
- def may_be_none(self):
- if self.type and self.type.is_builtin_type:
- # if we know the result type, we know the operation, so it can't be None
- return False
- type1 = self.operand1.type
- type2 = self.operand2.type
- if type1 and type1.is_builtin_type and type2 and type2.is_builtin_type:
- # XXX: I can't think of any case where a binary operation
- # on builtin types evaluates to None - add a special case
- # here if there is one.
- return False
- return super(NumBinopNode, self).may_be_none()
-
- def get_constant_c_result_code(self):
- value1 = self.operand1.get_constant_c_result_code()
- value2 = self.operand2.get_constant_c_result_code()
- if value1 and value2:
- return "(%s %s %s)" % (value1, self.operator, value2)
- else:
- return None
-
- def c_types_okay(self, type1, type2):
- #print "NumBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_numeric or type1.is_enum) \
- and (type2.is_numeric or type2.is_enum)
-
- def generate_evaluation_code(self, code):
- if self.overflow_check:
- self.overflow_bit_node = self
- self.overflow_bit = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % self.overflow_bit)
- super(NumBinopNode, self).generate_evaluation_code(code)
- if self.overflow_check:
- code.putln("if (unlikely(%s)) {" % self.overflow_bit)
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large");')
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- code.funcstate.release_temp(self.overflow_bit)
-
- def calculate_result_code(self):
- if self.overflow_bit_node is not None:
- return "%s(%s, %s, &%s)" % (
- self.func,
- self.operand1.result(),
- self.operand2.result(),
- self.overflow_bit_node.overflow_bit)
+ # FIXME: do we need to handle other cases here?
+ return cpp_type
+
+
+def c_binop_constructor(operator):
+ def make_binop_node(pos, **operands):
+ return CBinopNode(pos, operator=operator, **operands)
+ return make_binop_node
+
+class NumBinopNode(BinopNode):
+ # Binary operation taking numeric arguments.
+
+ infix = True
+ overflow_check = False
+ overflow_bit_node = None
+
+ def analyse_c_operation(self, env):
+ type1 = self.operand1.type
+ type2 = self.operand2.type
+ self.type = self.compute_c_result_type(type1, type2)
+ if not self.type:
+ self.type_error()
+ return
+ if self.type.is_complex:
+ self.infix = False
+ if (self.type.is_int
+ and env.directives['overflowcheck']
+ and self.operator in self.overflow_op_names):
+ if (self.operator in ('+', '*')
+ and self.operand1.has_constant_result()
+ and not self.operand2.has_constant_result()):
+ self.operand1, self.operand2 = self.operand2, self.operand1
+ self.overflow_check = True
+ self.overflow_fold = env.directives['overflowcheck.fold']
+ self.func = self.type.overflow_check_binop(
+ self.overflow_op_names[self.operator],
+ env,
+ const_rhs = self.operand2.has_constant_result())
+ self.is_temp = True
+ if not self.infix or (type1.is_numeric and type2.is_numeric):
+ self.operand1 = self.operand1.coerce_to(self.type, env)
+ self.operand2 = self.operand2.coerce_to(self.type, env)
+
+ def compute_c_result_type(self, type1, type2):
+ if self.c_types_okay(type1, type2):
+ widest_type = PyrexTypes.widest_numeric_type(type1, type2)
+ if widest_type is PyrexTypes.c_bint_type:
+ if self.operator not in '|^&':
+ # False + False == 0 # not False!
+ widest_type = PyrexTypes.c_int_type
+ else:
+ widest_type = PyrexTypes.widest_numeric_type(
+ widest_type, PyrexTypes.c_int_type)
+ return widest_type
+ else:
+ return None
+
+ def may_be_none(self):
+ if self.type and self.type.is_builtin_type:
+ # if we know the result type, we know the operation, so it can't be None
+ return False
+ type1 = self.operand1.type
+ type2 = self.operand2.type
+ if type1 and type1.is_builtin_type and type2 and type2.is_builtin_type:
+ # XXX: I can't think of any case where a binary operation
+ # on builtin types evaluates to None - add a special case
+ # here if there is one.
+ return False
+ return super(NumBinopNode, self).may_be_none()
+
+ def get_constant_c_result_code(self):
+ value1 = self.operand1.get_constant_c_result_code()
+ value2 = self.operand2.get_constant_c_result_code()
+ if value1 and value2:
+ return "(%s %s %s)" % (value1, self.operator, value2)
+ else:
+ return None
+
+ def c_types_okay(self, type1, type2):
+ #print "NumBinopNode.c_types_okay:", type1, type2 ###
+ return (type1.is_numeric or type1.is_enum) \
+ and (type2.is_numeric or type2.is_enum)
+
+ def generate_evaluation_code(self, code):
+ if self.overflow_check:
+ self.overflow_bit_node = self
+ self.overflow_bit = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
+ code.putln("%s = 0;" % self.overflow_bit)
+ super(NumBinopNode, self).generate_evaluation_code(code)
+ if self.overflow_check:
+ code.putln("if (unlikely(%s)) {" % self.overflow_bit)
+ code.putln('PyErr_SetString(PyExc_OverflowError, "value too large");')
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+ code.funcstate.release_temp(self.overflow_bit)
+
+ def calculate_result_code(self):
+ if self.overflow_bit_node is not None:
+ return "%s(%s, %s, &%s)" % (
+ self.func,
+ self.operand1.result(),
+ self.operand2.result(),
+ self.overflow_bit_node.overflow_bit)
elif self.type.is_cpp_class or self.infix:
if is_pythran_expr(self.type):
result1, result2 = self.operand1.pythran_result(), self.operand2.pythran_result()
else:
result1, result2 = self.operand1.result(), self.operand2.result()
return "(%s %s %s)" % (result1, self.operator, result2)
- else:
- func = self.type.binary_op(self.operator)
- if func is None:
- error(self.pos, "binary operator %s not supported for %s" % (self.operator, self.type))
- return "%s(%s, %s)" % (
- func,
- self.operand1.result(),
- self.operand2.result())
-
- def is_py_operation_types(self, type1, type2):
- return (type1.is_unicode_char or
- type2.is_unicode_char or
- BinopNode.is_py_operation_types(self, type1, type2))
-
- def py_operation_function(self, code):
- function_name = self.py_functions[self.operator]
- if self.inplace:
- function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace')
- return function_name
-
- py_functions = {
- "|": "PyNumber_Or",
- "^": "PyNumber_Xor",
- "&": "PyNumber_And",
- "<<": "PyNumber_Lshift",
- ">>": "PyNumber_Rshift",
- "+": "PyNumber_Add",
- "-": "PyNumber_Subtract",
- "*": "PyNumber_Multiply",
- "@": "__Pyx_PyNumber_MatrixMultiply",
- "/": "__Pyx_PyNumber_Divide",
- "//": "PyNumber_FloorDivide",
- "%": "PyNumber_Remainder",
- "**": "PyNumber_Power",
- }
-
- overflow_op_names = {
- "+": "add",
- "-": "sub",
- "*": "mul",
- "<<": "lshift",
- }
-
-
-class IntBinopNode(NumBinopNode):
- # Binary operation taking integer arguments.
-
- def c_types_okay(self, type1, type2):
- #print "IntBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_int or type1.is_enum) \
- and (type2.is_int or type2.is_enum)
-
-
-class AddNode(NumBinopNode):
- # '+' operator.
-
- def is_py_operation_types(self, type1, type2):
- if type1.is_string and type2.is_string or type1.is_pyunicode_ptr and type2.is_pyunicode_ptr:
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
-
- def infer_builtin_types_operation(self, type1, type2):
- # b'abc' + 'abc' raises an exception in Py3,
- # so we can safely infer the Py2 type for bytes here
+ else:
+ func = self.type.binary_op(self.operator)
+ if func is None:
+ error(self.pos, "binary operator %s not supported for %s" % (self.operator, self.type))
+ return "%s(%s, %s)" % (
+ func,
+ self.operand1.result(),
+ self.operand2.result())
+
+ def is_py_operation_types(self, type1, type2):
+ return (type1.is_unicode_char or
+ type2.is_unicode_char or
+ BinopNode.is_py_operation_types(self, type1, type2))
+
+ def py_operation_function(self, code):
+ function_name = self.py_functions[self.operator]
+ if self.inplace:
+ function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace')
+ return function_name
+
+ py_functions = {
+ "|": "PyNumber_Or",
+ "^": "PyNumber_Xor",
+ "&": "PyNumber_And",
+ "<<": "PyNumber_Lshift",
+ ">>": "PyNumber_Rshift",
+ "+": "PyNumber_Add",
+ "-": "PyNumber_Subtract",
+ "*": "PyNumber_Multiply",
+ "@": "__Pyx_PyNumber_MatrixMultiply",
+ "/": "__Pyx_PyNumber_Divide",
+ "//": "PyNumber_FloorDivide",
+ "%": "PyNumber_Remainder",
+ "**": "PyNumber_Power",
+ }
+
+ overflow_op_names = {
+ "+": "add",
+ "-": "sub",
+ "*": "mul",
+ "<<": "lshift",
+ }
+
+
+class IntBinopNode(NumBinopNode):
+ # Binary operation taking integer arguments.
+
+ def c_types_okay(self, type1, type2):
+ #print "IntBinopNode.c_types_okay:", type1, type2 ###
+ return (type1.is_int or type1.is_enum) \
+ and (type2.is_int or type2.is_enum)
+
+
+class AddNode(NumBinopNode):
+ # '+' operator.
+
+ def is_py_operation_types(self, type1, type2):
+ if type1.is_string and type2.is_string or type1.is_pyunicode_ptr and type2.is_pyunicode_ptr:
+ return 1
+ else:
+ return NumBinopNode.is_py_operation_types(self, type1, type2)
+
+ def infer_builtin_types_operation(self, type1, type2):
+ # b'abc' + 'abc' raises an exception in Py3,
+ # so we can safely infer the Py2 type for bytes here
string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2 in string_types:
- return string_types[max(string_types.index(type1),
- string_types.index(type2))]
- return None
-
- def compute_c_result_type(self, type1, type2):
- #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ###
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum):
- return type2
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
-
- def py_operation_function(self, code):
+ if type1 in string_types and type2 in string_types:
+ return string_types[max(string_types.index(type1),
+ string_types.index(type2))]
+ return None
+
+ def compute_c_result_type(self, type1, type2):
+ #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ###
+ if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
+ return type1
+ elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum):
+ return type2
+ else:
+ return NumBinopNode.compute_c_result_type(
+ self, type1, type2)
+
+ def py_operation_function(self, code):
type1, type2 = self.operand1.type, self.operand2.type
if type1 is unicode_type or type2 is unicode_type:
@@ -11433,100 +11433,100 @@ class AddNode(NumBinopNode):
else:
return '__Pyx_PyUnicode_Concat'
- return super(AddNode, self).py_operation_function(code)
-
-
-class SubNode(NumBinopNode):
- # '-' operator.
-
- def compute_c_result_type(self, type1, type2):
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array):
- return PyrexTypes.c_ptrdiff_t_type
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
-
-
-class MulNode(NumBinopNode):
- # '*' operator.
-
- def is_py_operation_types(self, type1, type2):
- if ((type1.is_string and type2.is_int) or
- (type2.is_string and type1.is_int)):
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
-
- def infer_builtin_types_operation(self, type1, type2):
- # let's assume that whatever builtin type you multiply a string with
- # will either return a string of the same type or fail with an exception
+ return super(AddNode, self).py_operation_function(code)
+
+
+class SubNode(NumBinopNode):
+ # '-' operator.
+
+ def compute_c_result_type(self, type1, type2):
+ if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
+ return type1
+ elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array):
+ return PyrexTypes.c_ptrdiff_t_type
+ else:
+ return NumBinopNode.compute_c_result_type(
+ self, type1, type2)
+
+
+class MulNode(NumBinopNode):
+ # '*' operator.
+
+ def is_py_operation_types(self, type1, type2):
+ if ((type1.is_string and type2.is_int) or
+ (type2.is_string and type1.is_int)):
+ return 1
+ else:
+ return NumBinopNode.is_py_operation_types(self, type1, type2)
+
+ def infer_builtin_types_operation(self, type1, type2):
+ # let's assume that whatever builtin type you multiply a string with
+ # will either return a string of the same type or fail with an exception
string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2.is_builtin_type:
- return type1
- if type2 in string_types and type1.is_builtin_type:
- return type2
- # multiplication of containers/numbers with an integer value
- # always (?) returns the same type
- if type1.is_int:
- return type2
- if type2.is_int:
- return type1
- return None
-
-
-class MatMultNode(NumBinopNode):
- # '@' operator.
-
- def is_py_operation_types(self, type1, type2):
- return True
-
- def generate_evaluation_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("MatrixMultiply", "ObjectHandling.c"))
- super(MatMultNode, self).generate_evaluation_code(code)
-
-
-class DivNode(NumBinopNode):
- # '/' or '//' operator.
-
- cdivision = None
- truedivision = None # == "unknown" if operator == '/'
- ctruedivision = False
- cdivision_warnings = False
- zerodivision_check = None
-
- def find_compile_time_binary_operator(self, op1, op2):
- func = compile_time_binary_operators[self.operator]
- if self.operator == '/' and self.truedivision is None:
- # => true div for floats, floor div for integers
+ if type1 in string_types and type2.is_builtin_type:
+ return type1
+ if type2 in string_types and type1.is_builtin_type:
+ return type2
+ # multiplication of containers/numbers with an integer value
+ # always (?) returns the same type
+ if type1.is_int:
+ return type2
+ if type2.is_int:
+ return type1
+ return None
+
+
+class MatMultNode(NumBinopNode):
+ # '@' operator.
+
+ def is_py_operation_types(self, type1, type2):
+ return True
+
+ def generate_evaluation_code(self, code):
+ code.globalstate.use_utility_code(UtilityCode.load_cached("MatrixMultiply", "ObjectHandling.c"))
+ super(MatMultNode, self).generate_evaluation_code(code)
+
+
+class DivNode(NumBinopNode):
+ # '/' or '//' operator.
+
+ cdivision = None
+ truedivision = None # == "unknown" if operator == '/'
+ ctruedivision = False
+ cdivision_warnings = False
+ zerodivision_check = None
+
+ def find_compile_time_binary_operator(self, op1, op2):
+ func = compile_time_binary_operators[self.operator]
+ if self.operator == '/' and self.truedivision is None:
+ # => true div for floats, floor div for integers
if isinstance(op1, _py_int_types) and isinstance(op2, _py_int_types):
- func = compile_time_binary_operators['//']
- return func
-
- def calculate_constant_result(self):
- op1 = self.operand1.constant_result
- op2 = self.operand2.constant_result
- func = self.find_compile_time_binary_operator(op1, op2)
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
-
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- func = self.find_compile_time_binary_operator(
- operand1, operand2)
- return func(operand1, operand2)
+ func = compile_time_binary_operators['//']
+ return func
+
+ def calculate_constant_result(self):
+ op1 = self.operand1.constant_result
+ op2 = self.operand2.constant_result
+ func = self.find_compile_time_binary_operator(op1, op2)
+ self.constant_result = func(
+ self.operand1.constant_result,
+ self.operand2.constant_result)
+
+ def compile_time_value(self, denv):
+ operand1 = self.operand1.compile_time_value(denv)
+ operand2 = self.operand2.compile_time_value(denv)
+ try:
+ func = self.find_compile_time_binary_operator(
+ operand1, operand2)
+ return func(operand1, operand2)
except Exception as e:
- self.compile_time_value_error(e)
-
+ self.compile_time_value_error(e)
+
def _check_truedivision(self, env):
- if self.cdivision or env.directives['cdivision']:
- self.ctruedivision = False
- else:
- self.ctruedivision = self.truedivision
+ if self.cdivision or env.directives['cdivision']:
+ self.ctruedivision = False
+ else:
+ self.ctruedivision = self.truedivision
def infer_type(self, env):
self._check_truedivision(env)
@@ -11536,93 +11536,93 @@ class DivNode(NumBinopNode):
def analyse_operation(self, env):
self._check_truedivision(env)
- NumBinopNode.analyse_operation(self, env)
- if self.is_cpp_operation():
- self.cdivision = True
- if not self.type.is_pyobject:
- self.zerodivision_check = (
- self.cdivision is None and not env.directives['cdivision']
- and (not self.operand2.has_constant_result() or
- self.operand2.constant_result == 0))
- if self.zerodivision_check or env.directives['cdivision_warnings']:
- # Need to check ahead of time to warn or raise zero division error
- self.operand1 = self.operand1.coerce_to_simple(env)
- self.operand2 = self.operand2.coerce_to_simple(env)
-
- def compute_c_result_type(self, type1, type2):
+ NumBinopNode.analyse_operation(self, env)
+ if self.is_cpp_operation():
+ self.cdivision = True
+ if not self.type.is_pyobject:
+ self.zerodivision_check = (
+ self.cdivision is None and not env.directives['cdivision']
+ and (not self.operand2.has_constant_result() or
+ self.operand2.constant_result == 0))
+ if self.zerodivision_check or env.directives['cdivision_warnings']:
+ # Need to check ahead of time to warn or raise zero division error
+ self.operand1 = self.operand1.coerce_to_simple(env)
+ self.operand2 = self.operand2.coerce_to_simple(env)
+
+ def compute_c_result_type(self, type1, type2):
if self.operator == '/' and self.ctruedivision and not type1.is_cpp_class and not type2.is_cpp_class:
- if not type1.is_float and not type2.is_float:
- widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
- widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
- return widest_type
- return NumBinopNode.compute_c_result_type(self, type1, type2)
-
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float division"
-
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.type.is_complex:
- if self.cdivision is None:
+ if not type1.is_float and not type2.is_float:
+ widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
+ widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
+ return widest_type
+ return NumBinopNode.compute_c_result_type(self, type1, type2)
+
+ def zero_division_message(self):
+ if self.type.is_int:
+ return "integer division or modulo by zero"
+ else:
+ return "float division"
+
+ def generate_evaluation_code(self, code):
+ if not self.type.is_pyobject and not self.type.is_complex:
+ if self.cdivision is None:
self.cdivision = (
code.globalstate.directives['cdivision']
or self.type.is_float
or ((self.type.is_numeric or self.type.is_enum) and not self.type.signed)
)
- if not self.cdivision:
+ if not self.cdivision:
code.globalstate.use_utility_code(
UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type))
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
-
- def generate_div_warning_code(self, code):
+ NumBinopNode.generate_evaluation_code(self, code)
+ self.generate_div_warning_code(code)
+
+ def generate_div_warning_code(self, code):
in_nogil = self.in_nogil_context
- if not self.type.is_pyobject:
- if self.zerodivision_check:
- if not self.infix:
- zero_test = "%s(%s)" % (self.type.unary_op('zero'), self.operand2.result())
- else:
- zero_test = "%s == 0" % self.operand2.result()
- code.putln("if (unlikely(%s)) {" % zero_test)
+ if not self.type.is_pyobject:
+ if self.zerodivision_check:
+ if not self.infix:
+ zero_test = "%s(%s)" % (self.type.unary_op('zero'), self.operand2.result())
+ else:
+ zero_test = "%s == 0" % self.operand2.result()
+ code.putln("if (unlikely(%s)) {" % zero_test)
if in_nogil:
code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message())
+ code.putln('PyErr_SetString(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message())
if in_nogil:
code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if self.type.is_int and self.type.signed and self.operator != '%':
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+ if self.type.is_int and self.type.signed and self.operator != '%':
code.globalstate.use_utility_code(UtilityCode.load_cached("UnaryNegOverflows", "Overflow.c"))
- if self.operand2.type.signed == 2:
- # explicitly signed, no runtime check needed
- minus1_check = 'unlikely(%s == -1)' % self.operand2.result()
- else:
+ if self.operand2.type.signed == 2:
+ # explicitly signed, no runtime check needed
+ minus1_check = 'unlikely(%s == -1)' % self.operand2.result()
+ else:
type_of_op2 = self.operand2.type.empty_declaration_code()
- minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
- type_of_op2, self.operand2.result(), type_of_op2)
- code.putln("else if (sizeof(%s) == sizeof(long) && %s "
- " && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
+ minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
+ type_of_op2, self.operand2.result(), type_of_op2)
+ code.putln("else if (sizeof(%s) == sizeof(long) && %s "
+ " && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
self.type.empty_declaration_code(),
- minus1_check,
- self.operand1.result()))
+ minus1_check,
+ self.operand1.result()))
if in_nogil:
code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large to perform division");')
+ code.putln('PyErr_SetString(PyExc_OverflowError, "value too large to perform division");')
if in_nogil:
code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if code.globalstate.directives['cdivision_warnings'] and self.operator != '/':
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+ if code.globalstate.directives['cdivision_warnings'] and self.operator != '/':
code.globalstate.use_utility_code(
UtilityCode.load_cached("CDivisionWarning", "CMath.c"))
- code.putln("if (unlikely((%s < 0) ^ (%s < 0))) {" % (
- self.operand1.result(),
- self.operand2.result()))
+ code.putln("if (unlikely((%s < 0) ^ (%s < 0))) {" % (
+ self.operand1.result(),
+ self.operand2.result()))
warning_code = "__Pyx_cdivision_warning(%(FILENAME)s, %(LINENO)s)" % {
- 'FILENAME': Naming.filename_cname,
- 'LINENO': Naming.lineno_cname,
+ 'FILENAME': Naming.filename_cname,
+ 'LINENO': Naming.lineno_cname,
}
if in_nogil:
@@ -11637,32 +11637,32 @@ class DivNode(NumBinopNode):
code.putln(code.set_error_info(self.pos, used=True))
code.put("if (unlikely(%s)) " % result_code)
- code.put_goto(code.error_label)
- code.putln("}")
-
- def calculate_result_code(self):
+ code.put_goto(code.error_label)
+ code.putln("}")
+
+ def calculate_result_code(self):
if self.type.is_complex or self.is_cpp_operation():
- return NumBinopNode.calculate_result_code(self)
- elif self.type.is_float and self.operator == '//':
- return "floor(%s / %s)" % (
- self.operand1.result(),
- self.operand2.result())
- elif self.truedivision or self.cdivision:
- op1 = self.operand1.result()
- op2 = self.operand2.result()
- if self.truedivision:
- if self.type != self.operand1.type:
- op1 = self.type.cast_code(op1)
- if self.type != self.operand2.type:
- op2 = self.type.cast_code(op2)
- return "(%s / %s)" % (op1, op2)
- else:
- return "__Pyx_div_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
-
-
+ return NumBinopNode.calculate_result_code(self)
+ elif self.type.is_float and self.operator == '//':
+ return "floor(%s / %s)" % (
+ self.operand1.result(),
+ self.operand2.result())
+ elif self.truedivision or self.cdivision:
+ op1 = self.operand1.result()
+ op2 = self.operand2.result()
+ if self.truedivision:
+ if self.type != self.operand1.type:
+ op1 = self.type.cast_code(op1)
+ if self.type != self.operand2.type:
+ op2 = self.type.cast_code(op2)
+ return "(%s / %s)" % (op1, op2)
+ else:
+ return "__Pyx_div_%s(%s, %s)" % (
+ self.type.specialization_name(),
+ self.operand1.result(),
+ self.operand2.result())
+
+
_find_formatting_types = re.compile(
br"%"
br"(?:%|" # %%
@@ -11677,835 +11677,835 @@ _safe_bytes_formats = set([
])
-class ModNode(DivNode):
- # '%' operator.
-
- def is_py_operation_types(self, type1, type2):
- return (type1.is_string
- or type2.is_string
- or NumBinopNode.is_py_operation_types(self, type1, type2))
-
- def infer_builtin_types_operation(self, type1, type2):
+class ModNode(DivNode):
+ # '%' operator.
+
+ def is_py_operation_types(self, type1, type2):
+ return (type1.is_string
+ or type2.is_string
+ or NumBinopNode.is_py_operation_types(self, type1, type2))
+
+ def infer_builtin_types_operation(self, type1, type2):
# b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for Py2 and later Py3's.
- if type1 is unicode_type:
- # None + xyz may be implemented by RHS
- if type2.is_builtin_type or not self.operand1.may_be_none():
- return type1
- elif type1 in (bytes_type, str_type, basestring_type):
- if type2 is unicode_type:
- return type2
- elif type2.is_numeric:
- return type1
+ if type1 is unicode_type:
+ # None + xyz may be implemented by RHS
+ if type2.is_builtin_type or not self.operand1.may_be_none():
+ return type1
+ elif type1 in (bytes_type, str_type, basestring_type):
+ if type2 is unicode_type:
+ return type2
+ elif type2.is_numeric:
+ return type1
elif self.operand1.is_string_literal:
if type1 is str_type or type1 is bytes_type:
if set(_find_formatting_types(self.operand1.value)) <= _safe_bytes_formats:
return type1
return basestring_type
- elif type1 is bytes_type and not type2.is_builtin_type:
- return None # RHS might implement '% operator differently in Py3
- else:
- return basestring_type # either str or unicode, can't tell
- return None
-
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float divmod()"
-
- def analyse_operation(self, env):
- DivNode.analyse_operation(self, env)
- if not self.type.is_pyobject:
- if self.cdivision is None:
- self.cdivision = env.directives['cdivision'] or not self.type.signed
- if not self.cdivision and not self.type.is_int and not self.type.is_float:
- error(self.pos, "mod operator not supported for type '%s'" % self.type)
-
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.cdivision:
- if self.type.is_int:
- code.globalstate.use_utility_code(
+ elif type1 is bytes_type and not type2.is_builtin_type:
+ return None # RHS might implement '% operator differently in Py3
+ else:
+ return basestring_type # either str or unicode, can't tell
+ return None
+
+ def zero_division_message(self):
+ if self.type.is_int:
+ return "integer division or modulo by zero"
+ else:
+ return "float divmod()"
+
+ def analyse_operation(self, env):
+ DivNode.analyse_operation(self, env)
+ if not self.type.is_pyobject:
+ if self.cdivision is None:
+ self.cdivision = env.directives['cdivision'] or not self.type.signed
+ if not self.cdivision and not self.type.is_int and not self.type.is_float:
+ error(self.pos, "mod operator not supported for type '%s'" % self.type)
+
+ def generate_evaluation_code(self, code):
+ if not self.type.is_pyobject and not self.cdivision:
+ if self.type.is_int:
+ code.globalstate.use_utility_code(
UtilityCode.load_cached("ModInt", "CMath.c").specialize(self.type))
- else: # float
- code.globalstate.use_utility_code(
+ else: # float
+ code.globalstate.use_utility_code(
UtilityCode.load_cached("ModFloat", "CMath.c").specialize(
- self.type, math_h_modifier=self.type.math_h_modifier))
+ self.type, math_h_modifier=self.type.math_h_modifier))
# NOTE: skipping over DivNode here
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
-
- def calculate_result_code(self):
- if self.cdivision:
- if self.type.is_float:
- return "fmod%s(%s, %s)" % (
- self.type.math_h_modifier,
- self.operand1.result(),
- self.operand2.result())
- else:
- return "(%s %% %s)" % (
- self.operand1.result(),
- self.operand2.result())
- else:
- return "__Pyx_mod_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
-
- def py_operation_function(self, code):
+ NumBinopNode.generate_evaluation_code(self, code)
+ self.generate_div_warning_code(code)
+
+ def calculate_result_code(self):
+ if self.cdivision:
+ if self.type.is_float:
+ return "fmod%s(%s, %s)" % (
+ self.type.math_h_modifier,
+ self.operand1.result(),
+ self.operand2.result())
+ else:
+ return "(%s %% %s)" % (
+ self.operand1.result(),
+ self.operand2.result())
+ else:
+ return "__Pyx_mod_%s(%s, %s)" % (
+ self.type.specialization_name(),
+ self.operand1.result(),
+ self.operand2.result())
+
+ def py_operation_function(self, code):
type1, type2 = self.operand1.type, self.operand2.type
# ("..." % x) must call "x.__rmod__()" for string subtypes.
if type1 is unicode_type:
if self.operand1.may_be_none() or (
type2.is_extension_type and type2.subtype_of(type1) or
type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyUnicode_FormatSafe'
- else:
- return 'PyUnicode_Format'
+ return '__Pyx_PyUnicode_FormatSafe'
+ else:
+ return 'PyUnicode_Format'
elif type1 is str_type:
if self.operand1.may_be_none() or (
type2.is_extension_type and type2.subtype_of(type1) or
type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyString_FormatSafe'
- else:
- return '__Pyx_PyString_Format'
- return super(ModNode, self).py_operation_function(code)
-
-
-class PowNode(NumBinopNode):
- # '**' operator.
-
- def analyse_c_operation(self, env):
- NumBinopNode.analyse_c_operation(self, env)
- if self.type.is_complex:
- if self.type.real_type.is_float:
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
+ return '__Pyx_PyString_FormatSafe'
+ else:
+ return '__Pyx_PyString_Format'
+ return super(ModNode, self).py_operation_function(code)
+
+
+class PowNode(NumBinopNode):
+ # '**' operator.
+
+ def analyse_c_operation(self, env):
+ NumBinopNode.analyse_c_operation(self, env)
+ if self.type.is_complex:
+ if self.type.real_type.is_float:
+ self.operand1 = self.operand1.coerce_to(self.type, env)
+ self.operand2 = self.operand2.coerce_to(self.type, env)
self.pow_func = self.type.binary_op('**')
- else:
- error(self.pos, "complex int powers not supported")
- self.pow_func = "<error>"
- elif self.type.is_float:
- self.pow_func = "pow" + self.type.math_h_modifier
- elif self.type.is_int:
+ else:
+ error(self.pos, "complex int powers not supported")
+ self.pow_func = "<error>"
+ elif self.type.is_float:
+ self.pow_func = "pow" + self.type.math_h_modifier
+ elif self.type.is_int:
self.pow_func = "__Pyx_pow_%s" % self.type.empty_declaration_code().replace(' ', '_')
- env.use_utility_code(
+ env.use_utility_code(
UtilityCode.load_cached("IntPow", "CMath.c").specialize(
- func_name=self.pow_func,
+ func_name=self.pow_func,
type=self.type.empty_declaration_code(),
- signed=self.type.signed and 1 or 0))
- elif not self.type.is_error:
- error(self.pos, "got unexpected types for C power operator: %s, %s" %
- (self.operand1.type, self.operand2.type))
-
- def calculate_result_code(self):
- # Work around MSVC overloading ambiguity.
- def typecast(operand):
- if self.type == operand.type:
- return operand.result()
- else:
- return self.type.cast_code(operand.result())
- return "%s(%s, %s)" % (
- self.pow_func,
- typecast(self.operand1),
- typecast(self.operand2))
-
- def py_operation_function(self, code):
- if (self.type.is_pyobject and
- self.operand1.constant_result == 2 and
+ signed=self.type.signed and 1 or 0))
+ elif not self.type.is_error:
+ error(self.pos, "got unexpected types for C power operator: %s, %s" %
+ (self.operand1.type, self.operand2.type))
+
+ def calculate_result_code(self):
+ # Work around MSVC overloading ambiguity.
+ def typecast(operand):
+ if self.type == operand.type:
+ return operand.result()
+ else:
+ return self.type.cast_code(operand.result())
+ return "%s(%s, %s)" % (
+ self.pow_func,
+ typecast(self.operand1),
+ typecast(self.operand2))
+
+ def py_operation_function(self, code):
+ if (self.type.is_pyobject and
+ self.operand1.constant_result == 2 and
isinstance(self.operand1.constant_result, _py_int_types) and
- self.operand2.type is py_object_type):
- code.globalstate.use_utility_code(UtilityCode.load_cached('PyNumberPow2', 'Optimize.c'))
- if self.inplace:
- return '__Pyx_PyNumber_InPlacePowerOf2'
- else:
- return '__Pyx_PyNumber_PowerOf2'
- return super(PowNode, self).py_operation_function(code)
-
-
-class BoolBinopNode(ExprNode):
- """
- Short-circuiting boolean operation.
-
- Note that this node provides the same code generation method as
- BoolBinopResultNode to simplify expression nesting.
-
- operator string "and"/"or"
- operand1 BoolBinopNode/BoolBinopResultNode left operand
- operand2 BoolBinopNode/BoolBinopResultNode right operand
- """
- subexprs = ['operand1', 'operand2']
- is_temp = True
- operator = None
- operand1 = None
- operand2 = None
-
- def infer_type(self, env):
- type1 = self.operand1.infer_type(env)
- type2 = self.operand2.infer_type(env)
- return PyrexTypes.independent_spanning_type(type1, type2)
-
- def may_be_none(self):
- if self.operator == 'or':
- return self.operand2.may_be_none()
- else:
- return self.operand1.may_be_none() or self.operand2.may_be_none()
-
- def calculate_constant_result(self):
- operand1 = self.operand1.constant_result
- operand2 = self.operand2.constant_result
- if self.operator == 'and':
- self.constant_result = operand1 and operand2
- else:
- self.constant_result = operand1 or operand2
-
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- if self.operator == 'and':
- return operand1 and operand2
- else:
- return operand1 or operand2
-
- def is_ephemeral(self):
- return self.operand1.is_ephemeral() or self.operand2.is_ephemeral()
-
- def analyse_types(self, env):
- # Note: we do not do any coercion here as we most likely do not know the final type anyway.
- # We even accept to set self.type to ErrorType if both operands do not have a spanning type.
- # The coercion to the final type and to a "simple" value is left to coerce_to().
- operand1 = self.operand1.analyse_types(env)
- operand2 = self.operand2.analyse_types(env)
- self.type = PyrexTypes.independent_spanning_type(
- operand1.type, operand2.type)
- self.operand1 = self._wrap_operand(operand1, env)
- self.operand2 = self._wrap_operand(operand2, env)
- return self
-
- def _wrap_operand(self, operand, env):
- if not isinstance(operand, (BoolBinopNode, BoolBinopResultNode)):
- operand = BoolBinopResultNode(operand, self.type, env)
- return operand
-
- def wrap_operands(self, env):
- """
- Must get called by transforms that want to create a correct BoolBinopNode
- after the type analysis phase.
- """
- self.operand1 = self._wrap_operand(self.operand1, env)
- self.operand2 = self._wrap_operand(self.operand2, env)
-
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
-
- def coerce_to(self, dst_type, env):
- operand1 = self.operand1.coerce_to(dst_type, env)
- operand2 = self.operand2.coerce_to(dst_type, env)
- return BoolBinopNode.from_node(
- self, type=dst_type,
- operator=self.operator,
- operand1=operand1, operand2=operand2)
-
+ self.operand2.type is py_object_type):
+ code.globalstate.use_utility_code(UtilityCode.load_cached('PyNumberPow2', 'Optimize.c'))
+ if self.inplace:
+ return '__Pyx_PyNumber_InPlacePowerOf2'
+ else:
+ return '__Pyx_PyNumber_PowerOf2'
+ return super(PowNode, self).py_operation_function(code)
+
+
+class BoolBinopNode(ExprNode):
+ """
+ Short-circuiting boolean operation.
+
+ Note that this node provides the same code generation method as
+ BoolBinopResultNode to simplify expression nesting.
+
+ operator string "and"/"or"
+ operand1 BoolBinopNode/BoolBinopResultNode left operand
+ operand2 BoolBinopNode/BoolBinopResultNode right operand
+ """
+ subexprs = ['operand1', 'operand2']
+ is_temp = True
+ operator = None
+ operand1 = None
+ operand2 = None
+
+ def infer_type(self, env):
+ type1 = self.operand1.infer_type(env)
+ type2 = self.operand2.infer_type(env)
+ return PyrexTypes.independent_spanning_type(type1, type2)
+
+ def may_be_none(self):
+ if self.operator == 'or':
+ return self.operand2.may_be_none()
+ else:
+ return self.operand1.may_be_none() or self.operand2.may_be_none()
+
+ def calculate_constant_result(self):
+ operand1 = self.operand1.constant_result
+ operand2 = self.operand2.constant_result
+ if self.operator == 'and':
+ self.constant_result = operand1 and operand2
+ else:
+ self.constant_result = operand1 or operand2
+
+ def compile_time_value(self, denv):
+ operand1 = self.operand1.compile_time_value(denv)
+ operand2 = self.operand2.compile_time_value(denv)
+ if self.operator == 'and':
+ return operand1 and operand2
+ else:
+ return operand1 or operand2
+
+ def is_ephemeral(self):
+ return self.operand1.is_ephemeral() or self.operand2.is_ephemeral()
+
+ def analyse_types(self, env):
+ # Note: we do not do any coercion here as we most likely do not know the final type anyway.
+ # We even accept to set self.type to ErrorType if both operands do not have a spanning type.
+ # The coercion to the final type and to a "simple" value is left to coerce_to().
+ operand1 = self.operand1.analyse_types(env)
+ operand2 = self.operand2.analyse_types(env)
+ self.type = PyrexTypes.independent_spanning_type(
+ operand1.type, operand2.type)
+ self.operand1 = self._wrap_operand(operand1, env)
+ self.operand2 = self._wrap_operand(operand2, env)
+ return self
+
+ def _wrap_operand(self, operand, env):
+ if not isinstance(operand, (BoolBinopNode, BoolBinopResultNode)):
+ operand = BoolBinopResultNode(operand, self.type, env)
+ return operand
+
+ def wrap_operands(self, env):
+ """
+ Must get called by transforms that want to create a correct BoolBinopNode
+ after the type analysis phase.
+ """
+ self.operand1 = self._wrap_operand(self.operand1, env)
+ self.operand2 = self._wrap_operand(self.operand2, env)
+
+ def coerce_to_boolean(self, env):
+ return self.coerce_to(PyrexTypes.c_bint_type, env)
+
+ def coerce_to(self, dst_type, env):
+ operand1 = self.operand1.coerce_to(dst_type, env)
+ operand2 = self.operand2.coerce_to(dst_type, env)
+ return BoolBinopNode.from_node(
+ self, type=dst_type,
+ operator=self.operator,
+ operand1=operand1, operand2=operand2)
+
def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
-
- outer_labels = (and_label, or_label)
- if self.operator == 'and':
- my_label = and_label = code.new_label('next_and')
- else:
- my_label = or_label = code.new_label('next_or')
+ code.mark_pos(self.pos)
+
+ outer_labels = (and_label, or_label)
+ if self.operator == 'and':
+ my_label = and_label = code.new_label('next_and')
+ else:
+ my_label = or_label = code.new_label('next_or')
self.operand1.generate_bool_evaluation_code(
code, final_result_temp, final_result_type, and_label, or_label, end_label, my_label)
-
- and_label, or_label = outer_labels
-
- code.put_label(my_label)
+
+ and_label, or_label = outer_labels
+
+ code.put_label(my_label)
self.operand2.generate_bool_evaluation_code(
code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through)
-
- def generate_evaluation_code(self, code):
- self.allocate_temp_result(code)
+
+ def generate_evaluation_code(self, code):
+ self.allocate_temp_result(code)
result_type = PyrexTypes.py_object_type if self.type.is_pyobject else self.type
- or_label = and_label = None
- end_label = code.new_label('bool_binop_done')
+ or_label = and_label = None
+ end_label = code.new_label('bool_binop_done')
self.generate_bool_evaluation_code(code, self.result(), result_type, and_label, or_label, end_label, end_label)
code.put_label(end_label)
-
- gil_message = "Truth-testing Python object"
-
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
-
- def generate_subexpr_disposal_code(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
-
- def free_subexpr_temps(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
-
- def generate_operand1_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.operand1.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.operand1.result()
- return (test_result, self.type.is_pyobject)
-
-
-class BoolBinopResultNode(ExprNode):
- """
- Intermediate result of a short-circuiting and/or expression.
- Tests the result for 'truthiness' and takes care of coercing the final result
- of the overall expression to the target type.
-
- Note that this node provides the same code generation method as
- BoolBinopNode to simplify expression nesting.
-
- arg ExprNode the argument to test
- value ExprNode the coerced result value node
- """
-
- subexprs = ['arg', 'value']
- is_temp = True
- arg = None
- value = None
-
- def __init__(self, arg, result_type, env):
- # using 'arg' multiple times, so it must be a simple/temp value
- arg = arg.coerce_to_simple(env)
- # wrap in ProxyNode, in case a transform wants to replace self.arg later
- arg = ProxyNode(arg)
- super(BoolBinopResultNode, self).__init__(
- arg.pos, arg=arg, type=result_type,
- value=CloneNode(arg).coerce_to(result_type, env))
-
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
-
- def coerce_to(self, dst_type, env):
- # unwrap, coerce, rewrap
- arg = self.arg.arg
- if dst_type is PyrexTypes.c_bint_type:
- arg = arg.coerce_to_boolean(env)
- # TODO: unwrap more coercion nodes?
- return BoolBinopResultNode(arg, dst_type, env)
-
- def nogil_check(self, env):
- # let's leave all errors to BoolBinopNode
- pass
-
- def generate_operand_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.arg.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.arg.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.arg.result()
- return (test_result, self.arg.type.is_pyobject)
-
+
+ gil_message = "Truth-testing Python object"
+
+ def check_const(self):
+ return self.operand1.check_const() and self.operand2.check_const()
+
+ def generate_subexpr_disposal_code(self, code):
+ pass # nothing to do here, all done in generate_evaluation_code()
+
+ def free_subexpr_temps(self, code):
+ pass # nothing to do here, all done in generate_evaluation_code()
+
+ def generate_operand1_test(self, code):
+ # Generate code to test the truth of the first operand.
+ if self.type.is_pyobject:
+ test_result = code.funcstate.allocate_temp(
+ PyrexTypes.c_bint_type, manage_ref=False)
+ code.putln(
+ "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
+ test_result,
+ self.operand1.py_result(),
+ code.error_goto_if_neg(test_result, self.pos)))
+ else:
+ test_result = self.operand1.result()
+ return (test_result, self.type.is_pyobject)
+
+
+class BoolBinopResultNode(ExprNode):
+ """
+ Intermediate result of a short-circuiting and/or expression.
+ Tests the result for 'truthiness' and takes care of coercing the final result
+ of the overall expression to the target type.
+
+ Note that this node provides the same code generation method as
+ BoolBinopNode to simplify expression nesting.
+
+ arg ExprNode the argument to test
+ value ExprNode the coerced result value node
+ """
+
+ subexprs = ['arg', 'value']
+ is_temp = True
+ arg = None
+ value = None
+
+ def __init__(self, arg, result_type, env):
+ # using 'arg' multiple times, so it must be a simple/temp value
+ arg = arg.coerce_to_simple(env)
+ # wrap in ProxyNode, in case a transform wants to replace self.arg later
+ arg = ProxyNode(arg)
+ super(BoolBinopResultNode, self).__init__(
+ arg.pos, arg=arg, type=result_type,
+ value=CloneNode(arg).coerce_to(result_type, env))
+
+ def coerce_to_boolean(self, env):
+ return self.coerce_to(PyrexTypes.c_bint_type, env)
+
+ def coerce_to(self, dst_type, env):
+ # unwrap, coerce, rewrap
+ arg = self.arg.arg
+ if dst_type is PyrexTypes.c_bint_type:
+ arg = arg.coerce_to_boolean(env)
+ # TODO: unwrap more coercion nodes?
+ return BoolBinopResultNode(arg, dst_type, env)
+
+ def nogil_check(self, env):
+ # let's leave all errors to BoolBinopNode
+ pass
+
+ def generate_operand_test(self, code):
+ # Generate code to test the truth of the first operand.
+ if self.arg.type.is_pyobject:
+ test_result = code.funcstate.allocate_temp(
+ PyrexTypes.c_bint_type, manage_ref=False)
+ code.putln(
+ "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
+ test_result,
+ self.arg.py_result(),
+ code.error_goto_if_neg(test_result, self.pos)))
+ else:
+ test_result = self.arg.result()
+ return (test_result, self.arg.type.is_pyobject)
+
def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
-
- # x => x
- # x and ... or ... => next 'and' / 'or'
- # False ... or x => next 'or'
- # True and x => next 'and'
- # True or x => True (operand)
-
- self.arg.generate_evaluation_code(code)
- if and_label or or_label:
- test_result, uses_temp = self.generate_operand_test(code)
+ code.mark_pos(self.pos)
+
+ # x => x
+ # x and ... or ... => next 'and' / 'or'
+ # False ... or x => next 'or'
+ # True and x => next 'and'
+ # True or x => True (operand)
+
+ self.arg.generate_evaluation_code(code)
+ if and_label or or_label:
+ test_result, uses_temp = self.generate_operand_test(code)
if uses_temp and (and_label and or_label):
# cannot become final result => free early
# disposal: uses_temp and (and_label and or_label)
self.arg.generate_disposal_code(code)
- sense = '!' if or_label else ''
- code.putln("if (%s%s) {" % (sense, test_result))
- if uses_temp:
- code.funcstate.release_temp(test_result)
+ sense = '!' if or_label else ''
+ code.putln("if (%s%s) {" % (sense, test_result))
+ if uses_temp:
+ code.funcstate.release_temp(test_result)
if not uses_temp or not (and_label and or_label):
# disposal: (not uses_temp) or {not (and_label and or_label) [if]}
self.arg.generate_disposal_code(code)
-
+
if or_label and or_label != fall_through:
- # value is false => short-circuit to next 'or'
- code.put_goto(or_label)
- if and_label:
- # value is true => go to next 'and'
+ # value is false => short-circuit to next 'or'
+ code.put_goto(or_label)
+ if and_label:
+ # value is true => go to next 'and'
if or_label:
- code.putln("} else {")
+ code.putln("} else {")
if not uses_temp:
# disposal: (not uses_temp) and {(and_label and or_label) [else]}
self.arg.generate_disposal_code(code)
if and_label != fall_through:
code.put_goto(and_label)
-
- if not and_label or not or_label:
- # if no next 'and' or 'or', we provide the result
+
+ if not and_label or not or_label:
+ # if no next 'and' or 'or', we provide the result
if and_label or or_label:
code.putln("} else {")
- self.value.generate_evaluation_code(code)
- self.value.make_owned_reference(code)
+ self.value.generate_evaluation_code(code)
+ self.value.make_owned_reference(code)
code.putln("%s = %s;" % (final_result_temp, self.value.result_as(final_result_type)))
- self.value.generate_post_assignment_code(code)
+ self.value.generate_post_assignment_code(code)
# disposal: {not (and_label and or_label) [else]}
- self.arg.generate_disposal_code(code)
- self.value.free_temps(code)
+ self.arg.generate_disposal_code(code)
+ self.value.free_temps(code)
if end_label != fall_through:
- code.put_goto(end_label)
-
- if and_label or or_label:
- code.putln("}")
- self.arg.free_temps(code)
-
-
-class CondExprNode(ExprNode):
- # Short-circuiting conditional expression.
- #
- # test ExprNode
- # true_val ExprNode
- # false_val ExprNode
-
- true_val = None
- false_val = None
+ code.put_goto(end_label)
+
+ if and_label or or_label:
+ code.putln("}")
+ self.arg.free_temps(code)
+
+
+class CondExprNode(ExprNode):
+ # Short-circuiting conditional expression.
+ #
+ # test ExprNode
+ # true_val ExprNode
+ # false_val ExprNode
+
+ true_val = None
+ false_val = None
is_temp = True
-
- subexprs = ['test', 'true_val', 'false_val']
-
- def type_dependencies(self, env):
- return self.true_val.type_dependencies(env) + self.false_val.type_dependencies(env)
-
- def infer_type(self, env):
- return PyrexTypes.independent_spanning_type(
- self.true_val.infer_type(env),
- self.false_val.infer_type(env))
-
- def calculate_constant_result(self):
- if self.test.constant_result:
- self.constant_result = self.true_val.constant_result
- else:
- self.constant_result = self.false_val.constant_result
-
- def is_ephemeral(self):
- return self.true_val.is_ephemeral() or self.false_val.is_ephemeral()
-
- def analyse_types(self, env):
- self.test = self.test.analyse_types(env).coerce_to_boolean(env)
- self.true_val = self.true_val.analyse_types(env)
- self.false_val = self.false_val.analyse_types(env)
- return self.analyse_result_type(env)
-
- def analyse_result_type(self, env):
- self.type = PyrexTypes.independent_spanning_type(
- self.true_val.type, self.false_val.type)
+
+ subexprs = ['test', 'true_val', 'false_val']
+
+ def type_dependencies(self, env):
+ return self.true_val.type_dependencies(env) + self.false_val.type_dependencies(env)
+
+ def infer_type(self, env):
+ return PyrexTypes.independent_spanning_type(
+ self.true_val.infer_type(env),
+ self.false_val.infer_type(env))
+
+ def calculate_constant_result(self):
+ if self.test.constant_result:
+ self.constant_result = self.true_val.constant_result
+ else:
+ self.constant_result = self.false_val.constant_result
+
+ def is_ephemeral(self):
+ return self.true_val.is_ephemeral() or self.false_val.is_ephemeral()
+
+ def analyse_types(self, env):
+ self.test = self.test.analyse_types(env).coerce_to_boolean(env)
+ self.true_val = self.true_val.analyse_types(env)
+ self.false_val = self.false_val.analyse_types(env)
+ return self.analyse_result_type(env)
+
+ def analyse_result_type(self, env):
+ self.type = PyrexTypes.independent_spanning_type(
+ self.true_val.type, self.false_val.type)
if self.type.is_reference:
self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
- error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
- if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
- self.true_val = self.true_val.coerce_to(self.type, env)
- self.false_val = self.false_val.coerce_to(self.type, env)
+ if self.type.is_pyobject:
+ self.result_ctype = py_object_type
+ elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
+ error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
+ if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
+ self.true_val = self.true_val.coerce_to(self.type, env)
+ self.false_val = self.false_val.coerce_to(self.type, env)
if self.type.is_error:
- self.type_error()
- return self
-
+ self.type_error()
+ return self
+
def coerce_to_integer(self, env):
self.true_val = self.true_val.coerce_to_integer(env)
self.false_val = self.false_val.coerce_to_integer(env)
self.result_ctype = None
return self.analyse_result_type(env)
- def coerce_to(self, dst_type, env):
- self.true_val = self.true_val.coerce_to(dst_type, env)
- self.false_val = self.false_val.coerce_to(dst_type, env)
- self.result_ctype = None
- return self.analyse_result_type(env)
-
- def type_error(self):
- if not (self.true_val.type.is_error or self.false_val.type.is_error):
- error(self.pos, "Incompatible types in conditional expression (%s; %s)" %
- (self.true_val.type, self.false_val.type))
- self.type = PyrexTypes.error_type
-
- def check_const(self):
- return (self.test.check_const()
- and self.true_val.check_const()
- and self.false_val.check_const())
-
- def generate_evaluation_code(self, code):
- # Because subexprs may not be evaluated we can use a more optimal
- # subexpr allocation strategy than the default, so override evaluation_code.
-
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- self.test.generate_evaluation_code(code)
- code.putln("if (%s) {" % self.test.result())
- self.eval_and_get(code, self.true_val)
- code.putln("} else {")
- self.eval_and_get(code, self.false_val)
- code.putln("}")
- self.test.generate_disposal_code(code)
- self.test.free_temps(code)
-
- def eval_and_get(self, code, expr):
- expr.generate_evaluation_code(code)
- if self.type.is_memoryviewslice:
- expr.make_owned_memoryviewslice(code)
- else:
- expr.make_owned_reference(code)
- code.putln('%s = %s;' % (self.result(), expr.result_as(self.ctype())))
- expr.generate_post_assignment_code(code)
- expr.free_temps(code)
-
- def generate_subexpr_disposal_code(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
-
- def free_subexpr_temps(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
-
-
-richcmp_constants = {
- "<" : "Py_LT",
- "<=": "Py_LE",
- "==": "Py_EQ",
- "!=": "Py_NE",
- "<>": "Py_NE",
- ">" : "Py_GT",
- ">=": "Py_GE",
- # the following are faked by special compare functions
- "in" : "Py_EQ",
- "not_in": "Py_NE",
-}
-
-class CmpNode(object):
- # Mixin class containing code common to PrimaryCmpNodes
- # and CascadedCmpNodes.
-
- special_bool_cmp_function = None
- special_bool_cmp_utility_code = None
-
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
-
- def calculate_cascaded_constant_result(self, operand1_result):
- func = compile_time_binary_operators[self.operator]
- operand2_result = self.operand2.constant_result
+ def coerce_to(self, dst_type, env):
+ self.true_val = self.true_val.coerce_to(dst_type, env)
+ self.false_val = self.false_val.coerce_to(dst_type, env)
+ self.result_ctype = None
+ return self.analyse_result_type(env)
+
+ def type_error(self):
+ if not (self.true_val.type.is_error or self.false_val.type.is_error):
+ error(self.pos, "Incompatible types in conditional expression (%s; %s)" %
+ (self.true_val.type, self.false_val.type))
+ self.type = PyrexTypes.error_type
+
+ def check_const(self):
+ return (self.test.check_const()
+ and self.true_val.check_const()
+ and self.false_val.check_const())
+
+ def generate_evaluation_code(self, code):
+ # Because subexprs may not be evaluated we can use a more optimal
+ # subexpr allocation strategy than the default, so override evaluation_code.
+
+ code.mark_pos(self.pos)
+ self.allocate_temp_result(code)
+ self.test.generate_evaluation_code(code)
+ code.putln("if (%s) {" % self.test.result())
+ self.eval_and_get(code, self.true_val)
+ code.putln("} else {")
+ self.eval_and_get(code, self.false_val)
+ code.putln("}")
+ self.test.generate_disposal_code(code)
+ self.test.free_temps(code)
+
+ def eval_and_get(self, code, expr):
+ expr.generate_evaluation_code(code)
+ if self.type.is_memoryviewslice:
+ expr.make_owned_memoryviewslice(code)
+ else:
+ expr.make_owned_reference(code)
+ code.putln('%s = %s;' % (self.result(), expr.result_as(self.ctype())))
+ expr.generate_post_assignment_code(code)
+ expr.free_temps(code)
+
+ def generate_subexpr_disposal_code(self, code):
+ pass # done explicitly above (cleanup must separately happen within the if/else blocks)
+
+ def free_subexpr_temps(self, code):
+ pass # done explicitly above (cleanup must separately happen within the if/else blocks)
+
+
+richcmp_constants = {
+ "<" : "Py_LT",
+ "<=": "Py_LE",
+ "==": "Py_EQ",
+ "!=": "Py_NE",
+ "<>": "Py_NE",
+ ">" : "Py_GT",
+ ">=": "Py_GE",
+ # the following are faked by special compare functions
+ "in" : "Py_EQ",
+ "not_in": "Py_NE",
+}
+
+class CmpNode(object):
+ # Mixin class containing code common to PrimaryCmpNodes
+ # and CascadedCmpNodes.
+
+ special_bool_cmp_function = None
+ special_bool_cmp_utility_code = None
+
+ def infer_type(self, env):
+ # TODO: Actually implement this (after merging with -unstable).
+ return py_object_type
+
+ def calculate_cascaded_constant_result(self, operand1_result):
+ func = compile_time_binary_operators[self.operator]
+ operand2_result = self.operand2.constant_result
if (isinstance(operand1_result, any_string_type) and
isinstance(operand2_result, any_string_type) and
- type(operand1_result) != type(operand2_result)):
- # string comparison of different types isn't portable
- return
-
- if self.operator in ('in', 'not_in'):
- if isinstance(self.operand2, (ListNode, TupleNode, SetNode)):
- if not self.operand2.args:
- self.constant_result = self.operator == 'not_in'
- return
- elif isinstance(self.operand2, ListNode) and not self.cascade:
- # tuples are more efficient to store than lists
- self.operand2 = self.operand2.as_tuple()
- elif isinstance(self.operand2, DictNode):
- if not self.operand2.key_value_pairs:
- self.constant_result = self.operator == 'not_in'
- return
-
- self.constant_result = func(operand1_result, operand2_result)
-
- def cascaded_compile_time_value(self, operand1, denv):
- func = get_compile_time_binop(self)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- result = func(operand1, operand2)
+ type(operand1_result) != type(operand2_result)):
+ # string comparison of different types isn't portable
+ return
+
+ if self.operator in ('in', 'not_in'):
+ if isinstance(self.operand2, (ListNode, TupleNode, SetNode)):
+ if not self.operand2.args:
+ self.constant_result = self.operator == 'not_in'
+ return
+ elif isinstance(self.operand2, ListNode) and not self.cascade:
+ # tuples are more efficient to store than lists
+ self.operand2 = self.operand2.as_tuple()
+ elif isinstance(self.operand2, DictNode):
+ if not self.operand2.key_value_pairs:
+ self.constant_result = self.operator == 'not_in'
+ return
+
+ self.constant_result = func(operand1_result, operand2_result)
+
+ def cascaded_compile_time_value(self, operand1, denv):
+ func = get_compile_time_binop(self)
+ operand2 = self.operand2.compile_time_value(denv)
+ try:
+ result = func(operand1, operand2)
except Exception as e:
- self.compile_time_value_error(e)
- result = None
- if result:
- cascade = self.cascade
- if cascade:
- result = result and cascade.cascaded_compile_time_value(operand2, denv)
- return result
-
- def is_cpp_comparison(self):
- return self.operand1.type.is_cpp_class or self.operand2.type.is_cpp_class
-
- def find_common_int_type(self, env, op, operand1, operand2):
- # type1 != type2 and at least one of the types is not a C int
- type1 = operand1.type
- type2 = operand2.type
- type1_can_be_int = False
- type2_can_be_int = False
-
- if operand1.is_string_literal and operand1.can_coerce_to_char_literal():
- type1_can_be_int = True
- if operand2.is_string_literal and operand2.can_coerce_to_char_literal():
- type2_can_be_int = True
-
- if type1.is_int:
- if type2_can_be_int:
- return type1
- elif type2.is_int:
- if type1_can_be_int:
- return type2
- elif type1_can_be_int:
- if type2_can_be_int:
- if Builtin.unicode_type in (type1, type2):
- return PyrexTypes.c_py_ucs4_type
- else:
- return PyrexTypes.c_uchar_type
-
- return None
-
- def find_common_type(self, env, op, operand1, common_type=None):
- operand2 = self.operand2
- type1 = operand1.type
- type2 = operand2.type
-
- new_common_type = None
-
- # catch general errors
+ self.compile_time_value_error(e)
+ result = None
+ if result:
+ cascade = self.cascade
+ if cascade:
+ result = result and cascade.cascaded_compile_time_value(operand2, denv)
+ return result
+
+ def is_cpp_comparison(self):
+ return self.operand1.type.is_cpp_class or self.operand2.type.is_cpp_class
+
+ def find_common_int_type(self, env, op, operand1, operand2):
+ # type1 != type2 and at least one of the types is not a C int
+ type1 = operand1.type
+ type2 = operand2.type
+ type1_can_be_int = False
+ type2_can_be_int = False
+
+ if operand1.is_string_literal and operand1.can_coerce_to_char_literal():
+ type1_can_be_int = True
+ if operand2.is_string_literal and operand2.can_coerce_to_char_literal():
+ type2_can_be_int = True
+
+ if type1.is_int:
+ if type2_can_be_int:
+ return type1
+ elif type2.is_int:
+ if type1_can_be_int:
+ return type2
+ elif type1_can_be_int:
+ if type2_can_be_int:
+ if Builtin.unicode_type in (type1, type2):
+ return PyrexTypes.c_py_ucs4_type
+ else:
+ return PyrexTypes.c_uchar_type
+
+ return None
+
+ def find_common_type(self, env, op, operand1, common_type=None):
+ operand2 = self.operand2
+ type1 = operand1.type
+ type2 = operand2.type
+
+ new_common_type = None
+
+ # catch general errors
if (type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or
type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type))):
- error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
- new_common_type = error_type
-
- # try to use numeric comparisons where possible
- elif type1.is_complex or type2.is_complex:
+ error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
+ new_common_type = error_type
+
+ # try to use numeric comparisons where possible
+ elif type1.is_complex or type2.is_complex:
if (op not in ('==', '!=')
and (type1.is_complex or type1.is_numeric)
and (type2.is_complex or type2.is_numeric)):
- error(self.pos, "complex types are unordered")
- new_common_type = error_type
- elif type1.is_pyobject:
+ error(self.pos, "complex types are unordered")
+ new_common_type = error_type
+ elif type1.is_pyobject:
new_common_type = Builtin.complex_type if type1.subtype_of(Builtin.complex_type) else py_object_type
- elif type2.is_pyobject:
+ elif type2.is_pyobject:
new_common_type = Builtin.complex_type if type2.subtype_of(Builtin.complex_type) else py_object_type
- else:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif type1.is_numeric and type2.is_numeric:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif common_type is None or not common_type.is_pyobject:
- new_common_type = self.find_common_int_type(env, op, operand1, operand2)
-
- if new_common_type is None:
- # fall back to generic type compatibility tests
+ else:
+ new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
+ elif type1.is_numeric and type2.is_numeric:
+ new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
+ elif common_type is None or not common_type.is_pyobject:
+ new_common_type = self.find_common_int_type(env, op, operand1, operand2)
+
+ if new_common_type is None:
+ # fall back to generic type compatibility tests
if type1.is_ctuple or type2.is_ctuple:
new_common_type = py_object_type
elif type1 == type2:
- new_common_type = type1
- elif type1.is_pyobject or type2.is_pyobject:
- if type2.is_numeric or type2.is_string:
- if operand2.check_for_coercion_error(type1, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif type1.is_numeric or type1.is_string:
- if operand1.check_for_coercion_error(type2, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif py_object_type.assignable_from(type1) and py_object_type.assignable_from(type2):
- new_common_type = py_object_type
- else:
- # one Python type and one non-Python type, not assignable
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
- elif type1.assignable_from(type2):
- new_common_type = type1
- elif type2.assignable_from(type1):
- new_common_type = type2
- else:
- # C types that we couldn't handle up to here are an error
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
-
- if new_common_type.is_string and (isinstance(operand1, BytesNode) or
- isinstance(operand2, BytesNode)):
- # special case when comparing char* to bytes literal: must
- # compare string values!
- new_common_type = bytes_type
-
- # recursively merge types
- if common_type is None or new_common_type.is_error:
- common_type = new_common_type
- else:
- # we could do a lot better by splitting the comparison
- # into a non-Python part and a Python part, but this is
- # safer for now
- common_type = PyrexTypes.spanning_type(common_type, new_common_type)
-
- if self.cascade:
- common_type = self.cascade.find_common_type(env, self.operator, operand2, common_type)
-
- return common_type
-
- def invalid_types_error(self, operand1, op, operand2):
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (op, operand1.type, operand2.type))
-
- def is_python_comparison(self):
- return (not self.is_ptr_contains()
- and not self.is_c_string_contains()
- and (self.has_python_operands()
- or (self.cascade and self.cascade.is_python_comparison())
- or self.operator in ('in', 'not_in')))
-
- def coerce_operands_to(self, dst_type, env):
- operand2 = self.operand2
- if operand2.type != dst_type:
- self.operand2 = operand2.coerce_to(dst_type, env)
- if self.cascade:
- self.cascade.coerce_operands_to(dst_type, env)
-
- def is_python_result(self):
- return ((self.has_python_operands() and
- self.special_bool_cmp_function is None and
- self.operator not in ('is', 'is_not', 'in', 'not_in') and
- not self.is_c_string_contains() and
- not self.is_ptr_contains())
- or (self.cascade and self.cascade.is_python_result()))
-
- def is_c_string_contains(self):
- return self.operator in ('in', 'not_in') and \
- ((self.operand1.type.is_int
- and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
- (self.operand1.type.is_unicode_char
- and self.operand2.type is unicode_type))
-
- def is_ptr_contains(self):
- if self.operator in ('in', 'not_in'):
- container_type = self.operand2.type
- return (container_type.is_ptr or container_type.is_array) \
- and not container_type.is_string
-
- def find_special_bool_compare_function(self, env, operand1, result_is_bool=False):
- # note: currently operand1 must get coerced to a Python object if we succeed here!
- if self.operator in ('==', '!='):
- type1, type2 = operand1.type, self.operand2.type
- if result_is_bool or (type1.is_builtin_type and type2.is_builtin_type):
- if type1 is Builtin.unicode_type or type2 is Builtin.unicode_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.bytes_type or type2 is Builtin.bytes_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("BytesEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyBytes_Equals"
- return True
- elif type1 is Builtin.basestring_type or type2 is Builtin.basestring_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.str_type or type2 is Builtin.str_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("StrEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyString_Equals"
- return True
- elif self.operator in ('in', 'not_in'):
- if self.operand2.type is Builtin.dict_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
+ new_common_type = type1
+ elif type1.is_pyobject or type2.is_pyobject:
+ if type2.is_numeric or type2.is_string:
+ if operand2.check_for_coercion_error(type1, env):
+ new_common_type = error_type
+ else:
+ new_common_type = py_object_type
+ elif type1.is_numeric or type1.is_string:
+ if operand1.check_for_coercion_error(type2, env):
+ new_common_type = error_type
+ else:
+ new_common_type = py_object_type
+ elif py_object_type.assignable_from(type1) and py_object_type.assignable_from(type2):
+ new_common_type = py_object_type
+ else:
+ # one Python type and one non-Python type, not assignable
+ self.invalid_types_error(operand1, op, operand2)
+ new_common_type = error_type
+ elif type1.assignable_from(type2):
+ new_common_type = type1
+ elif type2.assignable_from(type1):
+ new_common_type = type2
+ else:
+ # C types that we couldn't handle up to here are an error
+ self.invalid_types_error(operand1, op, operand2)
+ new_common_type = error_type
+
+ if new_common_type.is_string and (isinstance(operand1, BytesNode) or
+ isinstance(operand2, BytesNode)):
+ # special case when comparing char* to bytes literal: must
+ # compare string values!
+ new_common_type = bytes_type
+
+ # recursively merge types
+ if common_type is None or new_common_type.is_error:
+ common_type = new_common_type
+ else:
+ # we could do a lot better by splitting the comparison
+ # into a non-Python part and a Python part, but this is
+ # safer for now
+ common_type = PyrexTypes.spanning_type(common_type, new_common_type)
+
+ if self.cascade:
+ common_type = self.cascade.find_common_type(env, self.operator, operand2, common_type)
+
+ return common_type
+
+ def invalid_types_error(self, operand1, op, operand2):
+ error(self.pos, "Invalid types for '%s' (%s, %s)" %
+ (op, operand1.type, operand2.type))
+
+ def is_python_comparison(self):
+ return (not self.is_ptr_contains()
+ and not self.is_c_string_contains()
+ and (self.has_python_operands()
+ or (self.cascade and self.cascade.is_python_comparison())
+ or self.operator in ('in', 'not_in')))
+
+ def coerce_operands_to(self, dst_type, env):
+ operand2 = self.operand2
+ if operand2.type != dst_type:
+ self.operand2 = operand2.coerce_to(dst_type, env)
+ if self.cascade:
+ self.cascade.coerce_operands_to(dst_type, env)
+
+ def is_python_result(self):
+ return ((self.has_python_operands() and
+ self.special_bool_cmp_function is None and
+ self.operator not in ('is', 'is_not', 'in', 'not_in') and
+ not self.is_c_string_contains() and
+ not self.is_ptr_contains())
+ or (self.cascade and self.cascade.is_python_result()))
+
+ def is_c_string_contains(self):
+ return self.operator in ('in', 'not_in') and \
+ ((self.operand1.type.is_int
+ and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
+ (self.operand1.type.is_unicode_char
+ and self.operand2.type is unicode_type))
+
+ def is_ptr_contains(self):
+ if self.operator in ('in', 'not_in'):
+ container_type = self.operand2.type
+ return (container_type.is_ptr or container_type.is_array) \
+ and not container_type.is_string
+
+ def find_special_bool_compare_function(self, env, operand1, result_is_bool=False):
+ # note: currently operand1 must get coerced to a Python object if we succeed here!
+ if self.operator in ('==', '!='):
+ type1, type2 = operand1.type, self.operand2.type
+ if result_is_bool or (type1.is_builtin_type and type2.is_builtin_type):
+ if type1 is Builtin.unicode_type or type2 is Builtin.unicode_type:
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
+ self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
+ return True
+ elif type1 is Builtin.bytes_type or type2 is Builtin.bytes_type:
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("BytesEquals", "StringTools.c")
+ self.special_bool_cmp_function = "__Pyx_PyBytes_Equals"
+ return True
+ elif type1 is Builtin.basestring_type or type2 is Builtin.basestring_type:
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
+ self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
+ return True
+ elif type1 is Builtin.str_type or type2 is Builtin.str_type:
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("StrEquals", "StringTools.c")
+ self.special_bool_cmp_function = "__Pyx_PyString_Equals"
+ return True
+ elif self.operator in ('in', 'not_in'):
+ if self.operand2.type is Builtin.dict_type:
+ self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
self.special_bool_cmp_function = "__Pyx_PyDict_ContainsTF"
- return True
+ return True
elif self.operand2.type is Builtin.set_type:
self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySetContains", "ObjectHandling.c")
self.special_bool_cmp_function = "__Pyx_PySet_ContainsTF"
return True
- elif self.operand2.type is Builtin.unicode_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
+ elif self.operand2.type is Builtin.unicode_type:
+ self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
self.special_bool_cmp_function = "__Pyx_PyUnicode_ContainsTF"
- return True
- else:
- if not self.operand2.type.is_pyobject:
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySequenceContains", "ObjectHandling.c")
+ return True
+ else:
+ if not self.operand2.type.is_pyobject:
+ self.operand2 = self.operand2.coerce_to_pyobject(env)
+ self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySequenceContains", "ObjectHandling.c")
self.special_bool_cmp_function = "__Pyx_PySequence_ContainsTF"
- return True
- return False
-
- def generate_operation_code(self, code, result_code,
- operand1, op , operand2):
- if self.type.is_pyobject:
- error_clause = code.error_goto_if_null
- got_ref = "__Pyx_XGOTREF(%s); " % result_code
- if self.special_bool_cmp_function:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyBoolOrNullFromLong", "ObjectHandling.c"))
- coerce_result = "__Pyx_PyBoolOrNull_FromLong"
- else:
- coerce_result = "__Pyx_PyBool_FromLong"
- else:
- error_clause = code.error_goto_if_neg
- got_ref = ""
- coerce_result = ""
-
- if self.special_bool_cmp_function:
- if operand1.type.is_pyobject:
- result1 = operand1.py_result()
- else:
- result1 = operand1.result()
- if operand2.type.is_pyobject:
- result2 = operand2.py_result()
- else:
- result2 = operand2.result()
- if self.special_bool_cmp_utility_code:
- code.globalstate.use_utility_code(self.special_bool_cmp_utility_code)
- code.putln(
- "%s = %s(%s(%s, %s, %s)); %s%s" % (
- result_code,
- coerce_result,
- self.special_bool_cmp_function,
- result1, result2, richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
-
- elif operand1.type.is_pyobject and op not in ('is', 'is_not'):
- assert op not in ('in', 'not_in'), op
- code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s%s" % (
- result_code,
- operand1.py_result(),
- operand2.py_result(),
- richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
-
- elif operand1.type.is_complex:
- code.putln("%s = %s(%s%s(%s, %s));" % (
- result_code,
- coerce_result,
- op == "!=" and "!" or "",
- operand1.type.unary_op('eq'),
- operand1.result(),
- operand2.result()))
-
- else:
- type1 = operand1.type
- type2 = operand2.type
- if (type1.is_extension_type or type2.is_extension_type) \
- and not type1.same_as(type2):
- common_type = py_object_type
- elif type1.is_numeric:
- common_type = PyrexTypes.widest_numeric_type(type1, type2)
- else:
- common_type = type1
- code1 = operand1.result_as(common_type)
- code2 = operand2.result_as(common_type)
+ return True
+ return False
+
+ def generate_operation_code(self, code, result_code,
+ operand1, op , operand2):
+ if self.type.is_pyobject:
+ error_clause = code.error_goto_if_null
+ got_ref = "__Pyx_XGOTREF(%s); " % result_code
+ if self.special_bool_cmp_function:
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("PyBoolOrNullFromLong", "ObjectHandling.c"))
+ coerce_result = "__Pyx_PyBoolOrNull_FromLong"
+ else:
+ coerce_result = "__Pyx_PyBool_FromLong"
+ else:
+ error_clause = code.error_goto_if_neg
+ got_ref = ""
+ coerce_result = ""
+
+ if self.special_bool_cmp_function:
+ if operand1.type.is_pyobject:
+ result1 = operand1.py_result()
+ else:
+ result1 = operand1.result()
+ if operand2.type.is_pyobject:
+ result2 = operand2.py_result()
+ else:
+ result2 = operand2.result()
+ if self.special_bool_cmp_utility_code:
+ code.globalstate.use_utility_code(self.special_bool_cmp_utility_code)
+ code.putln(
+ "%s = %s(%s(%s, %s, %s)); %s%s" % (
+ result_code,
+ coerce_result,
+ self.special_bool_cmp_function,
+ result1, result2, richcmp_constants[op],
+ got_ref,
+ error_clause(result_code, self.pos)))
+
+ elif operand1.type.is_pyobject and op not in ('is', 'is_not'):
+ assert op not in ('in', 'not_in'), op
+ code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s%s" % (
+ result_code,
+ operand1.py_result(),
+ operand2.py_result(),
+ richcmp_constants[op],
+ got_ref,
+ error_clause(result_code, self.pos)))
+
+ elif operand1.type.is_complex:
+ code.putln("%s = %s(%s%s(%s, %s));" % (
+ result_code,
+ coerce_result,
+ op == "!=" and "!" or "",
+ operand1.type.unary_op('eq'),
+ operand1.result(),
+ operand2.result()))
+
+ else:
+ type1 = operand1.type
+ type2 = operand2.type
+ if (type1.is_extension_type or type2.is_extension_type) \
+ and not type1.same_as(type2):
+ common_type = py_object_type
+ elif type1.is_numeric:
+ common_type = PyrexTypes.widest_numeric_type(type1, type2)
+ else:
+ common_type = type1
+ code1 = operand1.result_as(common_type)
+ code2 = operand2.result_as(common_type)
statement = "%s = %s(%s %s %s);" % (
- result_code,
- coerce_result,
- code1,
- self.c_operator(op),
+ result_code,
+ coerce_result,
+ code1,
+ self.c_operator(op),
code2)
if self.is_cpp_comparison() and self.exception_check == '+':
translate_cpp_exception(
@@ -12517,36 +12517,36 @@ class CmpNode(object):
self.in_nogil_context)
else:
code.putln(statement)
-
- def c_operator(self, op):
- if op == 'is':
- return "=="
- elif op == 'is_not':
- return "!="
- else:
- return op
-
-class PrimaryCmpNode(ExprNode, CmpNode):
- # Non-cascaded comparison or first comparison of
- # a cascaded sequence.
- #
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- # cascade CascadedCmpNode
-
- # We don't use the subexprs mechanism, because
- # things here are too complicated for it to handle.
- # Instead, we override all the framework methods
- # which use it.
-
- child_attrs = ['operand1', 'operand2', 'coerced_operand2', 'cascade']
-
- cascade = None
- coerced_operand2 = None
- is_memslice_nonecheck = False
-
- def infer_type(self, env):
+
+ def c_operator(self, op):
+ if op == 'is':
+ return "=="
+ elif op == 'is_not':
+ return "!="
+ else:
+ return op
+
+class PrimaryCmpNode(ExprNode, CmpNode):
+ # Non-cascaded comparison or first comparison of
+ # a cascaded sequence.
+ #
+ # operator string
+ # operand1 ExprNode
+ # operand2 ExprNode
+ # cascade CascadedCmpNode
+
+ # We don't use the subexprs mechanism, because
+ # things here are too complicated for it to handle.
+ # Instead, we override all the framework methods
+ # which use it.
+
+ child_attrs = ['operand1', 'operand2', 'coerced_operand2', 'cascade']
+
+ cascade = None
+ coerced_operand2 = None
+ is_memslice_nonecheck = False
+
+ def infer_type(self, env):
type1 = self.operand1.infer_type(env)
type2 = self.operand2.infer_type(env)
@@ -12555,28 +12555,28 @@ class PrimaryCmpNode(ExprNode, CmpNode):
return PythranExpr(pythran_binop_type(self.operator, type1, type2))
# TODO: implement this for other types.
- return py_object_type
-
- def type_dependencies(self, env):
- return ()
-
- def calculate_constant_result(self):
- assert not self.cascade
- self.calculate_cascaded_constant_result(self.operand1.constant_result)
-
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- return self.cascaded_compile_time_value(operand1, denv)
-
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- if self.is_cpp_comparison():
- self.analyse_cpp_comparison(env)
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for cpp types.")
- return self
-
+ return py_object_type
+
+ def type_dependencies(self, env):
+ return ()
+
+ def calculate_constant_result(self):
+ assert not self.cascade
+ self.calculate_cascaded_constant_result(self.operand1.constant_result)
+
+ def compile_time_value(self, denv):
+ operand1 = self.operand1.compile_time_value(denv)
+ return self.cascaded_compile_time_value(operand1, denv)
+
+ def analyse_types(self, env):
+ self.operand1 = self.operand1.analyse_types(env)
+ self.operand2 = self.operand2.analyse_types(env)
+ if self.is_cpp_comparison():
+ self.analyse_cpp_comparison(env)
+ if self.cascade:
+ error(self.pos, "Cascading comparison not yet supported for cpp types.")
+ return self
+
type1 = self.operand1.type
type2 = self.operand2.type
if is_pythran_expr(type1) or is_pythran_expr(type2):
@@ -12585,171 +12585,171 @@ class PrimaryCmpNode(ExprNode, CmpNode):
self.is_pycmp = False
return self
- if self.analyse_memoryviewslice_comparison(env):
- return self
-
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
-
- if self.operator in ('in', 'not_in'):
- if self.is_c_string_contains():
- self.is_pycmp = False
- common_type = None
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for 'int_val in string'.")
- return self
- if self.operand2.type is unicode_type:
- env.use_utility_code(UtilityCode.load_cached("PyUCS4InUnicode", "StringTools.c"))
- else:
- if self.operand1.type is PyrexTypes.c_uchar_type:
- self.operand1 = self.operand1.coerce_to(PyrexTypes.c_char_type, env)
- if self.operand2.type is not bytes_type:
- self.operand2 = self.operand2.coerce_to(bytes_type, env)
- env.use_utility_code(UtilityCode.load_cached("BytesContains", "StringTools.c"))
- self.operand2 = self.operand2.as_none_safe_node(
- "argument of type 'NoneType' is not iterable")
- elif self.is_ptr_contains():
- if self.cascade:
- error(self.pos, "Cascading comparison not supported for 'val in sliced pointer'.")
- self.type = PyrexTypes.c_bint_type
- # Will be transformed by IterationTransform
- return self
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = py_object_type
- self.is_pycmp = True
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = self.find_common_type(env, self.operator, self.operand1)
- self.is_pycmp = common_type.is_pyobject
-
- if common_type is not None and not common_type.is_error:
- if self.operand1.type != common_type:
- self.operand1 = self.operand1.coerce_to(common_type, env)
- self.coerce_operands_to(common_type, env)
-
- if self.cascade:
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
- operand2 = self.cascade.optimise_comparison(self.operand2, env)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- if self.is_python_result():
- self.type = PyrexTypes.py_object_type
- else:
- self.type = PyrexTypes.c_bint_type
- cdr = self.cascade
- while cdr:
- cdr.type = self.type
- cdr = cdr.cascade
- if self.is_pycmp or self.cascade or self.special_bool_cmp_function:
- # 1) owned reference, 2) reused value, 3) potential function error return value
- self.is_temp = 1
- return self
-
- def analyse_cpp_comparison(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
+ if self.analyse_memoryviewslice_comparison(env):
+ return self
+
+ if self.cascade:
+ self.cascade = self.cascade.analyse_types(env)
+
+ if self.operator in ('in', 'not_in'):
+ if self.is_c_string_contains():
+ self.is_pycmp = False
+ common_type = None
+ if self.cascade:
+ error(self.pos, "Cascading comparison not yet supported for 'int_val in string'.")
+ return self
+ if self.operand2.type is unicode_type:
+ env.use_utility_code(UtilityCode.load_cached("PyUCS4InUnicode", "StringTools.c"))
+ else:
+ if self.operand1.type is PyrexTypes.c_uchar_type:
+ self.operand1 = self.operand1.coerce_to(PyrexTypes.c_char_type, env)
+ if self.operand2.type is not bytes_type:
+ self.operand2 = self.operand2.coerce_to(bytes_type, env)
+ env.use_utility_code(UtilityCode.load_cached("BytesContains", "StringTools.c"))
+ self.operand2 = self.operand2.as_none_safe_node(
+ "argument of type 'NoneType' is not iterable")
+ elif self.is_ptr_contains():
+ if self.cascade:
+ error(self.pos, "Cascading comparison not supported for 'val in sliced pointer'.")
+ self.type = PyrexTypes.c_bint_type
+ # Will be transformed by IterationTransform
+ return self
+ elif self.find_special_bool_compare_function(env, self.operand1):
+ if not self.operand1.type.is_pyobject:
+ self.operand1 = self.operand1.coerce_to_pyobject(env)
+ common_type = None # if coercion needed, the method call above has already done it
+ self.is_pycmp = False # result is bint
+ else:
+ common_type = py_object_type
+ self.is_pycmp = True
+ elif self.find_special_bool_compare_function(env, self.operand1):
+ if not self.operand1.type.is_pyobject:
+ self.operand1 = self.operand1.coerce_to_pyobject(env)
+ common_type = None # if coercion needed, the method call above has already done it
+ self.is_pycmp = False # result is bint
+ else:
+ common_type = self.find_common_type(env, self.operator, self.operand1)
+ self.is_pycmp = common_type.is_pyobject
+
+ if common_type is not None and not common_type.is_error:
+ if self.operand1.type != common_type:
+ self.operand1 = self.operand1.coerce_to(common_type, env)
+ self.coerce_operands_to(common_type, env)
+
+ if self.cascade:
+ self.operand2 = self.operand2.coerce_to_simple(env)
+ self.cascade.coerce_cascaded_operands_to_temp(env)
+ operand2 = self.cascade.optimise_comparison(self.operand2, env)
+ if operand2 is not self.operand2:
+ self.coerced_operand2 = operand2
+ if self.is_python_result():
+ self.type = PyrexTypes.py_object_type
+ else:
+ self.type = PyrexTypes.c_bint_type
+ cdr = self.cascade
+ while cdr:
+ cdr.type = self.type
+ cdr = cdr.cascade
+ if self.is_pycmp or self.cascade or self.special_bool_cmp_function:
+ # 1) owned reference, 2) reused value, 3) potential function error return value
+ self.is_temp = 1
+ return self
+
+ def analyse_cpp_comparison(self, env):
+ type1 = self.operand1.type
+ type2 = self.operand2.type
self.is_pycmp = False
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if entry is None:
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (self.operator, type1, type2))
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- func_type = entry.type
- if func_type.is_ptr:
- func_type = func_type.base_type
+ entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
+ if entry is None:
+ error(self.pos, "Invalid types for '%s' (%s, %s)" %
+ (self.operator, type1, type2))
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
+ return
+ func_type = entry.type
+ if func_type.is_ptr:
+ func_type = func_type.base_type
self.exception_check = func_type.exception_check
self.exception_value = func_type.exception_value
if self.exception_check == '+':
self.is_temp = True
if self.exception_value is None:
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
-
- def analyse_memoryviewslice_comparison(self, env):
- have_none = self.operand1.is_none or self.operand2.is_none
- have_slice = (self.operand1.type.is_memoryviewslice or
- self.operand2.type.is_memoryviewslice)
- ops = ('==', '!=', 'is', 'is_not')
- if have_slice and have_none and self.operator in ops:
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_memslice_nonecheck = True
- return True
-
- return False
-
- def coerce_to_boolean(self, env):
- if self.is_pycmp:
- # coercing to bool => may allow for more efficient comparison code
- if self.find_special_bool_compare_function(
- env, self.operand1, result_is_bool=True):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_temp = 1
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(
- self.operand2, env, result_is_bool=True)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return self
- # TODO: check if we can optimise parts of the cascade here
- return ExprNode.coerce_to_boolean(self, env)
-
- def has_python_operands(self):
- return (self.operand1.type.is_pyobject
- or self.operand2.type.is_pyobject)
-
- def check_const(self):
- if self.cascade:
- self.not_const()
- return False
- else:
- return self.operand1.check_const() and self.operand2.check_const()
-
- def calculate_result_code(self):
+ if len(func_type.args) == 1:
+ self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
+ else:
+ self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
+ self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
+ self.type = func_type.return_type
+
+ def analyse_memoryviewslice_comparison(self, env):
+ have_none = self.operand1.is_none or self.operand2.is_none
+ have_slice = (self.operand1.type.is_memoryviewslice or
+ self.operand2.type.is_memoryviewslice)
+ ops = ('==', '!=', 'is', 'is_not')
+ if have_slice and have_none and self.operator in ops:
+ self.is_pycmp = False
+ self.type = PyrexTypes.c_bint_type
+ self.is_memslice_nonecheck = True
+ return True
+
+ return False
+
+ def coerce_to_boolean(self, env):
+ if self.is_pycmp:
+ # coercing to bool => may allow for more efficient comparison code
+ if self.find_special_bool_compare_function(
+ env, self.operand1, result_is_bool=True):
+ self.is_pycmp = False
+ self.type = PyrexTypes.c_bint_type
+ self.is_temp = 1
+ if self.cascade:
+ operand2 = self.cascade.optimise_comparison(
+ self.operand2, env, result_is_bool=True)
+ if operand2 is not self.operand2:
+ self.coerced_operand2 = operand2
+ return self
+ # TODO: check if we can optimise parts of the cascade here
+ return ExprNode.coerce_to_boolean(self, env)
+
+ def has_python_operands(self):
+ return (self.operand1.type.is_pyobject
+ or self.operand2.type.is_pyobject)
+
+ def check_const(self):
+ if self.cascade:
+ self.not_const()
+ return False
+ else:
+ return self.operand1.check_const() and self.operand2.check_const()
+
+ def calculate_result_code(self):
operand1, operand2 = self.operand1, self.operand2
if operand1.type.is_complex:
- if self.operator == "!=":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
+ if self.operator == "!=":
+ negation = "!"
+ else:
+ negation = ""
+ return "(%s%s(%s, %s))" % (
+ negation,
operand1.type.binary_op('=='),
operand1.result(),
operand2.result())
- elif self.is_c_string_contains():
+ elif self.is_c_string_contains():
if operand2.type is unicode_type:
- method = "__Pyx_UnicodeContainsUCS4"
- else:
- method = "__Pyx_BytesContains"
- if self.operator == "not_in":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
- method,
+ method = "__Pyx_UnicodeContainsUCS4"
+ else:
+ method = "__Pyx_BytesContains"
+ if self.operator == "not_in":
+ negation = "!"
+ else:
+ negation = ""
+ return "(%s%s(%s, %s))" % (
+ negation,
+ method,
operand2.result(),
operand1.result())
- else:
+ else:
if is_pythran_expr(self.type):
result1, result2 = operand1.pythran_result(), operand2.pythran_result()
else:
@@ -12759,163 +12759,163 @@ class PrimaryCmpNode(ExprNode, CmpNode):
result1 = "((PyObject *) %s.memview)" % result1
else:
result2 = "((PyObject *) %s.memview)" % result2
-
- return "(%s %s %s)" % (
- result1,
- self.c_operator(self.operator),
- result2)
-
- def generate_evaluation_code(self, code):
- self.operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- if self.is_temp:
- self.allocate_temp_result(code)
- self.generate_operation_code(code, self.result(),
- self.operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, self.result(), self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- self.operand1.generate_disposal_code(code)
- self.operand1.free_temps(code)
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
-
- def generate_subexpr_disposal_code(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.generate_disposal_code(code)
- self.operand2.generate_disposal_code(code)
-
- def free_subexpr_temps(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.free_temps(code)
- self.operand2.free_temps(code)
-
- def annotate(self, code):
- self.operand1.annotate(code)
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
-
-
-class CascadedCmpNode(Node, CmpNode):
- # A CascadedCmpNode is not a complete expression node. It
- # hangs off the side of another comparison node, shares
- # its left operand with that node, and shares its result
- # with the PrimaryCmpNode at the head of the chain.
- #
- # operator string
- # operand2 ExprNode
- # cascade CascadedCmpNode
-
- child_attrs = ['operand2', 'coerced_operand2', 'cascade']
-
- cascade = None
- coerced_operand2 = None
- constant_result = constant_value_not_set # FIXME: where to calculate this?
-
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
-
- def type_dependencies(self, env):
- return ()
-
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
-
- def analyse_types(self, env):
- self.operand2 = self.operand2.analyse_types(env)
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
- return self
-
- def has_python_operands(self):
- return self.operand2.type.is_pyobject
-
+
+ return "(%s %s %s)" % (
+ result1,
+ self.c_operator(self.operator),
+ result2)
+
+ def generate_evaluation_code(self, code):
+ self.operand1.generate_evaluation_code(code)
+ self.operand2.generate_evaluation_code(code)
+ if self.is_temp:
+ self.allocate_temp_result(code)
+ self.generate_operation_code(code, self.result(),
+ self.operand1, self.operator, self.operand2)
+ if self.cascade:
+ self.cascade.generate_evaluation_code(
+ code, self.result(), self.coerced_operand2 or self.operand2,
+ needs_evaluation=self.coerced_operand2 is not None)
+ self.operand1.generate_disposal_code(code)
+ self.operand1.free_temps(code)
+ self.operand2.generate_disposal_code(code)
+ self.operand2.free_temps(code)
+
+ def generate_subexpr_disposal_code(self, code):
+ # If this is called, it is a non-cascaded cmp,
+ # so only need to dispose of the two main operands.
+ self.operand1.generate_disposal_code(code)
+ self.operand2.generate_disposal_code(code)
+
+ def free_subexpr_temps(self, code):
+ # If this is called, it is a non-cascaded cmp,
+ # so only need to dispose of the two main operands.
+ self.operand1.free_temps(code)
+ self.operand2.free_temps(code)
+
+ def annotate(self, code):
+ self.operand1.annotate(code)
+ self.operand2.annotate(code)
+ if self.cascade:
+ self.cascade.annotate(code)
+
+
+class CascadedCmpNode(Node, CmpNode):
+ # A CascadedCmpNode is not a complete expression node. It
+ # hangs off the side of another comparison node, shares
+ # its left operand with that node, and shares its result
+ # with the PrimaryCmpNode at the head of the chain.
+ #
+ # operator string
+ # operand2 ExprNode
+ # cascade CascadedCmpNode
+
+ child_attrs = ['operand2', 'coerced_operand2', 'cascade']
+
+ cascade = None
+ coerced_operand2 = None
+ constant_result = constant_value_not_set # FIXME: where to calculate this?
+
+ def infer_type(self, env):
+ # TODO: Actually implement this (after merging with -unstable).
+ return py_object_type
+
+ def type_dependencies(self, env):
+ return ()
+
+ def has_constant_result(self):
+ return self.constant_result is not constant_value_not_set and \
+ self.constant_result is not not_a_constant
+
+ def analyse_types(self, env):
+ self.operand2 = self.operand2.analyse_types(env)
+ if self.cascade:
+ self.cascade = self.cascade.analyse_types(env)
+ return self
+
+ def has_python_operands(self):
+ return self.operand2.type.is_pyobject
+
def is_cpp_comparison(self):
# cascaded comparisons aren't currently implemented for c++ classes.
return False
- def optimise_comparison(self, operand1, env, result_is_bool=False):
- if self.find_special_bool_compare_function(env, operand1, result_is_bool):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- if not operand1.type.is_pyobject:
- operand1 = operand1.coerce_to_pyobject(env)
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(self.operand2, env, result_is_bool)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return operand1
-
- def coerce_operands_to_pyobjects(self, env):
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- if self.operand2.type is dict_type and self.operator in ('in', 'not_in'):
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- if self.cascade:
- self.cascade.coerce_operands_to_pyobjects(env)
-
- def coerce_cascaded_operands_to_temp(self, env):
- if self.cascade:
- #self.operand2 = self.operand2.coerce_to_temp(env) #CTT
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
-
- def generate_evaluation_code(self, code, result, operand1, needs_evaluation=False):
- if self.type.is_pyobject:
- code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
- code.put_decref(result, self.type)
- else:
- code.putln("if (%s) {" % result)
- if needs_evaluation:
- operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- self.generate_operation_code(code, result,
- operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, result, self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- if needs_evaluation:
- operand1.generate_disposal_code(code)
- operand1.free_temps(code)
- # Cascaded cmp result is always temp
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
- code.putln("}")
-
- def annotate(self, code):
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
-
-
-binop_node_classes = {
- "or": BoolBinopNode,
- "and": BoolBinopNode,
- "|": IntBinopNode,
- "^": IntBinopNode,
- "&": IntBinopNode,
- "<<": IntBinopNode,
- ">>": IntBinopNode,
- "+": AddNode,
- "-": SubNode,
- "*": MulNode,
- "@": MatMultNode,
- "/": DivNode,
- "//": DivNode,
- "%": ModNode,
- "**": PowNode,
-}
-
+ def optimise_comparison(self, operand1, env, result_is_bool=False):
+ if self.find_special_bool_compare_function(env, operand1, result_is_bool):
+ self.is_pycmp = False
+ self.type = PyrexTypes.c_bint_type
+ if not operand1.type.is_pyobject:
+ operand1 = operand1.coerce_to_pyobject(env)
+ if self.cascade:
+ operand2 = self.cascade.optimise_comparison(self.operand2, env, result_is_bool)
+ if operand2 is not self.operand2:
+ self.coerced_operand2 = operand2
+ return operand1
+
+ def coerce_operands_to_pyobjects(self, env):
+ self.operand2 = self.operand2.coerce_to_pyobject(env)
+ if self.operand2.type is dict_type and self.operator in ('in', 'not_in'):
+ self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
+ if self.cascade:
+ self.cascade.coerce_operands_to_pyobjects(env)
+
+ def coerce_cascaded_operands_to_temp(self, env):
+ if self.cascade:
+ #self.operand2 = self.operand2.coerce_to_temp(env) #CTT
+ self.operand2 = self.operand2.coerce_to_simple(env)
+ self.cascade.coerce_cascaded_operands_to_temp(env)
+
+ def generate_evaluation_code(self, code, result, operand1, needs_evaluation=False):
+ if self.type.is_pyobject:
+ code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
+ code.put_decref(result, self.type)
+ else:
+ code.putln("if (%s) {" % result)
+ if needs_evaluation:
+ operand1.generate_evaluation_code(code)
+ self.operand2.generate_evaluation_code(code)
+ self.generate_operation_code(code, result,
+ operand1, self.operator, self.operand2)
+ if self.cascade:
+ self.cascade.generate_evaluation_code(
+ code, result, self.coerced_operand2 or self.operand2,
+ needs_evaluation=self.coerced_operand2 is not None)
+ if needs_evaluation:
+ operand1.generate_disposal_code(code)
+ operand1.free_temps(code)
+ # Cascaded cmp result is always temp
+ self.operand2.generate_disposal_code(code)
+ self.operand2.free_temps(code)
+ code.putln("}")
+
+ def annotate(self, code):
+ self.operand2.annotate(code)
+ if self.cascade:
+ self.cascade.annotate(code)
+
+
+binop_node_classes = {
+ "or": BoolBinopNode,
+ "and": BoolBinopNode,
+ "|": IntBinopNode,
+ "^": IntBinopNode,
+ "&": IntBinopNode,
+ "<<": IntBinopNode,
+ ">>": IntBinopNode,
+ "+": AddNode,
+ "-": SubNode,
+ "*": MulNode,
+ "@": MatMultNode,
+ "/": DivNode,
+ "//": DivNode,
+ "%": ModNode,
+ "**": PowNode,
+}
+
def binop_node(pos, operator, operand1, operand2, inplace=False, **kwargs):
- # Construct binop node of appropriate class for
- # given operator.
+ # Construct binop node of appropriate class for
+ # given operator.
return binop_node_classes[operator](
pos,
operator=operator,
@@ -12923,126 +12923,126 @@ def binop_node(pos, operator, operand1, operand2, inplace=False, **kwargs):
operand2=operand2,
inplace=inplace,
**kwargs)
-
-
-#-------------------------------------------------------------------
-#
-# Coercion nodes
-#
-# Coercion nodes are special in that they are created during
-# the analyse_types phase of parse tree processing.
-# Their __init__ methods consequently incorporate some aspects
-# of that phase.
-#
-#-------------------------------------------------------------------
-
-class CoercionNode(ExprNode):
- # Abstract base class for coercion nodes.
- #
- # arg ExprNode node being coerced
-
- subexprs = ['arg']
- constant_result = not_a_constant
-
- def __init__(self, arg):
- super(CoercionNode, self).__init__(arg.pos)
- self.arg = arg
- if debug_coercion:
- print("%s Coercing %s" % (self, self.arg))
-
- def calculate_constant_result(self):
- # constant folding can break type coercion, so this is disabled
- pass
-
- def annotate(self, code):
- self.arg.annotate(code)
- if self.arg.type != self.type:
- file, line, col = self.pos
- code.annotate((file, line, col-1), AnnotationItem(
- style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
-
-
-class CoerceToMemViewSliceNode(CoercionNode):
- """
- Coerce an object to a memoryview slice. This holds a new reference in
- a managed temp.
- """
-
- def __init__(self, arg, dst_type, env):
- assert dst_type.is_memoryviewslice
- assert not arg.type.is_memoryviewslice
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.is_temp = 1
- self.use_managed_ref = True
- self.arg = arg
+
+
+#-------------------------------------------------------------------
+#
+# Coercion nodes
+#
+# Coercion nodes are special in that they are created during
+# the analyse_types phase of parse tree processing.
+# Their __init__ methods consequently incorporate some aspects
+# of that phase.
+#
+#-------------------------------------------------------------------
+
+class CoercionNode(ExprNode):
+ # Abstract base class for coercion nodes.
+ #
+ # arg ExprNode node being coerced
+
+ subexprs = ['arg']
+ constant_result = not_a_constant
+
+ def __init__(self, arg):
+ super(CoercionNode, self).__init__(arg.pos)
+ self.arg = arg
+ if debug_coercion:
+ print("%s Coercing %s" % (self, self.arg))
+
+ def calculate_constant_result(self):
+ # constant folding can break type coercion, so this is disabled
+ pass
+
+ def annotate(self, code):
+ self.arg.annotate(code)
+ if self.arg.type != self.type:
+ file, line, col = self.pos
+ code.annotate((file, line, col-1), AnnotationItem(
+ style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
+
+
+class CoerceToMemViewSliceNode(CoercionNode):
+ """
+ Coerce an object to a memoryview slice. This holds a new reference in
+ a managed temp.
+ """
+
+ def __init__(self, arg, dst_type, env):
+ assert dst_type.is_memoryviewslice
+ assert not arg.type.is_memoryviewslice
+ CoercionNode.__init__(self, arg)
+ self.type = dst_type
+ self.is_temp = 1
+ self.use_managed_ref = True
+ self.arg = arg
self.type.create_from_py_utility_code(env)
-
- def generate_result_code(self, code):
+
+ def generate_result_code(self, code):
code.putln(self.type.from_py_call_code(
self.arg.py_result(),
self.result(),
self.pos,
code
))
-
-
-class CastNode(CoercionNode):
- # Wrap a node in a C type cast.
-
- def __init__(self, arg, new_type):
- CoercionNode.__init__(self, arg)
- self.type = new_type
-
- def may_be_none(self):
- return self.arg.may_be_none()
-
- def calculate_result_code(self):
- return self.arg.result_as(self.type)
-
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
-
-
-class PyTypeTestNode(CoercionNode):
- # This node is used to check that a generic Python
- # object is an instance of a particular extension type.
- # This node borrows the result of its argument node.
-
- exact_builtin_type = True
-
- def __init__(self, arg, dst_type, env, notnone=False):
- # The arg is know to be a Python object, and
- # the dst_type is known to be an extension type.
- assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.result_ctype = arg.ctype()
- self.notnone = notnone
-
- nogil_check = Node.gil_error
- gil_message = "Python type test"
-
- def analyse_types(self, env):
- return self
-
- def may_be_none(self):
- if self.notnone:
- return False
- return self.arg.may_be_none()
-
- def is_simple(self):
- return self.arg.is_simple()
-
- def result_in_temp(self):
- return self.arg.result_in_temp()
-
- def is_ephemeral(self):
- return self.arg.is_ephemeral()
-
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
-
+
+
+class CastNode(CoercionNode):
+ # Wrap a node in a C type cast.
+
+ def __init__(self, arg, new_type):
+ CoercionNode.__init__(self, arg)
+ self.type = new_type
+
+ def may_be_none(self):
+ return self.arg.may_be_none()
+
+ def calculate_result_code(self):
+ return self.arg.result_as(self.type)
+
+ def generate_result_code(self, code):
+ self.arg.generate_result_code(code)
+
+
+class PyTypeTestNode(CoercionNode):
+ # This node is used to check that a generic Python
+ # object is an instance of a particular extension type.
+ # This node borrows the result of its argument node.
+
+ exact_builtin_type = True
+
+ def __init__(self, arg, dst_type, env, notnone=False):
+ # The arg is know to be a Python object, and
+ # the dst_type is known to be an extension type.
+ assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
+ CoercionNode.__init__(self, arg)
+ self.type = dst_type
+ self.result_ctype = arg.ctype()
+ self.notnone = notnone
+
+ nogil_check = Node.gil_error
+ gil_message = "Python type test"
+
+ def analyse_types(self, env):
+ return self
+
+ def may_be_none(self):
+ if self.notnone:
+ return False
+ return self.arg.may_be_none()
+
+ def is_simple(self):
+ return self.arg.is_simple()
+
+ def result_in_temp(self):
+ return self.arg.result_in_temp()
+
+ def is_ephemeral(self):
+ return self.arg.is_ephemeral()
+
+ def nonlocally_immutable(self):
+ return self.arg.nonlocally_immutable()
+
def reanalyse(self):
if self.type != self.arg.type or not self.arg.is_temp:
return self
@@ -13052,90 +13052,90 @@ class PyTypeTestNode(CoercionNode):
return self.arg.as_none_safe_node("Cannot convert NoneType to %.200s" % self.type.name)
return self.arg
- def calculate_constant_result(self):
- # FIXME
- pass
-
- def calculate_result_code(self):
- return self.arg.result()
-
- def generate_result_code(self, code):
- if self.type.typeobj_is_available():
- if self.type.is_builtin_type:
- type_test = self.type.type_test_code(
- self.arg.py_result(),
- self.notnone, exact=self.exact_builtin_type)
- else:
- type_test = self.type.type_test_code(
- self.arg.py_result(), self.notnone)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
- code.putln("if (!(%s)) %s" % (
- type_test, code.error_goto(self.pos)))
- else:
- error(self.pos, "Cannot test type of extern C class "
- "without type object name specification")
-
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
-
+ def calculate_constant_result(self):
+ # FIXME
+ pass
+
+ def calculate_result_code(self):
+ return self.arg.result()
+
+ def generate_result_code(self, code):
+ if self.type.typeobj_is_available():
+ if self.type.is_builtin_type:
+ type_test = self.type.type_test_code(
+ self.arg.py_result(),
+ self.notnone, exact=self.exact_builtin_type)
+ else:
+ type_test = self.type.type_test_code(
+ self.arg.py_result(), self.notnone)
+ code.globalstate.use_utility_code(
+ UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
+ code.putln("if (!(%s)) %s" % (
+ type_test, code.error_goto(self.pos)))
+ else:
+ error(self.pos, "Cannot test type of extern C class "
+ "without type object name specification")
+
+ def generate_post_assignment_code(self, code):
+ self.arg.generate_post_assignment_code(code)
+
def allocate_temp_result(self, code):
pass
def release_temp_result(self, code):
pass
- def free_temps(self, code):
- self.arg.free_temps(code)
-
+ def free_temps(self, code):
+ self.arg.free_temps(code)
+
def free_subexpr_temps(self, code):
self.arg.free_subexpr_temps(code)
-
-
-class NoneCheckNode(CoercionNode):
- # This node is used to check that a Python object is not None and
- # raises an appropriate exception (as specified by the creating
- # transform).
-
- is_nonecheck = True
-
- def __init__(self, arg, exception_type_cname, exception_message,
+
+
+class NoneCheckNode(CoercionNode):
+ # This node is used to check that a Python object is not None and
+ # raises an appropriate exception (as specified by the creating
+ # transform).
+
+ is_nonecheck = True
+
+ def __init__(self, arg, exception_type_cname, exception_message,
exception_format_args=()):
- CoercionNode.__init__(self, arg)
- self.type = arg.type
- self.result_ctype = arg.ctype()
- self.exception_type_cname = exception_type_cname
- self.exception_message = exception_message
- self.exception_format_args = tuple(exception_format_args or ())
-
- nogil_check = None # this node only guards an operation that would fail already
-
- def analyse_types(self, env):
- return self
-
- def may_be_none(self):
- return False
-
- def is_simple(self):
- return self.arg.is_simple()
-
- def result_in_temp(self):
- return self.arg.result_in_temp()
-
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
-
- def calculate_result_code(self):
- return self.arg.result()
-
- def condition(self):
- if self.type.is_pyobject:
- return self.arg.py_result()
- elif self.type.is_memoryviewslice:
- return "((PyObject *) %s.memview)" % self.arg.result()
- else:
- raise Exception("unsupported type")
-
+ CoercionNode.__init__(self, arg)
+ self.type = arg.type
+ self.result_ctype = arg.ctype()
+ self.exception_type_cname = exception_type_cname
+ self.exception_message = exception_message
+ self.exception_format_args = tuple(exception_format_args or ())
+
+ nogil_check = None # this node only guards an operation that would fail already
+
+ def analyse_types(self, env):
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def is_simple(self):
+ return self.arg.is_simple()
+
+ def result_in_temp(self):
+ return self.arg.result_in_temp()
+
+ def nonlocally_immutable(self):
+ return self.arg.nonlocally_immutable()
+
+ def calculate_result_code(self):
+ return self.arg.result()
+
+ def condition(self):
+ if self.type.is_pyobject:
+ return self.arg.py_result()
+ elif self.type.is_memoryviewslice:
+ return "((PyObject *) %s.memview)" % self.arg.result()
+ else:
+ raise Exception("unsupported type")
+
@classmethod
def generate(cls, arg, code, exception_message,
exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
@@ -13149,180 +13149,180 @@ class NoneCheckNode(CoercionNode):
if arg.may_be_none():
cls.generate(arg, code, exception_message, exception_type_cname, exception_format_args, in_nogil_context)
- def put_nonecheck(self, code):
- code.putln(
- "if (unlikely(%s == Py_None)) {" % self.condition())
-
- if self.in_nogil_context:
- code.put_ensure_gil()
-
- escape = StringEncoding.escape_byte_string
- if self.exception_format_args:
- code.putln('PyErr_Format(%s, "%s", %s);' % (
- self.exception_type_cname,
- StringEncoding.escape_byte_string(
- self.exception_message.encode('UTF-8')),
- ', '.join([ '"%s"' % escape(str(arg).encode('UTF-8'))
- for arg in self.exception_format_args ])))
- else:
- code.putln('PyErr_SetString(%s, "%s");' % (
- self.exception_type_cname,
- escape(self.exception_message.encode('UTF-8'))))
-
- if self.in_nogil_context:
- code.put_release_ensured_gil()
-
- code.putln(code.error_goto(self.pos))
- code.putln("}")
-
- def generate_result_code(self, code):
- self.put_nonecheck(code)
-
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
-
- def free_temps(self, code):
- self.arg.free_temps(code)
-
-
-class CoerceToPyTypeNode(CoercionNode):
- # This node is used to convert a C data type
- # to a Python object.
-
- type = py_object_type
+ def put_nonecheck(self, code):
+ code.putln(
+ "if (unlikely(%s == Py_None)) {" % self.condition())
+
+ if self.in_nogil_context:
+ code.put_ensure_gil()
+
+ escape = StringEncoding.escape_byte_string
+ if self.exception_format_args:
+ code.putln('PyErr_Format(%s, "%s", %s);' % (
+ self.exception_type_cname,
+ StringEncoding.escape_byte_string(
+ self.exception_message.encode('UTF-8')),
+ ', '.join([ '"%s"' % escape(str(arg).encode('UTF-8'))
+ for arg in self.exception_format_args ])))
+ else:
+ code.putln('PyErr_SetString(%s, "%s");' % (
+ self.exception_type_cname,
+ escape(self.exception_message.encode('UTF-8'))))
+
+ if self.in_nogil_context:
+ code.put_release_ensured_gil()
+
+ code.putln(code.error_goto(self.pos))
+ code.putln("}")
+
+ def generate_result_code(self, code):
+ self.put_nonecheck(code)
+
+ def generate_post_assignment_code(self, code):
+ self.arg.generate_post_assignment_code(code)
+
+ def free_temps(self, code):
+ self.arg.free_temps(code)
+
+
+class CoerceToPyTypeNode(CoercionNode):
+ # This node is used to convert a C data type
+ # to a Python object.
+
+ type = py_object_type
target_type = py_object_type
- is_temp = 1
-
- def __init__(self, arg, env, type=py_object_type):
- if not arg.type.create_to_py_utility_code(env):
- error(arg.pos, "Cannot convert '%s' to Python object" % arg.type)
- elif arg.type.is_complex:
- # special case: complex coercion is so complex that it
- # uses a macro ("__pyx_PyComplex_FromComplex()"), for
- # which the argument must be simple
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- if type is py_object_type:
- # be specific about some known types
- if arg.type.is_string or arg.type.is_cpp_string:
- self.type = default_str_type(env)
- elif arg.type.is_pyunicode_ptr or arg.type.is_unicode_char:
- self.type = unicode_type
- elif arg.type.is_complex:
- self.type = Builtin.complex_type
+ is_temp = 1
+
+ def __init__(self, arg, env, type=py_object_type):
+ if not arg.type.create_to_py_utility_code(env):
+ error(arg.pos, "Cannot convert '%s' to Python object" % arg.type)
+ elif arg.type.is_complex:
+ # special case: complex coercion is so complex that it
+ # uses a macro ("__pyx_PyComplex_FromComplex()"), for
+ # which the argument must be simple
+ arg = arg.coerce_to_simple(env)
+ CoercionNode.__init__(self, arg)
+ if type is py_object_type:
+ # be specific about some known types
+ if arg.type.is_string or arg.type.is_cpp_string:
+ self.type = default_str_type(env)
+ elif arg.type.is_pyunicode_ptr or arg.type.is_unicode_char:
+ self.type = unicode_type
+ elif arg.type.is_complex:
+ self.type = Builtin.complex_type
self.target_type = self.type
- elif arg.type.is_string or arg.type.is_cpp_string:
- if (type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(arg.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (arg.type, type))
+ elif arg.type.is_string or arg.type.is_cpp_string:
+ if (type not in (bytes_type, bytearray_type)
+ and not env.directives['c_string_encoding']):
+ error(arg.pos,
+ "default encoding required for conversion from '%s' to '%s'" %
+ (arg.type, type))
self.type = self.target_type = type
- else:
- # FIXME: check that the target type and the resulting type are compatible
+ else:
+ # FIXME: check that the target type and the resulting type are compatible
self.target_type = type
-
- gil_message = "Converting to Python object"
-
- def may_be_none(self):
- # FIXME: is this always safe?
- return False
-
- def coerce_to_boolean(self, env):
- arg_type = self.arg.type
- if (arg_type == PyrexTypes.c_bint_type or
- (arg_type.is_pyobject and arg_type.name == 'bool')):
- return self.arg.coerce_to_temp(env)
- else:
- return CoerceToBooleanNode(self, env)
-
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.arg.type.is_int:
- return self.arg
- else:
- return self.arg.coerce_to(PyrexTypes.c_long_type, env)
-
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
-
- def generate_result_code(self, code):
+
+ gil_message = "Converting to Python object"
+
+ def may_be_none(self):
+ # FIXME: is this always safe?
+ return False
+
+ def coerce_to_boolean(self, env):
+ arg_type = self.arg.type
+ if (arg_type == PyrexTypes.c_bint_type or
+ (arg_type.is_pyobject and arg_type.name == 'bool')):
+ return self.arg.coerce_to_temp(env)
+ else:
+ return CoerceToBooleanNode(self, env)
+
+ def coerce_to_integer(self, env):
+ # If not already some C integer type, coerce to longint.
+ if self.arg.type.is_int:
+ return self.arg
+ else:
+ return self.arg.coerce_to(PyrexTypes.c_long_type, env)
+
+ def analyse_types(self, env):
+ # The arg is always already analysed
+ return self
+
+ def generate_result_code(self, code):
code.putln('%s; %s' % (
self.arg.type.to_py_call_code(
self.arg.result(),
self.result(),
self.target_type),
- code.error_goto_if_null(self.result(), self.pos)))
-
- code.put_gotref(self.py_result())
-
-
-class CoerceIntToBytesNode(CoerceToPyTypeNode):
- # This node is used to convert a C int type to a Python bytes
- # object.
-
- is_temp = 1
-
- def __init__(self, arg, env):
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- self.type = Builtin.bytes_type
-
- def generate_result_code(self, code):
- arg = self.arg
- arg_result = arg.result()
- if arg.type not in (PyrexTypes.c_char_type,
- PyrexTypes.c_uchar_type,
- PyrexTypes.c_schar_type):
- if arg.type.signed:
- code.putln("if ((%s < 0) || (%s > 255)) {" % (
- arg_result, arg_result))
- else:
- code.putln("if (%s > 255) {" % arg_result)
- code.putln('PyErr_SetString(PyExc_OverflowError, '
- '"value too large to pack into a byte"); %s' % (
- code.error_goto(self.pos)))
- code.putln('}')
- temp = None
- if arg.type is not PyrexTypes.c_char_type:
- temp = code.funcstate.allocate_temp(PyrexTypes.c_char_type, manage_ref=False)
- code.putln("%s = (char)%s;" % (temp, arg_result))
- arg_result = temp
- code.putln('%s = PyBytes_FromStringAndSize(&%s, 1); %s' % (
- self.result(),
- arg_result,
- code.error_goto_if_null(self.result(), self.pos)))
- if temp is not None:
- code.funcstate.release_temp(temp)
- code.put_gotref(self.py_result())
-
-
-class CoerceFromPyTypeNode(CoercionNode):
- # This node is used to convert a Python object
- # to a C data type.
-
- def __init__(self, result_type, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = result_type
- self.is_temp = 1
- if not result_type.create_from_py_utility_code(env):
- error(arg.pos,
- "Cannot convert Python object to '%s'" % result_type)
- if self.type.is_string or self.type.is_pyunicode_ptr:
- if self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
- warning(arg.pos,
- "Obtaining '%s' from externally modifiable global Python value" % result_type,
- level=1)
-
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
-
- def is_ephemeral(self):
+ code.error_goto_if_null(self.result(), self.pos)))
+
+ code.put_gotref(self.py_result())
+
+
+class CoerceIntToBytesNode(CoerceToPyTypeNode):
+ # This node is used to convert a C int type to a Python bytes
+ # object.
+
+ is_temp = 1
+
+ def __init__(self, arg, env):
+ arg = arg.coerce_to_simple(env)
+ CoercionNode.__init__(self, arg)
+ self.type = Builtin.bytes_type
+
+ def generate_result_code(self, code):
+ arg = self.arg
+ arg_result = arg.result()
+ if arg.type not in (PyrexTypes.c_char_type,
+ PyrexTypes.c_uchar_type,
+ PyrexTypes.c_schar_type):
+ if arg.type.signed:
+ code.putln("if ((%s < 0) || (%s > 255)) {" % (
+ arg_result, arg_result))
+ else:
+ code.putln("if (%s > 255) {" % arg_result)
+ code.putln('PyErr_SetString(PyExc_OverflowError, '
+ '"value too large to pack into a byte"); %s' % (
+ code.error_goto(self.pos)))
+ code.putln('}')
+ temp = None
+ if arg.type is not PyrexTypes.c_char_type:
+ temp = code.funcstate.allocate_temp(PyrexTypes.c_char_type, manage_ref=False)
+ code.putln("%s = (char)%s;" % (temp, arg_result))
+ arg_result = temp
+ code.putln('%s = PyBytes_FromStringAndSize(&%s, 1); %s' % (
+ self.result(),
+ arg_result,
+ code.error_goto_if_null(self.result(), self.pos)))
+ if temp is not None:
+ code.funcstate.release_temp(temp)
+ code.put_gotref(self.py_result())
+
+
+class CoerceFromPyTypeNode(CoercionNode):
+ # This node is used to convert a Python object
+ # to a C data type.
+
+ def __init__(self, result_type, arg, env):
+ CoercionNode.__init__(self, arg)
+ self.type = result_type
+ self.is_temp = 1
+ if not result_type.create_from_py_utility_code(env):
+ error(arg.pos,
+ "Cannot convert Python object to '%s'" % result_type)
+ if self.type.is_string or self.type.is_pyunicode_ptr:
+ if self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
+ warning(arg.pos,
+ "Obtaining '%s' from externally modifiable global Python value" % result_type,
+ level=1)
+
+ def analyse_types(self, env):
+ # The arg is always already analysed
+ return self
+
+ def is_ephemeral(self):
return (self.type.is_ptr and not self.type.is_array) and self.arg.is_ephemeral()
-
- def generate_result_code(self, code):
+
+ def generate_result_code(self, code):
from_py_function = None
# for certain source types, we can do better than the generic coercion
if self.type.is_string and self.arg.type is bytes_type:
@@ -13332,20 +13332,20 @@ class CoerceFromPyTypeNode(CoercionNode):
code.putln(self.type.from_py_call_code(
self.arg.py_result(), self.result(), self.pos, code, from_py_function=from_py_function))
- if self.type.is_pyobject:
- code.put_gotref(self.py_result())
-
- def nogil_check(self, env):
- error(self.pos, "Coercion from Python not allowed without the GIL")
-
-
-class CoerceToBooleanNode(CoercionNode):
- # This node is used when a result needs to be used
- # in a boolean context.
-
- type = PyrexTypes.c_bint_type
-
- _special_builtins = {
+ if self.type.is_pyobject:
+ code.put_gotref(self.py_result())
+
+ def nogil_check(self, env):
+ error(self.pos, "Coercion from Python not allowed without the GIL")
+
+
+class CoerceToBooleanNode(CoercionNode):
+ # This node is used when a result needs to be used
+ # in a boolean context.
+
+ type = PyrexTypes.c_bint_type
+
+ _special_builtins = {
Builtin.list_type: 'PyList_GET_SIZE',
Builtin.tuple_type: 'PyTuple_GET_SIZE',
Builtin.set_type: 'PySet_GET_SIZE',
@@ -13354,341 +13354,341 @@ class CoerceToBooleanNode(CoercionNode):
Builtin.bytearray_type: 'PyByteArray_GET_SIZE',
Builtin.unicode_type: '__Pyx_PyUnicode_IS_TRUE',
}
-
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- if arg.type.is_pyobject:
- self.is_temp = 1
-
- def nogil_check(self, env):
- if self.arg.type.is_pyobject and self._special_builtins.get(self.arg.type) is None:
- self.gil_error()
-
- gil_message = "Truth-testing Python object"
-
- def check_const(self):
- if self.is_temp:
- self.not_const()
- return False
- return self.arg.check_const()
-
- def calculate_result_code(self):
- return "(%s != 0)" % self.arg.result()
-
- def generate_result_code(self, code):
- if not self.is_temp:
- return
- test_func = self._special_builtins.get(self.arg.type)
- if test_func is not None:
+
+ def __init__(self, arg, env):
+ CoercionNode.__init__(self, arg)
+ if arg.type.is_pyobject:
+ self.is_temp = 1
+
+ def nogil_check(self, env):
+ if self.arg.type.is_pyobject and self._special_builtins.get(self.arg.type) is None:
+ self.gil_error()
+
+ gil_message = "Truth-testing Python object"
+
+ def check_const(self):
+ if self.is_temp:
+ self.not_const()
+ return False
+ return self.arg.check_const()
+
+ def calculate_result_code(self):
+ return "(%s != 0)" % self.arg.result()
+
+ def generate_result_code(self, code):
+ if not self.is_temp:
+ return
+ test_func = self._special_builtins.get(self.arg.type)
+ if test_func is not None:
checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else []
checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result()))
code.putln("%s = %s;" % (self.result(), '&&'.join(checks)))
- else:
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_neg(self.result(), self.pos)))
-
-
-class CoerceToComplexNode(CoercionNode):
-
- def __init__(self, arg, dst_type, env):
- if arg.type.is_complex:
- arg = arg.coerce_to_simple(env)
- self.type = dst_type
- CoercionNode.__init__(self, arg)
- dst_type.create_declaration_utility_code(env)
-
- def calculate_result_code(self):
- if self.arg.type.is_complex:
- real_part = "__Pyx_CREAL(%s)" % self.arg.result()
- imag_part = "__Pyx_CIMAG(%s)" % self.arg.result()
- else:
- real_part = self.arg.result()
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
-
- def generate_result_code(self, code):
- pass
-
-class CoerceToTempNode(CoercionNode):
- # This node is used to force the result of another node
- # to be stored in a temporary. It is only used if the
- # argument node's result is not already in a temporary.
-
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = self.arg.type.as_argument_type()
- self.constant_result = self.arg.constant_result
- self.is_temp = 1
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
-
- gil_message = "Creating temporary Python reference"
-
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
-
- def coerce_to_boolean(self, env):
- self.arg = self.arg.coerce_to_boolean(env)
- if self.arg.is_simple():
- return self.arg
- self.type = self.arg.type
- self.result_ctype = self.type
- return self
-
- def generate_result_code(self, code):
- #self.arg.generate_evaluation_code(code) # Already done
- # by generic generate_subexpr_evaluation_code!
- code.putln("%s = %s;" % (
- self.result(), self.arg.result_as(self.ctype())))
- if self.use_managed_ref:
- if self.type.is_pyobject:
- code.put_incref(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_incref_memoryviewslice(self.result(),
- not self.in_nogil_context)
-
-class ProxyNode(CoercionNode):
- """
- A node that should not be replaced by transforms or other means,
- and hence can be useful to wrap the argument to a clone node
-
- MyNode -> ProxyNode -> ArgNode
- CloneNode -^
- """
-
- nogil_check = None
-
- def __init__(self, arg):
- super(ProxyNode, self).__init__(arg)
- self.constant_result = arg.constant_result
- self._proxy_type()
-
- def analyse_types(self, env):
- self.arg = self.arg.analyse_expressions(env)
- self._proxy_type()
- return self
-
- def infer_type(self, env):
- return self.arg.infer_type(env)
-
- def _proxy_type(self):
- if hasattr(self.arg, 'type'):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
-
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
-
- def result(self):
- return self.arg.result()
-
- def is_simple(self):
- return self.arg.is_simple()
-
- def may_be_none(self):
- return self.arg.may_be_none()
-
- def generate_evaluation_code(self, code):
- self.arg.generate_evaluation_code(code)
-
- def generate_disposal_code(self, code):
- self.arg.generate_disposal_code(code)
-
- def free_temps(self, code):
- self.arg.free_temps(code)
-
-class CloneNode(CoercionNode):
- # This node is employed when the result of another node needs
- # to be used multiple times. The argument node's result must
- # be in a temporary. This node "borrows" the result from the
- # argument node, and does not generate any evaluation or
- # disposal code for it. The original owner of the argument
- # node is responsible for doing those things.
-
- subexprs = [] # Arg is not considered a subexpr
- nogil_check = None
-
- def __init__(self, arg):
- CoercionNode.__init__(self, arg)
- self.constant_result = arg.constant_result
- if hasattr(arg, 'type'):
- self.type = arg.type
- self.result_ctype = arg.result_ctype
- if hasattr(arg, 'entry'):
- self.entry = arg.entry
-
- def result(self):
- return self.arg.result()
-
- def may_be_none(self):
- return self.arg.may_be_none()
-
- def type_dependencies(self, env):
- return self.arg.type_dependencies(env)
-
- def infer_type(self, env):
- return self.arg.infer_type(env)
-
- def analyse_types(self, env):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- self.is_temp = 1
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
- return self
-
- def coerce_to(self, dest_type, env):
- if self.arg.is_literal:
- return self.arg.coerce_to(dest_type, env)
- return super(CloneNode, self).coerce_to(dest_type, env)
-
- def is_simple(self):
- return True # result is always in a temp (or a name)
-
- def generate_evaluation_code(self, code):
- pass
-
- def generate_result_code(self, code):
- pass
-
- def generate_disposal_code(self, code):
- pass
-
- def free_temps(self, code):
- pass
-
-
-class CMethodSelfCloneNode(CloneNode):
- # Special CloneNode for the self argument of builtin C methods
- # that accepts subtypes of the builtin type. This is safe only
- # for 'final' subtypes, as subtypes of the declared type may
- # override the C method.
-
- def coerce_to(self, dst_type, env):
- if dst_type.is_builtin_type and self.type.subtype_of(dst_type):
- return self
- return CloneNode.coerce_to(self, dst_type, env)
-
-
-class ModuleRefNode(ExprNode):
- # Simple returns the module object
-
- type = py_object_type
- is_temp = False
- subexprs = []
-
- def analyse_types(self, env):
- return self
-
- def may_be_none(self):
- return False
-
- def calculate_result_code(self):
- return Naming.module_cname
-
- def generate_result_code(self, code):
- pass
-
-class DocstringRefNode(ExprNode):
- # Extracts the docstring of the body element
-
- subexprs = ['body']
- type = py_object_type
- is_temp = True
-
- def __init__(self, pos, body):
- ExprNode.__init__(self, pos)
- assert body.type.is_pyobject
- self.body = body
-
- def analyse_types(self, env):
- return self
-
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_GetAttr(%s, %s); %s' % (
- self.result(), self.body.result(),
- code.intern_identifier(StringEncoding.EncodedString("__doc__")),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
-
-
-
-#------------------------------------------------------------------------------------
-#
-# Runtime support code
-#
-#------------------------------------------------------------------------------------
-
-pyerr_occurred_withgil_utility_code= UtilityCode(
-proto = """
-static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
-""",
-impl = """
-static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
- int err;
- #ifdef WITH_THREAD
- PyGILState_STATE _save = PyGILState_Ensure();
- #endif
- err = !!PyErr_Occurred();
- #ifdef WITH_THREAD
- PyGILState_Release(_save);
- #endif
- return err;
-}
-"""
-)
-
-#------------------------------------------------------------------------------------
-
-raise_unbound_local_error_utility_code = UtilityCode(
-proto = """
-static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
-""",
-impl = """
-static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
- PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
-}
-""")
-
-raise_closure_name_error_utility_code = UtilityCode(
-proto = """
-static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);
-""",
-impl = """
-static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
- PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
-}
-""")
-
-# Don't inline the function, it should really never be called in production
-raise_unbound_memoryview_utility_code_nogil = UtilityCode(
-proto = """
-static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);
-""",
-impl = """
-static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
- #ifdef WITH_THREAD
- PyGILState_STATE gilstate = PyGILState_Ensure();
- #endif
- __Pyx_RaiseUnboundLocalError(varname);
- #ifdef WITH_THREAD
- PyGILState_Release(gilstate);
- #endif
-}
-""",
-requires = [raise_unbound_local_error_utility_code])
-
-#------------------------------------------------------------------------------------
-
-raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
-raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
-tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")
+ else:
+ code.putln(
+ "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
+ self.result(),
+ self.arg.py_result(),
+ code.error_goto_if_neg(self.result(), self.pos)))
+
+
+class CoerceToComplexNode(CoercionNode):
+
+ def __init__(self, arg, dst_type, env):
+ if arg.type.is_complex:
+ arg = arg.coerce_to_simple(env)
+ self.type = dst_type
+ CoercionNode.__init__(self, arg)
+ dst_type.create_declaration_utility_code(env)
+
+ def calculate_result_code(self):
+ if self.arg.type.is_complex:
+ real_part = "__Pyx_CREAL(%s)" % self.arg.result()
+ imag_part = "__Pyx_CIMAG(%s)" % self.arg.result()
+ else:
+ real_part = self.arg.result()
+ imag_part = "0"
+ return "%s(%s, %s)" % (
+ self.type.from_parts,
+ real_part,
+ imag_part)
+
+ def generate_result_code(self, code):
+ pass
+
+class CoerceToTempNode(CoercionNode):
+ # This node is used to force the result of another node
+ # to be stored in a temporary. It is only used if the
+ # argument node's result is not already in a temporary.
+
+ def __init__(self, arg, env):
+ CoercionNode.__init__(self, arg)
+ self.type = self.arg.type.as_argument_type()
+ self.constant_result = self.arg.constant_result
+ self.is_temp = 1
+ if self.type.is_pyobject:
+ self.result_ctype = py_object_type
+
+ gil_message = "Creating temporary Python reference"
+
+ def analyse_types(self, env):
+ # The arg is always already analysed
+ return self
+
+ def coerce_to_boolean(self, env):
+ self.arg = self.arg.coerce_to_boolean(env)
+ if self.arg.is_simple():
+ return self.arg
+ self.type = self.arg.type
+ self.result_ctype = self.type
+ return self
+
+ def generate_result_code(self, code):
+ #self.arg.generate_evaluation_code(code) # Already done
+ # by generic generate_subexpr_evaluation_code!
+ code.putln("%s = %s;" % (
+ self.result(), self.arg.result_as(self.ctype())))
+ if self.use_managed_ref:
+ if self.type.is_pyobject:
+ code.put_incref(self.result(), self.ctype())
+ elif self.type.is_memoryviewslice:
+ code.put_incref_memoryviewslice(self.result(),
+ not self.in_nogil_context)
+
+class ProxyNode(CoercionNode):
+ """
+ A node that should not be replaced by transforms or other means,
+ and hence can be useful to wrap the argument to a clone node
+
+ MyNode -> ProxyNode -> ArgNode
+ CloneNode -^
+ """
+
+ nogil_check = None
+
+ def __init__(self, arg):
+ super(ProxyNode, self).__init__(arg)
+ self.constant_result = arg.constant_result
+ self._proxy_type()
+
+ def analyse_types(self, env):
+ self.arg = self.arg.analyse_expressions(env)
+ self._proxy_type()
+ return self
+
+ def infer_type(self, env):
+ return self.arg.infer_type(env)
+
+ def _proxy_type(self):
+ if hasattr(self.arg, 'type'):
+ self.type = self.arg.type
+ self.result_ctype = self.arg.result_ctype
+ if hasattr(self.arg, 'entry'):
+ self.entry = self.arg.entry
+
+ def generate_result_code(self, code):
+ self.arg.generate_result_code(code)
+
+ def result(self):
+ return self.arg.result()
+
+ def is_simple(self):
+ return self.arg.is_simple()
+
+ def may_be_none(self):
+ return self.arg.may_be_none()
+
+ def generate_evaluation_code(self, code):
+ self.arg.generate_evaluation_code(code)
+
+ def generate_disposal_code(self, code):
+ self.arg.generate_disposal_code(code)
+
+ def free_temps(self, code):
+ self.arg.free_temps(code)
+
+class CloneNode(CoercionNode):
+ # This node is employed when the result of another node needs
+ # to be used multiple times. The argument node's result must
+ # be in a temporary. This node "borrows" the result from the
+ # argument node, and does not generate any evaluation or
+ # disposal code for it. The original owner of the argument
+ # node is responsible for doing those things.
+
+ subexprs = [] # Arg is not considered a subexpr
+ nogil_check = None
+
+ def __init__(self, arg):
+ CoercionNode.__init__(self, arg)
+ self.constant_result = arg.constant_result
+ if hasattr(arg, 'type'):
+ self.type = arg.type
+ self.result_ctype = arg.result_ctype
+ if hasattr(arg, 'entry'):
+ self.entry = arg.entry
+
+ def result(self):
+ return self.arg.result()
+
+ def may_be_none(self):
+ return self.arg.may_be_none()
+
+ def type_dependencies(self, env):
+ return self.arg.type_dependencies(env)
+
+ def infer_type(self, env):
+ return self.arg.infer_type(env)
+
+ def analyse_types(self, env):
+ self.type = self.arg.type
+ self.result_ctype = self.arg.result_ctype
+ self.is_temp = 1
+ if hasattr(self.arg, 'entry'):
+ self.entry = self.arg.entry
+ return self
+
+ def coerce_to(self, dest_type, env):
+ if self.arg.is_literal:
+ return self.arg.coerce_to(dest_type, env)
+ return super(CloneNode, self).coerce_to(dest_type, env)
+
+ def is_simple(self):
+ return True # result is always in a temp (or a name)
+
+ def generate_evaluation_code(self, code):
+ pass
+
+ def generate_result_code(self, code):
+ pass
+
+ def generate_disposal_code(self, code):
+ pass
+
+ def free_temps(self, code):
+ pass
+
+
+class CMethodSelfCloneNode(CloneNode):
+ # Special CloneNode for the self argument of builtin C methods
+ # that accepts subtypes of the builtin type. This is safe only
+ # for 'final' subtypes, as subtypes of the declared type may
+ # override the C method.
+
+ def coerce_to(self, dst_type, env):
+ if dst_type.is_builtin_type and self.type.subtype_of(dst_type):
+ return self
+ return CloneNode.coerce_to(self, dst_type, env)
+
+
+class ModuleRefNode(ExprNode):
+ # Simple returns the module object
+
+ type = py_object_type
+ is_temp = False
+ subexprs = []
+
+ def analyse_types(self, env):
+ return self
+
+ def may_be_none(self):
+ return False
+
+ def calculate_result_code(self):
+ return Naming.module_cname
+
+ def generate_result_code(self, code):
+ pass
+
+class DocstringRefNode(ExprNode):
+ # Extracts the docstring of the body element
+
+ subexprs = ['body']
+ type = py_object_type
+ is_temp = True
+
+ def __init__(self, pos, body):
+ ExprNode.__init__(self, pos)
+ assert body.type.is_pyobject
+ self.body = body
+
+ def analyse_types(self, env):
+ return self
+
+ def generate_result_code(self, code):
+ code.putln('%s = __Pyx_GetAttr(%s, %s); %s' % (
+ self.result(), self.body.result(),
+ code.intern_identifier(StringEncoding.EncodedString("__doc__")),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.result())
+
+
+
+#------------------------------------------------------------------------------------
+#
+# Runtime support code
+#
+#------------------------------------------------------------------------------------
+
+pyerr_occurred_withgil_utility_code= UtilityCode(
+proto = """
+static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
+""",
+impl = """
+static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
+ int err;
+ #ifdef WITH_THREAD
+ PyGILState_STATE _save = PyGILState_Ensure();
+ #endif
+ err = !!PyErr_Occurred();
+ #ifdef WITH_THREAD
+ PyGILState_Release(_save);
+ #endif
+ return err;
+}
+"""
+)
+
+#------------------------------------------------------------------------------------
+
+raise_unbound_local_error_utility_code = UtilityCode(
+proto = """
+static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
+""",
+impl = """
+static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
+ PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
+}
+""")
+
+raise_closure_name_error_utility_code = UtilityCode(
+proto = """
+static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);
+""",
+impl = """
+static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
+ PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
+}
+""")
+
+# Don't inline the function, it should really never be called in production
+raise_unbound_memoryview_utility_code_nogil = UtilityCode(
+proto = """
+static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);
+""",
+impl = """
+static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
+ #ifdef WITH_THREAD
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ #endif
+ __Pyx_RaiseUnboundLocalError(varname);
+ #ifdef WITH_THREAD
+ PyGILState_Release(gilstate);
+ #endif
+}
+""",
+requires = [raise_unbound_local_error_utility_code])
+
+#------------------------------------------------------------------------------------
+
+raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
+raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
+tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")