Skip to content

Commit

Permalink
fix(sem): errors in lambdas no longer crash (#1437)
Browse files Browse the repository at this point in the history
## Summary

Errors in lambdas requiring inference ( `proc(e: auto) = echo e` ), no
longer crash the compiler. Instead errors within the lambda body are
reported and lambda inference fails as it should.

## Details

`semInferredLambda`  failed to wrap its output when there were errors.
This would reach `transf` and result in a compiler crash by hitting an
`unreachable`  assertion.

Now errors in the body are reported immediately (to provide context for
inference failure wrt matching the body) and AST output from the
inference attempt is correctly wrapped such that inference appropriately
fails.

Fixes #1381

In addition, some minor refactoring was done to avoid shadowing the
input parameter  `n`  and making it look like we were mutating the
input.

---------

Co-authored-by: zerbina <[email protected]>
  • Loading branch information
saem and zerbina authored Aug 30, 2024
1 parent d393260 commit 5d3fbf4
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 22 deletions.
45 changes: 24 additions & 21 deletions compiler/sem/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2182,45 +2182,48 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
## used for resolving 'auto' in lambdas based on their callsite
addInNimDebugUtils(c.config, "semInferredLambda", n, result)
var n = n
let original = n[namePos].sym
let s = original #copySym(original, false)
#incl(s.flags, sfFromGeneric)
#s.owner = original

n = instantiateTypesInBody(c, pt, n, original)
result = n
result = instantiateTypesInBody(c, pt, n, original)
s.ast = result
n[namePos].sym = s
n[genericParamsPos] = c.graph.emptyNode
result[namePos].sym = s
result[genericParamsPos] = c.graph.emptyNode
# for LL we need to avoid wrong aliasing
n[paramsPos] = newNodeI(nkFormalParams, n[paramsPos].info, n.typ.n.len)
for i, p in n.typ.n.pairs:
n[paramsPos][i] =
result[paramsPos] = newNodeI(nkFormalParams, result[paramsPos].info,
result.typ.n.len)
for i, p in result.typ.n.pairs:
result[paramsPos][i] =
case i
of 0: # return type
newNodeIT(nkType, n.info, n.typ[0])
newNodeIT(nkType, n.info, result.typ[0])
else: # copy instantiated parameters
n.typ.n[i]
s.typ = n.typ
let params = n.typ.n
for i in 1..<params.len:
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
tyFromExpr}+tyTypeClasses:
localReport(c.config, params[i].info, reportSym(
rsemCannotInferTypeOfParameter, params[i].sym))
#params[i].sym.owner = s
if p.typ.kind in {tyTypeDesc, tyGenericParam,
tyFromExpr}+tyTypeClasses:
localReport(c.config, p.info, reportSym(
rsemCannotInferTypeOfParameter, p.sym))
#params[i].sym.owner = s
result.typ.n[i]
s.typ = result.typ
openScope(c)
pushOwner(c, s)
addParams(c, params)
addParams(c, result.typ.n)
pushProcCon(c, s)
addResult(c, n, n.typ[0])
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
addResult(c, result, result.typ[0])
result[bodyPos] = semProcBody(c, result[bodyPos])
s.ast[bodyPos] = hloBody(c, result[bodyPos])
s.ast[bodyPos] = foldInAst(c.module, s.ast[bodyPos], c.idgen, c.graph)
trackProc(c, s, s.ast[bodyPos])
popProcCon(c)
popOwner(c)
closeScope(c)
# wrap in an error if there were issues along the way
for k in result.items:
if k.kind == nkError:
result = c.config.wrapError(result)
break
# alternative variant (not quite working):
# var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
Expand Down
7 changes: 6 additions & 1 deletion compiler/sem/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,11 @@ proc instantiateRoutineExpr(c: PContext, bindings: TIdTable, n: PNode): PNode =
case n.kind
of nkProcDef, nkFuncDef, nkIteratorDef, nkLambdaKinds:
result = c.semInferredLambda(c, bindings, n)
if result.kind == nkError:
# xxx: output the error now otherwise we'll just get an inferred lambda
# failure without an explanation, ideally this should be
# explained/added context of the inferred lambda error itself.
c.config.localReport(result)
of nkSym:
let inferred = c.semGenerateInstance(c, n.sym, bindings, n.info)
result =
Expand Down Expand Up @@ -2870,7 +2875,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
# untyped varargs
if a >= formalLen - 1 and # last or finished passing args
f < formalLen and # still have more formal params
m.callee.n[f].typ.isVarargsUntyped: # current formal is varargs untped
m.callee.n[f].typ.isVarargsUntyped: # current formal is varargs untyped

formal = m.callee.n[f].sym
incl(marker, formal.position)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
discard """
description: "Fail lambda inference due to error within the body."
errormsg: "undeclared identifier: 'i'"
line: 13
"""

# This is a regression test, ensure we report errors inside lambdas during
# inference

proc test(p: proc(x: int)) =
discard

test proc(e: auto) = i

0 comments on commit 5d3fbf4

Please sign in to comment.