John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 1 | // RUN: %clang_cc1 -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 | { |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 139 | // expr/6: If an expression initially has the type "reference to T" |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 | |
Abramo Bagnara | 9c0e1ec | 2011-11-15 21:43:28 +0000 | [diff] [blame] | 188 | template <class T> |
| 189 | static int& NestedFuncTemplate() { return variable; } // expected-note{{possible target for call}} |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 190 | |
| 191 | template <class T> |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 192 | int& NestedMemfunTemplate() { return variable; } |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 193 | |
| 194 | int operator*() const; |
| 195 | |
| 196 | template <class T> |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 197 | int operator+(T) const; |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 \ |
John McCall | 6dbba4f | 2011-10-11 23:14:30 +0000 | [diff] [blame] | 237 | // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 238 | |
| 239 | __is_lvalue_expr(::Class::NestedMemfunTemplate); // qualified-id: template \ |
John McCall | 6dbba4f | 2011-10-11 23:14:30 +0000 | [diff] [blame] | 240 | // expected-error{{reference to non-static member function must be called}} |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 241 | |
| 242 | __is_lvalue_expr(::Class::operator+); // operator-function-id: template \ |
John McCall | 6dbba4f | 2011-10-11 23:14:30 +0000 | [diff] [blame] | 243 | // expected-error{{reference to non-static member function must be called}} |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 244 | |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 245 | //ASSERT_RVALUE(::Class::operator*); // operator-function-id: member function |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 259 | //ASSERT_RVALUE(NonstaticMemberFunction); // identifier: member function |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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); |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 270 | //ASSERT_RVALUE(Class::NonstaticMemberFunction); // identifier: member function |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 271 | |
| 272 | ASSERT_LVALUE(Class::baseDataMember); |
| 273 | ASSERT_LVALUE(Class::BaseStaticMemberFunction); |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 274 | //ASSERT_RVALUE(Class::BaseNonstaticMemberFunction); // identifier: member function |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 313 | // shall have the type "pointer to T" and the other shall have |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 314 | // enumeration or integral type. The result is an lvalue of type |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 315 | // "T." |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 351 | // If E2 is declared to have type "reference to T", then E1.E2 is |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 352 | // an lvalue;.... Otherwise, one of the following rules applies. |
| 353 | ASSERT_LVALUE(Class().staticReferenceDataMember); |
| 354 | ASSERT_LVALUE(Class().referenceDataMember); |
| 355 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 356 | // - If E2 is a static data member, and the type of E2 is T, then |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 357 | // E1.E2 is an lvalue; ... |
| 358 | ASSERT_LVALUE(Class().staticNonreferenceDataMember); |
| 359 | ASSERT_LVALUE(Class().staticReferenceDataMember); |
| 360 | |
| 361 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 362 | // - If E2 is a non-static data member, ... If E1 is an lvalue, |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 363 | // then E1.E2 is an lvalue... |
| 364 | Class lvalue; |
| 365 | ASSERT_LVALUE(lvalue.dataMember); |
| 366 | ASSERT_RVALUE(Class().dataMember); |
| 367 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 368 | // - If E1.E2 refers to a static member function, ... then E1.E2 |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 369 | // is an lvalue |
| 370 | ASSERT_LVALUE(Class().StaticMemberFunction); |
| 371 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 372 | // - Otherwise, if E1.E2 refers to a non-static member function, |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 373 | // then E1.E2 is not an lvalue. |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 374 | //ASSERT_RVALUE(Class().NonstaticMemberFunction); |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 375 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 376 | // - If E2 is a member enumerator, and the type of E2 is T, the |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 | { |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 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 |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 | // |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 419 | // - If, in the most derived object pointed (referred) to by v, v |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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 | // |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 425 | // - Otherwise, if v points (refers) to a public base class sub-object |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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)); |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 505 | //ASSERT_RVALUE(MakeRValue().*(&Class::NonstaticMemberFunction)); |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 506 | Class lvalue; |
| 507 | ASSERT_LVALUE(lvalue.*(&Class::dataMember)); |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 508 | //ASSERT_RVALUE(lvalue.*(&Class::NonstaticMemberFunction)); |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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)); |
John McCall | 864c041 | 2011-04-26 20:42:42 +0000 | [diff] [blame] | 515 | //ASSERT_RVALUE((&lvalue)->*(&Class::NonstaticMemberFunction)); |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 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, then the lvalue-to-rvalue (4.1), |
| 524 | // array-to-pointer (4.2), and function-to-pointer (4.3) standard |
| 525 | // conversions are performed on the second and third operands, and one |
| 526 | // of the following shall hold: |
| 527 | // |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 528 | // - The second or the third operand (but not both) is a |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 529 | // throw-expression (15.1); the result is of the type of the other and |
| 530 | // is an rvalue. |
| 531 | |
| 532 | Class classLvalue; |
| 533 | ASSERT_RVALUE(cond ? throw 1 : (void)0); |
| 534 | ASSERT_RVALUE(cond ? (void)0 : throw 1); |
| 535 | ASSERT_RVALUE(cond ? throw 1 : classLvalue); |
| 536 | ASSERT_RVALUE(cond ? classLvalue : throw 1); |
| 537 | |
NAKAMURA Takumi | ddddd48 | 2011-08-12 05:49:51 +0000 | [diff] [blame] | 538 | // - Both the second and the third operands have type void; the result |
John Wiegley | 5526220 | 2011-04-25 06:54:41 +0000 | [diff] [blame] | 539 | // is of type void and is an rvalue. [Note: this includes the case |
| 540 | // where both operands are throw-expressions. ] |
| 541 | ASSERT_RVALUE(cond ? (void)1 : (void)0); |
| 542 | ASSERT_RVALUE(cond ? throw 1 : throw 0); |
| 543 | |
| 544 | // expr.cond/4: If the second and third operands are lvalues and |
| 545 | // have the same type, the result is of that type and is an |
| 546 | // lvalue. |
| 547 | ASSERT_LVALUE(cond ? classLvalue : classLvalue); |
| 548 | int intLvalue = 0; |
| 549 | ASSERT_LVALUE(cond ? intLvalue : intLvalue); |
| 550 | |
| 551 | // expr.cond/5:Otherwise, the result is an rvalue. |
| 552 | typedef Class MakeRValue; |
| 553 | ASSERT_RVALUE(cond ? MakeRValue() : classLvalue); |
| 554 | ASSERT_RVALUE(cond ? classLvalue : MakeRValue()); |
| 555 | ASSERT_RVALUE(cond ? MakeRValue() : MakeRValue()); |
| 556 | ASSERT_RVALUE(cond ? classLvalue : intLvalue); |
| 557 | ASSERT_RVALUE(cond ? intLvalue : int()); |
| 558 | } |
| 559 | |
| 560 | void expr_ass_1(int x) |
| 561 | { |
| 562 | // expr.ass/1: There are several assignment operators, all of |
| 563 | // which group right-to-left. All require a modifiable lvalue as |
| 564 | // their left operand, and the type of an assignment expression is |
| 565 | // that of its left operand. The result of the assignment |
| 566 | // operation is the value stored in the left operand after the |
| 567 | // assignment has taken place; the result is an lvalue. |
| 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 | ASSERT_LVALUE(x |= 1); |
| 577 | } |
| 578 | |
| 579 | void expr_comma(int x) |
| 580 | { |
| 581 | // expr.comma: A pair of expressions separated by a comma is |
| 582 | // evaluated left-to-right and the value of the left expression is |
| 583 | // discarded... result is an lvalue if its right operand is. |
| 584 | |
| 585 | // Can't use the ASSERT_XXXX macros without adding parens around |
| 586 | // the comma expression. |
| 587 | static_assert(__is_lvalue_expr(x,x), "expected an lvalue"); |
| 588 | static_assert(__is_rvalue_expr(x,1), "expected an rvalue"); |
| 589 | static_assert(__is_lvalue_expr(1,x), "expected an lvalue"); |
| 590 | static_assert(__is_rvalue_expr(1,1), "expected an rvalue"); |
| 591 | } |
| 592 | |
| 593 | #if 0 |
| 594 | template<typename T> void f(); |
| 595 | |
| 596 | // FIXME These currently fail |
| 597 | void expr_fun_lvalue() |
| 598 | { |
| 599 | ASSERT_LVALUE(&f<int>); |
| 600 | } |
| 601 | |
| 602 | void expr_fun_rvalue() |
| 603 | { |
| 604 | ASSERT_RVALUE(f<int>); |
| 605 | } |
| 606 | #endif |
| 607 | |
| 608 | template <int NonTypeNonReferenceParameter, int& NonTypeReferenceParameter> |
| 609 | void check_temp_param_6() |
| 610 | { |
| 611 | ASSERT_RVALUE(NonTypeNonReferenceParameter); |
| 612 | ASSERT_LVALUE(NonTypeReferenceParameter); |
| 613 | } |
| 614 | |
| 615 | int AnInt = 0; |
| 616 | |
| 617 | void temp_param_6() |
| 618 | { |
| 619 | check_temp_param_6<3,AnInt>(); |
| 620 | } |