Skip to content

Commit

Permalink
fix(sem): crash with erroneous is expression (#1485)
Browse files Browse the repository at this point in the history
## Summary

Fix a crash with erroneous `is` expressions appearing directly as a
`when` conditions expressions causing the compiler to crash.

## Details

There were two issues:
1. `semIs` modified input AST, which led to the error correction in the
   `fitNode` called by `forceBool` called by `semWhen` not triggering
2. `semWhen` used the `semConstExpr`, which doesn't return an `nkError`
   on failure, thus leading to a field access defect when happening in
   a context where `localReport` doesn't terminate the compiler

`semIs` is changed to not modify input AST. In addition, the workaround
for `is` being analyzed eagerly in generic expression is removed, as
it's no longer needed.

In `semWhen`, the usage of `semConstExpr` is replaced with
`semRealConstExpr`, which properly propagates `nkError`. The latter was
missing execution context handling (needed for detecting illegal
`break`s crossing the compile-time / run-time boundary), which is now
fixed too.

Fixes #1483.
  • Loading branch information
zerbina authored Jan 11, 2025
1 parent aa07c59 commit 80c46ad
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 21 deletions.
2 changes: 2 additions & 0 deletions compiler/sem/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,9 @@ proc semRealConstExpr(c: PContext, n: PNode): PNode =
addInNimDebugUtils(c.config, "semRealConstExpr", n, result)
assert not n.isError

pushExecCon(c, {})
result = semExprWithType(c, n)
popExecCon(c)
if result.kind != nkError:
result = evalConstExpr(c, result)

Expand Down
42 changes: 21 additions & 21 deletions compiler/sem/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -531,28 +531,28 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
addInNimDebugUtils(c.config, "semIs", n, result, flags)

if n.len != 3:
result = c.config.newError(n, PAstDiag(kind: adSemIsOperatorTakes2Args))
return
# the check is necessary because `is` can be analyzed pre-sigmatch
return c.config.newError(n, PAstDiag(kind: adSemIsOperatorTakes2Args))

let boolType = getSysType(c.graph, n.info, tyBool)
n.typ = boolType
var liftLhs = true

n[1] = semExprWithType(c, n[1], flags + {efWantIterator})
var
liftLhs = true
lhs = semExprWithType(c, n[1], flags + {efWantIterator})
rhs: PNode

case n[2].kind
of nkStrLiterals:
n[2] = semExpr(c, n[2])
rhs = semExpr(c, n[2])
of nkError:
discard # below we'll wrap the result in an error
else:
let t2 = semTypeNode(c, n[2], nil)
n[2] = newNodeIT(nkType, n[2].info, t2)
rhs = newNodeIT(nkType, n[2].info, t2)
if t2.kind == tyStatic:
let evaluated = tryConstExpr(c, n[1])
let evaluated = tryConstExpr(c, lhs)
if evaluated != nil:
c.fixupStaticType(evaluated)
n[1] = evaluated
lhs = evaluated
else:
result = newIntNode(nkIntLit, 0)
result.typ = boolType
Expand All @@ -564,17 +564,17 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
# not allow regular values to be matched against the type:
liftLhs = false

var lhsType = n[1].typ
if n[1].isError or n[2].isError:
result = wrapError(c.config, n)
elif lhsType.kind == tyTypeDesc and (lhsType.base.kind == tyNone or
(c.inGenericContext > 0 and lhsType.base.containsGenericType)):
# BUGFIX: don't evaluate this too early: ``T is void``
result = n
result = shallowCopy(n)
result.typ = boolType
result[0] = n[0]
result[1] = lhs
result[2] = rhs
if result[1].isError or result[2].isError:
result = wrapError(c.config, result)
else:
if lhsType.kind != tyTypeDesc and liftLhs:
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
result = isOpImpl(c, n, flags)
if lhs.typ.kind != tyTypeDesc and liftLhs:
result[1] = makeTypeSymNode(c, lhs.typ, n[1].info)
result = isOpImpl(c, result, flags)

proc semOpAux(c: PContext, n: PNode): bool =
## Returns whether n contains errors
Expand Down Expand Up @@ -2945,7 +2945,7 @@ proc semWhen(c: PContext, n: PNode, flags: TExprFlags): PNode =
case it.kind
of nkElifBranch, nkElifExpr:
checkSonsLen(it, 2, c.config)
let e = forceBool(c, semConstExpr(c, it[0]))
let e = forceBool(c, semRealConstExpr(c, it[0]))
if e.kind == nkError:
# error in the condition expression; wrap and return
result = copyNodeWithKids(n)
Expand Down
7 changes: 7 additions & 0 deletions tests/error_propagation/tin_expression_in_when.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
discard """
errormsg: "undeclared identifier: 'missing'"
"""

var x = missing
when x is int:
discard

0 comments on commit 80c46ad

Please sign in to comment.