1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
10 | // UNSUPPORTED: no-exceptions |
11 | |
12 | // If the invocation of any non-const member function of `iterator` exits via an |
13 | // exception, the iterator acquires a singular value. |
14 | |
15 | #include <ranges> |
16 | |
17 | #include <tuple> |
18 | |
19 | #include "../types.h" |
20 | |
21 | struct ThrowOnIncrementIterator { |
22 | int* it_; |
23 | |
24 | using value_type = int; |
25 | using difference_type = std::intptr_t; |
26 | using iterator_concept = std::input_iterator_tag; |
27 | |
28 | ThrowOnIncrementIterator() = default; |
29 | explicit ThrowOnIncrementIterator(int* it) : it_(it) {} |
30 | |
31 | ThrowOnIncrementIterator& operator++() { |
32 | ++it_; |
33 | throw 5; |
34 | return *this; |
35 | } |
36 | void operator++(int) { ++it_; } |
37 | |
38 | int& operator*() const { return *it_; } |
39 | |
40 | friend bool operator==(ThrowOnIncrementIterator const&, ThrowOnIncrementIterator const&) = default; |
41 | }; |
42 | |
43 | struct ThrowOnIncrementView : IntBufferView { |
44 | ThrowOnIncrementIterator begin() const { return ThrowOnIncrementIterator{buffer_}; } |
45 | ThrowOnIncrementIterator end() const { return ThrowOnIncrementIterator{buffer_ + size_}; } |
46 | }; |
47 | |
48 | // Cannot run the test at compile time because it is not allowed to throw exceptions |
49 | void test() { |
50 | int buffer[] = {1, 2, 3}; |
51 | { |
52 | // zip iterator should be able to be destroyed after member function throws |
53 | std::ranges::zip_view v{ThrowOnIncrementView{buffer}}; |
54 | auto it = v.begin(); |
55 | try { |
56 | ++it; |
57 | assert(false); // should not be reached as the above expression should throw. |
58 | } catch (int e) { |
59 | assert(e == 5); |
60 | } |
61 | } |
62 | |
63 | { |
64 | // zip iterator should be able to be assigned after member function throws |
65 | std::ranges::zip_view v{ThrowOnIncrementView{buffer}}; |
66 | auto it = v.begin(); |
67 | try { |
68 | ++it; |
69 | assert(false); // should not be reached as the above expression should throw. |
70 | } catch (int e) { |
71 | assert(e == 5); |
72 | } |
73 | it = v.begin(); |
74 | auto [x] = *it; |
75 | assert(x == 1); |
76 | } |
77 | } |
78 | |
79 | int main(int, char**) { |
80 | test(); |
81 | |
82 | return 0; |
83 | } |
84 | |