Skip to content

Commit

Permalink
Ensure non-exhaustive case and if are not considered no-return
Browse files Browse the repository at this point in the history
  • Loading branch information
saem committed Sep 12, 2024
1 parent d525378 commit de0e34d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
14 changes: 14 additions & 0 deletions compiler/ast/ast_query.nim
Original file line number Diff line number Diff line change
Expand Up @@ -704,12 +704,26 @@ proc endsInNoReturn*(n: PNode): bool =
result = endsInNoReturn(it[^1])
if not result:
break
result = result and n[^1].kind == nkElse
of nkCaseStmt:
# skip the selector expression
for i in 1..<n.len:
result = endsInNoReturn(n[i])
if not result:
break
# xxx: this duplicates logic in `semstmts.semCase`, this should eventually
# be combined
let
caseType = n[0].typ.skipTypes(abstractInst - {tyTypeDesc})
requiresElse =
case caseType.kind
of tyCaseExhaustive:
false
of tyRange:
not (caseType[0].skipTypes(abstractInst).kind in tyCaseExhaustive)
else:
true
result = result and (not requiresElse or n[^1].kind == nkElse)
of nkTryStmt:
# ignore the 'finally' -- it doesn't contribute to the type
for i in 0..<(n.len - ord(n[^1].kind == nkFinally)):
Expand Down
4 changes: 4 additions & 0 deletions compiler/ast/ast_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,10 @@ const
## type-bound operations are attached to. User type-classes are also
## unconditionally included in this set

tyCaseExhaustive* = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64,
tyBool}
## types that need exhaustiveness checking for case statement selectors

type
TTypeKinds* = set[TTypeKind]

Expand Down
7 changes: 4 additions & 3 deletions compiler/sem/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1641,13 +1641,14 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
var covered: Int128 = toInt128(0)
var typ = commonTypeBegin
var hasElse = false
# xxx: the exhaustiveness logic is duplicated in `ast_query.endsInNoReturn`,
# this should eventually be combined.
let caseTyp = skipTypes(selector, abstractInst-{tyTypeDesc})
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
case caseTyp.kind
of shouldChckCovered:
of tyCaseExhaustive:
chckCovered = true
of tyRange:
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
if skipTypes(caseTyp[0], abstractInst).kind in tyCaseExhaustive:
chckCovered = true
of tyFloat..tyFloat64, tyString:
discard "not all possible values have to be covered"
Expand Down
16 changes: 16 additions & 0 deletions tests/lang_exprs/tnoreturn_nested_reject_nonexhaustive_case.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
discard """
description: '''
Ensure nested non-noreturn statements aren't treated as such
'''
errormsg: "expression '1' is of type 'int literal(1)' and has to be used (or discarded)"
line: 11
"""

let x =
if true:
1
else:
let y = "hi"
case y:
of "hi":
raise newException(Defect, "gone") # no return
14 changes: 14 additions & 0 deletions tests/lang_exprs/tnoreturn_nested_reject_nonexhaustive_if.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
discard """
description: '''
Ensure nested non-noreturn statements aren't treated as such
'''
errormsg: "expression '1' is of type 'int literal(1)' and has to be used (or discarded)"
line: 11
"""

let x =
if true:
1
else:
if true:
raise newException(Defect, "gone") # no return

0 comments on commit de0e34d

Please sign in to comment.