Files
Nim/tests/ic/tclosure_hooks.nim
2026-09-01 16:47:03 +02:00

102 lines
2.7 KiB
Nim

discard """
description: '''IC vs `nim c`: closure environments, their hooks and their owners'''
"""
#? metamorphic
# A closure's environment type — and the `=destroy`/`=copy` the compiler lifts
# for it — is minted by the BACKEND, during the `lower` stage, and exists in no
# module's semmed NIF. The per-module backend has to decide which translation
# unit emits such a routine, and the owner walk it uses lands on the module of
# the ORIGINAL generic: for a generic closure iterator defined in one module and
# instantiated in another, that is a module which never sees the instance, so the
# env's `=destroy` was emitted by nobody (`undefined reference to
# eqdestroy__c485__…`). Every referencing TU emits it now.
#
# The steps then move the captured state around, because the env's LAYOUT is what
# decides whether those hooks are trivial: a body-only edit that adds a capture
# changes the env type of a routine whose importers do not re-sem.
#!FILE clleaf.nim
type Ev* = proc (s: string): string {.closure.}
proc leafMaker*(tag: string): Ev =
var n = 0
proc outer(s: string): string =
proc inner(t: string): string =
inc n
tag & ":" & t & ":" & $n
inner(s)
result = outer
iterator leafIter*[T](xs: seq[T]): T {.closure.} =
for x in xs: yield x
#!FILE clmid.nim
import clleaf
proc midMaker*(tag: string): Ev =
let base = leafMaker(tag & "/mid")
var calls = 0
result = proc (s: string): string =
inc calls
base(s) & "#" & $calls
proc midIter*(): seq[string] =
# instantiates `leafIter[string]` HERE, not where it is defined
result = @[]
for x in leafIter(@["p", "q"]): result.add x
#!FILE main.nim
import clleaf, clmid
let t = midMaker("top")
echo t("Alpha")
echo t("Beta")
echo midIter()
# an instance only the main module has
var fs: seq[float] = @[]
for x in leafIter(@[1.5, 2.5]): fs.add x
echo fs
#!STEP
# body-only edit that GROWS the environment: a second captured local
#!FILE clleaf.nim
type Ev* = proc (s: string): string {.closure.}
proc leafMaker*(tag: string): Ev =
var n = 0
var seen: seq[string] = @[]
proc outer(s: string): string =
proc inner(t: string): string =
inc n
seen.add t
tag & ":" & t & ":" & $n & ":" & $seen.len
inner(s)
result = outer
iterator leafIter*[T](xs: seq[T]): T {.closure.} =
var i = 0
for x in xs:
inc i
yield x
#!STEP
# and shrink it again
#!FILE clleaf.nim
type Ev* = proc (s: string): string {.closure.}
proc leafMaker*(tag: string): Ev =
var n = 0
proc outer(s: string): string =
proc inner(t: string): string =
inc n
tag & ":" & t & ":" & $n
inner(s)
result = outer
iterator leafIter*[T](xs: seq[T]): T {.closure.} =
for x in xs: yield x
#!STEP