forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			620 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
// 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; } // expected-note{{possible target for call}}
 | 
						|
 | 
						|
    int operator*() const;
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    int operator+(T) const; // expected-note{{possible target for call}}
 | 
						|
 | 
						|
    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, one of the following shall hold:
 | 
						|
    //
 | 
						|
    // - The second or the third operand (but not both) is a
 | 
						|
    // (possibly parenthesized) throw-expression (15.1); the result
 | 
						|
    // is of the type and value category of the other.
 | 
						|
 | 
						|
    Class classLvalue;
 | 
						|
    ASSERT_RVALUE(cond ? throw 1 : (void)0);
 | 
						|
    ASSERT_RVALUE(cond ? (void)0 : throw 1);
 | 
						|
    ASSERT_RVALUE(cond ? throw 1 : 0);
 | 
						|
    ASSERT_RVALUE(cond ? 0 : throw 1);
 | 
						|
    ASSERT_LVALUE(cond ? throw 1 : classLvalue);
 | 
						|
    ASSERT_LVALUE(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>();
 | 
						|
}
 |