|
20 | 20 | #include <catch2/catch.hpp> |
21 | 21 | #include <stdexec/execution.hpp> |
22 | 22 |
|
| 23 | +#include <stdexcept> |
23 | 24 | #include <type_traits> |
24 | 25 |
|
25 | 26 | namespace ex = STDEXEC; |
@@ -140,13 +141,13 @@ namespace |
140 | 141 | awaitable_ref(Awaitable&) -> awaitable_ref<Awaitable>; |
141 | 142 |
|
142 | 143 | template <class Awaitable> |
143 | | - requires requires(Awaitable& a) { |
144 | | - { operator co_await(a) }; |
| 144 | + requires requires(Awaitable&& a) { |
| 145 | + { operator co_await(std::move(a)) }; |
145 | 146 | } |
146 | 147 | constexpr auto operator co_await(awaitable_ref<Awaitable> ref) |
147 | | - noexcept(noexcept(operator co_await(*ref.awaitable_))) |
| 148 | + noexcept(noexcept(operator co_await(std::move(*ref.awaitable_)))) |
148 | 149 | { |
149 | | - return operator co_await(*ref.awaitable_); |
| 150 | + return operator co_await(std::move(*ref.awaitable_)); |
150 | 151 | } |
151 | 152 |
|
152 | 153 | template <class T> |
@@ -487,4 +488,224 @@ namespace |
487 | 488 | with_as_awaitable<with_friend_co_await<symmetrically_suspending_awaitable<void>>>(false)); |
488 | 489 | } |
489 | 490 | } |
| 491 | + |
| 492 | + TEST_CASE("exceptions thrown from await_ready are reported to set_error", |
| 493 | + "[cpo][cpo_connect_awaitable]") |
| 494 | + { |
| 495 | + struct throw_on_ready : ready_awaitable<void> |
| 496 | + { |
| 497 | + static bool await_ready() |
| 498 | + { |
| 499 | + throw std::runtime_error("not ready!"); |
| 500 | + } |
| 501 | + }; |
| 502 | + |
| 503 | + auto op = ex::connect(throw_on_ready{}, expect_error_receiver{}); |
| 504 | + op.start(); |
| 505 | + } |
| 506 | + |
| 507 | + TEST_CASE("exceptions thrown from void-returning await_suspend are reported to set_error", |
| 508 | + "[cpo][cpo_connect_awaitable]") |
| 509 | + { |
| 510 | + struct throw_on_suspend : suspending_awaitable<void> |
| 511 | + { |
| 512 | + static void await_suspend(std::coroutine_handle<>) |
| 513 | + { |
| 514 | + throw std::runtime_error("do not suspend!"); |
| 515 | + } |
| 516 | + } awaiter; |
| 517 | + |
| 518 | + auto op = ex::connect(awaitable_ref{awaiter}, expect_error_receiver{}); |
| 519 | + op.start(); |
| 520 | + } |
| 521 | + |
| 522 | + TEST_CASE("exceptions thrown from bool-returning await_suspend are reported to set_error", |
| 523 | + "[cpo][cpo_connect_awaitable]") |
| 524 | + { |
| 525 | + struct throw_on_suspend : suspending_awaitable<void> |
| 526 | + { |
| 527 | + static bool await_suspend(std::coroutine_handle<>) |
| 528 | + { |
| 529 | + throw std::runtime_error("do not suspend!"); |
| 530 | + } |
| 531 | + } awaiter; |
| 532 | + |
| 533 | + auto op = ex::connect(awaitable_ref{awaiter}, expect_error_receiver{}); |
| 534 | + op.start(); |
| 535 | + } |
| 536 | + |
| 537 | + TEST_CASE("exceptions thrown from handle-returning await_suspend are reported to set_error", |
| 538 | + "[cpo][cpo_connect_awaitable]") |
| 539 | + { |
| 540 | + struct throw_on_suspend : suspending_awaitable<void> |
| 541 | + { |
| 542 | + static std::coroutine_handle<> await_suspend(std::coroutine_handle<>) |
| 543 | + { |
| 544 | + throw std::runtime_error("do not suspend!"); |
| 545 | + } |
| 546 | + } awaiter; |
| 547 | + |
| 548 | + auto op = ex::connect(awaitable_ref{awaiter}, expect_error_receiver{}); |
| 549 | + op.start(); |
| 550 | + } |
| 551 | + |
| 552 | + TEST_CASE("exceptions thrown from immediately-invoked await_resume are reported to set_error", |
| 553 | + "[cpo][cpo_connect_awaitable]") |
| 554 | + { |
| 555 | + { |
| 556 | + struct throw_on_void_resume : ready_awaitable<void> |
| 557 | + { |
| 558 | + static void await_resume() |
| 559 | + { |
| 560 | + throw std::runtime_error("no result for you!"); |
| 561 | + } |
| 562 | + }; |
| 563 | + |
| 564 | + auto op = ex::connect(throw_on_void_resume{}, expect_error_receiver{}); |
| 565 | + op.start(); |
| 566 | + } |
| 567 | + { |
| 568 | + struct throw_on_int_resume : ready_awaitable<void> |
| 569 | + { |
| 570 | + static int await_resume() |
| 571 | + { |
| 572 | + throw std::runtime_error("no result for you!"); |
| 573 | + } |
| 574 | + }; |
| 575 | + |
| 576 | + auto op = ex::connect(throw_on_int_resume{}, expect_error_receiver{}); |
| 577 | + op.start(); |
| 578 | + } |
| 579 | + } |
| 580 | + |
| 581 | + TEST_CASE("exceptions thrown from deferred-invoked await_resume are reported to set_error", |
| 582 | + "[cpo][cpo_connect_awaitable]") |
| 583 | + { |
| 584 | + { |
| 585 | + { |
| 586 | + struct throw_on_void_resume : suspending_awaitable<void> |
| 587 | + { |
| 588 | + static void await_resume() |
| 589 | + { |
| 590 | + throw std::runtime_error("no result for you!"); |
| 591 | + } |
| 592 | + } awaitable; |
| 593 | + |
| 594 | + auto op = ex::connect(awaitable_ref{awaitable}, expect_error_receiver{}); |
| 595 | + op.start(); |
| 596 | + awaitable.resume_parent(); |
| 597 | + } |
| 598 | + { |
| 599 | + struct throw_on_int_resume : suspending_awaitable<void> |
| 600 | + { |
| 601 | + static int await_resume() |
| 602 | + { |
| 603 | + throw std::runtime_error("no result for you!"); |
| 604 | + } |
| 605 | + } awaitable; |
| 606 | + |
| 607 | + auto op = ex::connect(awaitable_ref{awaitable}, expect_error_receiver{}); |
| 608 | + op.start(); |
| 609 | + awaitable.resume_parent(); |
| 610 | + } |
| 611 | + } |
| 612 | + } |
| 613 | + |
| 614 | + template <template <class> class Wrapper = std::type_identity_t> |
| 615 | + struct throw_on_get_awaitable |
| 616 | + { |
| 617 | + template <class Promise> |
| 618 | + Wrapper<ready_awaitable<void>> as_awaitable(Promise&) |
| 619 | + { |
| 620 | + throw std::runtime_error("no awaitable for you!"); |
| 621 | + } |
| 622 | + }; |
| 623 | + |
| 624 | + TEST_CASE("exceptions thrown from __get_awaitable are reported to set_error", |
| 625 | + "[cpo][cpo_connect_awaitable]") |
| 626 | + { |
| 627 | + { |
| 628 | + auto op = ex::connect(throw_on_get_awaitable{}, expect_error_receiver{}); |
| 629 | + op.start(); |
| 630 | + } |
| 631 | + |
| 632 | + { |
| 633 | + auto op = ex::connect(throw_on_get_awaitable<with_member_co_await>{}, |
| 634 | + expect_error_receiver{}); |
| 635 | + op.start(); |
| 636 | + } |
| 637 | + |
| 638 | + { |
| 639 | + auto op = ex::connect(throw_on_get_awaitable<with_friend_co_await>{}, |
| 640 | + expect_error_receiver{}); |
| 641 | + op.start(); |
| 642 | + } |
| 643 | + } |
| 644 | + |
| 645 | + TEST_CASE("exceptions thrown from member operator co_await are reported to set_error", |
| 646 | + "[cpo][cpo_connect_awaitable]") |
| 647 | + { |
| 648 | + struct throw_on_co_await |
| 649 | + { |
| 650 | + ready_awaitable<void> operator co_await() |
| 651 | + { |
| 652 | + throw std::runtime_error("no awaitable for you!"); |
| 653 | + } |
| 654 | + }; |
| 655 | + |
| 656 | + { |
| 657 | + auto op = ex::connect(throw_on_co_await{}, expect_error_receiver{}); |
| 658 | + op.start(); |
| 659 | + } |
| 660 | + |
| 661 | + { |
| 662 | + auto op = ex::connect(with_as_awaitable<throw_on_co_await>{}, expect_error_receiver{}); |
| 663 | + op.start(); |
| 664 | + } |
| 665 | + } |
| 666 | + |
| 667 | + struct throw_on_co_await |
| 668 | + { |
| 669 | + friend ready_awaitable<void> operator co_await(throw_on_co_await&&) |
| 670 | + { |
| 671 | + throw std::runtime_error("no awaitable for you!"); |
| 672 | + } |
| 673 | + }; |
| 674 | + |
| 675 | + TEST_CASE("exceptions thrown from friend operator co_await are reported to set_error", |
| 676 | + "[cpo][cpo_connect_awaitable]") |
| 677 | + { |
| 678 | + { |
| 679 | + auto op = ex::connect(throw_on_co_await{}, expect_error_receiver{}); |
| 680 | + op.start(); |
| 681 | + } |
| 682 | + |
| 683 | + { |
| 684 | + auto op = ex::connect(with_as_awaitable<throw_on_co_await>{}, expect_error_receiver{}); |
| 685 | + op.start(); |
| 686 | + } |
| 687 | + } |
| 688 | + |
| 689 | + struct stop_on_suspend |
| 690 | + { |
| 691 | + static constexpr bool await_ready() noexcept |
| 692 | + { |
| 693 | + return false; |
| 694 | + } |
| 695 | + |
| 696 | + template <class Promise> |
| 697 | + static constexpr auto |
| 698 | + await_suspend(std::coroutine_handle<Promise> coro) noexcept -> std::coroutine_handle<> |
| 699 | + { |
| 700 | + return coro.promise().unhandled_stopped(); |
| 701 | + } |
| 702 | + |
| 703 | + static constexpr void await_resume() noexcept {} |
| 704 | + }; |
| 705 | + |
| 706 | + TEST_CASE("promise().unhandled_stopped() invokes set_stopped", "[cpo][cpo_connect_awaitable]") |
| 707 | + { |
| 708 | + auto op = ex::connect(stop_on_suspend{}, expect_stopped_receiver{}); |
| 709 | + op.start(); |
| 710 | + } |
490 | 711 | } // namespace |
0 commit comments