X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=Cython%2FCompiler%2FExprNodes.py;h=f42ab943c7029890a9f1326c8cdeeec8ef23476e;hb=73da9b353950aa68b4e059ca86ab58076af2103d;hp=eac472378cd352bd1e96889c4430c66e3ab40187;hpb=4df01a09a927c422fa102d2462a4a812f617f558;p=cython.git diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index eac47237..f42ab943 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4,16 +4,17 @@ import operator -from Errors import error, warning, InternalError +from Errors import error, warning, warn_once, InternalError from Errors import hold_errors, release_errors, held_errors, report_error -from Cython.Utils import UtilityCode +from Code import UtilityCode import StringEncoding import Naming import Nodes from Nodes import Node import PyrexTypes -from PyrexTypes import py_object_type, c_long_type, typecast, error_type -from Builtin import list_type, tuple_type, set_type, dict_type, unicode_type, bytes_type +from PyrexTypes import py_object_type, c_long_type, typecast, error_type, unspecified_type +from Builtin import list_type, tuple_type, set_type, dict_type, \ + unicode_type, str_type, bytes_type, type_type import Builtin import Symtab import Options @@ -32,6 +33,22 @@ class NotConstant(object): pass # just for the name not_a_constant = NotConstant() constant_value_not_set = object() +# error messages when coercing from key[0] to key[1] +find_coercion_error = { + # string related errors + (Builtin.unicode_type, Builtin.bytes_type) : "Cannot convert Unicode string to 'bytes' implicitly, encoding required.", + (Builtin.unicode_type, Builtin.str_type) : "Cannot convert Unicode string to 'str' implicitly. This is not portable and requires explicit encoding.", + (Builtin.unicode_type, PyrexTypes.c_char_ptr_type) : "Unicode objects do not support coercion to C types.", + (Builtin.bytes_type, Builtin.unicode_type) : "Cannot convert 'bytes' object to unicode implicitly, decoding required", + (Builtin.bytes_type, Builtin.str_type) : "Cannot convert 'bytes' object to str implicitly. This is not portable to Py3.", + (Builtin.str_type, Builtin.unicode_type) : "str objects do not support coercion to unicode, use a unicode string literal instead (u'')", + (Builtin.str_type, Builtin.bytes_type) : "Cannot convert 'str' to 'bytes' implicitly. This is not portable.", + (Builtin.str_type, PyrexTypes.c_char_ptr_type) : "'str' objects do not support coercion to C types (use 'bytes'?).", + (PyrexTypes.c_char_ptr_type, Builtin.unicode_type) : "Cannot convert 'char*' to unicode implicitly, decoding required", + (PyrexTypes.c_uchar_ptr_type, Builtin.unicode_type) : "Cannot convert 'char*' to unicode implicitly, decoding required", + }.get + + class ExprNode(Node): # subexprs [string] Class var holding names of subexpr node attrs # type PyrexType Type of the result @@ -40,12 +57,17 @@ class ExprNode(Node): # 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_ctype = None type = None + temp_code = None + old_temp = None # error checker for multiple frees etc. + use_managed_ref = True # can be set by optimisation transforms # The Analyse Expressions phase for expressions is split # into two sub-phases: @@ -100,20 +122,6 @@ class ExprNode(Node): # the LHS of an assignment or argument of a del # statement. Similar responsibilities to analyse_types. # - # allocate_temps - # - Call allocate_temps for all sub-nodes. - # - Call allocate_temp for this node. - # - If a temporary was allocated, call release_temp on - # all sub-expressions. - # - # allocate_target_temps - # - Call allocate_temps on sub-nodes and allocate any other - # temps used during assignment. - # - Fill in result_code with a C lvalue if needed. - # - If a rhs node is supplied, call release_temp on it. - # - Call release_temp on sub-nodes and release any other - # temps used during assignment. - # # target_code # Called by the default implementation of allocate_target_temps. # Should return a C lvalue for assigning to the node. The default @@ -180,6 +188,7 @@ class ExprNode(Node): saved_subexpr_nodes = None is_temp = 0 is_target = 0 + is_starred = 0 constant_result = constant_value_not_set @@ -222,10 +231,10 @@ class ExprNode(Node): return nodes def result(self): - if not self.is_temp or self.is_target: + if self.is_temp: + return self.temp_code + else: return self.calculate_result_code() - else: # i.e. self.is_temp: - return self.result_code def result_as(self, type = None): # Return the result code cast to the specified C type. @@ -240,10 +249,23 @@ class ExprNode(Node): # 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 result of this expression and store - # it in ``self.constant_result``. Does nothing by default, - # thus leaving ``self.constant_result`` unknown. + # 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 @@ -271,28 +293,24 @@ class ExprNode(Node): # checks whether it is a legal const expression, # and determines its value. self.analyse_types(env) - self.allocate_temps(env) - self.check_const() + return self.check_const() def analyse_expressions(self, env): # Convenience routine performing both the Type # Analysis and Temp Allocation phases for a whole # expression. self.analyse_types(env) - self.allocate_temps(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. self.analyse_target_types(env) - self.allocate_target_temps(env, rhs) def analyse_boolean_expression(self, env): # Analyse expression and coerce to a boolean. self.analyse_types(env) bool = self.coerce_to_boolean(env) - bool.allocate_temps(env) return bool def analyse_temp_boolean_expression(self, env): @@ -306,9 +324,29 @@ class ExprNode(Node): self.analyse_types(env) bool = self.coerce_to_boolean(env) temp_bool = bool.coerce_to_temp(env) - temp_bool.allocate_temps(env) return temp_bool + # --------------- Type Inference ----------------- + + def type_dependencies(self, env): + # Returns the list of entries whose types must be determined + # before the type of self can be infered. + 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") + # --------------- Type Analysis ------------------ def analyse_as_module(self, env): @@ -332,26 +370,31 @@ class ExprNode(Node): def analyse_target_types(self, env): 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.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") - def gil_check(self, env): - if env is not None and env.nogil and self.type.is_pyobject: - self.gil_error() - # ----------------- Result Allocation ----------------- def result_in_temp(self): @@ -361,65 +404,6 @@ class ExprNode(Node): # a subnode. return self.is_temp - def allocate_target_temps(self, env, rhs): - # Perform temp allocation for the LHS of an assignment. - if debug_temp_alloc: - print("%s Allocating target temps" % self) - self.allocate_subexpr_temps(env) - self.is_target = True - if rhs: - rhs.release_temp(env) - self.release_subexpr_temps(env) - - def allocate_temps(self, env, result = None): - # Allocate temporary variables for this node and - # all its sub-expressions. If a result is specified, - # this must be a temp node and the specified variable - # is used as the result instead of allocating a new - # one. - assert result is None, "deprecated, contact dagss if this triggers" - if debug_temp_alloc: - print("%s Allocating temps" % self) - self.allocate_subexpr_temps(env) - self.allocate_temp(env, result) - if self.is_temp: - self.release_subexpr_temps(env) - - def allocate_subexpr_temps(self, env): - # Allocate temporary variables for all sub-expressions - # of this node. - if debug_temp_alloc: - print("%s Allocating temps for: %s" % (self, self.subexprs)) - for node in self.subexpr_nodes(): - if node: - if debug_temp_alloc: - print("%s Allocating temps for %s" % (self, node)) - node.allocate_temps(env) - - def allocate_temp(self, env, result = None): - # If this node requires a temporary variable for its - # result, allocate one, otherwise set the result to - # a C code fragment. If a result is specified, - # this must be a temp node and the specified variable - # is used as the result instead of allocating a new - # one. - if debug_temp_alloc: - print("%s Allocating temp" % self) - if result: - if not self.is_temp: - raise InternalError("Result forced on non-temp node") - self.result_code = result - elif self.is_temp: - type = self.type - if not type.is_void: - if type.is_pyobject: - type = PyrexTypes.py_object_type - self.result_code = env.allocate_temp(type) - else: - self.result_code = None - if debug_temp_alloc: - print("%s Allocated result %s" % (self, self.result_code)) - def target_code(self): # Return code fragment for use as LHS of a C assignment. return self.calculate_result_code() @@ -431,23 +415,30 @@ class ExprNode(Node): # # Release temporaries used by LHS of an assignment. # self.release_subexpr_temps(env) - def release_temp(self, env): - # If this node owns a temporary result, release it, - # otherwise release results of its sub-expressions. - if self.is_temp: - if debug_temp_alloc: - print("%s Releasing result %s" % (self, self.result_code)) - env.release_temp(self.result_code) + 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 + self.temp_code = code.funcstate.allocate_temp( + type, manage_ref=self.use_managed_ref) else: - self.release_subexpr_temps(env) - - def release_subexpr_temps(self, env): - # Release the results of all sub-expressions of - # this node. - for node in self.subexpr_nodes(): - if node: - node.release_temp(env) - + self.temp_code = None + + def release_temp_result(self, code): + if not self.temp_code: + if self.old_temp: + raise RuntimeError("temp %s released multiple times in %s" % ( + self.old_temp, self.__class__.__name__)) + else: + raise RuntimeError("no temp, but release requested in %s" % ( + self.__class__.__name__)) + 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): @@ -458,12 +449,19 @@ class ExprNode(Node): def generate_evaluation_code(self, code): code.mark_pos(self.pos) + # 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) + + if self.is_temp: + self.allocate_temp_result(code) + self.generate_result_code(code) if self.is_temp: + # 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) @@ -475,14 +473,13 @@ class ExprNode(Node): self.not_implemented("generate_result_code") def generate_disposal_code(self, code): - # If necessary, generate code to dispose of - # temporary Python reference. if self.is_temp: if self.type.is_pyobject: code.put_decref_clear(self.result(), self.ctype()) 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. @@ -490,15 +487,12 @@ class ExprNode(Node): node.generate_disposal_code(code) def generate_post_assignment_code(self, code): - # Same as generate_disposal_code except that - # assignment will have absorbed a reference to - # the result if it is a Python object. if self.is_temp: if self.type.is_pyobject: code.putln("%s = 0;" % self.result()) else: self.generate_subexpr_disposal_code(code) - + def generate_assignment_code(self, rhs, code): # Stub method for nodes which are not legal as # the LHS of an assignment. An error will have @@ -512,9 +506,11 @@ class ExprNode(Node): pass def free_temps(self, code): - if not self.is_temp: + if self.is_temp: + if not self.type.is_void: + self.release_temp_result(code) + else: self.free_subexpr_temps(code) - # otherwise, already freed in generate_evaluation_code def free_subexpr_temps(self, code): for sub in self.subexpr_nodes(): @@ -536,11 +532,22 @@ class ExprNode(Node): # # 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 src_is_py_type = src_type.is_pyobject dst_is_py_type = dst_type.is_pyobject - + + if self.check_for_coercion_error(dst_type): + return self + if dst_type.is_pyobject: if not src.type.is_pyobject: src = CoerceToPyTypeNode(src, env) @@ -550,19 +557,32 @@ class ExprNode(Node): elif src.type.is_pyobject: src = CoerceFromPyTypeNode(dst_type, src, env) elif (dst_type.is_complex - and src_type != dst_type - and dst_type.assignable_from(src_type) - and not env.directives['c99_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. if not (str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)): - error(self.pos, "Cannot assign type '%s' to '%s'" % - (src.type, dst_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, 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) + 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) @@ -609,151 +629,29 @@ class ExprNode(Node): def as_cython_attribute(self): return None - -class RemoveAllocateTemps(type): - def __init__(cls, name, bases, dct): - super(RemoveAllocateTemps, cls).__init__(name, bases, dct) - def noop(self, env): pass - setattr(cls, 'allocate_temps', noop) - setattr(cls, 'allocate_temp', noop) - setattr(cls, 'release_temp', noop) - -class NewTempExprNode(ExprNode): - temp_code = None - old_temp = None # error checker for multiple frees etc. - -# Do not enable this unless you are trying to make all ExprNodes -# NewTempExprNodes (child nodes reached via recursion may not have -# transferred). -# __metaclass__ = RemoveAllocateTemps - - def result(self): - if self.is_temp: - return self.temp_code - else: - return self.calculate_result_code() - - def allocate_target_temps(self, env, rhs): - self.allocate_subexpr_temps(env) - self.is_target = True - if rhs: - rhs.release_temp(env) - self.release_subexpr_temps(env) - - def allocate_temps(self, env, result = None): - assert result is None, "deprecated, contact dagss if this triggers" - self.allocate_subexpr_temps(env) - if self.is_temp: - self.release_subexpr_temps(env) - - def allocate_temp(self, env, result = None): - assert result is None - - def release_temp(self, env): - if self.is_temp: - pass - else: - self.release_subexpr_temps(env) - - def allocate_temp_result(self, code): - if self.temp_code: - raise RuntimeError("Temp allocated multiple times") - type = self.type - if not type.is_void: - if type.is_pyobject: - type = PyrexTypes.py_object_type - self.temp_code = code.funcstate.allocate_temp( - type, manage_ref=True) - else: - self.temp_code = None - - def release_temp_result(self, code): - if not self.temp_code: - if self.old_temp: - raise RuntimeError("temp %s released multiple times in %s" % ( - self.old_temp, self.__class__.__name__)) - else: - raise RuntimeError("no temp, but release requested in %s" % ( - self.__class__.__name__)) - code.funcstate.release_temp(self.temp_code) - self.old_temp = self.temp_code - self.temp_code = None - - def generate_evaluation_code(self, code): - code.mark_pos(self.pos) - - # 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) - - if self.is_temp: - self.allocate_temp_result(code) - - self.generate_result_code(code) - if self.is_temp: - # 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_disposal_code(self, code): - if self.is_temp: - if self.type.is_pyobject: - code.put_decref_clear(self.result(), self.ctype()) - else: - # Already done if self.is_temp - self.generate_subexpr_disposal_code(code) - - def generate_post_assignment_code(self, code): - if self.is_temp: - if self.type.is_pyobject: - code.putln("%s = 0;" % self.result()) - else: - self.generate_subexpr_disposal_code(code) - - 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) - -# ExprNode = NewTempExprNode - class AtomicExprNode(ExprNode): # Abstract base class for expression nodes which have # no sub-expressions. subexprs = [] -class AtomicNewTempExprNode(NewTempExprNode): - # I do not dare to convert NameNode yet. This is now - # ancestor of all former AtomicExprNode except - # NameNode. Should be renamed to AtomicExprNode - # when done. - - # 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(AtomicNewTempExprNode): +class PyConstNode(AtomicExprNode): # Abstract base class for constant Python values. is_literal = 1 + type = py_object_type def is_simple(self): return 1 def analyse_types(self, env): - self.type = py_object_type + pass def calculate_result_code(self): return self.value @@ -768,6 +666,8 @@ class NoneNode(PyConstNode): value = "Py_None" constant_result = None + + nogil_check = None def compile_time_value(self, denv): return None @@ -783,13 +683,14 @@ class EllipsisNode(PyConstNode): return Ellipsis -class ConstNode(AtomicNewTempExprNode): +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 @@ -797,8 +698,11 @@ class ConstNode(AtomicNewTempExprNode): pass # Types are held in class variables def check_const(self): - pass + return True + def get_constant_c_result_code(self): + return self.calculate_result_code() + def calculate_result_code(self): return str(self.value) @@ -819,11 +723,15 @@ class BoolNode(ConstNode): def calculate_result_code(self): return str(int(self.value)) + 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 @@ -835,7 +743,7 @@ class CharNode(ConstNode): return ord(self.value) def calculate_result_code(self): - return "'%s'" % StringEncoding.escape_character(self.value) + return "'%s'" % StringEncoding.escape_char(self.value) class IntNode(ConstNode): @@ -848,28 +756,37 @@ class IntNode(ConstNode): type = PyrexTypes.c_long_type def coerce_to(self, dst_type, env): - if dst_type.is_numeric and not dst_type.is_complex: - self.type = PyrexTypes.c_long_type + if self.type is dst_type: return self - # Arrange for a Python version of the number to be pre-allocated - # when coercing to a Python type. + node = IntNode(self.pos, value=self.value, + unsigned=self.unsigned, longness=self.longness) + if dst_type.is_numeric and not dst_type.is_complex: + return node if dst_type.is_pyobject: - self.entry = env.get_py_num(self.value, self.longness) - self.type = PyrexTypes.py_object_type + node.type = PyrexTypes.py_object_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 ConstNode.coerce_to(self, dst_type, env) - + return ConstNode.coerce_to(node, dst_type, env) + def coerce_to_boolean(self, env): - self.type = PyrexTypes.c_bint_type - return self + return IntNode( + self.pos, value=self.value, + type = PyrexTypes.c_bint_type, + unsigned=self.unsigned, longness=self.longness) - def calculate_result_code(self): + def generate_evaluation_code(self, code): if self.type.is_pyobject: - return self.entry.cname + # pre-allocate a Python version of the number + self.result_code = code.get_py_num(self.value, self.longness) else: - return str(self.value) + self.unsigned + self.longness + self.result_code = self.get_constant_c_result_code() + + def get_constant_c_result_code(self): + return str(self.value) + self.unsigned + self.longness + + def calculate_result_code(self): + return self.result_code def calculate_constant_result(self): self.constant_result = int(self.value, 0) @@ -882,9 +799,7 @@ class FloatNode(ConstNode): type = PyrexTypes.c_double_type def calculate_constant_result(self): - # calculating float values is usually not a good idea - #self.constant_result = float(self.value) - pass + self.constant_result = float(self.value) def compile_time_value(self, denv): return float(self.value) @@ -901,17 +816,16 @@ class FloatNode(ConstNode): return strval -class StringNode(ConstNode): - # entry Symtab.Entry - +class BytesNode(ConstNode): + # A char* or bytes literal + # + # value BytesLiteral + type = PyrexTypes.c_char_ptr_type def compile_time_value(self, denv): return self.value - - def analyse_types(self, env): - self.entry = env.add_string_const(self.value) - + def analyse_as_type(self, env): type = PyrexTypes.parse_basic_type(self.value) if type is not None: @@ -923,97 +837,151 @@ class StringNode(ConstNode): sizeof_node.analyse_types(env) if isinstance(sizeof_node, SizeofTypeNode): return sizeof_node.arg_type - + + def can_coerce_to_char_literal(self): + return len(self.value) == 1 + def coerce_to(self, dst_type, env): - if dst_type == PyrexTypes.c_char_ptr_type: - self.type = PyrexTypes.c_char_ptr_type - return self - if dst_type.is_int: - if not self.type.is_pyobject and len(self.entry.init) == 1: - return CharNode(self.pos, value=self.value) - else: - error(self.pos, "Only coerce single-character ascii strings can be used as ints.") + if not self.can_coerce_to_char_literal(): + error(self.pos, "Only single-character strings can be coerced into ints.") return self - # Arrange for a Python version of the string to be pre-allocated - # when coercing to a Python type. - if dst_type.is_pyobject and not self.type.is_pyobject: - node = self.as_py_string_node(env) - else: - node = self + return CharNode(self.pos, value=self.value) + + node = BytesNode(self.pos, value=self.value) + if dst_type == PyrexTypes.c_char_ptr_type: + node.type = PyrexTypes.c_char_ptr_type + return node + elif dst_type == PyrexTypes.c_uchar_ptr_type: + node.type = PyrexTypes.c_char_ptr_type + return CastNode(node, PyrexTypes.c_uchar_ptr_type) + + if not self.type.is_pyobject: + if dst_type in (py_object_type, Builtin.bytes_type): + node.type = Builtin.bytes_type + elif dst_type.is_pyobject: + self.fail_assignment(dst_type) + return self + elif dst_type.is_pyobject and dst_type is not py_object_type: + self.check_for_coercion_error(dst_type, fail=True) + 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 as_py_string_node(self, env): - # Return a new StringNode with the same entry as this node + # Return a new BytesNode with the same value as this node # but whose type is a Python type instead of a C type. - entry = self.entry - env.add_py_string(entry) - return StringNode(self.pos, value = self.value, entry = entry, type = py_object_type) - - def calculate_result_code(self): + return BytesNode(self.pos, value = self.value, type = Builtin.bytes_type) + + def generate_evaluation_code(self, code): if self.type.is_pyobject: - return self.entry.pystring_cname + self.result_code = code.get_py_string_const(self.value) else: - return self.entry.cname + self.result_code = code.get_string_const(self.value) + + def get_constant_c_result_code(self): + return None # FIXME + + def calculate_result_code(self): + return self.result_code class UnicodeNode(PyConstNode): - # entry Symtab.Entry + # A Python unicode object + # + # value EncodedString type = unicode_type - def analyse_types(self, env): - self.entry = env.add_string_const(self.value) - env.add_py_string(self.entry) + def coerce_to(self, dst_type, env): + if dst_type is self.type: + pass + elif not dst_type.is_pyobject: + error(self.pos, "Unicode objects do not support coercion to C types.") + elif dst_type is not py_object_type: + if not self.check_for_coercion_error(dst_type): + self.fail_assignment(dst_type) + return self + + def generate_evaluation_code(self, code): + self.result_code = code.get_py_string_const(self.value) def calculate_result_code(self): - return self.entry.pystring_cname - - def _coerce_to(self, dst_type, env): - if not dst_type.is_pyobject: - node = StringNode(self.pos, entry = entry, type = py_object_type) - return ConstNode.coerce_to(node, dst_type, env) - else: - return self - # 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 self.result_code def compile_time_value(self, env): return self.value -class IdentifierStringNode(ConstNode): - # A Python string that behaves like an identifier, e.g. for - # keyword arguments in a call, or for imported names - type = PyrexTypes.py_object_type +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 + # is_identifier boolean - def analyse_types(self, env): - self.cname = env.intern_identifier(self.value) + type = str_type + is_identifier = None + + 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) + self.check_for_coercion_error(dst_type, fail=True) + + # this will be a unicode string in Py3, so make sure we can decode it + if self.value.encoding: + encoding = self.value.encoding + try: + self.value.decode(encoding) + except UnicodeDecodeError: + error(self.pos, "String decoding as '%s' failed. Consider using a byte string or unicode string explicitly, or adjust the source code encoding." % encoding) + + 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) + + def get_constant_c_result_code(self): + return None def calculate_result_code(self): - return self.cname + return self.result_code + + def compile_time_value(self, env): + return self.value + +class IdentifierStringNode(StringNode): + # A special str value that represents an identifier (bytes in Py2, + # unicode in Py3). + is_identifier = True -class LongNode(AtomicNewTempExprNode): + +class LongNode(AtomicExprNode): # Python long integer literal # # value string + type = py_object_type + def calculate_constant_result(self): self.constant_result = long(self.value) def compile_time_value(self, denv): return long(self.value) - - gil_message = "Constructing Python long int" def analyse_types(self, env): - self.type = py_object_type - self.gil_check(env) self.is_temp = 1 gil_message = "Constructing Python long int" @@ -1027,7 +995,7 @@ class LongNode(AtomicNewTempExprNode): code.put_gotref(self.py_result()) -class ImagNode(AtomicNewTempExprNode): +class ImagNode(AtomicExprNode): # Imaginary number literal # # value float imaginary part @@ -1044,24 +1012,22 @@ class ImagNode(AtomicNewTempExprNode): self.type.create_declaration_utility_code(env) def coerce_to(self, dst_type, env): - # Arrange for a Python version of the number to be pre-allocated - # when coercing to a Python type. + if self.type is dst_type: + return self + node = ImagNode(self.pos, value=self.value) if dst_type.is_pyobject: - self.is_temp = 1 - self.type = PyrexTypes.py_object_type - self.gil_check(env) + node.is_temp = 1 + node.type = PyrexTypes.py_object_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 AtomicNewTempExprNode.coerce_to(self, dst_type, env) + 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() - elif self.c99_complex: - return "%rj" % float(self.value) else: return "%s(0, %r)" % (self.type.from_parts, float(self.value)) @@ -1073,8 +1039,6 @@ class ImagNode(AtomicNewTempExprNode): float(self.value), code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) - else: - self.c99_complex = code.globalstate.directives['c99_complex'] class NewExprNode(AtomicExprNode): @@ -1116,15 +1080,16 @@ class NameNode(AtomicExprNode): # Reference to a local or global variable name. # # name string Python name of the variable - # # entry Entry Symbol table entry - # interned_cname string + # type_entry Entry For extension type names, the original type entry is_name = True is_cython_module = False cython_attribute = None lhs_of_first_assignment = False + is_used_as_rvalue = 0 entry = None + type_entry = None def create_analysed_rvalue(pos, env, entry): node = NameNode(pos) @@ -1136,11 +1101,37 @@ class NameNode(AtomicExprNode): create_analysed_rvalue = staticmethod(create_analysed_rvalue) + 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.entry,) + else: + return () + + def infer_type(self, env): + if self.entry is None: + self.entry = env.lookup(self.name) + if self.entry is None: + 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 + else: + 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 @@ -1158,7 +1149,7 @@ class NameNode(AtomicExprNode): node.entry = var_entry node.analyse_rvalue_entry(env) return node - return AtomicExprNode.coerce_to(self, dst_type, env) + return super(NameNode, self).coerce_to(dst_type, env) def analyse_as_module(self, env): # Try to interpret this as a reference to a cimported module. @@ -1200,7 +1191,11 @@ class NameNode(AtomicExprNode): if not self.entry: self.entry = env.lookup_here(self.name) if not self.entry: - self.entry = env.declare_var(self.name, py_object_type, self.pos) + if env.directives['infer_types'] != False: + type = unspecified_type + else: + type = py_object_type + self.entry = env.declare_var(self.name, type, self.pos) env.control_flow.set_state(self.pos, (self.name, 'initalized'), True) env.control_flow.set_state(self.pos, (self.name, 'source'), 'assignment') if self.entry.is_declared_generic: @@ -1214,6 +1209,14 @@ class NameNode(AtomicExprNode): if not self.entry: self.type = PyrexTypes.error_type return + entry = self.entry + if entry: + entry.used = 1 + if entry.type.is_buffer: + import Buffer + Buffer.used_buffer_aux_vars(entry) + if entry.utility_code: + env.use_utility_code(entry.utility_code) self.analyse_rvalue_entry(env) def analyse_target_types(self, env): @@ -1239,8 +1242,17 @@ class NameNode(AtomicExprNode): self.is_temp = 0 else: self.is_temp = 1 + self.is_used_as_rvalue = 1 env.use_utility_code(get_name_interned_utility_code) - self.gil_check(env) + + def nogil_check(self, env): + if self.is_used_as_rvalue: + entry = self.entry + if entry.is_builtin: + # if not Options.cache_builtins: # cached builtins are ok + self.gil_error() + elif entry.is_pyglobal: + self.gil_error() gil_message = "Accessing Python global or builtin" @@ -1250,16 +1262,14 @@ class NameNode(AtomicExprNode): entry = self.entry type = entry.type self.type = type - if entry.is_pyglobal or entry.is_builtin: - assert type.is_pyobject, "Python global or builtin not a Python object" - self.interned_cname = self.entry.interned_cname = \ - env.intern_identifier(self.entry.name) def check_identifier_kind(self): - #print "NameNode.check_identifier_kind:", self.entry.name ### - #print self.entry.__dict__ ### + # 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 - #entry.used = 1 + if entry.is_type and entry.type.is_extension_type: + self.type_entry = entry if not (entry.is_const or entry.is_variable or entry.is_builtin or entry.is_cfunction or entry.is_cpp_class): @@ -1267,8 +1277,8 @@ class NameNode(AtomicExprNode): self.entry = self.entry.as_variable else: error(self.pos, - "'%s' is not a constant, variable or function identifier" % self.name) - + "'%s' is not a constant, variable or function identifier" % self.name) + def is_simple(self): # If it's not a C variable, it'll be in a temp. return 1 @@ -1280,11 +1290,15 @@ class NameNode(AtomicExprNode): entry = self.entry if entry is not None and not (entry.is_const or entry.is_cfunction or entry.is_builtin): 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 \ @@ -1293,20 +1307,9 @@ class NameNode(AtomicExprNode): def is_ephemeral(self): # Name nodes are never ephemeral, even if the - # result is in a temporary. - return 0 - - def allocate_temp(self, env, result = None): - AtomicExprNode.allocate_temp(self, env, result) - entry = self.entry - if entry: - entry.used = 1 - if entry.type.is_buffer: - import Buffer - Buffer.used_buffer_aux_vars(entry) - if entry.utility_code: - env.use_utility_code(entry.utility_code) - + # result is in a temporary. + return 0 + def calculate_result_code(self): entry = self.entry if not entry: @@ -1321,6 +1324,8 @@ class NameNode(AtomicExprNode): if entry.is_builtin and Options.cache_builtins: return # Lookup already cached elif entry.is_pyglobal or entry.is_builtin: + 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 @@ -1329,7 +1334,7 @@ class NameNode(AtomicExprNode): '%s = __Pyx_GetName(%s, %s); %s' % ( self.result(), namespace, - self.interned_cname, + interned_cname, code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) @@ -1356,6 +1361,8 @@ class NameNode(AtomicExprNode): # 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 @@ -1363,7 +1370,7 @@ class NameNode(AtomicExprNode): code.put_error_if_neg(self.pos, 'PyDict_SetItem(%s->tp_dict, %s, %s)' % ( namespace, - self.interned_cname, + interned_cname, rhs.py_result())) rhs.generate_disposal_code(code) rhs.free_temps(code) @@ -1374,7 +1381,7 @@ class NameNode(AtomicExprNode): code.put_error_if_neg(self.pos, 'PyObject_SetAttr(%s, %s, %s)' % ( namespace, - self.interned_cname, + interned_cname, rhs.py_result())) if debug_disposal_code: print("NameNode.generate_assignment_code:") @@ -1393,14 +1400,15 @@ class NameNode(AtomicExprNode): self.generate_acquire_buffer(rhs, code) if self.type.is_pyobject: - rhs.make_owned_reference(code) #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 entry.is_cglobal: - code.put_gotref(self.py_result()) - if not self.lhs_of_first_assignment: + if self.use_managed_ref: + rhs.make_owned_reference(code) + if entry.is_cglobal: + code.put_gotref(self.py_result()) + if self.use_managed_ref and not self.lhs_of_first_assignment: if entry.is_local and not Options.init_local_none: initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos) if initalized is True: @@ -1409,8 +1417,9 @@ class NameNode(AtomicExprNode): code.put_xdecref(self.result(), self.ctype()) else: code.put_decref(self.result(), self.ctype()) - if entry.is_cglobal: - code.put_giveref(rhs.py_result()) + if self.use_managed_ref: + if entry.is_cglobal: + code.put_giveref(rhs.py_result()) code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype()))) if debug_disposal_code: print("NameNode.generate_assignment_code:") @@ -1464,13 +1473,13 @@ class BackquoteNode(ExprNode): # # arg ExprNode + type = py_object_type + subexprs = ['arg'] def analyse_types(self, env): self.arg.analyse_types(env) self.arg = self.arg.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) self.is_temp = 1 gil_message = "Backquote expression" @@ -1493,19 +1502,19 @@ class ImportNode(ExprNode): # Implements result = # __import__(module_name, globals(), None, name_list) # - # module_name IdentifierStringNode dotted name of module - # name_list ListNode or None list of names to be imported + # module_name StringNode dotted name of module + # name_list ListNode or None list of names to be imported + + type = py_object_type subexprs = ['module_name', 'name_list'] - + def analyse_types(self, env): self.module_name.analyse_types(env) self.module_name = self.module_name.coerce_to_pyobject(env) if self.name_list: self.name_list.analyse_types(env) self.name_list.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) self.is_temp = 1 env.use_utility_code(import_utility_code) @@ -1525,7 +1534,7 @@ class ImportNode(ExprNode): code.put_gotref(self.py_result()) -class IteratorNode(NewTempExprNode): +class IteratorNode(ExprNode): # Used as part of for statement implementation. # # allocate_counter_temp/release_counter_temp needs to be called @@ -1535,13 +1544,13 @@ class IteratorNode(NewTempExprNode): # # sequence ExprNode + type = py_object_type + subexprs = ['sequence'] def analyse_types(self, env): self.sequence.analyse_types(env) self.sequence = self.sequence.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) self.is_temp = 1 gil_message = "Iterating over Python object" @@ -1555,22 +1564,24 @@ class IteratorNode(NewTempExprNode): def generate_result_code(self, code): is_builtin_sequence = self.sequence.type is list_type or \ - self.sequence.type is tuple_type + self.sequence.type is tuple_type + may_be_a_sequence = is_builtin_sequence or not self.sequence.type.is_builtin_type if is_builtin_sequence: code.putln( "if (likely(%s != Py_None)) {" % self.sequence.py_result()) - else: + elif may_be_a_sequence: code.putln( "if (PyList_CheckExact(%s) || PyTuple_CheckExact(%s)) {" % ( self.sequence.py_result(), self.sequence.py_result())) - code.putln( - "%s = 0; %s = %s; __Pyx_INCREF(%s);" % ( - self.counter_cname, - self.result(), - self.sequence.py_result(), - self.result())) - code.putln("} else {") + if may_be_a_sequence: + code.putln( + "%s = 0; %s = %s; __Pyx_INCREF(%s);" % ( + self.counter_cname, + self.result(), + self.sequence.py_result(), + self.result())) + code.putln("} else {") if is_builtin_sequence: code.putln( 'PyErr_SetString(PyExc_TypeError, "\'NoneType\' object is not iterable"); %s' % @@ -1582,10 +1593,11 @@ class IteratorNode(NewTempExprNode): self.sequence.py_result(), code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) - code.putln("}") + if may_be_a_sequence: + code.putln("}") -class NextNode(AtomicNewTempExprNode): +class NextNode(AtomicExprNode): # Used as part of for statement implementation. # Implements result = iterator.next() # Created during analyse_types phase. @@ -1593,19 +1605,23 @@ class NextNode(AtomicNewTempExprNode): # # iterator ExprNode + type = py_object_type + def __init__(self, iterator, env): self.pos = iterator.pos self.iterator = iterator - self.type = py_object_type self.is_temp = 1 def generate_result_code(self, code): - if self.iterator.sequence.type is list_type: + sequence_type = self.iterator.sequence.type + if sequence_type is list_type: type_checks = [(list_type, "List")] - elif self.iterator.sequence.type is tuple_type: + elif sequence_type is tuple_type: type_checks = [(tuple_type, "Tuple")] - else: + elif not sequence_type.is_builtin_type: type_checks = [(list_type, "List"), (tuple_type, "Tuple")] + else: + type_checks = [] for py_type, prefix in type_checks: if len(type_checks) > 1: @@ -1644,14 +1660,17 @@ class NextNode(AtomicNewTempExprNode): code.putln("}") -class ExcValueNode(AtomicNewTempExprNode): +class ExcValueNode(AtomicExprNode): # Node created during analyse_types phase # of an ExceptClauseNode to fetch the current # exception value. - def __init__(self, pos, env, var): + type = py_object_type + + def __init__(self, pos, env): ExprNode.__init__(self, pos) - self.type = py_object_type + + def set_var(self, var): self.var = var def calculate_result_code(self): @@ -1665,8 +1684,13 @@ class ExcValueNode(AtomicNewTempExprNode): class TempNode(ExprNode): - # Node created during analyse_types phase - # of some nodes to hold a temporary value. + # 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 = [] @@ -1683,6 +1707,26 @@ class TempNode(ExprNode): 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. @@ -1690,6 +1734,25 @@ class PyTempNode(TempNode): def __init__(self, pos, env): TempNode.__init__(self, pos, PyrexTypes.py_object_type, env) +class RawCNameExprNode(ExprNode): + subexprs = [] + + def __init__(self, pos, type=None): + self.pos = pos + self.type = type + + def analyse_types(self, env): + return self.type + + def set_cname(self, cname): + self.cname = cname + + def result(self): + return self.cname + + def generate_result_code(self, code): + pass + #------------------------------------------------------------------- # @@ -1752,6 +1815,19 @@ class IndexNode(ExprNode): return PyrexTypes.CArrayType(base_type, int(self.index.compile_time_value(env))) return None + def type_dependencies(self, env): + return self.base.type_dependencies(env) + + def infer_type(self, env): + if isinstance(self.base, (StringNode, UnicodeNode)): # FIXME: BytesNode? + return py_object_type + base_type = self.base.infer_type(env) + if base_type.is_ptr or base_type.is_array: + return base_type.base_type + else: + # TODO: Handle buffers (hopefully without too much redundancy). + return py_object_type + def analyse_types(self, env): self.analyse_base_and_index_types(env, getting = 1) @@ -1769,8 +1845,14 @@ class IndexNode(ExprNode): self.is_buffer_access = False 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 + # Handle the case where base is a literal char* (and we expect a string, not an int) - if isinstance(self.base, StringNode): + if isinstance(self.base, BytesNode): self.base = self.base.coerce_to_pyobject(env) skip_child_analysis = False @@ -1817,11 +1899,18 @@ class IndexNode(ExprNode): self.original_index_type = self.index.type if self.base.type.is_pyobject: if self.index.type.is_int: + if (not setting + and (self.base.type is list_type or self.base.type is tuple_type) + and (not self.index.type.signed or isinstance(self.index, IntNode) and int(self.index.value) >= 0) + and not env.directives['boundscheck']): + self.is_temp = 0 + else: + self.is_temp = 1 self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env) else: self.index = self.index.coerce_to_pyobject(env) + self.is_temp = 1 self.type = py_object_type - self.is_temp = 1 else: if self.base.type.is_ptr or self.base.type.is_array: self.type = self.base.type.base_type @@ -1837,24 +1926,21 @@ class IndexNode(ExprNode): error(self.pos, "Invalid index type '%s'" % self.index.type) - self.gil_check(env) - gil_message = "Indexing Python object" - def gil_check(self, env): - if self.is_buffer_access and env.nogil: + def nogil_check(self, env): + if self.is_buffer_access: if env.directives['boundscheck']: error(self.pos, "Cannot check buffer index bounds without gil; use boundscheck(False) directive") return elif self.type.is_pyobject: error(self.pos, "Cannot access buffer with object dtype without gil") return - super(IndexNode, self).gil_check(env) + super(IndexNode, self).nogil_check(env) def check_const_addr(self): - self.base.check_const_addr() - self.index.check_const() + return self.base.check_const_addr() and self.index.check_const() def is_lvalue(self): return 1 @@ -1862,6 +1948,10 @@ class IndexNode(ExprNode): def calculate_result_code(self): if self.is_buffer_access: return "(*%s)" % self.buffer_ptr_code + elif self.base.type is list_type: + return "PyList_GET_ITEM(%s, %s)" % (self.base.result(), self.index.result()) + elif self.base.type is tuple_type: + return "PyTuple_GET_ITEM(%s, %s)" % (self.base.result(), self.index.result()) else: return "(%s[%s])" % ( self.base.result(), self.index.result()) @@ -1909,7 +1999,7 @@ class IndexNode(ExprNode): # is_temp is True, so must pull out value and incref it. code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code)) code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result()) - elif self.type.is_pyobject: + elif self.type.is_pyobject and self.is_temp: if self.index.type.is_int: index_code = self.index.result() if self.base.type is list_type: @@ -2052,6 +2142,15 @@ class SliceIndexNode(ExprNode): subexprs = ['base', 'start', 'stop'] + def infer_type(self, env): + base_type = self.base.infer_type(env) + if base_type.is_string: + return bytes_type + elif base_type in (bytes_type, str_type, unicode_type, + list_type, tuple_type): + return base_type + return py_object_type + def calculate_constant_result(self): self.constant_result = self.base.constant_result[ self.start.constant_result : self.stop.constant_result] @@ -2105,9 +2204,9 @@ class SliceIndexNode(ExprNode): self.start = self.start.coerce_to(c_int, env) if self.stop: self.stop = self.stop.coerce_to(c_int, env) - self.gil_check(env) self.is_temp = 1 + nogil_check = Node.gil_error gil_message = "Slicing Python object" def generate_result_code(self, code): @@ -2262,6 +2361,9 @@ class SliceNode(ExprNode): # start ExprNode # stop ExprNode # step ExprNode + + type = py_object_type + is_temp = 1 def calculate_constant_result(self): self.constant_result = self.base.constant_result[ @@ -2293,9 +2395,6 @@ class SliceNode(ExprNode): self.start = self.start.coerce_to_pyobject(env) self.stop = self.stop.coerce_to_pyobject(env) self.step = self.step.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) - self.is_temp = 1 gil_message = "Constructing Python slice object" @@ -2310,19 +2409,15 @@ class SliceNode(ExprNode): code.put_gotref(self.py_result()) -class CallNode(NewTempExprNode): - def gil_check(self, env): - # Make sure we're not in a nogil environment - if env.nogil: - error(self.pos, "Calling gil-requiring function without gil") - +class CallNode(ExprNode): + 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=IdentifierStringNode(pos=arg.pos, value=member.name), value=arg)) + 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 @@ -2331,6 +2426,15 @@ class CallNode(NewTempExprNode): self.coerce_to(type, env) return True + def nogil_check(self, env): + func_type = self.function_type() + if func_type.is_pyobject: + self.gil_error() + elif not getattr(func_type, 'nogil', False): + self.gil_error() + + gil_message = "Calling gil-requiring function" + class SimpleCallNode(CallNode): # Function call without keyword, * or ** args. @@ -2359,6 +2463,30 @@ class SimpleCallNode(CallNode): except Exception, e: self.compile_time_value_error(e) + 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 infer_type(self, env): + function = self.function + func_type = function.infer_type(env) + if func_type.is_ptr: + func_type = func_type.base_type + if func_type.is_cfunction: + 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 analyse_as_type(self, env): attr = self.function.as_cython_attribute() if attr == 'pointer': @@ -2380,15 +2508,6 @@ class SimpleCallNode(CallNode): function = self.function function.is_called = 1 self.function.analyse_types(env) - if function.is_attribute and function.is_py_attr and \ - function.attribute == "append" and len(self.args) == 1: - # L.append(x) is almost always applied to a list - self.py_func = self.function - self.function = NameNode(pos=self.function.pos, name="__Pyx_PyObject_Append") - self.function.analyse_types(env) - self.self = self.py_func.obj - function.obj = CloneNode(self.self) - env.use_utility_code(append_utility_code) 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'. @@ -2399,8 +2518,26 @@ class SimpleCallNode(CallNode): self.arg_tuple = TupleNode(self.pos, args = self.args) self.arg_tuple.analyse_types(env) self.args = None - self.type = py_object_type - self.gil_check(env) + if func_type is Builtin.type_type and function.is_name and \ + function.entry and \ + function.entry.is_builtin and \ + function.entry.name in Builtin.types_that_construct_their_instance: + # calling a builtin type that returns a specific object type + if function.entry.name == 'float': + # the following will come true later on in a transform + self.type = PyrexTypes.c_double_type + self.result_ctype = PyrexTypes.c_double_type + else: + self.type = Builtin.builtin_types[function.entry.name] + self.result_ctype = py_object_type + elif function.is_name and function.type_entry: + # We are calling an extension type constructor. As + # long as we do not support __new__(), the result type + # is clear + self.type = function.type_entry.type + self.result_ctype = py_object_type + else: + self.type = py_object_type self.is_temp = 1 else: for arg in self.args: @@ -2435,6 +2572,9 @@ class SimpleCallNode(CallNode): max_nargs = len(func_type.args) expected_nargs = max_nargs - func_type.optional_arg_count actual_nargs = len(self.args) + if func_type.optional_arg_count and expected_nargs != actual_nargs: + self.has_optional_args = 1 + self.is_temp = 1 # Coerce arguments for i in range(min(max_nargs, actual_nargs)): formal_type = func_type.args[i].type @@ -2458,16 +2598,7 @@ class SimpleCallNode(CallNode): if func_type.exception_check == '+': if func_type.exception_value is None: env.use_utility_code(cpp_exception_utility_code) - # Check gil - if not func_type.nogil: - self.gil_check(env) - if func_type.optional_arg_count and expected_nargs != actual_nargs: - self.has_optional_args = 1 - self.is_temp = 1 - self.opt_arg_struct = env.allocate_temp(func_type.op_arg_struct.base_type) - env.release_temp(self.opt_arg_struct) - return 1 - + def calculate_result_code(self): return self.c_call_code() @@ -2499,9 +2630,6 @@ class SimpleCallNode(CallNode): arg_list_code.append(actual_arg.result()) result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code)) -# if self.wrapper_call or \ -# self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable: -# result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result) return result def generate_result_code(self, code): @@ -2519,6 +2647,8 @@ class SimpleCallNode(CallNode): 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", @@ -2527,10 +2657,10 @@ class SimpleCallNode(CallNode): for formal_arg, actual_arg in args[expected_nargs:actual_nargs]: code.putln("%s.%s = %s;" % ( self.opt_arg_struct, - formal_arg.name, + func_type.opt_arg_cname(formal_arg.name), actual_arg.result_as(formal_arg.type))) exc_checks = [] - if self.type.is_pyobject: + if self.type.is_pyobject and self.is_temp: exc_checks.append("!%s" % self.result()) else: exc_val = func_type.exception_value @@ -2554,7 +2684,9 @@ class SimpleCallNode(CallNode): if func_type.exception_value is None: raise_py_exception = "__Pyx_CppExn2PyErr()" elif func_type.exception_value.type.is_pyobject: - raise_py_exception = 'PyErr_SetString(%s, "")' % func_type.exception_value.entry.cname + raise_py_exception = ' try { throw; } catch(const std::exception& exn) { PyErr_SetString(%s, exn.what()); } catch(...) { PyErr_SetNone(%s); }' % ( + func_type.exception_value.entry.cname, + func_type.exception_value.entry.cname) else: raise_py_exception = '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.")' % func_type.exception_value.entry.cname code.putln( @@ -2571,7 +2703,43 @@ class SimpleCallNode(CallNode): 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 PythonCapiFunctionNode(ExprNode): + subexprs = [] + def __init__(self, pos, py_name, cname, func_type, utility_code = None): + self.pos = pos + self.name = py_name + self.cname = cname + self.type = func_type + self.utility_code = utility_code + + def analyse_types(self, env): + pass + + 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) + + 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 GeneralCallNode(CallNode): # General Python function call, including keyword, @@ -2582,8 +2750,12 @@ class GeneralCallNode(CallNode): # keyword_args ExprNode or None Dict of keyword arguments # starstar_arg ExprNode or None Dict of extra keyword args + type = py_object_type + subexprs = ['function', 'positional_args', 'keyword_args', 'starstar_arg'] + 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) @@ -2611,8 +2783,11 @@ class GeneralCallNode(CallNode): if self.starstar_arg: self.starstar_arg.analyse_types(env) if not self.function.type.is_pyobject: + if self.function.type.is_error: + self.type = error_type + return if hasattr(self.function, 'entry') and not self.function.entry.as_variable: - error(self.pos, "Keyword arguments not allowed in cdef functions.") + error(self.pos, "Keyword and starred arguments not allowed in cdef functions.") else: self.function = self.function.coerce_to_pyobject(env) self.positional_args = \ @@ -2620,11 +2795,18 @@ class GeneralCallNode(CallNode): if self.starstar_arg: self.starstar_arg = \ self.starstar_arg.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) + function = self.function + if function.is_name and function.type_entry: + # We are calling an extension type constructor. As long + # as we do not support __new__(), the result type is clear + self.type = function.type_entry.type + self.result_ctype = py_object_type + else: + self.type = py_object_type self.is_temp = 1 def generate_result_code(self, code): + if self.type.is_error: return if self.keyword_args and self.starstar_arg: code.put_error_if_neg(self.pos, "PyDict_Update(%s, %s)" % ( @@ -2676,9 +2858,9 @@ class AsTupleNode(ExprNode): self.arg.analyse_types(env) self.arg = self.arg.coerce_to_pyobject(env) self.type = tuple_type - self.gil_check(env) self.is_temp = 1 + nogil_check = Node.gil_error gil_message = "Constructing Python tuple" def generate_result_code(self, code): @@ -2690,7 +2872,7 @@ class AsTupleNode(ExprNode): code.put_gotref(self.py_result()) -class AttributeNode(NewTempExprNode): +class AttributeNode(ExprNode): # obj.attribute # # obj ExprNode @@ -2704,7 +2886,6 @@ class AttributeNode(NewTempExprNode): # member string C name of struct member # is_called boolean Function call is being done on result # entry Entry Symbol table entry of attribute - # interned_attr_cname string C name of interned attribute name is_attribute = 1 subexprs = ['obj'] @@ -2748,6 +2929,18 @@ class AttributeNode(NewTempExprNode): return getattr(obj, attr) except Exception, e: self.compile_time_value_error(e) + + def type_dependencies(self, env): + return self.obj.type_dependencies(env) + + def infer_type(self, env): + if self.analyse_as_cimported_attribute(env, 0): + return self.entry.type + elif self.analyse_as_unbound_cmethod(env): + return self.entry.type + else: + self.analyse_attribute(env, obj_type = self.obj.infer_type(env)) + return self.type def analyse_target_declaration(self, env): pass @@ -2851,13 +3044,17 @@ class AttributeNode(NewTempExprNode): self.is_temp = 1 self.result_ctype = py_object_type - def analyse_attribute(self, env): + def analyse_attribute(self, env, obj_type = None): # Look up attribute and set self.type and self.member. self.is_py_attr = 0 self.member = self.attribute - if self.obj.type.is_string: - self.obj = self.obj.coerce_to_pyobject(env) - obj_type = self.obj.type + if obj_type is None: + if self.obj.type.is_string: + self.obj = self.obj.coerce_to_pyobject(env) + obj_type = self.obj.type + else: + if obj_type.is_string: + obj_type = py_object_type if obj_type.is_ptr or obj_type.is_array: obj_type = obj_type.base_type self.op = "->" @@ -2896,21 +3093,25 @@ class AttributeNode(NewTempExprNode): # 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) - - def analyse_as_python_attribute(self, env): - obj_type = self.obj.type + self.analyse_as_python_attribute(env, obj_type) + + def analyse_as_python_attribute(self, env, obj_type = None): + if obj_type is None: + obj_type = self.obj.type self.member = self.attribute - if obj_type.is_pyobject: - self.type = py_object_type - self.is_py_attr = 1 - self.interned_attr_cname = env.intern_identifier(self.attribute) - self.gil_check(env) - else: - if not obj_type.is_error: - error(self.pos, - "Object of type '%s' has no attribute '%s'" % - (obj_type, 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 obj_type.can_coerce_to_pyobject(env): + 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 nogil_check(self, env): + if self.is_py_attr: + self.gil_error() gil_message = "Accessing Python attribute" @@ -2947,17 +3148,18 @@ class AttributeNode(NewTempExprNode): else: return self.member elif obj.type.is_complex: - return "__Pyx_%s_PART(%s)" % (self.member.upper(), obj_code) + return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code) else: return "%s%s%s" % (obj_code, self.op, self.member) def generate_result_code(self, code): + interned_attr_cname = code.intern_identifier(self.attribute) if self.is_py_attr: code.putln( '%s = PyObject_GetAttr(%s, %s); %s' % ( self.result(), self.obj.py_result(), - self.interned_attr_cname, + interned_attr_cname, code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) else: @@ -2969,15 +3171,21 @@ class AttributeNode(NewTempExprNode): self.put_nonecheck(code) def generate_assignment_code(self, rhs, code): + interned_attr_cname = code.intern_identifier(self.attribute) self.obj.generate_evaluation_code(code) if self.is_py_attr: code.put_error_if_neg(self.pos, 'PyObject_SetAttr(%s, %s, %s)' % ( self.obj.py_result(), - self.interned_attr_cname, + interned_attr_cname, 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()))) else: if (self.obj.type.is_extension_type and self.needs_none_check @@ -2985,7 +3193,7 @@ class AttributeNode(NewTempExprNode): self.put_nonecheck(code) select_code = self.result() - if self.type.is_pyobject: + 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) @@ -3001,12 +3209,13 @@ class AttributeNode(NewTempExprNode): self.obj.free_temps(code) def generate_deletion_code(self, code): + interned_attr_cname = code.intern_identifier(self.attribute) self.obj.generate_evaluation_code(code) if self.is_py_attr: code.put_error_if_neg(self.pos, 'PyObject_DelAttr(%s, %s)' % ( self.obj.py_result(), - self.interned_attr_cname)) + interned_attr_cname)) else: error(self.pos, "Cannot delete C attribute of extension type") self.obj.generate_disposal_code(code) @@ -3021,7 +3230,7 @@ class AttributeNode(NewTempExprNode): def put_nonecheck(self, code): code.globalstate.use_utility_code(raise_noneattr_error_utility_code) code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.obj.result_as(PyrexTypes.py_object_type)) - code.putln("__Pyx_RaiseNoneAttributeError(\"%s\");" % self.attribute.encode("UTF-8")) # todo: fix encoding + code.putln("__Pyx_RaiseNoneAttributeError(\"%s\");" % self.attribute) code.putln(code.error_goto(self.pos)) code.putln("}") @@ -3032,7 +3241,51 @@ class AttributeNode(NewTempExprNode): # #------------------------------------------------------------------- -class SequenceNode(NewTempExprNode): +class StarredTargetNode(ExprNode): + # A starred expression like "*a" + # + # This is only allowed in sequence assignment targets such as + # + # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4] + # + # and will be removed 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 + + def __init__(self, pos, target): + self.pos = pos + self.target = target + + def analyse_declarations(self, env): + error(self.pos, "can use starred expression only as assignment target") + self.target.analyse_declarations(env) + + def analyse_types(self, env): + error(self.pos, "can use starred expression only as assignment target") + self.target.analyse_types(env) + self.type = self.target.type + + def analyse_target_declaration(self, env): + self.target.analyse_target_declaration(env) + + def analyse_target_types(self, env): + self.target.analyse_target_types(env) + self.type = self.target.type + + 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. # @@ -3045,11 +3298,26 @@ class SequenceNode(NewTempExprNode): is_sequence_constructor = 1 unpacked_items = None - + 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) @@ -3059,7 +3327,6 @@ class SequenceNode(NewTempExprNode): if not skip_children: arg.analyse_types(env) self.args[i] = arg.coerce_to_pyobject(env) self.type = py_object_type - self.gil_check(env) self.is_temp = 1 def analyse_target_types(self, env): @@ -3068,42 +3335,38 @@ class SequenceNode(NewTempExprNode): self.coerced_unpacked_items = [] for arg in self.args: arg.analyse_target_types(env) + if arg.is_starred: + if not arg.type.assignable_from(Builtin.list_type): + error(arg.pos, + "starred target must have Python object (list) type") + if arg.type is py_object_type: + arg.type = Builtin.list_type unpacked_item = PyTempNode(self.pos, env) coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env) self.unpacked_items.append(unpacked_item) self.coerced_unpacked_items.append(coerced_unpacked_item) self.type = py_object_type - env.use_utility_code(unpacking_utility_code) - - def allocate_target_temps(self, env, rhs): - self.iterator.allocate_temps(env) - for node in self.coerced_unpacked_items: - node.allocate_temps(env) - #arg.release_target_temp(env) - #node.release_temp(env) - for arg in self.args: - arg.allocate_target_temps(env, None) - if rhs: - rhs.release_temp(env) - self.iterator.release_temp(env) - for node in self.coerced_unpacked_items: - node.release_temp(env) -# def release_target_temp(self, env): -# #for arg in self.args: -# # arg.release_target_temp(env) -# #for node in self.coerced_unpacked_items: -# # node.release_temp(env) -# self.iterator.release_temp(env) - def generate_result_code(self, code): self.generate_operation_code(code) def generate_assignment_code(self, rhs, code): + 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) + + 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. + code.globalstate.use_utility_code(unpacking_utility_code) + if rhs.type is tuple_type: tuple_check = "likely(%s != Py_None)" else: @@ -3114,6 +3377,8 @@ class SequenceNode(NewTempExprNode): rhs.py_result(), len(self.args))) code.putln("PyObject* tuple = %s;" % rhs.py_result()) + for item in self.unpacked_items: + item.allocate(code) for i in range(len(self.args)): item = self.unpacked_items[i] code.put( @@ -3137,6 +3402,7 @@ class SequenceNode(NewTempExprNode): rhs.py_result(), len(self.args))) code.putln(code.error_goto(self.pos)) else: + self.iterator.allocate(code) code.putln( "%s = PyObject_GetIter(%s); %s" % ( self.iterator.result(), @@ -3164,14 +3430,81 @@ class SequenceNode(NewTempExprNode): print("...generating disposal code for %s" % self.iterator) self.iterator.generate_disposal_code(code) self.iterator.free_temps(code) + self.iterator.release(code) for i in range(len(self.args)): self.args[i].generate_assignment_code( self.coerced_unpacked_items[i], code) code.putln("}") - rhs.free_temps(code) - + + def generate_starred_assignment_code(self, rhs, code): + code.globalstate.use_utility_code(unpacking_utility_code) + + for i, arg in enumerate(self.args): + if arg.is_starred: + starred_target = self.unpacked_items[i] + fixed_args_left = self.args[:i] + fixed_args_right = self.args[i+1:] + break + + self.iterator.allocate(code) + code.putln( + "%s = PyObject_GetIter(%s); %s" % ( + self.iterator.result(), + rhs.py_result(), + code.error_goto_if_null(self.iterator.result(), self.pos))) + code.put_gotref(self.iterator.py_result()) + rhs.generate_disposal_code(code) + + for item in self.unpacked_items: + item.allocate(code) + for i in range(len(fixed_args_left)): + item = self.unpacked_items[i] + unpack_code = "__Pyx_UnpackItem(%s, %d)" % ( + self.iterator.py_result(), i) + code.putln( + "%s = %s; %s" % ( + item.result(), + typecast(item.ctype(), py_object_type, unpack_code), + code.error_goto_if_null(item.result(), self.pos))) + code.put_gotref(item.py_result()) + value_node = self.coerced_unpacked_items[i] + value_node.generate_evaluation_code(code) + + target_list = starred_target.result() + code.putln("%s = PySequence_List(%s); %s" % ( + target_list, self.iterator.py_result(), + code.error_goto_if_null(target_list, self.pos))) + code.put_gotref(target_list) + if fixed_args_right: + code.globalstate.use_utility_code(raise_need_more_values_to_unpack) + unpacked_right_args = self.unpacked_items[-len(fixed_args_right):] + code.putln("if (unlikely(PyList_GET_SIZE(%s) < %d)) {" % ( + (target_list, len(unpacked_right_args)))) + code.put("__Pyx_RaiseNeedMoreValuesError(%d+PyList_GET_SIZE(%s)); %s" % ( + len(fixed_args_left), target_list, + code.error_goto(self.pos))) + code.putln('}') + for i, (arg, coerced_arg) in enumerate(zip(unpacked_right_args[::-1], + self.coerced_unpacked_items[::-1])): + code.putln( + "%s = PyList_GET_ITEM(%s, PyList_GET_SIZE(%s)-1); " % ( + arg.py_result(), + target_list, target_list)) + # resize the list the hard way + code.putln("((PyVarObject*)%s)->ob_size--;" % target_list) + code.put_gotref(arg.py_result()) + coerced_arg.generate_evaluation_code(code) + + self.iterator.generate_disposal_code(code) + self.iterator.free_temps(code) + self.iterator.release(code) + + for i in range(len(self.args)): + self.args[i].generate_assignment_code( + self.coerced_unpacked_items[i], code) + def annotate(self, code): for arg in self.args: arg.annotate(code) @@ -3184,6 +3517,8 @@ class SequenceNode(NewTempExprNode): class TupleNode(SequenceNode): # Tuple constructor. + + type = tuple_type gil_message = "Constructing Python tuple" @@ -3193,7 +3528,6 @@ class TupleNode(SequenceNode): self.is_literal = 1 else: SequenceNode.analyse_types(self, env, skip_children) - self.type = tuple_type def calculate_result_code(self): if len(self.args) > 0: @@ -3249,7 +3583,16 @@ class ListNode(SequenceNode): # obj_conversion_errors [PyrexError] used internally # orignial_args [ExprNode] used internally + obj_conversion_errors = [] + gil_message = "Constructing Python list" + + def type_dependencies(self, env): + return () + + def infer_type(self, env): + # TOOD: Infer non-object list arrays. + return list_type def analyse_expressions(self, env): SequenceNode.analyse_expressions(self, env) @@ -3353,22 +3696,20 @@ class ListNode(SequenceNode): # generate_evaluation_code which will do that. -class ComprehensionNode(NewTempExprNode): +class ComprehensionNode(ExprNode): subexprs = ["target"] child_attrs = ["loop", "append"] + def infer_type(self, env): + return self.target.infer_type(env) + + def analyse_declarations(self, env): + self.append.target = self # this is used in the PyList_Append of the inner loop + self.loop.analyse_declarations(env) + def analyse_types(self, env): self.target.analyse_expressions(env) self.type = self.target.type - self.append.target = self # this is a CloneNode used in the PyList_Append in the inner loop - self.loop.analyse_declarations(env) - - def allocate_temps(self, env, result = None): - if debug_temp_alloc: - print("%s Allocating temps" % self) - self.allocate_temp(env, result) - # call loop.analyse_expressions() now to make sure temps get - # allocated at the right time self.loop.analyse_expressions(env) def calculate_result_code(self): @@ -3384,16 +3725,17 @@ class ComprehensionNode(NewTempExprNode): self.loop.annotate(code) -class ComprehensionAppendNode(NewTempExprNode): +class ComprehensionAppendNode(ExprNode): # Need to be careful to avoid infinite recursion: # target must not be in child_attrs/subexprs subexprs = ['expr'] + + type = PyrexTypes.c_int_type def analyse_types(self, env): self.expr.analyse_types(env) if not self.expr.type.is_pyobject: self.expr = self.expr.coerce_to_pyobject(env) - self.type = PyrexTypes.c_int_type self.is_temp = 1 def generate_result_code(self, code): @@ -3414,7 +3756,7 @@ class ComprehensionAppendNode(NewTempExprNode): class DictComprehensionAppendNode(ComprehensionAppendNode): subexprs = ['key_expr', 'value_expr'] - + def analyse_types(self, env): self.key_expr.analyse_types(env) if not self.key_expr.type.is_pyobject: @@ -3422,7 +3764,6 @@ class DictComprehensionAppendNode(ComprehensionAppendNode): self.value_expr.analyse_types(env) if not self.value_expr.type.is_pyobject: self.value_expr = self.value_expr.coerce_to_pyobject(env) - self.type = PyrexTypes.c_int_type self.is_temp = 1 def generate_result_code(self, code): @@ -3434,20 +3775,21 @@ class DictComprehensionAppendNode(ComprehensionAppendNode): code.error_goto_if(self.result(), self.pos))) -class SetNode(NewTempExprNode): +class SetNode(ExprNode): # Set constructor. + type = set_type + subexprs = ['args'] gil_message = "Constructing Python set" - + def analyse_types(self, env): for i in range(len(self.args)): arg = self.args[i] arg.analyse_types(env) self.args[i] = arg.coerce_to_pyobject(env) self.type = set_type - self.gil_check(env) self.is_temp = 1 def calculate_constant_result(self): @@ -3487,6 +3829,10 @@ class DictNode(ExprNode): # obj_conversion_errors [PyrexError] used internally subexprs = ['key_value_pairs'] + is_temp = 1 + type = dict_type + + obj_conversion_errors = [] def calculate_constant_result(self): self.constant_result = dict([ @@ -3500,15 +3846,19 @@ class DictNode(ExprNode): except Exception, e: self.compile_time_value_error(e) + def type_dependencies(self, env): + return () + + def infer_type(self, env): + # TOOD: Infer struct constructors. + return dict_type + def analyse_types(self, env): hold_errors() - self.type = dict_type for item in self.key_value_pairs: item.analyse_types(env) - self.gil_check(env) self.obj_conversion_errors = held_errors() release_errors(ignore=True) - self.is_temp = 1 def coerce_to(self, dst_type, env): if dst_type.is_pyobject: @@ -3524,13 +3874,14 @@ class DictNode(ExprNode): for item in self.key_value_pairs: if isinstance(item.key, CoerceToPyTypeNode): item.key = item.key.arg - if not isinstance(item.key, (StringNode, IdentifierStringNode)): + if not isinstance(item.key, (UnicodeNode, StringNode, BytesNode)): error(item.key.pos, "Invalid struct field identifier") - item.key = IdentifierStringNode(item.key.pos, value="") + item.key = StringNode(item.key.pos, value="") else: - member = dst_type.scope.lookup_here(item.key.value) + 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, item.key.value)) + error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key)) else: value = item.value if isinstance(value, CoerceToPyTypeNode): @@ -3548,19 +3899,11 @@ class DictNode(ExprNode): gil_message = "Constructing Python dict" - def allocate_temps(self, env, result = None): - # Custom method used here because key-value - # pairs are evaluated and used one at a time. - self.allocate_temp(env, result) - for item in self.key_value_pairs: - item.key.allocate_temps(env) - item.value.allocate_temps(env) - item.key.release_temp(env) - item.value.release_temp(env) - 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 self.type.is_pyobject: self.release_errors() code.putln( @@ -3595,6 +3938,8 @@ class DictItemNode(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) @@ -3627,7 +3972,6 @@ class ClassNode(ExprNode): # a name, tuple of bases and class dictionary. # # name EncodedString Name of the class - # cname string Class name as a Python string # bases ExprNode Base class tuple # dict ExprNode Class dict (not owned by this node) # doc ExprNode or None Doc string @@ -3636,20 +3980,19 @@ class ClassNode(ExprNode): subexprs = ['bases', 'doc'] def analyse_types(self, env): - self.cname = env.intern_identifier(self.name) self.bases.analyse_types(env) if self.doc: self.doc.analyse_types(env) self.doc = self.doc.coerce_to_pyobject(env) self.module_name = env.global_scope().qualified_name self.type = py_object_type - self.gil_check(env) self.is_temp = 1 env.use_utility_code(create_class_utility_code); gil_message = "Constructing Python class" def generate_result_code(self, code): + cname = code.intern_identifier(self.name) if self.doc: code.put_error_if_neg(self.pos, 'PyDict_SetItemString(%s, "__doc__", %s)' % ( @@ -3660,7 +4003,7 @@ class ClassNode(ExprNode): self.result(), self.bases.py_result(), self.dict.py_result(), - self.cname, + cname, self.module_name, code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) @@ -3671,40 +4014,41 @@ class UnboundMethodNode(ExprNode): # class definitions. Constructs an unbound method # object from a class and a function. # - # class_cname string C var holding the class object # function ExprNode Function object + type = py_object_type + is_temp = 1 + subexprs = ['function'] def analyse_types(self, env): self.function.analyse_types(env) - self.type = py_object_type - self.gil_check(env) - self.is_temp = 1 gil_message = "Constructing an unbound method" def generate_result_code(self, code): + class_cname = code.pyclass_stack[-1].classobj.result() code.putln( "%s = PyMethod_New(%s, 0, %s); %s" % ( self.result(), self.function.py_result(), - self.class_cname, + class_cname, code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) -class PyCFunctionNode(AtomicNewTempExprNode): +class PyCFunctionNode(AtomicExprNode): # Helper class used in the implementation of Python # class definitions. Constructs a PyCFunction object # from a PyMethodDef struct. # # pymethdef_cname string PyMethodDef structure + type = py_object_type + is_temp = 1 + def analyse_types(self, env): - self.type = py_object_type - self.gil_check(env) - self.is_temp = 1 - + pass + gil_message = "Constructing Python function" def generate_result_code(self, code): @@ -3758,13 +4102,15 @@ class UnopNode(ExprNode): return func(operand) except Exception, e: self.compile_time_value_error(e) + + def infer_type(self, env): + return self.operand.infer_type(env) def analyse_types(self, env): self.operand.analyse_types(env) if self.is_py_operation(): self.coerce_operand_to_pyobject(env) self.type = py_object_type - self.gil_check(env) self.is_temp = 1 elif self.is_cpp_operation(): self.analyse_cpp_operation(env) @@ -3772,11 +4118,16 @@ class UnopNode(ExprNode): self.analyse_c_operation(env) def check_const(self): - self.operand.check_const() + return self.operand.check_const() def is_py_operation(self): return self.operand.type.is_pyobject + def nogil_check(self, env): + if self.is_py_operation(): + self.gil_error() + + def is_cpp_operation(self): return self.operand.type.is_cpp_class @@ -3786,9 +4137,6 @@ class UnopNode(ExprNode): def generate_result_code(self, code): if self.operand.type.is_pyobject: self.generate_py_operation_code(code) - else: - if self.is_temp: - self.generate_c_operation_code(code) def generate_py_operation_code(self, code): function = self.py_operation_function() @@ -3825,7 +4173,11 @@ class NotNode(ExprNode): # 'not' operator # # operand ExprNode + + type = PyrexTypes.c_bint_type + subexprs = ['operand'] + def calculate_constant_result(self): self.constant_result = not self.operand.constant_result @@ -3836,12 +4188,12 @@ class NotNode(ExprNode): except Exception, e: self.compile_time_value_error(e) - subexprs = ['operand'] + def infer_type(self, env): + return PyrexTypes.c_bint_type def analyse_types(self, env): self.operand.analyse_types(env) self.operand = self.operand.coerce_to_boolean(env) - self.type = PyrexTypes.c_bint_type def calculate_result_code(self): return "(!%s)" % self.operand.result() @@ -3876,7 +4228,7 @@ class UnaryMinusNode(UnopNode): else: self.type_error() if self.type.is_complex: - self.infix = env.directives['c99_complex'] + self.infix = False def py_operation_function(self): return "PyNumber_Negative" @@ -3887,6 +4239,11 @@ class UnaryMinusNode(UnopNode): 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 @@ -3909,6 +4266,9 @@ class AmpersandNode(ExprNode): # operand ExprNode subexprs = ['operand'] + + def infer_type(self, env): + return PyrexTypes.c_ptr_type(self.operand.infer_type(env)) def analyse_types(self, env): self.operand.analyse_types(env) @@ -3922,7 +4282,7 @@ class AmpersandNode(ExprNode): self.type = PyrexTypes.c_ptr_type(argtype) def check_const(self): - self.operand.check_const_addr() + return self.operand.check_const_addr() def error(self, mess): error(self.pos, mess) @@ -3954,7 +4314,7 @@ def unop_node(pos, operator, operand): operand = operand) -class TypecastNode(NewTempExprNode): +class TypecastNode(ExprNode): # C type cast # # operand ExprNode @@ -3967,6 +4327,15 @@ class TypecastNode(NewTempExprNode): 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) @@ -3981,27 +4350,35 @@ class TypecastNode(NewTempExprNode): if from_py and not to_py and self.operand.is_ephemeral() and not self.type.is_numeric: error(self.pos, "Casting temporary Python object to non-numeric non-Python type") if to_py and not from_py: - if (self.operand.type.to_py_function and - self.operand.type.create_to_py_utility_code(env)): + if self.operand.type.can_coerce_to_pyobject(env): self.result_ctype = py_object_type self.operand = self.operand.coerce_to_pyobject(env) else: - if not (self.operand.type.is_ptr and self.operand.type.base_type.is_void): + 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.from_py_function: + if self.type.create_from_py_utility_code(env): self.operand = self.operand.coerce_to(self.type, env) - elif self.type.is_ptr and not (self.type.base_type.is_void or self.type.base_type.is_struct): - error(self.pos, "Python objects cannot be casted to pointers of primitive types") + 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 and self.type.is_extension_type: - self.operand = PyTypeTestNode(self.operand, self.type, env) - + self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True) + + def nogil_check(self, env): + if self.type and self.type.is_pyobject and self.is_temp: + self.gil_error() + def check_const(self): - self.operand.check_const() + return self.operand.check_const() def calculate_constant_result(self): # we usually do not know the result of a type cast at code @@ -4012,6 +4389,11 @@ class TypecastNode(NewTempExprNode): opnd = self.operand return self.type.cast_code(opnd.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 @@ -4034,7 +4416,7 @@ class SizeofNode(ExprNode): type = PyrexTypes.c_size_t_type def check_const(self): - pass + return True def generate_result_code(self, code): pass @@ -4113,6 +4495,29 @@ class SizeofVarNode(SizeofNode): def generate_result_code(self, code): pass +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.analyse_types(env) + self.literal = StringNode( + self.pos, value=StringEncoding.EncodedString(str(self.operand.type))) + self.literal.analyse_types(env) + self.literal = self.literal.coerce_to_pyobject(env) + + def generate_evaluation_code(self, code): + self.literal.generate_evaluation_code(code) + + def calculate_result_code(self): + return self.literal.calculate_result_code() #------------------------------------------------------------------- # @@ -4134,7 +4539,7 @@ compile_time_binary_operators = { 'is_not': operator.is_not, '+': operator.add, '&': operator.and_, - '/': operator.div, + '/': operator.truediv, '//': operator.floordiv, '<<': operator.lshift, '%': operator.mod, @@ -4143,7 +4548,6 @@ compile_time_binary_operators = { '**': operator.pow, '>>': operator.rshift, '-': operator.sub, - #'/': operator.truediv, '^': operator.xor, 'in': operator.contains, 'not_in': _not_in, @@ -4157,7 +4561,7 @@ def get_compile_time_binop(node): % node.operator) return func -class BinopNode(NewTempExprNode): +class BinopNode(ExprNode): # operator string # operand1 ExprNode # operand2 ExprNode @@ -4186,25 +4590,25 @@ class BinopNode(NewTempExprNode): return func(operand1, operand2) except Exception, e: 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)) + def analyse_types(self, env): self.operand1.analyse_types(env) self.operand2.analyse_types(env) if self.is_py_operation(): self.coerce_operands_to_pyobjects(env) self.type = py_object_type - self.gil_check(env) self.is_temp = 1 - if Options.incref_local_binop and self.operand1.type.is_pyobject: - self.operand1 = self.operand1.coerce_to_temp(env) elif self.is_cpp_operation(): self.analyse_cpp_operation(env) else: self.analyse_c_operation(env) def is_py_operation(self): - return (self.operand1.type.is_pyobject - or self.operand2.type.is_pyobject) + return self.is_py_operation_types(self.operand1.type, self.operand2.type) def is_cpp_operation(self): type1 = self.operand1.type @@ -4216,13 +4620,25 @@ class BinopNode(NewTempExprNode): return (type1.is_cpp_class or type2.is_cpp_class) + def is_py_operation_types(self, type1, type2): + return type1.is_pyobject or type2.is_pyobject + + def result_type(self, type1, type2): + if self.is_py_operation_types(type1, type2): + return py_object_type + else: + return self.compute_c_result_type(type1, type2) + + 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): - self.operand1.check_const() - self.operand2.check_const() + return self.operand1.check_const() and self.operand2.check_const() def generate_result_code(self, code): #print "BinopNode.generate_result_code:", self.operand1, self.operand2 ### @@ -4241,9 +4657,6 @@ class BinopNode(NewTempExprNode): extra_args, code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) - else: - if self.is_temp: - self.generate_c_operation_code(code) def type_error(self): if not (self.operand1.type.is_error @@ -4266,7 +4679,7 @@ class NumBinopNode(BinopNode): if not self.type: self.type_error() return - if self.type.is_complex and not env.directives['c99_complex']: + if self.type.is_complex: self.infix = False if not self.infix: self.operand1 = self.operand1.coerce_to(self.type, env) @@ -4304,6 +4717,14 @@ class NumBinopNode(BinopNode): return PyrexTypes.widest_numeric_type(type1, type2) else: return 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 ### @@ -4333,13 +4754,13 @@ class NumBinopNode(BinopNode): "|": "PyNumber_Or", "^": "PyNumber_Xor", "&": "PyNumber_And", - "<<": "PyNumber_Lshift", - ">>": "PyNumber_Rshift", + "<<": "PyNumber_Lshift", + ">>": "PyNumber_Rshift", "+": "PyNumber_Add", "-": "PyNumber_Subtract", "*": "PyNumber_Multiply", "/": "__Pyx_PyNumber_Divide", - "//": "PyNumber_FloorDivide", + "//": "PyNumber_FloorDivide", "%": "PyNumber_Remainder", "**": "PyNumber_Power" } @@ -4356,13 +4777,12 @@ class IntBinopNode(NumBinopNode): class AddNode(NumBinopNode): # '+' operator. - - def is_py_operation(self): - if self.operand1.type.is_string \ - and self.operand2.type.is_string: - return 1 + + def is_py_operation_types(self, type1, type2): + if type1.is_string and type2.is_string: + return 1 else: - return NumBinopNode.is_py_operation(self) + return NumBinopNode.is_py_operation_types(self, type1, type2) def compute_c_result_type(self, type1, type2): #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ### @@ -4391,34 +4811,75 @@ class SubNode(NumBinopNode): class MulNode(NumBinopNode): # '*' operator. - def is_py_operation(self): - type1 = self.operand1.type - type2 = self.operand2.type + 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(self) + return NumBinopNode.is_py_operation_types(self, type1, type2) 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, (int,long)) and isinstance(op2, (int,long)): + 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( + self, operand1, operand2) + return func(operand1, operand2) + except Exception, e: + self.compile_time_value_error(e) + def analyse_types(self, env): + if self.cdivision or env.directives['cdivision']: + self.ctruedivision = False + else: + self.ctruedivision = self.truedivision NumBinopNode.analyse_types(self, env) if not self.type.is_pyobject: - self.zerodivision_check = self.cdivision is None and not env.directives['cdivision'] + self.zerodivision_check = ( + self.cdivision is None and not env.directives['cdivision'] + and (self.operand2.constant_result is not_a_constant 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) if env.nogil: error(self.pos, "Pythonic division not allowed without gil, consider using cython.cdivision(True)") - + + def compute_c_result_type(self, type1, type2): + if self.operator == '/' and self.ctruedivision: + 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" @@ -4471,12 +4932,17 @@ class DivNode(NumBinopNode): 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.cdivision: - return "(%s / %s)" % ( - self.operand1.result(), + 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.specalization_name(), @@ -4487,10 +4953,10 @@ class DivNode(NumBinopNode): class ModNode(DivNode): # '%' operator. - def is_py_operation(self): - return (self.operand1.type.is_string - or self.operand2.type.is_string - or NumBinopNode.is_py_operation(self)) + 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 zero_division_message(self): if self.type.is_int: @@ -4558,7 +5024,7 @@ class PowNode(NumBinopNode): # one could have a look at adding this again after /all/ classes # are converted to the new temp scheme. (The temp juggling cannot work # otherwise). -class BoolBinopNode(NewTempExprNode): +class BoolBinopNode(ExprNode): # Short-circuiting boolean operation. # # operator string @@ -4566,6 +5032,11 @@ class BoolBinopNode(NewTempExprNode): # operand2 ExprNode subexprs = ['operand1', 'operand2'] + + def infer_type(self, env): + type1 = self.operand1.infer_type(env) + type2 = self.operand2.infer_type(env) + return PyrexTypes.spanning_type(type1, type2) def calculate_constant_result(self): if self.operator == 'and': @@ -4594,58 +5065,21 @@ class BoolBinopNode(NewTempExprNode): def analyse_types(self, env): self.operand1.analyse_types(env) self.operand2.analyse_types(env) - if self.operand1.type.is_pyobject or \ - self.operand2.type.is_pyobject: - self.operand1 = self.operand1.coerce_to_pyobject(env) - self.operand2 = self.operand2.coerce_to_pyobject(env) - self.type = py_object_type - self.gil_check(env) - else: - self.operand1 = self.operand1.coerce_to_boolean(env) - self.operand2 = self.operand2.coerce_to_boolean(env) - self.type = PyrexTypes.c_bint_type - - # Below disabled for + self.type = PyrexTypes.spanning_type(self.operand1.type, self.operand2.type) + self.operand1 = self.operand1.coerce_to(self.type, env) + self.operand2 = self.operand2.coerce_to(self.type, env) # For what we're about to do, it's vital that # both operands be temp nodes. -# self.operand1 = self.operand1.coerce_to_temp(env) #CTT -# self.operand2 = self.operand2.coerce_to_temp(env) + self.operand1 = self.operand1.coerce_to_simple(env) + self.operand2 = self.operand2.coerce_to_simple(env) self.is_temp = 1 gil_message = "Truth-testing Python object" -## def allocate_temps(self, env, result_code = None): -## # We don't need both operands at the same time, and -## # one of the operands will also be our result. So we -## # use an allocation strategy here which results in -## # this node and both its operands sharing the same -## # result variable. This allows us to avoid some -## # assignments and increfs/decrefs that would otherwise -## # be necessary. -## self.allocate_temp(env, result_code) -## self.operand1.allocate_temps(env, self.result()) -## self.operand2.allocate_temps(env, self.result()) -## # We haven't called release_temp on either operand, -## # because although they are temp nodes, they don't own -## # their result variable. And because they are temp -## # nodes, any temps in their subnodes will have been -## # released before their allocate_temps returned. -## # Therefore, they contain no temp vars that need to -## # be released. - def check_const(self): - self.operand1.check_const() - self.operand2.check_const() - - def calculate_result_code(self): - return "(%s %s %s)" % ( - self.operand1.result(), - self.py_to_c_op[self.operator], - self.operand2.result()) + return self.operand1.check_const() and self.operand2.check_const() - py_to_c_op = {'and': "&&", 'or': "||"} - def generate_evaluation_code(self, code): code.mark_pos(self.pos) self.operand1.generate_evaluation_code(code) @@ -4700,6 +5134,13 @@ class CondExprNode(ExprNode): false_val = None 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 self.compute_result_type(self.true_val.infer_type(env), + self.false_val.infer_type(env)) def calculate_constant_result(self): if self.test.constant_result: @@ -4745,16 +5186,16 @@ class CondExprNode(ExprNode): self.type = PyrexTypes.error_type def check_const(self): - self.test.check_const() - self.true_val.check_const() - self.false_val.check_const() + 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) # uncomment this when we switch to new temps + 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) @@ -4784,6 +5225,10 @@ richcmp_constants = { class CmpNode(object): # Mixin class containing code common to PrimaryCmpNodes # and CascadedCmpNodes. + + def infer_types(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] @@ -4818,44 +5263,125 @@ class CmpNode(object): type2 = type2.base_type return type1.is_cpp_class or type2.is_cpp_class - def is_python_comparison(self): - return (self.has_python_operands() - or (self.cascade and self.cascade.is_python_comparison()) - or self.operator in ('in', 'not_in')) + 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 isinstance(operand1, (StringNode, BytesNode)) \ + and operand1.can_coerce_to_char_literal(): + type1_can_be_int = True + if isinstance(operand2, (StringNode, BytesNode)) \ + 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: + return PyrexTypes.c_uchar_type - def is_python_result(self): - return ((self.has_python_operands() and self.operator not in ('is', 'is_not', 'in', 'not_in')) - or (self.cascade and self.cascade.is_python_result())) + return None - def check_types(self, env, operand1, op, operand2): - if operand1.type.is_complex or operand2.type.is_complex: - if op not in ('==', '!='): - error(self.pos, "complex types unordered") - common_type = PyrexTypes.widest_numeric_type(operand1.type, operand2.type) - self.operand1 = operand1.coerce_to(common_type, env) - self.operand2 = operand2.coerce_to(common_type, env) - elif not self.types_okay(operand1, op, operand2): - error(self.pos, "Invalid types for '%s' (%s, %s)" % - (self.operator, operand1.type, operand2.type)) - - def types_okay(self, operand1, op, operand2): + def find_common_type(self, env, op, operand1, common_type=None): + operand2 = self.operand2 type1 = operand1.type type2 = operand2.type - if type1.is_error or type2.is_error: - return 1 - if type1.is_pyobject: # type2 will be, too - return 1 - elif type1.is_ptr or type1.is_array: - return type1.is_null_ptr or type2.is_null_ptr \ - or ((type2.is_ptr or type2.is_array) - and type1.base_type.same_as(type2.base_type)) - elif ((type1.is_numeric and type2.is_numeric - or type1.is_enum and (type1 is type2 or type2.is_int) - or type1.is_int and type2.is_enum) - and op not in ('is', 'is_not')): - return 1 + + 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: + if op not in ('==', '!='): + error(self.pos, "complex types are unordered") + new_common_type = error_type + if type1.is_pyobject: + new_common_type = type1 + elif type2.is_pyobject: + new_common_type = type2 + 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 == 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): + 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): + 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 + + # recursively merge types + if common_type is None or new_common_type.is_error: + common_type = new_common_type else: - return type1.is_cfunction and type1.is_cfunction and type1 == type2 + # 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 (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.operator not in ('is', 'is_not', 'in', 'not_in')) + or (self.cascade and self.cascade.is_python_result())) def generate_operation_code(self, code, result_code, operand1, op , operand2): @@ -4863,17 +5389,45 @@ class CmpNode(object): coerce_result = "__Pyx_PyBool_FromLong" else: coerce_result = "" - if 'not' in op: negation = "!" - else: negation = "" + if 'not' in op: + negation = "!" + else: + negation = "" if op == 'in' or op == 'not_in': + code.globalstate.use_utility_code(contians_utility_code) + if self.type is PyrexTypes.py_object_type: + coerce_result = "__Pyx_PyBoolOrNull_FromLong" + if op == 'not_in': + negation = "__Pyx_NegateNonNeg" + if operand2.type is dict_type: + code.globalstate.use_utility_code( + raise_none_iter_error_utility_code) + code.putln("if (unlikely(%s == Py_None)) {" % operand2.py_result()) + code.putln("__Pyx_RaiseNoneNotIterableError(); %s" % + code.error_goto(self.pos)) + code.putln("} else {") + method = "PyDict_Contains" + else: + method = "PySequence_Contains" + if self.type is PyrexTypes.py_object_type: + error_clause = code.error_goto_if_null + got_ref = "__Pyx_XGOTREF(%s); " % result_code + else: + error_clause = code.error_goto_if_neg + got_ref = "" code.putln( - "%s = %s(%sPySequence_Contains(%s, %s)); %s" % ( - result_code, - coerce_result, + "%s = %s(%s(%s(%s, %s))); %s%s" % ( + result_code, + coerce_result, negation, + method, operand2.py_result(), operand1.py_result(), - code.error_goto_if_neg(result_code, self.pos))) + got_ref, + error_clause(result_code, self.pos))) + if operand2.type is dict_type: + code.putln("}") + elif (operand1.type.is_pyobject and op not in ('is', 'is_not')): code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s" % ( @@ -4883,9 +5437,11 @@ class CmpNode(object): richcmp_constants[op], code.error_goto_if_null(result_code, self.pos))) code.put_gotref(result_code) - elif operand1.type.is_complex and not code.globalstate.directives['c99_complex']: - if op == "!=": negation = "!" - else: negation = "" + elif operand1.type.is_complex: + if op == "!=": + negation = "!" + else: + negation = "" code.putln("%s = %s(%s%s(%s, %s));" % ( result_code, coerce_result, @@ -4920,8 +5476,16 @@ class CmpNode(object): else: return op +contians_utility_code = UtilityCode( +proto=""" +static INLINE long __Pyx_NegateNonNeg(long b) { return unlikely(b < 0) ? b : !b; } +static INLINE PyObject* __Pyx_PyBoolOrNull_FromLong(long b) { + return unlikely(b < 0) ? NULL : __Pyx_PyBool_FromLong(b); +} +""") -class PrimaryCmpNode(NewTempExprNode, CmpNode): + +class PrimaryCmpNode(ExprNode, CmpNode): # Non-cascaded comparison or first comparison of # a cascaded sequence. # @@ -4939,6 +5503,13 @@ class PrimaryCmpNode(NewTempExprNode, CmpNode): cascade = None + def infer_type(self, env): + # TODO: Actually implement this (after merging with -unstable). + return py_object_type + + def type_dependencies(self, env): + return () + def calculate_constant_result(self): self.constant_result = self.calculate_cascaded_constant_result( self.operand1.constant_result) @@ -4954,16 +5525,23 @@ class PrimaryCmpNode(NewTempExprNode, CmpNode): self.analyse_cpp_comparison(env) return if self.cascade: - self.cascade.analyse_types(env, self.operand2) - self.is_pycmp = self.is_python_comparison() - if self.is_pycmp: - self.coerce_operands_to_pyobjects(env) - if self.has_int_operands(): - self.coerce_chars_to_ints(env) + self.cascade.analyse_types(env) + + if self.operator in ('in', 'not_in'): + common_type = py_object_type + self.is_pycmp = True + else: + common_type = self.find_common_type(env, self.operator, self.operand1) + self.is_pycmp = common_type.is_pyobject + + if 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) - self.check_operand_types(env) if self.is_python_result(): self.type = PyrexTypes.py_object_type else: @@ -4998,52 +5576,16 @@ class PrimaryCmpNode(NewTempExprNode, CmpNode): else: self.type = entry.type.return_type - def check_operand_types(self, env): - self.check_types(env, - self.operand1, self.operator, self.operand2) - if self.cascade: - self.cascade.check_operand_types(env, self.operand2) - def has_python_operands(self): return (self.operand1.type.is_pyobject or self.operand2.type.is_pyobject) - - def coerce_operands_to_pyobjects(self, env): - self.operand1 = self.operand1.coerce_to_pyobject(env) - self.operand2 = self.operand2.coerce_to_pyobject(env) - if self.cascade: - self.cascade.coerce_operands_to_pyobjects(env) - - def has_int_operands(self): - return (self.operand1.type.is_int or self.operand2.type.is_int) \ - or (self.cascade and self.cascade.has_int_operands()) - - def coerce_chars_to_ints(self, env): - # coerce literal single-char strings to c chars - if self.operand1.type.is_string and isinstance(self.operand1, StringNode): - self.operand1 = self.operand1.coerce_to(PyrexTypes.c_uchar_type, env) - if self.operand2.type.is_string and isinstance(self.operand2, StringNode): - self.operand2 = self.operand2.coerce_to(PyrexTypes.c_uchar_type, env) - if self.cascade: - self.cascade.coerce_chars_to_ints(env) - - def allocate_subexpr_temps(self, env): - self.operand1.allocate_temps(env) - self.operand2.allocate_temps(env) - if self.cascade: - self.cascade.allocate_subexpr_temps(env) - - def release_subexpr_temps(self, env): - self.operand1.release_temp(env) - self.operand2.release_temp(env) - if self.cascade: - self.cascade.release_subexpr_temps(env) def check_const(self): - self.operand1.check_const() - self.operand2.check_const() 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 self.operand1.type.is_complex: @@ -5111,17 +5653,18 @@ class CascadedCmpNode(Node, CmpNode): cascade = None constant_result = constant_value_not_set # FIXME: where to calculate this? - def analyse_types(self, env, operand1): + def infer_type(self, env): + # TODO: Actually implement this (after merging with -unstable). + return py_object_type + + def type_dependencies(self, env): + return () + + def analyse_types(self, env): self.operand2.analyse_types(env) if self.cascade: - self.cascade.analyse_types(env, self.operand2) - - def check_operand_types(self, env, operand1): - self.check_types(env, - operand1, self.operator, self.operand2) - if self.cascade: - self.cascade.check_operand_types(env, self.operand2) - + self.cascade.analyse_types(env) + def has_python_operands(self): return self.operand2.type.is_pyobject @@ -5130,29 +5673,12 @@ class CascadedCmpNode(Node, CmpNode): if self.cascade: self.cascade.coerce_operands_to_pyobjects(env) - def has_int_operands(self): - return self.operand2.type.is_int - - def coerce_chars_to_ints(self, env): - if self.operand2.type.is_string and isinstance(self.operand2, StringNode): - self.operand2 = self.operand2.coerce_to(PyrexTypes.c_uchar_type, 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 allocate_subexpr_temps(self, env): - self.operand2.allocate_temps(env) - if self.cascade: - self.cascade.allocate_subexpr_temps(env) - - def release_subexpr_temps(self, env): - self.operand2.release_temp(env) - if self.cascade: - self.cascade.release_subexpr_temps(env) - def generate_evaluation_code(self, code, result, operand1): if self.type.is_pyobject: code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result) @@ -5212,12 +5738,13 @@ def binop_node(pos, operator, operand1, operand2): # #------------------------------------------------------------------- -class CoercionNode(NewTempExprNode): +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): self.pos = arg.pos @@ -5255,15 +5782,16 @@ class PyTypeTestNode(CoercionNode): # object is an instance of a particular extension type. # This node borrows the result of its argument node. - def __init__(self, arg, dst_type, env): + 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.gil_check(env) self.result_ctype = arg.ctype() + self.notnone = notnone + nogil_check = Node.gil_error gil_message = "Python type test" def analyse_types(self, env): @@ -5274,7 +5802,11 @@ class PyTypeTestNode(CoercionNode): def is_ephemeral(self): return self.arg.is_ephemeral() - + + def calculate_constant_result(self): + # FIXME + pass + def calculate_result_code(self): return self.arg.result() @@ -5284,7 +5816,7 @@ class PyTypeTestNode(CoercionNode): code.globalstate.use_utility_code(type_test_utility_code) code.putln( "if (!(%s)) %s" % ( - self.type.type_test_code(self.arg.py_result()), + self.type.type_test_code(self.arg.py_result(), self.notnone), code.error_goto(self.pos))) else: error(self.pos, "Cannot test type of extern C class " @@ -5295,23 +5827,65 @@ class PyTypeTestNode(CoercionNode): def free_temps(self, code): self.arg.free_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). + + def __init__(self, arg, exception_type_cname, exception_message): + 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 + + def analyse_types(self, env): + pass + + def result_in_temp(self): + return self.arg.result_in_temp() + + def calculate_result_code(self): + return self.arg.result() + + def generate_result_code(self, code): + code.putln( + "if (unlikely(%s == Py_None)) {" % self.arg.result()) + code.putln('PyErr_SetString(%s, "%s"); %s ' % ( + self.exception_type_cname, + StringEncoding.escape_byte_string( + self.exception_message.encode('UTF-8')), + code.error_goto(self.pos))) + code.putln("}") + + 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 + is_temp = 1 - def __init__(self, arg, env): + def __init__(self, arg, env, type=py_object_type): CoercionNode.__init__(self, arg) - self.type = py_object_type - self.gil_check(env) - self.is_temp = 1 if not arg.type.create_to_py_utility_code(env): error(arg.pos, "Cannot convert '%s' to Python object" % arg.type) - + if type is not py_object_type: + self.type = py_object_type + elif arg.type.is_string: + self.type = Builtin.bytes_type + gil_message = "Converting to Python object" - + def coerce_to_boolean(self, env): return self.arg.coerce_to_boolean(env).coerce_to_temp(env) @@ -5373,20 +5947,24 @@ class CoerceToBooleanNode(CoercionNode): # This node is used when a result needs to be used # in a boolean context. + type = PyrexTypes.c_bint_type + def __init__(self, arg, env): CoercionNode.__init__(self, arg) - self.type = PyrexTypes.c_bint_type if arg.type.is_pyobject: - if env.nogil: - self.gil_error() self.is_temp = 1 + def nogil_check(self, env): + if self.arg.type.is_pyobject: + self.gil_error() + gil_message = "Truth-testing Python object" def check_const(self): if self.is_temp: self.not_const() - self.arg.check_const() + return False + return self.arg.check_const() def calculate_result_code(self): return "(%s != 0)" % self.arg.result() @@ -5410,8 +5988,8 @@ class CoerceToComplexNode(CoercionNode): def calculate_result_code(self): if self.arg.type.is_complex: - real_part = "__Pyx_REAL_PART(%s)" % self.arg.result() - imag_part = "__Pyx_IMAG_PART(%s)" % self.arg.result() + 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" @@ -5433,7 +6011,6 @@ class CoerceToTempNode(CoercionNode): self.type = self.arg.type self.is_temp = 1 if self.type.is_pyobject: - self.gil_check(env) self.result_ctype = py_object_type gil_message = "Creating temporary Python reference" @@ -5453,7 +6030,7 @@ class CoerceToTempNode(CoercionNode): # by generic generate_subexpr_evaluation_code! code.putln("%s = %s;" % ( self.result(), self.arg.result_as(self.ctype()))) - if self.type.is_pyobject: + if self.type.is_pyobject and self.use_managed_ref: code.put_incref(self.result(), self.ctype()) @@ -5466,6 +6043,7 @@ class CloneNode(CoercionNode): # 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) @@ -5474,10 +6052,16 @@ class CloneNode(CoercionNode): self.result_ctype = arg.result_ctype if hasattr(arg, 'entry'): self.entry = arg.entry - + def result(self): return self.arg.result() - + + 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 @@ -5494,15 +6078,47 @@ class CloneNode(CoercionNode): def generate_disposal_code(self, code): pass - def allocate_temps(self, env): + def free_temps(self, code): pass - - def release_temp(self, env): + + +class ModuleRefNode(ExprNode): + # Simple returns the module object + + type = py_object_type + is_temp = False + subexprs = [] + + def analyse_types(self, env): pass - def free_temps(self, code): + 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): pass + def generate_result_code(self, code): + code.putln('%s = __Pyx_GetAttrString(%s, "__doc__");' % + (self.result(), self.body.result())) + code.put_gotref(self.result()) + + #------------------------------------------------------------------------------------ # @@ -5616,18 +6232,18 @@ bad: type_test_utility_code = UtilityCode( proto = """ -static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ +static INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ """, impl = """ -static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { - if (!type) { +static INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { + if (unlikely(!type)) { PyErr_Format(PyExc_SystemError, "Missing type object"); return 0; } - if (obj == Py_None || PyObject_TypeCheck(obj, type)) + if (likely(PyObject_TypeCheck(obj, type))) return 1; - PyErr_Format(PyExc_TypeError, "Cannot convert %s to %s", - Py_TYPE(obj)->tp_name, type->tp_name); + PyErr_Format(PyExc_TypeError, "Cannot convert %.200s to %.200s", + Py_TYPE(obj)->tp_name, type->tp_name); return 0; } """) @@ -5694,29 +6310,6 @@ impl = "" #------------------------------------------------------------------------------------ -append_utility_code = UtilityCode( -proto = """ -static INLINE PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) { - if (likely(PyList_CheckExact(L))) { - if (PyList_Append(L, x) < 0) return NULL; - Py_INCREF(Py_None); - return Py_None; /* this is just to have an accurate signature */ - } - else { - PyObject *r, *m; - m = __Pyx_GetAttrString(L, "append"); - if (!m) return NULL; - r = PyObject_CallFunctionObjArgs(m, x, NULL); - Py_DECREF(m); - return r; - } -} -""", -impl = "" -) - -#------------------------------------------------------------------------------------ - # If the is_unsigned flag is set, we need to do some extra work to make # sure the index doesn't become negative. @@ -5873,7 +6466,7 @@ static INLINE void __Pyx_RaiseNoneNotIterableError(void); """, impl = ''' static INLINE void __Pyx_RaiseNoneNotIterableError(void) { - PyErr_SetString(PyExc_TypeError, "'NoneType' object is iterable"); + PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable"); } ''')