From bebf5b1bcfbf591dd3cd80c4aebd6486bb34f41c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 26 Apr 2013 14:36:30 +0000 Subject: [PATCH] C++1y: support simple variable assignments in constexpr functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@180603 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 40 +- lib/AST/ExprConstant.cpp | 472 ++++++++++++++++----- lib/Sema/SemaDeclCXX.cpp | 11 +- lib/Sema/SemaInit.cpp | 1 + test/SemaCXX/constant-expression-cxx11.cpp | 10 +- test/SemaCXX/constant-expression-cxx1y.cpp | 157 +++++++ test/SemaCXX/constexpr-printing.cpp | 2 +- test/SemaCXX/constexpr-value-init.cpp | 2 +- test/SemaCXX/i-c-e-cxx.cpp | 2 +- 9 files changed, 575 insertions(+), 122 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 9b38a1580f..aea2980e6f 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -12,7 +12,7 @@ let Component = "AST" in { // Constant expression diagnostics. These (and their users) belong in Sema. def note_expr_divide_by_zero : Note<"division by zero">; def note_constexpr_invalid_cast : Note< - "%select{reinterpret_cast|dynamic_cast|cast which performs the conversions of" + "%select{reinterpret_cast|dynamic_cast|cast that performs the conversions of" " a reinterpret_cast|cast from %1}0 is not allowed in a constant expression">; def note_constexpr_invalid_downcast : Note< "cannot cast object of dynamic type %0 to type %1">; @@ -84,11 +84,20 @@ def note_constexpr_depth_limit_exceeded : Note< def note_constexpr_call_limit_exceeded : Note< "constexpr evaluation hit maximum call limit">; def note_constexpr_lifetime_ended : Note< - "read of %select{temporary|variable}0 whose lifetime has ended">; -def note_constexpr_ltor_volatile_type : Note< - "read of volatile-qualified type %0 is not allowed in a constant expression">; -def note_constexpr_ltor_volatile_obj : Note< - "read of volatile %select{temporary|object %1|member %1}0 is not allowed in " + "%select{read of|assignment to}0 %select{temporary|variable}1 " + "whose lifetime has ended">; +def note_constexpr_access_uninit : Note< + "%select{read of|assignment to}0 object outside its lifetime " + "is not allowed in a constant expression">; +def note_constexpr_modify_const_type : Note< + "modification of object of const-qualified type %0 is not allowed " + "in a constant expression">; +def note_constexpr_access_volatile_type : Note< + "%select{read of|assignment to}0 volatile-qualified type %1 " + "is not allowed in a constant expression">; +def note_constexpr_access_volatile_obj : Note< + "%select{read of|assignment to}0 volatile " + "%select{temporary|object %2|member %2}1 is not allowed in " "a constant expression">; def note_constexpr_ltor_mutable : Note< "read of mutable member %0 is not allowed in a constant expression">; @@ -96,14 +105,19 @@ def note_constexpr_ltor_non_const_int : Note< "read of non-const variable %0 is not allowed in a constant expression">; def note_constexpr_ltor_non_constexpr : Note< "read of non-constexpr variable %0 is not allowed in a constant expression">; -def note_constexpr_read_past_end : Note< - "read of dereferenced one-past-the-end pointer is not allowed in a " - "constant expression">; -def note_constexpr_read_inactive_union_member : Note< - "read of member %0 of union with %select{active member %2|no active member}1 " +def note_constexpr_access_null : Note< + "%select{read of|assignment to}0 dereferenced null pointer " + "is not allowed in a constant expression">; +def note_constexpr_access_past_end : Note< + "%select{read of|assignment to}0 dereferenced one-past-the-end pointer " + "is not allowed in a constant expression">; +def note_constexpr_access_inactive_union_member : Note< + "%select{read of|assignment to}0 member %1 of union with " + "%select{active member %3|no active member}2 " "is not allowed in a constant expression">; -def note_constexpr_read_uninit : Note< - "read of uninitialized object is not allowed in a constant expression">; +def note_constexpr_modify_global : Note< + "a constant expression cannot modify an object that is visible outside " + "that expression">; def note_constexpr_calls_suppressed : Note< "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to " "see all)">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 2a7e8a7d3b..ccad975796 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -286,7 +286,7 @@ namespace { /// ParmBindings - Parameter bindings for this function call, indexed by /// parameters' function scope indices. - const APValue *Arguments; + APValue *Arguments; // Note that we intentionally use std::map here so that references to // values are stable. @@ -297,7 +297,7 @@ namespace { CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, - const APValue *Arguments); + APValue *Arguments); ~CallStackFrame(); }; @@ -597,7 +597,7 @@ void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info, CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, - const APValue *Arguments) + APValue *Arguments) : Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee), Index(Info.NextCallIndex++), This(This), Arguments(Arguments) { Info.CurrentCall = this; @@ -1471,6 +1471,12 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, // 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 we've carried on past an unevaluatable local variable initializer, // we can't go any further. This can happen during potential constant @@ -1542,15 +1548,15 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived, llvm_unreachable("base class missing from derived class's bases list"); } -/// Extract the value of a character from a string literal. CharType is used to -/// determine the expected signedness of the result -- a string literal used to -/// initialize an array of 'signed char' or 'unsigned char' might contain chars -/// of the wrong signedness. -static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, - uint64_t Index, QualType CharType) { +/// Extract the value of a character from a string literal. +static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, + uint64_t Index) { // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant - const StringLiteral *S = dyn_cast(Lit); - assert(S && "unexpected string literal expression kind"); + const StringLiteral *S = cast(Lit); + const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(S->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); assert(CharType->isIntegerType() && "unexpected character type"); APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(), @@ -1560,24 +1566,77 @@ static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, return Value; } -/// Extract the designated sub-object of an rvalue. -static bool ExtractSubobject(EvalInfo &Info, const Expr *E, - APValue &Obj, QualType ObjType, - const SubobjectDesignator &Sub, QualType SubType) { +// Expand a string literal into an array of characters. +static void expandStringLiteral(EvalInfo &Info, const Expr *Lit, + APValue &Result) { + const StringLiteral *S = cast(Lit); + const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(S->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); + assert(CharType->isIntegerType() && "unexpected character type"); + + unsigned Elts = CAT->getSize().getZExtValue(); + Result = APValue(APValue::UninitArray(), + std::min(S->getLength(), Elts), Elts); + APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(), + CharType->isUnsignedIntegerType()); + if (Result.hasArrayFiller()) + Result.getArrayFiller() = APValue(Value); + for (unsigned I = 0, N = Result.getArrayInitializedElts(); I != N; ++I) { + Value = S->getCodeUnit(I); + Result.getArrayInitializedElt(I) = APValue(Value); + } +} + +// Expand an array so that it has more than Index filled elements. +static void expandArray(APValue &Array, unsigned Index) { + unsigned Size = Array.getArraySize(); + assert(Index < Size); + + // Always at least double the number of elements for which we store a value. + unsigned OldElts = Array.getArrayInitializedElts(); + unsigned NewElts = std::max(Index+1, OldElts * 2); + NewElts = std::min(Size, std::max(NewElts, 8u)); + + // Copy the data across. + APValue NewValue(APValue::UninitArray(), NewElts, Size); + for (unsigned I = 0; I != OldElts; ++I) + NewValue.getArrayInitializedElt(I).swap(Array.getArrayInitializedElt(I)); + for (unsigned I = OldElts; I != NewElts; ++I) + NewValue.getArrayInitializedElt(I) = Array.getArrayFiller(); + if (NewValue.hasArrayFiller()) + NewValue.getArrayFiller() = Array.getArrayFiller(); + Array.swap(NewValue); +} + +/// Kinds of access we can perform on an object. +enum AccessKinds { + AK_Read, + AK_Assign +}; + +/// Find the designated sub-object of an rvalue. +template +typename SubobjectHandler::result_type +findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType, + const SubobjectDesignator &Sub, SubobjectHandler &handler) { if (Sub.Invalid) // A diagnostic will have already been produced. - return false; + return handler.failed(); if (Sub.isOnePastTheEnd()) { - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } if (Sub.Entries.empty()) - return true; + return handler.found(Obj, ObjType); if (Info.CheckingPotentialConstantExpression && Obj.isUninit()) // This object might be initialized later. - return false; + return handler.failed(); APValue *O = &Obj; // Walk the designator's path to find the subobject. @@ -1590,49 +1649,67 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, if (CAT->getSize().ule(Index)) { // Note, it should not be possible to form a pointer with a valid // designator which points more than one past the end of the array. - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } + + ObjType = CAT->getElementType(); + // An array object is represented as either an Array APValue or as an // LValue which refers to a string literal. if (O->isLValue()) { assert(I == N - 1 && "extracting subobject of character?"); assert(!O->hasLValuePath() || O->getLValuePath().empty()); - Obj = APValue(ExtractStringLiteralCharacter( - Info, O->getLValueBase().get(), Index, SubType)); - return true; - } else if (O->getArrayInitializedElts() > Index) + if (handler.AccessKind != AK_Read) + expandStringLiteral(Info, O->getLValueBase().get(), + *O); + else + return handler.foundString(*O, ObjType, Index); + } + + if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); - else + else if (handler.AccessKind != AK_Read) { + expandArray(*O, Index); + O = &O->getArrayInitializedElt(Index); + } else O = &O->getArrayFiller(); - ObjType = CAT->getElementType(); } else if (ObjType->isAnyComplexType()) { // Next subobject is a complex number. uint64_t Index = Sub.Entries[I].ArrayIndex; if (Index > 1) { - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } + + bool WasConstQualified = ObjType.isConstQualified(); + ObjType = ObjType->castAs()->getElementType(); + if (WasConstQualified) + ObjType.addConst(); + assert(I == N - 1 && "extracting subobject of scalar?"); if (O->isComplexInt()) { - Obj = APValue(Index ? O->getComplexIntImag() - : O->getComplexIntReal()); + return handler.found(Index ? O->getComplexIntImag() + : O->getComplexIntReal(), ObjType); } else { assert(O->isComplexFloat()); - Obj = APValue(Index ? O->getComplexFloatImag() - : O->getComplexFloatReal()); + return handler.found(Index ? O->getComplexFloatImag() + : O->getComplexFloatReal(), ObjType); } - return true; } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - if (Field->isMutable()) { + if (Field->isMutable() && handler.AccessKind == AK_Read) { Info.Diag(E, diag::note_constexpr_ltor_mutable, 1) << Field; Info.Note(Field->getLocation(), diag::note_declared_at); - return false; + return handler.failed(); } // Next subobject is a class, struct or union field. @@ -1641,49 +1718,151 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) { - Info.Diag(E, diag::note_constexpr_read_inactive_union_member) - << Field << !UnionField << UnionField; - return false; + Info.Diag(E, diag::note_constexpr_access_inactive_union_member) + << handler.AccessKind << Field << !UnionField << UnionField; + return handler.failed(); } O = &O->getUnionValue(); } else O = &O->getStructField(Field->getFieldIndex()); + + bool WasConstQualified = ObjType.isConstQualified(); ObjType = Field->getType(); + if (WasConstQualified && !Field->isMutable()) + ObjType.addConst(); if (ObjType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { // FIXME: Include a description of the path to the volatile subobject. - Info.Diag(E, diag::note_constexpr_ltor_volatile_obj, 1) - << 2 << Field; + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << handler.AccessKind << 2 << Field; Info.Note(Field->getLocation(), diag::note_declared_at); } else { Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); } - return false; + return handler.failed(); } } else { // Next subobject is a base class. const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl(); const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]); O = &O->getStructBase(getBaseIndex(Derived, Base)); + + bool WasConstQualified = ObjType.isConstQualified(); ObjType = Info.Ctx.getRecordType(Base); + if (WasConstQualified) + ObjType.addConst(); } if (O->isUninit()) { if (!Info.CheckingPotentialConstantExpression) - Info.Diag(E, diag::note_constexpr_read_uninit); + Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind; + return handler.failed(); + } + } + + return handler.found(*O, ObjType); +} + +struct ExtractSubobjectHandler { + EvalInfo &Info; + APValue &Obj; + + 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); + } + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + Obj = APValue(Value); + return true; + } + bool found(APFloat &Value, QualType SubobjType) { + Obj = APValue(Value); + return true; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + Obj = APValue(extractStringLiteralCharacter( + Info, Subobj.getLValueBase().get(), Character)); + return true; + } +}; +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); +} + +struct ModifySubobjectHandler { + EvalInfo &Info; + APValue &NewVal; + const Expr *E; + + typedef bool result_type; + static const AccessKinds AccessKind = AK_Assign; + + bool checkConst(QualType QT) { + // Assigning to a const object has undefined behavior. + if (QT.isConstQualified()) { + Info.Diag(E, diag::note_constexpr_modify_const_type) << QT; return false; } + return true; } - // This may look super-stupid, but it serves an important purpose: if we just - // swapped Obj and *O, we'd create an object which had 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; - O->swap(Tmp); - Obj.swap(Tmp); - return true; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + // We've been given ownership of NewVal, so just swap it in. + Subobj.swap(NewVal); + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + if (!NewVal.isInt()) { + // Maybe trying to write a cast pointer value into a complex? + Info.Diag(E); + return false; + } + Value = NewVal.getInt(); + return true; + } + bool found(APFloat &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + Value = NewVal.getFloat(); + return true; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + llvm_unreachable("shouldn't encounter string elements with ExpandArrays"); + } +}; +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 SubobjectDesignator &Sub, + APValue &NewVal) { + ModifySubobjectHandler Handler = { Info, NewVal, E }; + return findSubobject(Info, E, Obj, ObjType, Sub, Handler); } /// Find the position where two subobject designators diverge, or equivalently @@ -1744,14 +1923,14 @@ static bool AreElementsOfSameArray(QualType ObjType, } /// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on -/// the given lvalue. This can also be used for 'lvalue-to-lvalue' conversions +/// 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 we expect this conversion to produce, before -/// stripping cv-qualifiers in the case of a non-clas type. +/// \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, @@ -1764,8 +1943,7 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, const Expr *Base = LVal.Base.dyn_cast(); if (!LVal.Base) { - // FIXME: Indirection through a null pointer deserves a specific diagnostic. - Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr); + Info.Diag(Conv, diag::note_constexpr_access_null) << AK_Read; return false; } @@ -1773,7 +1951,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, if (LVal.CallIndex) { Frame = Info.getCallFrame(LVal.CallIndex); if (!Frame) { - Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base; + Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) + << AK_Read << !Base; NoteLValueLocation(Info, LVal.Base); return false; } @@ -1785,7 +1964,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, // semantics. if (Type.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_type) << Type; + Info.Diag(Conv, diag::note_constexpr_access_volatile_type) + << AK_Read << Type; else Info.Diag(Conv); return false; @@ -1812,7 +1992,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType VT = VD->getType(); if (VT.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 1 << VD; + Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1) + << AK_Read << 1 << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { Info.Diag(Conv); @@ -1857,37 +2038,15 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } } - if (!EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal)) - return false; - - if (isa(VD) || !VD->getAnyInitializer()->isLValue()) - return ExtractSubobject(Info, Conv, RVal, VT, LVal.Designator, Type); - - // The declaration was initialized by an lvalue, with no lvalue-to-rvalue - // conversion. This happens when the declaration and the lvalue should be - // considered synonymous, for instance when initializing an array of char - // from a string literal. Continue as if the initializer lvalue was the - // value we were originally given. - assert(RVal.getLValueOffset().isZero() && - "offset for lvalue init of non-reference"); - Base = RVal.getLValueBase().get(); - - if (unsigned CallIndex = RVal.getLValueCallIndex()) { - Frame = Info.getCallFrame(CallIndex); - if (!Frame) { - Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base; - NoteLValueLocation(Info, RVal.getLValueBase()); - return false; - } - } else { - Frame = 0; - } + return EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal) && + extractSubobject(Info, Conv, RVal, VT, LVal.Designator); } // Volatile temporary objects cannot be read in constant expressions. if (Base->getType().isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 0; + 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); @@ -1896,6 +2055,12 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } 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]; @@ -1908,6 +2073,11 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, if (!Evaluate(RVal, Info, CLE->getInitializer())) return false; } else if (isa(Base)) { + if (Info.getLangOpts().CPlusPlus1y && + Info.CheckingPotentialConstantExpression && + !Base->getType().isConstQualified()) + return false; + // 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 @@ -1917,8 +2087,105 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, return false; } - return ExtractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator, - Type); + return extractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator); +} + +/// 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; + } + + if (LVal.Designator.Invalid) + // A diagnostic will have already been produced. + return false; + + if (Info.CheckingPotentialConstantExpression) + return false; + + const Expr *Base = LVal.Base.dyn_cast(); + + if (!LVal.Base) { + Info.Diag(E, diag::note_constexpr_access_null) << AK_Assign; + return false; + } + + if (!LVal.CallIndex) { + Info.Diag(E, diag::note_constexpr_modify_global); + return false; + } + + CallStackFrame *Frame = Info.getCallFrame(LVal.CallIndex); + if (!Frame) { + Info.Diag(E, diag::note_constexpr_lifetime_ended, 1) + << AK_Assign << !Base; + NoteLValueLocation(Info, LVal.Base); + 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); + 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); } /// Build an lvalue for the object argument of a member function call. @@ -2264,8 +2531,11 @@ static bool HandleFunctionCall(SourceLocation CallLoc, CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data()); EvalStmtResult ESR = EvaluateStmt(Result, Info, Body); - if (ESR == ESR_Succeeded) + if (ESR == ESR_Succeeded) { + if (Callee->getResultType()->isVoidType()) + return true; Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return); + } return ESR == ESR_Returned; } @@ -2715,7 +2985,7 @@ public: SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); - return ExtractSubobject(Info, E, Val, BaseTy, Designator, E->getType()) && + return extractSubobject(Info, E, Val, BaseTy, Designator) && DerivedSuccess(Val, E); } @@ -2829,6 +3099,16 @@ 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); + } } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5f0908ae93..48860e027b 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -845,7 +845,8 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, << (VD->getTLSKind() == VarDecl::TLS_Dynamic); return false; } - if (SemaRef.RequireLiteralType( + if (!VD->getType()->isDependentType() && + SemaRef.RequireLiteralType( VD->getLocation(), VD->getType(), diag::err_constexpr_local_var_non_literal_type, isa(Dcl))) @@ -1135,11 +1136,11 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { // statement. We still do, unless the return type is void, because // otherwise if there's no return statement, the function cannot // be used in a core constant expression. + bool OK = getLangOpts().CPlusPlus1y && Dcl->getResultType()->isVoidType(); Diag(Dcl->getLocation(), - getLangOpts().CPlusPlus1y && Dcl->getResultType()->isVoidType() - ? diag::warn_cxx11_compat_constexpr_body_no_return - : diag::err_constexpr_body_no_return); - return false; + OK ? diag::warn_cxx11_compat_constexpr_body_no_return + : diag::err_constexpr_body_no_return); + return OK; } if (ReturnStmts.size() > 1) { Diag(ReturnStmts.back(), diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 0e513992ba..3c942fa40e 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -97,6 +97,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, DeclT = S.Context.getConstantArrayType(IAT->getElementType(), ConstVal, ArrayType::Normal, 0); + Str->setType(DeclT); return; } diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index b8f57b6c64..b44bb8c907 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -304,16 +304,16 @@ struct Str { expected-note {{reinterpret_cast is not allowed in a constant expression}} int c : (S*)(long)(sptr) == (S*)(long)(sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int d : (S*)(42) == (S*)(42); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int e : (Str*)(sptr) == (Str*)(sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int f : &(U&)(*sptr) == &(U&)(*sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int g : (S*)(void*)(sptr) == sptr; // \ expected-warning {{not an integral constant expression}} \ expected-note {{cast from 'void *' is not allowed in a constant expression}} @@ -362,7 +362,7 @@ constexpr char c0 = "nought index"[0]; constexpr char c1 = "nice index"[10]; constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is past the end}} expected-note {{read of dereferenced one-past-the-end pointer}} constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}} expected-note {{cannot refer to element -1 of array of 15 elements}} -constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast which performs the conversions of a reinterpret_cast}} +constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast that performs the conversions of a reinterpret_cast}} constexpr const char *p = "test" + 2; static_assert(*p == 's', ""); diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index dab2496ed1..543c7cffdb 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -124,3 +124,160 @@ constexpr int namespace_alias() { namespace N = NS; return N::n; } + +namespace assign { + constexpr int a = 0; + const int b = 0; + int c = 0; // expected-note 2{{here}} + + constexpr void set(const int &a, int b) { + const_cast(a) = b; // expected-note 2{{constant expression cannot modify an object that is visible outside that expression}} + } + constexpr int wrap(int a, int b) { + set(a, b); + return a; + } + + static_assert((set(a, 1), a) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(a, 1)'}} + static_assert((set(b, 1), b) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(b, 1)'}} + static_assert((set(c, 1), c) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}} + + static_assert(wrap(a, 1) == 1, ""); + static_assert(wrap(b, 1) == 1, ""); + static_assert(wrap(c, 1) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}} +} + +namespace string_assign { + template + constexpr void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; + } + template + constexpr void reverse(Iterator begin, Iterator end) { +#if 0 // FIXME: once implementation is complete... + while (begin != end && begin != --end) + swap(*begin++, *end); +#else + if (begin != end) { + end = end - 1; + if (begin == end) + return; + swap(*begin, *end); + begin = begin + 1; + reverse(begin, end); + } +#endif + } + template + constexpr bool equal(Iterator1 a, Iterator1 ae, Iterator2 b, Iterator2 be) { +#if 0 // FIXME: once implementation is complete... + while (a != ae && b != be) { + if (*a != *b) + return false; + ++a, ++b; + } +#else + if (a != ae && b != be) { + if (*a != *b) + return false; + a = a + 1; + b = b + 1; + return equal(a, ae, b, be); + } +#endif + return a == ae && b == be; + } + constexpr bool test1(int n) { + char stuff[100] = "foobarfoo"; + const char stuff2[100] = "oofraboof"; + reverse(stuff, stuff + n); // expected-note {{cannot refer to element 101 of array of 100 elements}} + return equal(stuff, stuff + n, stuff2, stuff2 + n); + } + static_assert(!test1(1), ""); + static_assert(test1(3), ""); + static_assert(!test1(6), ""); + static_assert(test1(9), ""); + static_assert(!test1(100), ""); + static_assert(!test1(101), ""); // expected-error {{constant expression}} expected-note {{in call to 'test1(101)'}} + + // FIXME: We should be able to reject this before it's called + constexpr void f() { + char foo[10] = { "z" }; // expected-note {{here}} + foo[10] = 'x'; // expected-warning {{past the end}} expected-note {{assignment to dereferenced one-past-the-end pointer}} + } + constexpr int k = (f(), 0); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace array_resize { + constexpr int do_stuff(int k1, int k2) { + int arr[1234] = { 1, 2, 3, 4 }; + arr[k1] = 5; // expected-note {{past-the-end}} expected-note {{cannot refer to element 1235}} expected-note {{cannot refer to element -1}} + return arr[k2]; + } + static_assert(do_stuff(1, 2) == 3, ""); + static_assert(do_stuff(0, 0) == 5, ""); + static_assert(do_stuff(1233, 1233) == 5, ""); + static_assert(do_stuff(1233, 0) == 1, ""); + static_assert(do_stuff(1234, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(do_stuff(1235, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(do_stuff(-1, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace potential_const_expr { + constexpr void set(int &n) { n = 1; } + constexpr int div_zero_1() { int z = 0; set(z); return 100 / z; } // no error + constexpr int div_zero_2() { // expected-error {{never produces a constant expression}} + int z = 0; + return 100 / (set(z), 0); // expected-note {{division by zero}} + } +} + +namespace subobject { + union A { constexpr A() : y(5) {} int x, y; }; + struct B { A a; }; + struct C : B {}; + union D { constexpr D() : c() {} constexpr D(int n) : n(n) {} C c; int n; }; + constexpr void f(D &d) { + d.c.a.y = 3; + // expected-note@-1 {{cannot modify an object that is visible outside}} + // expected-note@-2 {{assignment to member 'c' of union with active member 'n'}} + } + constexpr bool check(D &d) { return d.c.a.y == 3; } + + constexpr bool g() { D d; f(d); return d.c.a.y == 3; } + static_assert(g(), ""); + + D d; + constexpr bool h() { f(d); return check(d); } // expected-note {{in call}} + static_assert(h(), ""); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool i() { D d(0); f(d); return check(d); } // expected-note {{in call}} + static_assert(i(), ""); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool j() { D d; d.c.a.x = 3; return check(d); } // expected-note {{assignment to member 'x' of union with active member 'y'}} + static_assert(j(), ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace lifetime { + constexpr int &&id(int &&n) { return static_cast(n); } + constexpr int &&dead() { return id(0); } // expected-note {{temporary created here}} + constexpr int bad() { int &&n = dead(); n = 1; return n; } // expected-note {{assignment to temporary whose lifetime has ended}} + static_assert(bad(), ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace const_modify { + constexpr int modify(int &n) { return n = 1; } // expected-note {{modification of object of const-qualified type 'const int'}} + constexpr int test1() { int k = 0; return modify(k); } + constexpr int test2() { const int k = 0; return modify(const_cast(k)); } // expected-note {{in call}} + static_assert(test1() == 1, ""); + static_assert(test2() == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace null { + constexpr int test(int *p) { + return *p = 123; // expected-note {{assignment to dereferenced null pointer}} + } + static_assert(test(0), ""); // expected-error {{constant expression}} expected-note {{in call}} +} diff --git a/test/SemaCXX/constexpr-printing.cpp b/test/SemaCXX/constexpr-printing.cpp index 9170fa1ec2..e545f45d60 100644 --- a/test/SemaCXX/constexpr-printing.cpp +++ b/test/SemaCXX/constexpr-printing.cpp @@ -9,7 +9,7 @@ struct S { int n, m; }; -constexpr int extract(const S &s) { return s.n; } // expected-note {{read of uninitialized object is not allowed in a constant expression}} +constexpr int extract(const S &s) { return s.n; } // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} constexpr S s1; // ok void f() { diff --git a/test/SemaCXX/constexpr-value-init.cpp b/test/SemaCXX/constexpr-value-init.cpp index e459f097b9..d137bd88db 100644 --- a/test/SemaCXX/constexpr-value-init.cpp +++ b/test/SemaCXX/constexpr-value-init.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify struct A { - constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{uninitialized}} + constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{outside its lifetime}} int a; int b; }; diff --git a/test/SemaCXX/i-c-e-cxx.cpp b/test/SemaCXX/i-c-e-cxx.cpp index 5631577e0f..c80323ccd7 100644 --- a/test/SemaCXX/i-c-e-cxx.cpp +++ b/test/SemaCXX/i-c-e-cxx.cpp @@ -60,7 +60,7 @@ int* y = reinterpret_cast(x); // expected-error {{cannot initialize // This isn't an integral constant expression, but make sure it folds anyway. struct PR8836 { char _; long long a; }; // expected-warning {{long long}} -int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} +int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} const int nonconst = 1.0; // expected-note {{declared here}} int arr[nonconst]; // expected-warning {{folded to constant array as an extension}} expected-note {{initializer of 'nonconst' is not a constant expression}} -- 2.40.0