[cfe-commits] PATCH [1/2]: Implementation of Embarcadero expression traits
Douglas Gregor
dgregor at apple.com
Mon Feb 21 17:43:49 PST 2011
On Feb 18, 2011, at 2:53 PM, John Wiegley wrote:
> Patch authored by David Abrahams.
>
> These two expression traits (__is_lvalue_expr, __is_rvalue_expr) are used for
> parsing code that employs certain features of the Embarcadero C++ compiler.
>> From 17936dde6e7160758a9edd64a4a734cb51603a81 Mon Sep 17 00:00:00 2001
> From: Dave Abrahams <dave at boostpro.com>
> Date: Sat, 8 Jan 2011 08:52:37 -0500
> Subject: [PATCH 1/2] Implementation of expression traits (__is_lvalue_expr/__is_rvalue_expr)
>
> ---
> include/clang/AST/EvaluatedExprVisitor.h | 1 +
> include/clang/AST/ExprCXX.h | 54 +++
> include/clang/AST/RecursiveASTVisitor.h | 4 +
> include/clang/Basic/ExpressionTraits.h | 25 ++
> include/clang/Basic/StmtNodes.td | 1 +
> include/clang/Basic/TokenKinds.def | 4 +
> include/clang/Parse/Parser.h | 4 +
> include/clang/Sema/Sema.h | 13 +
> include/clang/Serialization/ASTBitCodes.h | 1 +
> lib/AST/ExprCXX.cpp | 8 +
> lib/AST/ExprClassification.cpp | 1 +
> lib/AST/ExprConstant.cpp | 5 +
> lib/AST/ItaniumMangle.cpp | 1 +
> lib/AST/StmtPrinter.cpp | 15 +
> lib/AST/StmtProfile.cpp | 6 +
> lib/CodeGen/CGExprScalar.cpp | 4 +
> lib/Parse/ParseExpr.cpp | 8 +
> lib/Parse/ParseExprCXX.cpp | 30 ++
> lib/Sema/SemaExprCXX.cpp | 39 ++
> lib/Sema/TreeTransform.h | 29 ++
> lib/Serialization/ASTReaderStmt.cpp | 4 +
> lib/StaticAnalyzer/Checkers/ExprEngine.cpp | 1 +
> test/SemaCXX/expression-traits.cpp | 609 ++++++++++++++++++++++++++++
> tools/libclang/CIndex.cpp | 5 +
> tools/libclang/CXCursor.cpp | 1 +
> 25 files changed, 873 insertions(+), 0 deletions(-)
> create mode 100644 include/clang/Basic/ExpressionTraits.h
> create mode 100644 test/SemaCXX/expression-traits.cpp
>
> diff --git a/include/clang/AST/EvaluatedExprVisitor.h b/include/clang/AST/EvaluatedExprVisitor.h
> index be606e0..061f0ac 100644
> --- a/include/clang/AST/EvaluatedExprVisitor.h
> +++ b/include/clang/AST/EvaluatedExprVisitor.h
> @@ -38,6 +38,7 @@ public:
> void VisitDeclRefExpr(DeclRefExpr *E) { }
> void VisitOffsetOfExpr(OffsetOfExpr *E) { }
> void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { }
> + void VisitExpressionTraitExpr(ExpressionTraitExpr *E) { }
> void VisitBlockExpr(BlockExpr *E) { }
> void VisitCXXUuidofExpr(CXXUuidofExpr *E) { }
> void VisitCXXNoexceptExpr(CXXNoexceptExpr *E) { }
> diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
> index c0e35e3..26f0495 100644
> --- a/include/clang/AST/ExprCXX.h
> +++ b/include/clang/AST/ExprCXX.h
> @@ -15,6 +15,7 @@
> #define LLVM_CLANG_AST_EXPRCXX_H
>
> #include "clang/Basic/TypeTraits.h"
> +#include "clang/Basic/ExpressionTraits.h"
> #include "clang/AST/Expr.h"
> #include "clang/AST/UnresolvedSet.h"
> #include "clang/AST/TemplateBase.h"
> @@ -1559,6 +1560,59 @@ public:
> friend class ASTStmtReader;
> };
>
> +/// ExpressionTraitExpr - An expression trait intrinsic
> +/// Example:
> +/// __is_lvalue_expr(std::cout) == true
> +/// __is_lvalue_expr(1) == false
> +class ExpressionTraitExpr : public Expr {
> + /// ET - The trait. A ExpressionTrait enum in MSVC compat unsigned.
> + unsigned ET : 31;
> + /// The value of the type trait. Unspecified if dependent.
> + bool Value : 1;
> +
> + /// Loc - The location of the type trait keyword.
> + SourceLocation Loc;
> +
> + /// RParen - The location of the closing paren.
> + SourceLocation RParen;
> +
> + Expr* QueriedExpression;
> +public:
> + ExpressionTraitExpr(SourceLocation loc, ExpressionTrait et,
> + Expr *queried, bool value,
> + SourceLocation rparen, QualType resultType)
> + : Expr(ExpressionTraitExprClass, resultType, VK_RValue, OK_Ordinary,
> + false, // Not type-dependent
> + // Value-dependent if the argument is type-dependent.
> + queried->isTypeDependent(),
> + queried->containsUnexpandedParameterPack()),
> + ET(et), Value(value), Loc(loc), RParen(rparen), QueriedExpression(queried) { }
> +
> + explicit ExpressionTraitExpr(EmptyShell Empty)
> + : Expr(ExpressionTraitExprClass, Empty), ET(0), Value(false),
> + QueriedExpression() { }
> +
> + virtual SourceRange getSourceRange() const { return SourceRange(Loc, RParen);}
getSourceRange() should not be "virtual". We recently devirtualized Stmt and Expr nodes.
> + ExpressionTrait getTrait() const { return static_cast<ExpressionTrait>(ET); }
> +
> + Expr *getQueriedExpression() const { return QueriedExpression; }
> +
> + bool getValue() const { return Value; }
> +
> + static bool classof(const Stmt *T) {
> + return T->getStmtClass() == ExpressionTraitExprClass;
> + }
> + static bool classof(const ExpressionTraitExpr *) { return true; }
> +
> + // Iterators
> + virtual child_iterator child_begin();
> + virtual child_iterator child_end();
> +
> + friend class ASTStmtReader;
> +};
> +
> +
> /// \brief A reference to an overloaded function set, either an
> /// \t UnresolvedLookupExpr or an \t UnresolvedMemberExpr.
> class OverloadExpr : public Expr {
> diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
> index 0a98bea..f24b978 100644
> --- a/include/clang/AST/RecursiveASTVisitor.h
> +++ b/include/clang/AST/RecursiveASTVisitor.h
> @@ -1769,6 +1769,10 @@ DEF_TRAVERSE_STMT(BinaryTypeTraitExpr, {
> TRY_TO(TraverseTypeLoc(S->getRhsTypeSourceInfo()->getTypeLoc()));
> })
>
> +DEF_TRAVERSE_STMT(ExpressionTraitExpr, {
> + TRY_TO(TraverseStmt(S->getQueriedExpression()));
> + })
> +
> DEF_TRAVERSE_STMT(VAArgExpr, {
> // The child-iterator will pick up the expression argument.
> TRY_TO(TraverseTypeLoc(S->getWrittenTypeInfo()->getTypeLoc()));
> diff --git a/include/clang/Basic/ExpressionTraits.h b/include/clang/Basic/ExpressionTraits.h
> new file mode 100644
> index 0000000..403a59a
> --- /dev/null
> +++ b/include/clang/Basic/ExpressionTraits.h
> @@ -0,0 +1,25 @@
> +//===--- ExpressionTraits.h - C++ Expression Traits Support Enumerations ----*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file defines enumerations for expression traits intrinsics.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_EXPRESSIONTRAITS_H
> +#define LLVM_CLANG_EXPRESSIONTRAITS_H
> +
> +namespace clang {
> +
> + enum ExpressionTrait {
> + ET_IsLValueExpr,
> + ET_IsRValueExpr
> + };
> +}
> +
> +#endif
> diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
> index 58db87b..42a9514 100644
> --- a/include/clang/Basic/StmtNodes.td
> +++ b/include/clang/Basic/StmtNodes.td
> @@ -100,6 +100,7 @@ def CXXDeleteExpr : DStmt<Expr>;
> def CXXPseudoDestructorExpr : DStmt<Expr>;
> def UnaryTypeTraitExpr : DStmt<Expr>;
> def BinaryTypeTraitExpr : DStmt<Expr>;
> +def ExpressionTraitExpr : DStmt<Expr>;
> def DependentScopeDeclRefExpr : DStmt<Expr>;
> def CXXConstructExpr : DStmt<Expr>;
> def CXXBindTemporaryExpr : DStmt<Expr>;
> diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def
> index 5c21cd6..891bce2 100644
> --- a/include/clang/Basic/TokenKinds.def
> +++ b/include/clang/Basic/TokenKinds.def
> @@ -328,6 +328,10 @@ KEYWORD(__is_union , KEYCXX)
> // Tentative name - there's no implementation of std::is_literal_type yet.
> KEYWORD(__is_literal , KEYCXX)
>
> +// Embarcadero Expression Traits
> +KEYWORD(__is_lvalue_expr , KEYCXX)
> +KEYWORD(__is_rvalue_expr , KEYCXX)
> +
> // Apple Extension.
> KEYWORD(__private_extern__ , KEYALL)
>
> diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
> index f1cee27..506ec84 100644
> --- a/include/clang/Parse/Parser.h
> +++ b/include/clang/Parse/Parser.h
> @@ -1715,6 +1715,10 @@ private:
> // GNU G++: Type Traits [Type-Traits.html in the GCC manual]
> ExprResult ParseUnaryTypeTrait();
> ExprResult ParseBinaryTypeTrait();
> +
> + //===--------------------------------------------------------------------===//
> + // Embarcadero: Expression Traits
> + ExprResult ParseExpressionTrait();
>
> //===--------------------------------------------------------------------===//
> // Preprocessor code-completion pass-through
> diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
> index 8c48fc5..b9d0c6a 100644
> --- a/include/clang/Sema/Sema.h
> +++ b/include/clang/Sema/Sema.h
> @@ -27,6 +27,7 @@
> #include "clang/Basic/Specifiers.h"
> #include "clang/Basic/TemplateKinds.h"
> #include "clang/Basic/TypeTraits.h"
> +#include "clang/Basic/ExpressionTraits.h"
> #include "llvm/ADT/OwningPtr.h"
> #include "llvm/ADT/SmallPtrSet.h"
> #include "llvm/ADT/SmallVector.h"
> @@ -2393,6 +2394,18 @@ public:
> TypeSourceInfo *RhsT,
> SourceLocation RParen);
>
> + /// ActOnExpressionTrait - Parsed one of the unary type trait support
> + /// pseudo-functions.
> + ExprResult ActOnExpressionTrait(ExpressionTrait OET,
> + SourceLocation KWLoc,
> + Expr *Queried,
> + SourceLocation RParen);
> +
> + ExprResult BuildExpressionTrait(ExpressionTrait OET,
> + SourceLocation KWLoc,
> + Expr *Queried,
> + SourceLocation RParen);
> +
> ExprResult ActOnStartCXXMemberReference(Scope *S,
> Expr *Base,
> SourceLocation OpLoc,
> diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
> index 5c9a308..645c790 100644
> --- a/include/clang/Serialization/ASTBitCodes.h
> +++ b/include/clang/Serialization/ASTBitCodes.h
> @@ -939,6 +939,7 @@ namespace clang {
> EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr
>
> EXPR_CXX_UNARY_TYPE_TRAIT, // UnaryTypeTraitExpr
> + EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr
> EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr
>
> EXPR_OPAQUE_VALUE, // OpaqueValueExpr
> diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
> index a11d05a..85a5c90 100644
> --- a/lib/AST/ExprCXX.cpp
> +++ b/lib/AST/ExprCXX.cpp
> @@ -377,6 +377,14 @@ Stmt::child_iterator BinaryTypeTraitExpr::child_end() {
> return child_iterator();
> }
>
> +//ExpressionTraitExpr
> +Stmt::child_iterator ExpressionTraitExpr::child_begin() {
> + return child_iterator();
> +}
> +Stmt::child_iterator ExpressionTraitExpr::child_end() {
> + return child_iterator();
> +}
These functions should return child iterators for &QueriedExpression and &QueriedExpression +1, respectively.
> // DependentScopeDeclRefExpr
> DependentScopeDeclRefExpr::DependentScopeDeclRefExpr(QualType T,
> NestedNameSpecifier *Qualifier,
> diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
> index c9f4fa8..9dd1bf0 100644
> --- a/lib/AST/ExprClassification.cpp
> +++ b/lib/AST/ExprClassification.cpp
> @@ -146,6 +146,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
> case Expr::CXXScalarValueInitExprClass:
> case Expr::UnaryTypeTraitExprClass:
> case Expr::BinaryTypeTraitExprClass:
> + case Expr::ExpressionTraitExprClass:
> case Expr::ObjCSelectorExprClass:
> case Expr::ObjCProtocolExprClass:
> case Expr::ObjCStringLiteralClass:
> diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
> index 0a7a9d2..9e1a786 100644
> --- a/lib/AST/ExprConstant.cpp
> +++ b/lib/AST/ExprConstant.cpp
> @@ -958,6 +958,10 @@ public:
> return Success(E->getValue(), E);
> }
>
> + bool VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
> + return Success(E->getValue(), E);
> + }
> +
> bool VisitChooseExpr(const ChooseExpr *E) {
> return Visit(E->getChosenSubExpr(Info.Ctx));
> }
> @@ -2646,6 +2650,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
> case Expr::CXXScalarValueInitExprClass:
> case Expr::UnaryTypeTraitExprClass:
> case Expr::BinaryTypeTraitExprClass:
> + case Expr::ExpressionTraitExprClass:
> case Expr::CXXNoexceptExprClass:
> return NoDiag();
> case Expr::CallExprClass:
> diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
> index a84df12..b8cd41a 100644
> --- a/lib/AST/ItaniumMangle.cpp
> +++ b/lib/AST/ItaniumMangle.cpp
> @@ -1729,6 +1729,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
> case Expr::StmtExprClass:
> case Expr::UnaryTypeTraitExprClass:
> case Expr::BinaryTypeTraitExprClass:
> + case Expr::ExpressionTraitExprClass:
> case Expr::VAArgExprClass:
> case Expr::CXXUuidofExprClass:
> case Expr::CXXNoexceptExprClass: {
> diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
> index 2ed2c92..4ec3aa2 100644
> --- a/lib/AST/StmtPrinter.cpp
> +++ b/lib/AST/StmtPrinter.cpp
> @@ -1237,6 +1237,15 @@ static const char *getTypeTraitName(BinaryTypeTrait BTT) {
> return "";
> }
>
> +static const char *getExpressionTraitName(ExpressionTrait ET) {
> + switch (ET) {
> + default: llvm_unreachable("Unknown expression trait");
> + case ET_IsLValueExpr: return "__is_lvalue_expr";
> + case ET_IsRValueExpr: return "__is_rvalue_expr";
> + }
> + return "";
> +}
> +
> void StmtPrinter::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) {
> OS << getTypeTraitName(E->getTrait()) << "("
> << E->getQueriedType().getAsString(Policy) << ")";
> @@ -1248,6 +1257,12 @@ void StmtPrinter::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
> << E->getRhsType().getAsString(Policy) << ")";
> }
>
> +void StmtPrinter::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
> + OS << getExpressionTraitName(E->getTrait()) << "(";
> + PrintExpr(E->getQueriedExpression());
> + OS << ")";
> +}
> +
> void StmtPrinter::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
> OS << "noexcept(";
> PrintExpr(E->getOperand());
> diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
> index 842a2d9..044332f 100644
> --- a/lib/AST/StmtProfile.cpp
> +++ b/lib/AST/StmtProfile.cpp
> @@ -779,6 +779,12 @@ void StmtProfiler::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *S) {
> VisitType(S->getRhsType());
> }
>
> +void StmtProfiler::VisitExpressionTraitExpr(ExpressionTraitExpr *S) {
> + VisitExpr(S);
> + ID.AddInteger(S->getTrait());
> + VisitExpr(S->getQueriedExpression());
> +}
> +
> void
> StmtProfiler::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) {
> VisitExpr(S);
> diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
> index e309e36..b61192e 100644
> --- a/lib/CodeGen/CGExprScalar.cpp
> +++ b/lib/CodeGen/CGExprScalar.cpp
> @@ -362,6 +362,10 @@ public:
> return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
> }
>
> + Value *VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
> + return llvm::ConstantInt::get(Builder.getInt1Ty(), E->getValue());
> + }
> +
> Value *VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E) {
> // C++ [expr.pseudo]p1:
> // The result shall only be used as the operand for the function call
> diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
> index 5928871..2e6fc00 100644
> --- a/lib/Parse/ParseExpr.cpp
> +++ b/lib/Parse/ParseExpr.cpp
> @@ -541,6 +541,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
> /// [GNU] '__is_base_of'
> /// [MS] '__is_convertible_to'
> ///
> +/// [Embarcadero] expression-trait:
> +/// '__is_lvalue_expr'
> +/// '__is_rvalue_expr'
> +///
> ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
> bool isAddressOfOperand,
> bool &NotCastExpr,
> @@ -992,6 +996,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
> case tok::kw___is_convertible_to:
> return ParseBinaryTypeTrait();
>
> + case tok::kw___is_lvalue_expr:
> + case tok::kw___is_rvalue_expr:
> + return ParseExpressionTrait();
> +
> case tok::at: {
> SourceLocation AtLoc = ConsumeToken();
> return ParseObjCAtExpression(AtLoc);
> diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
> index e769eca..a854a74 100644
> --- a/lib/Parse/ParseExprCXX.cpp
> +++ b/lib/Parse/ParseExprCXX.cpp
> @@ -1832,6 +1832,14 @@ static BinaryTypeTrait BinaryTypeTraitFromTokKind(tok::TokenKind kind) {
> }
> }
>
> +static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) {
> + switch(kind) {
> + default: assert(false && "Not a known unary expression trait.");
> + case tok::kw___is_lvalue_expr: return ET_IsLValueExpr;
> + case tok::kw___is_rvalue_expr: return ET_IsRValueExpr;
> + }
> +}
> +
> /// ParseUnaryTypeTrait - Parse the built-in unary type-trait
> /// pseudo-functions that allow implementation of the TR1/C++0x type traits
> /// templates.
> @@ -1897,6 +1905,28 @@ ExprResult Parser::ParseBinaryTypeTrait() {
> return Actions.ActOnBinaryTypeTrait(BTT, Loc, LhsTy.get(), RhsTy.get(), RParen);
> }
>
> +/// ParseExpressionTrait - Parse built-in expression-trait
> +/// pseudo-functions like __is_lvalue_expr( xxx ).
> +///
> +/// primary-expression:
> +/// [EMBT] expression-trait '(' expression ')'
> +///
> +ExprResult Parser::ParseExpressionTrait() {
> + ExpressionTrait ET = ExpressionTraitFromTokKind(Tok.getKind());
> + SourceLocation Loc = ConsumeToken();
> +
> + SourceLocation LParen = Tok.getLocation();
> + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen))
> + return ExprError();
> +
> + ExprResult Expr = ParseExpression();
> +
> + SourceLocation RParen = MatchRHSPunctuation(tok::r_paren, LParen);
> +
> + return Actions.ActOnExpressionTrait(ET, Loc, Expr.get(), RParen);
> +}
> +
> +
> /// ParseCXXAmbiguousParenExpression - We have parsed the left paren of a
> /// parenthesized ambiguous type-id. This uses tentative parsing to disambiguate
> /// based on the context past the parens.
> diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
> index f886e17..e3eb129 100644
> --- a/lib/Sema/SemaExprCXX.cpp
> +++ b/lib/Sema/SemaExprCXX.cpp
> @@ -2636,6 +2636,45 @@ ExprResult Sema::BuildBinaryTypeTrait(BinaryTypeTrait BTT,
> ResultType));
> }
>
> +ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
> + SourceLocation KWLoc,
> + Expr* Queried,
> + SourceLocation RParen) {
> + // If error parsing the expression, ignore.
> + if (!Queried)
> + return ExprError();
> +
> + ExprResult Result
> + = BuildExpressionTrait(ET, KWLoc, Queried, RParen);
> +
> + return move(Result);
> +}
> +
> +ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET,
> + SourceLocation KWLoc,
> + Expr* Queried,
> + SourceLocation RParen) {
> + if (Queried->isTypeDependent()) {
> + // Delay type-checking for type-dependent expressions.
> + } else if (Queried->getType()->isPlaceholderType()) {
> + ExprResult PE = CheckPlaceholderExpr(Queried, KWLoc);
This makes me wonder if this code correctly handles code like
template<typename T> void f();
__is_lvalue_expr(&f<int>);
or
__is_rvalue_expr(f<int>);
?
> + if (PE.isInvalid()) return ExprError();
> + return BuildExpressionTrait(ET, KWLoc, PE.take(), RParen);
> + }
> +
> + bool value = false;
LLVM coding conventions suggest capitalizing "value" to "Value", here.
> + switch (ET) {
> + default: llvm_unreachable("Unknown or unimplemented expression trait");
> + case ET_IsLValueExpr: value = Queried->isLValue(); break;
> + case ET_IsRValueExpr: value = Queried->isRValue(); break;
> + }
> +
> + // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
> + return Owned(
> + new (Context) ExpressionTraitExpr(
> + KWLoc, ET, Queried, value, RParen, Context.BoolTy));
> +}
> +
> QualType Sema::CheckPointerToMemberOperands(Expr *&lex, Expr *&rex,
> ExprValueKind &VK,
> SourceLocation Loc,
> diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
> index 2a82869..0f52aaa 100644
> --- a/lib/Sema/TreeTransform.h
> +++ b/lib/Sema/TreeTransform.h
> @@ -1872,6 +1872,17 @@ public:
> return getSema().BuildBinaryTypeTrait(Trait, StartLoc, LhsT, RhsT, RParenLoc);
> }
>
> + /// \brief Build a new expression trait expression.
> + ///
> + /// By default, performs semantic analysis to build the new expression.
> + /// Subclasses may override this routine to provide different behavior.
> + ExprResult RebuildExpressionTrait(ExpressionTrait Trait,
> + SourceLocation StartLoc,
> + Expr *Queried,
> + SourceLocation RParenLoc) {
> + return getSema().BuildExpressionTrait(Trait, StartLoc, Queried, RParenLoc);
> + }
> +
> /// \brief Build a new (previously unresolved) declaration reference
> /// expression.
> ///
> @@ -6519,6 +6530,24 @@ TreeTransform<Derived>::TransformBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
>
> template<typename Derived>
> ExprResult
> +TreeTransform<Derived>::TransformExpressionTraitExpr(ExpressionTraitExpr *E) {
> + ExprResult SubExpr;
> + {
> + EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated);
> + SubExpr = getDerived().TransformExpr(E->getQueriedExpression());
> + if (SubExpr.isInvalid())
> + return ExprError();
> +
> + if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getQueriedExpression())
> + return SemaRef.Owned(E);
> + }
> +
> + return getDerived().RebuildExpressionTrait(
> + E->getTrait(), E->getLocStart(), SubExpr.get(), E->getLocEnd());
> +}
> +
> +template<typename Derived>
> +ExprResult
> TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
> DependentScopeDeclRefExpr *E) {
> NestedNameSpecifier *NNS
> diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
> index 8bd94b4..82719f5 100644
> --- a/lib/Serialization/ASTReaderStmt.cpp
> +++ b/lib/Serialization/ASTReaderStmt.cpp
> @@ -1853,6 +1853,10 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) {
> S = new (Context) BinaryTypeTraitExpr(Empty);
> break;
>
> + case EXPR_CXX_EXPRESSION_TRAIT:
> + S = new (Context) ExpressionTraitExpr(Empty);
> + break;
> +
> case EXPR_CXX_NOEXCEPT:
> S = new (Context) CXXNoexceptExpr(Empty);
> break;
> diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp
> index e2ad17e..5942955 100644
> --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp
> +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp
> @@ -833,6 +833,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
> case Stmt::DependentScopeDeclRefExprClass:
> case Stmt::UnaryTypeTraitExprClass:
> case Stmt::BinaryTypeTraitExprClass:
> + case Stmt::ExpressionTraitExprClass:
> case Stmt::UnresolvedLookupExprClass:
> case Stmt::UnresolvedMemberExprClass:
> case Stmt::CXXNoexceptExprClass:
> diff --git a/test/SemaCXX/expression-traits.cpp b/test/SemaCXX/expression-traits.cpp
> new file mode 100644
> index 0000000..051b19e
> --- /dev/null
> +++ b/test/SemaCXX/expression-traits.cpp
> @@ -0,0 +1,609 @@
> +// RUN: %clang_cc1 -fsyntax-only -verify %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 0
> +#include <typeinfo>
> +#endif
Why the #if 0 block?
> +#ifndef __GXX_EXPERIMENTAL_CXX0X__
How about
#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; }
> +
> + 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{{cannot resolve overloaded function from context}}
> +
> + __is_lvalue_expr(::Class::NestedMemfunTemplate); // qualified-id: template \
> + // expected-error{{cannot resolve overloaded function from context}}
> +
> + __is_lvalue_expr(::Class::operator+); // operator-function-id: template \
> + // expected-error{{cannot resolve overloaded function from context}}
> +
> + 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");
> +}
> +
> +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>();
> +}
> diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
> index 32e0039..b8eab67 100644
> --- a/tools/libclang/CIndex.cpp
> +++ b/tools/libclang/CIndex.cpp
> @@ -1636,6 +1636,7 @@ public:
> void VisitWhileStmt(WhileStmt *W);
> void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
> void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
> + void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
> void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U);
> void VisitVAArgExpr(VAArgExpr *E);
> void VisitSizeOfPackExpr(SizeOfPackExpr *E);
> @@ -1921,6 +1922,10 @@ void EnqueueVisitor::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
> AddTypeLoc(E->getLhsTypeSourceInfo());
> }
>
> +void EnqueueVisitor::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
> + EnqueueChildren(E);
> +}
> +
> void EnqueueVisitor::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U) {
> VisitOverloadExpr(U);
> if (!U->isImplicitAccess())
> diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
> index 7ae9f7d..da00eee 100644
> --- a/tools/libclang/CXCursor.cpp
> +++ b/tools/libclang/CXCursor.cpp
> @@ -149,6 +149,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent,
> case Stmt::UnresolvedLookupExprClass:
> case Stmt::UnaryTypeTraitExprClass:
> case Stmt::BinaryTypeTraitExprClass:
> + case Stmt::ExpressionTraitExprClass:
> case Stmt::DependentScopeDeclRefExprClass:
> case Stmt::CXXBindTemporaryExprClass:
> case Stmt::ExprWithCleanupsClass:
> --
> 1.7.4.1
- Doug
More information about the cfe-commits
mailing list