Exemple #1
0
    def make_control_flow_handlers(self, cont_n, status_n, expected_return,
                                   has_cont, has_break):
        '''
        Create the statements in charge of gathering control flow information
        for the static_if result, and executes the expected control flow
        instruction
        '''
        if expected_return:
            assign = cont_ass = [
                ast.Assign([ast.Tuple(expected_return, ast.Store())],
                           ast.Name(cont_n, ast.Load(), None))
            ]
        else:
            assign = cont_ass = []

        if has_cont:
            cmpr = ast.Compare(ast.Name(status_n, ast.Load(), None),
                               [ast.Eq()], [ast.Num(LOOP_CONT)])
            cont_ass = [
                ast.If(cmpr,
                       deepcopy(assign) + [ast.Continue()], cont_ass)
            ]
        if has_break:
            cmpr = ast.Compare(ast.Name(status_n, ast.Load(), None),
                               [ast.Eq()], [ast.Num(LOOP_BREAK)])
            cont_ass = [
                ast.If(cmpr,
                       deepcopy(assign) + [ast.Break()], cont_ass)
            ]
        return cont_ass
Exemple #2
0
    def test_copy_location(self):
        tree = gast.Num(n=1)
        tree.lineno = 1
        tree.col_offset = 2

        node = gast.Num(n=2)
        gast.copy_location(node, tree)
        self.assertEqual(node.lineno, tree.lineno)
        self.assertEqual(node.col_offset, tree.col_offset)
Exemple #3
0
    def visit_Compare(self, node):
        node = self.generic_visit(node)
        if len(node.ops) > 1:
            # in case we have more than one compare operator
            # we generate an auxiliary function
            # that lazily evaluates the needed parameters
            imported_ids = self.passmanager.gather(ImportedIds, node, self.ctx)
            imported_ids = sorted(imported_ids)
            binded_args = [ast.Name(i, ast.Load(), None) for i in imported_ids]

            # name of the new function
            forged_name = "{0}_compare{1}".format(self.prefix,
                                                  len(self.compare_functions))

            # call site
            call = ast.Call(ast.Name(forged_name, ast.Load(), None),
                            binded_args, [])

            # new function
            arg_names = [ast.Name(i, ast.Param(), None) for i in imported_ids]
            args = ast.arguments(arg_names, None, [], [], None, [])

            body = []  # iteratively fill the body (yeah, feel your body!)

            if is_trivially_copied(node.left):
                prev_holder = node.left
            else:
                body.append(
                    ast.Assign([ast.Name('$0', ast.Store(), None)], node.left))
                prev_holder = ast.Name('$0', ast.Load(), None)

            for i, exp in enumerate(node.comparators):
                if is_trivially_copied(exp):
                    holder = exp
                else:
                    body.append(
                        ast.Assign(
                            [ast.Name('${}'.format(i + 1), ast.Store(), None)],
                            exp))
                    holder = ast.Name('${}'.format(i + 1), ast.Load(), None)
                cond = ast.Compare(prev_holder, [node.ops[i]], [holder])
                body.append(
                    ast.If(cond, [ast.Pass()], [ast.Return(ast.Num(0))]))
                prev_holder = holder

            body.append(ast.Return(ast.Num(1)))

            forged_fdef = ast.FunctionDef(forged_name, args, body, [], None)
            self.compare_functions.append(forged_fdef)

            return call
        else:
            return node
Exemple #4
0
    def visit_BinOp(self, node):
        self.generic_visit(node)
        left_val = node.left
        right_val = node.right
        left_is_num = isinstance(left_val, gast.Num)
        right_is_num = isinstance(right_val, gast.Num)

        if isinstance(node.op, gast.Mult):
            if left_is_num and right_is_num:
                return gast.Num(left_val.n * right_val.n)
            if left_is_num:
                if left_val.n == 0:
                    return gast.Num(0)
                elif left_val.n == 1:
                    return right_val
            if right_is_num:
                if right_val.n == 0:
                    return gast.Num(0)
                elif right_val.n == 1:
                    return left_val
        elif isinstance(node.op, gast.Add):
            if left_is_num and right_is_num:
                return gast.Num(left_val.n + right_val.n)
            if left_is_num and left_val.n == 0:
                return right_val
            if right_is_num and right_val.n == 0:
                return left_val
        elif isinstance(node.op, gast.Sub):
            if left_is_num and right_is_num:
                return gast.Num(left_val.n - right_val.n)
            if left_is_num and left_val.n == 0:
                return gast.UnaryOp(op=gast.USub(), operand=right_val)
            if right_is_num and right_val.n == 0:
                return left_val
        elif isinstance(node.op, gast.Div):
            if left_is_num and right_is_num:
                return gast.Num(left_val.n / right_val.n)
            if right_is_num and right_val.n == 1:
                return left_val
        elif isinstance(node.op, gast.Pow):
            if left_is_num and right_is_num:
                return gast.Num(left_val.n**right_val.n)
            if left_is_num:
                if left_val.n == 0:
                    return gast.Num(0)
                elif left_val.n == 1:
                    return gast.Num(1)
            if right_is_num:
                if right_val.n == 0:
                    return gast.Num(1)
                elif right_val.n == 1:
                    return left_val
        return node
