| // RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s | |
| // RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - -fexperimental-new-constant-interpreter | FileCheck %s | |
| // expected-no-diagnostics | |
| namespace std { | |
| template <typename T> struct tuple_size; | |
| template <int, typename> struct tuple_element; | |
| } // namespace std | |
| namespace Case1 { | |
| struct S { | |
| int a, b; | |
| bool flag = false; | |
| constexpr explicit operator bool() { | |
| flag = true; | |
| return a != b; | |
| } | |
| constexpr operator int() { | |
| flag = true; | |
| return a * b; | |
| } | |
| constexpr bool operator==(S rhs) const { | |
| return a == rhs.a && b == rhs.b; | |
| } | |
| template <int I> | |
| constexpr int& get() { | |
| if (!flag) | |
| return a = a + b; | |
| return I == 0 ? a : b; | |
| } | |
| }; | |
| } // namespace Case1 | |
| template <> struct std::tuple_size<Case1::S> { | |
| static const int value = 2; | |
| }; | |
| template <int I> struct std::tuple_element<I, Case1::S> { | |
| using type = int; | |
| }; | |
| namespace Case1 { | |
| void foo() { | |
| if (S s(1, 2); auto [a, b] = s) { | |
| __builtin_assume(a == 1); | |
| __builtin_assume(b == 2); | |
| } | |
| // CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call]], label {{.*}}, label {{.*}} | |
| if (auto [a, b] = S(1, 2)) { | |
| __builtin_assume(a == 1); | |
| __builtin_assume(b == 2); | |
| } | |
| // CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}} | |
| if (S s(3, 4); auto& [a, b] = s) { | |
| __builtin_assume(a == 3); | |
| __builtin_assume(b == 4); | |
| } | |
| // CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}} | |
| while (auto [i, j] = S(5, 6)) | |
| break; | |
| // CHECK: while.cond{{.*}}: | |
| // CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}} | |
| S s(7, 8); | |
| while (auto& [i, j] = s) | |
| break; | |
| // CHECK: while.cond{{.*}}: | |
| // CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}} | |
| for (int k = 0; auto [i, j] = S(24, 42); ++k) | |
| break; | |
| // CHECK: for.cond{{.*}}: | |
| // CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}} | |
| for (S s(114, 514); auto& [i, j] = s; ++i) | |
| break; | |
| // CHECK: for.cond{{.*}}: | |
| // CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}} | |
| switch (S s(10, 11); auto& [i, j] = s) { | |
| case 10 * 11: | |
| __builtin_assume(i == 10); | |
| __builtin_assume(j == 11); | |
| break; | |
| default: | |
| break; | |
| } | |
| // CHECK: %[[call8:.+]] = call {{.*}} i32 @_ZN5Case11ScviEv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv | |
| // CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv | |
| // CHECK: switch i32 %[[call8]], label {{.*}} | |
| } | |
| constexpr int bar(auto) { | |
| constexpr auto value = [] { | |
| if (S s(1, 2); auto [i, j] = s) | |
| return S(i, j); | |
| return S(0, 0); | |
| }(); | |
| static_assert(value == S(1, 2)); | |
| constexpr auto value2 = [] { | |
| if (auto [a, b] = S(1, 2)) | |
| return S(a, b); | |
| return S(0, 0); | |
| }(); | |
| static_assert(value2 == S(1, 2)); | |
| constexpr auto value3 = [] { | |
| if (auto&& [a, b] = S(3, 4)) | |
| return S(a, b); | |
| return S(0, 0); | |
| }(); | |
| static_assert(value3 == S(3, 4)); | |
| constexpr auto value4 = [] { | |
| S s(7, 8); | |
| int cnt = 0; | |
| while (auto& [i, j] = s) { | |
| s.flag = false; | |
| ++i, ++j; | |
| if (++cnt == 10) | |
| break; | |
| } | |
| return s; | |
| }(); | |
| static_assert(value4 == S(17, 18)); | |
| constexpr auto value5 = [] { | |
| S s(3, 4); | |
| for (int cnt = 0; auto& [x, y] = s; s.flag = false, ++cnt) { | |
| if (cnt == 3) | |
| break; | |
| ++x, ++y; | |
| } | |
| return s; | |
| }(); | |
| static_assert(value5 == S(6, 7)); | |
| constexpr auto value6 = [] { | |
| switch (auto [x, y] = S(3, 4)) { | |
| case 3 * 4: | |
| return S(x, y); | |
| default: | |
| return S(y, x); | |
| } | |
| }(); | |
| static_assert(value6 == S(3, 4)); | |
| return 42; | |
| } | |
| constexpr int value = bar(1); | |
| #if 0 | |
| // FIXME: This causes clang to ICE, though this is not a regression. | |
| constexpr int ice(auto) { | |
| if constexpr (S s(1, 2); auto [i, j] = s) { | |
| static_assert(i == 1); | |
| } | |
| return 42; | |
| } | |
| constexpr int value2 = ice(1); | |
| #endif | |
| } // namespace Case1 |