Skip to content

Commit

Permalink
fix(sem): analysis considers nested noreturn (#1450)
Browse files Browse the repository at this point in the history
## Summary
No-return analysis ensures that nested final expressions (
`block/case/if/try` ) are accounted for no-return itself.

## Details
Prior to this change if the last expression within a greater expression
was a no-return expression it would not be deemed as such if it was a 
`block/case/if/try` , instead errors would be raised related to discard
checks or that path not resulting in the correct type.

For example, the following would result in an error where  `0`  must be
used or discarded:
```nim
proc foo() =
  let x =
    if false: 10
    else:
      block:
        return
```

Fixes #1440

---------

Co-authored-by: zerbina <[email protected]>
  • Loading branch information
saem and zerbina authored Sep 7, 2024
1 parent 3955af7 commit 4feef40
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
12 changes: 12 additions & 0 deletions compiler/ast/ast_query.nim
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,18 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
if posInCall < call.len:
yield (genericParam, call[posInCall])

proc endsInNoReturn*(n: PNode): bool =
## Checks if expression `n` ends in an unstructured exit (raise, return,
## etc.) or a call of a noreturn proc. This is meant to be called on a
## semmed `n`.
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
it = it.lastSon
result = it.kind in nkLastBlockStmts or
(it.kind in {nkIfStmt, nkTryStmt, nkCaseStmt, nkBlockStmt} and
it.typ.isEmptyType()) or
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags

type
NodePosName* = enum
## Named node position accessor
Expand Down
8 changes: 0 additions & 8 deletions compiler/sem/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,6 @@ proc commonType*(c: PContext; x, y: PType): PType =
result = newType(k, nextTypeId(c.idgen), r.owner)
result.addSonSkipIntLit(r, c.idgen)

proc endsInNoReturn(n: PNode): bool =
# check if expr ends in raise exception or call of noreturn proc
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} 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

proc commonType*(c: PContext; x: PType, y: PNode): PType =
# ignore exception raising branches in case/if expressions
addInNimDebugUtils(c.config, "commonType", y, x, result)
Expand Down
1 change: 0 additions & 1 deletion compiler/sem/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1705,7 +1705,6 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
localReport(c.config, result, SemReport(
kind: rsemMissingCaseBranches,
nodes: formatMissingBranches(c, result)))

else:
localReport(c.config, result, reportSem rsemMissingCaseBranches)

Expand Down
53 changes: 53 additions & 0 deletions tests/lang_exprs/tnoreturn_nested.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
discard """
description: '''
Ensure nested noreturn statements are considererd in noreturn analysis
'''
"""

block nested_in_if:
let x =
if true:
0
else:
if true:
raise newException(CatchableError, "error")
else:
raise newException(CatchableError, "another error")

doAssert x is int

block nested_in_try:
let x =
if true:
0
else:
try:
raise newException(CatchableError, "error")
finally:
discard

doAssert x is int

block nested_in_case:
let x =
if true:
0
else:
let s = false
case s
of true:
raise newException(CatchableError, "error")
of false:
raise newException(CatchableError, "another error")

doAssert x is int

block nested_in_block:
let x =
if true:
0
else:
block:
raise newException(CatchableError, "error")

doAssert x is int

0 comments on commit 4feef40

Please sign in to comment.