| 223 | return false; |
| 224 | } |
| 225 | SuperCount countSuperCalls ( const ExpressionPtr & expr, const SuperChainMatch & chainCall ) { |
| 226 | if ( !expr ) return SuperCount::onlyFall(0, 0); |
| 227 | if ( expr->rtti_isCall() ) { |
| 228 | auto call = static_cast<ExprCall*>(expr); |
| 229 | if ( chainCall(call) ) { |
| 230 | return SuperCount::onlyFall(1, 1); |
| 231 | } |
| 232 | // JIT-mode rewrite of try/recover: ast_infer_type_op.cpp:1015 emits |
| 233 | // `builtin_try_recover(make_block(try), make_block(catch))` and the typer |
| 234 | // appends `context` + `at` (final arity = 4). Match the resolved function on |
| 235 | // its source module ($) to avoid colliding with any user shadow. Count super |
| 236 | // in the try block only; the recover block is fatal-panic-with-diagnostics, |
| 237 | // irrelevant to chain semantics (mirrors the direct ExprTryCatch handler below). |
| 238 | if ( call->func && call->func->module && call->func->module->name == "$" |
| 239 | && call->func->name == "builtin_try_recover" |
| 240 | && call->arguments.size() >= 2 |
| 241 | && call->arguments[0]->rtti_isMakeBlock() ) { |
| 242 | auto mb = static_cast<ExprMakeBlock*>(call->arguments[0]); |
| 243 | return countSuperCalls(mb->block, chainCall); |
| 244 | } |
| 245 | return SuperCount::onlyFall(0, 0); |
| 246 | } |
| 247 | if ( expr->rtti_isReturn() ) { |
| 248 | // Early-exit at this point with the prefix accumulated so far; no super |
| 249 | // here, no fall-through. The caller (block walker) folds the prefix in. |
| 250 | return SuperCount::onlyExit(0, 0); |
| 251 | } |
| 252 | if ( expr->rtti_isBreak() || expr->rtti_isContinue() || expr->rtti_isGoto() ) { |
| 253 | // Don't escape the function — they stop block iteration (no fall-through) but |
| 254 | // contribute no function-level exit path. |
| 255 | return SuperCount::dead(); |
| 256 | } |
| 257 | if ( expr->rtti_isBlock() ) { |
| 258 | auto blk = static_cast<ExprBlock*>(expr); |
| 259 | int prefix_lo = 0, prefix_hi = 0; |
| 260 | SuperCount out = SuperCount::onlyFall(0, 0); // exits accumulated as we go |
| 261 | out.hasExits = false; |
| 262 | bool fallenThrough = true; |
| 263 | for ( auto & be : blk->list ) { |
| 264 | auto sub = countSuperCalls(be, chainCall); |
| 265 | if ( sub.hasExits ) { |
| 266 | mergeExit(out, safeAdd(prefix_lo, sub.exitLo), safeAdd(prefix_hi, sub.exitHi)); |
| 267 | } |
| 268 | if ( !sub.fallsThrough ) { |
| 269 | fallenThrough = false; |
| 270 | break; |
| 271 | } |
| 272 | prefix_lo = safeAdd(prefix_lo, sub.fallLo); |
| 273 | prefix_hi = safeAdd(prefix_hi, sub.fallHi); |
| 274 | } |
| 275 | out.fallsThrough = fallenThrough; |
| 276 | out.fallLo = prefix_lo; |
| 277 | out.fallHi = prefix_hi; |
| 278 | return out; |
| 279 | } |
| 280 | if ( expr->rtti_isIfThenElse() ) { |
| 281 | auto ite = static_cast<ExprIfThenElse*>(expr); |
| 282 | auto t = countSuperCalls(ite->if_true, chainCall); |