Exemple #5
0
    def visit_Subscript(self, node):
        """
        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> pm = passmanager.PassManager("test")

        >>> node = ast.parse("def foo(a): a[1:][3]")
        >>> _, node = pm.apply(PartialConstantFolding, node)
        >>> _, node = pm.apply(ConstantFolding, node)
        >>> print(pm.dump(backend.Python, node))
        def foo(a):
            a[4]

        >>> node = ast.parse("def foo(a): a[::2][3]")
        >>> _, node = pm.apply(PartialConstantFolding, node)
        >>> _, node = pm.apply(ConstantFolding, node)
        >>> print(pm.dump(backend.Python, node))
        def foo(a):
            a[6]

        >>> node = ast.parse("def foo(a): a[-4:][5]")
        >>> _, node = pm.apply(PartialConstantFolding, node)
        >>> _, node = pm.apply(ConstantFolding, node)
        >>> print(pm.dump(backend.Python, node))
        def foo(a):
            a[1]
        """
        self.generic_visit(node)
        if not isinstance(node.value, ast.Subscript):
            return node
        if not isinstance(node.value.slice, ast.Slice):
            return node
        if not isinstance(node.slice, ast.Index):
            return node

        if not isinstance(node.slice.value, ast.Num):
            return node

        slice_ = node.value.slice
        index = node.slice
        node = node.value

        node.slice = index
        lower = slice_.lower or ast.Num(0)
        step = slice_.step or ast.Num(1)
        node.slice.value = ast.BinOp(lower, ast.Add(),
                                     ast.BinOp(index.value, ast.Mult(), step))
        self.update = True
        return node
  def test_ast_to_object(self):
    node = gast.FunctionDef(
        name='f',
        args=gast.arguments(
            args=[gast.Name('a', gast.Param(), None)],
            vararg=None,
            kwonlyargs=[],
            kwarg=None,
            defaults=[],
            kw_defaults=[]),
        body=[
            gast.Return(
                gast.BinOp(
                    op=gast.Add(),
                    left=gast.Name('a', gast.Load(), None),
                    right=gast.Num(1)))
        ],
        decorator_list=[],
        returns=None)

    module, source, _ = compiler.ast_to_object(node)

    expected_source = """
      # coding=utf-8
      def f(a):
        return a + 1
    """
    self.assertEqual(
        textwrap.dedent(expected_source).strip(),
        source.strip())
    self.assertEqual(2, module.f(1))
    with open(module.__file__, 'r') as temp_output:
      self.assertEqual(
          textwrap.dedent(expected_source).strip(),
          temp_output.read().strip())
Exemple #7
0
    def attach_data(self, node):
        '''Generic method called for visit_XXXX() with XXXX in
        GatherOMPData.statements list

        '''
        if self.current:
            for curr in self.current:
                md = OMPDirective(curr)
                metadata.add(node, md)
            self.current = list()
        # add a Pass to hold some directives
        for field_name, field in ast.iter_fields(node):
            if field_name in GatherOMPData.statement_lists:
                if(field and
                   isinstance(field[-1], ast.Expr) and
                   self.isompdirective(field[-1].value)):
                    field.append(ast.Pass())
        self.generic_visit(node)

        # add an If to hold scoping OpenMP directives
        directives = metadata.get(node, OMPDirective)
        field_names = {n for n, _ in ast.iter_fields(node)}
        has_no_scope = field_names.isdisjoint(GatherOMPData.statement_lists)
        if directives and has_no_scope:
            # some directives create a scope, but the holding stmt may not
            # artificially create one here if needed
            sdirective = ''.join(d.s for d in directives)
            scoping = ('parallel', 'task', 'section')
            if any(s in sdirective for s in scoping):
                metadata.clear(node, OMPDirective)
                node = ast.If(ast.Num(1), [node], [])
                for directive in directives:
                    metadata.add(node, directive)

        return node
Exemple #8
0
    def visit_For(self, node):
        target = node.target
        if isinstance(target, ast.Tuple) or isinstance(target, ast.List):
            renamings = OrderedDict()
            self.traverse_tuples(target, (), renamings)
            if renamings:
                gtarget = self.get_new_id()
                node.target = ast.Name(gtarget, node.target.ctx, None)
                for rename, state in renamings.items():
                    nnode = reduce(
                        lambda x, y: ast.Subscript(
                            x,
                            ast.Index(ast.Num(y)),
                            ast.Load()),
                        state,
                        ast.Name(gtarget, ast.Load(), None))
                    if isinstance(rename, str):
                        node.body.insert(0,
                                         ast.Assign(
                                             [ast.Name(rename,
                                                       ast.Store(),
                                                       None)],
                                             nnode)
                                         )
                    else:
                        node.body.insert(0, ast.Assign([rename], nnode))

        self.generic_visit(node)
        return node
Exemple #9
0
 def visit_Subscript(self, node):
     if isinstance(node.value,
                   (gast.Name, gast.Num)) and node.value.id == 'd':
         if (not isinstance(node.slice, gast.Index)
                 or not isinstance(node.slice.value,
                                   (gast.Subscript, gast.Name, gast.Str))):
             # This happens when the gradient of a constant is taken
             if self.replace_grad == Replace.TANGENT:
                 new_node = gast.Num(0)
             else:
                 new_node = gast.Name(id='_', ctx=None, annotation=None)
                 self.remove(new_node)
         elif (self.replace_grad in (Replace.FULL, Replace.TANGENT)
               or isinstance(node.ctx, gast.Load)):
             new_node = create.create_grad(node.slice.value, self.namer,
                                           self.tangent)
         elif isinstance(node.ctx, gast.Store):
             new_node = create.create_temp_grad(node.slice.value,
                                                self.namer, self.tangent)
         else:
             raise ValueError
         new_node.ctx = node.ctx
         if isinstance(new_node, gast.Tuple):
             for elt in new_node.elts:
                 elt.ctx = node.ctx
         node = new_node
     return node
Exemple #10
0
def to_ast(value):
    """
    Turn a value into ast expression.

    >>> a = 1
    >>> print ast.dump(to_ast(a))
    Num(n=1)
    >>> a = [1, 2, 3]
    >>> print ast.dump(to_ast(a))
    List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load())
    """
    if isinstance(value, (type(None), bool)):
        return builtin_folding(value)
    if any(value is t for t in (bool, int, float)):
        return builtin_folding(value)
    elif isinstance(value, numpy.generic):
        return to_ast(numpy.asscalar(value))
    elif isinstance(value, numbers.Number):
        return ast.Num(value)
    elif isinstance(value, str):
        return ast.Str(value)
    elif isinstance(value, (list, tuple, set, dict, numpy.ndarray)):
        return size_container_folding(value)
    elif hasattr(value, "__module__") and value.__module__ == "__builtin__":
        # TODO Can be done the same way for others modules
        return builtin_folding(value)
    # only meaningful for python3
    elif sys.version_info.major == 3:
        if isinstance(value, (filter, map, zip)):
            return to_ast(list(value))
    raise ToNotEval()
Exemple #11
0
 def visit_Assign(self, node):
     self.generic_visit(node)
     # if the rhs is an identifier, we don't need to duplicate it
     # otherwise, better duplicate it...
     no_tmp = isinstance(node.value, ast.Name)
     extra_assign = [] if no_tmp else [node]
     for i, t in enumerate(node.targets):
         if isinstance(t, ast.Tuple) or isinstance(t, ast.List):
             renamings = OrderedDict()
             self.traverse_tuples(t, (), renamings)
             if renamings:
                 gtarget = node.value.id if no_tmp else self.get_new_id()
                 node.targets[i] = ast.Name(gtarget,
                                            node.targets[i].ctx,
                                            None)
                 for rename, state in renamings.items():
                     nnode = reduce(
                         lambda x, y: ast.Subscript(
                             x,
                             ast.Index(ast.Num(y)),
                             ast.Load()),
                         state,
                         ast.Name(gtarget, ast.Load(), None))
                     if isinstance(rename, str):
                         extra_assign.append(
                             ast.Assign(
                                 [ast.Name(rename, ast.Store(), None)],
                                 nnode))
                     else:
                         extra_assign.append(ast.Assign([rename], nnode))
     return extra_assign or node
Exemple #12
0
 def visit_Assign(self, node):
     self.src = quoting.unquote(node)
     self.mark(node)
     self.trivializing = True
     self.namer.target = node.targets[0]
     if isinstance(node.targets[0], (gast.Subscript, gast.Attribute)):
         node.value = self.trivialize(node.value)
         node.targets[0] = self.visit(node.targets[0])
     elif isinstance(node.targets[0], gast.Tuple):
         node.value = self.visit(node.value)
         name = self.namer.name(node.targets[0])
         target = gast.Name(id=name, ctx=gast.Store(), annotation=None)
         for i, elt in enumerate(node.targets[0].elts):
             stmt = gast.Assign(targets=[elt],
                                value=gast.Subscript(
                                    value=gast.Name(id=name,
                                                    ctx=gast.Load(),
                                                    annotation=None),
                                    slice=gast.Index(value=gast.Num(n=i)),
                                    ctx=gast.Load()))
             self.mark(stmt)
             self.append(stmt)
         node.targets[0] = target
     elif not isinstance(node.targets[0], gast.Name):
         raise ValueError
     node = self.generic_visit(node)
     self.namer.target = None
     self.trivializing = False
     return node
Exemple #13
0
 def test_fix_missing_locations(self):
     node = gast.Num(n=6)
     tree = gast.UnaryOp(gast.USub(), node)
     tree.lineno = 1
     tree.col_offset = 2
     gast.fix_missing_locations(tree)
     self.assertEqual(node.lineno, tree.lineno)
     self.assertEqual(node.col_offset, tree.col_offset)
 def visit_UnaryOp(self, node):
     node = self.generic_visit(node)
     if isinstance(node.op, gast.USub) and isinstance(node.operand, gast.Num):
         value = node.operand.n
         replacement = gast.Num(n=-value)
         return gast.copy_location(replacement, node)
     else:
         return node
 def visit_range(self, node):
     range_value = self.range_values[node]
     if isinf(range_value.high):
         return self.generic_visit(node)
     elif range_value.low == range_value.high:
         self.update = True
         return ast.Num(range_value.low)
     else:
         return self.generic_visit(node)
 def visit_Name(self, node):
     if node.id in self.renamings:
         nnode = reduce(
             lambda x, y: ast.Subscript(x, ast.Index(ast.Num(y)), ast.Load(
             )), self.renamings[node.id],
             ast.Name(self.tuple_id, ast.Load(), None))
         nnode.ctx = node.ctx
         return nnode
     return node
Exemple #17
0
 def visit_container(self, node):
     adjoint = []
     for i, elt in enumerate(node.elts):
         adjoint.append(
             template.replace('d[x] = d[t[i]]',
                              namer=self.namer,
                              t=self.target,
                              i=gast.Num(n=i),
                              x=elt))
     return node, adjoint
Exemple #18
0
 def visit_For(self, node):
     if isinstance(node.iter, gast.List):
         return self.make_unrolled_loop(node, node.iter.elts)
     elif isinstance(node.iter, gast.Call) and node.iter.func.id == 'range':
         range_args = [arg.n for arg in node.iter.args]
         # manually invoke the range to generate the list of values to use.
         value_node_list = [gast.Num(i) for i in range(*range_args)]
         return self.make_unrolled_loop(node, value_node_list)
     else:
         return node
Exemple #19
0
 def visit_For(self, node):
     if isinstance(node.iter, (ast.List, ast.Tuple)):
         range_params = self.isrange(node.iter.elts)
         if range_params:
             node.iter = ast.Call(ast.Attribute(
                 ast.Name('__builtin__', ast.Load(), None),
                 'xrange',
                 node.iter.ctx),
                 [ast.Num(param) for param in range_params],
                 [])
             self.update = True
     return self.generic_visit(node)
