From 2111c855343a0530e236bf0862358ec8d67b28f3 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Mon, 28 Jun 2010 15:09:07 +0000 Subject: [PATCH] Introduce Expr::Classify and Expr::ClassifyModifiable, which determine the classification of an expression under the C++0x taxology (value category). Reimplement isLvalue and isModifiableLvalue using these functions. No regressions in the test suite from this, and my rough performance check doesn't show any regressions either. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107007 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 92 ++++++- lib/AST/CMakeLists.txt | 1 + lib/AST/Expr.cpp | 372 -------------------------- lib/AST/ExprClassification.cpp | 470 +++++++++++++++++++++++++++++++++ 4 files changed, 560 insertions(+), 375 deletions(-) create mode 100644 lib/AST/ExprClassification.cpp diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 23a5cc47f7..77c969e745 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -162,9 +162,6 @@ public: }; isLvalueResult isLvalue(ASTContext &Ctx) const; - // Same as above, but excluding checks for non-object and void types in C - isLvalueResult isLvalueInternal(ASTContext &Ctx) const; - /// isModifiableLvalue - C99 6.3.2.1: an lvalue that does not have array type, /// does not have an incomplete type, does not have a const-qualified type, /// and if it is a structure or union, does not have any member (including, @@ -194,6 +191,95 @@ public: isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc = 0) const; + /// \brief The return type of classify(). Represents the C++0x expression + /// taxonomy. + class Classification { + public: + /// \brief The various classification results. Most of these mean prvalue. + enum Kinds { + CL_LValue, + CL_XValue, + CL_Function, // Functions cannot be lvalues in C. + CL_Void, // Void cannot be an lvalue in C. + CL_DuplicateVectorComponents, // A vector shuffle with dupes. + CL_MemberFunction, // An expression referring to a member function + CL_SubObjCPropertySetting, + CL_ClassTemporary, // A prvalue of class type + CL_PRValue // A prvalue for any other reason, of any other type + }; + /// \brief The results of modification testing. + enum ModifiableType { + CM_Untested, // testModifiable was false. + CM_Modifiable, + CM_RValue, // Not modifiable because it's an rvalue + CM_Function, // Not modifiable because it's a function; C++ only + CM_LValueCast, // Same as CM_RValue, but indicates GCC cast-as-lvalue ext + CM_NotBlockQualified, // Not captured in the closure + CM_NoSetterProperty,// Implicit assignment to ObjC property without setter + CM_ConstQualified, + CM_ArrayType, + CM_IncompleteType + }; + + private: + friend class Expr; + + unsigned short Kind; + unsigned short Modifiable; + + explicit Classification(Kinds k, ModifiableType m) + : Kind(k), Modifiable(m) + {} + + public: + Classification() {} + + Kinds getKind() const { return static_cast(Kind); } + ModifiableType getModifiable() const { + assert(Modifiable != CM_Untested && "Did not test for modifiability."); + return static_cast(Modifiable); + } + bool isLValue() const { return Kind == CL_LValue; } + bool isXValue() const { return Kind == CL_XValue; } + bool isGLValue() const { return Kind <= CL_XValue; } + bool isPRValue() const { return Kind >= CL_Function; } + bool isRValue() const { return Kind >= CL_XValue; } + bool isModifiable() const { return getModifiable() == CM_Modifiable; } + }; + /// \brief classify - Classify this expression according to the C++0x + /// expression taxonomy. + /// + /// C++0x defines ([basic.lval]) a new taxonomy of expressions to replace the + /// old lvalue vs rvalue. This function determines the type of expression this + /// is. There are three expression types: + /// - lvalues are classical lvalues as in C++03. + /// - prvalues are equivalent to rvalues in C++03. + /// - xvalues are expressions yielding unnamed rvalue references, e.g. a + /// function returning an rvalue reference. + /// lvalues and xvalues are collectively referred to as glvalues, while + /// prvalues and xvalues together form rvalues. + /// If a + Classification Classify(ASTContext &Ctx) const { + return ClassifyImpl(Ctx, 0); + } + + /// \brief classifyModifiable - Classify this expression according to the + /// C++0x expression taxonomy, and see if it is valid on the left side + /// of an assignment. + /// + /// This function extends classify in that it also tests whether the + /// expression is modifiable (C99 6.3.2.1p1). + /// \param Loc A source location that might be filled with a relevant location + /// if the expression is not modifiable. + Classification ClassifyModifiable(ASTContext &Ctx, SourceLocation &Loc) const{ + return ClassifyImpl(Ctx, &Loc); + } + +private: + Classification ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const; + +public: + /// \brief If this expression refers to a bit-field, retrieve the /// declaration of that bit-field. FieldDecl *getBitField(); diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 4e09a9b8fc..407ed95f3e 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangAST DeclPrinter.cpp DeclTemplate.cpp Expr.cpp + ExprClassification.cpp ExprConstant.cpp ExprCXX.cpp FullExpr.cpp diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 85af3cc508..4a8068629e 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1146,378 +1146,6 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, } } -/// DeclCanBeLvalue - Determine whether the given declaration can be -/// an lvalue. This is a helper routine for isLvalue. -static bool DeclCanBeLvalue(const NamedDecl *Decl, ASTContext &Ctx) { - // C++ [temp.param]p6: - // A non-type non-reference template-parameter is not an lvalue. - if (const NonTypeTemplateParmDecl *NTTParm - = dyn_cast(Decl)) - return NTTParm->getType()->isReferenceType(); - - return isa(Decl) || isa(Decl) || - // C++ 3.10p2: An lvalue refers to an object or function. - (Ctx.getLangOptions().CPlusPlus && - (isa(Decl) || isa(Decl))); -} - -/// isLvalue - C99 6.3.2.1: an lvalue is an expression with an object type or an -/// incomplete type other than void. Nonarray expressions that can be lvalues: -/// - name, where name must be a variable -/// - e[i] -/// - (e), where e must be an lvalue -/// - e.name, where e must be an lvalue -/// - e->name -/// - *e, the type of e cannot be a function type -/// - string-constant -/// - (__real__ e) and (__imag__ e) where e is an lvalue [GNU extension] -/// - reference type [C++ [expr]] -/// -Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { - assert(!TR->isReferenceType() && "Expressions can't have reference type."); - - isLvalueResult Res = isLvalueInternal(Ctx); - if (Res != LV_Valid || Ctx.getLangOptions().CPlusPlus) - return Res; - - // first, check the type (C99 6.3.2.1). Expressions with function - // type in C are not lvalues, but they can be lvalues in C++. - if (TR->isFunctionType() || TR == Ctx.OverloadTy) - return LV_NotObjectType; - - // Allow qualified void which is an incomplete type other than void (yuck). - if (TR->isVoidType() && !Ctx.getCanonicalType(TR).hasQualifiers()) - return LV_IncompleteVoidType; - - return LV_Valid; -} - -// Check whether the expression can be sanely treated like an l-value -Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { - switch (getStmtClass()) { - case ObjCIsaExprClass: - case StringLiteralClass: // C99 6.5.1p4 - case ObjCEncodeExprClass: // @encode behaves like its string in every way. - return LV_Valid; - case ArraySubscriptExprClass: // C99 6.5.3p4 (e1[e2] == (*((e1)+(e2)))) - // For vectors, make sure base is an lvalue (i.e. not a function call). - if (cast(this)->getBase()->getType()->isVectorType()) - return cast(this)->getBase()->isLvalue(Ctx); - return LV_Valid; - case DeclRefExprClass: { // C99 6.5.1p2 - const NamedDecl *RefdDecl = cast(this)->getDecl(); - if (DeclCanBeLvalue(RefdDecl, Ctx)) - return LV_Valid; - break; - } - case BlockDeclRefExprClass: { - const BlockDeclRefExpr *BDR = cast(this); - if (isa(BDR->getDecl())) - return LV_Valid; - break; - } - case MemberExprClass: { - const MemberExpr *m = cast(this); - if (Ctx.getLangOptions().CPlusPlus) { // C++ [expr.ref]p4: - NamedDecl *Member = m->getMemberDecl(); - // C++ [expr.ref]p4: - // If E2 is declared to have type "reference to T", then E1.E2 - // is an lvalue. - if (ValueDecl *Value = dyn_cast(Member)) - if (Value->getType()->isReferenceType()) - return LV_Valid; - - // -- If E2 is a static data member [...] then E1.E2 is an lvalue. - if (isa(Member) && Member->getDeclContext()->isRecord()) - return LV_Valid; - - // -- If E2 is a non-static data member [...]. If E1 is an - // lvalue, then E1.E2 is an lvalue. - if (isa(Member)) { - if (m->isArrow()) - return LV_Valid; - return m->getBase()->isLvalue(Ctx); - } - - // -- If it refers to a static member function [...], then - // E1.E2 is an lvalue. - // -- Otherwise, if E1.E2 refers to a non-static member - // function [...], then E1.E2 is not an lvalue. - if (CXXMethodDecl *Method = dyn_cast(Member)) - return Method->isStatic()? LV_Valid : LV_MemberFunction; - - // -- If E2 is a member enumerator [...], the expression E1.E2 - // is not an lvalue. - if (isa(Member)) - return LV_InvalidExpression; - - // Not an lvalue. - return LV_InvalidExpression; - } - - // C99 6.5.2.3p4 - if (m->isArrow()) - return LV_Valid; - Expr *BaseExp = m->getBase(); - if (BaseExp->getStmtClass() == ObjCPropertyRefExprClass || - BaseExp->getStmtClass() == ObjCImplicitSetterGetterRefExprClass) - return LV_SubObjCPropertySetting; - return - BaseExp->isLvalue(Ctx); - } - case UnaryOperatorClass: - if (cast(this)->getOpcode() == UnaryOperator::Deref) - return LV_Valid; // C99 6.5.3p4 - - if (cast(this)->getOpcode() == UnaryOperator::Real || - cast(this)->getOpcode() == UnaryOperator::Imag || - cast(this)->getOpcode() == UnaryOperator::Extension) - return cast(this)->getSubExpr()->isLvalue(Ctx); // GNU. - - if (Ctx.getLangOptions().CPlusPlus && // C++ [expr.pre.incr]p1 - (cast(this)->getOpcode() == UnaryOperator::PreInc || - cast(this)->getOpcode() == UnaryOperator::PreDec)) - return LV_Valid; - break; - case ImplicitCastExprClass: - if (cast(this)->isLvalueCast()) - return LV_Valid; - - // If this is a conversion to a class temporary, make a note of - // that. - if (Ctx.getLangOptions().CPlusPlus && getType()->isRecordType()) - return LV_ClassTemporary; - - break; - case ParenExprClass: // C99 6.5.1p5 - return cast(this)->getSubExpr()->isLvalue(Ctx); - case BinaryOperatorClass: - case CompoundAssignOperatorClass: { - const BinaryOperator *BinOp = cast(this); - - if (Ctx.getLangOptions().CPlusPlus && // C++ [expr.comma]p1 - BinOp->getOpcode() == BinaryOperator::Comma) - return BinOp->getRHS()->isLvalue(Ctx); - - // C++ [expr.mptr.oper]p6 - // 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. - if (BinOp->getOpcode() == BinaryOperator::PtrMemD && - !BinOp->getType()->isFunctionType()) - return BinOp->getLHS()->isLvalue(Ctx); - - // The result of an ->* expression is an lvalue only if its second operand - // is a pointer to data member. - if (BinOp->getOpcode() == BinaryOperator::PtrMemI && - !BinOp->getType()->isFunctionType()) { - QualType Ty = BinOp->getRHS()->getType(); - if (Ty->isMemberPointerType() && !Ty->isMemberFunctionPointerType()) - return LV_Valid; - } - - if (!BinOp->isAssignmentOp()) - return LV_InvalidExpression; - - if (Ctx.getLangOptions().CPlusPlus) - // C++ [expr.ass]p1: - // The result of an assignment operation [...] is an lvalue. - return LV_Valid; - - - // C99 6.5.16: - // An assignment expression [...] is not an lvalue. - return LV_InvalidExpression; - } - case CallExprClass: - case CXXOperatorCallExprClass: - case CXXMemberCallExprClass: { - // C++0x [expr.call]p10 - // A function call is an lvalue if and only if the result type - // is an lvalue reference. - QualType ReturnType = cast(this)->getCallReturnType(); - if (ReturnType->isLValueReferenceType()) - return LV_Valid; - - // If the function is returning a class temporary, make a note of - // that. - if (Ctx.getLangOptions().CPlusPlus && ReturnType->isRecordType()) - return LV_ClassTemporary; - - break; - } - case CompoundLiteralExprClass: // C99 6.5.2.5p5 - // FIXME: Is this what we want in C++? - return LV_Valid; - case ChooseExprClass: - // __builtin_choose_expr is an lvalue if the selected operand is. - return cast(this)->getChosenSubExpr(Ctx)->isLvalue(Ctx); - case ExtVectorElementExprClass: - if (cast(this)->containsDuplicateElements()) - return LV_DuplicateVectorComponents; - return LV_Valid; - case ObjCIvarRefExprClass: // ObjC instance variables are lvalues. - return LV_Valid; - case ObjCPropertyRefExprClass: // FIXME: check if read-only property. - return LV_Valid; - case ObjCImplicitSetterGetterRefExprClass: - // FIXME: check if read-only property. - return LV_Valid; - case PredefinedExprClass: - return LV_Valid; - case UnresolvedLookupExprClass: - case UnresolvedMemberExprClass: - return LV_Valid; - case CXXDefaultArgExprClass: - return cast(this)->getExpr()->isLvalue(Ctx); - case CStyleCastExprClass: - case CXXFunctionalCastExprClass: - case CXXStaticCastExprClass: - case CXXDynamicCastExprClass: - case CXXReinterpretCastExprClass: - case CXXConstCastExprClass: - // The result of an explicit cast is an lvalue if the type we are - // casting to is an lvalue reference type. See C++ [expr.cast]p1, - // C++ [expr.static.cast]p2, C++ [expr.dynamic.cast]p2, - // C++ [expr.reinterpret.cast]p1, C++ [expr.const.cast]p1. - if (cast(this)->getTypeAsWritten()-> - isLValueReferenceType()) - return LV_Valid; - - // If this is a conversion to a class temporary, make a note of - // that. - if (Ctx.getLangOptions().CPlusPlus && - cast(this)->getTypeAsWritten()->isRecordType()) - return LV_ClassTemporary; - - break; - case CXXTypeidExprClass: - // C++ 5.2.8p1: The result of a typeid expression is an lvalue of ... - return LV_Valid; - case CXXBindTemporaryExprClass: - return cast(this)->getSubExpr()-> - isLvalueInternal(Ctx); - case CXXBindReferenceExprClass: - // Something that's bound to a reference is always an lvalue. - return LV_Valid; - case ConditionalOperatorClass: { - // Complicated handling is only for C++. - if (!Ctx.getLangOptions().CPlusPlus) - return LV_InvalidExpression; - - // Sema should have taken care to ensure that a CXXTemporaryObjectExpr is - // everywhere there's an object converted to an rvalue. Also, any other - // casts should be wrapped by ImplicitCastExprs. There's just the special - // case involving throws to work out. - const ConditionalOperator *Cond = cast(this); - Expr *True = Cond->getTrueExpr(); - Expr *False = Cond->getFalseExpr(); - // C++0x 5.16p2 - // If either the second or the third operand has type (cv) void, [...] - // the result [...] is an rvalue. - if (True->getType()->isVoidType() || False->getType()->isVoidType()) - return LV_InvalidExpression; - - // Both sides must be lvalues for the result to be an lvalue. - if (True->isLvalue(Ctx) != LV_Valid || False->isLvalue(Ctx) != LV_Valid) - return LV_InvalidExpression; - - // That's it. - return LV_Valid; - } - - case Expr::CXXExprWithTemporariesClass: - return cast(this)->getSubExpr()->isLvalue(Ctx); - - case Expr::ObjCMessageExprClass: - if (const ObjCMethodDecl *Method - = cast(this)->getMethodDecl()) - if (Method->getResultType()->isLValueReferenceType()) - return LV_Valid; - break; - - case Expr::CXXConstructExprClass: - case Expr::CXXTemporaryObjectExprClass: - case Expr::CXXZeroInitValueExprClass: - return LV_ClassTemporary; - - default: - break; - } - return LV_InvalidExpression; -} - -/// isModifiableLvalue - C99 6.3.2.1: an lvalue that does not have array type, -/// does not have an incomplete type, does not have a const-qualified type, and -/// if it is a structure or union, does not have any member (including, -/// recursively, any member or element of all contained aggregates or unions) -/// with a const-qualified type. -Expr::isModifiableLvalueResult -Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { - isLvalueResult lvalResult = isLvalue(Ctx); - - switch (lvalResult) { - case LV_Valid: - // C++ 3.10p11: Functions cannot be modified, but pointers to - // functions can be modifiable. - if (Ctx.getLangOptions().CPlusPlus && TR->isFunctionType()) - return MLV_NotObjectType; - break; - - case LV_NotObjectType: return MLV_NotObjectType; - case LV_IncompleteVoidType: return MLV_IncompleteVoidType; - case LV_DuplicateVectorComponents: return MLV_DuplicateVectorComponents; - case LV_InvalidExpression: - // If the top level is a C-style cast, and the subexpression is a valid - // lvalue, then this is probably a use of the old-school "cast as lvalue" - // GCC extension. We don't support it, but we want to produce good - // diagnostics when it happens so that the user knows why. - if (const CStyleCastExpr *CE = dyn_cast(IgnoreParens())) { - if (CE->getSubExpr()->isLvalue(Ctx) == LV_Valid) { - if (Loc) - *Loc = CE->getLParenLoc(); - return MLV_LValueCast; - } - } - return MLV_InvalidExpression; - case LV_MemberFunction: return MLV_MemberFunction; - case LV_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; - case LV_ClassTemporary: - return MLV_ClassTemporary; - } - - // The following is illegal: - // void takeclosure(void (^C)(void)); - // void func() { int x = 1; takeclosure(^{ x = 7; }); } - // - if (const BlockDeclRefExpr *BDR = dyn_cast(this)) { - if (!BDR->isByRef() && isa(BDR->getDecl())) - return MLV_NotBlockQualified; - } - - // Assigning to an 'implicit' property? - if (const ObjCImplicitSetterGetterRefExpr* Expr = - dyn_cast(this)) { - if (Expr->getSetterMethod() == 0) - return MLV_NoSetterProperty; - } - - QualType CT = Ctx.getCanonicalType(getType()); - - if (CT.isConstQualified()) - return MLV_ConstQualified; - if (CT->isArrayType()) - return MLV_ArrayType; - if (CT->isIncompleteType()) - return MLV_IncompleteType; - - if (const RecordType *r = CT->getAs()) { - if (r->hasConstFields()) - return MLV_ConstQualified; - } - - return MLV_Valid; -} - /// isOBJCGCCandidate - Check if an expression is objc gc'able. /// returns true, if it is; false otherwise. bool Expr::isOBJCGCCandidate(ASTContext &Ctx) const { diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp new file mode 100644 index 0000000000..608706bdb0 --- /dev/null +++ b/lib/AST/ExprClassification.cpp @@ -0,0 +1,470 @@ +//===--- ExprClassification.cpp - Expression AST Node Implementation ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Expr::classify. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +using namespace clang; + +typedef Expr::Classification Cl; + +static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E); +static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D); +static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T); +static Cl::Kinds ClassifyMemberExpr(ASTContext &Ctx, const MemberExpr *E); +static Cl::Kinds ClassifyBinaryOp(ASTContext &Ctx, const BinaryOperator *E); +static Cl::Kinds ClassifyConditional(ASTContext &Ctx, + const ConditionalOperator *E); +static Cl::ModifiableType IsModifiable(ASTContext &Ctx, const Expr *E, + Cl::Kinds Kind, SourceLocation &Loc); + +Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { + assert(!TR->isReferenceType() && "Expressions can't have reference type."); + + Cl::Kinds kind = ClassifyInternal(Ctx, this); + // C99 6.3.2.1: An lvalue is an expression with an object type or an + // incomplete type other than void. + if (!Ctx.getLangOptions().CPlusPlus) { + // Thus, no functions. + if (TR->isFunctionType() || TR == Ctx.OverloadTy) + kind = Cl::CL_Function; + // No void either, but qualified void is OK because it is "other than void". + else if (TR->isVoidType() && !Ctx.getCanonicalType(TR).hasQualifiers()) + kind = Cl::CL_Void; + } + + Cl::ModifiableType modifiable = Cl::CM_Untested; + if (Loc) + modifiable = IsModifiable(Ctx, this, kind, *Loc); + return Classification(kind, modifiable); +} + +static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { + // This function takes the first stab at classifying expressions. + const LangOptions &Lang = Ctx.getLangOptions(); + + switch (E->getStmtClass()) { + // First come the expressions that are always lvalues, unconditionally. + + case Expr::ObjCIsaExprClass: + // C++ [expr.prim.general]p1: A string literal is an lvalue. + case Expr::StringLiteralClass: + // @encode is equivalent to its string + case Expr::ObjCEncodeExprClass: + // __func__ and friends are too. + case Expr::PredefinedExprClass: + // Property references are lvalues + case Expr::ObjCPropertyRefExprClass: + case Expr::ObjCImplicitSetterGetterRefExprClass: + // C++ [expr.typeid]p1: The result of a typeid expression is an lvalue of... + case Expr::CXXTypeidExprClass: + // Unresolved lookups get classified as lvalues. + // FIXME: Is this wise? Should they get their own kind? + case Expr::UnresolvedLookupExprClass: + case Expr::UnresolvedMemberExprClass: + // ObjC instance variables are lvalues + // FIXME: ObjC++0x might have different rules + case Expr::ObjCIvarRefExprClass: + // C99 6.5.2.5p5 says that compound literals are lvalues. + // FIXME: C++ might have a different opinion. + case Expr::CompoundLiteralExprClass: + return Cl::CL_LValue; + + // Next come the complicated cases. + + // C++ [expr.sub]p1: The result is an lvalue of type "T". + // However, subscripting vector types is more like member access. + case Expr::ArraySubscriptExprClass: + if (cast(E)->getBase()->getType()->isVectorType()) + return ClassifyInternal(Ctx, cast(E)->getBase()); + return Cl::CL_LValue; + + // C++ [expr.prim.general]p3: The result is an lvalue if the entity is a + // function or variable and a prvalue otherwise. + case Expr::DeclRefExprClass: + return ClassifyDecl(Ctx, cast(E)->getDecl()); + // We deal with names referenced from blocks the same way. + case Expr::BlockDeclRefExprClass: + return ClassifyDecl(Ctx, cast(E)->getDecl()); + + // Member access is complex. + case Expr::MemberExprClass: + return ClassifyMemberExpr(Ctx, cast(E)); + + case Expr::UnaryOperatorClass: + switch (cast(E)->getOpcode()) { + // C++ [expr.unary.op]p1: The unary * operator performs indirection: + // [...] the result is an lvalue referring to the object or function + // to which the expression points. + case UnaryOperator::Deref: + return Cl::CL_LValue; + + // GNU extensions, simply look through them. + case UnaryOperator::Real: + case UnaryOperator::Imag: + case UnaryOperator::Extension: + return ClassifyInternal(Ctx, cast(E)->getSubExpr()); + + // C++ [expr.pre.incr]p1: The result is the updated operand; it is an + // lvalue, [...] + // Not so in C. + case UnaryOperator::PreInc: + case UnaryOperator::PreDec: + return Lang.CPlusPlus ? Cl::CL_LValue : Cl::CL_PRValue; + + default: + return Cl::CL_PRValue; + } + + // Implicit casts are lvalues if they're lvalue casts. Other than that, we + // only specifically record class temporaries. + case Expr::ImplicitCastExprClass: + if (cast(E)->isLvalueCast()) + return Cl::CL_LValue; + return Lang.CPlusPlus && E->getType()->isRecordType() ? + Cl::CL_ClassTemporary : Cl::CL_PRValue; + + // C++ [expr.prim.general]p4: The presence of parentheses does not affect + // whether the expression is an lvalue. + case Expr::ParenExprClass: + return ClassifyInternal(Ctx, cast(E)->getSubExpr()); + + case Expr::BinaryOperatorClass: + case Expr::CompoundAssignOperatorClass: + // C doesn't have any binary expressions that are lvalues. + if (Lang.CPlusPlus) + return ClassifyBinaryOp(Ctx, cast(E)); + return Cl::CL_PRValue; + + case Expr::CallExprClass: + case Expr::CXXOperatorCallExprClass: + case Expr::CXXMemberCallExprClass: + return ClassifyUnnamed(Ctx, cast(E)->getCallReturnType()); + + // __builtin_choose_expr is equivalent to the chosen expression. + case Expr::ChooseExprClass: + return ClassifyInternal(Ctx, cast(E)->getChosenSubExpr(Ctx)); + + // Extended vector element access is an lvalue unless there are duplicates + // in the shuffle expression. + case Expr::ExtVectorElementExprClass: + return cast(E)->containsDuplicateElements() ? + Cl::CL_DuplicateVectorComponents : Cl::CL_LValue; + + // Simply look at the actual default argument. + case Expr::CXXDefaultArgExprClass: + return ClassifyInternal(Ctx, cast(E)->getExpr()); + + // Same idea for temporary binding. + case Expr::CXXBindTemporaryExprClass: + return ClassifyInternal(Ctx, cast(E)->getSubExpr()); + + // And the temporary lifetime guard. + case Expr::CXXExprWithTemporariesClass: + return ClassifyInternal(Ctx, cast(E)->getSubExpr()); + + // Casts depend completely on the target type. All casts work the same. + case Expr::CStyleCastExprClass: + case Expr::CXXFunctionalCastExprClass: + case Expr::CXXStaticCastExprClass: + case Expr::CXXDynamicCastExprClass: + case Expr::CXXReinterpretCastExprClass: + case Expr::CXXConstCastExprClass: + // Only in C++ can casts be interesting at all. + if (!Lang.CPlusPlus) return Cl::CL_PRValue; + return ClassifyUnnamed(Ctx, cast(E)->getTypeAsWritten()); + + case Expr::ConditionalOperatorClass: + // Once again, only C++ is interesting. + if (!Lang.CPlusPlus) return Cl::CL_PRValue; + return ClassifyConditional(Ctx, cast(E)); + + // ObjC message sends are effectively function calls, if the target function + // is known. + case Expr::ObjCMessageExprClass: + if (const ObjCMethodDecl *Method = + cast(E)->getMethodDecl()) { + return ClassifyUnnamed(Ctx, Method->getResultType()); + } + + // Some C++ expressions are always class temporaries. + case Expr::CXXConstructExprClass: + case Expr::CXXTemporaryObjectExprClass: + case Expr::CXXZeroInitValueExprClass: + return Cl::CL_ClassTemporary; + + // Everything we haven't handled is a prvalue. + default: + return Cl::CL_PRValue; + } +} + +/// ClassifyDecl - Return the classification of an expression referencing the +/// given declaration. +static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) { + // C++ [expr.prim.general]p6: The result is an lvalue if the entity is a + // function, variable, or data member and a prvalue otherwise. + // In C, functions are not lvalues. + // In addition, NonTypeTemplateParmDecl derives from VarDecl but isn't an + // lvalue unless it's a reference type (C++ [temp.param]p6), so we need to + // special-case this. + bool islvalue; + if (const NonTypeTemplateParmDecl *NTTParm = + dyn_cast(D)) + islvalue = NTTParm->getType()->isReferenceType(); + else + islvalue = isa(D) || isa(D) || + (Ctx.getLangOptions().CPlusPlus && + (isa(D) || isa(D))); + + return islvalue ? Cl::CL_LValue : Cl::CL_PRValue; +} + +/// ClassifyUnnamed - Return the classification of an expression yielding an +/// unnamed value of the given type. This applies in particular to function +/// calls and casts. +static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) { + // In C, function calls are always rvalues. + if (!Ctx.getLangOptions().CPlusPlus) return Cl::CL_PRValue; + + // C++ [expr.call]p10: A function call is an lvalue if the result type is an + // lvalue reference type or an rvalue reference to function type, an xvalue + // if the result type is an rvalue refernence to object type, and a prvalue + // otherwise. + if (T->isLValueReferenceType()) + return Cl::CL_LValue; + const RValueReferenceType *RV = T->getAs(); + if (!RV) // Could still be a class temporary, though. + return T->isRecordType() ? Cl::CL_ClassTemporary : Cl::CL_PRValue; + + return RV->getPointeeType()->isFunctionType() ? Cl::CL_LValue : Cl::CL_XValue; +} + +static Cl::Kinds ClassifyMemberExpr(ASTContext &Ctx, const MemberExpr *E) { + // Handle C first, it's easier. + if (!Ctx.getLangOptions().CPlusPlus) { + // C99 6.5.2.3p3 + // For dot access, the expression is an lvalue if the first part is. For + // arrow access, it always is an lvalue. + if (E->isArrow()) + return Cl::CL_LValue; + // ObjC property accesses are not lvalues, but get special treatment. + Expr *Base = E->getBase(); + if (isa(Base) || + isa(Base)) + return Cl::CL_SubObjCPropertySetting; + return ClassifyInternal(Ctx, Base); + } + + NamedDecl *Member = E->getMemberDecl(); + // C++ [expr.ref]p3: E1->E2 is converted to the equivalent form (*(E1)).E2. + // C++ [expr.ref]p4: If E2 is declared to have type "reference to T", then + // E1.E2 is an lvalue. + if (ValueDecl *Value = dyn_cast(Member)) + if (Value->getType()->isReferenceType()) + return Cl::CL_LValue; + + // Otherwise, one of the following rules applies. + // -- If E2 is a static member [...] then E1.E2 is an lvalue. + if (isa(Member) && Member->getDeclContext()->isRecord()) + return Cl::CL_LValue; + + // -- If E2 is a non-static data member [...]. If E1 is an lvalue, then + // E1.E2 is an lvalue; if E1 is an xvalue, then E1.E2 is an xvalue; + // otherwise, it is a prvalue. + if (isa(Member)) { + // *E1 is an lvalue + if (E->isArrow()) + return Cl::CL_LValue; + return ClassifyInternal(Ctx, E->getBase()); + } + + // -- If E2 is a [...] member function, [...] + // -- If it refers to a static member function [...], then E1.E2 is an + // lvalue; [...] + // -- Otherwise [...] E1.E2 is a prvalue. + if (CXXMethodDecl *Method = dyn_cast(Member)) + return Method->isStatic() ? Cl::CL_LValue : Cl::CL_MemberFunction; + + // -- If E2 is a member enumerator [...], the expression E1.E2 is a prvalue. + // So is everything else we haven't handled yet. + return Cl::CL_PRValue; +} + +static Cl::Kinds ClassifyBinaryOp(ASTContext &Ctx, const BinaryOperator *E) { + assert(Ctx.getLangOptions().CPlusPlus && + "This is only relevant for C++."); + // C++ [expr.ass]p1: All [...] return an lvalue referring to the left operand. + if (E->isAssignmentOp()) + return Cl::CL_LValue; + + // C++ [expr.comma]p1: the result is of the same value category as its right + // operand, [...]. + if (E->getOpcode() == BinaryOperator::Comma) + return ClassifyInternal(Ctx, E->getRHS()); + + // C++ [expr.mptr.oper]p6: The result of a .* expression whose second operand + // is a pointer to a data member is of the same value category as its first + // operand. + if (E->getOpcode() == BinaryOperator::PtrMemD) + return E->getType()->isFunctionType() ? Cl::CL_MemberFunction : + ClassifyInternal(Ctx, E->getLHS()); + + // C++ [expr.mptr.oper]p6: The result of an ->* expression is an lvalue if its + // second operand is a pointer to data member and a prvalue otherwise. + if (E->getOpcode() == BinaryOperator::PtrMemI) + return E->getType()->isFunctionType() ? + Cl::CL_MemberFunction : Cl::CL_LValue; + + // All other binary operations are prvalues. + return Cl::CL_PRValue; +} + +static Cl::Kinds ClassifyConditional(ASTContext &Ctx, + const ConditionalOperator *E) { + assert(Ctx.getLangOptions().CPlusPlus && + "This is only relevant for C++."); + + Expr *True = E->getTrueExpr(); + Expr *False = E->getFalseExpr(); + // C++ [expr.cond]p2 + // If either the second or the third operand has type (cv) void, [...] + // the result [...] is a prvalue. + if (True->getType()->isVoidType() || False->getType()->isVoidType()) + return Cl::CL_PRValue; + + // Note that at this point, we have already performed all conversions + // according to [expr.cond]p3. + // C++ [expr.cond]p4: If the second and third operands are glvalues of the + // same value category [...], the result is of that [...] value category. + // C++ [expr.cond]p5: Otherwise, the result is a prvalue. + Cl::Kinds LCl = ClassifyInternal(Ctx, True), + RCl = ClassifyInternal(Ctx, False); + return LCl == RCl ? LCl : Cl::CL_PRValue; +} + +static Cl::ModifiableType IsModifiable(ASTContext &Ctx, const Expr *E, + Cl::Kinds Kind, SourceLocation &Loc) { + // As a general rule, we only care about lvalues. But there are some rvalues + // for which we want to generate special results. + if (Kind == Cl::CL_PRValue) { + // For the sake of better diagnostics, we want to specifically recognize + // use of the GCC cast-as-lvalue extension. + if (const CStyleCastExpr *CE = dyn_cast(E->IgnoreParens())){ + if (CE->getSubExpr()->Classify(Ctx).isLValue()) { + Loc = CE->getLParenLoc(); + return Cl::CM_LValueCast; + } + } + } + if (Kind != Cl::CL_LValue) + return Cl::CM_RValue; + + // This is the lvalue case. + // Functions are lvalues in C++, but not modifiable. (C++ [basic.lval]p6) + if (Ctx.getLangOptions().CPlusPlus && E->getType()->isFunctionType()) + return Cl::CM_Function; + + // You cannot assign to a variable outside a block from within the block if + // it is not marked __block, e.g. + // void takeclosure(void (^C)(void)); + // void func() { int x = 1; takeclosure(^{ x = 7; }); } + if (const BlockDeclRefExpr *BDR = dyn_cast(E)) { + if (!BDR->isByRef() && isa(BDR->getDecl())) + return Cl::CM_NotBlockQualified; + } + + // Assignment to a property in ObjC is an implicit setter access. But a + // setter might not exist. + if (const ObjCImplicitSetterGetterRefExpr *Expr = + dyn_cast(E)) { + if (Expr->getSetterMethod() == 0) + return Cl::CM_NoSetterProperty; + } + + CanQualType CT = Ctx.getCanonicalType(E->getType()); + // Const stuff is obviously not modifiable. + if (CT.isConstQualified()) + return Cl::CM_ConstQualified; + // Arrays are not modifiable, only their elements are. + if (CT->isArrayType()) + return Cl::CM_ArrayType; + // Incomplete types are not modifiable. + if (CT->isIncompleteType()) + return Cl::CM_IncompleteType; + + // Records with any const fields (recursively) are not modifiable. + if (const RecordType *R = CT->getAs()) { + assert(!Ctx.getLangOptions().CPlusPlus && + "C++ struct assignment should be resolved by the " + "copy assignment operator."); + if (R->hasConstFields()) + return Cl::CM_ConstQualified; + } + + return Cl::CM_Modifiable; +} + +Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { + Classification VC = Classify(Ctx); + switch (VC.getKind()) { + case Cl::CL_LValue: return LV_Valid; + case Cl::CL_XValue: return LV_InvalidExpression; + case Cl::CL_Function: return LV_NotObjectType; + case Cl::CL_Void: return LV_IncompleteVoidType; + case Cl::CL_DuplicateVectorComponents: return LV_DuplicateVectorComponents; + case Cl::CL_MemberFunction: return LV_MemberFunction; + case Cl::CL_SubObjCPropertySetting: return LV_SubObjCPropertySetting; + case Cl::CL_ClassTemporary: return LV_ClassTemporary; + case Cl::CL_PRValue: return LV_InvalidExpression; + } + assert(false && "Unhandled kind"); +} + +Expr::isModifiableLvalueResult +Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { + SourceLocation dummy; + Classification VC = ClassifyModifiable(Ctx, Loc ? *Loc : dummy); + switch (VC.getKind()) { + case Cl::CL_LValue: break; + case Cl::CL_XValue: return MLV_InvalidExpression; + case Cl::CL_Function: return MLV_NotObjectType; + case Cl::CL_Void: return MLV_IncompleteVoidType; + case Cl::CL_DuplicateVectorComponents: return MLV_DuplicateVectorComponents; + case Cl::CL_MemberFunction: return MLV_MemberFunction; + case Cl::CL_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; + case Cl::CL_ClassTemporary: return MLV_ClassTemporary; + case Cl::CL_PRValue: + return VC.getModifiable() == Cl::CM_LValueCast ? + MLV_LValueCast : MLV_InvalidExpression; + } + assert(VC.getKind() == Cl::CL_LValue && "Unhandled kind"); + switch (VC.getModifiable()) { + case Cl::CM_Untested: assert(false && "Did not test modifiability"); + case Cl::CM_Modifiable: return MLV_Valid; + case Cl::CM_RValue: assert(false && "CM_RValue and CL_LValue don't match"); + case Cl::CM_Function: return MLV_NotObjectType; + case Cl::CM_LValueCast: + assert(false && "CM_LValueCast and CL_LValue don't match"); + case Cl::CM_NotBlockQualified: return MLV_NotBlockQualified; + case Cl::CM_NoSetterProperty: return MLV_NoSetterProperty; + case Cl::CM_ConstQualified: return MLV_ConstQualified; + case Cl::CM_ArrayType: return MLV_ArrayType; + case Cl::CM_IncompleteType: return MLV_IncompleteType; + } + assert(false && "Unhandled modifiable type"); +} -- 2.40.0