Skip to content

Closures assigned within an elif block in another closure cause segfault #25321

@GunTurtle

Description

@GunTurtle

Nim Version

Nim Compiler Version 2.2.6 [Linux: amd64]
Compiled at 2025-10-31
Copyright (c) 2006-2025 by Andreas Rumpf

git hash: ab00c56
active boot switches: -d:release

Description

Here is a snippet closure_segfault_elif.nim demonstrating the problem:

var p1: proc() {.closure.}
var p2: proc() {.closure.}
var p3: proc() {.closure.}

proc makeClosure(): proc(i: int) =
    var arr = @[0,0,0]
    echo "arr defined: " & $arr
    var p = proc(i: int) =
        if i == 0:
            p1 = proc() =
                arr[0] = 1
                echo "p1; arr = " & $arr
        elif i == 1:
            p2 = proc() =
                arr[1] = 2
                echo "p2; arr = " & $arr
            p3 = proc() =
                arr[2] = 3
                echo "p3; arr = " & $arr
    return p

var dostuff = makeClosure()
dostuff(0) # define p1
dostuff(1) # define p2, p3
p1()
p2()
p3() # causes segfault if p3 is assigned in 'elif' block

The closure p3 will not capture arr and will segfault when it is called. The same error occurs if an else block is used instead of elif.

Current Output

arr defined: @[0, 0, 0]
p1; arr = @[1, 0, 0]
p2; arr = @[1, 2, 0]
Traceback (most recent call last)
closure_segfault_elif.nim(27) closure_segfault_elif
closure_segfault_elif.nim(18) :anonymous
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault (core dumped)

Expected Output

arr defined: @[0, 0, 0]
p1; arr = @[1, 0, 0]
p2; arr = @[1, 2, 0]
p3; arr = @[1, 2, 3]

Known Workarounds

Using if for all branches instead of if...elif resolves the problem:

var p1: proc() {.closure.}
var p2: proc() {.closure.}
var p3: proc() {.closure.}

proc makeClosure(): proc(i: int) =
    var arr = @[0,0,0]
    echo "arr defined: " & $arr
    var p = proc(i: int) =
        if i == 0:
            p1 = proc() =
                arr[0] = 1
                echo "p1; arr = " & $arr
        if i == 1:
            p2 = proc() =
                arr[1] = 2
                echo "p2; arr = " & $arr
            p3 = proc() =
                arr[2] = 3
                echo "p3; arr = " & $arr
    return p

var dostuff = makeClosure()
dostuff(0) # define p1
dostuff(1) # define p2, p3
p1()
p2()
p3()

The problem does not occur if a greater or equal number of closures are assigned in the if block as the elif block:

var p1: proc() {.closure.}
var p2: proc() {.closure.}
var p3: proc() {.closure.}
var p4: proc() {.closure.}

proc makeClosure(): proc(i: int) =
    var arr = @[0,0,0]
    echo "arr defined: " & $arr
    var p = proc(i: int) =
        if i == 0:
            p1 = proc() =
                arr[0] = 1
                echo "p1; arr = " & $arr
            p4 = proc() =
                arr[0] = 9
                echo "p4; arr = " & $arr
        elif i == 1:
            p2 = proc() =
                arr[1] = 2
                echo "p2; arr = " & $arr
            p3 = proc() =
                arr[2] = 3
                echo "p3; arr = " & $arr
    return p

var dostuff = makeClosure()
dostuff(0) # define p1, p4
dostuff(1) # define p2, p3
p1()
p2()
p3()
p4()

The problem does not occur if closures are only assigned in one branch of the if...elif statement:

var p2: proc() {.closure.}
var p3: proc() {.closure.}

proc makeClosure(): proc(i: int) =
    var arr = @[0,0,0]
    echo "arr defined: " & $arr
    var p = proc(i: int) =
        if i == 0:
            discard
        elif i == 1:
            p2 = proc() =
                arr[1] = 2
                echo "p2; arr = " & $arr
            p3 = proc() =
                arr[2] = 3
                echo "p3; arr = " & $arr
    return p

var dostuff = makeClosure()
dostuff(0) # do nothing
dostuff(1) # define p2, p3
p2()
p3()

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions