// RUN: %clang_cc1 -fsyntax-only -verify -fcxx-exceptions %s // // Tests for "expression traits" intrinsics such as __is_lvalue_expr. // // For the time being, these tests are written against the 2003 C++ // standard (ISO/IEC 14882:2003 -- see draft at // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2001/n1316/). // // C++0x has its own, more-refined, idea of lvalues and rvalues. // If/when we need to support those, we'll need to track both // standard documents. #if !__has_feature(cxx_static_assert) # define CONCAT_(X_, Y_) CONCAT1_(X_, Y_) # define CONCAT1_(X_, Y_) X_ ## Y_ // This emulation can be used multiple times on one line (and thus in // a macro), except at class scope # define static_assert(b_, m_) \ typedef int CONCAT_(sa_, __LINE__)[b_ ? 1 : -1] #endif // Tests are broken down according to section of the C++03 standard // (ISO/IEC 14882:2003(E)) // Assertion macros encoding the following two paragraphs // // basic.lval/1 Every expression is either an lvalue or an rvalue. // // expr.prim/5 A parenthesized expression is a primary expression whose type // and value are identical to those of the enclosed expression. The // presence of parentheses does not affect whether the expression is // an lvalue. // // Note: these asserts cannot be made at class scope in C++03. Put // them in a member function instead. #define ASSERT_LVALUE(expr) \ static_assert(__is_lvalue_expr(expr), "should be an lvalue"); \ static_assert(__is_lvalue_expr((expr)), \ "the presence of parentheses should have" \ " no effect on lvalueness (expr.prim/5)"); \ static_assert(!__is_rvalue_expr(expr), "should be an lvalue"); \ static_assert(!__is_rvalue_expr((expr)), \ "the presence of parentheses should have" \ " no effect on lvalueness (expr.prim/5)") #define ASSERT_RVALUE(expr); \ static_assert(__is_rvalue_expr(expr), "should be an rvalue"); \ static_assert(__is_rvalue_expr((expr)), \ "the presence of parentheses should have" \ " no effect on lvalueness (expr.prim/5)"); \ static_assert(!__is_lvalue_expr(expr), "should be an rvalue"); \ static_assert(!__is_lvalue_expr((expr)), \ "the presence of parentheses should have" \ " no effect on lvalueness (expr.prim/5)") enum Enum { Enumerator }; int ReturnInt(); void ReturnVoid(); Enum ReturnEnum(); void basic_lval_5() { // basic.lval/5: The result of calling a function that does not return // a reference is an rvalue. ASSERT_RVALUE(ReturnInt()); ASSERT_RVALUE(ReturnVoid()); ASSERT_RVALUE(ReturnEnum()); } int& ReturnIntReference(); extern Enum& ReturnEnumReference(); void basic_lval_6() { // basic.lval/6: An expression which holds a temporary object resulting // from a cast to a nonreference type is an rvalue (this includes // the explicit creation of an object using functional notation struct IntClass { explicit IntClass(int = 0); IntClass(char const*); operator int() const; }; struct ConvertibleToIntClass { operator IntClass() const; }; ConvertibleToIntClass b; // Make sure even trivial conversions are not detected as lvalues int intLvalue = 0; ASSERT_RVALUE((int)intLvalue); ASSERT_RVALUE((short)intLvalue); ASSERT_RVALUE((long)intLvalue); // Same tests with function-call notation ASSERT_RVALUE(int(intLvalue)); ASSERT_RVALUE(short(intLvalue)); ASSERT_RVALUE(long(intLvalue)); char charLValue = 'x'; ASSERT_RVALUE((signed char)charLValue); ASSERT_RVALUE((unsigned char)charLValue); ASSERT_RVALUE(static_cast<int>(IntClass())); IntClass intClassLValue; ASSERT_RVALUE(static_cast<int>(intClassLValue)); ASSERT_RVALUE(static_cast<IntClass>(ConvertibleToIntClass())); ConvertibleToIntClass convertibleToIntClassLValue; ASSERT_RVALUE(static_cast<IntClass>(convertibleToIntClassLValue)); typedef signed char signed_char; typedef unsigned char unsigned_char; ASSERT_RVALUE(signed_char(charLValue)); ASSERT_RVALUE(unsigned_char(charLValue)); ASSERT_RVALUE(int(IntClass())); ASSERT_RVALUE(int(intClassLValue)); ASSERT_RVALUE(IntClass(ConvertibleToIntClass())); ASSERT_RVALUE(IntClass(convertibleToIntClassLValue)); } void conv_ptr_1() { // conv.ptr/1: A null pointer constant is an integral constant // expression (5.19) rvalue of integer type that evaluates to // zero. ASSERT_RVALUE(0); } void expr_6() { // expr/6: If an expression initially has the type "reference to T" // (8.3.2, 8.5.3), ... the expression is an lvalue. int x = 0; int& referenceToInt = x; ASSERT_LVALUE(referenceToInt); ASSERT_LVALUE(ReturnIntReference()); } void expr_prim_2() { // 5.1/2 A string literal is an lvalue; all other // literals are rvalues. ASSERT_LVALUE("foo"); ASSERT_RVALUE(1); ASSERT_RVALUE(1.2); ASSERT_RVALUE(10UL); } void expr_prim_3() { // 5.1/3: The keyword "this" names a pointer to the object for // which a nonstatic member function (9.3.2) is invoked. ...The // expression is an rvalue. struct ThisTest { void f() { ASSERT_RVALUE(this); } }; } extern int variable; void Function(); struct BaseClass { virtual ~BaseClass(); int BaseNonstaticMemberFunction(); static int BaseStaticMemberFunction(); int baseDataMember; }; struct Class : BaseClass { static void function(); static int variable; template <class T> struct NestedClassTemplate {}; template <class T> static int& NestedFuncTemplate() { return variable; } // expected-note{{possible target for call}} template <class T> int& NestedMemfunTemplate() { return variable; } int operator*() const; template <class T> int operator+(T) const; int NonstaticMemberFunction(); static int StaticMemberFunction(); int dataMember; int& referenceDataMember; static int& staticReferenceDataMember; static int staticNonreferenceDataMember; enum Enum { Enumerator }; operator long() const; Class(); Class(int,int); void expr_prim_4() { // 5.1/4: The operator :: followed by an identifier, a // qualified-id, or an operator-function-id is a primary- // expression. ...The result is an lvalue if the entity is // a function or variable. ASSERT_LVALUE(::Function); // identifier: function ASSERT_LVALUE(::variable); // identifier: variable // the only qualified-id form that can start without "::" (and thus // be legal after "::" ) is // // ::<sub>opt</sub> nested-name-specifier template<sub>opt</sub> unqualified-id ASSERT_LVALUE(::Class::function); // qualified-id: function ASSERT_LVALUE(::Class::variable); // qualified-id: variable // The standard doesn't give a clear answer about whether these // should really be lvalues or rvalues without some surrounding // context that forces them to be interpreted as naming a // particular function template specialization (that situation // doesn't come up in legal pure C++ programs). This language // extension simply rejects them as requiring additional context __is_lvalue_expr(::Class::NestedFuncTemplate); // qualified-id: template \ // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} __is_lvalue_expr(::Class::NestedMemfunTemplate); // qualified-id: template \ // expected-error{{reference to non-static member function must be called}} __is_lvalue_expr(::Class::operator+); // operator-function-id: template \ // expected-error{{reference to non-static member function must be called}} //ASSERT_RVALUE(::Class::operator*); // operator-function-id: member function } void expr_prim_7() { // expr.prim/7 An identifier is an id-expression provided it has been // suitably declared (clause 7). [Note: ... ] The type of the // expression is the type of the identifier. The result is the // entity denoted by the identifier. The result is an lvalue if // the entity is a function, variable, or data member... (cont'd) ASSERT_LVALUE(Function); // identifier: function ASSERT_LVALUE(StaticMemberFunction); // identifier: function ASSERT_LVALUE(variable); // identifier: variable ASSERT_LVALUE(dataMember); // identifier: data member //ASSERT_RVALUE(NonstaticMemberFunction); // identifier: member function // (cont'd)...A nested-name-specifier that names a class, // optionally followed by the keyword template (14.2), and then // followed by the name of a member of either that class (9.2) or // one of its base classes... is a qualified-id... The result is // the member. The type of the result is the type of the // member. The result is an lvalue if the member is a static // member function or a data member. ASSERT_LVALUE(Class::dataMember); ASSERT_LVALUE(Class::StaticMemberFunction); //ASSERT_RVALUE(Class::NonstaticMemberFunction); // identifier: member function ASSERT_LVALUE(Class::baseDataMember); ASSERT_LVALUE(Class::BaseStaticMemberFunction); //ASSERT_RVALUE(Class::BaseNonstaticMemberFunction); // identifier: member function } }; void expr_call_10() { // expr.call/10: A function call is an lvalue if and only if the // result type is a reference. This statement is partially // redundant with basic.lval/5 basic_lval_5(); ASSERT_LVALUE(ReturnIntReference()); ASSERT_LVALUE(ReturnEnumReference()); } namespace Namespace { int x; void function(); } void expr_prim_8() { // expr.prim/8 A nested-name-specifier that names a namespace // (7.3), followed by the name of a member of that namespace (or // the name of a member of a namespace made visible by a // using-directive ) is a qualified-id; 3.4.3.2 describes name // lookup for namespace members that appear in qualified-ids. The // result is the member. The type of the result is the type of the // member. The result is an lvalue if the member is a function or // a variable. ASSERT_LVALUE(Namespace::x); ASSERT_LVALUE(Namespace::function); } void expr_sub_1(int* pointer) { // expr.sub/1 A postfix expression followed by an expression in // square brackets is a postfix expression. One of the expressions // shall have the type "pointer to T" and the other shall have // enumeration or integral type. The result is an lvalue of type // "T." ASSERT_LVALUE(pointer[1]); // The expression E1[E2] is identical (by definition) to *((E1)+(E2)). ASSERT_LVALUE(*(pointer+1)); } void expr_type_conv_1() { // expr.type.conv/1 A simple-type-specifier (7.1.5) followed by a // parenthesized expression-list constructs a value of the specified // type given the expression list. ... If the expression list // specifies more than a single value, the type shall be a class with // a suitably declared constructor (8.5, 12.1), and the expression // T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, // x2, ...); for some invented temporary variable t, with the result // being the value of t as an rvalue. ASSERT_RVALUE(Class(2,2)); } void expr_type_conv_2() { // expr.type.conv/2 The expression T(), where T is a // simple-type-specifier (7.1.5.2) for a non-array complete object // type or the (possibly cv-qualified) void type, creates an // rvalue of the specified type, ASSERT_RVALUE(int()); ASSERT_RVALUE(Class()); ASSERT_RVALUE(void()); } void expr_ref_4() { // Applies to expressions of the form E1.E2 // If E2 is declared to have type "reference to T", then E1.E2 is // an lvalue;.... Otherwise, one of the following rules applies. ASSERT_LVALUE(Class().staticReferenceDataMember); ASSERT_LVALUE(Class().referenceDataMember); // - If E2 is a static data member, and the type of E2 is T, then // E1.E2 is an lvalue; ... ASSERT_LVALUE(Class().staticNonreferenceDataMember); ASSERT_LVALUE(Class().staticReferenceDataMember); // - If E2 is a non-static data member, ... If E1 is an lvalue, // then E1.E2 is an lvalue... Class lvalue; ASSERT_LVALUE(lvalue.dataMember); ASSERT_RVALUE(Class().dataMember); // - If E1.E2 refers to a static member function, ... then E1.E2 // is an lvalue ASSERT_LVALUE(Class().StaticMemberFunction); // - Otherwise, if E1.E2 refers to a non-static member function, // then E1.E2 is not an lvalue. //ASSERT_RVALUE(Class().NonstaticMemberFunction); // - If E2 is a member enumerator, and the type of E2 is T, the // expression E1.E2 is not an lvalue. The type of E1.E2 is T. ASSERT_RVALUE(Class().Enumerator); ASSERT_RVALUE(lvalue.Enumerator); } void expr_post_incr_1(int x) { // expr.post.incr/1 The value obtained by applying a postfix ++ is // the value that the operand had before applying the // operator... The result is an rvalue. ASSERT_RVALUE(x++); } void expr_dynamic_cast_2() { // expr.dynamic.cast/2: If T is a pointer type, v shall be an // rvalue of a pointer to complete class type, and the result is // an rvalue of type T. Class instance; ASSERT_RVALUE(dynamic_cast<Class*>(&instance)); // If T is a reference type, v shall be an // lvalue of a complete class type, and the result is an lvalue of // the type referred to by T. ASSERT_LVALUE(dynamic_cast<Class&>(instance)); } void expr_dynamic_cast_5() { // expr.dynamic.cast/5: If T is "reference to cv1 B" and v has type // "cv2 D" such that B is a base class of D, the result is an // lvalue for the unique B sub-object of the D object referred // to by v. typedef BaseClass B; typedef Class D; D object; ASSERT_LVALUE(dynamic_cast<B&>(object)); } // expr.dynamic.cast/8: The run-time check logically executes as follows: // // - If, in the most derived object pointed (referred) to by v, v // points (refers) to a public base class subobject of a T object, and // if only one object of type T is derived from the sub-object pointed // (referred) to by v, the result is a pointer (an lvalue referring) // to that T object. // // - Otherwise, if v points (refers) to a public base class sub-object // of the most derived object, and the type of the most derived object // has a base class, of type T, that is unambiguous and public, the // result is a pointer (an lvalue referring) to the T sub-object of // the most derived object. // // The mention of "lvalue" in the text above appears to be a // defect that is being corrected by the response to UK65 (see // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2841.html). #if 0 void expr_typeid_1() { // expr.typeid/1: The result of a typeid expression is an lvalue... ASSERT_LVALUE(typeid(1)); } #endif void expr_static_cast_1(int x) { // expr.static.cast/1: The result of the expression // static_cast<T>(v) is the result of converting the expression v // to type T. If T is a reference type, the result is an lvalue; // otherwise, the result is an rvalue. ASSERT_LVALUE(static_cast<int&>(x)); ASSERT_RVALUE(static_cast<int>(x)); } void expr_reinterpret_cast_1() { // expr.reinterpret.cast/1: The result of the expression // reinterpret_cast<T>(v) is the result of converting the // expression v to type T. If T is a reference type, the result is // an lvalue; otherwise, the result is an rvalue ASSERT_RVALUE(reinterpret_cast<int*>(0)); char const v = 0; ASSERT_LVALUE(reinterpret_cast<char const&>(v)); } void expr_unary_op_1(int* pointer, struct incomplete* pointerToIncompleteType) { // expr.unary.op/1: The unary * operator performs indirection: the // expression to which it is applied shall be a pointer to an // object type, or a pointer to a function type and the result is // an lvalue referring to the object or function to which the // expression points. ASSERT_LVALUE(*pointer); ASSERT_LVALUE(*Function); // [Note: a pointer to an incomplete type // (other than cv void ) can be dereferenced. ] ASSERT_LVALUE(*pointerToIncompleteType); } void expr_pre_incr_1(int operand) { // expr.pre.incr/1: The operand of prefix ++ ... shall be a // modifiable lvalue.... The value is the new value of the // operand; it is an lvalue. ASSERT_LVALUE(++operand); } void expr_cast_1(int x) { // expr.cast/1: The result of the expression (T) cast-expression // is of type T. The result is an lvalue if T is a reference type, // otherwise the result is an rvalue. ASSERT_LVALUE((void(&)())expr_cast_1); ASSERT_LVALUE((int&)x); ASSERT_RVALUE((void(*)())expr_cast_1); ASSERT_RVALUE((int)x); } void expr_mptr_oper() { // expr.mptr.oper/6: The result of a .* expression is an lvalue // only if its first operand is an lvalue and its second operand // is a pointer to data member... (cont'd) typedef Class MakeRValue; ASSERT_RVALUE(MakeRValue().*(&Class::dataMember)); //ASSERT_RVALUE(MakeRValue().*(&Class::NonstaticMemberFunction)); Class lvalue; ASSERT_LVALUE(lvalue.*(&Class::dataMember)); //ASSERT_RVALUE(lvalue.*(&Class::NonstaticMemberFunction)); // (cont'd)...The result of an ->* expression is an lvalue only // if its second operand is a pointer to data member. If the // second operand is the null pointer to member value (4.11), the // behavior is undefined. ASSERT_LVALUE((&lvalue)->*(&Class::dataMember)); //ASSERT_RVALUE((&lvalue)->*(&Class::NonstaticMemberFunction)); } void expr_cond(bool cond) { // 5.16 Conditional operator [expr.cond] // // 2 If either the second or the third operand has type (possibly // cv-qualified) void, then the lvalue-to-rvalue (4.1), // array-to-pointer (4.2), and function-to-pointer (4.3) standard // conversions are performed on the second and third operands, and one // of the following shall hold: // // - The second or the third operand (but not both) is a // throw-expression (15.1); the result is of the type of the other and // is an rvalue. Class classLvalue; ASSERT_RVALUE(cond ? throw 1 : (void)0); ASSERT_RVALUE(cond ? (void)0 : throw 1); ASSERT_RVALUE(cond ? throw 1 : classLvalue); ASSERT_RVALUE(cond ? classLvalue : throw 1); // - Both the second and the third operands have type void; the result // is of type void and is an rvalue. [Note: this includes the case // where both operands are throw-expressions. ] ASSERT_RVALUE(cond ? (void)1 : (void)0); ASSERT_RVALUE(cond ? throw 1 : throw 0); // expr.cond/4: If the second and third operands are lvalues and // have the same type, the result is of that type and is an // lvalue. ASSERT_LVALUE(cond ? classLvalue : classLvalue); int intLvalue = 0; ASSERT_LVALUE(cond ? intLvalue : intLvalue); // expr.cond/5:Otherwise, the result is an rvalue. typedef Class MakeRValue; ASSERT_RVALUE(cond ? MakeRValue() : classLvalue); ASSERT_RVALUE(cond ? classLvalue : MakeRValue()); ASSERT_RVALUE(cond ? MakeRValue() : MakeRValue()); ASSERT_RVALUE(cond ? classLvalue : intLvalue); ASSERT_RVALUE(cond ? intLvalue : int()); } void expr_ass_1(int x) { // expr.ass/1: There are several assignment operators, all of // which group right-to-left. All require a modifiable lvalue as // their left operand, and the type of an assignment expression is // that of its left operand. The result of the assignment // operation is the value stored in the left operand after the // assignment has taken place; the result is an lvalue. ASSERT_LVALUE(x = 1); ASSERT_LVALUE(x += 1); ASSERT_LVALUE(x -= 1); ASSERT_LVALUE(x *= 1); ASSERT_LVALUE(x /= 1); ASSERT_LVALUE(x %= 1); ASSERT_LVALUE(x ^= 1); ASSERT_LVALUE(x &= 1); ASSERT_LVALUE(x |= 1); } void expr_comma(int x) { // expr.comma: A pair of expressions separated by a comma is // evaluated left-to-right and the value of the left expression is // discarded... result is an lvalue if its right operand is. // Can't use the ASSERT_XXXX macros without adding parens around // the comma expression. static_assert(__is_lvalue_expr(x,x), "expected an lvalue"); static_assert(__is_rvalue_expr(x,1), "expected an rvalue"); static_assert(__is_lvalue_expr(1,x), "expected an lvalue"); static_assert(__is_rvalue_expr(1,1), "expected an rvalue"); } #if 0 template<typename T> void f(); // FIXME These currently fail void expr_fun_lvalue() { ASSERT_LVALUE(&f<int>); } void expr_fun_rvalue() { ASSERT_RVALUE(f<int>); } #endif template <int NonTypeNonReferenceParameter, int& NonTypeReferenceParameter> void check_temp_param_6() { ASSERT_RVALUE(NonTypeNonReferenceParameter); ASSERT_LVALUE(NonTypeReferenceParameter); } int AnInt = 0; void temp_param_6() { check_temp_param_6<3,AnInt>(); }