1# Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com 2# 3# Part of "Nuitka", an optimizing Python compiler that is compatible and 4# integrates with CPython, but also works on its own. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18""" Reformulation of while loop statements. 19 20Loops in Nuitka have no condition attached anymore, so while loops are 21re-formulated like this: 22 23.. code-block:: python 24 25 while condition: 26 something() 27 28.. code-block:: python 29 30 while 1: 31 if not condition: 32 break 33 34 something() 35 36This is to totally remove the specialization of loops, with the condition moved 37to the loop body in an initial conditional statement, which contains a ``break`` 38statement. 39 40That achieves, that only ``break`` statements exit the loop, and allow for 41optimization to remove always true loop conditions, without concerning code 42generation about it, and to detect such a situation, consider e.g. endless 43loops. 44 45.. note:: 46 47 Loop analysis (not yet done) can then work on a reduced problem (which 48 ``break`` statements are executed under what conditions) and is then 49 automatically very general. 50 51 The fact that the loop body may not be entered at all, is still optimized, 52 but also in the general sense. Explicit breaks at the loop start and loop 53 conditions are the same. 54 55""" 56 57from nuitka.nodes.AssignNodes import StatementAssignmentVariable 58from nuitka.nodes.ComparisonNodes import ExpressionComparisonIs 59from nuitka.nodes.ConditionalNodes import makeStatementConditional 60from nuitka.nodes.ConstantRefNodes import makeConstantRefNode 61from nuitka.nodes.LoopNodes import StatementLoop, StatementLoopBreak 62from nuitka.nodes.OperatorNodesUnary import ExpressionOperationNot 63from nuitka.nodes.StatementNodes import StatementsSequence 64from nuitka.nodes.VariableRefNodes import ExpressionTempVariableRef 65 66from .TreeHelpers import ( 67 buildNode, 68 buildStatementsNode, 69 makeStatementsSequence, 70 makeStatementsSequenceFromStatements, 71 popBuildContext, 72 pushBuildContext, 73) 74 75 76def buildWhileLoopNode(provider, node, source_ref): 77 # The while loop is re-formulated according to developer manual. The 78 # condition becomes an early condition to break the loop. The else block is 79 # taken if a while loop exits normally, i.e. because of condition not being 80 # true. We do this by introducing an indicator variable. 81 82 else_block = buildStatementsNode( 83 provider=provider, 84 nodes=node.orelse if node.orelse else None, 85 source_ref=source_ref, 86 ) 87 88 if else_block is not None: 89 temp_scope = provider.allocateTempScope("while_loop") 90 91 # Indicator variable, will end up with C bool type, and need not be released. 92 tmp_break_indicator = provider.allocateTempVariable( 93 temp_scope=temp_scope, name="break_indicator", temp_type="bool" 94 ) 95 96 statements = ( 97 StatementAssignmentVariable( 98 variable=tmp_break_indicator, 99 source=makeConstantRefNode(constant=True, source_ref=source_ref), 100 source_ref=source_ref, 101 ), 102 StatementLoopBreak(source_ref=source_ref), 103 ) 104 else: 105 statements = (StatementLoopBreak(source_ref=source_ref),) 106 107 pushBuildContext("loop_body") 108 loop_statements = buildStatementsNode( 109 provider=provider, nodes=node.body, source_ref=source_ref 110 ) 111 popBuildContext() 112 113 # The loop body contains a conditional statement at the start that breaks 114 # the loop if it fails. 115 loop_body = makeStatementsSequence( 116 statements=( 117 makeStatementConditional( 118 condition=ExpressionOperationNot( 119 operand=buildNode(provider, node.test, source_ref), 120 source_ref=source_ref, 121 ), 122 yes_branch=StatementsSequence( 123 statements=statements, source_ref=source_ref 124 ), 125 no_branch=None, 126 source_ref=source_ref, 127 ), 128 loop_statements, 129 ), 130 allow_none=True, 131 source_ref=source_ref, 132 ) 133 134 loop_statement = StatementLoop(loop_body=loop_body, source_ref=source_ref) 135 136 if else_block is None: 137 return loop_statement 138 else: 139 return makeStatementsSequenceFromStatements( 140 StatementAssignmentVariable( 141 variable=tmp_break_indicator, 142 source=makeConstantRefNode(constant=False, source_ref=source_ref), 143 source_ref=source_ref, 144 ), 145 loop_statement, 146 makeStatementConditional( 147 condition=ExpressionComparisonIs( 148 left=ExpressionTempVariableRef( 149 variable=tmp_break_indicator, source_ref=source_ref 150 ), 151 right=makeConstantRefNode(constant=True, source_ref=source_ref), 152 source_ref=source_ref, 153 ), 154 yes_branch=else_block, 155 no_branch=None, 156 source_ref=source_ref, 157 ), 158 ) 159