Exemple #20
0
 def expand_pow(self, node, n):
     if n == 0:
         return ast.Num(1)
     elif n == 1:
         return node
     else:
         node_square = self.replace(node)
         node_pow = self.expand_pow(node_square, n >> 1)
         if n & 1:
             return ast.BinOp(node_pow, ast.Mult(), copy.deepcopy(node))
         else:
             return node_pow
Exemple #21
0
def to_ast(value):
    """
    Turn a value into ast expression.

    >>> a = 1
    >>> print ast.dump(to_ast(a))
    Num(n=1)
    >>> a = [1, 2, 3]
    >>> print ast.dump(to_ast(a))
    List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load())
    """
    numpy_type = (numpy.float64, numpy.float32, numpy.float16, numpy.complex_,
                  numpy.complex64, numpy.complex128, numpy.float_, numpy.uint8,
                  numpy.uint16, numpy.uint32, numpy.uint64, numpy.int8,
                  numpy.int16, numpy.int32, numpy.int64, numpy.intp,
                  numpy.intc, numpy.int_, numpy.bool_)
    itertools_t = [
        getattr(itertools, fun) for fun in dir(itertools)
        if isinstance(getattr(itertools, fun), type)
    ]
    unfolded_type = (types.BuiltinFunctionType, types.BuiltinMethodType,
                     numpy.ufunc, type(list.append), BaseException,
                     types.GeneratorType) + tuple(itertools_t)
    if sys.version_info.major == 2:
        unfolded_type += (types.FunctionType, types.FileType, types.TypeType,
                          types.XRangeType)
    else:
        unfolded_type += type, range, type(numpy.array2string)

    if isinstance(value, (type(None), bool)):
        return ast.Attribute(ast.Name('__builtin__', ast.Load(), None),
                             str(value), ast.Load())
    elif isinstance(value, numpy_type):
        return to_ast(numpy.asscalar(value))
    elif isinstance(value, (int, long, float, complex)):
        return ast.Num(value)
    elif isinstance(value, str):
        return ast.Str(value)
    elif isinstance(value, (list, tuple, set, dict, numpy.ndarray)):
        return size_container_folding(value)
    elif hasattr(value, "__module__") and value.__module__ == "__builtin__":
        # TODO Can be done the same way for others modules
        return builtin_folding(value)
    elif isinstance(value, unfolded_type):
        raise ToNotEval()
    elif value in numpy_type:
        raise ToNotEval()
    # only meaningful for python3
    elif isinstance(value, (filter, map, zip)):
        return to_ast(list(value))
    else:
        raise ConversionError()
Exemple #22
0
    def test_NodeTransformer(self):
        node = gast.Num(n=6)
        tree = gast.UnaryOp(gast.USub(), node)

        class Trans(gast.NodeTransformer):

            def visit_Num(self, node):
                node.n *= 2
                return node

        tree = Trans().visit(tree)

        self.assertEqual(node.n, 12)
Exemple #23
0
 def visit_If(self, node):
     node = self.generic_visit(node)
     node = self.add_mask(node, node.test)
     nodes = [node]
     if len(node.orelse) > 0:
         test_inverse = gast.Call(
             gast.Attribute(node.test, gast.Name('eq', gast.Load(), None),
                            gast.Load()), [gast.Num(0)], [])
         else_node = gast.If(any_active(test_inverse), node.orelse, [])
         node.orelse = []
         self.add_mask(else_node, test_inverse)
         nodes.append(else_node)
     node.test = any_active(node.test)
     return nodes
Exemple #24
0
    def test_code_block(self):
        def template(block):  # pylint:disable=unused-argument
            def test_fn(a):  # pylint:disable=unused-variable
                block  # pylint:disable=pointless-statement
                return a

        node = templates.replace(
            template,
            block=[
                gast.Assign([gast.Name('a', gast.Store(), None)],
                            gast.BinOp(gast.Name('a', gast.Load(), None),
                                       gast.Add(), gast.Num(1))),
            ] * 2)[0]
        result = compiler.ast_to_object(node)
        self.assertEquals(3, result.test_fn(1))
