Nim icon indicating copy to clipboard operation
Nim copied to clipboard

Closures capture scope is confusing, leading to unexpective behaviour

Open Alogani opened this issue 1 year ago • 0 comments

Description

Hello,

There can be multiple scopes inside a same functions thanks to blocks and templates. But this scope aren't captured as is when creating a closure. This can result in pointing to the wrong variable inside the closure.

I think an example will be more explicit :

type MyRef = ref object
var myClosures: seq[proc()]

echo "DECEPTIVE EXAMPLE - with template"
template generateClosure(): proc() =
    let myRef = MyRef()
    echo "OUTSIDE:", cast[int](myRef)
    proc closure() =
        echo "INSIDE:", cast[int](myRef)
    closure

for i in 0..<2:
    myClosures.add generateClosure()
for p in myClosures:
    p()
myClosures.setLen(0)

echo "DECEPTIVE EXAMPLE - direct substitution"
for i in 0..<2:
    myClosures.add block:
        let myRef = MyRef()
        echo "OUTSIDE:", cast[int](myRef)
        proc closure() =
            echo "INSIDE:", cast[int](myRef)
        closure

for p in myClosures:
    p()
myClosures.setLen(0)


echo "WORKING EXAMPLE"
for i in 0..<2:
    myClosures.add block:
        proc closureGenerator(): proc() =
            let myRef = MyRef()
            echo "OUTSIDE:", cast[int](myRef)
            proc closure() =
                echo "INSIDE:", cast[int](myRef)
            closure
        closureGenerator()

for p in myClosures:
    p()

Nim Version

On fedora, v2.0.4

Current Output

DECEPTIVE EXAMPLE - with template
OUTSIDE:139977855484000
OUTSIDE:139977855484096
INSIDE:139977855484096
INSIDE:139977855484096

DECEPTIVE EXAMPLE - direct subsitution
OUTSIDE:139977855484064
OUTSIDE:139977855484192
INSIDE:139977855484192
INSIDE:139977855484192

WORKING EXAMPLE
OUTSIDE:139977855484256
OUTSIDE:139977855484288
INSIDE:139977855484256
INSIDE:139977855484288

Possible Solution

I'm not sure if it is possible to capture a nested scope correctly (blocks/templates) without implying a closure. So I don't think a correction is possible without overhead or breaking how nim actually works.

However, it might certainly be possible for the compiler to detect when a referenced scope will be erased and should emit a warning or an error.

Alogani avatar Jun 06 '24 08:06 Alogani