diff --git a/clang/include/clang/AST/EvaluatedExprVisitor.h b/clang/include/clang/AST/EvaluatedExprVisitor.h index 3bc62eda2d06..bab1606dcee4 100644 --- a/clang/include/clang/AST/EvaluatedExprVisitor.h +++ b/clang/include/clang/AST/EvaluatedExprVisitor.h @@ -38,6 +38,7 @@ public: 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/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index ed4661b3a4ff..5aaeb8e7ab7f 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/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 @@ 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() { } + + SourceRange getSourceRange() const { return SourceRange(Loc, RParen);} + + ExpressionTrait getTrait() const { return static_cast(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/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 0b3993d88e77..93cc446afd1b 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1849,6 +1849,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/clang/include/clang/Basic/ExpressionTraits.h b/clang/include/clang/Basic/ExpressionTraits.h new file mode 100644 index 000000000000..403a59a8d19c --- /dev/null +++ b/clang/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/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index eaf4b8598466..de3fedb724e8 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -104,6 +104,7 @@ def CXXDeleteExpr : DStmt; def CXXPseudoDestructorExpr : DStmt; def UnaryTypeTraitExpr : DStmt; def BinaryTypeTraitExpr : DStmt; +def ExpressionTraitExpr : DStmt; def DependentScopeDeclRefExpr : DStmt; def CXXConstructExpr : DStmt; def CXXBindTemporaryExpr : DStmt; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index ac8e694337e4..98487397ca30 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -346,6 +346,10 @@ KEYWORD(__is_polymorphic , KEYCXX) 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/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 1c0d63b02627..de4904a9a981 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1802,6 +1802,10 @@ private: ExprResult ParseUnaryTypeTrait(); ExprResult ParseBinaryTypeTrait(); + //===--------------------------------------------------------------------===// + // Embarcadero: Expression Traits + ExprResult ParseExpressionTrait(); + //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through virtual void CodeCompleteDirective(bool InConditional); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 85f36b3d621f..6c08712fe715 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/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 @@ 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/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 4df6a29c1be6..72252d4da80d 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -970,6 +970,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/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 0d018804143e..ebbdf94f100a 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -152,6 +152,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/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e738f0dc908c..519bbaa115ec 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1061,6 +1061,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)); } @@ -2875,6 +2879,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/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index a32b0ef7512e..b62442d6d2a8 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1910,6 +1910,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/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 3591c32ad828..cf42e633791d 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1281,6 +1281,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) << ")"; @@ -1292,6 +1301,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/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index dbcb7e4fe60c..fe9a10b01e0f 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -802,6 +802,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/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 6c5556d4f18a..f6de1fa1273e 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -367,6 +367,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/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index cd8f9c53f75b..12761e8d9f84 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -555,6 +555,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, @@ -1021,6 +1025,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/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 1165ff0978f7..e6abac3b4ce0 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1944,6 +1944,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. @@ -2009,6 +2017,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: +/// [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/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 27545d802e43..edfa4a532dfc 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -2739,6 +2739,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); + 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/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index dc96e59420b3..c642e642cc83 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1915,6 +1915,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. /// @@ -6909,6 +6920,24 @@ TreeTransform::TransformBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) { E->getLocEnd()); } +template +ExprResult +TreeTransform::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 ExprResult TreeTransform::TransformDependentScopeDeclRefExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 07c5c7b80477..608aafc3ce48 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -179,6 +179,7 @@ namespace clang { 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 @@ void ASTStmtReader::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) { 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 @@ 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/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index c2b038284cf1..19cd834dd454 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -153,6 +153,7 @@ namespace clang { 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 @@ void ASTStmtWriter::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) { 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/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index ac7e687b935b..49af4304b054 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -435,6 +435,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/clang/test/SemaCXX/expression-traits.cpp b/clang/test/SemaCXX/expression-traits.cpp new file mode 100644 index 000000000000..6b644ea84daa --- /dev/null +++ b/clang/test/SemaCXX/expression-traits.cpp @@ -0,0 +1,620 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fcxx-exceptions %s + +// +// Tests for "expression traits" intrinsics such as __is_lvalue_expr. +// +// For the time being, these tests are written against the 2003 C++ +// standard (ISO/IEC 14882:2003 -- see draft at +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2001/n1316/). +// +// C++0x has its own, more-refined, idea of lvalues and rvalues. +// If/when we need to support those, we'll need to track both +// standard documents. + +#if !__has_feature(cxx_static_assert) +# define CONCAT_(X_, Y_) CONCAT1_(X_, Y_) +# define CONCAT1_(X_, Y_) X_ ## Y_ + +// This emulation can be used multiple times on one line (and thus in +// a macro), except at class scope +# define static_assert(b_, m_) \ + typedef int CONCAT_(sa_, __LINE__)[b_ ? 1 : -1] +#endif + +// Tests are broken down according to section of the C++03 standard +// (ISO/IEC 14882:2003(E)) + +// Assertion macros encoding the following two paragraphs +// +// basic.lval/1 Every expression is either an lvalue or an rvalue. +// +// expr.prim/5 A parenthesized expression is a primary expression whose type +// and value are identical to those of the enclosed expression. The +// presence of parentheses does not affect whether the expression is +// an lvalue. +// +// Note: these asserts cannot be made at class scope in C++03. Put +// them in a member function instead. +#define ASSERT_LVALUE(expr) \ + static_assert(__is_lvalue_expr(expr), "should be an lvalue"); \ + static_assert(__is_lvalue_expr((expr)), \ + "the presence of parentheses should have" \ + " no effect on lvalueness (expr.prim/5)"); \ + static_assert(!__is_rvalue_expr(expr), "should be an lvalue"); \ + static_assert(!__is_rvalue_expr((expr)), \ + "the presence of parentheses should have" \ + " no effect on lvalueness (expr.prim/5)") + +#define ASSERT_RVALUE(expr); \ + static_assert(__is_rvalue_expr(expr), "should be an rvalue"); \ + static_assert(__is_rvalue_expr((expr)), \ + "the presence of parentheses should have" \ + " no effect on lvalueness (expr.prim/5)"); \ + static_assert(!__is_lvalue_expr(expr), "should be an rvalue"); \ + static_assert(!__is_lvalue_expr((expr)), \ + "the presence of parentheses should have" \ + " no effect on lvalueness (expr.prim/5)") + +enum Enum { Enumerator }; + +int ReturnInt(); +void ReturnVoid(); +Enum ReturnEnum(); + +void basic_lval_5() +{ + // basic.lval/5: The result of calling a function that does not return + // a reference is an rvalue. + ASSERT_RVALUE(ReturnInt()); + ASSERT_RVALUE(ReturnVoid()); + ASSERT_RVALUE(ReturnEnum()); +} + +int& ReturnIntReference(); +extern Enum& ReturnEnumReference(); + +void basic_lval_6() +{ + // basic.lval/6: An expression which holds a temporary object resulting + // from a cast to a nonreference type is an rvalue (this includes + // the explicit creation of an object using functional notation + struct IntClass + { + explicit IntClass(int = 0); + IntClass(char const*); + operator int() const; + }; + + struct ConvertibleToIntClass + { + operator IntClass() const; + }; + + ConvertibleToIntClass b; + + // Make sure even trivial conversions are not detected as lvalues + int intLvalue = 0; + ASSERT_RVALUE((int)intLvalue); + ASSERT_RVALUE((short)intLvalue); + ASSERT_RVALUE((long)intLvalue); + + // Same tests with function-call notation + ASSERT_RVALUE(int(intLvalue)); + ASSERT_RVALUE(short(intLvalue)); + ASSERT_RVALUE(long(intLvalue)); + + char charLValue = 'x'; + ASSERT_RVALUE((signed char)charLValue); + ASSERT_RVALUE((unsigned char)charLValue); + + ASSERT_RVALUE(static_cast(IntClass())); + IntClass intClassLValue; + ASSERT_RVALUE(static_cast(intClassLValue)); + ASSERT_RVALUE(static_cast(ConvertibleToIntClass())); + ConvertibleToIntClass convertibleToIntClassLValue; + ASSERT_RVALUE(static_cast(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 + struct NestedClassTemplate {}; + + template + static int& NestedFuncTemplate() { return variable; } // expected-note{{candidate function}} + + template + int& NestedMemfunTemplate() { return variable; } // expected-note{{candidate function}} + + int operator*() const; + + template + 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 + // + // ::opt nested-name-specifier templateopt 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(&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(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(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(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(x)); + ASSERT_RVALUE(static_cast(x)); +} + +void expr_reinterpret_cast_1() +{ + // expr.reinterpret.cast/1: The result of the expression + // reinterpret_cast(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(0)); + char const v = 0; + ASSERT_LVALUE(reinterpret_cast(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 void f(); + +// FIXME These currently fail +void expr_fun_lvalue() +{ + ASSERT_LVALUE(&f); +} + +void expr_fun_rvalue() +{ + ASSERT_RVALUE(f); +} +#endif + +template +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/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 98edd521ef4b..a50c987b3c25 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1789,6 +1789,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); @@ -2077,6 +2078,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/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index b3ad83daafa6..81beba822f91 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -151,6 +151,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: