• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package rangefunc_test
6
7import (
8	"fmt"
9	"regexp"
10	"slices"
11	"testing"
12)
13
14type Seq[T any] func(yield func(T) bool)
15type Seq2[T1, T2 any] func(yield func(T1, T2) bool)
16
17// OfSliceIndex returns a Seq2 over the elements of s. It is equivalent
18// to range s.
19func OfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
20	return func(yield func(int, T) bool) {
21		for i, v := range s {
22			if !yield(i, v) {
23				return
24			}
25		}
26		return
27	}
28}
29
30// BadOfSliceIndex is "bad" because it ignores the return value from yield
31// and just keeps on iterating.
32func BadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
33	return func(yield func(int, T) bool) {
34		for i, v := range s {
35			yield(i, v)
36		}
37		return
38	}
39}
40
41// VeryBadOfSliceIndex is "very bad" because it ignores the return value from yield
42// and just keeps on iterating, and also wraps that call in a defer-recover so it can
43// keep on trying after the first panic.
44func VeryBadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
45	return func(yield func(int, T) bool) {
46		for i, v := range s {
47			func() {
48				defer func() {
49					recover()
50				}()
51				yield(i, v)
52			}()
53		}
54		return
55	}
56}
57
58// SwallowPanicOfSliceIndex hides panics and converts them to normal return
59func SwallowPanicOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
60	return func(yield func(int, T) bool) {
61		for i, v := range s {
62			done := false
63			func() {
64				defer func() {
65					if r := recover(); r != nil {
66						done = true
67					}
68				}()
69				done = !yield(i, v)
70			}()
71			if done {
72				return
73			}
74		}
75		return
76	}
77}
78
79// PanickyOfSliceIndex iterates the slice but panics if it exits the loop early
80func PanickyOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
81	return func(yield func(int, T) bool) {
82		for i, v := range s {
83			if !yield(i, v) {
84				panic(fmt.Errorf("Panicky iterator panicking"))
85			}
86		}
87		return
88	}
89}
90
91// CooperativeBadOfSliceIndex calls the loop body from a goroutine after
92// a ping on a channel, and returns recover()on that same channel.
93func CooperativeBadOfSliceIndex[T any, S ~[]T](s S, proceed chan any) Seq2[int, T] {
94	return func(yield func(int, T) bool) {
95		for i, v := range s {
96			if !yield(i, v) {
97				// if the body breaks, call yield just once in a goroutine
98				go func() {
99					<-proceed
100					defer func() {
101						proceed <- recover()
102					}()
103					yield(0, s[0])
104				}()
105				return
106			}
107		}
108		return
109	}
110}
111
112// TrickyIterator is a type intended to test whether an iterator that
113// calls a yield function after loop exit must inevitably escape the
114// closure; this might be relevant to future checking/optimization.
115type TrickyIterator struct {
116	yield func(int, int) bool
117}
118
119func (ti *TrickyIterator) iterEcho(s []int) Seq2[int, int] {
120	return func(yield func(int, int) bool) {
121		for i, v := range s {
122			if !yield(i, v) {
123				ti.yield = yield
124				return
125			}
126			if ti.yield != nil && !ti.yield(i, v) {
127				return
128			}
129		}
130		ti.yield = yield
131		return
132	}
133}
134
135func (ti *TrickyIterator) iterAll(s []int) Seq2[int, int] {
136	return func(yield func(int, int) bool) {
137		ti.yield = yield // Save yield for future abuse
138		for i, v := range s {
139			if !yield(i, v) {
140				return
141			}
142		}
143		return
144	}
145}
146
147func (ti *TrickyIterator) iterOne(s []int) Seq2[int, int] {
148	return func(yield func(int, int) bool) {
149		ti.yield = yield // Save yield for future abuse
150		if len(s) > 0 {  // Not in a loop might escape differently
151			yield(0, s[0])
152		}
153		return
154	}
155}
156
157func (ti *TrickyIterator) iterZero(s []int) Seq2[int, int] {
158	return func(yield func(int, int) bool) {
159		ti.yield = yield // Save yield for future abuse
160		// Don't call it at all, maybe it won't escape
161		return
162	}
163}
164
165func (ti *TrickyIterator) fail() {
166	if ti.yield != nil {
167		ti.yield(1, 1)
168	}
169}
170
171const DONE = 0      // body of loop has exited in a non-panic way
172const READY = 1     // body of loop has not exited yet, is not running
173const PANIC = 2     // body of loop is either currently running, or has panicked
174const EXHAUSTED = 3 // iterator function return, i.e., sequence is "exhausted"
175
176const MISSING_PANIC = 4 // overload "READY" for panic call
177
178// Check2 wraps the function body passed to iterator forall
179// in code that ensures that it cannot (successfully) be called
180// either after body return false (control flow out of loop) or
181// forall itself returns (the iteration is now done).
182//
183// Note that this can catch errors before the inserted checks.
184func Check2[U, V any](forall Seq2[U, V]) Seq2[U, V] {
185	return func(body func(U, V) bool) {
186		state := READY
187		forall(func(u U, v V) bool {
188			if state != READY {
189				panic(fail[state])
190			}
191			state = PANIC
192			ret := body(u, v)
193			if ret {
194				state = READY
195			} else {
196				state = DONE
197			}
198			return ret
199		})
200		if state == PANIC {
201			panic(fail[MISSING_PANIC])
202		}
203		state = EXHAUSTED
204	}
205}
206
207func Check[U any](forall Seq[U]) Seq[U] {
208	return func(body func(U) bool) {
209		state := READY
210		forall(func(u U) bool {
211			if state != READY {
212				panic(fail[state])
213			}
214			state = PANIC
215			ret := body(u)
216			if ret {
217				state = READY
218			} else {
219				state = DONE
220			}
221			return ret
222		})
223		if state == PANIC {
224			panic(fail[MISSING_PANIC])
225		}
226		state = EXHAUSTED
227	}
228}
229
230func matchError(r any, x string) bool {
231	if r == nil {
232		return false
233	}
234	if x == "" {
235		return true
236	}
237	if p, ok := r.(errorString); ok {
238		return p.Error() == x
239	}
240	if p, ok := r.(error); ok {
241		e, err := regexp.Compile(x)
242		if err != nil {
243			panic(fmt.Errorf("Bad regexp '%s' passed to matchError", x))
244		}
245		return e.MatchString(p.Error())
246	}
247	return false
248}
249
250func matchErrorHelper(t *testing.T, r any, x string) {
251	if matchError(r, x) {
252		t.Logf("Saw expected panic '%v'", r)
253	} else {
254		t.Errorf("Saw wrong panic '%v', expected '%s'", r, x)
255	}
256}
257
258// An errorString represents a runtime error described by a single string.
259type errorString string
260
261func (e errorString) Error() string {
262	return string(e)
263}
264
265const (
266	// RERR_ is for runtime error, and may be regexps/substrings, to simplify use of tests with tools
267	RERR_DONE      = "runtime error: range function continued iteration after function for loop body returned false"
268	RERR_PANIC     = "runtime error: range function continued iteration after loop body panic"
269	RERR_EXHAUSTED = "runtime error: range function continued iteration after whole loop exit"
270	RERR_MISSING   = "runtime error: range function recovered a loop body panic and did not resume panicking"
271
272	// CERR_ is for checked errors in the Check combinator defined above, and should be literal strings
273	CERR_PFX       = "checked rangefunc error: "
274	CERR_DONE      = CERR_PFX + "loop iteration after body done"
275	CERR_PANIC     = CERR_PFX + "loop iteration after panic"
276	CERR_EXHAUSTED = CERR_PFX + "loop iteration after iterator exit"
277	CERR_MISSING   = CERR_PFX + "loop iterator swallowed panic"
278)
279
280var fail []error = []error{
281	errorString(CERR_DONE),
282	errorString(CERR_PFX + "loop iterator, unexpected error"),
283	errorString(CERR_PANIC),
284	errorString(CERR_EXHAUSTED),
285	errorString(CERR_MISSING),
286}
287
288func TestCheck(t *testing.T) {
289	i := 0
290	defer func() {
291		if r := recover(); r != nil {
292			if matchError(r, CERR_DONE) {
293				t.Logf("Saw expected panic '%v'", r)
294			} else {
295				t.Errorf("Saw wrong panic '%v'", r)
296			}
297		} else {
298			t.Error("Wanted to see a failure")
299		}
300	}()
301	for _, x := range Check2(BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) {
302		i += x
303		if i > 4*9 {
304			break
305		}
306	}
307}
308
309func TestCooperativeBadOfSliceIndex(t *testing.T) {
310	i := 0
311	proceed := make(chan any)
312	for _, x := range CooperativeBadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, proceed) {
313		i += x
314		if i >= 36 {
315			break
316		}
317	}
318	proceed <- true
319	if r := <-proceed; r != nil {
320		if matchError(r, RERR_EXHAUSTED) {
321			t.Logf("Saw expected panic '%v'", r)
322		} else {
323			t.Errorf("Saw wrong panic '%v'", r)
324		}
325	} else {
326		t.Error("Wanted to see a failure")
327	}
328	if i != 36 {
329		t.Errorf("Expected i == 36, saw %d instead", i)
330	} else {
331		t.Logf("i = %d", i)
332	}
333}
334
335func TestCooperativeBadOfSliceIndexCheck(t *testing.T) {
336	i := 0
337	proceed := make(chan any)
338	for _, x := range Check2(CooperativeBadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, proceed)) {
339		i += x
340		if i >= 36 {
341			break
342		}
343	}
344	proceed <- true
345	if r := <-proceed; r != nil {
346		if matchError(r, CERR_EXHAUSTED) {
347			t.Logf("Saw expected panic '%v'", r)
348		} else {
349			t.Errorf("Saw wrong panic '%v'", r)
350		}
351
352	} else {
353		t.Error("Wanted to see a failure")
354	}
355	if i != 36 {
356		t.Errorf("Expected i == 36, saw %d instead", i)
357	} else {
358		t.Logf("i = %d", i)
359	}
360}
361
362func TestTrickyIterAll(t *testing.T) {
363	trickItAll := TrickyIterator{}
364	i := 0
365	for _, x := range trickItAll.iterAll([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
366		i += x
367		if i >= 36 {
368			break
369		}
370	}
371
372	if i != 36 {
373		t.Errorf("Expected i == 36, saw %d instead", i)
374	} else {
375		t.Logf("i = %d", i)
376	}
377
378	defer func() {
379		if r := recover(); r != nil {
380			if matchError(r, RERR_EXHAUSTED) {
381				t.Logf("Saw expected panic '%v'", r)
382			} else {
383				t.Errorf("Saw wrong panic '%v'", r)
384			}
385		} else {
386			t.Error("Wanted to see a failure")
387		}
388	}()
389
390	trickItAll.fail()
391}
392
393func TestTrickyIterOne(t *testing.T) {
394	trickItOne := TrickyIterator{}
395	i := 0
396	for _, x := range trickItOne.iterOne([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
397		i += x
398		if i >= 36 {
399			break
400		}
401	}
402
403	// Don't care about value, ought to be 36 anyhow.
404	t.Logf("i = %d", i)
405
406	defer func() {
407		if r := recover(); r != nil {
408			if matchError(r, RERR_EXHAUSTED) {
409				t.Logf("Saw expected panic '%v'", r)
410			} else {
411				t.Errorf("Saw wrong panic '%v'", r)
412			}
413		} else {
414			t.Error("Wanted to see a failure")
415		}
416	}()
417
418	trickItOne.fail()
419}
420
421func TestTrickyIterZero(t *testing.T) {
422	trickItZero := TrickyIterator{}
423	i := 0
424	for _, x := range trickItZero.iterZero([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
425		i += x
426		if i >= 36 {
427			break
428		}
429	}
430
431	// Don't care about value, ought to be 0 anyhow.
432	t.Logf("i = %d", i)
433
434	defer func() {
435		if r := recover(); r != nil {
436			if matchError(r, RERR_EXHAUSTED) {
437				t.Logf("Saw expected panic '%v'", r)
438			} else {
439				t.Errorf("Saw wrong panic '%v'", r)
440			}
441		} else {
442			t.Error("Wanted to see a failure")
443		}
444	}()
445
446	trickItZero.fail()
447}
448
449func TestTrickyIterZeroCheck(t *testing.T) {
450	trickItZero := TrickyIterator{}
451	i := 0
452	for _, x := range Check2(trickItZero.iterZero([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) {
453		i += x
454		if i >= 36 {
455			break
456		}
457	}
458
459	// Don't care about value, ought to be 0 anyhow.
460	t.Logf("i = %d", i)
461
462	defer func() {
463		if r := recover(); r != nil {
464			if matchError(r, CERR_EXHAUSTED) {
465				t.Logf("Saw expected panic '%v'", r)
466			} else {
467				t.Errorf("Saw wrong panic '%v'", r)
468			}
469		} else {
470			t.Error("Wanted to see a failure")
471		}
472	}()
473
474	trickItZero.fail()
475}
476
477func TestTrickyIterEcho(t *testing.T) {
478	trickItAll := TrickyIterator{}
479	i := 0
480	for _, x := range trickItAll.iterAll([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
481		t.Logf("first loop i=%d", i)
482		i += x
483		if i >= 10 {
484			break
485		}
486	}
487
488	if i != 10 {
489		t.Errorf("Expected i == 10, saw %d instead", i)
490	} else {
491		t.Logf("i = %d", i)
492	}
493
494	defer func() {
495		if r := recover(); r != nil {
496			if matchError(r, RERR_EXHAUSTED) {
497				t.Logf("Saw expected panic '%v'", r)
498			} else {
499				t.Errorf("Saw wrong panic '%v'", r)
500			}
501		} else {
502			t.Error("Wanted to see a failure")
503		}
504	}()
505
506	i = 0
507	for _, x := range trickItAll.iterEcho([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
508		t.Logf("second loop i=%d", i)
509		if x >= 5 {
510			break
511		}
512	}
513
514}
515
516func TestTrickyIterEcho2(t *testing.T) {
517	trickItAll := TrickyIterator{}
518	var i int
519
520	defer func() {
521		if r := recover(); r != nil {
522			if matchError(r, RERR_EXHAUSTED) {
523				t.Logf("Saw expected panic '%v'", r)
524			} else {
525				t.Errorf("Saw wrong panic '%v'", r)
526			}
527		} else {
528			t.Error("Wanted to see a failure")
529		}
530	}()
531
532	for k := range 2 {
533		i = 0
534		for _, x := range trickItAll.iterEcho([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
535			t.Logf("k,x,i=%d,%d,%d", k, x, i)
536			i += x
537			if i >= 10 {
538				break
539			}
540		}
541		t.Logf("i = %d", i)
542
543		if i != 10 {
544			t.Errorf("Expected i == 10, saw %d instead", i)
545		}
546	}
547}
548
549// TestBreak1 should just work, with well-behaved iterators.
550// (The misbehaving iterator detector should not trigger.)
551func TestBreak1(t *testing.T) {
552	var result []int
553	var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3}
554	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) {
555		if x == -4 {
556			break
557		}
558		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
559			if y == 3 {
560				break
561			}
562			result = append(result, y)
563		}
564		result = append(result, x)
565	}
566	if !slices.Equal(expect, result) {
567		t.Errorf("Expected %v, got %v", expect, result)
568	}
569}
570
571// TestBreak2 should just work, with well-behaved iterators.
572// (The misbehaving iterator detector should not trigger.)
573func TestBreak2(t *testing.T) {
574	var result []int
575	var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3}
576outer:
577	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) {
578		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
579			if y == 3 {
580				break
581			}
582			if x == -4 {
583				break outer
584			}
585
586			result = append(result, y)
587		}
588		result = append(result, x)
589	}
590	if !slices.Equal(expect, result) {
591		t.Errorf("Expected %v, got %v", expect, result)
592	}
593}
594
595// TestContinue should just work, with well-behaved iterators.
596// (The misbehaving iterator detector should not trigger.)
597func TestContinue(t *testing.T) {
598	var result []int
599	var expect = []int{-1, 1, 2, -2, 1, 2, -3, 1, 2, -4}
600outer:
601	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) {
602		result = append(result, x)
603		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
604			if y == 3 {
605				continue outer
606			}
607			if x == -4 {
608				break outer
609			}
610
611			result = append(result, y)
612		}
613		result = append(result, x-10)
614	}
615	if !slices.Equal(expect, result) {
616		t.Errorf("Expected %v, got %v", expect, result)
617	}
618}
619
620// TestBreak3 should just work, with well-behaved iterators.
621// (The misbehaving iterator detector should not trigger.)
622func TestBreak3(t *testing.T) {
623	var result []int
624	var expect = []int{100, 10, 2, 4, 200, 10, 2, 4, 20, 2, 4, 300, 10, 2, 4, 20, 2, 4, 30}
625X:
626	for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
627	Y:
628		for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
629			if 10*y >= x {
630				break
631			}
632			result = append(result, y)
633			if y == 30 {
634				continue X
635			}
636		Z:
637			for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
638				if z&1 == 1 {
639					continue Z
640				}
641				result = append(result, z)
642				if z >= 4 {
643					continue Y
644				}
645			}
646			result = append(result, -y) // should never be executed
647		}
648		result = append(result, x)
649	}
650	if !slices.Equal(expect, result) {
651		t.Errorf("Expected %v, got %v", expect, result)
652	}
653}
654
655// TestBreak1BadA should end in a panic when the outer-loop's
656// single-level break is ignore by BadOfSliceIndex
657func TestBreak1BadA(t *testing.T) {
658	var result []int
659	var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3}
660
661	defer func() {
662		if r := recover(); r != nil {
663			if matchError(r, RERR_DONE) {
664				t.Logf("Saw expected panic '%v'", r)
665			} else {
666				t.Errorf("Saw wrong panic '%v'", r)
667			}
668			if !slices.Equal(expect, result) {
669				t.Errorf("Expected %v, got %v", expect, result)
670			}
671		} else {
672			t.Error("Wanted to see a failure")
673		}
674	}()
675
676	for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) {
677		if x == -4 {
678			break
679		}
680		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
681			if y == 3 {
682				break
683			}
684			result = append(result, y)
685		}
686		result = append(result, x)
687	}
688}
689
690// TestBreak1BadB should end in a panic, sooner, when the inner-loop's
691// (nested) single-level break is ignored by BadOfSliceIndex
692func TestBreak1BadB(t *testing.T) {
693	var result []int
694	var expect = []int{1, 2} // inner breaks, panics, after before outer appends
695
696	defer func() {
697		if r := recover(); r != nil {
698			if matchError(r, RERR_DONE) {
699				t.Logf("Saw expected panic '%v'", r)
700			} else {
701				t.Errorf("Saw wrong panic '%v'", r)
702			}
703			if !slices.Equal(expect, result) {
704				t.Errorf("Expected %v, got %v", expect, result)
705			}
706		} else {
707			t.Error("Wanted to see a failure")
708		}
709	}()
710
711	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
712		if x == -4 {
713			break
714		}
715		for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
716			if y == 3 {
717				break
718			}
719			result = append(result, y)
720		}
721		result = append(result, x)
722	}
723}
724
725// TestMultiCont0 tests multilevel continue with no bad iterators
726// (it should just work)
727func TestMultiCont0(t *testing.T) {
728	var result []int
729	var expect = []int{1000, 10, 2, 4, 2000}
730
731W:
732	for _, w := range OfSliceIndex([]int{1000, 2000}) {
733		result = append(result, w)
734		if w == 2000 {
735			break
736		}
737		for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
738			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
739				result = append(result, y)
740				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
741					if z&1 == 1 {
742						continue
743					}
744					result = append(result, z)
745					if z >= 4 {
746						continue W // modified to be multilevel
747					}
748				}
749				result = append(result, -y) // should never be executed
750			}
751			result = append(result, x)
752		}
753	}
754	if !slices.Equal(expect, result) {
755		t.Errorf("Expected %v, got %v", expect, result)
756	}
757}
758
759// TestMultiCont1 tests multilevel continue with a bad iterator
760// in the outermost loop exited by the continue.
761func TestMultiCont1(t *testing.T) {
762	var result []int
763	var expect = []int{1000, 10, 2, 4}
764	defer func() {
765		if r := recover(); r != nil {
766			if matchError(r, RERR_DONE) {
767				t.Logf("Saw expected panic '%v'", r)
768			} else {
769				t.Errorf("Saw wrong panic '%v'", r)
770			}
771			if !slices.Equal(expect, result) {
772				t.Errorf("Expected %v, got %v", expect, result)
773			}
774		} else {
775			t.Errorf("Wanted to see a failure, result was %v", result)
776		}
777	}()
778
779W:
780	for _, w := range OfSliceIndex([]int{1000, 2000}) {
781		result = append(result, w)
782		if w == 2000 {
783			break
784		}
785		for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) {
786			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
787				result = append(result, y)
788				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
789					if z&1 == 1 {
790						continue
791					}
792					result = append(result, z)
793					if z >= 4 {
794						continue W
795					}
796				}
797				result = append(result, -y) // should never be executed
798			}
799			result = append(result, x)
800		}
801	}
802	if !slices.Equal(expect, result) {
803		t.Errorf("Expected %v, got %v", expect, result)
804	}
805}
806
807// TestMultiCont2 tests multilevel continue with a bad iterator
808// in a middle loop exited by the continue.
809func TestMultiCont2(t *testing.T) {
810	var result []int
811	var expect = []int{1000, 10, 2, 4}
812	defer func() {
813		if r := recover(); r != nil {
814			if matchError(r, RERR_DONE) {
815				t.Logf("Saw expected panic '%v'", r)
816			} else {
817				t.Errorf("Saw wrong panic '%v'", r)
818			}
819			if !slices.Equal(expect, result) {
820				t.Errorf("Expected %v, got %v", expect, result)
821			}
822		} else {
823			t.Errorf("Wanted to see a failure, result was %v", result)
824		}
825	}()
826
827W:
828	for _, w := range OfSliceIndex([]int{1000, 2000}) {
829		result = append(result, w)
830		if w == 2000 {
831			break
832		}
833		for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
834			for _, y := range BadOfSliceIndex([]int{10, 20, 30, 40}) {
835				result = append(result, y)
836				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
837					if z&1 == 1 {
838						continue
839					}
840					result = append(result, z)
841					if z >= 4 {
842						continue W
843					}
844				}
845				result = append(result, -y) // should never be executed
846			}
847			result = append(result, x)
848		}
849	}
850	if !slices.Equal(expect, result) {
851		t.Errorf("Expected %v, got %v", expect, result)
852	}
853}
854
855// TestMultiCont3 tests multilevel continue with a bad iterator
856// in the innermost loop exited by the continue.
857func TestMultiCont3(t *testing.T) {
858	var result []int
859	var expect = []int{1000, 10, 2, 4}
860	defer func() {
861		if r := recover(); r != nil {
862			if matchError(r, RERR_DONE) {
863				t.Logf("Saw expected panic '%v'", r)
864			} else {
865				t.Errorf("Saw wrong panic '%v'", r)
866			}
867			if !slices.Equal(expect, result) {
868				t.Errorf("Expected %v, got %v", expect, result)
869			}
870		} else {
871			t.Errorf("Wanted to see a failure, result was %v", result)
872		}
873	}()
874
875W:
876	for _, w := range OfSliceIndex([]int{1000, 2000}) {
877		result = append(result, w)
878		if w == 2000 {
879			break
880		}
881		for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
882			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
883				result = append(result, y)
884				for _, z := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
885					if z&1 == 1 {
886						continue
887					}
888					result = append(result, z)
889					if z >= 4 {
890						continue W
891					}
892				}
893				result = append(result, -y) // should never be executed
894			}
895			result = append(result, x)
896		}
897	}
898	if !slices.Equal(expect, result) {
899		t.Errorf("Expected %v, got %v", expect, result)
900	}
901}
902
903// TestMultiBreak0 tests multilevel break with a bad iterator
904// in the outermost loop exited by the break (the outermost loop).
905func TestMultiBreak0(t *testing.T) {
906	var result []int
907	var expect = []int{1000, 10, 2, 4}
908	defer func() {
909		if r := recover(); r != nil {
910			if matchError(r, RERR_DONE) {
911				t.Logf("Saw expected panic '%v'", r)
912			} else {
913				t.Errorf("Saw wrong panic '%v'", r)
914			}
915			if !slices.Equal(expect, result) {
916				t.Errorf("Expected %v, got %v", expect, result)
917			}
918		} else {
919			t.Errorf("Wanted to see a failure, result was %v", result)
920		}
921	}()
922
923W:
924	for _, w := range BadOfSliceIndex([]int{1000, 2000}) {
925		result = append(result, w)
926		if w == 2000 {
927			break
928		}
929		for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
930			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
931				result = append(result, y)
932				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
933					if z&1 == 1 {
934						continue
935					}
936					result = append(result, z)
937					if z >= 4 {
938						break W
939					}
940				}
941				result = append(result, -y) // should never be executed
942			}
943			result = append(result, x)
944		}
945	}
946	if !slices.Equal(expect, result) {
947		t.Errorf("Expected %v, got %v", expect, result)
948	}
949}
950
951// TestMultiBreak1 tests multilevel break with a bad iterator
952// in an intermediate loop exited by the break.
953func TestMultiBreak1(t *testing.T) {
954	var result []int
955	var expect = []int{1000, 10, 2, 4}
956	defer func() {
957		if r := recover(); r != nil {
958			if matchError(r, RERR_DONE) {
959				t.Logf("Saw expected panic '%v'", r)
960			} else {
961				t.Errorf("Saw wrong panic '%v'", r)
962			}
963			if !slices.Equal(expect, result) {
964				t.Errorf("Expected %v, got %v", expect, result)
965			}
966		} else {
967			t.Errorf("Wanted to see a failure, result was %v", result)
968		}
969	}()
970
971W:
972	for _, w := range OfSliceIndex([]int{1000, 2000}) {
973		result = append(result, w)
974		if w == 2000 {
975			break
976		}
977		for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) {
978			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
979				result = append(result, y)
980				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
981					if z&1 == 1 {
982						continue
983					}
984					result = append(result, z)
985					if z >= 4 {
986						break W
987					}
988				}
989				result = append(result, -y) // should never be executed
990			}
991			result = append(result, x)
992		}
993	}
994	if !slices.Equal(expect, result) {
995		t.Errorf("Expected %v, got %v", expect, result)
996	}
997}
998
999// TestMultiBreak2 tests multilevel break with two bad iterators
1000// in intermediate loops exited by the break.
1001func TestMultiBreak2(t *testing.T) {
1002	var result []int
1003	var expect = []int{1000, 10, 2, 4}
1004	defer func() {
1005		if r := recover(); r != nil {
1006			if matchError(r, RERR_DONE) {
1007				t.Logf("Saw expected panic '%v'", r)
1008			} else {
1009				t.Errorf("Saw wrong panic '%v'", r)
1010			}
1011			if !slices.Equal(expect, result) {
1012				t.Errorf("Expected %v, got %v", expect, result)
1013			}
1014		} else {
1015			t.Errorf("Wanted to see a failure, result was %v", result)
1016		}
1017	}()
1018
1019W:
1020	for _, w := range OfSliceIndex([]int{1000, 2000}) {
1021		result = append(result, w)
1022		if w == 2000 {
1023			break
1024		}
1025		for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) {
1026			for _, y := range BadOfSliceIndex([]int{10, 20, 30, 40}) {
1027				result = append(result, y)
1028				for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1029					if z&1 == 1 {
1030						continue
1031					}
1032					result = append(result, z)
1033					if z >= 4 {
1034						break W
1035					}
1036				}
1037				result = append(result, -y) // should never be executed
1038			}
1039			result = append(result, x)
1040		}
1041	}
1042	if !slices.Equal(expect, result) {
1043		t.Errorf("Expected %v, got %v", expect, result)
1044	}
1045}
1046
1047// TestMultiBreak3 tests multilevel break with the bad iterator
1048// in the innermost loop exited by the break.
1049func TestMultiBreak3(t *testing.T) {
1050	var result []int
1051	var expect = []int{1000, 10, 2, 4}
1052	defer func() {
1053		if r := recover(); r != nil {
1054			if matchError(r, RERR_DONE) {
1055				t.Logf("Saw expected panic '%v'", r)
1056			} else {
1057				t.Errorf("Saw wrong panic '%v'", r)
1058			}
1059			if !slices.Equal(expect, result) {
1060				t.Errorf("Expected %v, got %v", expect, result)
1061			}
1062		} else {
1063			t.Errorf("Wanted to see a failure, result was %v", result)
1064		}
1065	}()
1066
1067W:
1068	for _, w := range OfSliceIndex([]int{1000, 2000}) {
1069		result = append(result, w)
1070		if w == 2000 {
1071			break
1072		}
1073		for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) {
1074			for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) {
1075				result = append(result, y)
1076				for _, z := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1077					if z&1 == 1 {
1078						continue
1079					}
1080					result = append(result, z)
1081					if z >= 4 {
1082						break W
1083					}
1084				}
1085				result = append(result, -y) // should never be executed
1086			}
1087			result = append(result, x)
1088		}
1089	}
1090	if !slices.Equal(expect, result) {
1091		t.Errorf("Expected %v, got %v", expect, result)
1092	}
1093}
1094
1095func TestPanickyIterator1(t *testing.T) {
1096	var result []int
1097	var expect = []int{1, 2, 3, 4}
1098	defer func() {
1099		if r := recover(); r != nil {
1100			if matchError(r, "Panicky iterator panicking") {
1101				t.Logf("Saw expected panic '%v'", r)
1102			} else {
1103				t.Errorf("Saw wrong panic '%v'", r)
1104			}
1105		} else {
1106			t.Errorf("Wanted to see a failure, result was %v", result)
1107		}
1108		if !slices.Equal(expect, result) {
1109			t.Errorf("Expected %v, got %v", expect, result)
1110		}
1111	}()
1112	for _, z := range PanickyOfSliceIndex([]int{1, 2, 3, 4}) {
1113		result = append(result, z)
1114		if z == 4 {
1115			break
1116		}
1117	}
1118}
1119
1120func TestPanickyIterator1Check(t *testing.T) {
1121	var result []int
1122	var expect = []int{1, 2, 3, 4}
1123	defer func() {
1124		if r := recover(); r != nil {
1125			if matchError(r, "Panicky iterator panicking") {
1126				t.Logf("Saw expected panic '%v'", r)
1127			} else {
1128				t.Errorf("Saw wrong panic '%v'", r)
1129			}
1130			if !slices.Equal(expect, result) {
1131				t.Errorf("Expected %v, got %v", expect, result)
1132			}
1133		} else {
1134			t.Errorf("Wanted to see a failure, result was %v", result)
1135		}
1136	}()
1137	for _, z := range Check2(PanickyOfSliceIndex([]int{1, 2, 3, 4})) {
1138		result = append(result, z)
1139		if z == 4 {
1140			break
1141		}
1142	}
1143}
1144
1145func TestPanickyIterator2(t *testing.T) {
1146	var result []int
1147	var expect = []int{100, 10, 1, 2}
1148	defer func() {
1149		if r := recover(); r != nil {
1150			if matchError(r, RERR_MISSING) {
1151				t.Logf("Saw expected panic '%v'", r)
1152			} else {
1153				t.Errorf("Saw wrong panic '%v'", r)
1154			}
1155		} else {
1156			t.Errorf("Wanted to see a failure, result was %v", result)
1157		}
1158		if !slices.Equal(expect, result) {
1159			t.Errorf("Expected %v, got %v", expect, result)
1160		}
1161	}()
1162	for _, x := range OfSliceIndex([]int{100, 200}) {
1163		result = append(result, x)
1164	Y:
1165		// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
1166		for _, y := range VeryBadOfSliceIndex([]int{10, 20}) {
1167			result = append(result, y)
1168
1169			// converts early exit into a panic --> 1, 2
1170			for k, z := range PanickyOfSliceIndex([]int{1, 2}) { // iterator panics
1171				result = append(result, z)
1172				if k == 1 {
1173					break Y
1174				}
1175			}
1176		}
1177	}
1178}
1179
1180func TestPanickyIterator2Check(t *testing.T) {
1181	var result []int
1182	var expect = []int{100, 10, 1, 2}
1183	defer func() {
1184		if r := recover(); r != nil {
1185			if matchError(r, CERR_MISSING) {
1186				t.Logf("Saw expected panic '%v'", r)
1187			} else {
1188				t.Errorf("Saw wrong panic '%v'", r)
1189			}
1190		} else {
1191			t.Errorf("Wanted to see a failure, result was %v", result)
1192		}
1193		if !slices.Equal(expect, result) {
1194			t.Errorf("Expected %v, got %v", expect, result)
1195		}
1196	}()
1197	for _, x := range Check2(OfSliceIndex([]int{100, 200})) {
1198		result = append(result, x)
1199	Y:
1200		// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
1201		for _, y := range Check2(VeryBadOfSliceIndex([]int{10, 20})) {
1202			result = append(result, y)
1203
1204			// converts early exit into a panic --> 1, 2
1205			for k, z := range Check2(PanickyOfSliceIndex([]int{1, 2})) { // iterator panics
1206				result = append(result, z)
1207				if k == 1 {
1208					break Y
1209				}
1210			}
1211		}
1212	}
1213}
1214
1215func TestPanickyIterator3(t *testing.T) {
1216	var result []int
1217	var expect = []int{100, 10, 1, 2, 200, 10, 1, 2}
1218	defer func() {
1219		if r := recover(); r != nil {
1220			t.Errorf("Unexpected panic '%v'", r)
1221		}
1222		if !slices.Equal(expect, result) {
1223			t.Errorf("Expected %v, got %v", expect, result)
1224		}
1225	}()
1226	for _, x := range OfSliceIndex([]int{100, 200}) {
1227		result = append(result, x)
1228	Y:
1229		// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
1230		// This is cross-checked against the checked iterator below; the combinator should behave the same.
1231		for _, y := range VeryBadOfSliceIndex([]int{10, 20}) {
1232			result = append(result, y)
1233
1234			for k, z := range OfSliceIndex([]int{1, 2}) { // iterator does not panic
1235				result = append(result, z)
1236				if k == 1 {
1237					break Y
1238				}
1239			}
1240		}
1241	}
1242}
1243func TestPanickyIterator3Check(t *testing.T) {
1244	var result []int
1245	var expect = []int{100, 10, 1, 2, 200, 10, 1, 2}
1246	defer func() {
1247		if r := recover(); r != nil {
1248			t.Errorf("Unexpected panic '%v'", r)
1249		}
1250		if !slices.Equal(expect, result) {
1251			t.Errorf("Expected %v, got %v", expect, result)
1252		}
1253	}()
1254	for _, x := range Check2(OfSliceIndex([]int{100, 200})) {
1255		result = append(result, x)
1256	Y:
1257		// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2
1258		for _, y := range Check2(VeryBadOfSliceIndex([]int{10, 20})) {
1259			result = append(result, y)
1260
1261			for k, z := range Check2(OfSliceIndex([]int{1, 2})) { // iterator does not panic
1262				result = append(result, z)
1263				if k == 1 {
1264					break Y
1265				}
1266			}
1267		}
1268	}
1269}
1270
1271func TestPanickyIterator4(t *testing.T) {
1272	var result []int
1273	var expect = []int{1, 2, 3}
1274	defer func() {
1275		if r := recover(); r != nil {
1276			if matchError(r, RERR_MISSING) {
1277				t.Logf("Saw expected panic '%v'", r)
1278			} else {
1279				t.Errorf("Saw wrong panic '%v'", r)
1280			}
1281		}
1282		if !slices.Equal(expect, result) {
1283			t.Errorf("Expected %v, got %v", expect, result)
1284		}
1285	}()
1286	for _, x := range SwallowPanicOfSliceIndex([]int{1, 2, 3, 4}) {
1287		result = append(result, x)
1288		if x == 3 {
1289			panic("x is 3")
1290		}
1291	}
1292
1293}
1294func TestPanickyIterator4Check(t *testing.T) {
1295	var result []int
1296	var expect = []int{1, 2, 3}
1297	defer func() {
1298		if r := recover(); r != nil {
1299			if matchError(r, CERR_MISSING) {
1300				t.Logf("Saw expected panic '%v'", r)
1301			} else {
1302				t.Errorf("Saw wrong panic '%v'", r)
1303			}
1304		}
1305		if !slices.Equal(expect, result) {
1306			t.Errorf("Expected %v, got %v", expect, result)
1307		}
1308	}()
1309	for _, x := range Check2(SwallowPanicOfSliceIndex([]int{1, 2, 3, 4})) {
1310		result = append(result, x)
1311		if x == 3 {
1312			panic("x is 3")
1313		}
1314	}
1315
1316}
1317
1318// veryBad tests that a loop nest behaves sensibly in the face of a
1319// "very bad" iterator.  In this case, "sensibly" means that the
1320// break out of X still occurs after the very bad iterator finally
1321// quits running (the control flow bread crumbs remain.)
1322func veryBad(s []int) []int {
1323	var result []int
1324X:
1325	for _, x := range OfSliceIndex([]int{1, 2, 3}) {
1326
1327		result = append(result, x)
1328
1329		for _, y := range VeryBadOfSliceIndex(s) {
1330			result = append(result, y)
1331			break X
1332		}
1333		for _, z := range OfSliceIndex([]int{100, 200, 300}) {
1334			result = append(result, z)
1335			if z == 100 {
1336				break
1337			}
1338		}
1339	}
1340	return result
1341}
1342
1343// veryBadCheck wraps a "very bad" iterator with Check,
1344// demonstrating that the very bad iterator also hides panics
1345// thrown by Check.
1346func veryBadCheck(s []int) []int {
1347	var result []int
1348X:
1349	for _, x := range OfSliceIndex([]int{1, 2, 3}) {
1350
1351		result = append(result, x)
1352
1353		for _, y := range Check2(VeryBadOfSliceIndex(s)) {
1354			result = append(result, y)
1355			break X
1356		}
1357		for _, z := range OfSliceIndex([]int{100, 200, 300}) {
1358			result = append(result, z)
1359			if z == 100 {
1360				break
1361			}
1362		}
1363	}
1364	return result
1365}
1366
1367// okay is the not-bad version of veryBad.
1368// They should behave the same.
1369func okay(s []int) []int {
1370	var result []int
1371X:
1372	for _, x := range OfSliceIndex([]int{1, 2, 3}) {
1373
1374		result = append(result, x)
1375
1376		for _, y := range OfSliceIndex(s) {
1377			result = append(result, y)
1378			break X
1379		}
1380		for _, z := range OfSliceIndex([]int{100, 200, 300}) {
1381			result = append(result, z)
1382			if z == 100 {
1383				break
1384			}
1385		}
1386	}
1387	return result
1388}
1389
1390// TestVeryBad1 checks the behavior of an extremely poorly behaved iterator.
1391func TestVeryBad1(t *testing.T) {
1392	result := veryBad([]int{10, 20, 30, 40, 50}) // odd length
1393	expect := []int{1, 10}
1394
1395	if !slices.Equal(expect, result) {
1396		t.Errorf("Expected %v, got %v", expect, result)
1397	}
1398}
1399
1400// TestVeryBad2 checks the behavior of an extremely poorly behaved iterator.
1401func TestVeryBad2(t *testing.T) {
1402	result := veryBad([]int{10, 20, 30, 40}) // even length
1403	expect := []int{1, 10}
1404
1405	if !slices.Equal(expect, result) {
1406		t.Errorf("Expected %v, got %v", expect, result)
1407	}
1408}
1409
1410// TestVeryBadCheck checks the behavior of an extremely poorly behaved iterator,
1411// which also suppresses the exceptions from "Check"
1412func TestVeryBadCheck(t *testing.T) {
1413	result := veryBadCheck([]int{10, 20, 30, 40}) // even length
1414	expect := []int{1, 10}
1415
1416	if !slices.Equal(expect, result) {
1417		t.Errorf("Expected %v, got %v", expect, result)
1418	}
1419}
1420
1421// TestOk is the nice version of the very bad iterator.
1422func TestOk(t *testing.T) {
1423	result := okay([]int{10, 20, 30, 40, 50}) // odd length
1424	expect := []int{1, 10}
1425
1426	if !slices.Equal(expect, result) {
1427		t.Errorf("Expected %v, got %v", expect, result)
1428	}
1429}
1430
1431// testBreak1BadDefer checks that defer behaves properly even in
1432// the presence of loop bodies panicking out of bad iterators.
1433// (i.e., the instrumentation did not break defer in these loops)
1434func testBreak1BadDefer(t *testing.T) (result []int) {
1435	var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3, -30, -20, -10}
1436
1437	defer func() {
1438		if r := recover(); r != nil {
1439			if matchError(r, RERR_DONE) {
1440				t.Logf("Saw expected panic '%v'", r)
1441			} else {
1442				t.Errorf("Saw wrong panic '%v'", r)
1443			}
1444			if !slices.Equal(expect, result) {
1445				t.Errorf("(Inner) Expected %v, got %v", expect, result)
1446			}
1447		} else {
1448			t.Error("Wanted to see a failure")
1449		}
1450	}()
1451
1452	for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1453		if x == -4 {
1454			break
1455		}
1456		defer func() {
1457			result = append(result, x*10)
1458		}()
1459		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1460			if y == 3 {
1461				break
1462			}
1463			result = append(result, y)
1464		}
1465		result = append(result, x)
1466	}
1467	return
1468}
1469
1470func TestBreak1BadDefer(t *testing.T) {
1471	var result []int
1472	var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3, -30, -20, -10}
1473	result = testBreak1BadDefer(t)
1474	if !slices.Equal(expect, result) {
1475		t.Errorf("(Outer) Expected %v, got %v", expect, result)
1476	}
1477}
1478
1479// testReturn1 has no bad iterators.
1480func testReturn1(t *testing.T) (result []int, err any) {
1481	defer func() {
1482		err = recover()
1483	}()
1484	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1485		result = append(result, x)
1486		if x == -4 {
1487			break
1488		}
1489		defer func() {
1490			result = append(result, x*10)
1491		}()
1492		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1493			if y == 3 {
1494				return
1495			}
1496			result = append(result, y)
1497		}
1498		result = append(result, x)
1499	}
1500	return
1501}
1502
1503// testReturn2 has an outermost bad iterator
1504func testReturn2(t *testing.T) (result []int, err any) {
1505	defer func() {
1506		err = recover()
1507	}()
1508	for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1509		result = append(result, x)
1510		if x == -4 {
1511			break
1512		}
1513		defer func() {
1514			result = append(result, x*10)
1515		}()
1516		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1517			if y == 3 {
1518				return
1519			}
1520			result = append(result, y)
1521		}
1522		result = append(result, x)
1523	}
1524	return
1525}
1526
1527// testReturn3 has an innermost bad iterator
1528func testReturn3(t *testing.T) (result []int, err any) {
1529	defer func() {
1530		err = recover()
1531	}()
1532	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1533		result = append(result, x)
1534		if x == -4 {
1535			break
1536		}
1537		defer func() {
1538			result = append(result, x*10)
1539		}()
1540		for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1541			if y == 3 {
1542				return
1543			}
1544			result = append(result, y)
1545		}
1546	}
1547	return
1548}
1549
1550// testReturn4 has no bad iterators, but exercises  return variable rewriting
1551// differs from testReturn1 because deferred append to "result" does not change
1552// the return value in this case.
1553func testReturn4(t *testing.T) (_ []int, _ []int, err any) {
1554	var result []int
1555	defer func() {
1556		err = recover()
1557	}()
1558	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1559		result = append(result, x)
1560		if x == -4 {
1561			break
1562		}
1563		defer func() {
1564			result = append(result, x*10)
1565		}()
1566		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1567			if y == 3 {
1568				return result, result, nil
1569			}
1570			result = append(result, y)
1571		}
1572		result = append(result, x)
1573	}
1574	return
1575}
1576
1577// TestReturns checks that returns through bad iterators behave properly,
1578// for inner and outer bad iterators.
1579func TestReturns(t *testing.T) {
1580	var result []int
1581	var result2 []int
1582	var expect = []int{-1, 1, 2, -10}
1583	var expect2 = []int{-1, 1, 2}
1584	var err any
1585
1586	result, err = testReturn1(t)
1587	if !slices.Equal(expect, result) {
1588		t.Errorf("Expected %v, got %v", expect, result)
1589	}
1590	if err != nil {
1591		t.Errorf("Unexpected error %v", err)
1592	}
1593
1594	result, err = testReturn2(t)
1595	if !slices.Equal(expect, result) {
1596		t.Errorf("Expected %v, got %v", expect, result)
1597	}
1598	if err == nil {
1599		t.Errorf("Missing expected error")
1600	} else {
1601		if matchError(err, RERR_DONE) {
1602			t.Logf("Saw expected panic '%v'", err)
1603		} else {
1604			t.Errorf("Saw wrong panic '%v'", err)
1605		}
1606	}
1607
1608	result, err = testReturn3(t)
1609	if !slices.Equal(expect, result) {
1610		t.Errorf("Expected %v, got %v", expect, result)
1611	}
1612	if err == nil {
1613		t.Errorf("Missing expected error")
1614	} else {
1615		if matchError(err, RERR_DONE) {
1616			t.Logf("Saw expected panic '%v'", err)
1617		} else {
1618			t.Errorf("Saw wrong panic '%v'", err)
1619		}
1620	}
1621
1622	result, result2, err = testReturn4(t)
1623	if !slices.Equal(expect2, result) {
1624		t.Errorf("Expected %v, got %v", expect2, result)
1625	}
1626	if !slices.Equal(expect2, result2) {
1627		t.Errorf("Expected %v, got %v", expect2, result2)
1628	}
1629	if err != nil {
1630		t.Errorf("Unexpected error %v", err)
1631	}
1632}
1633
1634// testGotoA1 tests loop-nest-internal goto, no bad iterators.
1635func testGotoA1(t *testing.T) (result []int, err any) {
1636	defer func() {
1637		err = recover()
1638	}()
1639	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1640		result = append(result, x)
1641		if x == -4 {
1642			break
1643		}
1644		defer func() {
1645			result = append(result, x*10)
1646		}()
1647		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1648			if y == 3 {
1649				goto A
1650			}
1651			result = append(result, y)
1652		}
1653		result = append(result, x)
1654	A:
1655	}
1656	return
1657}
1658
1659// testGotoA2 tests loop-nest-internal goto, outer bad iterator.
1660func testGotoA2(t *testing.T) (result []int, err any) {
1661	defer func() {
1662		err = recover()
1663	}()
1664	for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1665		result = append(result, x)
1666		if x == -4 {
1667			break
1668		}
1669		defer func() {
1670			result = append(result, x*10)
1671		}()
1672		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1673			if y == 3 {
1674				goto A
1675			}
1676			result = append(result, y)
1677		}
1678		result = append(result, x)
1679	A:
1680	}
1681	return
1682}
1683
1684// testGotoA3 tests loop-nest-internal goto, inner bad iterator.
1685func testGotoA3(t *testing.T) (result []int, err any) {
1686	defer func() {
1687		err = recover()
1688	}()
1689	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1690		result = append(result, x)
1691		if x == -4 {
1692			break
1693		}
1694		defer func() {
1695			result = append(result, x*10)
1696		}()
1697		for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1698			if y == 3 {
1699				goto A
1700			}
1701			result = append(result, y)
1702		}
1703		result = append(result, x)
1704	A:
1705	}
1706	return
1707}
1708
1709func TestGotoA(t *testing.T) {
1710	var result []int
1711	var expect = []int{-1, 1, 2, -2, 1, 2, -3, 1, 2, -4, -30, -20, -10}
1712	var expect3 = []int{-1, 1, 2, -10} // first goto becomes a panic
1713	var err any
1714
1715	result, err = testGotoA1(t)
1716	if !slices.Equal(expect, result) {
1717		t.Errorf("Expected %v, got %v", expect, result)
1718	}
1719	if err != nil {
1720		t.Errorf("Unexpected error %v", err)
1721	}
1722
1723	result, err = testGotoA2(t)
1724	if !slices.Equal(expect, result) {
1725		t.Errorf("Expected %v, got %v", expect, result)
1726	}
1727	if err == nil {
1728		t.Errorf("Missing expected error")
1729	} else {
1730		if matchError(err, RERR_DONE) {
1731			t.Logf("Saw expected panic '%v'", err)
1732		} else {
1733			t.Errorf("Saw wrong panic '%v'", err)
1734		}
1735	}
1736
1737	result, err = testGotoA3(t)
1738	if !slices.Equal(expect3, result) {
1739		t.Errorf("Expected %v, got %v", expect3, result)
1740	}
1741	if err == nil {
1742		t.Errorf("Missing expected error")
1743	} else {
1744		if matchError(err, RERR_DONE) {
1745			t.Logf("Saw expected panic '%v'", err)
1746		} else {
1747			t.Errorf("Saw wrong panic '%v'", err)
1748		}
1749	}
1750}
1751
1752// testGotoB1 tests loop-nest-exiting goto, no bad iterators.
1753func testGotoB1(t *testing.T) (result []int, err any) {
1754	defer func() {
1755		err = recover()
1756	}()
1757	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1758		result = append(result, x)
1759		if x == -4 {
1760			break
1761		}
1762		defer func() {
1763			result = append(result, x*10)
1764		}()
1765		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1766			if y == 3 {
1767				goto B
1768			}
1769			result = append(result, y)
1770		}
1771		result = append(result, x)
1772	}
1773B:
1774	result = append(result, 999)
1775	return
1776}
1777
1778// testGotoB2 tests loop-nest-exiting goto, outer bad iterator.
1779func testGotoB2(t *testing.T) (result []int, err any) {
1780	defer func() {
1781		err = recover()
1782	}()
1783	for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1784		result = append(result, x)
1785		if x == -4 {
1786			break
1787		}
1788		defer func() {
1789			result = append(result, x*10)
1790		}()
1791		for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1792			if y == 3 {
1793				goto B
1794			}
1795			result = append(result, y)
1796		}
1797		result = append(result, x)
1798	}
1799B:
1800	result = append(result, 999)
1801	return
1802}
1803
1804// testGotoB3 tests loop-nest-exiting goto, inner bad iterator.
1805func testGotoB3(t *testing.T) (result []int, err any) {
1806	defer func() {
1807		err = recover()
1808	}()
1809	for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) {
1810		result = append(result, x)
1811		if x == -4 {
1812			break
1813		}
1814		defer func() {
1815			result = append(result, x*10)
1816		}()
1817		for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
1818			if y == 3 {
1819				goto B
1820			}
1821			result = append(result, y)
1822		}
1823		result = append(result, x)
1824	}
1825B:
1826	result = append(result, 999)
1827	return
1828}
1829
1830func TestGotoB(t *testing.T) {
1831	var result []int
1832	var expect = []int{-1, 1, 2, 999, -10}
1833	var expectX = []int{-1, 1, 2, -10}
1834	var err any
1835
1836	result, err = testGotoB1(t)
1837	if !slices.Equal(expect, result) {
1838		t.Errorf("Expected %v, got %v", expect, result)
1839	}
1840	if err != nil {
1841		t.Errorf("Unexpected error %v", err)
1842	}
1843
1844	result, err = testGotoB2(t)
1845	if !slices.Equal(expectX, result) {
1846		t.Errorf("Expected %v, got %v", expectX, result)
1847	}
1848	if err == nil {
1849		t.Errorf("Missing expected error")
1850	} else {
1851		if matchError(err, RERR_DONE) {
1852			t.Logf("Saw expected panic '%v'", err)
1853		} else {
1854			t.Errorf("Saw wrong panic '%v'", err)
1855		}
1856	}
1857
1858	result, err = testGotoB3(t)
1859	if !slices.Equal(expectX, result) {
1860		t.Errorf("Expected %v, got %v", expectX, result)
1861	}
1862	if err == nil {
1863		t.Errorf("Missing expected error")
1864	} else {
1865		matchErrorHelper(t, err, RERR_DONE)
1866	}
1867}
1868
1869// once returns an iterator that runs its loop body once with the supplied value
1870func once[T any](x T) Seq[T] {
1871	return func(yield func(T) bool) {
1872		yield(x)
1873	}
1874}
1875
1876// terrify converts an iterator into one that panics with the supplied string
1877// if/when the loop body terminates early (returns false, for break, goto, outer
1878// continue, or return).
1879func terrify[T any](s string, forall Seq[T]) Seq[T] {
1880	return func(yield func(T) bool) {
1881		forall(func(v T) bool {
1882			if !yield(v) {
1883				panic(s)
1884			}
1885			return true
1886		})
1887	}
1888}
1889
1890func use[T any](T) {
1891}
1892
1893// f runs a not-rangefunc iterator that recovers from a panic that follows execution of a return.
1894// what does f return?
1895func f() string {
1896	defer func() { recover() }()
1897	defer panic("f panic")
1898	for _, s := range []string{"f return"} {
1899		return s
1900	}
1901	return "f not reached"
1902}
1903
1904// g runs a rangefunc iterator that recovers from a panic that follows execution of a return.
1905// what does g return?
1906func g() string {
1907	defer func() { recover() }()
1908	for s := range terrify("g panic", once("g return")) {
1909		return s
1910	}
1911	return "g not reached"
1912}
1913
1914// h runs a rangefunc iterator that recovers from a panic that follows execution of a return.
1915// the panic occurs in the rangefunc iterator itself.
1916// what does h return?
1917func h() (hashS string) {
1918	defer func() { recover() }()
1919	for s := range terrify("h panic", once("h return")) {
1920		hashS := s
1921		use(hashS)
1922		return s
1923	}
1924	return "h not reached"
1925}
1926
1927func j() (hashS string) {
1928	defer func() { recover() }()
1929	for s := range terrify("j panic", once("j return")) {
1930		hashS = s
1931		return
1932	}
1933	return "j not reached"
1934}
1935
1936// k runs a rangefunc iterator that recovers from a panic that follows execution of a return.
1937// the panic occurs in the rangefunc iterator itself.
1938// k includes an additional mechanism to for making the return happen
1939// what does k return?
1940func k() (hashS string) {
1941	_return := func(s string) { hashS = s }
1942
1943	defer func() { recover() }()
1944	for s := range terrify("k panic", once("k return")) {
1945		_return(s)
1946		return
1947	}
1948	return "k not reached"
1949}
1950
1951func m() (hashS string) {
1952	_return := func(s string) { hashS = s }
1953
1954	defer func() { recover() }()
1955	for s := range terrify("m panic", once("m return")) {
1956		defer _return(s)
1957		return s + ", but should be replaced in a defer"
1958	}
1959	return "m not reached"
1960}
1961
1962func n() string {
1963	defer func() { recover() }()
1964	for s := range terrify("n panic", once("n return")) {
1965		return s + func(s string) string {
1966			defer func() { recover() }()
1967			for s := range terrify("n closure panic", once(s)) {
1968				return s
1969			}
1970			return "n closure not reached"
1971		}(" and n closure return")
1972	}
1973	return "n not reached"
1974}
1975
1976type terrifyTestCase struct {
1977	f func() string
1978	e string
1979}
1980
1981func TestPanicReturns(t *testing.T) {
1982	tcs := []terrifyTestCase{
1983		{f, "f return"},
1984		{g, "g return"},
1985		{h, "h return"},
1986		{k, "k return"},
1987		{j, "j return"},
1988		{m, "m return"},
1989		{n, "n return and n closure return"},
1990	}
1991
1992	for _, tc := range tcs {
1993		got := tc.f()
1994		if got != tc.e {
1995			t.Errorf("Got %s expected %s", got, tc.e)
1996		} else {
1997			t.Logf("Got expected %s", got)
1998		}
1999	}
2000}
2001
2002// twice calls yield twice, the first time defer-recover-saving any panic,
2003// for re-panicking later if the second call to yield does not also panic.
2004// If the first call panicked, the second call ought to also panic because
2005// it was called after a panic-termination of the loop body.
2006func twice[T any](x, y T) Seq[T] {
2007	return func(yield func(T) bool) {
2008		var p any
2009		done := false
2010		func() {
2011			defer func() {
2012				p = recover()
2013			}()
2014			done = !yield(x)
2015		}()
2016		if done {
2017			return
2018		}
2019		yield(y)
2020		if p != nil {
2021			// do not swallow the panic
2022			panic(p)
2023		}
2024	}
2025}
2026
2027func TestRunBodyAfterPanic(t *testing.T) {
2028	defer func() {
2029		if r := recover(); r != nil {
2030			if matchError(r, RERR_PANIC) {
2031				t.Logf("Saw expected panic '%v'", r)
2032			} else {
2033				t.Errorf("Saw wrong panic '%v'", r)
2034			}
2035		} else {
2036			t.Errorf("Wanted to see a failure, result")
2037		}
2038	}()
2039	for x := range twice(0, 1) {
2040		if x == 0 {
2041			panic("x is zero")
2042		}
2043	}
2044}
2045
2046func TestRunBodyAfterPanicCheck(t *testing.T) {
2047	defer func() {
2048		if r := recover(); r != nil {
2049			if matchError(r, CERR_PANIC) {
2050				t.Logf("Saw expected panic '%v'", r)
2051			} else {
2052				t.Errorf("Saw wrong panic '%v'", r)
2053			}
2054		} else {
2055			t.Errorf("Wanted to see a failure, result")
2056		}
2057	}()
2058	for x := range Check(twice(0, 1)) {
2059		if x == 0 {
2060			panic("x is zero")
2061		}
2062	}
2063}
2064
2065func TestTwoLevelReturn(t *testing.T) {
2066	f := func() int {
2067		for a := range twice(0, 1) {
2068			for b := range twice(0, 2) {
2069				x := a + b
2070				t.Logf("x=%d", x)
2071				if x == 3 {
2072					return x
2073				}
2074			}
2075		}
2076		return -1
2077	}
2078	y := f()
2079	if y != 3 {
2080		t.Errorf("Expected y=3, got y=%d\n", y)
2081	}
2082}
2083
2084func TestTwoLevelReturnCheck(t *testing.T) {
2085	f := func() int {
2086		for a := range Check(twice(0, 1)) {
2087			for b := range Check(twice(0, 2)) {
2088				x := a + b
2089				t.Logf("a=%d, b=%d, x=%d", a, b, x)
2090				if x == 3 {
2091					return x
2092				}
2093			}
2094		}
2095		return -1
2096	}
2097	y := f()
2098	if y != 3 {
2099		t.Errorf("Expected y=3, got y=%d\n", y)
2100	}
2101}
2102
2103func Bug70035(s1, s2, s3 []string) string {
2104	var c1 string
2105	for v1 := range slices.Values(s1) {
2106		var c2 string
2107		for v2 := range slices.Values(s2) {
2108			var c3 string
2109			for v3 := range slices.Values(s3) {
2110				c3 = c3 + v3
2111			}
2112			c2 = c2 + v2 + c3
2113		}
2114		c1 = c1 + v1 + c2
2115	}
2116	return c1
2117}
2118
2119func Test70035(t *testing.T) {
2120	got := Bug70035([]string{"1", "2", "3"}, []string{"a", "b", "c"}, []string{"A", "B", "C"})
2121	want := "1aABCbABCcABC2aABCbABCcABC3aABCbABCcABC"
2122	if got != want {
2123		t.Errorf("got %v, want %v", got, want)
2124	}
2125}
2126