grain-lang / grain

The Grain compiler toolchain and CLI. Home of the modern web staple. 🌾
https://grain-lang.org/
GNU Lesser General Public License v3.0
3.29k stars 114 forks source link

DecRefError related to closures for non heap based mutable values. #2197

Open spotandjake opened 2 weeks ago

spotandjake commented 2 weeks ago

I ran into a wierd decRef error with the below code:

let test = () => {
  let mut dependencyExists = false
  (() => {
    print(dependencyExists)
  })()
  (() => {
    print(dependencyExists)
  })()
  return dependencyExists
}
print(test())

This also happens if dependencyExists is a char or really any stack type. However if it is a Number, or heap type this error does not occur. Some cases that do work are below:

let test = () => {
  let dependencyExists = box(void)
  (() => {
    print(dependencyExists)
  })()
  (() => {
    print(dependencyExists)
  })()
  return dependencyExists
}
print("Test1")
print(test())

let test = () => {
  let mut dependencyExists = 1
  (() => {
    print(dependencyExists)
  })()
  (() => {
    print(dependencyExists)
  })()
  return dependencyExists
}
print("Test2")
print(test())
spotandjake commented 1 week ago

I stumbled upon some more cases where this is occurring:

module Main
from "set" include Set
use Set.{ module Immutable as Set }
from "array" include Array
Array.reduce((argResult, arg) => {
  Set.empty
}, Set.empty, [> 1, 1, 1, 1])
print("me")

Which also fails when refactored as below as if the reduce was inlined (to verify the behaviour):

module Main

from "set" include Set
use Set.{ module Immutable as Set }
from "array" include Array
let test = () => {
  let mut acc = Set.empty
  Array.forEach(el => acc = ((_, _) => {
    Set.empty
  })(acc, el), [>1, 1, 1, 1])
}
test()