t/clang/expr-traits
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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130122 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/EvaluatedExprVisitor.h b/include/clang/AST/EvaluatedExprVisitor.h
index 3bc62ed..bab1606 100644
--- a/include/clang/AST/EvaluatedExprVisitor.h
+++ b/include/clang/AST/EvaluatedExprVisitor.h
@@ -38,6 +38,7 @@
void VisitDeclRefExpr(DeclRefExpr *E) { }
void VisitOffsetOfExpr(OffsetOfExpr *E) { }
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *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 ed4661b..5aaeb8e 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"
@@ -1593,6 +1594,58 @@
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() { }
+
+ SourceRange getSourceRange() const { return SourceRange(Loc, RParen);}
+
+ 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
+ child_range children() { return child_range(); }
+
+ 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 0b3993d..93cc446 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -1849,6 +1849,10 @@
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 eaf4b85..de3fedb 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -104,6 +104,7 @@
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 ac8e694..9848739 100644
--- a/include/clang/Basic/TokenKinds.def
+++ b/include/clang/Basic/TokenKinds.def
@@ -346,6 +346,10 @@
KEYWORD(__is_trivial , KEYCXX)
KEYWORD(__is_union , 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 1c0d63b..de4904a 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1803,6 +1803,10 @@
ExprResult ParseBinaryTypeTrait();
//===--------------------------------------------------------------------===//
+ // Embarcadero: Expression Traits
+ ExprResult ParseExpressionTrait();
+
+ //===--------------------------------------------------------------------===//
// Preprocessor code-completion pass-through
virtual void CodeCompleteDirective(bool InConditional);
virtual void CodeCompleteInConditionalExclusion();
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 85f36b3..6c08712 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"
@@ -2732,6 +2733,18 @@
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 4df6a29..72252d4 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -970,6 +970,7 @@
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/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index 0d01880..ebbdf94 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -152,6 +152,7 @@
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 e738f0d..519bbaa 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -1061,6 +1061,10 @@
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));
}
@@ -2875,6 +2879,7 @@
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 a32b0ef..b62442d 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -1910,6 +1910,7 @@
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 3591c32..cf42e63 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -1281,6 +1281,15 @@
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) << ")";
@@ -1292,6 +1301,12 @@
<< 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 dbcb7e4..fe9a10b 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -802,6 +802,12 @@
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 6c5556d..f6de1fa 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -367,6 +367,10 @@
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 cd8f9c5..12761e8 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -555,6 +555,10 @@
/// [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,
@@ -1021,6 +1025,10 @@
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 1165ff0..e6abac3 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -1944,6 +1944,14 @@
}
}
+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.
@@ -2009,6 +2017,28 @@
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:
+/// [Embarcadero] 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 27545d8..edfa4a5 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -2739,6 +2739,45 @@
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);
+ if (PE.isInvalid()) return ExprError();
+ return BuildExpressionTrait(ET, KWLoc, PE.take(), RParen);
+ }
+
+ bool Value = false;
+ 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(ExprResult &lex, ExprResult &rex,
ExprValueKind &VK,
SourceLocation Loc,
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index dc96e59..c642e64 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -1915,6 +1915,17 @@
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.
///
@@ -6911,6 +6922,24 @@
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) {
NestedNameSpecifierLoc QualifierLoc
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 07c5c7b..608aafc 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -179,6 +179,7 @@
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
+ void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
void VisitPackExpansionExpr(PackExpansionExpr *E);
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
@@ -1343,6 +1344,16 @@
E->RhsType = GetTypeSourceInfo(Record, Idx);
}
+void ASTStmtReader::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
+ VisitExpr(E);
+ E->ET = (ExpressionTrait)Record[Idx++];
+ E->Value = (bool)Record[Idx++];
+ SourceRange Range = ReadSourceRange(Record, Idx);
+ E->QueriedExpression = Reader.ReadSubExpr();
+ E->Loc = Range.getBegin();
+ E->RParen = Range.getEnd();
+}
+
void ASTStmtReader::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
VisitExpr(E);
E->Value = (bool)Record[Idx++];
@@ -1935,6 +1946,10 @@
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/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index c2b0382..19cd834 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -153,6 +153,7 @@
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
+ void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
void VisitPackExpansionExpr(PackExpansionExpr *E);
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
@@ -1339,6 +1340,15 @@
Code = serialization::EXPR_BINARY_TYPE_TRAIT;
}
+void ASTStmtWriter::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
+ VisitExpr(E);
+ Record.push_back(E->getTrait());
+ Record.push_back(E->getValue());
+ Writer.AddSourceRange(E->getSourceRange(), Record);
+ Writer.AddStmt(E->getQueriedExpression());
+ Code = serialization::EXPR_CXX_EXPRESSION_TRAIT;
+}
+
void ASTStmtWriter::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
VisitExpr(E);
Record.push_back(E->getValue());
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index ac7e687..49af430 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -435,6 +435,7 @@
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..6b644ea
--- /dev/null
+++ b/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{{candidate function}}
+
+ template <class T>
+ int& NestedMemfunTemplate() { return variable; } // expected-note{{candidate function}}
+
+ int operator*() const;
+
+ template <class T>
+ int operator+(T) const; // expected-note{{candidate function}}
+
+ 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 'NestedFuncTemplate' from context}}
+
+ __is_lvalue_expr(::Class::NestedMemfunTemplate); // qualified-id: template \
+ // expected-error{{cannot resolve overloaded function 'NestedMemfunTemplate' from context}}
+
+ __is_lvalue_expr(::Class::operator+); // operator-function-id: template \
+ // expected-error{{cannot resolve overloaded function 'operator+' 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");
+}
+
+#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>();
+}
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 98edd52..a50c987 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -1789,6 +1789,7 @@
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);
@@ -2077,6 +2078,10 @@
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 b3ad83d..81beba8 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -151,6 +151,7 @@
case Stmt::UnresolvedLookupExprClass:
case Stmt::UnaryTypeTraitExprClass:
case Stmt::BinaryTypeTraitExprClass:
+ case Stmt::ExpressionTraitExprClass:
case Stmt::DependentScopeDeclRefExprClass:
case Stmt::CXXBindTemporaryExprClass:
case Stmt::ExprWithCleanupsClass: