diff --git a/compiler/coroutine_test.go b/compiler/coroutine_test.go index 8fd3e55..6098b86 100644 --- a/compiler/coroutine_test.go +++ b/compiler/coroutine_test.go @@ -124,6 +124,12 @@ func TestCoroutineYield(t *testing.T) { yields: []int{0, 3, 6, 9}, }, + { + name: "range over yield and assign to pointer", + coro: func() { RangeYieldAndAssign(4) }, + yields: []int{0, 1, 2, 3}, + }, + { name: "range over closure capturing values", coro: Range10ClosureCapturingValues, diff --git a/compiler/testdata/coroutine.go b/compiler/testdata/coroutine.go index 89114ab..654075c 100644 --- a/compiler/testdata/coroutine.go +++ b/compiler/testdata/coroutine.go @@ -282,6 +282,35 @@ func RangeTripleFuncValue(n int) { Range(n, f) } +func yieldAndAssign(assign *int, yield, value int) { + // The pointer assignment here gets confused because on resume, the variable + // that it refers to is recreated during the call to RangeYieldAndAssign, + // resulting in assigning the value to a different value than the local `i` + // variable of the parent function. + f := func() { *assign = value } + + // TODO: remove this function; it's here to ensure that both assign and + // value are seen as potentially being written to, so the compiler doesn't + // dereference their values in the closure type of f. + g := func() { assign = &value } + + coroutine.Yield[int, any](yield) + // If we make the call to f before yielding, the coroutine behaves as + // expected because the assign pointer is pointing to the `i` variable of + /// the parent function, but after resuming the pointers aren't the same + // anymore. + f() + + // TODO: remove + g() +} + +func RangeYieldAndAssign(n int) { + for i := 0; i < n; { + yieldAndAssign(&i, i, i+1) + } +} + func Range10ClosureCapturingValues() { i := 0 n := 10 diff --git a/compiler/testdata/coroutine_durable.go b/compiler/testdata/coroutine_durable.go index 309969f..c1c4346 100644 --- a/compiler/testdata/coroutine_durable.go +++ b/compiler/testdata/coroutine_durable.go @@ -2273,6 +2273,121 @@ func RangeTripleFuncValue(n int) { } } +func yieldAndAssign(assign *int, yield, value int) { + _c := coroutine.LoadContext[int, any]() + _f, _fp := _c.Push() + var _o0 func() + var _o1 func() + if _f.IP > 0 { + if _v := _f.Get(0); _v != nil { + assign = _v.(*int) + } + if _v := _f.Get(1); _v != nil { + yield = _v.(int) + } + if _v := _f.Get(2); _v != nil { + value = _v.(int) + } + if _v := _f.Get(3); _v != nil { + + _o0 = _v.(func()) + } + if _v := _f.Get(4); _v != nil { + + _o1 = _v.(func()) + } + } + defer func() { + if _c.Unwinding() { + _f.Set(0, assign) + _f.Set(1, yield) + _f.Set(2, value) + _f.Set(3, _o0) + _f.Set(4, _o1) + _c.Store(_fp, _f) + } else { + _c.Pop() + } + }() + switch { + case _f.IP < 2: + _o0 = func() { *assign = value } + _f.IP = 2 + fallthrough + case _f.IP < 3: + + _o1 = func() { assign = &value } + _f.IP = 3 + fallthrough + case _f.IP < 4: + + _o0() + _f.IP = 4 + fallthrough + case _f.IP < 5: + coroutine.Yield[int, any](yield) + _f.IP = 5 + fallthrough + case _f.IP < 6: + _o1() + } +} + +func RangeYieldAndAssign(n int) { + _c := coroutine.LoadContext[int, any]() + _f, _fp := _c.Push() + var _o0 int + var _o1 bool + if _f.IP > 0 { + if _v := _f.Get(0); _v != nil { + n = _v.(int) + } + if _v := _f.Get(1); _v != nil { + _o0 = _v.(int) + } + if _v := _f.Get(2); _v != nil { + _o1 = _v.(bool) + } + } + defer func() { + if _c.Unwinding() { + _f.Set(0, n) + _f.Set(1, _o0) + _f.Set(2, _o1) + _c.Store(_fp, _f) + } else { + _c.Pop() + } + }() + switch { + case _f.IP < 2: + _o0 = 0 + _f.IP = 2 + fallthrough + case _f.IP < 5: + _l0: + for ; ; _f.IP = 2 { + switch { + case _f.IP < 4: + switch { + case _f.IP < 3: + _o1 = !(_o0 < n) + _f.IP = 3 + fallthrough + case _f.IP < 4: + if _o1 { + break _l0 + } + } + _f.IP = 4 + fallthrough + case _f.IP < 5: + yieldAndAssign(&_o0, _o0, _o0+1) + } + } + } +} + func Range10ClosureCapturingValues() { _c := coroutine.LoadContext[int, any]() _f, _fp := _c.Push() diff --git a/compiler/testdata/coroutine_functypes.go b/compiler/testdata/coroutine_functypes.go index 6e8a0b4..c827f04 100644 --- a/compiler/testdata/coroutine_functypes.go +++ b/compiler/testdata/coroutine_functypes.go @@ -54,6 +54,7 @@ func init() { _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.RangeTriple.func1") _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.RangeTripleFuncValue") _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.RangeTripleFuncValue.func2") + _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.RangeYieldAndAssign") _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.Select") _types.RegisterFunc[func(int)]("github.com/stealthrocket/coroutine/compiler/testdata.Shadowing") _types.RegisterFunc[func()]("github.com/stealthrocket/coroutine/compiler/testdata.SomeFunctionThatShouldExistInTheCompiledFile") @@ -65,4 +66,15 @@ func init() { _types.RegisterFunc[func(int) int]("github.com/stealthrocket/coroutine/compiler/testdata.a") _types.RegisterFunc[func(int) int]("github.com/stealthrocket/coroutine/compiler/testdata.b") _types.RegisterFunc[func()]("github.com/stealthrocket/coroutine/compiler/testdata.init") + _types.RegisterFunc[func(*int, int, int)]("github.com/stealthrocket/coroutine/compiler/testdata.yieldAndAssign") + _types.RegisterClosure[func(), struct { + _ uintptr + assign **int + value *int + }]("github.com/stealthrocket/coroutine/compiler/testdata.yieldAndAssign.func2") + _types.RegisterClosure[func(), struct { + _ uintptr + assign **int + value *int + }]("github.com/stealthrocket/coroutine/compiler/testdata.yieldAndAssign.func3") }