xref: /aosp_15_r20/external/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li // RUN: %clang_cc1 -std=c++98 -verify -fsyntax-only %s -Wno-c++11-extensions -Wno-c++1y-extensions -DPRECXX11
2*67e74705SXin Li // RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -Wno-c++1y-extensions %s
3*67e74705SXin Li // RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only %s -DCPP1Y
4*67e74705SXin Li 
5*67e74705SXin Li #define CONST const
6*67e74705SXin Li 
7*67e74705SXin Li #ifdef PRECXX11
8*67e74705SXin Li #define static_assert(expr, msg) typedef int static_assert[(expr) ? 1 : -1];
9*67e74705SXin Li #endif
10*67e74705SXin Li 
11*67e74705SXin Li class A {
12*67e74705SXin Li   template<typename T> CONST T wrong;           // expected-error {{member 'wrong' declared as a template}}
13*67e74705SXin Li   template<typename T> CONST T wrong_init = 5;      // expected-error {{member 'wrong_init' declared as a template}}
14*67e74705SXin Li   template<typename T, typename T0> static CONST T right = T(100);
15*67e74705SXin Li   template<typename T> static CONST T right<T,int> = 5;
16*67e74705SXin Li   template<typename T> CONST int right<int,T>;  // expected-error {{member 'right' declared as a template}}
17*67e74705SXin Li   template<typename T> CONST float right<float,T> = 5;  // expected-error {{member 'right' declared as a template}}
18*67e74705SXin Li   template<> static CONST int right<int,int> = 7;       // expected-error {{explicit specialization of 'right' in class scope}}
19*67e74705SXin Li   template<> static CONST float right<float,int>;       // expected-error {{explicit specialization of 'right' in class scope}}
20*67e74705SXin Li   template static CONST int right<int,int>;     // expected-error {{template specialization requires 'template<>'}} \
21*67e74705SXin Li                                                 // expected-error {{explicit specialization of 'right' in class scope}}
22*67e74705SXin Li };
23*67e74705SXin Li 
24*67e74705SXin Li namespace out_of_line {
25*67e74705SXin Li   class B0 {
26*67e74705SXin Li     template<typename T, typename T0> static CONST T right = T(100);
27*67e74705SXin Li     template<typename T> static CONST T right<T,int> = T(5);
28*67e74705SXin Li   };
29*67e74705SXin Li   template<> CONST int B0::right<int,int> = 7;
30*67e74705SXin Li   template CONST int B0::right<int,int>;
31*67e74705SXin Li   template<> CONST int B0::right<int,float>;
32*67e74705SXin Li   template CONST int B0::right<int,float>;
33*67e74705SXin Li 
34*67e74705SXin Li   class B1 {
35*67e74705SXin Li     template<typename T, typename T0> static CONST T right;
36*67e74705SXin Li     template<typename T> static CONST T right<T,int>;
37*67e74705SXin Li   };
38*67e74705SXin Li   template<typename T, typename T0> CONST T B1::right = T(100);
39*67e74705SXin Li   template<typename T> CONST T B1::right<T,int> = T(5);
40*67e74705SXin Li 
41*67e74705SXin Li   class B2 {
42*67e74705SXin Li     template<typename T, typename T0> static CONST T right = T(100);  // expected-note {{previous initialization is here}}
43*67e74705SXin Li     template<typename T> static CONST T right<T,int> = T(5);          // expected-note {{previous initialization is here}}
44*67e74705SXin Li   };
45*67e74705SXin Li   template<typename T, typename T0> CONST T B2::right = T(100);   // expected-error {{static data member 'right' already has an initializer}}
46*67e74705SXin Li   template<typename T> CONST T B2::right<T,int> = T(5);           // expected-error {{static data member 'right' already has an initializer}}
47*67e74705SXin Li 
48*67e74705SXin Li   class B3 {
49*67e74705SXin Li     template<typename T, typename T0> static CONST T right = T(100);
50*67e74705SXin Li     template<typename T> static CONST T right<T,int> = T(5);
51*67e74705SXin Li   };
52*67e74705SXin Li   template<typename T, typename T0> CONST T B3::right;
53*67e74705SXin Li   template<typename T> CONST T B3::right<T,int>;
54*67e74705SXin Li 
55*67e74705SXin Li   class B4 {
56*67e74705SXin Li     template<typename T, typename T0> static CONST T a;
57*67e74705SXin Li     template<typename T> static CONST T a<T,int> = T(100);
58*67e74705SXin Li     template<typename T, typename T0> static CONST T b = T(100);
59*67e74705SXin Li     template<typename T> static CONST T b<T,int>;
60*67e74705SXin Li   };
61*67e74705SXin Li   template<typename T, typename T0> CONST T B4::a; // expected-error {{default initialization of an object of const type 'const int'}}
62*67e74705SXin Li   template<typename T> CONST T B4::a<T,int>;
63*67e74705SXin Li   template CONST int B4::a<int,char>; // expected-note {{in instantiation of}}
64*67e74705SXin Li   template CONST int B4::a<int,int>;
65*67e74705SXin Li 
66*67e74705SXin Li   template<typename T, typename T0> CONST T B4::b;
67*67e74705SXin Li   template<typename T> CONST T B4::b<T,int>; // expected-error {{default initialization of an object of const type 'const int'}}
68*67e74705SXin Li   template CONST int B4::b<int,char>;
69*67e74705SXin Li   template CONST int B4::b<int,int>; // expected-note {{in instantiation of}}
70*67e74705SXin Li }
71*67e74705SXin Li 
72*67e74705SXin Li namespace non_const_init {
73*67e74705SXin Li   class A {
74*67e74705SXin Li     template<typename T> static T wrong_inst_undefined = T(10); // expected-note {{refers here}}
75*67e74705SXin Li     template<typename T> static T wrong_inst_defined = T(10); // expected-error {{non-const static data member must be initialized out of line}}
76*67e74705SXin Li     template<typename T> static T wrong_inst_out_of_line;
77*67e74705SXin Li   };
78*67e74705SXin Li 
79*67e74705SXin Li   template const int A::wrong_inst_undefined<const int>; // expected-error {{undefined}}
80*67e74705SXin Li 
81*67e74705SXin Li   template<typename T> T A::wrong_inst_defined;
82*67e74705SXin Li   template const int A::wrong_inst_defined<const int>;
83*67e74705SXin Li   template int A::wrong_inst_defined<int>; // expected-note {{in instantiation of static data member 'non_const_init::A::wrong_inst_defined<int>' requested here}}
84*67e74705SXin Li 
85*67e74705SXin Li   template<typename T> T A::wrong_inst_out_of_line = T(10);
86*67e74705SXin Li   template int A::wrong_inst_out_of_line<int>;
87*67e74705SXin Li 
88*67e74705SXin Li   class B {
89*67e74705SXin Li     template<typename T> static T wrong_inst; // expected-note {{refers here}}
90*67e74705SXin Li     template<typename T> static T wrong_inst<T*> = T(100); // expected-error {{non-const static data member must be initialized out of line}} expected-note {{refers here}}
91*67e74705SXin Li 
92*67e74705SXin Li     template<typename T> static T wrong_inst_fixed;
93*67e74705SXin Li     template<typename T> static T wrong_inst_fixed<T*>;
94*67e74705SXin Li   };
95*67e74705SXin Li   template int B::wrong_inst<int>; // expected-error {{undefined}}
96*67e74705SXin Li   // FIXME: It'd be better to produce the 'explicit instantiation of undefined
97*67e74705SXin Li   // template' diagnostic here, not the 'must be initialized out of line'
98*67e74705SXin Li   // diagnostic.
99*67e74705SXin Li   template int B::wrong_inst<int*>; // expected-note {{in instantiation of static data member 'non_const_init::B::wrong_inst<int *>' requested here}}
100*67e74705SXin Li   template const int B::wrong_inst<const int*>; // expected-error {{undefined}}
101*67e74705SXin Li   template<typename T> T B::wrong_inst_fixed = T(100);
102*67e74705SXin Li   template int B::wrong_inst_fixed<int>;
103*67e74705SXin Li 
104*67e74705SXin Li   class C {
105*67e74705SXin Li     template<typename T> static CONST T right_inst = T(10); // expected-note {{here}}
106*67e74705SXin Li     template<typename T> static CONST T right_inst<T*> = T(100); // expected-note {{here}}
107*67e74705SXin Li   };
108*67e74705SXin Li   template CONST int C::right_inst<int>; // expected-error {{undefined variable template}}
109*67e74705SXin Li   template CONST int C::right_inst<int*>; // expected-error {{undefined variable template}}
110*67e74705SXin Li 
111*67e74705SXin Li   namespace pointers {
112*67e74705SXin Li 
113*67e74705SXin Li     struct C0 {
114*67e74705SXin Li       template<typename U> static U Data;
115*67e74705SXin Li       template<typename U> static CONST U Data<U*> = U(); // expected-note {{here}}
116*67e74705SXin Li 
117*67e74705SXin Li       template<typename U> static U Data2;
118*67e74705SXin Li       template<typename U> static CONST U Data2<U*> = U();
119*67e74705SXin Li     };
120*67e74705SXin Li     const int c0_test = C0::Data<int*>;
121*67e74705SXin Li     static_assert(c0_test == 0, "");
122*67e74705SXin Li     template const int C0::Data<int*>; // expected-error {{undefined}}
123*67e74705SXin Li 
124*67e74705SXin Li     template<typename U> const U C0::Data2<U*>;
125*67e74705SXin Li     template const int C0::Data2<int*>;
126*67e74705SXin Li 
127*67e74705SXin Li     struct C1a {
128*67e74705SXin Li       template<typename U> static U Data;
129*67e74705SXin Li       template<typename U> static U* Data<U*>;   // Okay, with out-of-line definition
130*67e74705SXin Li     };
131*67e74705SXin Li     template<typename T> T* C1a::Data<T*> = new T();
132*67e74705SXin Li     template int* C1a::Data<int*>;
133*67e74705SXin Li 
134*67e74705SXin Li     struct C1b {
135*67e74705SXin Li       template<typename U> static U Data;
136*67e74705SXin Li       template<typename U> static CONST U* Data<U*>;   // Okay, with out-of-line definition
137*67e74705SXin Li     };
138*67e74705SXin Li     template<typename T> CONST T* C1b::Data<T*> = (T*)(0);
139*67e74705SXin Li     template CONST int* C1b::Data<int*>;
140*67e74705SXin Li 
141*67e74705SXin Li     struct C2a {
142*67e74705SXin Li       template<typename U> static int Data;
143*67e74705SXin Li       template<typename U> static U* Data<U*> = new U();   // expected-error {{non-const static data member must be initialized out of line}}
144*67e74705SXin Li     };
145*67e74705SXin Li     template int* C2a::Data<int*>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2a::Data<int *>' requested here}}
146*67e74705SXin Li 
147*67e74705SXin Li     struct C2b {
148*67e74705SXin Li       template<typename U> static int Data;
149*67e74705SXin Li       template<typename U> static U *const Data<U*> = (U*)(0); // expected-error {{static data member of type 'int *const'}}
150*67e74705SXin Li     };
151*67e74705SXin Li     template<typename U> U *const C2b::Data<U*>;
152*67e74705SXin Li     template int *const C2b::Data<int*>; // expected-note {{in instantiation of static data member 'non_const_init::pointers::C2b::Data<int *>' requested here}}
153*67e74705SXin Li   }
154*67e74705SXin Li }
155*67e74705SXin Li 
156*67e74705SXin Li #ifndef PRECXX11
157*67e74705SXin Li namespace constexpred {
158*67e74705SXin Li   class A {
159*67e74705SXin Li     template<typename T> constexpr T wrong;           // expected-error {{member 'wrong' declared as a template}} \
160*67e74705SXin Li                                                       // expected-error {{non-static data member cannot be constexpr; did you intend to make it const?}}
161*67e74705SXin Li     template<typename T> constexpr T wrong_init = 5;      // expected-error {{non-static data member cannot be constexpr; did you intend to make it static?}}
162*67e74705SXin Li     template<typename T, typename T0> static constexpr T right = T(100);
163*67e74705SXin Li     template<typename T> static constexpr T right<T,int> = 5;
164*67e74705SXin Li     template<typename T> constexpr int right<int,T>;  // expected-error {{member 'right' declared as a template}} \
165*67e74705SXin Li                                                       // expected-error {{non-static data member cannot be constexpr; did you intend to make it const?}}
166*67e74705SXin Li     template<typename T> constexpr float right<float,T> = 5;  // expected-error {{non-static data member cannot be constexpr; did you intend to make it static?}}
167*67e74705SXin Li     template<> static constexpr int right<int,int> = 7;       // expected-error {{explicit specialization of 'right' in class scope}}
168*67e74705SXin Li     template<> static constexpr float right<float,int>;       // expected-error {{explicit specialization of 'right' in class scope}}
169*67e74705SXin Li     template static constexpr int right<int,int>;     // expected-error {{template specialization requires 'template<>'}} \
170*67e74705SXin Li                                                   // expected-error {{explicit specialization of 'right' in class scope}}
171*67e74705SXin Li   };
172*67e74705SXin Li }
173*67e74705SXin Li #endif
174*67e74705SXin Li 
175*67e74705SXin Li namespace in_class_template {
176*67e74705SXin Li 
177*67e74705SXin Li   template<typename T>
178*67e74705SXin Li   class D0 {
179*67e74705SXin Li     template<typename U> static U Data; // expected-note {{here}}
180*67e74705SXin Li     template<typename U> static CONST U Data<U*> = U();
181*67e74705SXin Li   };
182*67e74705SXin Li   template CONST int D0<float>::Data<int*>;
183*67e74705SXin Li   template int D0<float>::Data<int>; // expected-error {{undefined}}
184*67e74705SXin Li   template<typename T> template<typename U> const U D0<T>::Data<U*>;
185*67e74705SXin Li 
186*67e74705SXin Li   template<typename T>
187*67e74705SXin Li   class D1 {
188*67e74705SXin Li     template<typename U> static U Data;
189*67e74705SXin Li     template<typename U> static U* Data<U*>;
190*67e74705SXin Li   };
191*67e74705SXin Li   template<typename T>
192*67e74705SXin Li   template<typename U> U* D1<T>::Data<U*> = (U*)(0);
193*67e74705SXin Li   template int* D1<float>::Data<int*>; // expected-note {{previous}}
194*67e74705SXin Li   template int* D1<float>::Data<int*>; // expected-error {{duplicate explicit instantiation}}
195*67e74705SXin Li 
196*67e74705SXin Li   template<typename T>
197*67e74705SXin Li   class D2 {
198*67e74705SXin Li     template<typename U> static U Data;
199*67e74705SXin Li     template<typename U> static U* Data<U*>;
200*67e74705SXin Li   };
201*67e74705SXin Li   template<>
202*67e74705SXin Li   template<typename U> U* D2<float>::Data<U*> = (U*)(0) + 1;
203*67e74705SXin Li   template int* D2<float>::Data<int*>; // expected-note {{previous}}
204*67e74705SXin Li   template int* D2<float>::Data<int*>; // expected-error {{duplicate explicit instantiation}}
205*67e74705SXin Li 
206*67e74705SXin Li   template<typename T>
207*67e74705SXin Li   struct D3 {
208*67e74705SXin Li     template<typename U> static CONST U Data = U(100); // expected-note {{here}}
209*67e74705SXin Li   };
210*67e74705SXin Li   static_assert(D3<float>::Data<int> == 100, "");
211*67e74705SXin Li   template const char D3<float>::Data<char>; // expected-error {{undefined}}
212*67e74705SXin Li 
213*67e74705SXin Li   namespace bug_files {
214*67e74705SXin Li     template<typename T>
215*67e74705SXin Li     class D0a {
216*67e74705SXin Li       template<typename U> static U Data;
217*67e74705SXin Li       template<typename U> static CONST U Data<U*> = U(10);  // expected-note {{previous declaration is here}}
218*67e74705SXin Li     };
219*67e74705SXin Li     template<>
220*67e74705SXin Li     template<typename U> U D0a<float>::Data<U*> = U(100);  // expected-error {{redefinition of 'Data'}}
221*67e74705SXin Li 
222*67e74705SXin Li     // FIXME: We should accept this, and the corresponding case for class
223*67e74705SXin Li     // templates.
224*67e74705SXin Li     //
225*67e74705SXin Li     // [temp.class.spec.mfunc]/2: If the primary member template is explicitly
226*67e74705SXin Li     // specialized for a given specialization of the enclosing class template,
227*67e74705SXin Li     // the partial specializations of the member template are ignored
228*67e74705SXin Li     template<typename T>
229*67e74705SXin Li     class D1 {
230*67e74705SXin Li       template<typename U> static U Data;
231*67e74705SXin Li       template<typename U> static CONST U Data<U*> = U(10);  // expected-note {{previous declaration is here}}
232*67e74705SXin Li     };
233*67e74705SXin Li     template<>
234*67e74705SXin Li     template<typename U> U D1<float>::Data = U(10);
235*67e74705SXin Li     template<>
236*67e74705SXin Li     template<typename U> U D1<float>::Data<U*> = U(100);  // expected-error{{redefinition of 'Data'}}
237*67e74705SXin Li   }
238*67e74705SXin Li 
239*67e74705SXin Li   namespace definition_after_outer_instantiation {
240*67e74705SXin Li     template<typename A> struct S {
241*67e74705SXin Li       template<typename B> static const int V1;
242*67e74705SXin Li       template<typename B> static const int V2;
243*67e74705SXin Li     };
244*67e74705SXin Li     template struct S<int>;
245*67e74705SXin Li     template<typename A> template<typename B> const int S<A>::V1 = 123;
246*67e74705SXin Li     template<typename A> template<typename B> const int S<A>::V2<B*> = 456;
247*67e74705SXin Li 
248*67e74705SXin Li     static_assert(S<int>::V1<int> == 123, "");
249*67e74705SXin Li 
250*67e74705SXin Li     // FIXME: The first and third case below possibly should be accepted. We're
251*67e74705SXin Li     // not picking up partial specializations added after the primary template
252*67e74705SXin Li     // is instantiated. This is kind of implied by [temp.class.spec.mfunc]/2,
253*67e74705SXin Li     // and matches our behavior for member class templates, but it's not clear
254*67e74705SXin Li     // that this is intentional. See PR17294 and core-24030.
255*67e74705SXin Li     static_assert(S<int>::V2<int*> == 456, ""); // FIXME expected-error {{}}
256*67e74705SXin Li     static_assert(S<int>::V2<int&> == 789, ""); // expected-error {{}}
257*67e74705SXin Li 
258*67e74705SXin Li     template<typename A> template<typename B> const int S<A>::V2<B&> = 789;
259*67e74705SXin Li     static_assert(S<int>::V2<int&> == 789, ""); // FIXME expected-error {{}}
260*67e74705SXin Li 
261*67e74705SXin Li     // All is OK if the partial specialization is declared before the implicit
262*67e74705SXin Li     // instantiation of the class template specialization.
263*67e74705SXin Li     static_assert(S<char>::V1<int> == 123, "");
264*67e74705SXin Li     static_assert(S<char>::V2<int*> == 456, "");
265*67e74705SXin Li     static_assert(S<char>::V2<int&> == 789, "");
266*67e74705SXin Li   }
267*67e74705SXin Li 
268*67e74705SXin Li   namespace incomplete_array {
269*67e74705SXin Li     template<typename T> extern T var[];
270*67e74705SXin Li     template<typename T> T var[] = { 1, 2, 3 };
271*67e74705SXin Li     template<> char var<char>[] = "hello";
272*67e74705SXin Li     template<typename T> char var<T*>[] = "pointer";
273*67e74705SXin Li 
274*67e74705SXin Li     static_assert(sizeof(var<int>) == 12, "");
275*67e74705SXin Li     static_assert(sizeof(var<char>) == 6, "");
276*67e74705SXin Li     static_assert(sizeof(var<void*>) == 8, "");
277*67e74705SXin Li 
278*67e74705SXin Li     template<typename...> struct tuple;
279*67e74705SXin Li 
280*67e74705SXin Li     template<typename T> struct A {
281*67e74705SXin Li       template<typename U> static T x[];
282*67e74705SXin Li       template<typename U> static T y[];
283*67e74705SXin Li 
284*67e74705SXin Li       template<typename...U> static T y<tuple<U...> >[];
285*67e74705SXin Li     };
286*67e74705SXin Li 
287*67e74705SXin Li     int *use_before_definition = A<int>::x<char>;
288*67e74705SXin Li     template<typename T> template<typename U> T A<T>::x[sizeof(U)];
289*67e74705SXin Li     static_assert(sizeof(A<int>::x<char>) == 4, "");
290*67e74705SXin Li 
291*67e74705SXin Li     template<typename T> template<typename...U> T A<T>::y<tuple<U...> >[] = { U()... };
292*67e74705SXin Li     static_assert(sizeof(A<int>::y<tuple<char, char, char> >) == 12, "");
293*67e74705SXin Li   }
294*67e74705SXin Li 
295*67e74705SXin Li   namespace bad_reference {
296*67e74705SXin Li     struct S {
297*67e74705SXin Li       template<typename T> static int A; // expected-note 4{{here}}
298*67e74705SXin Li     };
299*67e74705SXin Li 
f()300*67e74705SXin Li     template<typename T> void f() {
301*67e74705SXin Li       typename T::template A<int> a; // expected-error {{template name refers to non-type template 'S::A'}}
302*67e74705SXin Li     }
g()303*67e74705SXin Li     template<typename T> void g() {
304*67e74705SXin Li       T::template A<int>::B = 0; // expected-error {{template name refers to non-type template 'S::A'}}
305*67e74705SXin Li     }
h()306*67e74705SXin Li     template<typename T> void h() {
307*67e74705SXin Li       class T::template A<int> c; // expected-error {{template name refers to non-type template 'S::A'}}
308*67e74705SXin Li     }
309*67e74705SXin Li 
310*67e74705SXin Li     template<typename T>
311*67e74705SXin Li     struct X : T::template A<int> {}; // expected-error {{template name refers to non-type template 'S::A'}}
312*67e74705SXin Li 
313*67e74705SXin Li     template void f<S>(); // expected-note {{in instantiation of}}
314*67e74705SXin Li     template void g<S>(); // expected-note {{in instantiation of}}
315*67e74705SXin Li     template void h<S>(); // expected-note {{in instantiation of}}
316*67e74705SXin Li     template struct X<S>; // expected-note {{in instantiation of}}
317*67e74705SXin Li   }
318*67e74705SXin Li }
319*67e74705SXin Li 
320*67e74705SXin Li namespace in_nested_classes {
321*67e74705SXin Li   // TODO:
322*67e74705SXin Li }
323*67e74705SXin Li 
324*67e74705SXin Li namespace bitfield {
325*67e74705SXin Li struct S {
326*67e74705SXin Li   template <int I>
327*67e74705SXin Li   static int f : I; // expected-error {{static member 'f' cannot be a bit-field}}
328*67e74705SXin Li };
329*67e74705SXin Li }
330*67e74705SXin Li 
331*67e74705SXin Li namespace b20896909 {
332*67e74705SXin Li   // This used to crash.
333*67e74705SXin Li   template<typename T> struct helper {};
334*67e74705SXin Li   template<typename T> class A {
335*67e74705SXin Li     template <typename> static helper<typename T::error> x;  // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
336*67e74705SXin Li   };
test()337*67e74705SXin Li   void test() {
338*67e74705SXin Li     A<int> ai;  // expected-note {{in instantiation of}}
339*67e74705SXin Li   }
340*67e74705SXin Li }
341*67e74705SXin Li namespace member_access_is_ok {
342*67e74705SXin Li #ifdef CPP1Y
343*67e74705SXin Li   namespace ns1 {
344*67e74705SXin Li     struct A {
345*67e74705SXin Li       template<class T, T N> constexpr static T Var = N;
346*67e74705SXin Li     };
347*67e74705SXin Li     static_assert(A{}.Var<int,5> == 5,"");
348*67e74705SXin Li   } // end ns1
349*67e74705SXin Li #endif // CPP1Y
350*67e74705SXin Li 
351*67e74705SXin Li namespace ns2 {
352*67e74705SXin Li   template<class T> struct A {
353*67e74705SXin Li     template<class U, T N, U M> static T&& Var;
354*67e74705SXin Li   };
355*67e74705SXin Li   template<class T> template<class U, T N, U M> T&& A<T>::Var = T(N + M);
356*67e74705SXin Li   int *AV = &A<int>().Var<char, 5, 'A'>;
357*67e74705SXin Li 
358*67e74705SXin Li } //end ns2
359*67e74705SXin Li } // end ns member_access_is_ok
360*67e74705SXin Li 
361*67e74705SXin Li #ifdef CPP1Y
362*67e74705SXin Li namespace PR24473 {
363*67e74705SXin Li struct Value
364*67e74705SXin Li {
365*67e74705SXin Li     template<class T>
366*67e74705SXin Li     static constexpr T value = 0;
367*67e74705SXin Li };
368*67e74705SXin Li 
369*67e74705SXin Li template<typename TValue>
370*67e74705SXin Li struct Something
371*67e74705SXin Li {
fooPR24473::Something372*67e74705SXin Li     void foo() {
373*67e74705SXin Li         static_assert(TValue::template value<int> == 0, ""); // error
374*67e74705SXin Li     }
375*67e74705SXin Li };
376*67e74705SXin Li 
main()377*67e74705SXin Li int main() {
378*67e74705SXin Li     Something<Value>{}.foo();
379*67e74705SXin Li     return 0;
380*67e74705SXin Li }
381*67e74705SXin Li 
382*67e74705SXin Li } // end ns PR24473
383*67e74705SXin Li #endif // CPP1Y
384*67e74705SXin Li 
385