| 1288 | } |
| 1289 | |
| 1290 | ExpressionPtr replaceGeneratorWhile ( ExprWhile * expr, const FunctionPtr & func ) { |
| 1291 | auto begin_loop_label = func->totalGenLabel ++; |
| 1292 | auto iter_end_loop_label = func->totalGenLabel ++; |
| 1293 | auto end_loop_label = func->totalGenLabel ++; |
| 1294 | ExprBlock * bodyBlock = nullptr; |
| 1295 | const bool hasFinally = expr->body->rtti_isBlock() && |
| 1296 | !static_cast<ExprBlock*>(expr->body)->finalList.empty(); |
| 1297 | // only introduce __broke / __returning flags when body has a finally — |
| 1298 | // otherwise classic break -> end, and return just exits the enclosing function |
| 1299 | string breakFlag, returnFlag, returnValueName; |
| 1300 | bool hasReturn = false; |
| 1301 | if ( hasFinally ) { |
| 1302 | breakFlag = "__broke_while_" + to_string(expr->at.line) + "_" + to_string(expr->at.column); |
| 1303 | hasReturn = bodyHasTopLevelReturn(expr->body); |
| 1304 | if ( hasReturn ) { |
| 1305 | returnFlag = "__returning_while_" + to_string(expr->at.line) + "_" + to_string(expr->at.column); |
| 1306 | returnValueName = "__return_value_while_" + to_string(expr->at.line) + "_" + to_string(expr->at.column); |
| 1307 | } |
| 1308 | } |
| 1309 | if ( expr->body->rtti_isBlock() ) { |
| 1310 | bodyBlock = static_cast<ExprBlock*>(expr->body->clone()); |
| 1311 | giveBlockVariablesUniqueNames(bodyBlock); |
| 1312 | if ( hasFinally ) { |
| 1313 | // break -> set flag, goto iter_end (runs finally, then exits via flag check) |
| 1314 | // continue -> iter_end (runs finally, flag=false, loops back) |
| 1315 | // return -> spill value, set flag, goto iter_end (runs finally, then real return) |
| 1316 | replaceBreakContinueAndReturn(bodyBlock, iter_end_loop_label, iter_end_loop_label, breakFlag, |
| 1317 | iter_end_loop_label, returnFlag, returnValueName); |
| 1318 | } else { |
| 1319 | replaceBreakAndContinue(bodyBlock, end_loop_label, begin_loop_label); |
| 1320 | } |
| 1321 | } |
| 1322 | auto blk = new ExprBlock(); |
| 1323 | blk->at = expr->at; |
| 1324 | blk->isCollapseable = true; |
| 1325 | // __broke = false (only when finally present) |
| 1326 | if ( hasFinally ) { |
| 1327 | auto leqt = new ExprLet(); |
| 1328 | leqt->at = expr->at; |
| 1329 | leqt->atInit = expr->at; |
| 1330 | leqt->visibility = expr->at; |
| 1331 | auto bvar = new Variable(); |
| 1332 | bvar->generated = true; |
| 1333 | bvar->at = expr->at; |
| 1334 | bvar->name = breakFlag; |
| 1335 | bvar->type = new TypeDecl(Type::tBool); |
| 1336 | bvar->init = new ExprConstBool(expr->at, false); |
| 1337 | leqt->variables.push_back(bvar); |
| 1338 | blk->list.push_back(leqt); |
| 1339 | } |
| 1340 | // __returning = false; __return_value : <func->result> (only when body has return) |
| 1341 | if ( hasReturn ) { |
| 1342 | auto leqt = new ExprLet(); |
| 1343 | leqt->at = expr->at; |
| 1344 | leqt->atInit = expr->at; |
| 1345 | leqt->visibility = expr->at; |
| 1346 | auto rvar = new Variable(); |
| 1347 | rvar->generated = true; |
no test coverage detected