Skip to content

Commit

Permalink
sem: correctly show errors from defer node body
Browse files Browse the repository at this point in the history
Summary
=======

Errors in a defer node body are now correctly reported, no longer
producing misleading `'defer' takes a 'void' expression` messages.

Details
======

A`defer` with an error node body is no longer treated as an
expression and having the underlying error supressed. Instead the body
error is correctly reported. The `defer` AST is also correctly wrapped
in an error node.

A relatively comprehensive test sutie for `defer` statements was added,
along with an improvement to the void expression error message
requirement, which shows the `defer` body itself.
  • Loading branch information
saem committed Feb 11, 2024
1 parent bd28145 commit 4e82e7a
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 4 deletions.
2 changes: 1 addition & 1 deletion compiler/front/cli_reporter.nim
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ proc reportBody*(conf: ConfigRef, r: SemReport): string =
result = "invalid context for 'bind' statement: " & render(r.ast)

of rsemExpectedTypelessDeferBody:
result = "'defer' takes a 'void' expression"
result = "'defer' takes a 'void' expression, got: " & render(r.ast[0])

of rsemUnexpectedToplevelDefer:
result = "defer statement not supported at top level"
Expand Down
19 changes: 16 additions & 3 deletions compiler/sem/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3878,9 +3878,22 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkDefer:
if c.currentScope == c.topLevelScope:
localReport(c.config, n, reportSem rsemUnexpectedToplevelDefer)
n[0] = semExpr(c, n[0])
if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]):
localReport(c.config, n, reportSem rsemExpectedTypelessDeferBody)
let res = semExpr(c, n[0])
case res.kind
of nkError:
result = copyNodeWithKids(n)
result[0] = res
result = c.config.wrapError(result)
else:
if res == n[0]: # no change
discard
else:
result = copyNodeWithKids(n)
result[0] = res

# TODO: convert to nkError
if not res.typ.isEmptyType and not implicitlyDiscardable(res):
localReport(c.config, n, reportSem rsemExpectedTypelessDeferBody)
of nkMixinStmt: discard
of nkBindStmt:
if c.p != nil:
Expand Down
34 changes: 34 additions & 0 deletions tests/lang_stmts/defer/tdefer.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
discard """
description: "Test for defer statements"
"""

block runs_at_the_end_of_scope:
proc foo(): int =
defer:
inc result
doAssert result == 0

doAssert foo() == 1

block questionably_not_at_the_end_of_proc_scope:
proc foo(): int =
if true:
defer:
inc result
doAssert result == 0
doAssert result == 1
inc result
doAssert result == 2

doAssert foo() == 2

block templates_do_not_create_scopes_defer_applies_to_the_surrounding:
template bar() =
defer:
inc result

proc foo(): int =
bar()
doAssert result == 0

doAssert foo() == 1
8 changes: 8 additions & 0 deletions tests/lang_stmts/defer/tdefer_cannot_be_top_level.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
discard """
description: "Defer cannot be decalred at the top level"
errormsg: "defer statement not supported at top level"
line: 7
"""

defer:
discard
9 changes: 9 additions & 0 deletions tests/lang_stmts/defer/tdefer_requires_stmt_1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
discard """
description: "Defer body must be statement"
errormsg: "'defer' takes a 'void' expression"
line: 8
"""

block:
defer:
true # this needs to be used
13 changes: 13 additions & 0 deletions tests/lang_stmts/defer/tdefer_requires_stmt_2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
discard """
description: "Defer body must be a statement, without error"
errormsg: "type mismatch: got <bool, int literal(1)>"
line: 12
"""

## somewhat of a regression test to see what happens when the defer body has an
## error, this was an issue encountered in CPS, where an error body was treated
## as an expression.

block:
defer:
true + 1

0 comments on commit 4e82e7a

Please sign in to comment.