1# cython: language_level=3str
2
3from __future__ import absolute_import
4
5import cython
6cython.declare(PyrexTypes=object, Naming=object, ExprNodes=object, Nodes=object,
7               Options=object, UtilNodes=object, LetNode=object,
8               LetRefNode=object, TreeFragment=object, EncodedString=object,
9               error=object, warning=object, copy=object, _unicode=object)
10
11import copy
12import hashlib
13
14from . import PyrexTypes
15from . import Naming
16from . import ExprNodes
17from . import Nodes
18from . import Options
19from . import Builtin
20from . import Errors
21
22from .Visitor import VisitorTransform, TreeVisitor
23from .Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform
24from .UtilNodes import LetNode, LetRefNode
25from .TreeFragment import TreeFragment
26from .StringEncoding import EncodedString, _unicode
27from .Errors import error, warning, CompileError, InternalError
28from .Code import UtilityCode
29
30
31class SkipDeclarations(object):
32    """
33    Variable and function declarations can often have a deep tree structure,
34    and yet most transformations don't need to descend to this depth.
35
36    Declaration nodes are removed after AnalyseDeclarationsTransform, so there
37    is no need to use this for transformations after that point.
38    """
39    def visit_CTypeDefNode(self, node):
40        return node
41
42    def visit_CVarDefNode(self, node):
43        return node
44
45    def visit_CDeclaratorNode(self, node):
46        return node
47
48    def visit_CBaseTypeNode(self, node):
49        return node
50
51    def visit_CEnumDefNode(self, node):
52        return node
53
54    def visit_CStructOrUnionDefNode(self, node):
55        return node
56
57    def visit_CppClassNode(self, node):
58        if node.visibility != "extern":
59            # Need to traverse methods.
60            self.visitchildren(node)
61        return node
62
63
64class NormalizeTree(CythonTransform):
65    """
66    This transform fixes up a few things after parsing
67    in order to make the parse tree more suitable for
68    transforms.
69
70    a) After parsing, blocks with only one statement will
71    be represented by that statement, not by a StatListNode.
72    When doing transforms this is annoying and inconsistent,
73    as one cannot in general remove a statement in a consistent
74    way and so on. This transform wraps any single statements
75    in a StatListNode containing a single statement.
76
77    b) The PassStatNode is a noop and serves no purpose beyond
78    plugging such one-statement blocks; i.e., once parsed a
79`    "pass" can just as well be represented using an empty
80    StatListNode. This means less special cases to worry about
81    in subsequent transforms (one always checks to see if a
82    StatListNode has no children to see if the block is empty).
83    """
84
85    def __init__(self, context):
86        super(NormalizeTree, self).__init__(context)
87        self.is_in_statlist = False
88        self.is_in_expr = False
89
90    def visit_ModuleNode(self, node):
91        self.visitchildren(node)
92        if not isinstance(node.body, Nodes.StatListNode):
93            # This can happen when the body only consists of a single (unused) declaration and no statements.
94            node.body = Nodes.StatListNode(pos=node.pos, stats=[node.body])
95        return node
96
97    def visit_ExprNode(self, node):
98        stacktmp = self.is_in_expr
99        self.is_in_expr = True
100        self.visitchildren(node)
101        self.is_in_expr = stacktmp
102        return node
103
104    def visit_StatNode(self, node, is_listcontainer=False):
105        stacktmp = self.is_in_statlist
106        self.is_in_statlist = is_listcontainer
107        self.visitchildren(node)
108        self.is_in_statlist = stacktmp
109        if not self.is_in_statlist and not self.is_in_expr:
110            return Nodes.StatListNode(pos=node.pos, stats=[node])
111        else:
112            return node
113
114    def visit_StatListNode(self, node):
115        self.is_in_statlist = True
116        self.visitchildren(node)
117        self.is_in_statlist = False
118        return node
119
120    def visit_ParallelAssignmentNode(self, node):
121        return self.visit_StatNode(node, True)
122
123    def visit_CEnumDefNode(self, node):
124        return self.visit_StatNode(node, True)
125
126    def visit_CStructOrUnionDefNode(self, node):
127        return self.visit_StatNode(node, True)
128
129    def visit_PassStatNode(self, node):
130        """Eliminate PassStatNode"""
131        if not self.is_in_statlist:
132            return Nodes.StatListNode(pos=node.pos, stats=[])
133        else:
134            return []
135
136    def visit_ExprStatNode(self, node):
137        """Eliminate useless string literals"""
138        if node.expr.is_string_literal:
139            return self.visit_PassStatNode(node)
140        else:
141            return self.visit_StatNode(node)
142
143    def visit_CDeclaratorNode(self, node):
144        return node
145
146
147class PostParseError(CompileError): pass
148
149# error strings checked by unit tests, so define them
150ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
151ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
152ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
153class PostParse(ScopeTrackingTransform):
154    """
155    Basic interpretation of the parse tree, as well as validity
156    checking that can be done on a very basic level on the parse
157    tree (while still not being a problem with the basic syntax,
158    as such).
159
160    Specifically:
161    - Default values to cdef assignments are turned into single
162    assignments following the declaration (everywhere but in class
163    bodies, where they raise a compile error)
164
165    - Interpret some node structures into Python runtime values.
166    Some nodes take compile-time arguments (currently:
167    TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}),
168    which should be interpreted. This happens in a general way
169    and other steps should be taken to ensure validity.
170
171    Type arguments cannot be interpreted in this way.
172
173    - For __cythonbufferdefaults__ the arguments are checked for
174    validity.
175
176    TemplatedTypeNode has its directives interpreted:
177    Any first positional argument goes into the "dtype" attribute,
178    any "ndim" keyword argument goes into the "ndim" attribute and
179    so on. Also it is checked that the directive combination is valid.
180    - __cythonbufferdefaults__ attributes are parsed and put into the
181    type information.
182
183    Note: Currently Parsing.py does a lot of interpretation and
184    reorganization that can be refactored into this transform
185    if a more pure Abstract Syntax Tree is wanted.
186    """
187    def __init__(self, context):
188        super(PostParse, self).__init__(context)
189        self.specialattribute_handlers = {
190            '__cythonbufferdefaults__' : self.handle_bufferdefaults
191        }
192
193    def visit_LambdaNode(self, node):
194        # unpack a lambda expression into the corresponding DefNode
195        collector = YieldNodeCollector()
196        collector.visitchildren(node.result_expr)
197        if collector.has_yield or collector.has_await or isinstance(node.result_expr, ExprNodes.YieldExprNode):
198            body = Nodes.ExprStatNode(
199                node.result_expr.pos, expr=node.result_expr)
200        else:
201            body = Nodes.ReturnStatNode(
202                node.result_expr.pos, value=node.result_expr)
203        node.def_node = Nodes.DefNode(
204            node.pos, name=node.name,
205            args=node.args, star_arg=node.star_arg,
206            starstar_arg=node.starstar_arg,
207            body=body, doc=None)
208        self.visitchildren(node)
209        return node
210
211    def visit_GeneratorExpressionNode(self, node):
212        # unpack a generator expression into the corresponding DefNode
213        collector = YieldNodeCollector()
214        collector.visitchildren(node.loop)
215        node.def_node = Nodes.DefNode(
216            node.pos, name=node.name, doc=None,
217            args=[], star_arg=None, starstar_arg=None,
218            body=node.loop, is_async_def=collector.has_await)
219        self.visitchildren(node)
220        return node
221
222    def visit_ComprehensionNode(self, node):
223        # enforce local scope also in Py2 for async generators (seriously, that's a Py3.6 feature...)
224        if not node.has_local_scope:
225            collector = YieldNodeCollector()
226            collector.visitchildren(node.loop)
227            if collector.has_await:
228                node.has_local_scope = True
229        self.visitchildren(node)
230        return node
231
232    # cdef variables
233    def handle_bufferdefaults(self, decl):
234        if not isinstance(decl.default, ExprNodes.DictNode):
235            raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
236        self.scope_node.buffer_defaults_node = decl.default
237        self.scope_node.buffer_defaults_pos = decl.pos
238
239    def visit_CVarDefNode(self, node):
240        # This assumes only plain names and pointers are assignable on
241        # declaration. Also, it makes use of the fact that a cdef decl
242        # must appear before the first use, so we don't have to deal with
243        # "i = 3; cdef int i = i" and can simply move the nodes around.
244        try:
245            self.visitchildren(node)
246            stats = [node]
247            newdecls = []
248            for decl in node.declarators:
249                declbase = decl
250                while isinstance(declbase, Nodes.CPtrDeclaratorNode):
251                    declbase = declbase.base
252                if isinstance(declbase, Nodes.CNameDeclaratorNode):
253                    if declbase.default is not None:
254                        if self.scope_type in ('cclass', 'pyclass', 'struct'):
255                            if isinstance(self.scope_node, Nodes.CClassDefNode):
256                                handler = self.specialattribute_handlers.get(decl.name)
257                                if handler:
258                                    if decl is not declbase:
259                                        raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
260                                    handler(decl)
261                                    continue  # Remove declaration
262                            raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
263                        first_assignment = self.scope_type != 'module'
264                        stats.append(Nodes.SingleAssignmentNode(node.pos,
265                            lhs=ExprNodes.NameNode(node.pos, name=declbase.name),
266                            rhs=declbase.default, first=first_assignment))
267                        declbase.default = None
268                newdecls.append(decl)
269            node.declarators = newdecls
270            return stats
271        except PostParseError as e:
272            # An error in a cdef clause is ok, simply remove the declaration
273            # and try to move on to report more errors
274            self.context.nonfatal_error(e)
275            return None
276
277    # Split parallel assignments (a,b = b,a) into separate partial
278    # assignments that are executed rhs-first using temps.  This
279    # restructuring must be applied before type analysis so that known
280    # types on rhs and lhs can be matched directly.  It is required in
281    # the case that the types cannot be coerced to a Python type in
282    # order to assign from a tuple.
283
284    def visit_SingleAssignmentNode(self, node):
285        self.visitchildren(node)
286        return self._visit_assignment_node(node, [node.lhs, node.rhs])
287
288    def visit_CascadedAssignmentNode(self, node):
289        self.visitchildren(node)
290        return self._visit_assignment_node(node, node.lhs_list + [node.rhs])
291
292    def _visit_assignment_node(self, node, expr_list):
293        """Flatten parallel assignments into separate single
294        assignments or cascaded assignments.
295        """
296        if sum([ 1 for expr in expr_list
297                 if expr.is_sequence_constructor or expr.is_string_literal ]) < 2:
298            # no parallel assignments => nothing to do
299            return node
300
301        expr_list_list = []
302        flatten_parallel_assignments(expr_list, expr_list_list)
303        temp_refs = []
304        eliminate_rhs_duplicates(expr_list_list, temp_refs)
305
306        nodes = []
307        for expr_list in expr_list_list:
308            lhs_list = expr_list[:-1]
309            rhs = expr_list[-1]
310            if len(lhs_list) == 1:
311                node = Nodes.SingleAssignmentNode(rhs.pos,
312                    lhs = lhs_list[0], rhs = rhs)
313            else:
314                node = Nodes.CascadedAssignmentNode(rhs.pos,
315                    lhs_list = lhs_list, rhs = rhs)
316            nodes.append(node)
317
318        if len(nodes) == 1:
319            assign_node = nodes[0]
320        else:
321            assign_node = Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes)
322
323        if temp_refs:
324            duplicates_and_temps = [ (temp.expression, temp)
325                                     for temp in temp_refs ]
326            sort_common_subsequences(duplicates_and_temps)
327            for _, temp_ref in duplicates_and_temps[::-1]:
328                assign_node = LetNode(temp_ref, assign_node)
329
330        return assign_node
331
332    def _flatten_sequence(self, seq, result):
333        for arg in seq.args:
334            if arg.is_sequence_constructor:
335                self._flatten_sequence(arg, result)
336            else:
337                result.append(arg)
338        return result
339
340    def visit_DelStatNode(self, node):
341        self.visitchildren(node)
342        node.args = self._flatten_sequence(node, [])
343        return node
344
345    def visit_ExceptClauseNode(self, node):
346        if node.is_except_as:
347            # except-as must delete NameNode target at the end
348            del_target = Nodes.DelStatNode(
349                node.pos,
350                args=[ExprNodes.NameNode(
351                    node.target.pos, name=node.target.name)],
352                ignore_nonexisting=True)
353            node.body = Nodes.StatListNode(
354                node.pos,
355                stats=[Nodes.TryFinallyStatNode(
356                    node.pos,
357                    body=node.body,
358                    finally_clause=Nodes.StatListNode(
359                        node.pos,
360                        stats=[del_target]))])
361        self.visitchildren(node)
362        return node
363
364    def visit_AssertStatNode(self, node):
365        """Extract the exception raising into a RaiseStatNode to simplify GIL handling.
366        """
367        if node.exception is None:
368            node.exception = Nodes.RaiseStatNode(
369                node.pos,
370                exc_type=ExprNodes.NameNode(node.pos, name=EncodedString("AssertionError")),
371                exc_value=node.value,
372                exc_tb=None,
373                cause=None,
374                builtin_exc_name="AssertionError",
375                wrap_tuple_value=True,
376            )
377            node.value = None
378        self.visitchildren(node)
379        return node
380
381
382def eliminate_rhs_duplicates(expr_list_list, ref_node_sequence):
383    """Replace rhs items by LetRefNodes if they appear more than once.
384    Creates a sequence of LetRefNodes that set up the required temps
385    and appends them to ref_node_sequence.  The input list is modified
386    in-place.
387    """
388    seen_nodes = set()
389    ref_nodes = {}
390    def find_duplicates(node):
391        if node.is_literal or node.is_name:
392            # no need to replace those; can't include attributes here
393            # as their access is not necessarily side-effect free
394            return
395        if node in seen_nodes:
396            if node not in ref_nodes:
397                ref_node = LetRefNode(node)
398                ref_nodes[node] = ref_node
399                ref_node_sequence.append(ref_node)
400        else:
401            seen_nodes.add(node)
402            if node.is_sequence_constructor:
403                for item in node.args:
404                    find_duplicates(item)
405
406    for expr_list in expr_list_list:
407        rhs = expr_list[-1]
408        find_duplicates(rhs)
409    if not ref_nodes:
410        return
411
412    def substitute_nodes(node):
413        if node in ref_nodes:
414            return ref_nodes[node]
415        elif node.is_sequence_constructor:
416            node.args = list(map(substitute_nodes, node.args))
417        return node
418
419    # replace nodes inside of the common subexpressions
420    for node in ref_nodes:
421        if node.is_sequence_constructor:
422            node.args = list(map(substitute_nodes, node.args))
423
424    # replace common subexpressions on all rhs items
425    for expr_list in expr_list_list:
426        expr_list[-1] = substitute_nodes(expr_list[-1])
427
428def sort_common_subsequences(items):
429    """Sort items/subsequences so that all items and subsequences that
430    an item contains appear before the item itself.  This is needed
431    because each rhs item must only be evaluated once, so its value
432    must be evaluated first and then reused when packing sequences
433    that contain it.
434
435    This implies a partial order, and the sort must be stable to
436    preserve the original order as much as possible, so we use a
437    simple insertion sort (which is very fast for short sequences, the
438    normal case in practice).
439    """
440    def contains(seq, x):
441        for item in seq:
442            if item is x:
443                return True
444            elif item.is_sequence_constructor and contains(item.args, x):
445                return True
446        return False
447    def lower_than(a,b):
448        return b.is_sequence_constructor and contains(b.args, a)
449
450    for pos, item in enumerate(items):
451        key = item[1]  # the ResultRefNode which has already been injected into the sequences
452        new_pos = pos
453        for i in range(pos-1, -1, -1):
454            if lower_than(key, items[i][0]):
455                new_pos = i
456        if new_pos != pos:
457            for i in range(pos, new_pos, -1):
458                items[i] = items[i-1]
459            items[new_pos] = item
460
461def unpack_string_to_character_literals(literal):
462    chars = []
463    pos = literal.pos
464    stype = literal.__class__
465    sval = literal.value
466    sval_type = sval.__class__
467    for char in sval:
468        cval = sval_type(char)
469        chars.append(stype(pos, value=cval, constant_result=cval))
470    return chars
471
472def flatten_parallel_assignments(input, output):
473    #  The input is a list of expression nodes, representing the LHSs
474    #  and RHS of one (possibly cascaded) assignment statement.  For
475    #  sequence constructors, rearranges the matching parts of both
476    #  sides into a list of equivalent assignments between the
477    #  individual elements.  This transformation is applied
478    #  recursively, so that nested structures get matched as well.
479    rhs = input[-1]
480    if (not (rhs.is_sequence_constructor or isinstance(rhs, ExprNodes.UnicodeNode))
481            or not sum([lhs.is_sequence_constructor for lhs in input[:-1]])):
482        output.append(input)
483        return
484
485    complete_assignments = []
486
487    if rhs.is_sequence_constructor:
488        rhs_args = rhs.args
489    elif rhs.is_string_literal:
490        rhs_args = unpack_string_to_character_literals(rhs)
491
492    rhs_size = len(rhs_args)
493    lhs_targets = [[] for _ in range(rhs_size)]
494    starred_assignments = []
495    for lhs in input[:-1]:
496        if not lhs.is_sequence_constructor:
497            if lhs.is_starred:
498                error(lhs.pos, "starred assignment target must be in a list or tuple")
499            complete_assignments.append(lhs)
500            continue
501        lhs_size = len(lhs.args)
502        starred_targets = sum([1 for expr in lhs.args if expr.is_starred])
503        if starred_targets > 1:
504            error(lhs.pos, "more than 1 starred expression in assignment")
505            output.append([lhs,rhs])
506            continue
507        elif lhs_size - starred_targets > rhs_size:
508            error(lhs.pos, "need more than %d value%s to unpack"
509                  % (rhs_size, (rhs_size != 1) and 's' or ''))
510            output.append([lhs,rhs])
511            continue
512        elif starred_targets:
513            map_starred_assignment(lhs_targets, starred_assignments,
514                                   lhs.args, rhs_args)
515        elif lhs_size < rhs_size:
516            error(lhs.pos, "too many values to unpack (expected %d, got %d)"
517                  % (lhs_size, rhs_size))
518            output.append([lhs,rhs])
519            continue
520        else:
521            for targets, expr in zip(lhs_targets, lhs.args):
522                targets.append(expr)
523
524    if complete_assignments:
525        complete_assignments.append(rhs)
526        output.append(complete_assignments)
527
528    # recursively flatten partial assignments
529    for cascade, rhs in zip(lhs_targets, rhs_args):
530        if cascade:
531            cascade.append(rhs)
532            flatten_parallel_assignments(cascade, output)
533
534    # recursively flatten starred assignments
535    for cascade in starred_assignments:
536        if cascade[0].is_sequence_constructor:
537            flatten_parallel_assignments(cascade, output)
538        else:
539            output.append(cascade)
540
541def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args):
542    # Appends the fixed-position LHS targets to the target list that
543    # appear left and right of the starred argument.
544    #
545    # The starred_assignments list receives a new tuple
546    # (lhs_target, rhs_values_list) that maps the remaining arguments
547    # (those that match the starred target) to a list.
548
549    # left side of the starred target
550    for i, (targets, expr) in enumerate(zip(lhs_targets, lhs_args)):
551        if expr.is_starred:
552            starred = i
553            lhs_remaining = len(lhs_args) - i - 1
554            break
555        targets.append(expr)
556    else:
557        raise InternalError("no starred arg found when splitting starred assignment")
558
559    # right side of the starred target
560    for i, (targets, expr) in enumerate(zip(lhs_targets[-lhs_remaining:],
561                                            lhs_args[starred + 1:])):
562        targets.append(expr)
563
564    # the starred target itself, must be assigned a (potentially empty) list
565    target = lhs_args[starred].target  # unpack starred node
566    starred_rhs = rhs_args[starred:]
567    if lhs_remaining:
568        starred_rhs = starred_rhs[:-lhs_remaining]
569    if starred_rhs:
570        pos = starred_rhs[0].pos
571    else:
572        pos = target.pos
573    starred_assignments.append([
574        target, ExprNodes.ListNode(pos=pos, args=starred_rhs)])
575
576
577class PxdPostParse(CythonTransform, SkipDeclarations):
578    """
579    Basic interpretation/validity checking that should only be
580    done on pxd trees.
581
582    A lot of this checking currently happens in the parser; but
583    what is listed below happens here.
584
585    - "def" functions are let through only if they fill the
586    getbuffer/releasebuffer slots
587
588    - cdef functions are let through only if they are on the
589    top level and are declared "inline"
590    """
591    ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
592    ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
593
594    def __call__(self, node):
595        self.scope_type = 'pxd'
596        return super(PxdPostParse, self).__call__(node)
597
598    def visit_CClassDefNode(self, node):
599        old = self.scope_type
600        self.scope_type = 'cclass'
601        self.visitchildren(node)
602        self.scope_type = old
603        return node
604
605    def visit_FuncDefNode(self, node):
606        # FuncDefNode always come with an implementation (without
607        # an imp they are CVarDefNodes..)
608        err = self.ERR_INLINE_ONLY
609
610        if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass'
611                and node.name in ('__getbuffer__', '__releasebuffer__')):
612            err = None  # allow these slots
613
614        if isinstance(node, Nodes.CFuncDefNode):
615            if (u'inline' in node.modifiers and
616                    self.scope_type in ('pxd', 'cclass')):
617                node.inline_in_pxd = True
618                if node.visibility != 'private':
619                    err = self.ERR_NOGO_WITH_INLINE % node.visibility
620                elif node.api:
621                    err = self.ERR_NOGO_WITH_INLINE % 'api'
622                else:
623                    err = None  # allow inline function
624            else:
625                err = self.ERR_INLINE_ONLY
626
627        if err:
628            self.context.nonfatal_error(PostParseError(node.pos, err))
629            return None
630        else:
631            return node
632
633
634class TrackNumpyAttributes(VisitorTransform, SkipDeclarations):
635    # TODO: Make name handling as good as in InterpretCompilerDirectives() below - probably best to merge the two.
636    def __init__(self):
637        super(TrackNumpyAttributes, self).__init__()
638        self.numpy_module_names = set()
639
640    def visit_CImportStatNode(self, node):
641        if node.module_name == u"numpy":
642            self.numpy_module_names.add(node.as_name or u"numpy")
643        return node
644
645    def visit_AttributeNode(self, node):
646        self.visitchildren(node)
647        obj = node.obj
648        if (obj.is_name and obj.name in self.numpy_module_names) or obj.is_numpy_attribute:
649            node.is_numpy_attribute = True
650        return node
651
652    visit_Node = VisitorTransform.recurse_to_children
653
654
655class InterpretCompilerDirectives(CythonTransform):
656    """
657    After parsing, directives can be stored in a number of places:
658    - #cython-comments at the top of the file (stored in ModuleNode)
659    - Command-line arguments overriding these
660    - @cython.directivename decorators
661    - with cython.directivename: statements
662    - replaces "cython.compiled" with BoolNode(value=True)
663      allowing unreachable blocks to be removed at a fairly early stage
664      before cython typing rules are forced on applied
665
666    This transform is responsible for interpreting these various sources
667    and store the directive in two ways:
668    - Set the directives attribute of the ModuleNode for global directives.
669    - Use a CompilerDirectivesNode to override directives for a subtree.
670
671    (The first one is primarily to not have to modify with the tree
672    structure, so that ModuleNode stay on top.)
673
674    The directives are stored in dictionaries from name to value in effect.
675    Each such dictionary is always filled in for all possible directives,
676    using default values where no value is given by the user.
677
678    The available directives are controlled in Options.py.
679
680    Note that we have to run this prior to analysis, and so some minor
681    duplication of functionality has to occur: We manually track cimports
682    and which names the "cython" module may have been imported to.
683    """
684    unop_method_nodes = {
685        'typeof': ExprNodes.TypeofNode,
686
687        'operator.address': ExprNodes.AmpersandNode,
688        'operator.dereference': ExprNodes.DereferenceNode,
689        'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'),
690        'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
691        'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
692        'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
693        'operator.typeid'       : ExprNodes.TypeidNode,
694
695        # For backwards compatibility.
696        'address': ExprNodes.AmpersandNode,
697    }
698
699    binop_method_nodes = {
700        'operator.comma'        : ExprNodes.c_binop_constructor(','),
701    }
702
703    special_methods = {
704        'declare', 'union', 'struct', 'typedef',
705        'sizeof', 'cast', 'pointer', 'compiled',
706        'NULL', 'fused_type', 'parallel',
707    }
708    special_methods.update(unop_method_nodes)
709
710    valid_parallel_directives = {
711        "parallel",
712        "prange",
713        "threadid",
714        #"threadsavailable",
715    }
716
717    def __init__(self, context, compilation_directive_defaults):
718        super(InterpretCompilerDirectives, self).__init__(context)
719        self.cython_module_names = set()
720        self.directive_names = {'staticmethod': 'staticmethod'}
721        self.parallel_directives = {}
722        directives = copy.deepcopy(Options.get_directive_defaults())
723        for key, value in compilation_directive_defaults.items():
724            directives[_unicode(key)] = copy.deepcopy(value)
725        self.directives = directives
726
727    def check_directive_scope(self, pos, directive, scope):
728        legal_scopes = Options.directive_scopes.get(directive, None)
729        if legal_scopes and scope not in legal_scopes:
730            self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
731                                        'is not allowed in %s scope' % (directive, scope)))
732            return False
733        else:
734            if directive not in Options.directive_types:
735                error(pos, "Invalid directive: '%s'." % (directive,))
736            return True
737
738    # Set up processing and handle the cython: comments.
739    def visit_ModuleNode(self, node):
740        for key in sorted(node.directive_comments):
741            if not self.check_directive_scope(node.pos, key, 'module'):
742                self.wrong_scope_error(node.pos, key, 'module')
743                del node.directive_comments[key]
744
745        self.module_scope = node.scope
746
747        self.directives.update(node.directive_comments)
748        node.directives = self.directives
749        node.parallel_directives = self.parallel_directives
750        self.visitchildren(node)
751        node.cython_module_names = self.cython_module_names
752        return node
753
754    # The following four functions track imports and cimports that
755    # begin with "cython"
756    def is_cython_directive(self, name):
757        return (name in Options.directive_types or
758                name in self.special_methods or
759                PyrexTypes.parse_basic_type(name))
760
761    def is_parallel_directive(self, full_name, pos):
762        """
763        Checks to see if fullname (e.g. cython.parallel.prange) is a valid
764        parallel directive. If it is a star import it also updates the
765        parallel_directives.
766        """
767        result = (full_name + ".").startswith("cython.parallel.")
768
769        if result:
770            directive = full_name.split('.')
771            if full_name == u"cython.parallel":
772                self.parallel_directives[u"parallel"] = u"cython.parallel"
773            elif full_name == u"cython.parallel.*":
774                for name in self.valid_parallel_directives:
775                    self.parallel_directives[name] = u"cython.parallel.%s" % name
776            elif (len(directive) != 3 or
777                  directive[-1] not in self.valid_parallel_directives):
778                error(pos, "No such directive: %s" % full_name)
779
780            self.module_scope.use_utility_code(
781                UtilityCode.load_cached("InitThreads", "ModuleSetupCode.c"))
782
783        return result
784
785    def visit_CImportStatNode(self, node):
786        module_name = node.module_name
787        if module_name == u"cython.cimports":
788            error(node.pos, "Cannot cimport the 'cython.cimports' package directly, only submodules.")
789        if module_name.startswith(u"cython.cimports."):
790            if node.as_name and node.as_name != u'cython':
791                node.module_name = module_name[len(u"cython.cimports."):]
792                return node
793            error(node.pos,
794                  "Python cimports must use 'from cython.cimports... import ...'"
795                  " or 'import ... as ...', not just 'import ...'")
796
797        if module_name == u"cython":
798            self.cython_module_names.add(node.as_name or u"cython")
799        elif module_name.startswith(u"cython."):
800            if module_name.startswith(u"cython.parallel."):
801                error(node.pos, node.module_name + " is not a module")
802            if module_name == u"cython.parallel":
803                if node.as_name and node.as_name != u"cython":
804                    self.parallel_directives[node.as_name] = module_name
805                else:
806                    self.cython_module_names.add(u"cython")
807                    self.parallel_directives[
808                                    u"cython.parallel"] = module_name
809                self.module_scope.use_utility_code(
810                    UtilityCode.load_cached("InitThreads", "ModuleSetupCode.c"))
811            elif node.as_name:
812                self.directive_names[node.as_name] = module_name[7:]
813            else:
814                self.cython_module_names.add(u"cython")
815            # if this cimport was a compiler directive, we don't
816            # want to leave the cimport node sitting in the tree
817            return None
818        return node
819
820    def visit_FromCImportStatNode(self, node):
821        module_name = node.module_name
822        if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."):
823            # only supported for convenience
824            return self._create_cimport_from_import(
825                node.pos, module_name, node.relative_level, node.imported_names)
826        elif not node.relative_level and (
827                module_name == u"cython" or module_name.startswith(u"cython.")):
828            submodule = (module_name + u".")[7:]
829            newimp = []
830
831            for pos, name, as_name, kind in node.imported_names:
832                full_name = submodule + name
833                qualified_name = u"cython." + full_name
834
835                if self.is_parallel_directive(qualified_name, node.pos):
836                    # from cython cimport parallel, or
837                    # from cython.parallel cimport parallel, prange, ...
838                    self.parallel_directives[as_name or name] = qualified_name
839                elif self.is_cython_directive(full_name):
840                    self.directive_names[as_name or name] = full_name
841                    if kind is not None:
842                        self.context.nonfatal_error(PostParseError(pos,
843                            "Compiler directive imports must be plain imports"))
844                else:
845                    newimp.append((pos, name, as_name, kind))
846
847            if not newimp:
848                return None
849
850            node.imported_names = newimp
851        return node
852
853    def visit_FromImportStatNode(self, node):
854        import_node = node.module
855        module_name = import_node.module_name.value
856        if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."):
857            imported_names = []
858            for name, name_node in node.items:
859                imported_names.append(
860                    (name_node.pos, name, None if name == name_node.name else name_node.name, None))
861            return self._create_cimport_from_import(
862                node.pos, module_name, import_node.level, imported_names)
863        elif module_name == u"cython" or module_name.startswith(u"cython."):
864            submodule = (module_name + u".")[7:]
865            newimp = []
866            for name, name_node in node.items:
867                full_name = submodule + name
868                qualified_name = u"cython." + full_name
869                if self.is_parallel_directive(qualified_name, node.pos):
870                    self.parallel_directives[name_node.name] = qualified_name
871                elif self.is_cython_directive(full_name):
872                    self.directive_names[name_node.name] = full_name
873                else:
874                    newimp.append((name, name_node))
875            if not newimp:
876                return None
877            node.items = newimp
878        return node
879
880    def _create_cimport_from_import(self, node_pos, module_name, level, imported_names):
881        if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."):
882            module_name = EncodedString(module_name[len(u"cython.cimports."):])  # may be empty
883
884        if module_name:
885            # from cython.cimports.a.b import x, y, z  =>  from a.b cimport x, y, z
886            return Nodes.FromCImportStatNode(
887                node_pos, module_name=module_name,
888                relative_level=level,
889                imported_names=imported_names)
890        else:
891            # from cython.cimports import x, y, z  =>  cimport x; cimport y; cimport z
892            return [
893                Nodes.CImportStatNode(
894                    pos,
895                    module_name=dotted_name,
896                    as_name=as_name,
897                    is_absolute=level == 0)
898                for pos, dotted_name, as_name, _ in imported_names
899            ]
900
901    def visit_SingleAssignmentNode(self, node):
902        if isinstance(node.rhs, ExprNodes.ImportNode):
903            module_name = node.rhs.module_name.value
904            is_special_module = (module_name + u".").startswith((u"cython.parallel.", u"cython.cimports."))
905            if module_name != u"cython" and not is_special_module:
906                return node
907
908            node = Nodes.CImportStatNode(node.pos, module_name=module_name, as_name=node.lhs.name)
909            node = self.visit_CImportStatNode(node)
910        else:
911            self.visitchildren(node)
912
913        return node
914
915    def visit_NameNode(self, node):
916        if node.name in self.cython_module_names:
917            node.is_cython_module = True
918        else:
919            directive = self.directive_names.get(node.name)
920            if directive is not None:
921                node.cython_attribute = directive
922        if node.as_cython_attribute() == "compiled":
923            return ExprNodes.BoolNode(node.pos, value=True)  # replace early so unused branches can be dropped
924                # before they have a chance to cause compile-errors
925        return node
926
927    def visit_AttributeNode(self, node):
928        self.visitchildren(node)
929        if node.as_cython_attribute() == "compiled":
930            return ExprNodes.BoolNode(node.pos, value=True)  # replace early so unused branches can be dropped
931                # before they have a chance to cause compile-errors
932        return node
933
934    def visit_AnnotationNode(self, node):
935        # for most transforms annotations are left unvisited (because they're unevaluated)
936        # however, it is important to pick up compiler directives from them
937        if node.expr:
938            self.visitchildren(node.expr)
939        return node
940
941    def visit_NewExprNode(self, node):
942        self.visit(node.cppclass)
943        self.visitchildren(node)
944        return node
945
946    def try_to_parse_directives(self, node):
947        # If node is the contents of an directive (in a with statement or
948        # decorator), returns a list of (directivename, value) pairs.
949        # Otherwise, returns None
950        if isinstance(node, ExprNodes.CallNode):
951            self.visit(node.function)
952            optname = node.function.as_cython_attribute()
953            if optname:
954                directivetype = Options.directive_types.get(optname)
955                if directivetype:
956                    args, kwds = node.explicit_args_kwds()
957                    directives = []
958                    key_value_pairs = []
959                    if kwds is not None and directivetype is not dict:
960                        for keyvalue in kwds.key_value_pairs:
961                            key, value = keyvalue
962                            sub_optname = "%s.%s" % (optname, key.value)
963                            if Options.directive_types.get(sub_optname):
964                                directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos))
965                            else:
966                                key_value_pairs.append(keyvalue)
967                        if not key_value_pairs:
968                            kwds = None
969                        else:
970                            kwds.key_value_pairs = key_value_pairs
971                        if directives and not kwds and not args:
972                            return directives
973                    directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
974                    return directives
975        elif isinstance(node, (ExprNodes.AttributeNode, ExprNodes.NameNode)):
976            self.visit(node)
977            optname = node.as_cython_attribute()
978            if optname:
979                directivetype = Options.directive_types.get(optname)
980                if directivetype is bool:
981                    arg = ExprNodes.BoolNode(node.pos, value=True)
982                    return [self.try_to_parse_directive(optname, [arg], None, node.pos)]
983                elif directivetype is None:
984                    return [(optname, None)]
985                else:
986                    raise PostParseError(
987                        node.pos, "The '%s' directive should be used as a function call." % optname)
988        return None
989
990    def try_to_parse_directive(self, optname, args, kwds, pos):
991        if optname == 'np_pythran' and not self.context.cpp:
992            raise PostParseError(pos, 'The %s directive can only be used in C++ mode.' % optname)
993        elif optname == 'exceptval':
994            # default: exceptval(None, check=True)
995            arg_error = len(args) > 1
996            check = True
997            if kwds and kwds.key_value_pairs:
998                kw = kwds.key_value_pairs[0]
999                if (len(kwds.key_value_pairs) == 1 and
1000                        kw.key.is_string_literal and kw.key.value == 'check' and
1001                        isinstance(kw.value, ExprNodes.BoolNode)):
1002                    check = kw.value.value
1003                else:
1004                    arg_error = True
1005            if arg_error:
1006                raise PostParseError(
1007                    pos, 'The exceptval directive takes 0 or 1 positional arguments and the boolean keyword "check"')
1008            return ('exceptval', (args[0] if args else None, check))
1009
1010        directivetype = Options.directive_types.get(optname)
1011        if len(args) == 1 and isinstance(args[0], ExprNodes.NoneNode):
1012            return optname, Options.get_directive_defaults()[optname]
1013        elif directivetype is bool:
1014            if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode):
1015                raise PostParseError(pos,
1016                    'The %s directive takes one compile-time boolean argument' % optname)
1017            return (optname, args[0].value)
1018        elif directivetype is int:
1019            if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.IntNode):
1020                raise PostParseError(pos,
1021                    'The %s directive takes one compile-time integer argument' % optname)
1022            return (optname, int(args[0].value))
1023        elif directivetype is str:
1024            if kwds is not None or len(args) != 1 or not isinstance(
1025                    args[0], (ExprNodes.StringNode, ExprNodes.UnicodeNode)):
1026                raise PostParseError(pos,
1027                    'The %s directive takes one compile-time string argument' % optname)
1028            return (optname, str(args[0].value))
1029        elif directivetype is type:
1030            if kwds is not None or len(args) != 1:
1031                raise PostParseError(pos,
1032                    'The %s directive takes one type argument' % optname)
1033            return (optname, args[0])
1034        elif directivetype is dict:
1035            if len(args) != 0:
1036                raise PostParseError(pos,
1037                    'The %s directive takes no prepositional arguments' % optname)
1038            return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
1039        elif directivetype is list:
1040            if kwds and len(kwds.key_value_pairs) != 0:
1041                raise PostParseError(pos,
1042                    'The %s directive takes no keyword arguments' % optname)
1043            return optname, [ str(arg.value) for arg in args ]
1044        elif callable(directivetype):
1045            if kwds is not None or len(args) != 1 or not isinstance(
1046                    args[0], (ExprNodes.StringNode, ExprNodes.UnicodeNode)):
1047                raise PostParseError(pos,
1048                    'The %s directive takes one compile-time string argument' % optname)
1049            return (optname, directivetype(optname, str(args[0].value)))
1050        else:
1051            assert False
1052
1053    def visit_with_directives(self, node, directives):
1054        if not directives:
1055            return self.visit_Node(node)
1056
1057        old_directives = self.directives
1058        new_directives = Options.copy_inherited_directives(old_directives, **directives)
1059
1060        if new_directives == old_directives:
1061            return self.visit_Node(node)
1062
1063        self.directives = new_directives
1064        retbody = self.visit_Node(node)
1065        self.directives = old_directives
1066
1067        if not isinstance(retbody, Nodes.StatListNode):
1068            retbody = Nodes.StatListNode(node.pos, stats=[retbody])
1069        return Nodes.CompilerDirectivesNode(
1070            pos=retbody.pos, body=retbody, directives=new_directives)
1071
1072    # Handle decorators
1073    def visit_FuncDefNode(self, node):
1074        directives = self._extract_directives(node, 'function')
1075        return self.visit_with_directives(node, directives)
1076
1077    def visit_CVarDefNode(self, node):
1078        directives = self._extract_directives(node, 'function')
1079        for name, value in directives.items():
1080            if name == 'locals':
1081                node.directive_locals = value
1082            elif name not in ('final', 'staticmethod'):
1083                self.context.nonfatal_error(PostParseError(
1084                    node.pos,
1085                    "Cdef functions can only take cython.locals(), "
1086                    "staticmethod, or final decorators, got %s." % name))
1087        return self.visit_with_directives(node, directives)
1088
1089    def visit_CClassDefNode(self, node):
1090        directives = self._extract_directives(node, 'cclass')
1091        return self.visit_with_directives(node, directives)
1092
1093    def visit_CppClassNode(self, node):
1094        directives = self._extract_directives(node, 'cppclass')
1095        return self.visit_with_directives(node, directives)
1096
1097    def visit_PyClassDefNode(self, node):
1098        directives = self._extract_directives(node, 'class')
1099        return self.visit_with_directives(node, directives)
1100
1101    def _extract_directives(self, node, scope_name):
1102        if not node.decorators:
1103            return {}
1104        # Split the decorators into two lists -- real decorators and directives
1105        directives = []
1106        realdecs = []
1107        both = []
1108        # Decorators coming first take precedence.
1109        for dec in node.decorators[::-1]:
1110            new_directives = self.try_to_parse_directives(dec.decorator)
1111            if new_directives is not None:
1112                for directive in new_directives:
1113                    if self.check_directive_scope(node.pos, directive[0], scope_name):
1114                        name, value = directive
1115                        if self.directives.get(name, object()) != value:
1116                            directives.append(directive)
1117                        if directive[0] == 'staticmethod':
1118                            both.append(dec)
1119                    # Adapt scope type based on decorators that change it.
1120                    if directive[0] == 'cclass' and scope_name == 'class':
1121                        scope_name = 'cclass'
1122            else:
1123                realdecs.append(dec)
1124        if realdecs and (scope_name == 'cclass' or
1125                         isinstance(node, (Nodes.CClassDefNode, Nodes.CVarDefNode))):
1126            # Note - arbitrary C function decorators are caught later in DecoratorTransform
1127            raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
1128        node.decorators = realdecs[::-1] + both[::-1]
1129        # merge or override repeated directives
1130        optdict = {}
1131        for directive in directives:
1132            name, value = directive
1133            if name in optdict:
1134                old_value = optdict[name]
1135                # keywords and arg lists can be merged, everything
1136                # else overrides completely
1137                if isinstance(old_value, dict):
1138                    old_value.update(value)
1139                elif isinstance(old_value, list):
1140                    old_value.extend(value)
1141                else:
1142                    optdict[name] = value
1143            else:
1144                optdict[name] = value
1145        return optdict
1146
1147    # Handle with-statements
1148    def visit_WithStatNode(self, node):
1149        directive_dict = {}
1150        for directive in self.try_to_parse_directives(node.manager) or []:
1151            if directive is not None:
1152                if node.target is not None:
1153                    self.context.nonfatal_error(
1154                        PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
1155                else:
1156                    name, value = directive
1157                    if name in ('nogil', 'gil'):
1158                        # special case: in pure mode, "with nogil" spells "with cython.nogil"
1159                        node = Nodes.GILStatNode(node.pos, state = name, body = node.body)
1160                        return self.visit_Node(node)
1161                    if self.check_directive_scope(node.pos, name, 'with statement'):
1162                        directive_dict[name] = value
1163        if directive_dict:
1164            return self.visit_with_directives(node.body, directive_dict)
1165        return self.visit_Node(node)
1166
1167
1168class ParallelRangeTransform(CythonTransform, SkipDeclarations):
1169    """
1170    Transform cython.parallel stuff. The parallel_directives come from the
1171    module node, set there by InterpretCompilerDirectives.
1172
1173        x = cython.parallel.threadavailable()   -> ParallelThreadAvailableNode
1174        with nogil, cython.parallel.parallel(): -> ParallelWithBlockNode
1175            print cython.parallel.threadid()    -> ParallelThreadIdNode
1176            for i in cython.parallel.prange(...):  -> ParallelRangeNode
1177                ...
1178    """
1179
1180    # a list of names, maps 'cython.parallel.prange' in the code to
1181    # ['cython', 'parallel', 'prange']
1182    parallel_directive = None
1183
1184    # Indicates whether a namenode in an expression is the cython module
1185    namenode_is_cython_module = False
1186
1187    # Keep track of whether we are the context manager of a 'with' statement
1188    in_context_manager_section = False
1189
1190    # One of 'prange' or 'with parallel'. This is used to disallow closely
1191    # nested 'with parallel:' blocks
1192    state = None
1193
1194    directive_to_node = {
1195        u"cython.parallel.parallel": Nodes.ParallelWithBlockNode,
1196        # u"cython.parallel.threadsavailable": ExprNodes.ParallelThreadsAvailableNode,
1197        u"cython.parallel.threadid": ExprNodes.ParallelThreadIdNode,
1198        u"cython.parallel.prange": Nodes.ParallelRangeNode,
1199    }
1200
1201    def node_is_parallel_directive(self, node):
1202        return node.name in self.parallel_directives or node.is_cython_module
1203
1204    def get_directive_class_node(self, node):
1205        """
1206        Figure out which parallel directive was used and return the associated
1207        Node class.
1208
1209        E.g. for a cython.parallel.prange() call we return ParallelRangeNode
1210        """
1211        if self.namenode_is_cython_module:
1212            directive = '.'.join(self.parallel_directive)
1213        else:
1214            directive = self.parallel_directives[self.parallel_directive[0]]
1215            directive = '%s.%s' % (directive,
1216                                   '.'.join(self.parallel_directive[1:]))
1217            directive = directive.rstrip('.')
1218
1219        cls = self.directive_to_node.get(directive)
1220        if cls is None and not (self.namenode_is_cython_module and
1221                                self.parallel_directive[0] != 'parallel'):
1222            error(node.pos, "Invalid directive: %s" % directive)
1223
1224        self.namenode_is_cython_module = False
1225        self.parallel_directive = None
1226
1227        return cls
1228
1229    def visit_ModuleNode(self, node):
1230        """
1231        If any parallel directives were imported, copy them over and visit
1232        the AST
1233        """
1234        if node.parallel_directives:
1235            self.parallel_directives = node.parallel_directives
1236            return self.visit_Node(node)
1237
1238        # No parallel directives were imported, so they can't be used :)
1239        return node
1240
1241    def visit_NameNode(self, node):
1242        if self.node_is_parallel_directive(node):
1243            self.parallel_directive = [node.name]
1244            self.namenode_is_cython_module = node.is_cython_module
1245        return node
1246
1247    def visit_AttributeNode(self, node):
1248        self.visitchildren(node)
1249        if self.parallel_directive:
1250            self.parallel_directive.append(node.attribute)
1251        return node
1252
1253    def visit_CallNode(self, node):
1254        self.visit(node.function)
1255        if not self.parallel_directive:
1256            self.visitchildren(node, exclude=('function',))
1257            return node
1258
1259        # We are a parallel directive, replace this node with the
1260        # corresponding ParallelSomethingSomething node
1261
1262        if isinstance(node, ExprNodes.GeneralCallNode):
1263            args = node.positional_args.args
1264            kwargs = node.keyword_args
1265        else:
1266            args = node.args
1267            kwargs = {}
1268
1269        parallel_directive_class = self.get_directive_class_node(node)
1270        if parallel_directive_class:
1271            # Note: in case of a parallel() the body is set by
1272            # visit_WithStatNode
1273            node = parallel_directive_class(node.pos, args=args, kwargs=kwargs)
1274
1275        return node
1276
1277    def visit_WithStatNode(self, node):
1278        "Rewrite with cython.parallel.parallel() blocks"
1279        newnode = self.visit(node.manager)
1280
1281        if isinstance(newnode, Nodes.ParallelWithBlockNode):
1282            if self.state == 'parallel with':
1283                error(node.manager.pos,
1284                      "Nested parallel with blocks are disallowed")
1285
1286            self.state = 'parallel with'
1287            body = self.visit(node.body)
1288            self.state = None
1289
1290            newnode.body = body
1291            return newnode
1292        elif self.parallel_directive:
1293            parallel_directive_class = self.get_directive_class_node(node)
1294
1295            if not parallel_directive_class:
1296                # There was an error, stop here and now
1297                return None
1298
1299            if parallel_directive_class is Nodes.ParallelWithBlockNode:
1300                error(node.pos, "The parallel directive must be called")
1301                return None
1302
1303        node.body = self.visit(node.body)
1304        return node
1305
1306    def visit_ForInStatNode(self, node):
1307        "Rewrite 'for i in cython.parallel.prange(...):'"
1308        self.visit(node.iterator)
1309        self.visit(node.target)
1310
1311        in_prange = isinstance(node.iterator.sequence,
1312                               Nodes.ParallelRangeNode)
1313        previous_state = self.state
1314
1315        if in_prange:
1316            # This will replace the entire ForInStatNode, so copy the
1317            # attributes
1318            parallel_range_node = node.iterator.sequence
1319
1320            parallel_range_node.target = node.target
1321            parallel_range_node.body = node.body
1322            parallel_range_node.else_clause = node.else_clause
1323
1324            node = parallel_range_node
1325
1326            if not isinstance(node.target, ExprNodes.NameNode):
1327                error(node.target.pos,
1328                      "Can only iterate over an iteration variable")
1329
1330            self.state = 'prange'
1331
1332        self.visit(node.body)
1333        self.state = previous_state
1334        self.visit(node.else_clause)
1335        return node
1336
1337    def visit(self, node):
1338        "Visit a node that may be None"
1339        if node is not None:
1340            return super(ParallelRangeTransform, self).visit(node)
1341
1342
1343class WithTransform(CythonTransform, SkipDeclarations):
1344    def visit_WithStatNode(self, node):
1345        self.visitchildren(node, 'body')
1346        pos = node.pos
1347        is_async = node.is_async
1348        body, target, manager = node.body, node.target, node.manager
1349        node.enter_call = ExprNodes.SimpleCallNode(
1350            pos, function=ExprNodes.AttributeNode(
1351                pos, obj=ExprNodes.CloneNode(manager),
1352                attribute=EncodedString('__aenter__' if is_async else '__enter__'),
1353                is_special_lookup=True),
1354            args=[],
1355            is_temp=True)
1356
1357        if is_async:
1358            node.enter_call = ExprNodes.AwaitExprNode(pos, arg=node.enter_call)
1359
1360        if target is not None:
1361            body = Nodes.StatListNode(
1362                pos, stats=[
1363                    Nodes.WithTargetAssignmentStatNode(
1364                        pos, lhs=target, with_node=node),
1365                    body])
1366
1367        excinfo_target = ExprNodes.TupleNode(pos, slow=True, args=[
1368            ExprNodes.ExcValueNode(pos) for _ in range(3)])
1369        except_clause = Nodes.ExceptClauseNode(
1370            pos, body=Nodes.IfStatNode(
1371                pos, if_clauses=[
1372                    Nodes.IfClauseNode(
1373                        pos, condition=ExprNodes.NotNode(
1374                            pos, operand=ExprNodes.WithExitCallNode(
1375                                pos, with_stat=node,
1376                                test_if_run=False,
1377                                args=excinfo_target,
1378                                await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)),
1379                        body=Nodes.ReraiseStatNode(pos),
1380                    ),
1381                ],
1382                else_clause=None),
1383            pattern=None,
1384            target=None,
1385            excinfo_target=excinfo_target,
1386        )
1387
1388        node.body = Nodes.TryFinallyStatNode(
1389            pos, body=Nodes.TryExceptStatNode(
1390                pos, body=body,
1391                except_clauses=[except_clause],
1392                else_clause=None,
1393            ),
1394            finally_clause=Nodes.ExprStatNode(
1395                pos, expr=ExprNodes.WithExitCallNode(
1396                    pos, with_stat=node,
1397                    test_if_run=True,
1398                    args=ExprNodes.TupleNode(
1399                        pos, args=[ExprNodes.NoneNode(pos) for _ in range(3)]),
1400                    await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)),
1401            handle_error_case=False,
1402        )
1403        return node
1404
1405    def visit_ExprNode(self, node):
1406        # With statements are never inside expressions.
1407        return node
1408
1409
1410class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations):
1411    """
1412    Transforms method decorators in cdef classes into nested calls or properties.
1413
1414    Python-style decorator properties are transformed into a PropertyNode
1415    with up to the three getter, setter and deleter DefNodes.
1416    The functional style isn't supported yet.
1417    """
1418    _properties = None
1419
1420    _map_property_attribute = {
1421        'getter': EncodedString('__get__'),
1422        'setter': EncodedString('__set__'),
1423        'deleter': EncodedString('__del__'),
1424    }.get
1425
1426    def visit_CClassDefNode(self, node):
1427        if self._properties is None:
1428            self._properties = []
1429        self._properties.append({})
1430        node = super(DecoratorTransform, self).visit_CClassDefNode(node)
1431        self._properties.pop()
1432        return node
1433
1434    def visit_PropertyNode(self, node):
1435        # Low-level warning for other code until we can convert all our uses over.
1436        level = 2 if isinstance(node.pos[0], str) else 0
1437        warning(node.pos, "'property %s:' syntax is deprecated, use '@property'" % node.name, level)
1438        return node
1439
1440    def visit_CFuncDefNode(self, node):
1441        node = self.visit_FuncDefNode(node)
1442        if not node.decorators:
1443            return node
1444        elif self.scope_type != 'cclass' or self.scope_node.visibility != "extern":
1445            # at the moment cdef functions are very restricted in what decorators they can take
1446            # so it's simple to test for the small number of allowed decorators....
1447            if not (len(node.decorators) == 1 and node.decorators[0].decorator.is_name and
1448                    node.decorators[0].decorator.name == "staticmethod"):
1449                error(node.decorators[0].pos, "Cdef functions cannot take arbitrary decorators.")
1450            return node
1451
1452        ret_node = node
1453        decorator_node = self._find_property_decorator(node)
1454        if decorator_node:
1455            if decorator_node.decorator.is_name:
1456                name = node.declared_name()
1457                if name:
1458                    ret_node = self._add_property(node, name, decorator_node)
1459            else:
1460                error(decorator_node.pos, "C property decorator can only be @property")
1461
1462        if node.decorators:
1463            return self._reject_decorated_property(node, node.decorators[0])
1464        return ret_node
1465
1466    def visit_DefNode(self, node):
1467        scope_type = self.scope_type
1468        node = self.visit_FuncDefNode(node)
1469        if scope_type != 'cclass' or not node.decorators:
1470            return node
1471
1472        # transform @property decorators
1473        decorator_node = self._find_property_decorator(node)
1474        if decorator_node is not None:
1475            decorator = decorator_node.decorator
1476            if decorator.is_name:
1477                return self._add_property(node, node.name, decorator_node)
1478            else:
1479                handler_name = self._map_property_attribute(decorator.attribute)
1480                if handler_name:
1481                    if decorator.obj.name != node.name:
1482                        # CPython does not generate an error or warning, but not something useful either.
1483                        error(decorator_node.pos,
1484                              "Mismatching property names, expected '%s', got '%s'" % (
1485                                  decorator.obj.name, node.name))
1486                    elif len(node.decorators) > 1:
1487                        return self._reject_decorated_property(node, decorator_node)
1488                    else:
1489                        return self._add_to_property(node, handler_name, decorator_node)
1490
1491        # we clear node.decorators, so we need to set the
1492        # is_staticmethod/is_classmethod attributes now
1493        for decorator in node.decorators:
1494            func = decorator.decorator
1495            if func.is_name:
1496                node.is_classmethod |= func.name == 'classmethod'
1497                node.is_staticmethod |= func.name == 'staticmethod'
1498
1499        # transform normal decorators
1500        decs = node.decorators
1501        node.decorators = None
1502        return self.chain_decorators(node, decs, node.name)
1503
1504    def _find_property_decorator(self, node):
1505        properties = self._properties[-1]
1506        for decorator_node in node.decorators[::-1]:
1507            decorator = decorator_node.decorator
1508            if decorator.is_name and decorator.name == 'property':
1509                # @property
1510                return decorator_node
1511            elif decorator.is_attribute and decorator.obj.name in properties:
1512                # @prop.setter etc.
1513                return decorator_node
1514        return None
1515
1516    @staticmethod
1517    def _reject_decorated_property(node, decorator_node):
1518        # restrict transformation to outermost decorator as wrapped properties will probably not work
1519        for deco in node.decorators:
1520            if deco != decorator_node:
1521                error(deco.pos, "Property methods with additional decorators are not supported")
1522        return node
1523
1524    def _add_property(self, node, name, decorator_node):
1525        if len(node.decorators) > 1:
1526            return self._reject_decorated_property(node, decorator_node)
1527        node.decorators.remove(decorator_node)
1528        properties = self._properties[-1]
1529        is_cproperty = isinstance(node, Nodes.CFuncDefNode)
1530        body = Nodes.StatListNode(node.pos, stats=[node])
1531        if is_cproperty:
1532            if name in properties:
1533                error(node.pos, "C property redeclared")
1534            if 'inline' not in node.modifiers:
1535                error(node.pos, "C property method must be declared 'inline'")
1536            prop = Nodes.CPropertyNode(node.pos, doc=node.doc, name=name, body=body)
1537        elif name in properties:
1538            prop = properties[name]
1539            if prop.is_cproperty:
1540                error(node.pos, "C property redeclared")
1541            else:
1542                node.name = EncodedString("__get__")
1543                prop.pos = node.pos
1544                prop.doc = node.doc
1545                prop.body.stats = [node]
1546            return None
1547        else:
1548            node.name = EncodedString("__get__")
1549            prop = Nodes.PropertyNode(
1550                node.pos, name=name, doc=node.doc, body=body)
1551        properties[name] = prop
1552        return prop
1553
1554    def _add_to_property(self, node, name, decorator):
1555        properties = self._properties[-1]
1556        prop = properties[node.name]
1557        if prop.is_cproperty:
1558            error(node.pos, "C property redeclared")
1559            return None
1560        node.name = name
1561        node.decorators.remove(decorator)
1562        stats = prop.body.stats
1563        for i, stat in enumerate(stats):
1564            if stat.name == name:
1565                stats[i] = node
1566                break
1567        else:
1568            stats.append(node)
1569        return None
1570
1571    @staticmethod
1572    def chain_decorators(node, decorators, name):
1573        """
1574        Decorators are applied directly in DefNode and PyClassDefNode to avoid
1575        reassignments to the function/class name - except for cdef class methods.
1576        For those, the reassignment is required as methods are originally
1577        defined in the PyMethodDef struct.
1578
1579        The IndirectionNode allows DefNode to override the decorator.
1580        """
1581        decorator_result = ExprNodes.NameNode(node.pos, name=name)
1582        for decorator in decorators[::-1]:
1583            decorator_result = ExprNodes.SimpleCallNode(
1584                decorator.pos,
1585                function=decorator.decorator,
1586                args=[decorator_result])
1587
1588        name_node = ExprNodes.NameNode(node.pos, name=name)
1589        reassignment = Nodes.SingleAssignmentNode(
1590            node.pos,
1591            lhs=name_node,
1592            rhs=decorator_result)
1593
1594        reassignment = Nodes.IndirectionNode([reassignment])
1595        node.decorator_indirection = reassignment
1596        return [node, reassignment]
1597
1598
1599class CnameDirectivesTransform(CythonTransform, SkipDeclarations):
1600    """
1601    Only part of the CythonUtilityCode pipeline. Must be run before
1602    DecoratorTransform in case this is a decorator for a cdef class.
1603    It filters out @cname('my_cname') decorators and rewrites them to
1604    CnameDecoratorNodes.
1605    """
1606
1607    def handle_function(self, node):
1608        if not getattr(node, 'decorators', None):
1609            return self.visit_Node(node)
1610
1611        for i, decorator in enumerate(node.decorators):
1612            decorator = decorator.decorator
1613
1614            if (isinstance(decorator, ExprNodes.CallNode) and
1615                    decorator.function.is_name and
1616                    decorator.function.name == 'cname'):
1617                args, kwargs = decorator.explicit_args_kwds()
1618
1619                if kwargs:
1620                    raise AssertionError(
1621                            "cname decorator does not take keyword arguments")
1622
1623                if len(args) != 1:
1624                    raise AssertionError(
1625                            "cname decorator takes exactly one argument")
1626
1627                if not (args[0].is_literal and
1628                        args[0].type == Builtin.str_type):
1629                    raise AssertionError(
1630                            "argument to cname decorator must be a string literal")
1631
1632                cname = args[0].compile_time_value(None)
1633                del node.decorators[i]
1634                node = Nodes.CnameDecoratorNode(pos=node.pos, node=node,
1635                                                cname=cname)
1636                break
1637
1638        return self.visit_Node(node)
1639
1640    visit_FuncDefNode = handle_function
1641    visit_CClassDefNode = handle_function
1642    visit_CEnumDefNode = handle_function
1643    visit_CStructOrUnionDefNode = handle_function
1644
1645
1646class ForwardDeclareTypes(CythonTransform):
1647    """
1648    Declare all global cdef names that we allow referencing in other places,
1649    before declaring everything (else) in source code order.
1650    """
1651
1652    def visit_CompilerDirectivesNode(self, node):
1653        env = self.module_scope
1654        old = env.directives
1655        env.directives = node.directives
1656        self.visitchildren(node)
1657        env.directives = old
1658        return node
1659
1660    def visit_ModuleNode(self, node):
1661        self.module_scope = node.scope
1662        self.module_scope.directives = node.directives
1663        self.visitchildren(node)
1664        return node
1665
1666    def visit_CDefExternNode(self, node):
1667        old_cinclude_flag = self.module_scope.in_cinclude
1668        self.module_scope.in_cinclude = 1
1669        self.visitchildren(node)
1670        self.module_scope.in_cinclude = old_cinclude_flag
1671        return node
1672
1673    def visit_CEnumDefNode(self, node):
1674        node.declare(self.module_scope)
1675        return node
1676
1677    def visit_CStructOrUnionDefNode(self, node):
1678        if node.name not in self.module_scope.entries:
1679            node.declare(self.module_scope)
1680        return node
1681
1682    def visit_CClassDefNode(self, node):
1683        if node.class_name not in self.module_scope.entries:
1684            node.declare(self.module_scope)
1685        # Expand fused methods of .pxd declared types to construct the final vtable order.
1686        type = self.module_scope.entries[node.class_name].type
1687        if type is not None and type.is_extension_type and not type.is_builtin_type and type.scope:
1688            scope = type.scope
1689            for entry in scope.cfunc_entries:
1690                if entry.type and entry.type.is_fused:
1691                    entry.type.get_all_specialized_function_types()
1692        return node
1693
1694    def visit_FuncDefNode(self, node):
1695        # no traversal needed
1696        return node
1697
1698    def visit_PyClassDefNode(self, node):
1699        # no traversal needed
1700        return node
1701
1702
1703class AnalyseDeclarationsTransform(EnvTransform):
1704
1705    basic_property = TreeFragment(u"""
1706property NAME:
1707    def __get__(self):
1708        return ATTR
1709    def __set__(self, value):
1710        ATTR = value
1711    """, level='c_class', pipeline=[NormalizeTree(None)])
1712    basic_pyobject_property = TreeFragment(u"""
1713property NAME:
1714    def __get__(self):
1715        return ATTR
1716    def __set__(self, value):
1717        ATTR = value
1718    def __del__(self):
1719        ATTR = None
1720    """, level='c_class', pipeline=[NormalizeTree(None)])
1721    basic_property_ro = TreeFragment(u"""
1722property NAME:
1723    def __get__(self):
1724        return ATTR
1725    """, level='c_class', pipeline=[NormalizeTree(None)])
1726
1727    struct_or_union_wrapper = TreeFragment(u"""
1728cdef class NAME:
1729    cdef TYPE value
1730    def __init__(self, MEMBER=None):
1731        cdef int count
1732        count = 0
1733        INIT_ASSIGNMENTS
1734        if IS_UNION and count > 1:
1735            raise ValueError, "At most one union member should be specified."
1736    def __str__(self):
1737        return STR_FORMAT % MEMBER_TUPLE
1738    def __repr__(self):
1739        return REPR_FORMAT % MEMBER_TUPLE
1740    """, pipeline=[NormalizeTree(None)])
1741
1742    init_assignment = TreeFragment(u"""
1743if VALUE is not None:
1744    ATTR = VALUE
1745    count += 1
1746    """, pipeline=[NormalizeTree(None)])
1747
1748    fused_function = None
1749    in_lambda = 0
1750
1751    def __call__(self, root):
1752        # needed to determine if a cdef var is declared after it's used.
1753        self.seen_vars_stack = []
1754        self.fused_error_funcs = set()
1755        super_class = super(AnalyseDeclarationsTransform, self)
1756        self._super_visit_FuncDefNode = super_class.visit_FuncDefNode
1757        return super_class.__call__(root)
1758
1759    def visit_NameNode(self, node):
1760        self.seen_vars_stack[-1].add(node.name)
1761        return node
1762
1763    def visit_ModuleNode(self, node):
1764        # Pickling support requires injecting module-level nodes.
1765        self.extra_module_declarations = []
1766        self.seen_vars_stack.append(set())
1767        node.analyse_declarations(self.current_env())
1768        self.visitchildren(node)
1769        self.seen_vars_stack.pop()
1770        node.body.stats.extend(self.extra_module_declarations)
1771        return node
1772
1773    def visit_LambdaNode(self, node):
1774        self.in_lambda += 1
1775        node.analyse_declarations(self.current_env())
1776        self.visitchildren(node)
1777        self.in_lambda -= 1
1778        return node
1779
1780    def visit_CClassDefNode(self, node):
1781        node = self.visit_ClassDefNode(node)
1782        if node.scope and node.scope.implemented and node.body:
1783            stats = []
1784            for entry in node.scope.var_entries:
1785                if entry.needs_property:
1786                    property = self.create_Property(entry)
1787                    property.analyse_declarations(node.scope)
1788                    self.visit(property)
1789                    stats.append(property)
1790            if stats:
1791                node.body.stats += stats
1792            if (node.visibility != 'extern'
1793                    and not node.scope.lookup('__reduce__')
1794                    and not node.scope.lookup('__reduce_ex__')):
1795                self._inject_pickle_methods(node)
1796        return node
1797
1798    def _inject_pickle_methods(self, node):
1799        env = self.current_env()
1800        if node.scope.directives['auto_pickle'] is False:   # None means attempt it.
1801            # Old behavior of not doing anything.
1802            return
1803        auto_pickle_forced = node.scope.directives['auto_pickle'] is True
1804
1805        all_members = []
1806        cls = node.entry.type
1807        cinit = None
1808        inherited_reduce = None
1809        while cls is not None:
1810            all_members.extend(e for e in cls.scope.var_entries if e.name not in ('__weakref__', '__dict__'))
1811            cinit = cinit or cls.scope.lookup('__cinit__')
1812            inherited_reduce = inherited_reduce or cls.scope.lookup('__reduce__') or cls.scope.lookup('__reduce_ex__')
1813            cls = cls.base_type
1814        all_members.sort(key=lambda e: e.name)
1815
1816        if inherited_reduce:
1817            # This is not failsafe, as we may not know whether a cimported class defines a __reduce__.
1818            # This is why we define __reduce_cython__ and only replace __reduce__
1819            # (via ExtensionTypes.SetupReduce utility code) at runtime on class creation.
1820            return
1821
1822        non_py = [
1823            e for e in all_members
1824            if not e.type.is_pyobject and (not e.type.can_coerce_to_pyobject(env)
1825                                           or not e.type.can_coerce_from_pyobject(env))
1826        ]
1827
1828        structs = [e for e in all_members if e.type.is_struct_or_union]
1829
1830        if cinit or non_py or (structs and not auto_pickle_forced):
1831            if cinit:
1832                # TODO(robertwb): We could allow this if __cinit__ has no require arguments.
1833                msg = 'no default __reduce__ due to non-trivial __cinit__'
1834            elif non_py:
1835                msg = "%s cannot be converted to a Python object for pickling" % ','.join("self.%s" % e.name for e in non_py)
1836            else:
1837                # Extern structs may be only partially defined.
1838                # TODO(robertwb): Limit the restriction to extern
1839                # (and recursively extern-containing) structs.
1840                msg = ("Pickling of struct members such as %s must be explicitly requested "
1841                       "with @auto_pickle(True)" % ','.join("self.%s" % e.name for e in structs))
1842
1843            if auto_pickle_forced:
1844                error(node.pos, msg)
1845
1846            pickle_func = TreeFragment(u"""
1847                def __reduce_cython__(self):
1848                    raise TypeError, "%(msg)s"
1849                def __setstate_cython__(self, __pyx_state):
1850                    raise TypeError, "%(msg)s"
1851                """ % {'msg': msg},
1852                level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
1853            pickle_func.analyse_declarations(node.scope)
1854            self.visit(pickle_func)
1855            node.body.stats.append(pickle_func)
1856
1857        else:
1858            for e in all_members:
1859                if not e.type.is_pyobject:
1860                    e.type.create_to_py_utility_code(env)
1861                    e.type.create_from_py_utility_code(env)
1862            all_members_names = sorted([e.name for e in all_members])
1863            checksum = '0x%s' % hashlib.sha1(' '.join(all_members_names).encode('utf-8')).hexdigest()[:7]
1864            unpickle_func_name = '__pyx_unpickle_%s' % node.punycode_class_name
1865
1866            # TODO(robertwb): Move the state into the third argument
1867            # so it can be pickled *after* self is memoized.
1868            unpickle_func = TreeFragment(u"""
1869                def %(unpickle_func_name)s(__pyx_type, long __pyx_checksum, __pyx_state):
1870                    cdef object __pyx_PickleError
1871                    cdef object __pyx_result
1872                    if __pyx_checksum != %(checksum)s:
1873                        from pickle import PickleError as __pyx_PickleError
1874                        raise __pyx_PickleError, "Incompatible checksums (%%s vs %(checksum)s = (%(members)s))" %% __pyx_checksum
1875                    __pyx_result = %(class_name)s.__new__(__pyx_type)
1876                    if __pyx_state is not None:
1877                        %(unpickle_func_name)s__set_state(<%(class_name)s> __pyx_result, __pyx_state)
1878                    return __pyx_result
1879
1880                cdef %(unpickle_func_name)s__set_state(%(class_name)s __pyx_result, tuple __pyx_state):
1881                    %(assignments)s
1882                    if len(__pyx_state) > %(num_members)d and hasattr(__pyx_result, '__dict__'):
1883                        __pyx_result.__dict__.update(__pyx_state[%(num_members)d])
1884                """ % {
1885                    'unpickle_func_name': unpickle_func_name,
1886                    'checksum': checksum,
1887                    'members': ', '.join(all_members_names),
1888                    'class_name': node.class_name,
1889                    'assignments': '; '.join(
1890                        '__pyx_result.%s = __pyx_state[%s]' % (v, ix)
1891                        for ix, v in enumerate(all_members_names)),
1892                    'num_members': len(all_members_names),
1893                }, level='module', pipeline=[NormalizeTree(None)]).substitute({})
1894            unpickle_func.analyse_declarations(node.entry.scope)
1895            self.visit(unpickle_func)
1896            self.extra_module_declarations.append(unpickle_func)
1897
1898            pickle_func = TreeFragment(u"""
1899                def __reduce_cython__(self):
1900                    cdef tuple state
1901                    cdef object _dict
1902                    cdef bint use_setstate
1903                    state = (%(members)s)
1904                    _dict = getattr(self, '__dict__', None)
1905                    if _dict is not None:
1906                        state += (_dict,)
1907                        use_setstate = True
1908                    else:
1909                        use_setstate = %(any_notnone_members)s
1910                    if use_setstate:
1911                        return %(unpickle_func_name)s, (type(self), %(checksum)s, None), state
1912                    else:
1913                        return %(unpickle_func_name)s, (type(self), %(checksum)s, state)
1914
1915                def __setstate_cython__(self, __pyx_state):
1916                    %(unpickle_func_name)s__set_state(self, __pyx_state)
1917                """ % {
1918                    'unpickle_func_name': unpickle_func_name,
1919                    'checksum': checksum,
1920                    'members': ', '.join('self.%s' % v for v in all_members_names) + (',' if len(all_members_names) == 1 else ''),
1921                    # Even better, we could check PyType_IS_GC.
1922                    'any_notnone_members' : ' or '.join(['self.%s is not None' % e.name for e in all_members if e.type.is_pyobject] or ['False']),
1923                },
1924                level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
1925            pickle_func.analyse_declarations(node.scope)
1926            self.enter_scope(node, node.scope)  # functions should be visited in the class scope
1927            self.visit(pickle_func)
1928            self.exit_scope()
1929            node.body.stats.append(pickle_func)
1930
1931    def _handle_fused_def_decorators(self, old_decorators, env, node):
1932        """
1933        Create function calls to the decorators and reassignments to
1934        the function.
1935        """
1936        # Delete staticmethod and classmethod decorators, this is
1937        # handled directly by the fused function object.
1938        decorators = []
1939        for decorator in old_decorators:
1940            func = decorator.decorator
1941            if (not func.is_name or
1942                    func.name not in ('staticmethod', 'classmethod') or
1943                    env.lookup_here(func.name)):
1944                # not a static or classmethod
1945                decorators.append(decorator)
1946
1947        if decorators:
1948            transform = DecoratorTransform(self.context)
1949            def_node = node.node
1950            _, reassignments = transform.chain_decorators(
1951                def_node, decorators, def_node.name)
1952            reassignments.analyse_declarations(env)
1953            node = [node, reassignments]
1954
1955        return node
1956
1957    def _handle_def(self, decorators, env, node):
1958        "Handle def or cpdef fused functions"
1959        # Create PyCFunction nodes for each specialization
1960        node.stats.insert(0, node.py_func)
1961        node.py_func = self.visit(node.py_func)
1962        node.update_fused_defnode_entry(env)
1963        # For the moment, fused functions do not support METH_FASTCALL
1964        node.py_func.entry.signature.use_fastcall = False
1965        pycfunc = ExprNodes.PyCFunctionNode.from_defnode(node.py_func, binding=True)
1966        pycfunc = ExprNodes.ProxyNode(pycfunc.coerce_to_temp(env))
1967        node.resulting_fused_function = pycfunc
1968        # Create assignment node for our def function
1969        node.fused_func_assignment = self._create_assignment(
1970            node.py_func, ExprNodes.CloneNode(pycfunc), env)
1971
1972        if decorators:
1973            node = self._handle_fused_def_decorators(decorators, env, node)
1974
1975        return node
1976
1977    def _create_fused_function(self, env, node):
1978        "Create a fused function for a DefNode with fused arguments"
1979        from . import FusedNode
1980
1981        if self.fused_function or self.in_lambda:
1982            if self.fused_function not in self.fused_error_funcs:
1983                if self.in_lambda:
1984                    error(node.pos, "Fused lambdas not allowed")
1985                else:
1986                    error(node.pos, "Cannot nest fused functions")
1987
1988            self.fused_error_funcs.add(self.fused_function)
1989
1990            node.body = Nodes.PassStatNode(node.pos)
1991            for arg in node.args:
1992                if arg.type.is_fused:
1993                    arg.type = arg.type.get_fused_types()[0]
1994
1995            return node
1996
1997        decorators = getattr(node, 'decorators', None)
1998        node = FusedNode.FusedCFuncDefNode(node, env)
1999        self.fused_function = node
2000        self.visitchildren(node)
2001        self.fused_function = None
2002        if node.py_func:
2003            node = self._handle_def(decorators, env, node)
2004
2005        return node
2006
2007    def _handle_fused(self, node):
2008        if node.is_generator and node.has_fused_arguments:
2009            node.has_fused_arguments = False
2010            error(node.pos, "Fused generators not supported")
2011            node.gbody = Nodes.StatListNode(node.pos,
2012                                            stats=[],
2013                                            body=Nodes.PassStatNode(node.pos))
2014
2015        return node.has_fused_arguments
2016
2017    def visit_FuncDefNode(self, node):
2018        """
2019        Analyse a function and its body, as that hasn't happened yet.  Also
2020        analyse the directive_locals set by @cython.locals().
2021
2022        Then, if we are a function with fused arguments, replace the function
2023        (after it has declared itself in the symbol table!) with a
2024        FusedCFuncDefNode, and analyse its children (which are in turn normal
2025        functions). If we're a normal function, just analyse the body of the
2026        function.
2027        """
2028        env = self.current_env()
2029
2030        self.seen_vars_stack.append(set())
2031        lenv = node.local_scope
2032        node.declare_arguments(lenv)
2033
2034        # @cython.locals(...)
2035        for var, type_node in node.directive_locals.items():
2036            if not lenv.lookup_here(var):   # don't redeclare args
2037                type = type_node.analyse_as_type(lenv)
2038                if type and type.is_fused and lenv.fused_to_specific:
2039                    type = type.specialize(lenv.fused_to_specific)
2040                if type:
2041                    lenv.declare_var(var, type, type_node.pos)
2042                else:
2043                    error(type_node.pos, "Not a type")
2044
2045        if self._handle_fused(node):
2046            node = self._create_fused_function(env, node)
2047        else:
2048            node.body.analyse_declarations(lenv)
2049            self._super_visit_FuncDefNode(node)
2050
2051        self.seen_vars_stack.pop()
2052        return node
2053
2054    def visit_DefNode(self, node):
2055        node = self.visit_FuncDefNode(node)
2056        env = self.current_env()
2057        if (not isinstance(node, Nodes.DefNode) or
2058                node.fused_py_func or node.is_generator_body or
2059                not node.needs_assignment_synthesis(env)):
2060            return node
2061        return [node, self._synthesize_assignment(node, env)]
2062
2063    def visit_GeneratorBodyDefNode(self, node):
2064        return self.visit_FuncDefNode(node)
2065
2066    def _synthesize_assignment(self, node, env):
2067        # Synthesize assignment node and put it right after defnode
2068        genv = env
2069        while genv.is_py_class_scope or genv.is_c_class_scope:
2070            genv = genv.outer_scope
2071
2072        if genv.is_closure_scope:
2073            rhs = node.py_cfunc_node = ExprNodes.InnerFunctionNode(
2074                node.pos, def_node=node,
2075                pymethdef_cname=node.entry.pymethdef_cname,
2076                code_object=ExprNodes.CodeObjectNode(node))
2077        else:
2078            binding = self.current_directives.get('binding')
2079            rhs = ExprNodes.PyCFunctionNode.from_defnode(node, binding)
2080            node.code_object = rhs.code_object
2081            if node.is_generator:
2082                node.gbody.code_object = node.code_object
2083
2084        if env.is_py_class_scope:
2085            rhs.binding = True
2086
2087        node.is_cyfunction = rhs.binding
2088        return self._create_assignment(node, rhs, env)
2089
2090    def _create_assignment(self, def_node, rhs, env):
2091        if def_node.decorators:
2092            for decorator in def_node.decorators[::-1]:
2093                rhs = ExprNodes.SimpleCallNode(
2094                    decorator.pos,
2095                    function = decorator.decorator,
2096                    args = [rhs])
2097            def_node.decorators = None
2098
2099        assmt = Nodes.SingleAssignmentNode(
2100            def_node.pos,
2101            lhs=ExprNodes.NameNode(def_node.pos, name=def_node.name),
2102            rhs=rhs)
2103        assmt.analyse_declarations(env)
2104        return assmt
2105
2106    def visit_ScopedExprNode(self, node):
2107        env = self.current_env()
2108        node.analyse_declarations(env)
2109        # the node may or may not have a local scope
2110        if node.has_local_scope:
2111            self.seen_vars_stack.append(set(self.seen_vars_stack[-1]))
2112            self.enter_scope(node, node.expr_scope)
2113            node.analyse_scoped_declarations(node.expr_scope)
2114            self.visitchildren(node)
2115            self.exit_scope()
2116            self.seen_vars_stack.pop()
2117        else:
2118            node.analyse_scoped_declarations(env)
2119            self.visitchildren(node)
2120        return node
2121
2122    def visit_TempResultFromStatNode(self, node):
2123        self.visitchildren(node)
2124        node.analyse_declarations(self.current_env())
2125        return node
2126
2127    def visit_CppClassNode(self, node):
2128        if node.visibility == 'extern':
2129            return None
2130        else:
2131            return self.visit_ClassDefNode(node)
2132
2133    def visit_CStructOrUnionDefNode(self, node):
2134        # Create a wrapper node if needed.
2135        # We want to use the struct type information (so it can't happen
2136        # before this phase) but also create new objects to be declared
2137        # (so it can't happen later).
2138        # Note that we don't return the original node, as it is
2139        # never used after this phase.
2140        if True:  # private (default)
2141            return None
2142
2143        self_value = ExprNodes.AttributeNode(
2144            pos = node.pos,
2145            obj = ExprNodes.NameNode(pos=node.pos, name=u"self"),
2146            attribute = EncodedString(u"value"))
2147        var_entries = node.entry.type.scope.var_entries
2148        attributes = []
2149        for entry in var_entries:
2150            attributes.append(ExprNodes.AttributeNode(pos = entry.pos,
2151                                                      obj = self_value,
2152                                                      attribute = entry.name))
2153        # __init__ assignments
2154        init_assignments = []
2155        for entry, attr in zip(var_entries, attributes):
2156            # TODO: branch on visibility
2157            init_assignments.append(self.init_assignment.substitute({
2158                    u"VALUE": ExprNodes.NameNode(entry.pos, name = entry.name),
2159                    u"ATTR": attr,
2160                }, pos = entry.pos))
2161
2162        # create the class
2163        str_format = u"%s(%s)" % (node.entry.type.name, ("%s, " * len(attributes))[:-2])
2164        wrapper_class = self.struct_or_union_wrapper.substitute({
2165            u"INIT_ASSIGNMENTS": Nodes.StatListNode(node.pos, stats = init_assignments),
2166            u"IS_UNION": ExprNodes.BoolNode(node.pos, value = not node.entry.type.is_struct),
2167            u"MEMBER_TUPLE": ExprNodes.TupleNode(node.pos, args=attributes),
2168            u"STR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format)),
2169            u"REPR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format.replace("%s", "%r"))),
2170        }, pos = node.pos).stats[0]
2171        wrapper_class.class_name = node.name
2172        wrapper_class.shadow = True
2173        class_body = wrapper_class.body.stats
2174
2175        # fix value type
2176        assert isinstance(class_body[0].base_type, Nodes.CSimpleBaseTypeNode)
2177        class_body[0].base_type.name = node.name
2178
2179        # fix __init__ arguments
2180        init_method = class_body[1]
2181        assert isinstance(init_method, Nodes.DefNode) and init_method.name == '__init__'
2182        arg_template = init_method.args[1]
2183        if not node.entry.type.is_struct:
2184            arg_template.kw_only = True
2185        del init_method.args[1]
2186        for entry, attr in zip(var_entries, attributes):
2187            arg = copy.deepcopy(arg_template)
2188            arg.declarator.name = entry.name
2189            init_method.args.append(arg)
2190
2191        # setters/getters
2192        for entry, attr in zip(var_entries, attributes):
2193            # TODO: branch on visibility
2194            if entry.type.is_pyobject:
2195                template = self.basic_pyobject_property
2196            else:
2197                template = self.basic_property
2198            property = template.substitute({
2199                    u"ATTR": attr,
2200                }, pos = entry.pos).stats[0]
2201            property.name = entry.name
2202            wrapper_class.body.stats.append(property)
2203
2204        wrapper_class.analyse_declarations(self.current_env())
2205        return self.visit_CClassDefNode(wrapper_class)
2206
2207    # Some nodes are no longer needed after declaration
2208    # analysis and can be dropped. The analysis was performed
2209    # on these nodes in a separate recursive process from the
2210    # enclosing function or module, so we can simply drop them.
2211    def visit_CDeclaratorNode(self, node):
2212        # necessary to ensure that all CNameDeclaratorNodes are visited.
2213        self.visitchildren(node)
2214        return node
2215
2216    def visit_CTypeDefNode(self, node):
2217        return node
2218
2219    def visit_CBaseTypeNode(self, node):
2220        return None
2221
2222    def visit_CEnumDefNode(self, node):
2223        if node.visibility == 'public':
2224            return node
2225        else:
2226            return None
2227
2228    def visit_CNameDeclaratorNode(self, node):
2229        if node.name in self.seen_vars_stack[-1]:
2230            entry = self.current_env().lookup(node.name)
2231            if (entry is None or entry.visibility != 'extern'
2232                    and not entry.scope.is_c_class_scope):
2233                error(node.pos, "cdef variable '%s' declared after it is used" % node.name)
2234        self.visitchildren(node)
2235        return node
2236
2237    def visit_CVarDefNode(self, node):
2238        # to ensure all CNameDeclaratorNodes are visited.
2239        self.visitchildren(node)
2240        return None
2241
2242    def visit_CnameDecoratorNode(self, node):
2243        child_node = self.visit(node.node)
2244        if not child_node:
2245            return None
2246        if type(child_node) is list:  # Assignment synthesized
2247            node.child_node = child_node[0]
2248            return [node] + child_node[1:]
2249        node.node = child_node
2250        return node
2251
2252    def create_Property(self, entry):
2253        if entry.visibility == 'public':
2254            if entry.type.is_pyobject:
2255                template = self.basic_pyobject_property
2256            else:
2257                template = self.basic_property
2258        elif entry.visibility == 'readonly':
2259            template = self.basic_property_ro
2260        property = template.substitute({
2261                u"ATTR": ExprNodes.AttributeNode(pos=entry.pos,
2262                                                 obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
2263                                                 attribute=entry.name),
2264            }, pos=entry.pos).stats[0]
2265        property.name = entry.name
2266        property.doc = entry.doc
2267        return property
2268
2269
2270class CalculateQualifiedNamesTransform(EnvTransform):
2271    """
2272    Calculate and store the '__qualname__' and the global
2273    module name on some nodes.
2274    """
2275    def visit_ModuleNode(self, node):
2276        self.module_name = self.global_scope().qualified_name
2277        self.qualified_name = []
2278        _super = super(CalculateQualifiedNamesTransform, self)
2279        self._super_visit_FuncDefNode = _super.visit_FuncDefNode
2280        self._super_visit_ClassDefNode = _super.visit_ClassDefNode
2281        self.visitchildren(node)
2282        return node
2283
2284    def _set_qualname(self, node, name=None):
2285        if name:
2286            qualname = self.qualified_name[:]
2287            qualname.append(name)
2288        else:
2289            qualname = self.qualified_name
2290        node.qualname = EncodedString('.'.join(qualname))
2291        node.module_name = self.module_name
2292
2293    def _append_entry(self, entry):
2294        if entry.is_pyglobal and not entry.is_pyclass_attr:
2295            self.qualified_name = [entry.name]
2296        else:
2297            self.qualified_name.append(entry.name)
2298
2299    def visit_ClassNode(self, node):
2300        self._set_qualname(node, node.name)
2301        self.visitchildren(node)
2302        return node
2303
2304    def visit_PyClassNamespaceNode(self, node):
2305        # class name was already added by parent node
2306        self._set_qualname(node)
2307        self.visitchildren(node)
2308        return node
2309
2310    def visit_PyCFunctionNode(self, node):
2311        orig_qualified_name = self.qualified_name[:]
2312        if node.def_node.is_wrapper and self.qualified_name and self.qualified_name[-1] == '<locals>':
2313            self.qualified_name.pop()
2314            self._set_qualname(node)
2315        else:
2316            self._set_qualname(node, node.def_node.name)
2317        self.visitchildren(node)
2318        self.qualified_name = orig_qualified_name
2319        return node
2320
2321    def visit_DefNode(self, node):
2322        if node.is_wrapper and self.qualified_name:
2323            assert self.qualified_name[-1] == '<locals>', self.qualified_name
2324            orig_qualified_name = self.qualified_name[:]
2325            self.qualified_name.pop()
2326            self._set_qualname(node)
2327            self._super_visit_FuncDefNode(node)
2328            self.qualified_name = orig_qualified_name
2329        else:
2330            self._set_qualname(node, node.name)
2331            self.visit_FuncDefNode(node)
2332        return node
2333
2334    def visit_FuncDefNode(self, node):
2335        orig_qualified_name = self.qualified_name[:]
2336        if getattr(node, 'name', None) == '<lambda>':
2337            self.qualified_name.append('<lambda>')
2338        else:
2339            self._append_entry(node.entry)
2340        self.qualified_name.append('<locals>')
2341        self._super_visit_FuncDefNode(node)
2342        self.qualified_name = orig_qualified_name
2343        return node
2344
2345    def visit_ClassDefNode(self, node):
2346        orig_qualified_name = self.qualified_name[:]
2347        entry = (getattr(node, 'entry', None) or             # PyClass
2348                 self.current_env().lookup_here(node.target.name))  # CClass
2349        self._append_entry(entry)
2350        self._super_visit_ClassDefNode(node)
2351        self.qualified_name = orig_qualified_name
2352        return node
2353
2354
2355class AnalyseExpressionsTransform(CythonTransform):
2356
2357    def visit_ModuleNode(self, node):
2358        node.scope.infer_types()
2359        node.body = node.body.analyse_expressions(node.scope)
2360        self.visitchildren(node)
2361        return node
2362
2363    def visit_FuncDefNode(self, node):
2364        node.local_scope.infer_types()
2365        node.body = node.body.analyse_expressions(node.local_scope)
2366        self.visitchildren(node)
2367        return node
2368
2369    def visit_ScopedExprNode(self, node):
2370        if node.has_local_scope:
2371            node.expr_scope.infer_types()
2372            node = node.analyse_scoped_expressions(node.expr_scope)
2373        self.visitchildren(node)
2374        return node
2375
2376    def visit_IndexNode(self, node):
2377        """
2378        Replace index nodes used to specialize cdef functions with fused
2379        argument types with the Attribute- or NameNode referring to the
2380        function. We then need to copy over the specialization properties to
2381        the attribute or name node.
2382
2383        Because the indexing might be a Python indexing operation on a fused
2384        function, or (usually) a Cython indexing operation, we need to
2385        re-analyse the types.
2386        """
2387        self.visit_Node(node)
2388        if node.is_fused_index and not node.type.is_error:
2389            node = node.base
2390        return node
2391
2392
2393class FindInvalidUseOfFusedTypes(CythonTransform):
2394
2395    def visit_FuncDefNode(self, node):
2396        # Errors related to use in functions with fused args will already
2397        # have been detected
2398        if not node.has_fused_arguments:
2399            if not node.is_generator_body and node.return_type.is_fused:
2400                error(node.pos, "Return type is not specified as argument type")
2401            else:
2402                self.visitchildren(node)
2403
2404        return node
2405
2406    def visit_ExprNode(self, node):
2407        if node.type and node.type.is_fused:
2408            error(node.pos, "Invalid use of fused types, type cannot be specialized")
2409        else:
2410            self.visitchildren(node)
2411
2412        return node
2413
2414
2415class ExpandInplaceOperators(EnvTransform):
2416
2417    def visit_InPlaceAssignmentNode(self, node):
2418        lhs = node.lhs
2419        rhs = node.rhs
2420        if lhs.type.is_cpp_class:
2421            # No getting around this exact operator here.
2422            return node
2423        if isinstance(lhs, ExprNodes.BufferIndexNode):
2424            # There is code to handle this case in InPlaceAssignmentNode
2425            return node
2426
2427        env = self.current_env()
2428        def side_effect_free_reference(node, setting=False):
2429            if node.is_name:
2430                return node, []
2431            elif node.type.is_pyobject and not setting:
2432                node = LetRefNode(node)
2433                return node, [node]
2434            elif node.is_subscript:
2435                base, temps = side_effect_free_reference(node.base)
2436                index = LetRefNode(node.index)
2437                return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index]
2438            elif node.is_attribute:
2439                obj, temps = side_effect_free_reference(node.obj)
2440                return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps
2441            elif isinstance(node, ExprNodes.BufferIndexNode):
2442                raise ValueError("Don't allow things like attributes of buffer indexing operations")
2443            else:
2444                node = LetRefNode(node)
2445                return node, [node]
2446        try:
2447            lhs, let_ref_nodes = side_effect_free_reference(lhs, setting=True)
2448        except ValueError:
2449            return node
2450        dup = lhs.__class__(**lhs.__dict__)
2451        binop = ExprNodes.binop_node(node.pos,
2452                                     operator = node.operator,
2453                                     operand1 = dup,
2454                                     operand2 = rhs,
2455                                     inplace=True)
2456        # Manually analyse types for new node.
2457        lhs = lhs.analyse_target_types(env)
2458        dup.analyse_types(env)  # FIXME: no need to reanalyse the copy, right?
2459        binop.analyse_operation(env)
2460        node = Nodes.SingleAssignmentNode(
2461            node.pos,
2462            lhs = lhs,
2463            rhs=binop.coerce_to(lhs.type, env))
2464        # Use LetRefNode to avoid side effects.
2465        let_ref_nodes.reverse()
2466        for t in let_ref_nodes:
2467            node = LetNode(t, node)
2468        return node
2469
2470    def visit_ExprNode(self, node):
2471        # In-place assignments can't happen within an expression.
2472        return node
2473
2474
2475class AdjustDefByDirectives(CythonTransform, SkipDeclarations):
2476    """
2477    Adjust function and class definitions by the decorator directives:
2478
2479    @cython.cfunc
2480    @cython.cclass
2481    @cython.ccall
2482    @cython.inline
2483    @cython.nogil
2484    """
2485
2486    def visit_ModuleNode(self, node):
2487        self.directives = node.directives
2488        self.in_py_class = False
2489        self.visitchildren(node)
2490        return node
2491
2492    def visit_CompilerDirectivesNode(self, node):
2493        old_directives = self.directives
2494        self.directives = node.directives
2495        self.visitchildren(node)
2496        self.directives = old_directives
2497        return node
2498
2499    def visit_DefNode(self, node):
2500        modifiers = []
2501        if 'inline' in self.directives:
2502            modifiers.append('inline')
2503        nogil = self.directives.get('nogil')
2504        except_val = self.directives.get('exceptval')
2505        return_type_node = self.directives.get('returns')
2506        if return_type_node is None and self.directives['annotation_typing']:
2507            return_type_node = node.return_type_annotation
2508            # for Python annotations, prefer safe exception handling by default
2509            if return_type_node is not None and except_val is None:
2510                except_val = (None, True)  # except *
2511        elif except_val is None:
2512            # backward compatible default: no exception check, unless there's also a "@returns" declaration
2513            except_val = (None, True if return_type_node else False)
2514        if 'ccall' in self.directives:
2515            node = node.as_cfunction(
2516                overridable=True, modifiers=modifiers, nogil=nogil,
2517                returns=return_type_node, except_val=except_val)
2518            return self.visit(node)
2519        if 'cfunc' in self.directives:
2520            if self.in_py_class:
2521                error(node.pos, "cfunc directive is not allowed here")
2522            else:
2523                node = node.as_cfunction(
2524                    overridable=False, modifiers=modifiers, nogil=nogil,
2525                    returns=return_type_node, except_val=except_val)
2526                return self.visit(node)
2527        if 'inline' in modifiers:
2528            error(node.pos, "Python functions cannot be declared 'inline'")
2529        if nogil:
2530            # TODO: turn this into a "with gil" declaration.
2531            error(node.pos, "Python functions cannot be declared 'nogil'")
2532        self.visitchildren(node)
2533        return node
2534
2535    def visit_LambdaNode(self, node):
2536        # No directives should modify lambdas or generator expressions (and also nothing in them).
2537        return node
2538
2539    def visit_PyClassDefNode(self, node):
2540        if 'cclass' in self.directives:
2541            node = node.as_cclass()
2542            return self.visit(node)
2543        else:
2544            old_in_pyclass = self.in_py_class
2545            self.in_py_class = True
2546            self.visitchildren(node)
2547            self.in_py_class = old_in_pyclass
2548            return node
2549
2550    def visit_CClassDefNode(self, node):
2551        old_in_pyclass = self.in_py_class
2552        self.in_py_class = False
2553        self.visitchildren(node)
2554        self.in_py_class = old_in_pyclass
2555        return node
2556
2557
2558class AlignFunctionDefinitions(CythonTransform):
2559    """
2560    This class takes the signatures from a .pxd file and applies them to
2561    the def methods in a .py file.
2562    """
2563
2564    def visit_ModuleNode(self, node):
2565        self.scope = node.scope
2566        self.visitchildren(node)
2567        return node
2568
2569    def visit_PyClassDefNode(self, node):
2570        pxd_def = self.scope.lookup(node.name)
2571        if pxd_def:
2572            if pxd_def.is_cclass:
2573                return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
2574            elif not pxd_def.scope or not pxd_def.scope.is_builtin_scope:
2575                error(node.pos, "'%s' redeclared" % node.name)
2576                if pxd_def.pos:
2577                    error(pxd_def.pos, "previous declaration here")
2578                return None
2579        return node
2580
2581    def visit_CClassDefNode(self, node, pxd_def=None):
2582        if pxd_def is None:
2583            pxd_def = self.scope.lookup(node.class_name)
2584        if pxd_def:
2585            if not pxd_def.defined_in_pxd:
2586                return node
2587            outer_scope = self.scope
2588            self.scope = pxd_def.type.scope
2589        self.visitchildren(node)
2590        if pxd_def:
2591            self.scope = outer_scope
2592        return node
2593
2594    def visit_DefNode(self, node):
2595        pxd_def = self.scope.lookup(node.name)
2596        if pxd_def and (not pxd_def.scope or not pxd_def.scope.is_builtin_scope):
2597            if not pxd_def.is_cfunction:
2598                error(node.pos, "'%s' redeclared" % node.name)
2599                if pxd_def.pos:
2600                    error(pxd_def.pos, "previous declaration here")
2601                return None
2602            node = node.as_cfunction(pxd_def)
2603        # Enable this when nested cdef functions are allowed.
2604        # self.visitchildren(node)
2605        return node
2606
2607    def visit_ExprNode(self, node):
2608        # ignore lambdas and everything else that appears in expressions
2609        return node
2610
2611
2612class AutoCpdefFunctionDefinitions(CythonTransform):
2613
2614    def visit_ModuleNode(self, node):
2615        self.directives = node.directives
2616        self.imported_names = set()  # hack, see visit_FromImportStatNode()
2617        self.scope = node.scope
2618        self.visitchildren(node)
2619        return node
2620
2621    def visit_DefNode(self, node):
2622        if (self.scope.is_module_scope and self.directives['auto_cpdef']
2623                and node.name not in self.imported_names
2624                and node.is_cdef_func_compatible()):
2625            # FIXME: cpdef-ing should be done in analyse_declarations()
2626            node = node.as_cfunction(scope=self.scope)
2627        return node
2628
2629    def visit_CClassDefNode(self, node, pxd_def=None):
2630        if pxd_def is None:
2631            pxd_def = self.scope.lookup(node.class_name)
2632        if pxd_def:
2633            if not pxd_def.defined_in_pxd:
2634                return node
2635            outer_scope = self.scope
2636            self.scope = pxd_def.type.scope
2637        self.visitchildren(node)
2638        if pxd_def:
2639            self.scope = outer_scope
2640        return node
2641
2642    def visit_FromImportStatNode(self, node):
2643        # hack to prevent conditional import fallback functions from
2644        # being cdpef-ed (global Python variables currently conflict
2645        # with imports)
2646        if self.scope.is_module_scope:
2647            for name, _ in node.items:
2648                self.imported_names.add(name)
2649        return node
2650
2651    def visit_ExprNode(self, node):
2652        # ignore lambdas and everything else that appears in expressions
2653        return node
2654
2655
2656class RemoveUnreachableCode(CythonTransform):
2657    def visit_StatListNode(self, node):
2658        if not self.current_directives['remove_unreachable']:
2659            return node
2660        self.visitchildren(node)
2661        for idx, stat in enumerate(node.stats):
2662            idx += 1
2663            if stat.is_terminator:
2664                if idx < len(node.stats):
2665                    if self.current_directives['warn.unreachable']:
2666                        warning(node.stats[idx].pos, "Unreachable code", 2)
2667                    node.stats = node.stats[:idx]
2668                node.is_terminator = True
2669                break
2670        return node
2671
2672    def visit_IfClauseNode(self, node):
2673        self.visitchildren(node)
2674        if node.body.is_terminator:
2675            node.is_terminator = True
2676        return node
2677
2678    def visit_IfStatNode(self, node):
2679        self.visitchildren(node)
2680        if node.else_clause and node.else_clause.is_terminator:
2681            for clause in node.if_clauses:
2682                if not clause.is_terminator:
2683                    break
2684            else:
2685                node.is_terminator = True
2686        return node
2687
2688    def visit_TryExceptStatNode(self, node):
2689        self.visitchildren(node)
2690        if node.body.is_terminator and node.else_clause:
2691            if self.current_directives['warn.unreachable']:
2692                warning(node.else_clause.pos, "Unreachable code", 2)
2693            node.else_clause = None
2694        return node
2695
2696    def visit_TryFinallyStatNode(self, node):
2697        self.visitchildren(node)
2698        if node.finally_clause.is_terminator:
2699            node.is_terminator = True
2700        return node
2701
2702
2703class YieldNodeCollector(TreeVisitor):
2704
2705    def __init__(self):
2706        super(YieldNodeCollector, self).__init__()
2707        self.yields = []
2708        self.returns = []
2709        self.finallys = []
2710        self.excepts = []
2711        self.has_return_value = False
2712        self.has_yield = False
2713        self.has_await = False
2714
2715    def visit_Node(self, node):
2716        self.visitchildren(node)
2717
2718    def visit_YieldExprNode(self, node):
2719        self.yields.append(node)
2720        self.has_yield = True
2721        self.visitchildren(node)
2722
2723    def visit_AwaitExprNode(self, node):
2724        self.yields.append(node)
2725        self.has_await = True
2726        self.visitchildren(node)
2727
2728    def visit_ReturnStatNode(self, node):
2729        self.visitchildren(node)
2730        if node.value:
2731            self.has_return_value = True
2732        self.returns.append(node)
2733
2734    def visit_TryFinallyStatNode(self, node):
2735        self.visitchildren(node)
2736        self.finallys.append(node)
2737
2738    def visit_TryExceptStatNode(self, node):
2739        self.visitchildren(node)
2740        self.excepts.append(node)
2741
2742    def visit_ClassDefNode(self, node):
2743        pass
2744
2745    def visit_FuncDefNode(self, node):
2746        pass
2747
2748    def visit_LambdaNode(self, node):
2749        pass
2750
2751    def visit_GeneratorExpressionNode(self, node):
2752        pass
2753
2754    def visit_CArgDeclNode(self, node):
2755        # do not look into annotations
2756        # FIXME: support (yield) in default arguments (currently crashes)
2757        pass
2758
2759
2760class MarkClosureVisitor(CythonTransform):
2761
2762    def visit_ModuleNode(self, node):
2763        self.needs_closure = False
2764        self.visitchildren(node)
2765        return node
2766
2767    def visit_FuncDefNode(self, node):
2768        self.needs_closure = False
2769        self.visitchildren(node)
2770        node.needs_closure = self.needs_closure
2771        self.needs_closure = True
2772
2773        collector = YieldNodeCollector()
2774        collector.visitchildren(node)
2775
2776        if node.is_async_def:
2777            coroutine_type = Nodes.AsyncDefNode
2778            if collector.has_yield:
2779                coroutine_type = Nodes.AsyncGenNode
2780                for yield_expr in collector.yields + collector.returns:
2781                    yield_expr.in_async_gen = True
2782            elif self.current_directives['iterable_coroutine']:
2783                coroutine_type = Nodes.IterableAsyncDefNode
2784        elif collector.has_await:
2785            found = next(y for y in collector.yields if y.is_await)
2786            error(found.pos, "'await' not allowed in generators (use 'yield')")
2787            return node
2788        elif collector.has_yield:
2789            coroutine_type = Nodes.GeneratorDefNode
2790        else:
2791            return node
2792
2793        for i, yield_expr in enumerate(collector.yields, 1):
2794            yield_expr.label_num = i
2795        for retnode in collector.returns + collector.finallys + collector.excepts:
2796            retnode.in_generator = True
2797
2798        gbody = Nodes.GeneratorBodyDefNode(
2799            pos=node.pos, name=node.name, body=node.body,
2800            is_async_gen_body=node.is_async_def and collector.has_yield)
2801        coroutine = coroutine_type(
2802            pos=node.pos, name=node.name, args=node.args,
2803            star_arg=node.star_arg, starstar_arg=node.starstar_arg,
2804            doc=node.doc, decorators=node.decorators,
2805            gbody=gbody, lambda_name=node.lambda_name,
2806            return_type_annotation=node.return_type_annotation)
2807        return coroutine
2808
2809    def visit_CFuncDefNode(self, node):
2810        self.needs_closure = False
2811        self.visitchildren(node)
2812        node.needs_closure = self.needs_closure
2813        self.needs_closure = True
2814        if node.needs_closure and node.overridable:
2815            error(node.pos, "closures inside cpdef functions not yet supported")
2816        return node
2817
2818    def visit_LambdaNode(self, node):
2819        self.needs_closure = False
2820        self.visitchildren(node)
2821        node.needs_closure = self.needs_closure
2822        self.needs_closure = True
2823        return node
2824
2825    def visit_ClassDefNode(self, node):
2826        self.visitchildren(node)
2827        self.needs_closure = True
2828        return node
2829
2830
2831class CreateClosureClasses(CythonTransform):
2832    # Output closure classes in module scope for all functions
2833    # that really need it.
2834
2835    def __init__(self, context):
2836        super(CreateClosureClasses, self).__init__(context)
2837        self.path = []
2838        self.in_lambda = False
2839
2840    def visit_ModuleNode(self, node):
2841        self.module_scope = node.scope
2842        self.visitchildren(node)
2843        return node
2844
2845    def find_entries_used_in_closures(self, node):
2846        from_closure = []
2847        in_closure = []
2848        for scope in node.local_scope.iter_local_scopes():
2849            for name, entry in scope.entries.items():
2850                if not name:
2851                    continue
2852                if entry.from_closure:
2853                    from_closure.append((name, entry))
2854                elif entry.in_closure:
2855                    in_closure.append((name, entry))
2856        return from_closure, in_closure
2857
2858    def create_class_from_scope(self, node, target_module_scope, inner_node=None):
2859        # move local variables into closure
2860        if node.is_generator:
2861            for scope in node.local_scope.iter_local_scopes():
2862                for entry in scope.entries.values():
2863                    if not (entry.from_closure or entry.is_pyglobal or entry.is_cglobal):
2864                        entry.in_closure = True
2865
2866        from_closure, in_closure = self.find_entries_used_in_closures(node)
2867        in_closure.sort()
2868
2869        # Now from the beginning
2870        node.needs_closure = False
2871        node.needs_outer_scope = False
2872
2873        func_scope = node.local_scope
2874        cscope = node.entry.scope
2875        while cscope.is_py_class_scope or cscope.is_c_class_scope:
2876            cscope = cscope.outer_scope
2877
2878        if not from_closure and (self.path or inner_node):
2879            if not inner_node:
2880                if not node.py_cfunc_node:
2881                    raise InternalError("DefNode does not have assignment node")
2882                inner_node = node.py_cfunc_node
2883            inner_node.needs_closure_code = False
2884            node.needs_outer_scope = False
2885
2886        if node.is_generator:
2887            pass
2888        elif not in_closure and not from_closure:
2889            return
2890        elif not in_closure:
2891            func_scope.is_passthrough = True
2892            func_scope.scope_class = cscope.scope_class
2893            node.needs_outer_scope = True
2894            return
2895
2896        # entry.cname can contain periods (eg. a derived C method of a class).
2897        # We want to use the cname as part of a C struct name, so we replace
2898        # periods with double underscores.
2899        as_name = '%s_%s' % (
2900            target_module_scope.next_id(Naming.closure_class_prefix),
2901            node.entry.cname.replace('.','__'))
2902        as_name = EncodedString(as_name)
2903
2904        entry = target_module_scope.declare_c_class(
2905            name=as_name, pos=node.pos, defining=True,
2906            implementing=True)
2907        entry.type.is_final_type = True
2908
2909        func_scope.scope_class = entry
2910        class_scope = entry.type.scope
2911        class_scope.is_internal = True
2912        class_scope.is_closure_class_scope = True
2913        if node.is_async_def or node.is_generator:
2914            # Generators need their closure intact during cleanup as they resume to handle GeneratorExit
2915            class_scope.directives['no_gc_clear'] = True
2916        if Options.closure_freelist_size:
2917            class_scope.directives['freelist'] = Options.closure_freelist_size
2918
2919        if from_closure:
2920            assert cscope.is_closure_scope
2921            class_scope.declare_var(pos=node.pos,
2922                                    name=Naming.outer_scope_cname,
2923                                    cname=Naming.outer_scope_cname,
2924                                    type=cscope.scope_class.type,
2925                                    is_cdef=True)
2926            node.needs_outer_scope = True
2927        for name, entry in in_closure:
2928            closure_entry = class_scope.declare_var(
2929                pos=entry.pos,
2930                name=entry.name if not entry.in_subscope else None,
2931                cname=entry.cname,
2932                type=entry.type,
2933                is_cdef=True)
2934            if entry.is_declared_generic:
2935                closure_entry.is_declared_generic = 1
2936        node.needs_closure = True
2937        # Do it here because other classes are already checked
2938        target_module_scope.check_c_class(func_scope.scope_class)
2939
2940    def visit_LambdaNode(self, node):
2941        if not isinstance(node.def_node, Nodes.DefNode):
2942            # fused function, an error has been previously issued
2943            return node
2944
2945        was_in_lambda = self.in_lambda
2946        self.in_lambda = True
2947        self.create_class_from_scope(node.def_node, self.module_scope, node)
2948        self.visitchildren(node)
2949        self.in_lambda = was_in_lambda
2950        return node
2951
2952    def visit_FuncDefNode(self, node):
2953        if self.in_lambda:
2954            self.visitchildren(node)
2955            return node
2956        if node.needs_closure or self.path:
2957            self.create_class_from_scope(node, self.module_scope)
2958            self.path.append(node)
2959            self.visitchildren(node)
2960            self.path.pop()
2961        return node
2962
2963    def visit_GeneratorBodyDefNode(self, node):
2964        self.visitchildren(node)
2965        return node
2966
2967    def visit_CFuncDefNode(self, node):
2968        if not node.overridable:
2969            return self.visit_FuncDefNode(node)
2970        else:
2971            self.visitchildren(node)
2972            return node
2973
2974
2975class InjectGilHandling(VisitorTransform, SkipDeclarations):
2976    """
2977    Allow certain Python operations inside of nogil blocks by implicitly acquiring the GIL.
2978
2979    Must run before the AnalyseDeclarationsTransform to make sure the GILStatNodes get
2980    set up, parallel sections know that the GIL is acquired inside of them, etc.
2981    """
2982    nogil = False
2983
2984    # special node handling
2985
2986    def _inject_gil_in_nogil(self, node):
2987        """Allow the (Python statement) node in nogil sections by wrapping it in a 'with gil' block."""
2988        if self.nogil:
2989            node = Nodes.GILStatNode(node.pos, state='gil', body=node)
2990        return node
2991
2992    visit_RaiseStatNode = _inject_gil_in_nogil
2993    visit_PrintStatNode = _inject_gil_in_nogil  # sadly, not the function
2994
2995    # further candidates:
2996    # def visit_ReraiseStatNode(self, node):
2997
2998    # nogil tracking
2999
3000    def visit_GILStatNode(self, node):
3001        was_nogil = self.nogil
3002        self.nogil = (node.state == 'nogil')
3003        self.visitchildren(node)
3004        self.nogil = was_nogil
3005        return node
3006
3007    def visit_CFuncDefNode(self, node):
3008        was_nogil = self.nogil
3009        if isinstance(node.declarator, Nodes.CFuncDeclaratorNode):
3010            self.nogil = node.declarator.nogil and not node.declarator.with_gil
3011        self.visitchildren(node)
3012        self.nogil = was_nogil
3013        return node
3014
3015    def visit_ParallelRangeNode(self, node):
3016        was_nogil = self.nogil
3017        self.nogil = node.nogil
3018        self.visitchildren(node)
3019        self.nogil = was_nogil
3020        return node
3021
3022    def visit_ExprNode(self, node):
3023        # No special GIL handling inside of expressions for now.
3024        return node
3025
3026    visit_Node = VisitorTransform.recurse_to_children
3027
3028
3029class GilCheck(VisitorTransform):
3030    """
3031    Call `node.gil_check(env)` on each node to make sure we hold the
3032    GIL when we need it.  Raise an error when on Python operations
3033    inside a `nogil` environment.
3034
3035    Additionally, raise exceptions for closely nested with gil or with nogil
3036    statements. The latter would abort Python.
3037    """
3038
3039    def __call__(self, root):
3040        self.env_stack = [root.scope]
3041        self.nogil = False
3042
3043        # True for 'cdef func() nogil:' functions, as the GIL may be held while
3044        # calling this function (thus contained 'nogil' blocks may be valid).
3045        self.nogil_declarator_only = False
3046        return super(GilCheck, self).__call__(root)
3047
3048    def _visit_scoped_children(self, node, gil_state):
3049        was_nogil = self.nogil
3050        outer_attrs = node.outer_attrs
3051        if outer_attrs and len(self.env_stack) > 1:
3052            self.nogil = self.env_stack[-2].nogil
3053            self.visitchildren(node, outer_attrs)
3054
3055        self.nogil = gil_state
3056        self.visitchildren(node, attrs=None, exclude=outer_attrs)
3057        self.nogil = was_nogil
3058
3059    def visit_FuncDefNode(self, node):
3060        self.env_stack.append(node.local_scope)
3061        inner_nogil = node.local_scope.nogil
3062
3063        if inner_nogil:
3064            self.nogil_declarator_only = True
3065
3066        if inner_nogil and node.nogil_check:
3067            node.nogil_check(node.local_scope)
3068
3069        self._visit_scoped_children(node, inner_nogil)
3070
3071        # This cannot be nested, so it doesn't need backup/restore
3072        self.nogil_declarator_only = False
3073
3074        self.env_stack.pop()
3075        return node
3076
3077    def visit_GILStatNode(self, node):
3078        if node.condition is not None:
3079            error(node.condition.pos,
3080                  "Non-constant condition in a "
3081                  "`with %s(<condition>)` statement" % node.state)
3082            return node
3083
3084        if self.nogil and node.nogil_check:
3085            node.nogil_check()
3086
3087        was_nogil = self.nogil
3088        is_nogil = (node.state == 'nogil')
3089
3090        if was_nogil == is_nogil and not self.nogil_declarator_only:
3091            if not was_nogil:
3092                error(node.pos, "Trying to acquire the GIL while it is "
3093                                "already held.")
3094            else:
3095                error(node.pos, "Trying to release the GIL while it was "
3096                                "previously released.")
3097
3098        if isinstance(node.finally_clause, Nodes.StatListNode):
3099            # The finally clause of the GILStatNode is a GILExitNode,
3100            # which is wrapped in a StatListNode. Just unpack that.
3101            node.finally_clause, = node.finally_clause.stats
3102
3103        self._visit_scoped_children(node, is_nogil)
3104        return node
3105
3106    def visit_ParallelRangeNode(self, node):
3107        if node.nogil:
3108            node.nogil = False
3109            node = Nodes.GILStatNode(node.pos, state='nogil', body=node)
3110            return self.visit_GILStatNode(node)
3111
3112        if not self.nogil:
3113            error(node.pos, "prange() can only be used without the GIL")
3114            # Forget about any GIL-related errors that may occur in the body
3115            return None
3116
3117        node.nogil_check(self.env_stack[-1])
3118        self.visitchildren(node)
3119        return node
3120
3121    def visit_ParallelWithBlockNode(self, node):
3122        if not self.nogil:
3123            error(node.pos, "The parallel section may only be used without "
3124                            "the GIL")
3125            return None
3126
3127        if node.nogil_check:
3128            # It does not currently implement this, but test for it anyway to
3129            # avoid potential future surprises
3130            node.nogil_check(self.env_stack[-1])
3131
3132        self.visitchildren(node)
3133        return node
3134
3135    def visit_TryFinallyStatNode(self, node):
3136        """
3137        Take care of try/finally statements in nogil code sections.
3138        """
3139        if not self.nogil or isinstance(node, Nodes.GILStatNode):
3140            return self.visit_Node(node)
3141
3142        node.nogil_check = None
3143        node.is_try_finally_in_nogil = True
3144        self.visitchildren(node)
3145        return node
3146
3147    def visit_Node(self, node):
3148        if self.env_stack and self.nogil and node.nogil_check:
3149            node.nogil_check(self.env_stack[-1])
3150        if node.outer_attrs:
3151            self._visit_scoped_children(node, self.nogil)
3152        else:
3153            self.visitchildren(node)
3154        if self.nogil:
3155            node.in_nogil_context = True
3156        return node
3157
3158
3159class CoerceCppTemps(EnvTransform, SkipDeclarations):
3160    """
3161    For temporary expression that are implemented using std::optional it's necessary the temps are
3162    assigned using `__pyx_t_x = value;` but accessed using `something = (*__pyx_t_x)`. This transform
3163    inserts a coercion node to take care of this, and runs absolutely last (once nothing else can be
3164    inserted into the tree)
3165
3166    TODO: a possible alternative would be to split ExprNode.result() into ExprNode.rhs_rhs() and ExprNode.lhs_rhs()???
3167    """
3168    def visit_ModuleNode(self, node):
3169        if self.current_env().cpp:
3170            # skipping this makes it essentially free for C files
3171            self.visitchildren(node)
3172        return node
3173
3174    def visit_ExprNode(self, node):
3175        self.visitchildren(node)
3176        if (self.current_env().directives['cpp_locals'] and
3177                node.is_temp and node.type.is_cpp_class and
3178                # Fake references are not replaced with "std::optional()".
3179                not node.type.is_fake_reference):
3180            node = ExprNodes.CppOptionalTempCoercion(node)
3181
3182        return node
3183
3184
3185class TransformBuiltinMethods(EnvTransform):
3186    """
3187    Replace Cython's own cython.* builtins by the corresponding tree nodes.
3188    """
3189
3190    def visit_SingleAssignmentNode(self, node):
3191        if node.declaration_only:
3192            return None
3193        else:
3194            self.visitchildren(node)
3195            return node
3196
3197    def visit_AttributeNode(self, node):
3198        self.visitchildren(node)
3199        return self.visit_cython_attribute(node)
3200
3201    def visit_NameNode(self, node):
3202        return self.visit_cython_attribute(node)
3203
3204    def visit_cython_attribute(self, node):
3205        attribute = node.as_cython_attribute()
3206        if attribute:
3207            if attribute == u'__version__':
3208                from .. import __version__ as version
3209                node = ExprNodes.StringNode(node.pos, value=EncodedString(version))
3210            elif attribute == u'NULL':
3211                node = ExprNodes.NullNode(node.pos)
3212            elif attribute in (u'set', u'frozenset', u'staticmethod'):
3213                node = ExprNodes.NameNode(node.pos, name=EncodedString(attribute),
3214                                          entry=self.current_env().builtin_scope().lookup_here(attribute))
3215            elif PyrexTypes.parse_basic_type(attribute):
3216                pass
3217            elif self.context.cython_scope.lookup_qualified_name(attribute):
3218                pass
3219            else:
3220                error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
3221        return node
3222
3223    def visit_ExecStatNode(self, node):
3224        lenv = self.current_env()
3225        self.visitchildren(node)
3226        if len(node.args) == 1:
3227            node.args.append(ExprNodes.GlobalsExprNode(node.pos))
3228            if not lenv.is_module_scope:
3229                node.args.append(
3230                    ExprNodes.LocalsExprNode(
3231                        node.pos, self.current_scope_node(), lenv))
3232        return node
3233
3234    def _inject_locals(self, node, func_name):
3235        # locals()/dir()/vars() builtins
3236        lenv = self.current_env()
3237        entry = lenv.lookup_here(func_name)
3238        if entry:
3239            # not the builtin
3240            return node
3241        pos = node.pos
3242        if func_name in ('locals', 'vars'):
3243            if func_name == 'locals' and len(node.args) > 0:
3244                error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d"
3245                      % len(node.args))
3246                return node
3247            elif func_name == 'vars':
3248                if len(node.args) > 1:
3249                    error(self.pos, "Builtin 'vars()' called with wrong number of args, expected 0-1, got %d"
3250                          % len(node.args))
3251                if len(node.args) > 0:
3252                    return node  # nothing to do
3253            return ExprNodes.LocalsExprNode(pos, self.current_scope_node(), lenv)
3254        else:  # dir()
3255            if len(node.args) > 1:
3256                error(self.pos, "Builtin 'dir()' called with wrong number of args, expected 0-1, got %d"
3257                      % len(node.args))
3258            if len(node.args) > 0:
3259                # optimised in Builtin.py
3260                return node
3261            if lenv.is_py_class_scope or lenv.is_module_scope:
3262                if lenv.is_py_class_scope:
3263                    pyclass = self.current_scope_node()
3264                    locals_dict = ExprNodes.CloneNode(pyclass.dict)
3265                else:
3266                    locals_dict = ExprNodes.GlobalsExprNode(pos)
3267                return ExprNodes.SortedDictKeysNode(locals_dict)
3268            local_names = sorted(var.name for var in lenv.entries.values() if var.name)
3269            items = [ExprNodes.IdentifierStringNode(pos, value=var)
3270                     for var in local_names]
3271            return ExprNodes.ListNode(pos, args=items)
3272
3273    def visit_PrimaryCmpNode(self, node):
3274        # special case: for in/not-in test, we do not need to sort locals()
3275        self.visitchildren(node)
3276        if node.operator in 'not_in':  # in/not_in
3277            if isinstance(node.operand2, ExprNodes.SortedDictKeysNode):
3278                arg = node.operand2.arg
3279                if isinstance(arg, ExprNodes.NoneCheckNode):
3280                    arg = arg.arg
3281                node.operand2 = arg
3282        return node
3283
3284    def visit_CascadedCmpNode(self, node):
3285        return self.visit_PrimaryCmpNode(node)
3286
3287    def _inject_eval(self, node, func_name):
3288        lenv = self.current_env()
3289        entry = lenv.lookup(func_name)
3290        if len(node.args) != 1 or (entry and not entry.is_builtin):
3291            return node
3292        # Inject globals and locals
3293        node.args.append(ExprNodes.GlobalsExprNode(node.pos))
3294        if not lenv.is_module_scope:
3295            node.args.append(
3296                ExprNodes.LocalsExprNode(
3297                    node.pos, self.current_scope_node(), lenv))
3298        return node
3299
3300    def _inject_super(self, node, func_name):
3301        lenv = self.current_env()
3302        entry = lenv.lookup_here(func_name)
3303        if entry or node.args:
3304            return node
3305        # Inject no-args super
3306        def_node = self.current_scope_node()
3307        if not isinstance(def_node, Nodes.DefNode) or not def_node.args or len(self.env_stack) < 2:
3308            return node
3309        class_node, class_scope = self.env_stack[-2]
3310        if class_scope.is_py_class_scope:
3311            def_node.requires_classobj = True
3312            class_node.class_cell.is_active = True
3313            node.args = [
3314                ExprNodes.ClassCellNode(
3315                    node.pos, is_generator=def_node.is_generator),
3316                ExprNodes.NameNode(node.pos, name=def_node.args[0].name)
3317                ]
3318        elif class_scope.is_c_class_scope:
3319            node.args = [
3320                ExprNodes.NameNode(
3321                    node.pos, name=class_node.scope.name,
3322                    entry=class_node.entry),
3323                ExprNodes.NameNode(node.pos, name=def_node.args[0].name)
3324                ]
3325        return node
3326
3327    def visit_SimpleCallNode(self, node):
3328        # cython.foo
3329        function = node.function.as_cython_attribute()
3330        if function:
3331            if function in InterpretCompilerDirectives.unop_method_nodes:
3332                if len(node.args) != 1:
3333                    error(node.function.pos, u"%s() takes exactly one argument" % function)
3334                else:
3335                    node = InterpretCompilerDirectives.unop_method_nodes[function](
3336                        node.function.pos, operand=node.args[0])
3337            elif function in InterpretCompilerDirectives.binop_method_nodes:
3338                if len(node.args) != 2:
3339                    error(node.function.pos, u"%s() takes exactly two arguments" % function)
3340                else:
3341                    node = InterpretCompilerDirectives.binop_method_nodes[function](
3342                        node.function.pos, operand1=node.args[0], operand2=node.args[1])
3343            elif function == u'cast':
3344                if len(node.args) != 2:
3345                    error(node.function.pos,
3346                          u"cast() takes exactly two arguments and an optional typecheck keyword")
3347                else:
3348                    type = node.args[0].analyse_as_type(self.current_env())
3349                    if type:
3350                        node = ExprNodes.TypecastNode(
3351                            node.function.pos, type=type, operand=node.args[1], typecheck=False)
3352                    else:
3353                        error(node.args[0].pos, "Not a type")
3354            elif function == u'sizeof':
3355                if len(node.args) != 1:
3356                    error(node.function.pos, u"sizeof() takes exactly one argument")
3357                else:
3358                    type = node.args[0].analyse_as_type(self.current_env())
3359                    if type:
3360                        node = ExprNodes.SizeofTypeNode(node.function.pos, arg_type=type)
3361                    else:
3362                        node = ExprNodes.SizeofVarNode(node.function.pos, operand=node.args[0])
3363            elif function == 'cmod':
3364                if len(node.args) != 2:
3365                    error(node.function.pos, u"cmod() takes exactly two arguments")
3366                else:
3367                    node = ExprNodes.binop_node(node.function.pos, '%', node.args[0], node.args[1])
3368                    node.cdivision = True
3369            elif function == 'cdiv':
3370                if len(node.args) != 2:
3371                    error(node.function.pos, u"cdiv() takes exactly two arguments")
3372                else:
3373                    node = ExprNodes.binop_node(node.function.pos, '/', node.args[0], node.args[1])
3374                    node.cdivision = True
3375            elif function == u'set':
3376                node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
3377            elif function == u'staticmethod':
3378                node.function = ExprNodes.NameNode(node.pos, name=EncodedString('staticmethod'))
3379            elif self.context.cython_scope.lookup_qualified_name(function):
3380                pass
3381            else:
3382                error(node.function.pos,
3383                      u"'%s' not a valid cython language construct" % function)
3384
3385        self.visitchildren(node)
3386
3387        if isinstance(node, ExprNodes.SimpleCallNode) and node.function.is_name:
3388            func_name = node.function.name
3389            if func_name in ('dir', 'locals', 'vars'):
3390                return self._inject_locals(node, func_name)
3391            if func_name == 'eval':
3392                return self._inject_eval(node, func_name)
3393            if func_name == 'super':
3394                return self._inject_super(node, func_name)
3395        return node
3396
3397    def visit_GeneralCallNode(self, node):
3398        function = node.function.as_cython_attribute()
3399        if function == u'cast':
3400            # NOTE: assuming simple tuple/dict nodes for positional_args and keyword_args
3401            args = node.positional_args.args
3402            kwargs = node.keyword_args.compile_time_value(None)
3403            if (len(args) != 2 or len(kwargs) > 1 or
3404                    (len(kwargs) == 1 and 'typecheck' not in kwargs)):
3405                error(node.function.pos,
3406                      u"cast() takes exactly two arguments and an optional typecheck keyword")
3407            else:
3408                type = args[0].analyse_as_type(self.current_env())
3409                if type:
3410                    typecheck = kwargs.get('typecheck', False)
3411                    node = ExprNodes.TypecastNode(
3412                        node.function.pos, type=type, operand=args[1], typecheck=typecheck)
3413                else:
3414                    error(args[0].pos, "Not a type")
3415
3416        self.visitchildren(node)
3417        return node
3418
3419
3420class ReplaceFusedTypeChecks(VisitorTransform):
3421    """
3422    This is not a transform in the pipeline. It is invoked on the specific
3423    versions of a cdef function with fused argument types. It filters out any
3424    type branches that don't match. e.g.
3425
3426        if fused_t is mytype:
3427            ...
3428        elif fused_t in other_fused_type:
3429            ...
3430    """
3431    def __init__(self, local_scope):
3432        super(ReplaceFusedTypeChecks, self).__init__()
3433        self.local_scope = local_scope
3434        # defer the import until now to avoid circular import time dependencies
3435        from .Optimize import ConstantFolding
3436        self.transform = ConstantFolding(reevaluate=True)
3437
3438    def visit_IfStatNode(self, node):
3439        """
3440        Filters out any if clauses with false compile time type check
3441        expression.
3442        """
3443        self.visitchildren(node)
3444        return self.transform(node)
3445
3446    def visit_GILStatNode(self, node):
3447        """
3448        Fold constant condition of GILStatNode.
3449        """
3450        self.visitchildren(node)
3451        return self.transform(node)
3452
3453    def visit_PrimaryCmpNode(self, node):
3454        with Errors.local_errors(ignore=True):
3455            type1 = node.operand1.analyse_as_type(self.local_scope)
3456            type2 = node.operand2.analyse_as_type(self.local_scope)
3457
3458        if type1 and type2:
3459            false_node = ExprNodes.BoolNode(node.pos, value=False)
3460            true_node = ExprNodes.BoolNode(node.pos, value=True)
3461
3462            type1 = self.specialize_type(type1, node.operand1.pos)
3463            op = node.operator
3464
3465            if op in ('is', 'is_not', '==', '!='):
3466                type2 = self.specialize_type(type2, node.operand2.pos)
3467
3468                is_same = type1.same_as(type2)
3469                eq = op in ('is', '==')
3470
3471                if (is_same and eq) or (not is_same and not eq):
3472                    return true_node
3473
3474            elif op in ('in', 'not_in'):
3475                # We have to do an instance check directly, as operand2
3476                # needs to be a fused type and not a type with a subtype
3477                # that is fused. First unpack the typedef
3478                if isinstance(type2, PyrexTypes.CTypedefType):
3479                    type2 = type2.typedef_base_type
3480
3481                if type1.is_fused:
3482                    error(node.operand1.pos, "Type is fused")
3483                elif not type2.is_fused:
3484                    error(node.operand2.pos,
3485                          "Can only use 'in' or 'not in' on a fused type")
3486                else:
3487                    types = PyrexTypes.get_specialized_types(type2)
3488
3489                    for specialized_type in types:
3490                        if type1.same_as(specialized_type):
3491                            if op == 'in':
3492                                return true_node
3493                            else:
3494                                return false_node
3495
3496                    if op == 'not_in':
3497                        return true_node
3498
3499            return false_node
3500
3501        return node
3502
3503    def specialize_type(self, type, pos):
3504        try:
3505            return type.specialize(self.local_scope.fused_to_specific)
3506        except KeyError:
3507            error(pos, "Type is not specific")
3508            return type
3509
3510    def visit_Node(self, node):
3511        self.visitchildren(node)
3512        return node
3513
3514
3515class DebugTransform(CythonTransform):
3516    """
3517    Write debug information for this Cython module.
3518    """
3519
3520    def __init__(self, context, options, result):
3521        super(DebugTransform, self).__init__(context)
3522        self.visited = set()
3523        # our treebuilder and debug output writer
3524        # (see Cython.Debugger.debug_output.CythonDebugWriter)
3525        self.tb = self.context.gdb_debug_outputwriter
3526        #self.c_output_file = options.output_file
3527        self.c_output_file = result.c_file
3528
3529        # Closure support, basically treat nested functions as if the AST were
3530        # never nested
3531        self.nested_funcdefs = []
3532
3533        # tells visit_NameNode whether it should register step-into functions
3534        self.register_stepinto = False
3535
3536    def visit_ModuleNode(self, node):
3537        self.tb.module_name = node.full_module_name
3538        attrs = dict(
3539            module_name=node.full_module_name,
3540            filename=node.pos[0].filename,
3541            c_filename=self.c_output_file)
3542
3543        self.tb.start('Module', attrs)
3544
3545        # serialize functions
3546        self.tb.start('Functions')
3547        # First, serialize functions normally...
3548        self.visitchildren(node)
3549
3550        # ... then, serialize nested functions
3551        for nested_funcdef in self.nested_funcdefs:
3552            self.visit_FuncDefNode(nested_funcdef)
3553
3554        self.register_stepinto = True
3555        self.serialize_modulenode_as_function(node)
3556        self.register_stepinto = False
3557        self.tb.end('Functions')
3558
3559        # 2.3 compatibility. Serialize global variables
3560        self.tb.start('Globals')
3561        entries = {}
3562
3563        for k, v in node.scope.entries.items():
3564            if (v.qualified_name not in self.visited and not
3565                    v.name.startswith('__pyx_') and not
3566                    v.type.is_cfunction and not
3567                    v.type.is_extension_type):
3568                entries[k]= v
3569
3570        self.serialize_local_variables(entries)
3571        self.tb.end('Globals')
3572        # self.tb.end('Module') # end Module after the line number mapping in
3573        # Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
3574        return node
3575
3576    def visit_FuncDefNode(self, node):
3577        self.visited.add(node.local_scope.qualified_name)
3578
3579        if getattr(node, 'is_wrapper', False):
3580            return node
3581
3582        if self.register_stepinto:
3583            self.nested_funcdefs.append(node)
3584            return node
3585
3586        # node.entry.visibility = 'extern'
3587        if node.py_func is None:
3588            pf_cname = ''
3589        else:
3590            pf_cname = node.py_func.entry.func_cname
3591
3592        # For functions defined using def, cname will be pyfunc_cname=__pyx_pf_*
3593        # For functions defined using cpdef or cdef, cname will be func_cname=__pyx_f_*
3594        # In all cases, cname will be the name of the function containing the actual code
3595        cname = node.entry.pyfunc_cname or node.entry.func_cname
3596
3597        attrs = dict(
3598            name=node.entry.name or getattr(node, 'name', '<unknown>'),
3599            cname=cname,
3600            pf_cname=pf_cname,
3601            qualified_name=node.local_scope.qualified_name,
3602            lineno=str(node.pos[1]))
3603
3604        self.tb.start('Function', attrs=attrs)
3605
3606        self.tb.start('Locals')
3607        self.serialize_local_variables(node.local_scope.entries)
3608        self.tb.end('Locals')
3609
3610        self.tb.start('Arguments')
3611        for arg in node.local_scope.arg_entries:
3612            self.tb.start(arg.name)
3613            self.tb.end(arg.name)
3614        self.tb.end('Arguments')
3615
3616        self.tb.start('StepIntoFunctions')
3617        self.register_stepinto = True
3618        self.visitchildren(node)
3619        self.register_stepinto = False
3620        self.tb.end('StepIntoFunctions')
3621        self.tb.end('Function')
3622
3623        return node
3624
3625    def visit_NameNode(self, node):
3626        if (self.register_stepinto and
3627                node.type is not None and
3628                node.type.is_cfunction and
3629                getattr(node, 'is_called', False) and
3630                node.entry.func_cname is not None):
3631            # don't check node.entry.in_cinclude, as 'cdef extern: ...'
3632            # declared functions are not 'in_cinclude'.
3633            # This means we will list called 'cdef' functions as
3634            # "step into functions", but this is not an issue as they will be
3635            # recognized as Cython functions anyway.
3636            attrs = dict(name=node.entry.func_cname)
3637            self.tb.start('StepIntoFunction', attrs=attrs)
3638            self.tb.end('StepIntoFunction')
3639
3640        self.visitchildren(node)
3641        return node
3642
3643    def serialize_modulenode_as_function(self, node):
3644        """
3645        Serialize the module-level code as a function so the debugger will know
3646        it's a "relevant frame" and it will know where to set the breakpoint
3647        for 'break modulename'.
3648        """
3649        self._serialize_modulenode_as_function(node, dict(
3650            name=node.full_module_name.rpartition('.')[-1],
3651            cname=node.module_init_func_cname(),
3652            pf_cname='',
3653            # Ignore the qualified_name, breakpoints should be set using
3654            # `cy break modulename:lineno` for module-level breakpoints.
3655            qualified_name='',
3656            lineno='1',
3657            is_initmodule_function="True",
3658        ))
3659
3660    def _serialize_modulenode_as_function(self, node, attrs):
3661        self.tb.start('Function', attrs=attrs)
3662
3663        self.tb.start('Locals')
3664        self.serialize_local_variables(node.scope.entries)
3665        self.tb.end('Locals')
3666
3667        self.tb.start('Arguments')
3668        self.tb.end('Arguments')
3669
3670        self.tb.start('StepIntoFunctions')
3671        self.register_stepinto = True
3672        self.visitchildren(node)
3673        self.register_stepinto = False
3674        self.tb.end('StepIntoFunctions')
3675
3676        self.tb.end('Function')
3677
3678    def serialize_local_variables(self, entries):
3679        for entry in entries.values():
3680            if not entry.cname:
3681                # not a local variable
3682                continue
3683            if entry.type.is_pyobject:
3684                vartype = 'PythonObject'
3685            else:
3686                vartype = 'CObject'
3687
3688            if entry.from_closure:
3689                # We're dealing with a closure where a variable from an outer
3690                # scope is accessed, get it from the scope object.
3691                cname = '%s->%s' % (Naming.cur_scope_cname,
3692                                    entry.outer_entry.cname)
3693
3694                qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name,
3695                                      entry.scope.name,
3696                                      entry.name)
3697            elif entry.in_closure:
3698                cname = '%s->%s' % (Naming.cur_scope_cname,
3699                                    entry.cname)
3700                qname = entry.qualified_name
3701            else:
3702                cname = entry.cname
3703                qname = entry.qualified_name
3704
3705            if not entry.pos:
3706                # this happens for variables that are not in the user's code,
3707                # e.g. for the global __builtins__, __doc__, etc. We can just
3708                # set the lineno to 0 for those.
3709                lineno = '0'
3710            else:
3711                lineno = str(entry.pos[1])
3712
3713            attrs = dict(
3714                name=entry.name,
3715                cname=cname,
3716                qualified_name=qname,
3717                type=vartype,
3718                lineno=lineno)
3719
3720            self.tb.start('LocalVar', attrs)
3721            self.tb.end('LocalVar')
3722