p_closurecap.gno
2.24 Kb · 60 lines
1// Package p_closurecap exercises the "var stopped bool in
2// memberStorage.IterateByOffset" pattern from boards2/commondao:
3// a method declares a local var that an inline closure captures and
4// writes to. Without the unreal-HIV exception in the readonly check,
5// this failed across borrow-realm transitions because the HIV's PkgID
6// stamp records the alloc-site realm, but the closure body may run
7// under a different borrowed realm — the check would fire on a write
8// the closure itself made to its own captured slot.
9package p_closurecap
10
11// Inner is the receiver of loop(). When Inner lives in a different
12// realm than the Outer-method's caller realm, PushFrameCall's borrow
13// rule 2 shifts m.Realm to Inner's realm for the entire body of loop()
14// (and the synchronously-invoked closure). That shift is what makes
15// HIV.PkgID (stamped at var stopped's alloc) differ from m.Realm at
16// the closure's write site.
17type Inner struct {
18 N int
19}
20
21// Outer is the var-stopped pattern. The inline closure captures
22// `stopped` and writes to it. Returns true if `fn` ever returned true.
23//
24// The key shape: Outer is a top-level /p/ func (no receiver, so no
25// borrow on entry — HIV stamps with caller's realm), but it delegates
26// to `inn.loop(...)`, where `inn` is supplied by the caller and may
27// live in a different realm. inn.loop's borrow flips m.Realm to inn's
28// realm; the inline closure then writes to `stopped` under that
29// borrowed realm.
30func Outer(inn *Inner, count int, fn func(i int) bool) bool {
31 var stopped bool
32 inn.loop(count, func(i int) bool {
33 stopped = fn(i)
34 return stopped
35 })
36 return stopped
37}
38
39// loop is a /p/ method on *Inner. As a receiver-method on a /p/ type,
40// PushFrameCall's borrow rule 2 fires here: m.Realm becomes
41// Inner.PkgID's realm for the duration of loop().
42func (in *Inner) loop(count int, cb func(i int) bool) {
43 for i := 0; i < count; i++ {
44 if cb(i) {
45 return
46 }
47 }
48}
49
50// MakeCounter returns a closure that captures a local int. The
51// returned closure can be stored in /r/ state — verifying that a
52// persisted closure-capture HIV (whose FuncLit lives in /p/) is still
53// writable when invoked from a foreign realm context.
54func MakeCounter(start int) func() int {
55 c := start
56 return func() int {
57 c++
58 return c
59 }
60}