aboutsummaryrefslogtreecommitdiff
path: root/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
blob: 1ac821002f95c746ca31a53fd32642d52d7c4880 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// RUN: %clang_cc1 -std=c++1z -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s

template<typename ...T> constexpr auto sum(T ...t) { return (... + t); }
template<typename ...T> constexpr auto product(T ...t) { return (t * ...); }
template<typename ...T> constexpr auto all(T ...t) { return (true && ... && t); }
template<typename ...T> constexpr auto dumb(T ...t) { return (false && ... && t); }

static_assert(sum(1, 2, 3, 4, 5) == 15);
static_assert(product(1, 2, 3, 4, 5) == 120);
static_assert(!all(true, true, false, true, false));
static_assert(all(true, true, true, true, true));
static_assert(!dumb(true, true, true, true, true));

struct S {
  int a, b, c, d, e;
};
template<typename ...T> constexpr auto increment_all(T &...t) {
  (++t, ...);
}
constexpr bool check() {
  S s = { 1, 2, 3, 4, 5 };
  increment_all(s.a, s.b, s.c, s.d, s.e);
  return s.a == 2 && s.b == 3 && s.c == 4 && s.d == 5 && s.e == 6;
}
static_assert(check());

template<int ...N> void empty() {
  static_assert((N || ...) == false);
  static_assert((N && ...) == true);
  (N, ...);
}
template void empty<>();

// An empty fold-expression isn't a null pointer just because it's an integer
// with value 0. (This is no longer an issue since empty pack expansions don't
// produce integers any more.)
template<int ...N> void null_ptr() {
  void *p = (N || ...); // expected-error {{rvalue of type 'bool'}}
  void *q = (N , ...); // expected-error {{rvalue of type 'void'}}
}
template void null_ptr<>(); // expected-note {{in instantiation of}}

template<int ...N> void bad_empty() {
  (N + ...); // expected-error {{empty expansion for operator '+' with no fallback}}
  (N * ...); // expected-error {{empty expansion for operator '*' with no fallback}}
  (N | ...); // expected-error {{empty expansion for operator '|' with no fallback}}
  (N & ...); // expected-error {{empty expansion for operator '&' with no fallback}}
  (N - ...); // expected-error {{empty expansion for operator '-' with no fallback}}
  (N / ...); // expected-error {{empty expansion for operator '/' with no fallback}}
  (N % ...); // expected-error {{empty expansion for operator '%' with no fallback}}
  (N = ...); // expected-error {{empty expansion for operator '=' with no fallback}}
}
template void bad_empty<>(); // expected-note {{in instantiation of}}

template<int ...N> void empty_with_base() {
  extern int k;
  (k = ... = N); // expected-warning{{unused}}

  void (k = ... = N); // expected-error {{expected ')'}} expected-note {{to match}}
  void ((k = ... = N));
  (void) (k = ... = N);
}
template void empty_with_base<>(); // expected-note {{in instantiation of}}
template void empty_with_base<1>();

struct A {
  struct B {
    struct C {
      struct D {
        int e;
      } d;
    } c;
  } b;
} a;
template<typename T, typename ...Ts> constexpr decltype(auto) apply(T &t, Ts ...ts) {
  return (t.*....*ts);
}
static_assert(&apply(a, &A::b, &A::B::c, &A::B::C::d, &A::B::C::D::e) == &a.b.c.d.e);

#if __cplusplus > 201703L
// The <=> operator is unique among binary operators in not being a
// fold-operator.
// FIXME: This diagnostic is not great.
template<typename ...T> constexpr auto spaceship1(T ...t) { return (t <=> ...); } // expected-error {{expected expression}}
template<typename ...T> constexpr auto spaceship2(T ...t) { return (... <=> t); } // expected-error {{expected expression}}
template<typename ...T> constexpr auto spaceship3(T ...t) { return (t <=> ... <=> 0); } // expected-error {{expected expression}}
#endif

// The GNU binary conditional operator ?: is not recognized as a fold-operator.
// FIXME: Why not? This seems like it would be useful.
template<typename ...T> constexpr auto binary_conditional1(T ...t) { return (t ?: ...); } // expected-error {{expected expression}}
template<typename ...T> constexpr auto binary_conditional2(T ...t) { return (... ?: t); } // expected-error {{expected expression}}
template<typename ...T> constexpr auto binary_conditional3(T ...t) { return (t ?: ... ?: 0); } // expected-error {{expected expression}}

namespace PR41845 {
  template <int I> struct Constant {};

  template <int... Is> struct Sum {
    template <int... Js> using type = Constant<((Is + Js) + ... + 0)>; // expected-error {{pack expansion contains parameter pack 'Js' that has a different length (1 vs. 2) from outer parameter packs}}
  };

  Sum<1>::type<1, 2> x; // expected-note {{instantiation of}}
}

namespace PR30738 {
  namespace N {
    struct S {};
  }

  namespace T {
    void operator+(N::S, N::S) {}
    template<typename ...Ts> void f() { (Ts{} + ...); }
  }

  void g() { T::f<N::S, N::S>(); }

  template<typename T, typename ...U> auto h(U ...v) {
    T operator+(T, T); // expected-note {{candidate}}
    return (v + ...); // expected-error {{invalid operands}}
  }
  int test_h1 = h<N::S>(1, 2, 3);
  N::S test_h2 = h<N::S>(N::S(), N::S(), N::S());
  int test_h3 = h<struct X>(1, 2, 3);
  N::S test_h4 = h<struct X>(N::S(), N::S(), N::S()); // expected-note {{instantiation of}}
}

namespace GH67395 {
template <typename>
bool f();

template <typename... T>
void g(bool = (f<T>() || ...));
}


namespace comparison_warning {
  struct S {
    bool operator<(const S&) const;
    bool operator<(int) const;
    bool operator==(const S&) const;
  };

  template <typename...T>
  void f(T... ts) {
    (void)(ts == ...);
    // expected-error@-1 2{{comparison in fold expression would evaluate to '(X == Y) == Z'}}
    (void)(ts < ...);
    // expected-error@-1 2{{comparison in fold expression would evaluate to '(X < Y) < Z'}}
    (void)(... < ts);
    // expected-error@-1 2{{comparison in fold expression would evaluate to '(X < Y) < Z'}}
  }

  void test() {
    f(0, 1, 2); // expected-note{{in instantiation}}
    f(0, 1); // expected-note{{in instantiation}}
    f(S{}, S{});
    f(0);
  }

};