зеркало из https://github.com/microsoft/clang.git
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
This commit is contained in:
Родитель
62395c9666
Коммит
552622067d
|
@ -38,6 +38,7 @@ public:
|
||||||
void VisitDeclRefExpr(DeclRefExpr *E) { }
|
void VisitDeclRefExpr(DeclRefExpr *E) { }
|
||||||
void VisitOffsetOfExpr(OffsetOfExpr *E) { }
|
void VisitOffsetOfExpr(OffsetOfExpr *E) { }
|
||||||
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { }
|
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { }
|
||||||
|
void VisitExpressionTraitExpr(ExpressionTraitExpr *E) { }
|
||||||
void VisitBlockExpr(BlockExpr *E) { }
|
void VisitBlockExpr(BlockExpr *E) { }
|
||||||
void VisitCXXUuidofExpr(CXXUuidofExpr *E) { }
|
void VisitCXXUuidofExpr(CXXUuidofExpr *E) { }
|
||||||
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E) { }
|
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E) { }
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#define LLVM_CLANG_AST_EXPRCXX_H
|
#define LLVM_CLANG_AST_EXPRCXX_H
|
||||||
|
|
||||||
#include "clang/Basic/TypeTraits.h"
|
#include "clang/Basic/TypeTraits.h"
|
||||||
|
#include "clang/Basic/ExpressionTraits.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
#include "clang/AST/UnresolvedSet.h"
|
#include "clang/AST/UnresolvedSet.h"
|
||||||
#include "clang/AST/TemplateBase.h"
|
#include "clang/AST/TemplateBase.h"
|
||||||
|
@ -1593,6 +1594,58 @@ public:
|
||||||
friend class ASTStmtReader;
|
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
|
/// \brief A reference to an overloaded function set, either an
|
||||||
/// \t UnresolvedLookupExpr or an \t UnresolvedMemberExpr.
|
/// \t UnresolvedLookupExpr or an \t UnresolvedMemberExpr.
|
||||||
class OverloadExpr : public Expr {
|
class OverloadExpr : public Expr {
|
||||||
|
|
|
@ -1849,6 +1849,10 @@ DEF_TRAVERSE_STMT(BinaryTypeTraitExpr, {
|
||||||
TRY_TO(TraverseTypeLoc(S->getRhsTypeSourceInfo()->getTypeLoc()));
|
TRY_TO(TraverseTypeLoc(S->getRhsTypeSourceInfo()->getTypeLoc()));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DEF_TRAVERSE_STMT(ExpressionTraitExpr, {
|
||||||
|
TRY_TO(TraverseStmt(S->getQueriedExpression()));
|
||||||
|
})
|
||||||
|
|
||||||
DEF_TRAVERSE_STMT(VAArgExpr, {
|
DEF_TRAVERSE_STMT(VAArgExpr, {
|
||||||
// The child-iterator will pick up the expression argument.
|
// The child-iterator will pick up the expression argument.
|
||||||
TRY_TO(TraverseTypeLoc(S->getWrittenTypeInfo()->getTypeLoc()));
|
TRY_TO(TraverseTypeLoc(S->getWrittenTypeInfo()->getTypeLoc()));
|
||||||
|
|
|
@ -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
|
|
@ -104,6 +104,7 @@ def CXXDeleteExpr : DStmt<Expr>;
|
||||||
def CXXPseudoDestructorExpr : DStmt<Expr>;
|
def CXXPseudoDestructorExpr : DStmt<Expr>;
|
||||||
def UnaryTypeTraitExpr : DStmt<Expr>;
|
def UnaryTypeTraitExpr : DStmt<Expr>;
|
||||||
def BinaryTypeTraitExpr : DStmt<Expr>;
|
def BinaryTypeTraitExpr : DStmt<Expr>;
|
||||||
|
def ExpressionTraitExpr : DStmt<Expr>;
|
||||||
def DependentScopeDeclRefExpr : DStmt<Expr>;
|
def DependentScopeDeclRefExpr : DStmt<Expr>;
|
||||||
def CXXConstructExpr : DStmt<Expr>;
|
def CXXConstructExpr : DStmt<Expr>;
|
||||||
def CXXBindTemporaryExpr : DStmt<Expr>;
|
def CXXBindTemporaryExpr : DStmt<Expr>;
|
||||||
|
|
|
@ -346,6 +346,10 @@ KEYWORD(__is_polymorphic , KEYCXX)
|
||||||
KEYWORD(__is_trivial , KEYCXX)
|
KEYWORD(__is_trivial , KEYCXX)
|
||||||
KEYWORD(__is_union , KEYCXX)
|
KEYWORD(__is_union , KEYCXX)
|
||||||
|
|
||||||
|
// Embarcadero Expression Traits
|
||||||
|
KEYWORD(__is_lvalue_expr , KEYCXX)
|
||||||
|
KEYWORD(__is_rvalue_expr , KEYCXX)
|
||||||
|
|
||||||
// Apple Extension.
|
// Apple Extension.
|
||||||
KEYWORD(__private_extern__ , KEYALL)
|
KEYWORD(__private_extern__ , KEYALL)
|
||||||
|
|
||||||
|
|
|
@ -1802,6 +1802,10 @@ private:
|
||||||
ExprResult ParseUnaryTypeTrait();
|
ExprResult ParseUnaryTypeTrait();
|
||||||
ExprResult ParseBinaryTypeTrait();
|
ExprResult ParseBinaryTypeTrait();
|
||||||
|
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
// Embarcadero: Expression Traits
|
||||||
|
ExprResult ParseExpressionTrait();
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// Preprocessor code-completion pass-through
|
// Preprocessor code-completion pass-through
|
||||||
virtual void CodeCompleteDirective(bool InConditional);
|
virtual void CodeCompleteDirective(bool InConditional);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "clang/Basic/Specifiers.h"
|
#include "clang/Basic/Specifiers.h"
|
||||||
#include "clang/Basic/TemplateKinds.h"
|
#include "clang/Basic/TemplateKinds.h"
|
||||||
#include "clang/Basic/TypeTraits.h"
|
#include "clang/Basic/TypeTraits.h"
|
||||||
|
#include "clang/Basic/ExpressionTraits.h"
|
||||||
#include "llvm/ADT/OwningPtr.h"
|
#include "llvm/ADT/OwningPtr.h"
|
||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
@ -2732,6 +2733,18 @@ public:
|
||||||
TypeSourceInfo *RhsT,
|
TypeSourceInfo *RhsT,
|
||||||
SourceLocation RParen);
|
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,
|
ExprResult ActOnStartCXXMemberReference(Scope *S,
|
||||||
Expr *Base,
|
Expr *Base,
|
||||||
SourceLocation OpLoc,
|
SourceLocation OpLoc,
|
||||||
|
|
|
@ -970,6 +970,7 @@ namespace clang {
|
||||||
EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr
|
EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr
|
||||||
|
|
||||||
EXPR_CXX_UNARY_TYPE_TRAIT, // UnaryTypeTraitExpr
|
EXPR_CXX_UNARY_TYPE_TRAIT, // UnaryTypeTraitExpr
|
||||||
|
EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr
|
||||||
EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr
|
EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr
|
||||||
|
|
||||||
EXPR_OPAQUE_VALUE, // OpaqueValueExpr
|
EXPR_OPAQUE_VALUE, // OpaqueValueExpr
|
||||||
|
|
|
@ -152,6 +152,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
|
||||||
case Expr::CXXScalarValueInitExprClass:
|
case Expr::CXXScalarValueInitExprClass:
|
||||||
case Expr::UnaryTypeTraitExprClass:
|
case Expr::UnaryTypeTraitExprClass:
|
||||||
case Expr::BinaryTypeTraitExprClass:
|
case Expr::BinaryTypeTraitExprClass:
|
||||||
|
case Expr::ExpressionTraitExprClass:
|
||||||
case Expr::ObjCSelectorExprClass:
|
case Expr::ObjCSelectorExprClass:
|
||||||
case Expr::ObjCProtocolExprClass:
|
case Expr::ObjCProtocolExprClass:
|
||||||
case Expr::ObjCStringLiteralClass:
|
case Expr::ObjCStringLiteralClass:
|
||||||
|
|
|
@ -1061,6 +1061,10 @@ public:
|
||||||
return Success(E->getValue(), E);
|
return Success(E->getValue(), E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
|
||||||
|
return Success(E->getValue(), E);
|
||||||
|
}
|
||||||
|
|
||||||
bool VisitChooseExpr(const ChooseExpr *E) {
|
bool VisitChooseExpr(const ChooseExpr *E) {
|
||||||
return Visit(E->getChosenSubExpr(Info.Ctx));
|
return Visit(E->getChosenSubExpr(Info.Ctx));
|
||||||
}
|
}
|
||||||
|
@ -2875,6 +2879,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
|
||||||
case Expr::CXXScalarValueInitExprClass:
|
case Expr::CXXScalarValueInitExprClass:
|
||||||
case Expr::UnaryTypeTraitExprClass:
|
case Expr::UnaryTypeTraitExprClass:
|
||||||
case Expr::BinaryTypeTraitExprClass:
|
case Expr::BinaryTypeTraitExprClass:
|
||||||
|
case Expr::ExpressionTraitExprClass:
|
||||||
case Expr::CXXNoexceptExprClass:
|
case Expr::CXXNoexceptExprClass:
|
||||||
return NoDiag();
|
return NoDiag();
|
||||||
case Expr::CallExprClass:
|
case Expr::CallExprClass:
|
||||||
|
|
|
@ -1910,6 +1910,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
|
||||||
case Expr::StmtExprClass:
|
case Expr::StmtExprClass:
|
||||||
case Expr::UnaryTypeTraitExprClass:
|
case Expr::UnaryTypeTraitExprClass:
|
||||||
case Expr::BinaryTypeTraitExprClass:
|
case Expr::BinaryTypeTraitExprClass:
|
||||||
|
case Expr::ExpressionTraitExprClass:
|
||||||
case Expr::VAArgExprClass:
|
case Expr::VAArgExprClass:
|
||||||
case Expr::CXXUuidofExprClass:
|
case Expr::CXXUuidofExprClass:
|
||||||
case Expr::CXXNoexceptExprClass:
|
case Expr::CXXNoexceptExprClass:
|
||||||
|
|
|
@ -1281,6 +1281,15 @@ static const char *getTypeTraitName(BinaryTypeTrait BTT) {
|
||||||
return "";
|
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) {
|
void StmtPrinter::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) {
|
||||||
OS << getTypeTraitName(E->getTrait()) << "("
|
OS << getTypeTraitName(E->getTrait()) << "("
|
||||||
<< E->getQueriedType().getAsString(Policy) << ")";
|
<< E->getQueriedType().getAsString(Policy) << ")";
|
||||||
|
@ -1292,6 +1301,12 @@ void StmtPrinter::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
|
||||||
<< E->getRhsType().getAsString(Policy) << ")";
|
<< E->getRhsType().getAsString(Policy) << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StmtPrinter::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
|
||||||
|
OS << getExpressionTraitName(E->getTrait()) << "(";
|
||||||
|
PrintExpr(E->getQueriedExpression());
|
||||||
|
OS << ")";
|
||||||
|
}
|
||||||
|
|
||||||
void StmtPrinter::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
|
void StmtPrinter::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
|
||||||
OS << "noexcept(";
|
OS << "noexcept(";
|
||||||
PrintExpr(E->getOperand());
|
PrintExpr(E->getOperand());
|
||||||
|
|
|
@ -802,6 +802,12 @@ void StmtProfiler::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *S) {
|
||||||
VisitType(S->getRhsType());
|
VisitType(S->getRhsType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StmtProfiler::VisitExpressionTraitExpr(ExpressionTraitExpr *S) {
|
||||||
|
VisitExpr(S);
|
||||||
|
ID.AddInteger(S->getTrait());
|
||||||
|
VisitExpr(S->getQueriedExpression());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StmtProfiler::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) {
|
StmtProfiler::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) {
|
||||||
VisitExpr(S);
|
VisitExpr(S);
|
||||||
|
|
|
@ -367,6 +367,10 @@ public:
|
||||||
return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
|
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) {
|
Value *VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E) {
|
||||||
// C++ [expr.pseudo]p1:
|
// C++ [expr.pseudo]p1:
|
||||||
// The result shall only be used as the operand for the function call
|
// The result shall only be used as the operand for the function call
|
||||||
|
|
|
@ -555,6 +555,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
||||||
/// [GNU] '__is_base_of'
|
/// [GNU] '__is_base_of'
|
||||||
/// [MS] '__is_convertible_to'
|
/// [MS] '__is_convertible_to'
|
||||||
///
|
///
|
||||||
|
/// [Embarcadero] expression-trait:
|
||||||
|
/// '__is_lvalue_expr'
|
||||||
|
/// '__is_rvalue_expr'
|
||||||
|
///
|
||||||
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
||||||
bool isAddressOfOperand,
|
bool isAddressOfOperand,
|
||||||
bool &NotCastExpr,
|
bool &NotCastExpr,
|
||||||
|
@ -1021,6 +1025,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
||||||
case tok::kw___is_convertible_to:
|
case tok::kw___is_convertible_to:
|
||||||
return ParseBinaryTypeTrait();
|
return ParseBinaryTypeTrait();
|
||||||
|
|
||||||
|
case tok::kw___is_lvalue_expr:
|
||||||
|
case tok::kw___is_rvalue_expr:
|
||||||
|
return ParseExpressionTrait();
|
||||||
|
|
||||||
case tok::at: {
|
case tok::at: {
|
||||||
SourceLocation AtLoc = ConsumeToken();
|
SourceLocation AtLoc = ConsumeToken();
|
||||||
return ParseObjCAtExpression(AtLoc);
|
return ParseObjCAtExpression(AtLoc);
|
||||||
|
|
|
@ -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
|
/// ParseUnaryTypeTrait - Parse the built-in unary type-trait
|
||||||
/// pseudo-functions that allow implementation of the TR1/C++0x type traits
|
/// pseudo-functions that allow implementation of the TR1/C++0x type traits
|
||||||
/// templates.
|
/// templates.
|
||||||
|
@ -2009,6 +2017,28 @@ ExprResult Parser::ParseBinaryTypeTrait() {
|
||||||
return Actions.ActOnBinaryTypeTrait(BTT, Loc, LhsTy.get(), RhsTy.get(), RParen);
|
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
|
/// ParseCXXAmbiguousParenExpression - We have parsed the left paren of a
|
||||||
/// parenthesized ambiguous type-id. This uses tentative parsing to disambiguate
|
/// parenthesized ambiguous type-id. This uses tentative parsing to disambiguate
|
||||||
/// based on the context past the parens.
|
/// based on the context past the parens.
|
||||||
|
|
|
@ -2739,6 +2739,45 @@ ExprResult Sema::BuildBinaryTypeTrait(BinaryTypeTrait BTT,
|
||||||
ResultType));
|
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,
|
QualType Sema::CheckPointerToMemberOperands(ExprResult &lex, ExprResult &rex,
|
||||||
ExprValueKind &VK,
|
ExprValueKind &VK,
|
||||||
SourceLocation Loc,
|
SourceLocation Loc,
|
||||||
|
|
|
@ -1915,6 +1915,17 @@ public:
|
||||||
return getSema().BuildBinaryTypeTrait(Trait, StartLoc, LhsT, RhsT, RParenLoc);
|
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
|
/// \brief Build a new (previously unresolved) declaration reference
|
||||||
/// expression.
|
/// expression.
|
||||||
///
|
///
|
||||||
|
@ -6909,6 +6920,24 @@ TreeTransform<Derived>::TransformBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
|
||||||
E->getLocEnd());
|
E->getLocEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
template<typename Derived>
|
||||||
ExprResult
|
ExprResult
|
||||||
TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
|
TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
|
||||||
|
|
|
@ -179,6 +179,7 @@ namespace clang {
|
||||||
|
|
||||||
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
||||||
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
||||||
|
void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
|
||||||
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
|
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
|
||||||
void VisitPackExpansionExpr(PackExpansionExpr *E);
|
void VisitPackExpansionExpr(PackExpansionExpr *E);
|
||||||
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
||||||
|
@ -1343,6 +1344,16 @@ void ASTStmtReader::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
|
||||||
E->RhsType = GetTypeSourceInfo(Record, Idx);
|
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) {
|
void ASTStmtReader::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
|
||||||
VisitExpr(E);
|
VisitExpr(E);
|
||||||
E->Value = (bool)Record[Idx++];
|
E->Value = (bool)Record[Idx++];
|
||||||
|
@ -1935,6 +1946,10 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) {
|
||||||
S = new (Context) BinaryTypeTraitExpr(Empty);
|
S = new (Context) BinaryTypeTraitExpr(Empty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EXPR_CXX_EXPRESSION_TRAIT:
|
||||||
|
S = new (Context) ExpressionTraitExpr(Empty);
|
||||||
|
break;
|
||||||
|
|
||||||
case EXPR_CXX_NOEXCEPT:
|
case EXPR_CXX_NOEXCEPT:
|
||||||
S = new (Context) CXXNoexceptExpr(Empty);
|
S = new (Context) CXXNoexceptExpr(Empty);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -153,6 +153,7 @@ namespace clang {
|
||||||
|
|
||||||
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
||||||
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
||||||
|
void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
|
||||||
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
|
void VisitCXXNoexceptExpr(CXXNoexceptExpr *E);
|
||||||
void VisitPackExpansionExpr(PackExpansionExpr *E);
|
void VisitPackExpansionExpr(PackExpansionExpr *E);
|
||||||
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
||||||
|
@ -1339,6 +1340,15 @@ void ASTStmtWriter::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
|
||||||
Code = serialization::EXPR_BINARY_TYPE_TRAIT;
|
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) {
|
void ASTStmtWriter::VisitCXXNoexceptExpr(CXXNoexceptExpr *E) {
|
||||||
VisitExpr(E);
|
VisitExpr(E);
|
||||||
Record.push_back(E->getValue());
|
Record.push_back(E->getValue());
|
||||||
|
|
|
@ -435,6 +435,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
|
||||||
case Stmt::DependentScopeDeclRefExprClass:
|
case Stmt::DependentScopeDeclRefExprClass:
|
||||||
case Stmt::UnaryTypeTraitExprClass:
|
case Stmt::UnaryTypeTraitExprClass:
|
||||||
case Stmt::BinaryTypeTraitExprClass:
|
case Stmt::BinaryTypeTraitExprClass:
|
||||||
|
case Stmt::ExpressionTraitExprClass:
|
||||||
case Stmt::UnresolvedLookupExprClass:
|
case Stmt::UnresolvedLookupExprClass:
|
||||||
case Stmt::UnresolvedMemberExprClass:
|
case Stmt::UnresolvedMemberExprClass:
|
||||||
case Stmt::CXXNoexceptExprClass:
|
case Stmt::CXXNoexceptExprClass:
|
||||||
|
|
|
@ -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>();
|
||||||
|
}
|
|
@ -1789,6 +1789,7 @@ public:
|
||||||
void VisitWhileStmt(WhileStmt *W);
|
void VisitWhileStmt(WhileStmt *W);
|
||||||
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E);
|
||||||
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E);
|
||||||
|
void VisitExpressionTraitExpr(ExpressionTraitExpr *E);
|
||||||
void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U);
|
void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U);
|
||||||
void VisitVAArgExpr(VAArgExpr *E);
|
void VisitVAArgExpr(VAArgExpr *E);
|
||||||
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
|
||||||
|
@ -2077,6 +2078,10 @@ void EnqueueVisitor::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) {
|
||||||
AddTypeLoc(E->getLhsTypeSourceInfo());
|
AddTypeLoc(E->getLhsTypeSourceInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnqueueVisitor::VisitExpressionTraitExpr(ExpressionTraitExpr *E) {
|
||||||
|
EnqueueChildren(E);
|
||||||
|
}
|
||||||
|
|
||||||
void EnqueueVisitor::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U) {
|
void EnqueueVisitor::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U) {
|
||||||
VisitOverloadExpr(U);
|
VisitOverloadExpr(U);
|
||||||
if (!U->isImplicitAccess())
|
if (!U->isImplicitAccess())
|
||||||
|
|
|
@ -151,6 +151,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent,
|
||||||
case Stmt::UnresolvedLookupExprClass:
|
case Stmt::UnresolvedLookupExprClass:
|
||||||
case Stmt::UnaryTypeTraitExprClass:
|
case Stmt::UnaryTypeTraitExprClass:
|
||||||
case Stmt::BinaryTypeTraitExprClass:
|
case Stmt::BinaryTypeTraitExprClass:
|
||||||
|
case Stmt::ExpressionTraitExprClass:
|
||||||
case Stmt::DependentScopeDeclRefExprClass:
|
case Stmt::DependentScopeDeclRefExprClass:
|
||||||
case Stmt::CXXBindTemporaryExprClass:
|
case Stmt::CXXBindTemporaryExprClass:
|
||||||
case Stmt::ExprWithCleanupsClass:
|
case Stmt::ExprWithCleanupsClass:
|
||||||
|
|
Загрузка…
Ссылка в новой задаче