|
20 | 20 |
|
21 | 21 | namespace tolk { |
22 | 22 |
|
| 23 | +// functions returning "never" are assumed to interrupt flow |
| 24 | +// for instance, variables after their call aren't considered used |
| 25 | +// its main purpose is `throw` statement, it's a call to a built-in `__throw` function |
| 26 | +static bool does_function_always_throw(FunctionPtr fun_ref) { |
| 27 | + return fun_ref->declared_return_type == TypeDataNever::create(); |
| 28 | +} |
| 29 | + |
23 | 30 | /* |
24 | 31 | * |
25 | 32 | * ANALYZE AND PREPROCESS ABSTRACT CODE |
@@ -262,17 +269,6 @@ VarDescrList& VarDescrList::operator|=(const VarDescrList& y) { |
262 | 269 | } |
263 | 270 | } |
264 | 271 |
|
265 | | -VarDescrList& VarDescrList::operator&=(const VarDescrList& values) { |
266 | | - for (const VarDescr& vd : values.list) { |
267 | | - VarDescr* item = operator[](vd.idx); |
268 | | - if (item) { |
269 | | - *item &= vd; |
270 | | - } |
271 | | - } |
272 | | - unreachable |= values.unreachable; |
273 | | - return *this; |
274 | | -} |
275 | | - |
276 | 272 | VarDescrList& VarDescrList::import_values(const VarDescrList& values) { |
277 | 273 | if (values.unreachable) { |
278 | 274 | set_unreachable(); |
@@ -326,6 +322,17 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { |
326 | 322 | } |
327 | 323 | return std_compute_used_vars(true); |
328 | 324 | } |
| 325 | + if (cl == _Call && does_function_always_throw(f_sym)) { |
| 326 | + VarDescrList new_var_info; // empty, not next->var_info |
| 327 | + if (args.size() == right.size()) { |
| 328 | + for (const VarDescr& arg : args) { |
| 329 | + new_var_info.add_var(arg.idx, arg.is_unused()); |
| 330 | + } |
| 331 | + } else { |
| 332 | + new_var_info.add_vars(right, false); |
| 333 | + } |
| 334 | + return set_var_info(std::move(new_var_info)); |
| 335 | + } |
329 | 336 | return std_compute_used_vars(); |
330 | 337 | } |
331 | 338 | case _SetGlob: { |
@@ -516,20 +523,19 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) { |
516 | 523 | case Op::_SliceConst: |
517 | 524 | case Op::_GlobVar: |
518 | 525 | case Op::_SetGlob: |
519 | | - case Op::_Call: |
520 | 526 | case Op::_CallInd: |
521 | 527 | case Op::_Tuple: |
522 | 528 | case Op::_UnTuple: |
523 | 529 | case Op::_Import: |
| 530 | + case Op::_Let: |
524 | 531 | reach = true; |
525 | 532 | break; |
526 | | - case Op::_Let: { |
527 | | - reach = true; |
528 | | - break; |
529 | | - } |
530 | 533 | case Op::_Return: |
531 | 534 | reach = false; |
532 | 535 | break; |
| 536 | + case Op::_Call: |
| 537 | + reach = !does_function_always_throw(op.f_sym); |
| 538 | + break; |
533 | 539 | case Op::_If: { |
534 | 540 | // if left then block0 else block1; ... |
535 | 541 | VarDescr* c_var = op.var_info[op.left[0]]; |
@@ -712,6 +718,9 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { |
712 | 718 | values.add_newval(i); |
713 | 719 | } |
714 | 720 | } |
| 721 | + if (does_function_always_throw(f_sym)) { |
| 722 | + values.set_unreachable(); |
| 723 | + } |
715 | 724 | break; |
716 | 725 | } |
717 | 726 | case _Tuple: |
@@ -860,10 +869,11 @@ bool Op::mark_noreturn() { |
860 | 869 | case _SetGlob: |
861 | 870 | case _GlobVar: |
862 | 871 | case _CallInd: |
863 | | - case _Call: |
864 | 872 | return set_noreturn(next->mark_noreturn()); |
865 | 873 | case _Return: |
866 | 874 | return set_noreturn(); |
| 875 | + case _Call: |
| 876 | + return set_noreturn(next->mark_noreturn() || does_function_always_throw(f_sym)); |
867 | 877 | case _If: |
868 | 878 | case _TryCatch: |
869 | 879 | // note, that & | (not && ||) here and below is mandatory to invoke both left and right calls |
|
0 commit comments