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

proc: initial stepping with range-over-func support #3736

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
328 changes: 328 additions & 0 deletions _fixtures/rangeoverfunc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
package main

// The tests here are adapted from $GOROOT/src/cmd/compile/internal/rangefunc/rangefunc_test.go

import (
"fmt"
)

/*



THESE
LINES
INTENTIONALLY
LEFT
BLANK




*/

func TestTrickyIterAll() {
trickItAll := TrickyIterator{}
i := 0
for _, x := range trickItAll.iterAll([]int{30, 7, 8, 9, 10}) {
i += x
if i >= 36 {
break
}
}

fmt.Println("Got i = ", i)
}

func TestTrickyIterAll2() {
trickItAll := TrickyIterator{}
i := 0
for _, x := range trickItAll.iterAll([]int{42, 47}) {
i += x
}
fmt.Println(i)
}

func TestBreak1() {
var result []int
for _, x := range OfSliceIndex([]int{-1, -2, -4}) {
if x == -4 {
break
}
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if y == 3 {
break
}
result = append(result, y)
}
result = append(result, x)
}
fmt.Println(result)
}

func TestBreak2() {
var result []int
outer:
for _, x := range OfSliceIndex([]int{-1, -2, -4}) {
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if y == 3 {
break
}
if x == -4 {
break outer
}
result = append(result, y)
}
result = append(result, x)
}
fmt.Println(result)
}

func TestMultiCont0() {
var result []int

W:
for _, w := range OfSliceIndex([]int{1000, 2000}) {
result = append(result, w)
if w == 2000 {
break
}
for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
result = append(result, y)
for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if z&1 == 1 {
continue
}
result = append(result, z)
if z >= 4 {
continue W // modified to be multilevel
}
}
result = append(result, -y) // should never be executed
}
result = append(result, x)
}
}
fmt.Println(result)
}

func TestPanickyIterator1() {
var result []int
defer func() {
r := recover()
fmt.Println("Recovering", r)
}()
for _, z := range PanickyOfSliceIndex([]int{1, 2, 3, 4}) {
result = append(result, z)
if z == 4 {
break
}
}
fmt.Println(result)
}

func TestPanickyIterator2() {
var result []int
defer func() {
r := recover()
fmt.Println("Recovering ", r)
}()
for _, x := range OfSliceIndex([]int{100, 200}) {
result = append(result, x)
Y:
// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
for _, y := range VeryBadOfSliceIndex([]int{10, 20}) {
result = append(result, y)

// converts early exit into a panic --> 1, 2
for k, z := range PanickyOfSliceIndex([]int{1, 2}) { // iterator panics
result = append(result, z)
if k == 1 {
break Y
}
}
}
}
}

func TestPanickyIteratorWithNewDefer() {
var result []int
defer func() {
r := recover()
fmt.Println("Recovering ", r)
}()
for _, x := range OfSliceIndex([]int{100, 200}) {
result = append(result, x)
Y:
// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
for _, y := range VeryBadOfSliceIndex([]int{10, 20}) {
defer func() { // This defer will be set on TestPanickyIteratorWithNewDefer from TestPanickyIteratorWithNewDefer-range2
fmt.Println("y loop defer")
}()
result = append(result, y)

// converts early exit into a panic --> 1, 2
for k, z := range PanickyOfSliceIndex([]int{1, 2}) { // iterator panics
result = append(result, z)
if k == 1 {
break Y
}
}
}
}
}

func TestLongReturnWrapper() {
TestLongReturn()
fmt.Println("returned")
}

func TestLongReturn() {
for _, x := range OfSliceIndex([]int{1, 2, 3}) {
for _, y := range OfSliceIndex([]int{10, 20, 30}) {
if y == 10 {
return
}
}
fmt.Println(x)
}
}

func TestGotoA1() {
result := []int{}
for _, x := range OfSliceIndex([]int{-1, -4, -5}) {
result = append(result, x)
if x == -4 {
break
}
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if y == 3 {
goto A
}
result = append(result, y)
}
A:
result = append(result, x)
}
fmt.Println(result)
}

func TestGotoB1() {
result := []int{}
for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
result = append(result, x)
if x == -4 {
break
}
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if y == 3 {
goto B
}
result = append(result, y)
}
result = append(result, x)
}
B:
result = append(result, 999)
fmt.Println(result)
}

func main() {
TestTrickyIterAll()
TestTrickyIterAll2()
TestBreak1()
TestBreak2()
TestMultiCont0()
TestPanickyIterator1()
TestPanickyIterator2()
TestPanickyIteratorWithNewDefer()
TestLongReturnWrapper()
TestGotoA1()
TestGotoB1()
}

type Seq[T any] func(yield func(T) bool)
type Seq2[T1, T2 any] func(yield func(T1, T2) bool)

type TrickyIterator struct {
yield func(int, int) bool
}

func (ti *TrickyIterator) iterEcho(s []int) Seq2[int, int] {
return func(yield func(int, int) bool) {
for i, v := range s {
if !yield(i, v) {
ti.yield = yield
return
}
if ti.yield != nil && !ti.yield(i, v) {
return
}
}
ti.yield = yield
return
}
}

func (ti *TrickyIterator) iterAll(s []int) Seq2[int, int] {
return func(yield func(int, int) bool) {
ti.yield = yield // Save yield for future abuse
for i, v := range s {
if !yield(i, v) {
return
}
}
return
}
}

func (ti *TrickyIterator) iterZero(s []int) Seq2[int, int] {
return func(yield func(int, int) bool) {
ti.yield = yield // Save yield for future abuse
// Don't call it at all, maybe it won't escape
return
}
}

// OfSliceIndex returns a Seq2 over the elements of s. It is equivalent
// to range s.
func OfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
return func(yield func(int, T) bool) {
for i, v := range s {
if !yield(i, v) {
return
}
}
return
}
}

// PanickyOfSliceIndex iterates the slice but panics if it exits the loop early
func PanickyOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
return func(yield func(int, T) bool) {
for i, v := range s {
if !yield(i, v) {
panic(fmt.Errorf("Panicky iterator panicking"))
}
}
return
}
}

// VeryBadOfSliceIndex is "very bad" because it ignores the return value from yield
// and just keeps on iterating, and also wraps that call in a defer-recover so it can
// keep on trying after the first panic.
func VeryBadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
return func(yield func(int, T) bool) {
for i, v := range s {
func() {
defer func() {
recover()
}()
yield(i, v)
}()
}
return
}
}
Loading