1// { dg-do run }
2
3extern "C" void abort ();
4
5void
6dblinit (double *p)
7{
8  *p = 2.0;
9}
10
11namespace NS
12{
13  template <int N>
14  struct U
15  {
16    void foo (U &, bool);
17    U ();
18  };
19  template <int N>
20  struct S
21  {
22    int s;
23    #pragma omp declare reduction (foo : U<0>, S : omp_out.foo (omp_in, false))
24    #pragma omp declare reduction (foo : int : omp_out += omp_in) \
25	initializer (omp_priv = N + 2)
26    #pragma omp declare reduction (foo : double : omp_out += omp_in) \
27	initializer (dblinit (&omp_priv))
28    void baz (int v)
29    {
30      S s;
31      int q = 0;
32      if (s.s != 6 || v != 0) abort ();
33      s.s = 20;
34      double d = 4.0;
35      #pragma omp parallel num_threads (4) reduction (foo : s, v, d) \
36	reduction (::NS::U<N>::operator + : q)
37      {
38	if (s.s != 6 || q != 0 || v != N + 2 || d != 2.0) abort ();
39	asm volatile ("" : "+m" (s.s), "+r" (q), "+r" (v));
40	s.s++; q++; v++;
41      }
42      if (s.s != 20 + q * 7 || (N + 3) * q != v || d != 4.0 + 2.0 * q)
43	abort ();
44    }
45    void foo (S &x) { s += x.s; }
46    void foo (S &x, bool y) { s += x.s; if (y) abort (); }
47    S (const S &x) { s = x.s + 1; }
48    S (const S &x, bool y) { s = x.s + 2; if (y) abort (); }
49    S () { s = 6; }
50    S (int x) { s = x; }
51    ~S ();
52  };
53  #pragma omp declare reduction (bar : S<1> : omp_out.foo (omp_in)) \
54	initializer (omp_priv (8))
55}
56
57template <int N>
58NS::S<N>::~S ()
59{
60  if (s < 6) abort ();
61  s = -1;
62  /* Ensure the above store is not DSEd.  */
63  asm volatile ("" : : "r" (&s) : "memory");
64}
65
66template <int N>
67struct T : public NS::S<N>
68{
69  void baz ()
70  {
71    NS::S<N> s;
72    int q = 0;
73    if (s.s != 6) abort ();
74    #pragma omp parallel num_threads (4) reduction (foo:s) \
75	reduction (+: q)
76    {
77      if (s.s != 6 || q != 0) abort ();
78      asm volatile ("" : "+m" (s.s), "+r" (q));
79      s.s += 2; q++;
80    }
81    if (s.s != 6 + q * 8) abort ();
82  }
83};
84
85struct W
86{
87  int v;
88  W () : v (6) {}
89  ~W () {}
90};
91
92template <typename T, typename D>
93struct V
94{
95  #pragma omp declare reduction (baz: T: omp_out.s += omp_in.s) \
96	initializer (omp_priv (11))
97  #pragma omp declare reduction (baz: D: omp_out += omp_in) \
98	initializer (dblinit (&omp_priv))
99  static void dblinit (D *x) { *x = 3.0; }
100  void baz ()
101  {
102    T t;
103    V v;
104    int q = 0;
105    D d = 4.0;
106    if (t.s != 6 || v.v != 4) abort ();
107    #pragma omp declare reduction (+ : V, W : omp_out.v -= omp_in.v) \
108	initializer (omp_priv (12))
109    {
110      #pragma omp declare reduction (+ : W, V : omp_out.v += omp_in.v) \
111	initializer (omp_priv (9))
112      #pragma omp parallel num_threads (4) reduction (+: v, q) \
113	reduction (baz: t, d)
114      {
115	if (t.s != 11 || v.v != 9 || q != 0 || d != 3.0) abort ();
116	asm volatile ("" : "+m" (t.s), "+m" (v.v), "+r" (q));
117	t.s += 2; v.v += 3; q++;
118      }
119      if (t.s != 6 + 13 * q || v.v != 4 + 12 * q || d != 4.0 + 3.0 * q)
120	abort ();
121    }
122  }
123  int v;
124  V () : v (4) {}
125  V (int x) : v (x) {}
126  ~V () {}
127};
128
129int
130main ()
131{
132  NS::S<0> u;
133  u.baz (0);
134  T<2> t;
135  t.baz ();
136  NS::S<1> s;
137  int q = 0;
138  if (s.s != 6) abort ();
139  // Test ADL
140  #pragma omp parallel num_threads (4) reduction (bar:s) reduction (+:q)
141  {
142    if (s.s != 8 || q != 0) abort ();
143    asm volatile ("" : "+m" (s.s), "+r" (q));
144    s.s += 4; q++;
145  }
146  if (s.s != 6 + q * 12) abort ();
147  V <NS::S <0>, double> v;
148  v.baz ();
149}
150