From b476a14e9b35ef2448b42b033e1f0cceaa3f2778 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 5 May 2013 21:17:10 +0000 Subject: [PATCH] Factor out duplication between lvalue-to-rvalue conversions and variable assignments in constant expressions. No significant functionality changes (slight improvement to potential constant expression checking). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181170 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 482 +++++++++--------- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 10 +- test/SemaCXX/constant-expression-cxx1y.cpp | 5 + 3 files changed, 255 insertions(+), 242 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index d1500d490c..13fd9df266 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1451,9 +1451,16 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, } /// Try to evaluate the initializer for a variable declaration. -static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, - const VarDecl *VD, - CallStackFrame *Frame, APValue &Result) { +/// +/// \param Info Information about the ongoing evaluation. +/// \param E An expression to be used when printing diagnostics. +/// \param VD The variable whose initializer should be obtained. +/// \param Frame The frame in which the variable was created. Must be null +/// if this variable is not local to the evaluation. +/// \param Result Filled in with a pointer to the value of the variable. +static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, + const VarDecl *VD, CallStackFrame *Frame, + APValue *&Result) { // If this is a parameter to an active constexpr function call, perform // argument substitution. if (const ParmVarDecl *PVD = dyn_cast(VD)) { @@ -1465,23 +1472,17 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); return false; } - Result = Frame->Arguments[PVD->getFunctionScopeIndex()]; + Result = &Frame->Arguments[PVD->getFunctionScopeIndex()]; return true; } // If this is a local variable, dig out its value. - if (VD->hasLocalStorage() && Frame && Frame->Index > 1) { - // In C++1y, we can't safely read anything which might have been mutated - // when checking a potential constant expression. - if (Info.getLangOpts().CPlusPlus1y && - Info.CheckingPotentialConstantExpression) - return false; - - Result = Frame->Temporaries[VD]; + if (Frame) { + Result = &Frame->Temporaries[VD]; // If we've carried on past an unevaluatable local variable initializer, // we can't go any further. This can happen during potential constant // expression checking. - return !Result.isUninit(); + return !Result->isUninit(); } // Dig out the initializer, and use the declaration which it's attached to. @@ -1497,8 +1498,8 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, // If we're currently evaluating the initializer of this declaration, use that // in-flight value. if (Info.EvaluatingDecl == VD) { - Result = *Info.EvaluatingDeclValue; - return !Result.isUninit(); + Result = Info.EvaluatingDeclValue; + return !Result->isUninit(); } // Never evaluate the initializer of a weak variable. We can't be sure that @@ -1524,7 +1525,7 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.addNotes(Notes); } - Result = *VD->getEvaluatedValue(); + Result = VD->getEvaluatedValue(); return true; } @@ -1616,10 +1617,27 @@ enum AccessKinds { AK_Assign }; +/// A handle to a complete object (an object that is not a subobject of +/// another object). +struct CompleteObject { + /// The value of the complete object. + APValue *Value; + /// The type of the complete object. + QualType Type; + + CompleteObject() : Value(0) {} + CompleteObject(APValue *Value, QualType Type) + : Value(Value), Type(Type) { + assert(Value && "missing value for complete object"); + } + + operator bool() const { return Value; } +}; + /// Find the designated sub-object of an rvalue. template typename SubobjectHandler::result_type -findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType, +findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, const SubobjectDesignator &Sub, SubobjectHandler &handler) { if (Sub.Invalid) // A diagnostic will have already been produced. @@ -1633,12 +1651,13 @@ findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType, return handler.failed(); } if (Sub.Entries.empty()) - return handler.found(Obj, ObjType); - if (Info.CheckingPotentialConstantExpression && Obj.isUninit()) + return handler.found(*Obj.Value, Obj.Type); + if (Info.CheckingPotentialConstantExpression && Obj.Value->isUninit()) // This object might be initialized later. return handler.failed(); - APValue *O = &Obj; + APValue *O = Obj.Value; + QualType ObjType = Obj.Type; // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) { if (ObjType->isArrayType()) { @@ -1767,48 +1786,44 @@ findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType, namespace { struct ExtractSubobjectHandler { EvalInfo &Info; - APValue &Obj; + APValue &Result; static const AccessKinds AccessKind = AK_Read; typedef bool result_type; bool failed() { return false; } bool found(APValue &Subobj, QualType SubobjType) { - if (&Subobj != &Obj) { - // We can't just swap Obj and Subobj here, because we'd create an object - // that has itself as a subobject. To avoid the leak, we ensure that Tmp - // ends up owning the original complete object, which is destroyed by - // Tmp's destructor. - APValue Tmp; - Subobj.swap(Tmp); - Obj.swap(Tmp); - } + Result = Subobj; return true; } bool found(APSInt &Value, QualType SubobjType) { - Obj = APValue(Value); + Result = APValue(Value); return true; } bool found(APFloat &Value, QualType SubobjType) { - Obj = APValue(Value); + Result = APValue(Value); return true; } bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - Obj = APValue(extractStringLiteralCharacter( + Result = APValue(extractStringLiteralCharacter( Info, Subobj.getLValueBase().get(), Character)); return true; } }; +} // end anonymous namespace + const AccessKinds ExtractSubobjectHandler::AccessKind; /// Extract the designated sub-object of an rvalue. static bool extractSubobject(EvalInfo &Info, const Expr *E, - APValue &Obj, QualType ObjType, - const SubobjectDesignator &Sub) { - ExtractSubobjectHandler Handler = { Info, Obj }; - return findSubobject(Info, E, Obj, ObjType, Sub, Handler); + const CompleteObject &Obj, + const SubobjectDesignator &Sub, + APValue &Result) { + ExtractSubobjectHandler Handler = { Info, Result }; + return findSubobject(Info, E, Obj, Sub, Handler); } +namespace { struct ModifySubobjectHandler { EvalInfo &Info; APValue &NewVal; @@ -1855,16 +1870,17 @@ struct ModifySubobjectHandler { llvm_unreachable("shouldn't encounter string elements with ExpandArrays"); } }; -const AccessKinds ModifySubobjectHandler::AccessKind; } // end anonymous namespace +const AccessKinds ModifySubobjectHandler::AccessKind; + /// Update the designated sub-object of an rvalue to the given value. static bool modifySubobject(EvalInfo &Info, const Expr *E, - APValue &Obj, QualType ObjType, + const CompleteObject &Obj, const SubobjectDesignator &Sub, APValue &NewVal) { ModifySubobjectHandler Handler = { Info, NewVal, E }; - return findSubobject(Info, E, Obj, ObjType, Sub, Handler); + return findSubobject(Info, E, Obj, Sub, Handler); } /// Find the position where two subobject designators diverge, or equivalently @@ -1924,60 +1940,52 @@ static bool AreElementsOfSameArray(QualType ObjType, return CommonLength >= A.Entries.size() - IsArray; } -/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on -/// the given glvalue. This can also be used for 'lvalue-to-lvalue' conversions -/// for looking up the glvalue referred to by an entity of reference type. -/// -/// \param Info - Information about the ongoing evaluation. -/// \param Conv - The expression for which we are performing the conversion. -/// Used for diagnostics. -/// \param Type - The type of the glvalue (before stripping cv-qualifiers in the -/// case of a non-class type). -/// \param LVal - The glvalue on which we are attempting to perform this action. -/// \param RVal - The produced value will be placed here. -static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, - QualType Type, - const LValue &LVal, APValue &RVal) { - if (LVal.Designator.Invalid) - // A diagnostic will have already been produced. - return false; - - const Expr *Base = LVal.Base.dyn_cast(); - +/// Find the complete object to which an LValue refers. +CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK, + const LValue &LVal, QualType LValType) { if (!LVal.Base) { - Info.Diag(Conv, diag::note_constexpr_access_null) << AK_Read; - return false; + Info.Diag(E, diag::note_constexpr_access_null) << AK; + return CompleteObject(); } CallStackFrame *Frame = 0; if (LVal.CallIndex) { Frame = Info.getCallFrame(LVal.CallIndex); if (!Frame) { - Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) - << AK_Read << !Base; + Info.Diag(E, diag::note_constexpr_lifetime_ended, 1) + << AK << LVal.Base.is(); NoteLValueLocation(Info, LVal.Base); - return false; + return CompleteObject(); } + } else if (AK != AK_Read) { + Info.Diag(E, diag::note_constexpr_modify_global); + return CompleteObject(); } // C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type // is not a constant expression (even if the object is non-volatile). We also // apply this rule to C++98, in order to conform to the expected 'volatile' // semantics. - if (Type.isVolatileQualified()) { + if (LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) - Info.Diag(Conv, diag::note_constexpr_access_volatile_type) - << AK_Read << Type; + Info.Diag(E, diag::note_constexpr_access_volatile_type) + << AK << LValType; else - Info.Diag(Conv); - return false; + Info.Diag(E); + return CompleteObject(); } + // Compute value storage location and type of base object. + APValue *BaseVal = 0; + QualType BaseType; + if (const ValueDecl *D = LVal.Base.dyn_cast()) { // In C++98, const, non-volatile integers initialized with ICEs are ICEs. // In C++11, constexpr, non-volatile variables initialized with constant // expressions are constant expressions too. Inside constexpr functions, // parameters are constant expressions even if they're non-const. + // In C++1y, objects local to a constant expression (those with a Frame) are + // both readable and writable inside constant expressions. // In C, such things can also be folded, although they are not ICEs. const VarDecl *VD = dyn_cast(D); if (VD) { @@ -1985,209 +1993,158 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, VD = VDef; } if (!VD || VD->isInvalidDecl()) { - Info.Diag(Conv); - return false; + Info.Diag(E); + return CompleteObject(); } - // DR1313: If the object is volatile-qualified but the glvalue was not, - // behavior is undefined so the result is not a constant expression. - QualType VT = VD->getType(); - if (VT.isVolatileQualified()) { + // Accesses of volatile-qualified objects are not allowed. + BaseType = VD->getType(); + if (BaseType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1) - << AK_Read << 1 << VD; + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 1 << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } // Unless we're looking at a local variable or argument in a constexpr call, // the variable we're reading must be const. - if (LVal.CallIndex <= 1 || !VD->hasLocalStorage()) { + if (!Frame) { + assert(AK == AK_Read && "can't modify non-local"); if (VD->isConstexpr()) { // OK, we can read this variable. - } else if (VT->isIntegralOrEnumerationType()) { - if (!VT.isConstQualified()) { + } else if (BaseType->isIntegralOrEnumerationType()) { + if (!BaseType.isConstQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_non_const_int, 1) << VD; + Info.Diag(E, diag::note_constexpr_ltor_non_const_int, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } - } else if (VT->isFloatingType() && VT.isConstQualified()) { + } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) { // We support folding of const floating-point types, in order to make // static const data members of such types (supported as an extension) // more useful. if (Info.getLangOpts().CPlusPlus11) { - Info.CCEDiag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.CCEDiag(Conv); + Info.CCEDiag(E); } } else { // FIXME: Allow folding of values of any literal type in all languages. if (Info.getLangOpts().CPlusPlus11) { - Info.Diag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + Info.Diag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } } - return EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal) && - extractSubobject(Info, Conv, RVal, VT, LVal.Designator); - } + if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal)) + return CompleteObject(); + } else { + const Expr *Base = LVal.Base.dyn_cast(); - // Volatile temporary objects cannot be read in constant expressions. - if (Base->getType().isVolatileQualified()) { - if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1) - << AK_Read << 0; - Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); - } else { - Info.Diag(Conv); + if (!Frame) { + Info.Diag(E); + return CompleteObject(); } - return false; - } - if (Frame) { - // In C++1y, we can't safely read anything which might have been mutated - // when checking a potential constant expression. - if (Info.getLangOpts().CPlusPlus1y && - Info.CheckingPotentialConstantExpression) - return false; - - // If this is a temporary expression with a nontrivial initializer, grab the - // value from the relevant stack frame. - RVal = Frame->Temporaries[Base]; - } else if (const CompoundLiteralExpr *CLE - = dyn_cast(Base)) { - // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the - // initializer until now for such expressions. Such an expression can't be - // an ICE in C, so this only matters for fold. - assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?"); - if (!Evaluate(RVal, Info, CLE->getInitializer())) - return false; - } else if (isa(Base)) { - if (Info.getLangOpts().CPlusPlus1y && - Info.CheckingPotentialConstantExpression && - !Base->getType().isConstQualified()) - return false; + BaseType = Base->getType(); + BaseVal = &Frame->Temporaries[Base]; - // We represent a string literal array as an lvalue pointing at the - // corresponding expression, rather than building an array of chars. - // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant - RVal = APValue(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); - } else { - Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr); - return false; + // Volatile temporary objects cannot be accessed in constant expressions. + if (BaseType.isVolatileQualified()) { + if (Info.getLangOpts().CPlusPlus) { + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 0; + Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); + } else { + Info.Diag(E); + } + return CompleteObject(); + } } - return extractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator); -} + // In C++1y, we can't safely access any mutable state when checking a + // potential constant expression. + if (Frame && Info.getLangOpts().CPlusPlus1y && + Info.CheckingPotentialConstantExpression) + return CompleteObject(); -/// Perform an assignment of Val to LVal. Takes ownership of Val. -// FIXME: Factor out duplication with HandleLValueToRValueConversion. -static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal, - QualType LValType, APValue &Val) { - if (!Info.getLangOpts().CPlusPlus1y) { - Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); - return false; - } + return CompleteObject(BaseVal, BaseType); +} +/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on +/// the given glvalue. This can also be used for 'lvalue-to-lvalue' conversions +/// for looking up the glvalue referred to by an entity of reference type. +/// +/// \param Info - Information about the ongoing evaluation. +/// \param Conv - The expression for which we are performing the conversion. +/// Used for diagnostics. +/// \param Type - The type of the glvalue (before stripping cv-qualifiers in the +/// case of a non-class type). +/// \param LVal - The glvalue on which we are attempting to perform this action. +/// \param RVal - The produced value will be placed here. +static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, + QualType Type, + const LValue &LVal, APValue &RVal) { if (LVal.Designator.Invalid) - // A diagnostic will have already been produced. - return false; - - if (Info.CheckingPotentialConstantExpression) return false; + // Check for special cases where there is no existing APValue to look at. const Expr *Base = LVal.Base.dyn_cast(); - - if (!LVal.Base) { - Info.Diag(E, diag::note_constexpr_access_null) << AK_Assign; - return false; + if (!LVal.Designator.Invalid && Base && !LVal.CallIndex && + !Type.isVolatileQualified()) { + if (const CompoundLiteralExpr *CLE = dyn_cast(Base)) { + // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the + // initializer until now for such expressions. Such an expression can't be + // an ICE in C, so this only matters for fold. + assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?"); + if (Type.isVolatileQualified()) { + Info.Diag(Conv); + return false; + } + APValue Lit; + if (!Evaluate(Lit, Info, CLE->getInitializer())) + return false; + CompleteObject LitObj(&Lit, Base->getType()); + return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); + } else if (isa(Base)) { + // We represent a string literal array as an lvalue pointing at the + // corresponding expression, rather than building an array of chars. + // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant + APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); + CompleteObject StrObj(&Str, Base->getType()); + return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); + } } - if (!LVal.CallIndex) { - Info.Diag(E, diag::note_constexpr_modify_global); - return false; - } + CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type); + return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal); +} - CallStackFrame *Frame = Info.getCallFrame(LVal.CallIndex); - if (!Frame) { - Info.Diag(E, diag::note_constexpr_lifetime_ended, 1) - << AK_Assign << !Base; - NoteLValueLocation(Info, LVal.Base); +/// Perform an assignment of Val to LVal. Takes ownership of Val. +static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal, + QualType LValType, APValue &Val) { + if (LVal.Designator.Invalid) return false; - } - if (LValType.isVolatileQualified()) { - if (Info.getLangOpts().CPlusPlus) - Info.Diag(E, diag::note_constexpr_access_volatile_type) - << AK_Assign << LValType; - else - Info.Diag(E); + if (!Info.getLangOpts().CPlusPlus1y) { + Info.Diag(E); return false; } - // Compute value storage location and type of base object. - APValue *BaseVal = 0; - QualType BaseType; - - if (const ValueDecl *D = LVal.Base.dyn_cast()) { - const VarDecl *VD = dyn_cast(D); - if (VD) { - if (const VarDecl *VDef = VD->getDefinition(Info.Ctx)) - VD = VDef; - } - if (!VD || VD->isInvalidDecl()) { - Info.Diag(E); - return false; - } - - // Modifications to volatile-qualified objects are not allowed. - BaseType = VD->getType(); - if (BaseType.isVolatileQualified()) { - Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) - << AK_Assign << 1 << VD; - Info.Note(VD->getLocation(), diag::note_declared_at); - return false; - } - - if (const ParmVarDecl *PVD = dyn_cast(VD)) { - if (!Frame || !Frame->Arguments) { - Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); - return false; - } - BaseVal = &Frame->Arguments[PVD->getFunctionScopeIndex()]; - } else if (VD->hasLocalStorage() && Frame && Frame->Index > 1) { - BaseVal = &Frame->Temporaries[VD]; - } else { - // FIXME: Can this happen? - Info.Diag(E); - return false; - } - } else { - BaseType = Base->getType(); - BaseVal = &Frame->Temporaries[Base]; - - // Volatile temporary objects cannot be modified in constant expressions. - if (BaseType.isVolatileQualified()) { - Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) - << AK_Assign << 0; - Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); - return false; - } - } - - return modifySubobject(Info, E, *BaseVal, BaseType, LVal.Designator, Val); + CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType); + return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val); } /// Build an lvalue for the object argument of a member function call. @@ -2984,11 +2941,13 @@ public: assert(BaseTy->castAs()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); + CompleteObject Obj(&Val, BaseTy); SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); - return extractSubobject(Info, E, Val, BaseTy, Designator) && - DerivedSuccess(Val, E); + APValue Result; + return extractSubobject(Info, E, Obj, Designator, Result) && + DerivedSuccess(Result, E); } RetTy VisitCastExpr(const CastExpr *E) { @@ -3101,16 +3060,6 @@ public: case BO_PtrMemD: case BO_PtrMemI: return HandleMemberPointerAccess(this->Info, E, Result); - - case BO_Assign: { - if (!this->Visit(E->getLHS())) - return false; - APValue NewVal; - if (!Evaluate(NewVal, this->Info, E->getRHS())) - return false; - return HandleAssignment(this->Info, E, Result, E->getLHS()->getType(), - NewVal); - } } } @@ -3178,6 +3127,7 @@ public: LValueExprEvaluatorBaseTy(Info, Result) {} bool VisitVarDecl(const Expr *E, const VarDecl *VD); + bool VisitIncDec(const UnaryOperator *UO); bool VisitDeclRefExpr(const DeclRefExpr *E); bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); } @@ -3192,6 +3142,10 @@ public: bool VisitUnaryDeref(const UnaryOperator *E); bool VisitUnaryReal(const UnaryOperator *E); bool VisitUnaryImag(const UnaryOperator *E); + bool VisitUnaryPreInc(const UnaryOperator *UO) { return VisitIncDec(UO); } + bool VisitUnaryPreDec(const UnaryOperator *UO) { return VisitIncDec(UO); } + bool VisitBinAssign(const BinaryOperator *BO); + bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO); bool VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { @@ -3233,18 +3187,22 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { } bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { + CallStackFrame *Frame = 0; + if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) + Frame = Info.CurrentCall; + if (!VD->getType()->isReferenceType()) { - if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) { - Result.set(VD, Info.CurrentCall->Index); + if (Frame) { + Result.set(VD, Frame->Index); return true; } return Success(VD); } - APValue V; - if (!EvaluateVarDeclInit(Info, E, VD, Info.CurrentCall, V)) + APValue *V; + if (!evaluateVarDeclInit(Info, E, VD, Frame, V)) return false; - return Success(V, E); + return Success(*V, E); } bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( @@ -3277,7 +3235,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { bool LValueExprEvaluator::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { return Success(E); -} +} bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { // Handle static data members. @@ -3338,6 +3296,54 @@ bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) { return true; } +bool LValueExprEvaluator::VisitIncDec(const UnaryOperator *UO) { + if (!Info.getLangOpts().CPlusPlus1y) + return Error(UO); + + if (!this->Visit(UO->getSubExpr())) + return false; + + // FIXME: + //return handleIncDec( + // this->Info, CAO, Result, UO->getSubExpr()->getType(), + // UO->isIncrementOp()); + // (Watch out for promotions: ++short can't overflow, ++bool is always true). + return Error(UO); +} + +bool LValueExprEvaluator::VisitCompoundAssignOperator( + const CompoundAssignOperator *CAO) { + if (!Info.getLangOpts().CPlusPlus1y) + return Error(CAO); + + // The overall lvalue result is the result of evaluating the LHS. + if (!this->Visit(CAO->getLHS())) + return false; + + APValue RHS; + if (!Evaluate(RHS, this->Info, CAO->getRHS())) + return false; + + // FIXME: + //return handleCompoundAssignment( + // this->Info, CAO, + // Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(), + // RHS, CAO->getRHS()->getType(), + // CAO->getOpForCompoundAssignment(CAO->getOpcode()), + // CAO->getComputationResultType()); + return Error(CAO); +} + +bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { + if (!this->Visit(E->getLHS())) + return false; + APValue NewVal; + if (!Evaluate(NewVal, this->Info, E->getRHS())) + return false; + return HandleAssignment(this->Info, E, Result, E->getLHS()->getType(), + NewVal); +} + //===----------------------------------------------------------------------===// // Pointer Evaluation //===----------------------------------------------------------------------===// diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index 9361642326..e12011d19c 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -274,11 +274,13 @@ namespace std_example { int a; // expected-error {{must be initialized}} return a; } - // FIXME: Once we support variable mutation, this can produce a - // constant expression. - constexpr int prev(int x) { // expected-error {{never produces a constant expression}} - return --x; // expected-note {{subexpression}} + constexpr int prev(int x) { + return --x; } +#if 1 // FIXME: !defined CXX1Y + // expected-error@-4 {{never produces a constant expression}} + // expected-note@-4 {{subexpression}} +#endif constexpr int g(int x, int n) { int r = 1; while (--n > 0) r *= x; diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 543c7cffdb..62739ee8c1 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -232,6 +232,11 @@ namespace potential_const_expr { int z = 0; return 100 / (set(z), 0); // expected-note {{division by zero}} } + int n; // expected-note {{declared here}} + constexpr int ref() { // expected-error {{never produces a constant expression}} + int &r = n; + return r; // expected-note {{read of non-const variable 'n'}} + } } namespace subobject { -- 2.40.0