From ce8d9dd1cb08a04282d1737e4d053011051f36a8 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Fri, 1 Sep 2023 06:41:39 +0200 Subject: [PATCH] Fix #22604: Make endsInNoReturn traverse the tree (#22612) * Rewrite endsInNoReturn * Handle `try` stmt again and add tests * Fix unreachable code warning * Remove unreachable code in semexprs again * Check `it.len` before skip * Move import of assertions --------- Co-authored-by: SirOlaf <> (cherry picked from commit 3b206ed988765419c5ff02c2b08645f24ed0cbad) --- compiler/sem.nim | 54 +++++++++++++++++++++++++++--- compiler/semexprs.nim | 1 - compiler/semstmts.nim | 4 +-- tests/controlflow/tunreachable.nim | 16 +++++++-- tests/exprs/t22604.nim | 49 +++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 tests/exprs/t22604.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 4531947dc2..818eaa2f11 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -203,12 +203,58 @@ proc commonType*(c: PContext; x, y: PType): PType = result.addSonSkipIntLit(r, c.idgen) proc endsInNoReturn(n: PNode): bool = - # check if expr ends in raise exception or call of noreturn proc + ## check if expr ends the block like raising or call of noreturn procs do + result = false # assume it does return + + template checkBranch(branch) = + if not endsInNoReturn(branch): + # proved a branch returns + return false + var it = n - while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: + # skip these beforehand, no special handling needed + while it.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt} and it.len > 0: it = it.lastSon - result = it.kind in nkLastBlockStmts or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + + case it.kind + of nkIfStmt: + var hasElse = false + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `if` statement during endsInNoReturn" + # none of the branches returned + result = hasElse # Only truly a no-return when it's exhaustive + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in endsInNoReturn" + # none of the branches returned + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + else: + result = it.kind in nkLastBlockStmts or + it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc commonType*(c: PContext; x: PType, y: PNode): PType = # ignore exception raising branches in case/if expressions diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2db60f0b3e..3999196154 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1050,7 +1050,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType msg.addDeclaredLocMaybe(c.config, typ) localError(c.config, n.info, msg) return errorNode(c, n) - result = nil else: result = m.call instGenericConvertersSons(c, result, m) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7b97deb42d..906362e0b5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2427,9 +2427,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = var m = n[i] while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates m = m.lastSon - if m.kind in nkLastBlockStmts or - m.kind in nkCallKinds and m[0].kind == nkSym and - sfNoReturn in m[0].sym.flags: + if endsInNoReturn(m): for j in i + 1..