Exemple #25
0
    def test_NodeVisitor(self):
        node = gast.Num(n=6)
        tree = gast.UnaryOp(gast.USub(), node)

        class Vis(gast.NodeTransformer):

            def __init__(self):
                self.state = []

            def visit_Num(self, node):
                self.state.append(node.n)

        vis = Vis()
        vis.visit(tree)

        self.assertEqual(vis.state, [6])
Exemple #26
0
    def test_replace_code_block(self):
        template = """
      def test_fn(a):
        block
        return a
    """

        node = templates.replace(
            template,
            block=[
                gast.Assign([gast.Name('a', None, None)],
                            gast.BinOp(gast.Name('a', None, None), gast.Add(),
                                       gast.Num(1))),
            ] * 2)[0]
        result, _ = compiler.ast_to_object(node)
        self.assertEquals(3, result.test_fn(1))
Exemple #27
0
  def create_grad_list(self, node):
    assert isinstance(node, (gast.List, gast.Tuple)), 'Must be list or tuple'
    list_of_nodes = node.elts
    elts = []
    for _node in list_of_nodes:
      if isinstance(_node, (gast.Name, gast.Subscript)):
        grad_node = create.create_grad(_node, self.namer, tangent=True)
        grad_node.ctx = node.ctx
        elts.append(grad_node)
      elif isinstance(_node, gast.Num):
        elts.append(gast.Num(0))
      elif isinstance(_node, (gast.List, gast.Tuple)):
        elts.append(self.create_grad_list(_node.elts))
      else:
        raise ValueError('Cannot handle node type %s' % type(_node))

    return node.__class__(elts=elts, ctx=node.ctx)
Exemple #28
0
    def ast(self):
        # The caller must adjust the context appropriately.
        if self.has_subscript():
            return gast.Subscript(self.parent.ast(),
                                  gast.Index(self.qn[-1].ast()), None)
        if self.has_attr():
            return gast.Attribute(self.parent.ast(), self.qn[-1], None)

        base = self.qn[0]
        if isinstance(base, str):
            return gast.Name(base, None, None)
        elif isinstance(base, StringLiteral):
            return gast.Str(base.value)
        elif isinstance(base, NumberLiteral):
            return gast.Num(base.value)
        else:
            assert False, ('the constructor should prevent types other than '
                           'str, StringLiteral and NumberLiteral')
Exemple #29
0
    def test_ast_to_source(self):
        node = gast.If(
            test=gast.Num(1),
            body=[
                gast.Assign(targets=[gast.Name('a', gast.Store(), None)],
                            value=gast.Name('b', gast.Load(), None))
            ],
            orelse=[
                gast.Assign(targets=[gast.Name('a', gast.Store(), None)],
                            value=gast.Str('c'))
            ])

        source = compiler.ast_to_source(node, indentation='  ')
        self.assertEqual(
            textwrap.dedent("""
            if 1:
              a = b
            else:
              a = 'c'
        """).strip(), source.strip())
Exemple #30
0
def outline(name, formal_parameters, out_parameters, stmts, has_return,
            has_break, has_cont):

    args = ast.arguments(
        [ast.Name(fp, ast.Param(), None) for fp in formal_parameters], None,
        [], [], None, [])

    if isinstance(stmts, ast.expr):
        assert not out_parameters, "no out parameters with expr"
        fdef = ast.FunctionDef(name, args, [ast.Return(stmts)], [], None)
    else:
        fdef = ast.FunctionDef(name, args, stmts, [], None)

        # this is part of a huge trick that plays with delayed type inference
        # it basically computes the return type based on out parameters, and
        # the return statement is unconditionally added so if we have other
        # returns, there will be a computation of the output type based on the
        # __combined of the regular return types and this one The original
        # returns have been patched above to have a different type that
        # cunningly combines with this output tuple
        #
        # This is the only trick I found to let pythran compute both the output
        # variable type and the early return type. But hey, a dirty one :-/

        stmts.append(
            ast.Return(
                ast.Tuple(
                    [ast.Name(fp, ast.Load(), None) for fp in out_parameters],
                    ast.Load())))
        if has_return:
            pr = PatchReturn(stmts[-1], has_break or has_cont)
            pr.visit(fdef)

        if has_break or has_cont:
            if not has_return:
                stmts[-1].value = ast.Tuple(
                    [ast.Num(LOOP_NONE), stmts[-1].value], ast.Load())
            pbc = PatchBreakContinue(stmts[-1])
            pbc.visit(fdef)

    return fdef