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