summaryrefslogtreecommitdiff
path: root/clang/test/SemaCXX/expression-traits.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/SemaCXX/expression-traits.cpp')
-rw-r--r--clang/test/SemaCXX/expression-traits.cpp620
1 files changed, 620 insertions, 0 deletions
diff --git a/clang/test/SemaCXX/expression-traits.cpp b/clang/test/SemaCXX/expression-traits.cpp
new file mode 100644
index 0000000..2767d4a
--- /dev/null
+++ b/clang/test/SemaCXX/expression-traits.cpp
@@ -0,0 +1,620 @@
+// 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>();
+}