1 | // RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify -fcxx-exceptions %s |
2 | |
3 | // |
4 | // Tests for "expression traits" intrinsics such as __is_lvalue_expr. |
5 | // |
6 | // For the time being, these tests are written against the 2003 C++ |
7 | // standard (ISO/IEC 14882:2003 -- see draft at |
8 | // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2001/n1316/). |
9 | // |
10 | // C++0x has its own, more-refined, idea of lvalues and rvalues. |
11 | // If/when we need to support those, we'll need to track both |
12 | // standard documents. |
13 | |
14 | #if !__has_feature(cxx_static_assert) |
15 | # define CONCAT_(X_, Y_) CONCAT1_(X_, Y_) |
16 | # define CONCAT1_(X_, Y_) X_ ## Y_ |
17 | |
18 | // This emulation can be used multiple times on one line (and thus in |
19 | // a macro), except at class scope |
20 | # define static_assert(b_, m_) \ |
21 | typedef int CONCAT_(sa_, __LINE__)[b_ ? 1 : -1] |
22 | #endif |
23 | |
24 | // Tests are broken down according to section of the C++03 standard |
25 | // (ISO/IEC 14882:2003(E)) |
26 | |
27 | // Assertion macros encoding the following two paragraphs |
28 | // |
29 | // basic.lval/1 Every expression is either an lvalue or an rvalue. |
30 | // |
31 | // expr.prim/5 A parenthesized expression is a primary expression whose type |
32 | // and value are identical to those of the enclosed expression. The |
33 | // presence of parentheses does not affect whether the expression is |
34 | // an lvalue. |
35 | // |
36 | // Note: these asserts cannot be made at class scope in C++03. Put |
37 | // them in a member function instead. |
38 | #define ASSERT_LVALUE(expr) \ |
39 | static_assert(__is_lvalue_expr(expr), "should be an lvalue"); \ |
40 | static_assert(__is_lvalue_expr((expr)), \ |
41 | "the presence of parentheses should have" \ |
42 | " no effect on lvalueness (expr.prim/5)"); \ |
43 | static_assert(!__is_rvalue_expr(expr), "should be an lvalue"); \ |
44 | static_assert(!__is_rvalue_expr((expr)), \ |
45 | "the presence of parentheses should have" \ |
46 | " no effect on lvalueness (expr.prim/5)") |
47 | |
48 | #define ASSERT_RVALUE(expr); \ |
49 | static_assert(__is_rvalue_expr(expr), "should be an rvalue"); \ |
50 | static_assert(__is_rvalue_expr((expr)), \ |
51 | "the presence of parentheses should have" \ |
52 | " no effect on lvalueness (expr.prim/5)"); \ |
53 | static_assert(!__is_lvalue_expr(expr), "should be an rvalue"); \ |
54 | static_assert(!__is_lvalue_expr((expr)), \ |
55 | "the presence of parentheses should have" \ |
56 | " no effect on lvalueness (expr.prim/5)") |
57 | |
58 | enum Enum { Enumerator }; |
59 | |
60 | int ReturnInt(); |
61 | void ReturnVoid(); |
62 | Enum ReturnEnum(); |
63 | |
64 | void basic_lval_5() |
65 | { |
66 | // basic.lval/5: The result of calling a function that does not return |
67 | // a reference is an rvalue. |
68 | ASSERT_RVALUE(ReturnInt()); |
69 | ASSERT_RVALUE(ReturnVoid()); |
70 | ASSERT_RVALUE(ReturnEnum()); |
71 | } |
72 | |
73 | int& ReturnIntReference(); |
74 | extern Enum& ReturnEnumReference(); |
75 | |
76 | void basic_lval_6() |
77 | { |
78 | // basic.lval/6: An expression which holds a temporary object resulting |
79 | // from a cast to a nonreference type is an rvalue (this includes |
80 | // the explicit creation of an object using functional notation |
81 | struct IntClass |
82 | { |
83 | explicit IntClass(int = 0); |
84 | IntClass(char const*); |
85 | operator int() const; |
86 | }; |
87 | |
88 | struct ConvertibleToIntClass |
89 | { |
90 | operator IntClass() const; |
91 | }; |
92 | |
93 | ConvertibleToIntClass b; |
94 | |
95 | // Make sure even trivial conversions are not detected as lvalues |
96 | int intLvalue = 0; |
97 | ASSERT_RVALUE((int)intLvalue); |
98 | ASSERT_RVALUE((short)intLvalue); |
99 | ASSERT_RVALUE((long)intLvalue); |
100 | |
101 | // Same tests with function-call notation |
102 | ASSERT_RVALUE(int(intLvalue)); |
103 | ASSERT_RVALUE(short(intLvalue)); |
104 | ASSERT_RVALUE(long(intLvalue)); |
105 | |
106 | char charLValue = 'x'; |
107 | ASSERT_RVALUE((signed char)charLValue); |
108 | ASSERT_RVALUE((unsigned char)charLValue); |
109 | |
110 | ASSERT_RVALUE(static_cast<int>(IntClass())); |
111 | IntClass intClassLValue; |
112 | ASSERT_RVALUE(static_cast<int>(intClassLValue)); |
113 | ASSERT_RVALUE(static_cast<IntClass>(ConvertibleToIntClass())); |
114 | ConvertibleToIntClass convertibleToIntClassLValue; |
115 | ASSERT_RVALUE(static_cast<IntClass>(convertibleToIntClassLValue)); |
116 | |
117 | |
118 | typedef signed char signed_char; |
119 | typedef unsigned char unsigned_char; |
120 | ASSERT_RVALUE(signed_char(charLValue)); |
121 | ASSERT_RVALUE(unsigned_char(charLValue)); |
122 | |
123 | ASSERT_RVALUE(int(IntClass())); |
124 | ASSERT_RVALUE(int(intClassLValue)); |
125 | ASSERT_RVALUE(IntClass(ConvertibleToIntClass())); |
126 | ASSERT_RVALUE(IntClass(convertibleToIntClassLValue)); |
127 | } |
128 | |
129 | void conv_ptr_1() |
130 | { |
131 | // conv.ptr/1: A null pointer constant is an integral constant |
132 | // expression (5.19) rvalue of integer type that evaluates to |
133 | // zero. |
134 | ASSERT_RVALUE(0); |
135 | } |
136 | |
137 | void expr_6() |
138 | { |
139 | // expr/6: If an expression initially has the type "reference to T" |
140 | // (8.3.2, 8.5.3), ... the expression is an lvalue. |
141 | int x = 0; |
142 | int& referenceToInt = x; |
143 | ASSERT_LVALUE(referenceToInt); |
144 | ASSERT_LVALUE(ReturnIntReference()); |
145 | } |
146 | |
147 | void expr_prim_2() |
148 | { |
149 | // 5.1/2 A string literal is an lvalue; all other |
150 | // literals are rvalues. |
151 | ASSERT_LVALUE("foo"); |
152 | ASSERT_RVALUE(1); |
153 | ASSERT_RVALUE(1.2); |
154 | ASSERT_RVALUE(10UL); |
155 | } |
156 | |
157 | void expr_prim_3() |
158 | { |
159 | // 5.1/3: The keyword "this" names a pointer to the object for |
160 | // which a nonstatic member function (9.3.2) is invoked. ...The |
161 | // expression is an rvalue. |
162 | struct ThisTest |
163 | { |
164 | void f() { ASSERT_RVALUE(this); } |
165 | }; |
166 | } |
167 | |
168 | extern int variable; |
169 | void Function(); |
170 | |
171 | struct BaseClass |
172 | { |
173 | virtual ~BaseClass(); |
174 | |
175 | int BaseNonstaticMemberFunction(); |
176 | static int BaseStaticMemberFunction(); |
177 | int baseDataMember; |
178 | }; |
179 | |
180 | struct Class : BaseClass |
181 | { |
182 | static void function(); |
183 | static int variable; |
184 | |
185 | template <class T> |
186 | struct NestedClassTemplate {}; |
187 | |
188 | template <class T> |
189 | static int& NestedFuncTemplate() { return variable; } // expected-note{{possible target for call}} |
190 | |
191 | template <class T> |
192 | int& NestedMemfunTemplate() { return variable; } // expected-note{{possible target for call}} |
193 | |
194 | int operator*() const; |
195 | |
196 | template <class T> |
197 | int operator+(T) const; // expected-note{{possible target for call}} |
198 | |
199 | int NonstaticMemberFunction(); |
200 | static int StaticMemberFunction(); |
201 | int dataMember; |
202 | |
203 | int& referenceDataMember; |
204 | static int& staticReferenceDataMember; |
205 | static int staticNonreferenceDataMember; |
206 | |
207 | enum Enum { Enumerator }; |
208 | |
209 | operator long() const; |
210 | |
211 | Class(); |
212 | Class(int,int); |
213 | |
214 | void expr_prim_4() |
215 | { |
216 | // 5.1/4: The operator :: followed by an identifier, a |
217 | // qualified-id, or an operator-function-id is a primary- |
218 | // expression. ...The result is an lvalue if the entity is |
219 | // a function or variable. |
220 | ASSERT_LVALUE(::Function); // identifier: function |
221 | ASSERT_LVALUE(::variable); // identifier: variable |
222 | |
223 | // the only qualified-id form that can start without "::" (and thus |
224 | // be legal after "::" ) is |
225 | // |
226 | // ::<sub>opt</sub> nested-name-specifier template<sub>opt</sub> unqualified-id |
227 | ASSERT_LVALUE(::Class::function); // qualified-id: function |
228 | ASSERT_LVALUE(::Class::variable); // qualified-id: variable |
229 | |
230 | // The standard doesn't give a clear answer about whether these |
231 | // should really be lvalues or rvalues without some surrounding |
232 | // context that forces them to be interpreted as naming a |
233 | // particular function template specialization (that situation |
234 | // doesn't come up in legal pure C++ programs). This language |
235 | // extension simply rejects them as requiring additional context |
236 | __is_lvalue_expr(::Class::NestedFuncTemplate); // qualified-id: template \ |
237 | // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} |
238 | |
239 | __is_lvalue_expr(::Class::NestedMemfunTemplate); // qualified-id: template \ |
240 | // expected-error{{reference to non-static member function must be called}} |
241 | |
242 | __is_lvalue_expr(::Class::operator+); // operator-function-id: template \ |
243 | // expected-error{{reference to non-static member function must be called}} |
244 | |
245 | //ASSERT_RVALUE(::Class::operator*); // operator-function-id: member function |
246 | } |
247 | |
248 | void expr_prim_7() |
249 | { |
250 | // expr.prim/7 An identifier is an id-expression provided it has been |
251 | // suitably declared (clause 7). [Note: ... ] The type of the |
252 | // expression is the type of the identifier. The result is the |
253 | // entity denoted by the identifier. The result is an lvalue if |
254 | // the entity is a function, variable, or data member... (cont'd) |
255 | ASSERT_LVALUE(Function); // identifier: function |
256 | ASSERT_LVALUE(StaticMemberFunction); // identifier: function |
257 | ASSERT_LVALUE(variable); // identifier: variable |
258 | ASSERT_LVALUE(dataMember); // identifier: data member |
259 | //ASSERT_RVALUE(NonstaticMemberFunction); // identifier: member function |
260 | |
261 | // (cont'd)...A nested-name-specifier that names a class, |
262 | // optionally followed by the keyword template (14.2), and then |
263 | // followed by the name of a member of either that class (9.2) or |
264 | // one of its base classes... is a qualified-id... The result is |
265 | // the member. The type of the result is the type of the |
266 | // member. The result is an lvalue if the member is a static |
267 | // member function or a data member. |
268 | ASSERT_LVALUE(Class::dataMember); |
269 | ASSERT_LVALUE(Class::StaticMemberFunction); |
270 | //ASSERT_RVALUE(Class::NonstaticMemberFunction); // identifier: member function |
271 | |
272 | ASSERT_LVALUE(Class::baseDataMember); |
273 | ASSERT_LVALUE(Class::BaseStaticMemberFunction); |
274 | //ASSERT_RVALUE(Class::BaseNonstaticMemberFunction); // identifier: member function |
275 | } |
276 | }; |
277 | |
278 | void expr_call_10() |
279 | { |
280 | // expr.call/10: A function call is an lvalue if and only if the |
281 | // result type is a reference. This statement is partially |
282 | // redundant with basic.lval/5 |
283 | basic_lval_5(); |
284 | |
285 | ASSERT_LVALUE(ReturnIntReference()); |
286 | ASSERT_LVALUE(ReturnEnumReference()); |
287 | } |
288 | |
289 | namespace Namespace |
290 | { |
291 | int x; |
292 | void function(); |
293 | } |
294 | |
295 | void expr_prim_8() |
296 | { |
297 | // expr.prim/8 A nested-name-specifier that names a namespace |
298 | // (7.3), followed by the name of a member of that namespace (or |
299 | // the name of a member of a namespace made visible by a |
300 | // using-directive ) is a qualified-id; 3.4.3.2 describes name |
301 | // lookup for namespace members that appear in qualified-ids. The |
302 | // result is the member. The type of the result is the type of the |
303 | // member. The result is an lvalue if the member is a function or |
304 | // a variable. |
305 | ASSERT_LVALUE(Namespace::x); |
306 | ASSERT_LVALUE(Namespace::function); |
307 | } |
308 | |
309 | void expr_sub_1(int* pointer) |
310 | { |
311 | // expr.sub/1 A postfix expression followed by an expression in |
312 | // square brackets is a postfix expression. One of the expressions |
313 | // shall have the type "pointer to T" and the other shall have |
314 | // enumeration or integral type. The result is an lvalue of type |
315 | // "T." |
316 | ASSERT_LVALUE(pointer[1]); |
317 | |
318 | // The expression E1[E2] is identical (by definition) to *((E1)+(E2)). |
319 | ASSERT_LVALUE(*(pointer+1)); |
320 | } |
321 | |
322 | void expr_type_conv_1() |
323 | { |
324 | // expr.type.conv/1 A simple-type-specifier (7.1.5) followed by a |
325 | // parenthesized expression-list constructs a value of the specified |
326 | // type given the expression list. ... If the expression list |
327 | // specifies more than a single value, the type shall be a class with |
328 | // a suitably declared constructor (8.5, 12.1), and the expression |
329 | // T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, |
330 | // x2, ...); for some invented temporary variable t, with the result |
331 | // being the value of t as an rvalue. |
332 | ASSERT_RVALUE(Class(2,2)); |
333 | } |
334 | |
335 | void expr_type_conv_2() |
336 | { |
337 | // expr.type.conv/2 The expression T(), where T is a |
338 | // simple-type-specifier (7.1.5.2) for a non-array complete object |
339 | // type or the (possibly cv-qualified) void type, creates an |
340 | // rvalue of the specified type, |
341 | ASSERT_RVALUE(int()); |
342 | ASSERT_RVALUE(Class()); |
343 | ASSERT_RVALUE(void()); |
344 | } |
345 | |
346 | |
347 | void expr_ref_4() |
348 | { |
349 | // Applies to expressions of the form E1.E2 |
350 | |
351 | // If E2 is declared to have type "reference to T", then E1.E2 is |
352 | // an lvalue;.... Otherwise, one of the following rules applies. |
353 | ASSERT_LVALUE(Class().staticReferenceDataMember); |
354 | ASSERT_LVALUE(Class().referenceDataMember); |
355 | |
356 | // - If E2 is a static data member, and the type of E2 is T, then |
357 | // E1.E2 is an lvalue; ... |
358 | ASSERT_LVALUE(Class().staticNonreferenceDataMember); |
359 | ASSERT_LVALUE(Class().staticReferenceDataMember); |
360 | |
361 | |
362 | // - If E2 is a non-static data member, ... If E1 is an lvalue, |
363 | // then E1.E2 is an lvalue... |
364 | Class lvalue; |
365 | ASSERT_LVALUE(lvalue.dataMember); |
366 | ASSERT_RVALUE(Class().dataMember); |
367 | |
368 | // - If E1.E2 refers to a static member function, ... then E1.E2 |
369 | // is an lvalue |
370 | ASSERT_LVALUE(Class().StaticMemberFunction); |
371 | |
372 | // - Otherwise, if E1.E2 refers to a non-static member function, |
373 | // then E1.E2 is not an lvalue. |
374 | //ASSERT_RVALUE(Class().NonstaticMemberFunction); |
375 | |
376 | // - If E2 is a member enumerator, and the type of E2 is T, the |
377 | // expression E1.E2 is not an lvalue. The type of E1.E2 is T. |
378 | ASSERT_RVALUE(Class().Enumerator); |
379 | ASSERT_RVALUE(lvalue.Enumerator); |
380 | } |
381 | |
382 | |
383 | void expr_post_incr_1(int x) |
384 | { |
385 | // expr.post.incr/1 The value obtained by applying a postfix ++ is |
386 | // the value that the operand had before applying the |
387 | // operator... The result is an rvalue. |
388 | ASSERT_RVALUE(x++); |
389 | } |
390 | |
391 | void expr_dynamic_cast_2() |
392 | { |
393 | // expr.dynamic.cast/2: If T is a pointer type, v shall be an |
394 | // rvalue of a pointer to complete class type, and the result is |
395 | // an rvalue of type T. |
396 | Class instance; |
397 | ASSERT_RVALUE(dynamic_cast<Class*>(&instance)); |
398 | |
399 | // If T is a reference type, v shall be an |
400 | // lvalue of a complete class type, and the result is an lvalue of |
401 | // the type referred to by T. |
402 | ASSERT_LVALUE(dynamic_cast<Class&>(instance)); |
403 | } |
404 | |
405 | void expr_dynamic_cast_5() |
406 | { |
407 | // expr.dynamic.cast/5: If T is "reference to cv1 B" and v has type |
408 | // "cv2 D" such that B is a base class of D, the result is an |
409 | // lvalue for the unique B sub-object of the D object referred |
410 | // to by v. |
411 | typedef BaseClass B; |
412 | typedef Class D; |
413 | D object; |
414 | ASSERT_LVALUE(dynamic_cast<B&>(object)); |
415 | } |
416 | |
417 | // expr.dynamic.cast/8: The run-time check logically executes as follows: |
418 | // |
419 | // - If, in the most derived object pointed (referred) to by v, v |
420 | // points (refers) to a public base class subobject of a T object, and |
421 | // if only one object of type T is derived from the sub-object pointed |
422 | // (referred) to by v, the result is a pointer (an lvalue referring) |
423 | // to that T object. |
424 | // |
425 | // - Otherwise, if v points (refers) to a public base class sub-object |
426 | // of the most derived object, and the type of the most derived object |
427 | // has a base class, of type T, that is unambiguous and public, the |
428 | // result is a pointer (an lvalue referring) to the T sub-object of |
429 | // the most derived object. |
430 | // |
431 | // The mention of "lvalue" in the text above appears to be a |
432 | // defect that is being corrected by the response to UK65 (see |
433 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2841.html). |
434 | |
435 | #if 0 |
436 | void expr_typeid_1() |
437 | { |
438 | // expr.typeid/1: The result of a typeid expression is an lvalue... |
439 | ASSERT_LVALUE(typeid(1)); |
440 | } |
441 | #endif |
442 | |
443 | void expr_static_cast_1(int x) |
444 | { |
445 | // expr.static.cast/1: The result of the expression |
446 | // static_cast<T>(v) is the result of converting the expression v |
447 | // to type T. If T is a reference type, the result is an lvalue; |
448 | // otherwise, the result is an rvalue. |
449 | ASSERT_LVALUE(static_cast<int&>(x)); |
450 | ASSERT_RVALUE(static_cast<int>(x)); |
451 | } |
452 | |
453 | void expr_reinterpret_cast_1() |
454 | { |
455 | // expr.reinterpret.cast/1: The result of the expression |
456 | // reinterpret_cast<T>(v) is the result of converting the |
457 | // expression v to type T. If T is a reference type, the result is |
458 | // an lvalue; otherwise, the result is an rvalue |
459 | ASSERT_RVALUE(reinterpret_cast<int*>(0)); |
460 | char const v = 0; |
461 | ASSERT_LVALUE(reinterpret_cast<char const&>(v)); |
462 | } |
463 | |
464 | void expr_unary_op_1(int* pointer, struct incomplete* pointerToIncompleteType) |
465 | { |
466 | // expr.unary.op/1: The unary * operator performs indirection: the |
467 | // expression to which it is applied shall be a pointer to an |
468 | // object type, or a pointer to a function type and the result is |
469 | // an lvalue referring to the object or function to which the |
470 | // expression points. |
471 | ASSERT_LVALUE(*pointer); |
472 | ASSERT_LVALUE(*Function); |
473 | |
474 | // [Note: a pointer to an incomplete type |
475 | // (other than cv void ) can be dereferenced. ] |
476 | ASSERT_LVALUE(*pointerToIncompleteType); |
477 | } |
478 | |
479 | void expr_pre_incr_1(int operand) |
480 | { |
481 | // expr.pre.incr/1: The operand of prefix ++ ... shall be a |
482 | // modifiable lvalue.... The value is the new value of the |
483 | // operand; it is an lvalue. |
484 | ASSERT_LVALUE(++operand); |
485 | } |
486 | |
487 | void expr_cast_1(int x) |
488 | { |
489 | // expr.cast/1: The result of the expression (T) cast-expression |
490 | // is of type T. The result is an lvalue if T is a reference type, |
491 | // otherwise the result is an rvalue. |
492 | ASSERT_LVALUE((void(&)())expr_cast_1); |
493 | ASSERT_LVALUE((int&)x); |
494 | ASSERT_RVALUE((void(*)())expr_cast_1); |
495 | ASSERT_RVALUE((int)x); |
496 | } |
497 | |
498 | void expr_mptr_oper() |
499 | { |
500 | // expr.mptr.oper/6: The result of a .* expression is an lvalue |
501 | // only if its first operand is an lvalue and its second operand |
502 | // is a pointer to data member... (cont'd) |
503 | typedef Class MakeRValue; |
504 | ASSERT_RVALUE(MakeRValue().*(&Class::dataMember)); |
505 | //ASSERT_RVALUE(MakeRValue().*(&Class::NonstaticMemberFunction)); |
506 | Class lvalue; |
507 | ASSERT_LVALUE(lvalue.*(&Class::dataMember)); |
508 | //ASSERT_RVALUE(lvalue.*(&Class::NonstaticMemberFunction)); |
509 | |
510 | // (cont'd)...The result of an ->* expression is an lvalue only |
511 | // if its second operand is a pointer to data member. If the |
512 | // second operand is the null pointer to member value (4.11), the |
513 | // behavior is undefined. |
514 | ASSERT_LVALUE((&lvalue)->*(&Class::dataMember)); |
515 | //ASSERT_RVALUE((&lvalue)->*(&Class::NonstaticMemberFunction)); |
516 | } |
517 | |
518 | void expr_cond(bool cond) |
519 | { |
520 | // 5.16 Conditional operator [expr.cond] |
521 | // |
522 | // 2 If either the second or the third operand has type (possibly |
523 | // cv-qualified) void, one of the following shall hold: |
524 | // |
525 | // - The second or the third operand (but not both) is a |
526 | // (possibly parenthesized) throw-expression (15.1); the result |
527 | // is of the type and value category of the other. |
528 | |
529 | Class classLvalue; |
530 | ASSERT_RVALUE(cond ? throw 1 : (void)0); |
531 | ASSERT_RVALUE(cond ? (void)0 : throw 1); |
532 | ASSERT_RVALUE(cond ? throw 1 : 0); |
533 | ASSERT_RVALUE(cond ? 0 : throw 1); |
534 | ASSERT_LVALUE(cond ? throw 1 : classLvalue); |
535 | ASSERT_LVALUE(cond ? classLvalue : throw 1); |
536 | |
537 | // - Both the second and the third operands have type void; the result |
538 | // is of type void and is an rvalue. [Note: this includes the case |
539 | // where both operands are throw-expressions. ] |
540 | ASSERT_RVALUE(cond ? (void)1 : (void)0); |
541 | ASSERT_RVALUE(cond ? throw 1 : throw 0); |
542 | |
543 | // expr.cond/4: If the second and third operands are lvalues and |
544 | // have the same type, the result is of that type and is an |
545 | // lvalue. |
546 | ASSERT_LVALUE(cond ? classLvalue : classLvalue); |
547 | int intLvalue = 0; |
548 | ASSERT_LVALUE(cond ? intLvalue : intLvalue); |
549 | |
550 | // expr.cond/5:Otherwise, the result is an rvalue. |
551 | typedef Class MakeRValue; |
552 | ASSERT_RVALUE(cond ? MakeRValue() : classLvalue); |
553 | ASSERT_RVALUE(cond ? classLvalue : MakeRValue()); |
554 | ASSERT_RVALUE(cond ? MakeRValue() : MakeRValue()); |
555 | ASSERT_RVALUE(cond ? classLvalue : intLvalue); |
556 | ASSERT_RVALUE(cond ? intLvalue : int()); |
557 | } |
558 | |
559 | void expr_ass_1(int x) |
560 | { |
561 | // expr.ass/1: There are several assignment operators, all of |
562 | // which group right-to-left. All require a modifiable lvalue as |
563 | // their left operand, and the type of an assignment expression is |
564 | // that of its left operand. The result of the assignment |
565 | // operation is the value stored in the left operand after the |
566 | // assignment has taken place; the result is an lvalue. |
567 | ASSERT_LVALUE(x = 1); |
568 | ASSERT_LVALUE(x += 1); |
569 | ASSERT_LVALUE(x -= 1); |
570 | ASSERT_LVALUE(x *= 1); |
571 | ASSERT_LVALUE(x /= 1); |
572 | ASSERT_LVALUE(x %= 1); |
573 | ASSERT_LVALUE(x ^= 1); |
574 | ASSERT_LVALUE(x &= 1); |
575 | ASSERT_LVALUE(x |= 1); |
576 | } |
577 | |
578 | void expr_comma(int x) |
579 | { |
580 | // expr.comma: A pair of expressions separated by a comma is |
581 | // evaluated left-to-right and the value of the left expression is |
582 | // discarded... result is an lvalue if its right operand is. |
583 | |
584 | // Can't use the ASSERT_XXXX macros without adding parens around |
585 | // the comma expression. |
586 | static_assert(__is_lvalue_expr(x,x), "expected an lvalue"); |
587 | static_assert(__is_rvalue_expr(x,1), "expected an rvalue"); |
588 | static_assert(__is_lvalue_expr(1,x), "expected an lvalue"); |
589 | static_assert(__is_rvalue_expr(1,1), "expected an rvalue"); |
590 | } |
591 | |
592 | #if 0 |
593 | template<typename T> void f(); |
594 | |
595 | // FIXME These currently fail |
596 | void expr_fun_lvalue() |
597 | { |
598 | ASSERT_LVALUE(&f<int>); |
599 | } |
600 | |
601 | void expr_fun_rvalue() |
602 | { |
603 | ASSERT_RVALUE(f<int>); |
604 | } |
605 | #endif |
606 | |
607 | template <int NonTypeNonReferenceParameter, int& NonTypeReferenceParameter> |
608 | void check_temp_param_6() |
609 | { |
610 | ASSERT_RVALUE(NonTypeNonReferenceParameter); |
611 | ASSERT_LVALUE(NonTypeReferenceParameter); |
612 | } |
613 | |
614 | int AnInt = 0; |
615 | |
616 | void temp_param_6() |
617 | { |
618 | check_temp_param_6<3,AnInt>(); |
619 | } |
620 | |