Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closures capture scope is confusing, leading to unexpective behaviour #23684

Open
Alogani opened this issue Jun 6, 2024 · 0 comments
Open

Closures capture scope is confusing, leading to unexpective behaviour #23684

Alogani opened this issue Jun 6, 2024 · 0 comments
Labels

Comments

@Alogani
Copy link

Alogani commented Jun 6, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants