From b4e85ed51905fc94378d7b4ff62b06e0d08042b7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 6 Jan 2012 16:39:00 +0000 Subject: [PATCH] C++11 generalized constant expressions: implement checking and diagnostics for pointer-arithmetic-related undefined behavior and unspecified results. We continue to fold such values, but now notice they aren't constant expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147659 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 15 +- lib/AST/ExprConstant.cpp | 546 ++++++++++++--------- test/CXX/expr/expr.const/p2-0x.cpp | 21 + test/SemaCXX/constant-expression-cxx11.cpp | 49 +- test/SemaCXX/constexpr-backtrace-limit.cpp | 4 +- 5 files changed, 378 insertions(+), 257 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index df0a50823d..eb46e5f6f0 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -16,6 +16,8 @@ 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" " 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">; def note_constexpr_overflow : Note< "value %0 is outside the range of representable values of type %1">; def note_constexpr_invalid_function : Note< @@ -28,9 +30,20 @@ def note_constexpr_non_global : Note< "%select{temporary|%4}2 %select{is not a constant expression|" "cannot be returned from a constexpr function|" "cannot be used to initialize a member in a constant expression}3">; +def note_constexpr_array_index : Note<"cannot refer to element %0 of " + "%select{array of %2 elements|non-array object}1 in a constant expression">; +def note_constexpr_pointer_arithmetic : Note< + "cannot refer to element %0 of non-array object in a constant " + "expression">; def note_constexpr_past_end : Note< - "dereferenced pointer past the end of %select{|subobject of}0 " + "dereferenced pointer past the end of %select{|subobject of }0" "%select{temporary|%2}1 is not a constant expression">; +def note_constexpr_past_end_subobject : Note< + "cannot access %select{base class|derived class|field|array element}0 of " + "pointer past the end of object">; +def note_constexpr_null_subobject : Note< + "cannot %select{access base class of|access derived class of|access field of|" + "access array element of|perform pointer arithmetic on}0 null pointer">; def note_constexpr_var_init_non_constant : Note< "initializer of %0 is not a constant expression">; def note_constexpr_typeid_polymorphic : Note< diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 949988db1a..1ad17ea376 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -76,24 +76,38 @@ namespace { return Value.getInt(); } - /// Determine whether the described subobject is an array element. - static bool SubobjectIsArrayElement(QualType Base, - ArrayRef Path) { - bool IsArrayElement = false; - const Type *T = Base.getTypePtr(); + /// Find the path length and type of the most-derived subobject in the given + /// path, and find the size of the containing array, if any. + static + unsigned findMostDerivedSubobject(ASTContext &Ctx, QualType Base, + ArrayRef Path, + uint64_t &ArraySize, QualType &Type) { + unsigned MostDerivedLength = 0; + Type = Base; for (unsigned I = 0, N = Path.size(); I != N; ++I) { - IsArrayElement = T && T->isArrayType(); - if (IsArrayElement) - T = T->getBaseElementTypeUnsafe(); - else if (const FieldDecl *FD = getAsField(Path[I])) - T = FD->getType().getTypePtr(); - else + if (Type->isArrayType()) { + const ConstantArrayType *CAT = + cast(Ctx.getAsArrayType(Type)); + Type = CAT->getElementType(); + ArraySize = CAT->getSize().getZExtValue(); + MostDerivedLength = I + 1; + } else if (const FieldDecl *FD = getAsField(Path[I])) { + Type = FD->getType(); + ArraySize = 0; + MostDerivedLength = I + 1; + } else { // Path[I] describes a base class. - T = 0; + ArraySize = 0; + } } - return IsArrayElement; + return MostDerivedLength; } + // The order of this enum is important for diagnostics. + enum CheckSubobjectKind { + CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex + }; + /// A path from a glvalue to a subobject of that glvalue. struct SubobjectDesignator { /// True if the subobject was named in a manner not supported by C++11. Such @@ -101,32 +115,43 @@ namespace { /// and we cannot perform lvalue-to-rvalue conversions on them. bool Invalid : 1; - /// Whether this designates an array element. - bool ArrayElement : 1; + /// Is this a pointer one past the end of an object? + bool IsOnePastTheEnd : 1; + + /// The length of the path to the most-derived object of which this is a + /// subobject. + unsigned MostDerivedPathLength : 30; + + /// The size of the array of which the most-derived object is an element, or + /// 0 if the most-derived object is not an array element. + uint64_t MostDerivedArraySize; - /// Whether this designates 'one past the end' of the current subobject. - bool OnePastTheEnd : 1; + /// The type of the most derived object referred to by this address. + QualType MostDerivedType; typedef APValue::LValuePathEntry PathEntry; /// The entries on the path from the glvalue to the designated subobject. SmallVector Entries; - SubobjectDesignator() : - Invalid(false), ArrayElement(false), OnePastTheEnd(false) {} + SubobjectDesignator() : Invalid(true) {} - SubobjectDesignator(const APValue &V) : - Invalid(!V.isLValue() || !V.hasLValuePath()), ArrayElement(false), - OnePastTheEnd(false) { + explicit SubobjectDesignator(QualType T) + : Invalid(false), IsOnePastTheEnd(false), MostDerivedPathLength(0), + MostDerivedArraySize(0), MostDerivedType(T) {} + + SubobjectDesignator(ASTContext &Ctx, const APValue &V) + : Invalid(!V.isLValue() || !V.hasLValuePath()), IsOnePastTheEnd(false), + MostDerivedPathLength(0), MostDerivedArraySize(0) { if (!Invalid) { + IsOnePastTheEnd = V.isLValueOnePastTheEnd(); ArrayRef VEntries = V.getLValuePath(); Entries.insert(Entries.end(), VEntries.begin(), VEntries.end()); if (V.getLValueBase()) - ArrayElement = SubobjectIsArrayElement(getType(V.getLValueBase()), - V.getLValuePath()); - else - assert(V.getLValuePath().empty() &&"Null pointer with nonempty path"); - OnePastTheEnd = V.isLValueOnePastTheEnd(); + MostDerivedPathLength = + findMostDerivedSubobject(Ctx, getType(V.getLValueBase()), + V.getLValuePath(), MostDerivedArraySize, + MostDerivedType); } } @@ -134,46 +159,76 @@ namespace { Invalid = true; Entries.clear(); } - /// Update this designator to refer to the given element within this array. - void addIndex(uint64_t N) { - if (Invalid) return; - if (OnePastTheEnd) { - setInvalid(); - return; - } + + /// Determine whether this is a one-past-the-end pointer. + bool isOnePastTheEnd() const { + if (IsOnePastTheEnd) + return true; + if (MostDerivedArraySize && + Entries[MostDerivedPathLength - 1].ArrayIndex == MostDerivedArraySize) + return true; + return false; + } + + /// Check that this refers to a valid subobject. + bool isValidSubobject() const { + if (Invalid) + return false; + return !isOnePastTheEnd(); + } + /// Check that this refers to a valid subobject, and if not, produce a + /// relevant diagnostic and set the designator as invalid. + bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind CSK); + + /// Update this designator to refer to the first element within this array. + void addArrayUnchecked(const ConstantArrayType *CAT) { PathEntry Entry; - Entry.ArrayIndex = N; + Entry.ArrayIndex = 0; Entries.push_back(Entry); - ArrayElement = true; + + // This is a most-derived object. + MostDerivedType = CAT->getElementType(); + MostDerivedArraySize = CAT->getSize().getZExtValue(); + MostDerivedPathLength = Entries.size(); } /// Update this designator to refer to the given base or member of this /// object. - void addDecl(const Decl *D, bool Virtual = false) { - if (Invalid) return; - if (OnePastTheEnd) { - setInvalid(); - return; - } + void addDeclUnchecked(const Decl *D, bool Virtual = false) { PathEntry Entry; APValue::BaseOrMemberType Value(D, Virtual); Entry.BaseOrMember = Value.getOpaqueValue(); Entries.push_back(Entry); - ArrayElement = false; + + // If this isn't a base class, it's a new most-derived object. + if (const FieldDecl *FD = dyn_cast(D)) { + MostDerivedType = FD->getType(); + MostDerivedArraySize = 0; + MostDerivedPathLength = Entries.size(); + } } + void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, uint64_t N); /// Add N to the address of this subobject. - void adjustIndex(uint64_t N) { + void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { if (Invalid) return; - if (ArrayElement) { - // FIXME: Make sure the index stays within bounds, or one past the end. + if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) { Entries.back().ArrayIndex += N; + if (Entries.back().ArrayIndex > MostDerivedArraySize) { + diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex); + setInvalid(); + } return; } - if (OnePastTheEnd && N == (uint64_t)-1) - OnePastTheEnd = false; - else if (!OnePastTheEnd && N == 1) - OnePastTheEnd = true; - else if (N != 0) + // [expr.add]p4: For the purposes of these operators, a pointer to a + // nonarray object behaves the same as a pointer to the first element of + // an array of length one with the type of the object as its element type. + if (IsOnePastTheEnd && N == (uint64_t)-1) + IsOnePastTheEnd = false; + else if (!IsOnePastTheEnd && N == 1) + IsOnePastTheEnd = true; + else if (N != 0) { + diagnosePointerArithmetic(Info, E, uint64_t(IsOnePastTheEnd) + N); setInvalid(); + } } }; @@ -205,8 +260,8 @@ namespace { CCValue(LValueBase B, const CharUnits &O, CallStackFrame *F, const SubobjectDesignator &D) : APValue(B, O, APValue::NoLValuePath()), CallFrame(F), Designator(D) {} - CCValue(const APValue &V, GlobalValue) : - APValue(V), CallFrame(0), Designator(V) {} + CCValue(ASTContext &Ctx, const APValue &V, GlobalValue) : + APValue(V), CallFrame(0), Designator(Ctx, V) {} CCValue(const ValueDecl *D, bool IsDerivedMember, ArrayRef Path) : APValue(D, IsDerivedMember, Path) {} @@ -397,6 +452,31 @@ namespace { }; } +bool SubobjectDesignator::checkSubobject(EvalInfo &Info, const Expr *E, + CheckSubobjectKind CSK) { + if (Invalid) + return false; + if (isOnePastTheEnd()) { + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_past_end_subobject) + << CSK; + setInvalid(); + return false; + } + return true; +} + +void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info, + const Expr *E, uint64_t N) { + if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_array_index) + << static_cast(N) << /*array*/ 0 + << static_cast(MostDerivedArraySize); + else + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_array_index) + << static_cast(N) << /*non-array*/ 1; + setInvalid(); +} + CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, const CCValue *Arguments) @@ -434,7 +514,7 @@ static void describeCall(CallStackFrame *Frame, llvm::raw_ostream &Out) { // Deliberately slice off the frame to form an APValue we can print. APValue Value(Arg.getLValueBase(), Arg.getLValueOffset(), Arg.getLValueDesignator().Entries, - Arg.getLValueDesignator().OnePastTheEnd); + Arg.getLValueDesignator().IsOnePastTheEnd); Value.printPretty(Out, Frame->Info.Ctx, Param->getType()); } @@ -544,7 +624,44 @@ namespace { Base = B; Offset = CharUnits::Zero(); Frame = F; - Designator = SubobjectDesignator(); + Designator = SubobjectDesignator(getType(B)); + } + + // Check that this LValue is not based on a null pointer. If it is, produce + // a diagnostic and mark the designator as invalid. + bool checkNullPointer(EvalInfo &Info, const Expr *E, + CheckSubobjectKind CSK) { + if (Designator.Invalid) + return false; + if (!Base) { + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_null_subobject) + << CSK; + Designator.setInvalid(); + return false; + } + return true; + } + + // Check this LValue refers to an object. If not, set the designator to be + // invalid and emit a diagnostic. + bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind CSK) { + return checkNullPointer(Info, E, CSK) && + Designator.checkSubobject(Info, E, CSK); + } + + void addDecl(EvalInfo &Info, const Expr *E, + const Decl *D, bool Virtual = false) { + checkSubobject(Info, E, isa(D) ? CSK_Field : CSK_Base); + Designator.addDeclUnchecked(D, Virtual); + } + void addArray(EvalInfo &Info, const Expr *E, const ConstantArrayType *CAT) { + checkSubobject(Info, E, CSK_ArrayToPointer); + Designator.addArrayUnchecked(CAT); + } + void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { + if (!checkNullPointer(Info, E, CSK_ArrayIndex)) + return; + Designator.adjustIndex(Info, E, N); } }; @@ -714,7 +831,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { } /// Check that this reference or pointer core constant expression is a valid -/// value for a constant expression. Type T should be either LValue or CCValue. +/// value for an address or reference constant expression. Type T should be +/// either LValue or CCValue. template static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, const T &LVal, APValue &Value, @@ -739,20 +857,38 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, return false; } - // A constant expression must refer to an object or be a null pointer. - if (Designator.Invalid || - (!LVal.getLValueBase() && !Designator.Entries.empty())) { - // FIXME: This is not a core constant expression. We should have already - // produced a CCE diagnostic. + bool IsReferenceType = E->isGLValue(); + + if (Designator.Invalid) { + // This is not a core constant expression. A diagnostic will have already + // been produced. + if (IsReferenceType) + return false; + + // Allow this for pointers, so we can fold things like integers cast to + // pointers. Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), APValue::NoLValuePath()); return true; } + Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), + Designator.Entries, Designator.IsOnePastTheEnd); + + // Allow address constant expressions to be past-the-end pointers. This is + // an extension: the standard requires them to point to an object. + if (!IsReferenceType) + return true; + + // A reference constant expression must refer to an object. + if (!Base) { + // FIXME: diagnostic + Info.CCEDiag(E->getExprLoc()); + return false; + } + // Does this refer one past the end of some object? - // This is technically not an address constant expression nor a reference - // constant expression, but we allow it for address constant expressions. - if (E->isGLValue() && Base && Designator.OnePastTheEnd) { + if (Designator.isOnePastTheEnd()) { const ValueDecl *VD = Base.dyn_cast(); Info.Diag(E->getExprLoc(), diag::note_constexpr_past_end, 1) << !Designator.Entries.empty() << !!VD << VD; @@ -764,8 +900,6 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, return false; } - Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), - Designator.Entries, Designator.OnePastTheEnd); return true; } @@ -980,49 +1114,22 @@ static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E, return false; } -static bool FindMostDerivedObject(EvalInfo &Info, const LValue &LVal, - const CXXRecordDecl *&MostDerivedType, - unsigned &MostDerivedPathLength, - bool &MostDerivedIsArrayElement) { - const SubobjectDesignator &D = LVal.Designator; - if (D.Invalid || !LVal.Base) - return false; - - const Type *T = getType(LVal.Base).getTypePtr(); - - // Find path prefix which leads to the most-derived subobject. - MostDerivedType = T->getAsCXXRecordDecl(); - MostDerivedPathLength = 0; - MostDerivedIsArrayElement = false; - - for (unsigned I = 0, N = D.Entries.size(); I != N; ++I) { - bool IsArray = T && T->isArrayType(); - if (IsArray) - T = T->getBaseElementTypeUnsafe(); - else if (const FieldDecl *FD = getAsField(D.Entries[I])) - T = FD->getType().getTypePtr(); - else - T = 0; - - if (T) { - MostDerivedType = T->getAsCXXRecordDecl(); - MostDerivedPathLength = I + 1; - MostDerivedIsArrayElement = IsArray; - } - } +/// Cast an lvalue referring to a base subobject to a derived class, by +/// truncating the lvalue's path to the given length. +static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, + const RecordDecl *TruncatedType, + unsigned TruncatedElements) { + SubobjectDesignator &D = Result.Designator; - // (B*)&d + 1 has no most-derived object. - if (D.OnePastTheEnd && MostDerivedPathLength != D.Entries.size()) + // Check we actually point to a derived class object. + if (TruncatedElements == D.Entries.size()) + return true; + assert(TruncatedElements >= D.MostDerivedPathLength && + "not casting to a derived class"); + if (!Result.checkSubobject(Info, E, CSK_Derived)) return false; - return MostDerivedType != 0; -} - -static void TruncateLValueBasePath(EvalInfo &Info, LValue &Result, - const RecordDecl *TruncatedType, - unsigned TruncatedElements, - bool IsArrayElement) { - SubobjectDesignator &D = Result.Designator; + // Truncate the path to the subobject, and remove any derived-to-base offsets. const RecordDecl *RD = TruncatedType; for (unsigned I = TruncatedElements, N = D.Entries.size(); I != N; ++I) { const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); @@ -1034,58 +1141,47 @@ static void TruncateLValueBasePath(EvalInfo &Info, LValue &Result, RD = Base; } D.Entries.resize(TruncatedElements); - D.ArrayElement = IsArrayElement; -} - -/// If the given LValue refers to a base subobject of some object, find the most -/// derived object and the corresponding complete record type. This is necessary -/// in order to find the offset of a virtual base class. -static bool ExtractMostDerivedObject(EvalInfo &Info, LValue &Result, - const CXXRecordDecl *&MostDerivedType) { - unsigned MostDerivedPathLength; - bool MostDerivedIsArrayElement; - if (!FindMostDerivedObject(Info, Result, MostDerivedType, - MostDerivedPathLength, MostDerivedIsArrayElement)) - return false; - - // Remove the trailing base class path entries and their offsets. - TruncateLValueBasePath(Info, Result, MostDerivedType, MostDerivedPathLength, - MostDerivedIsArrayElement); return true; } -static void HandleLValueDirectBase(EvalInfo &Info, LValue &Obj, +static void HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXRecordDecl *Derived, const CXXRecordDecl *Base, const ASTRecordLayout *RL = 0) { if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived); Obj.getLValueOffset() += RL->getBaseClassOffset(Base); - Obj.Designator.addDecl(Base, /*Virtual*/ false); + Obj.addDecl(Info, E, Base, /*Virtual*/ false); } -static bool HandleLValueBase(EvalInfo &Info, LValue &Obj, +static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXRecordDecl *DerivedDecl, const CXXBaseSpecifier *Base) { const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); if (!Base->isVirtual()) { - HandleLValueDirectBase(Info, Obj, DerivedDecl, BaseDecl); + HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl); return true; } + SubobjectDesignator &D = Obj.Designator; + if (D.Invalid) + return false; + // Extract most-derived object and corresponding type. - if (!ExtractMostDerivedObject(Info, Obj, DerivedDecl)) + DerivedDecl = D.MostDerivedType->getAsCXXRecordDecl(); + if (!CastToDerivedClass(Info, E, Obj, DerivedDecl, D.MostDerivedPathLength)) return false; + // Find the virtual base class. const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl); Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl); - Obj.Designator.addDecl(BaseDecl, /*Virtual*/ true); + Obj.addDecl(Info, E, BaseDecl, /*Virtual*/ true); return true; } /// Update LVal to refer to the given field, which must be a member of the type /// currently described by LVal. -static void HandleLValueMember(EvalInfo &Info, LValue &LVal, +static void HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, const FieldDecl *FD, const ASTRecordLayout *RL = 0) { if (!RL) @@ -1093,7 +1189,7 @@ static void HandleLValueMember(EvalInfo &Info, LValue &LVal, unsigned I = FD->getFieldIndex(); LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I)); - LVal.Designator.addDecl(FD); + LVal.addDecl(Info, E, FD); } /// Get the size of the given type in char units. @@ -1107,6 +1203,7 @@ static bool HandleSizeof(EvalInfo &Info, QualType Type, CharUnits &Size) { if (!Type->isConstantSizeType()) { // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2. + // FIXME: Diagnostic. return false; } @@ -1116,18 +1213,20 @@ static bool HandleSizeof(EvalInfo &Info, QualType Type, CharUnits &Size) { /// Update a pointer value to model pointer arithmetic. /// \param Info - Information about the ongoing evaluation. +/// \param E - The expression being evaluated, for diagnostic purposes. /// \param LVal - The pointer value to be updated. /// \param EltTy - The pointee type represented by LVal. /// \param Adjustment - The adjustment, in objects of type EltTy, to add. -static bool HandleLValueArrayAdjustment(EvalInfo &Info, LValue &LVal, - QualType EltTy, int64_t Adjustment) { +static bool HandleLValueArrayAdjustment(EvalInfo &Info, const Expr *E, + LValue &LVal, QualType EltTy, + int64_t Adjustment) { CharUnits SizeOfPointee; if (!HandleSizeof(Info, EltTy, SizeOfPointee)) return false; // Compute the new offset in the appropriate width. LVal.Offset += Adjustment * SizeOfPointee; - LVal.Designator.adjustIndex(Adjustment); + LVal.adjustIndex(Info, E, Adjustment); return true; } @@ -1156,7 +1255,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 = CCValue(*Info.EvaluatingDeclValue, CCValue::GlobalValue()); + Result = CCValue(Info.Ctx, *Info.EvaluatingDeclValue, + CCValue::GlobalValue()); return !Result.isUninit(); } @@ -1183,7 +1283,7 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.addNotes(Notes); } - Result = CCValue(*VD->getEvaluatedValue(), CCValue::GlobalValue()); + Result = CCValue(Info.Ctx, *VD->getEvaluatedValue(), CCValue::GlobalValue()); return true; } @@ -1211,11 +1311,10 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived, static bool ExtractSubobject(EvalInfo &Info, const Expr *E, CCValue &Obj, QualType ObjType, const SubobjectDesignator &Sub, QualType SubType) { - if (Sub.Invalid) { - Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + if (Sub.Invalid) + // A diagnostic will have already been produced. return false; - } - if (Sub.OnePastTheEnd) { + if (Sub.isOnePastTheEnd()) { Info.Diag(E->getExprLoc(), Info.getLangOpts().CPlusPlus0x ? (unsigned)diag::note_constexpr_read_past_end : (unsigned)diag::note_invalid_subexpr_in_const_expr); @@ -1288,7 +1387,7 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, } } - Obj = CCValue(*O, CCValue::GlobalValue()); + Obj = CCValue(Info.Ctx, *O, CCValue::GlobalValue()); return true; } @@ -1309,6 +1408,10 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, if (!Info.getLangOpts().CPlusPlus) Info.CCEDiag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + if (LVal.Designator.Invalid) + // A diagnostic will have already been produced. + return false; + const Expr *Base = LVal.Base.dyn_cast(); CallStackFrame *Frame = LVal.Frame; SourceLocation Loc = Conv->getExprLoc(); @@ -1513,16 +1616,9 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, if (MemPtr.isDerivedMember()) { // This is a member of some derived class. Truncate LV appropriately. - const CXXRecordDecl *MostDerivedType; - unsigned MostDerivedPathLength; - bool MostDerivedIsArrayElement; - if (!FindMostDerivedObject(Info, LV, MostDerivedType, MostDerivedPathLength, - MostDerivedIsArrayElement)) - return 0; - // The end of the derived-to-base path for the base object must match the // derived-to-base path for the member pointer. - if (MostDerivedPathLength + MemPtr.Path.size() > + if (LV.Designator.MostDerivedPathLength + MemPtr.Path.size() > LV.Designator.Entries.size()) return 0; unsigned PathLengthToMember = @@ -1536,11 +1632,9 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, } // Truncate the lvalue to the appropriate derived class. - bool ResultIsArray = false; - if (PathLengthToMember == MostDerivedPathLength) - ResultIsArray = MostDerivedIsArrayElement; - TruncateLValueBasePath(Info, LV, MemPtr.getContainingRecord(), - PathLengthToMember, ResultIsArray); + if (!CastToDerivedClass(Info, BO, LV, MemPtr.getContainingRecord(), + PathLengthToMember)) + return 0; } else if (!MemPtr.Path.empty()) { // Extend the LValue path with the member pointer's path. LV.Designator.Entries.reserve(LV.Designator.Entries.size() + @@ -1555,11 +1649,11 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, // The first class in the path is that of the lvalue. for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) { const CXXRecordDecl *Base = MemPtr.Path[N - I - 1]; - HandleLValueDirectBase(Info, LV, RD, Base); + HandleLValueDirectBase(Info, BO, LV, RD, Base); RD = Base; } // Finally cast to the class containing the member. - HandleLValueDirectBase(Info, LV, RD, MemPtr.getContainingRecord()); + HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord()); } // Add the member. Note that we cannot build bound member functions here. @@ -1567,7 +1661,7 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, // FIXME: Deal with IndirectFieldDecls. const FieldDecl *FD = dyn_cast(MemPtr.getDecl()); if (!FD) return 0; - HandleLValueMember(Info, LV, FD); + HandleLValueMember(Info, BO, LV, FD); } return MemPtr.getDecl(); @@ -1577,40 +1671,38 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, /// the provided lvalue, which currently refers to the base object. static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, LValue &Result) { - const CXXRecordDecl *MostDerivedType; - unsigned MostDerivedPathLength; - bool MostDerivedIsArrayElement; - - // Check this cast doesn't take us outside the object. - if (!FindMostDerivedObject(Info, Result, MostDerivedType, - MostDerivedPathLength, - MostDerivedIsArrayElement)) - return false; SubobjectDesignator &D = Result.Designator; - if (MostDerivedPathLength + E->path_size() > D.Entries.size()) + if (D.Invalid || !Result.checkNullPointer(Info, E, CSK_Derived)) return false; - // Check the type of the final cast. We don't need to check the path, - // since a cast can only be formed if the path is unique. - unsigned NewEntriesSize = D.Entries.size() - E->path_size(); - bool ResultIsArray = false; QualType TargetQT = E->getType(); if (const PointerType *PT = TargetQT->getAs()) TargetQT = PT->getPointeeType(); + + // Check this cast lands within the final derived-to-base subobject path. + if (D.MostDerivedPathLength + E->path_size() > D.Entries.size()) { + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_invalid_downcast) + << D.MostDerivedType << TargetQT; + return false; + } + + // Check the type of the final cast. We don't need to check the path, + // since a cast can only be formed if the path is unique. + unsigned NewEntriesSize = D.Entries.size() - E->path_size(); const CXXRecordDecl *TargetType = TargetQT->getAsCXXRecordDecl(); const CXXRecordDecl *FinalType; - if (NewEntriesSize == MostDerivedPathLength) { - ResultIsArray = MostDerivedIsArrayElement; - FinalType = MostDerivedType; - } else + if (NewEntriesSize == D.MostDerivedPathLength) + FinalType = D.MostDerivedType->getAsCXXRecordDecl(); + else FinalType = getAsBaseClass(D.Entries[NewEntriesSize - 1]); - if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl()) + if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl()) { + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_invalid_downcast) + << D.MostDerivedType << TargetQT; return false; + } // Truncate the lvalue to the appropriate derived class. - TruncateLValueBasePath(Info, Result, TargetType, NewEntriesSize, - ResultIsArray); - return true; + return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize); } namespace { @@ -1787,14 +1879,14 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This, ++BaseIt; #endif LValue Subobject = This; - HandleLValueDirectBase(Info, Subobject, RD, + HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD, BaseType->getAsCXXRecordDecl(), &Layout); if (!EvaluateConstantExpression(Result.getStructBase(BasesSeen++), Info, Subobject, (*I)->getInit())) return false; } else if (FieldDecl *FD = (*I)->getMember()) { LValue Subobject = This; - HandleLValueMember(Info, Subobject, FD, &Layout); + HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout); if (RD->isUnion()) { Result = APValue(FD); if (!EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject, @@ -2083,11 +2175,11 @@ public: if (!FD) return Error(Callee); } else if (CalleeType->isFunctionPointerType()) { - CCValue Call; - if (!Evaluate(Call, Info, Callee)) + LValue Call; + if (!EvaluatePointer(Callee, Call, Info)) return false; - if (!Call.isLValue() || !Call.getLValueOffset().isZero()) + if (!Call.getLValueOffset().isZero()) return Error(Callee); FD = dyn_cast_or_null( Call.getLValueBase().dyn_cast()); @@ -2124,7 +2216,7 @@ public: !HandleFunctionCall(E, Definition, This, Args, Body, Info, Result)) return false; - return DerivedSuccess(CCValue(Result, CCValue::GlobalValue()), E); + return DerivedSuccess(CCValue(Info.Ctx, Result, CCValue::GlobalValue()), E); } RetTy VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { @@ -2163,8 +2255,8 @@ public: assert(BaseTy->getAs()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); - SubobjectDesignator Designator; - Designator.addDecl(FD); + SubobjectDesignator Designator(BaseTy); + Designator.addDeclUnchecked(FD); return ExtractSubobject(Info, E, Val, BaseTy, Designator, E->getType()) && DerivedSuccess(Val, E); @@ -2228,14 +2320,6 @@ public: return true; } - bool CheckValidLValue() { - // C++11 [basic.lval]p1: An lvalue designates a function or an object. Hence - // there are no null references, nor once-past-the-end references. - // FIXME: Check for one-past-the-end array indices - return Result.Base && !Result.Designator.Invalid && - !Result.Designator.OnePastTheEnd; - } - bool VisitMemberExpr(const MemberExpr *E) { // Handle non-static data members. QualType BaseTy; @@ -2253,7 +2337,6 @@ public: return false; BaseTy = E->getBase()->getType(); } - // FIXME: In C++11, require the result to be a valid lvalue. const FieldDecl *FD = dyn_cast(E->getMemberDecl()); // FIXME: Handle IndirectFieldDecls @@ -2262,7 +2345,7 @@ public: FD->getParent()->getCanonicalDecl() && "record / field mismatch"); (void)BaseTy; - HandleLValueMember(this->Info, Result, FD); + HandleLValueMember(this->Info, E, Result, FD); if (FD->getType()->isReferenceType()) { CCValue RefValue; @@ -2294,8 +2377,6 @@ public: case CK_UncheckedDerivedToBase: { if (!this->Visit(E->getSubExpr())) return false; - if (!CheckValidLValue()) - return false; // Now figure out the necessary offset to add to the base LV to get from // the derived class to the base class. @@ -2303,7 +2384,7 @@ public: for (CastExpr::path_const_iterator PathI = E->path_begin(), PathE = E->path_end(); PathI != PathE; ++PathI) { - if (!HandleLValueBase(this->Info, Result, Type->getAsCXXRecordDecl(), + if (!HandleLValueBase(this->Info, E, Result, Type->getAsCXXRecordDecl(), *PathI)) return false; Type = (*PathI)->getType(); @@ -2378,8 +2459,6 @@ public: case CK_BaseToDerived: if (!Visit(E->getSubExpr())) return false; - if (!CheckValidLValue()) - return false; return HandleBaseToDerivedCast(Info, E, Result); } } @@ -2502,12 +2581,10 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { = Index.isSigned() ? Index.getSExtValue() : static_cast(Index.getZExtValue()); - // FIXME: In C++11, require the result to be a valid lvalue. - return HandleLValueArrayAdjustment(Info, Result, E->getType(), IndexValue); + return HandleLValueArrayAdjustment(Info, E, Result, E->getType(), IndexValue); } bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { - // FIXME: In C++11, require the result to be a valid lvalue. return EvaluatePointer(E->getSubExpr(), Result, Info); } @@ -2589,8 +2666,8 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { AdditionalOffset = -AdditionalOffset; QualType Pointee = PExp->getType()->getAs()->getPointeeType(); - // FIXME: In C++11, require the result to be a valid lvalue. - return HandleLValueArrayAdjustment(Info, Result, Pointee, AdditionalOffset); + return HandleLValueArrayAdjustment(Info, E, Result, Pointee, + AdditionalOffset); } bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { @@ -2637,7 +2714,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { for (CastExpr::path_const_iterator PathI = E->path_begin(), PathE = E->path_end(); PathI != PathE; ++PathI) { - if (!HandleLValueBase(Info, Result, Type->getAsCXXRecordDecl(), *PathI)) + if (!HandleLValueBase(Info, E, Result, Type->getAsCXXRecordDecl(), + *PathI)) return false; Type = (*PathI)->getType(); } @@ -2687,7 +2765,11 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { return false; } // The result is a pointer to the first element of the array. - Result.Designator.addIndex(0); + if (const ConstantArrayType *CAT + = Info.Ctx.getAsConstantArrayType(SubExpr->getType())) + Result.addArray(Info, E, CAT); + else + Result.Designator.setInvalid(); return true; case CK_FunctionToPointerDecay: @@ -2823,7 +2905,8 @@ namespace { /// -- if T is a (possibly cv-qualified) non-union class type, /// each non-static data member and each base-class subobject is /// zero-initialized -static bool HandleClassZeroInitialization(EvalInfo &Info, const RecordDecl *RD, +static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, + const RecordDecl *RD, const LValue &This, APValue &Result) { assert(!RD->isUnion() && "Expected non-union class type"); const CXXRecordDecl *CD = dyn_cast(RD); @@ -2835,24 +2918,24 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const RecordDecl *RD, if (CD) { unsigned Index = 0; for (CXXRecordDecl::base_class_const_iterator I = CD->bases_begin(), - E = CD->bases_end(); I != E; ++I, ++Index) { + End = CD->bases_end(); I != End; ++I, ++Index) { const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl(); LValue Subobject = This; - HandleLValueDirectBase(Info, Subobject, CD, Base, &Layout); - if (!HandleClassZeroInitialization(Info, Base, Subobject, + HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout); + if (!HandleClassZeroInitialization(Info, E, Base, Subobject, Result.getStructBase(Index))) return false; } } - for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); - I != E; ++I) { + for (RecordDecl::field_iterator I = RD->field_begin(), End = RD->field_end(); + I != End; ++I) { // -- if T is a reference type, no initialization is performed. if ((*I)->getType()->isReferenceType()) continue; LValue Subobject = This; - HandleLValueMember(Info, Subobject, *I, &Layout); + HandleLValueMember(Info, E, Subobject, *I, &Layout); ImplicitValueInitExpr VIE((*I)->getType()); if (!EvaluateConstantExpression( @@ -2875,14 +2958,14 @@ bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { } LValue Subobject = This; - HandleLValueMember(Info, Subobject, *I); + HandleLValueMember(Info, E, Subobject, *I); Result = APValue(*I); ImplicitValueInitExpr VIE((*I)->getType()); return EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject, &VIE); } - return HandleClassZeroInitialization(Info, RD, This, Result); + return HandleClassZeroInitialization(Info, E, RD, This, Result); } bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) { @@ -2926,8 +3009,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { if (!E->getNumInits()) return true; LValue Subobject = This; - HandleLValueMember(Info, Subobject, E->getInitializedFieldInUnion(), - &Layout); + HandleLValueMember(Info, E->getInit(0), Subobject, + E->getInitializedFieldInUnion(), &Layout); return EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject, E->getInit(0)); } @@ -2945,9 +3028,10 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { continue; LValue Subobject = This; - HandleLValueMember(Info, Subobject, *Field, &Layout); if (ElementNo < E->getNumInits()) { + HandleLValueMember(Info, E->getInit(ElementNo), Subobject, *Field, + &Layout); if (!EvaluateConstantExpression( Result.getStructField((*Field)->getFieldIndex()), Info, Subobject, E->getInit(ElementNo++))) @@ -2955,6 +3039,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } else { // Perform an implicit value-initialization for members beyond the end of // the initializer list. + HandleLValueMember(Info, E, Subobject, *Field, &Layout); ImplicitValueInitExpr VIE(Field->getType()); if (!EvaluateConstantExpression( Result.getStructField((*Field)->getFieldIndex()), @@ -3274,7 +3359,7 @@ namespace { // Zero-initialize all elements. LValue Subobject = This; - Subobject.Designator.addIndex(0); + Subobject.addArray(Info, E, CAT); ImplicitValueInitExpr VIE(CAT->getElementType()); return EvaluateConstantExpression(Result.getArrayFiller(), Info, Subobject, &VIE); @@ -3307,7 +3392,7 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { Result = APValue(APValue::UninitArray(), NumElements, NumElements); // Copy the string literal into the array. FIXME: Do this better. - LV.Designator.addIndex(0); + LV.addArray(Info, E, CAT); for (uint64_t I = 0; I < NumElements; ++I) { CCValue Char; if (!HandleLValueToRValueConversion(Info, E->getInit(0), @@ -3316,7 +3401,8 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { if (!CheckConstantExpression(Info, E->getInit(0), Char, Result.getArrayInitializedElt(I))) return false; - if (!HandleLValueArrayAdjustment(Info, LV, CAT->getElementType(), 1)) + if (!HandleLValueArrayAdjustment(Info, E->getInit(0), LV, + CAT->getElementType(), 1)) return false; } return true; @@ -3325,14 +3411,15 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { Result = APValue(APValue::UninitArray(), E->getNumInits(), CAT->getSize().getZExtValue()); LValue Subobject = This; - Subobject.Designator.addIndex(0); + Subobject.addArray(Info, E, CAT); unsigned Index = 0; for (InitListExpr::const_iterator I = E->begin(), End = E->end(); I != End; ++I, ++Index) { if (!EvaluateConstantExpression(Result.getArrayInitializedElt(Index), Info, Subobject, cast(*I))) return false; - if (!HandleLValueArrayAdjustment(Info, Subobject, CAT->getElementType(), 1)) + if (!HandleLValueArrayAdjustment(Info, cast(*I), Subobject, + CAT->getElementType(), 1)) return false; } @@ -3361,7 +3448,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { if (CheckTrivialDefaultConstructor(Info, E->getExprLoc(), FD, ZeroInit)) { if (ZeroInit) { LValue Subobject = This; - Subobject.Designator.addIndex(0); + Subobject.addArray(Info, E, CAT); ImplicitValueInitExpr VIE(CAT->getElementType()); return EvaluateConstantExpression(Result.getArrayFiller(), Info, Subobject, &VIE); @@ -3388,7 +3475,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { // struct S { constexpr S() : p(&p) {} void *p; }; // S s[10]; LValue Subobject = This; - Subobject.Designator.addIndex(0); + Subobject.addArray(Info, E, CAT); if (ZeroInit) { ImplicitValueInitExpr VIE(CAT->getElementType()); @@ -5284,7 +5371,8 @@ bool Expr::EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx) const { EvalResult Scratch; return EvaluateAsRValue(Scratch, Ctx) && - HandleConversionToBool(CCValue(Scratch.Val, CCValue::GlobalValue()), + HandleConversionToBool(CCValue(const_cast(Ctx), + Scratch.Val, CCValue::GlobalValue()), Result); } diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index f0581fcc09..78d78b64c7 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -137,6 +137,27 @@ namespace UndefinedBehavior { struct T { int n : f(p); // expected-error {{not an integer constant expression}} expected-note {{in call to 'f(&s.m + 1)'}} }; + + namespace Ptr { + struct A {}; + struct B : A { int n; }; + B a[3][3]; + constexpr B *p = a[0] + 4; // expected-error {{constant expression}} expected-note {{element 4 of array of 3 elements}} + B b = {}; + constexpr A *pa = &b + 1; // expected-error {{constant expression}} expected-note {{base class of pointer past the end}} + constexpr B *pb = (B*)((A*)&b + 1); // expected-error {{constant expression}} expected-note {{derived class of pointer past the end}} + constexpr const int *pn = &(&b + 1)->n; // expected-error {{constant expression}} expected-note {{field of pointer past the end}} + constexpr B *parr = &a[3][0]; // expected-error {{constant expression}} expected-note {{array element of pointer past the end}} + + constexpr A *na = nullptr; + constexpr B *nb = nullptr; + constexpr A &ra = *nb; // expected-error {{constant expression}} expected-note {{cannot access base class of null pointer}} + constexpr B &rb = (B&)*na; // expected-error {{constant expression}} expected-note {{cannot access derived class of null pointer}} + static_assert((A*)nb == 0, ""); + static_assert((B*)na == 0, ""); + constexpr const int &nf = nb->n; // expected-error {{constant expression}} expected-note {{cannot access field of null pointer}} + constexpr const int &np = (*(int(*)[4])nullptr)[2]; // expected-error {{constant expression}} expected-note {{cannot access array element of null pointer}} + } } // - a lambda-expression (5.1.2); diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 3dbb3c7e9e..d865010920 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -363,9 +363,8 @@ static_assert(MangleChars(U"constexpr!") == 1768383, ""); 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}} -// FIXME: block the pointer arithmetic with undefined behavior here -constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}} expected-note {{read of dereferenced one-past-the-end pointer}} -constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} +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 const char *p = "test" + 2; static_assert(*p == 's', ""); @@ -443,9 +442,9 @@ static_assert(ZipFoldR(SubMul, 3, xs+3, ys+3, 1), ""); // \ constexpr const int *p = xs + 3; constexpr int xs4 = p[1]; // ok constexpr int xs5 = p[2]; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} +constexpr int xs6 = p[3]; // expected-error {{constant expression}} expected-note {{cannot refer to element 6}} constexpr int xs0 = p[-3]; // ok -// FIXME: check pointer arithmetic here -constexpr int xs_1 = p[-4]; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} +constexpr int xs_1 = p[-4]; // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} constexpr int zs[2][2][2][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; static_assert(zs[0][0][0][0] == 1, ""); @@ -453,12 +452,14 @@ static_assert(zs[1][1][1][1] == 16, ""); static_assert(zs[0][0][0][2] == 3, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} static_assert((&zs[0][0][0][2])[-1] == 2, ""); static_assert(**(**(zs + 1) + 1) == 11, ""); -static_assert(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, ""); +static_assert(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, ""); // expected-error {{constant expression}} expected-note {{cannot refer to element -1 of array of 2 elements in a constant expression}} +static_assert(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][2] - 2) == 11, ""); +constexpr int err_zs_1_2_0_0 = zs[1][2][0][0]; // expected-error {{constant expression}} expected-note {{cannot access array element of pointer past the end}} constexpr int fail(const int &p) { - return (&p)[64]; // expected-note {{read of dereferenced one-past-the-end pointer}} + return (&p)[64]; // expected-note {{cannot refer to element 64 of array of 2 elements}} } -static_assert(fail(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1)) == 11, ""); // \ +static_assert(fail(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][2] - 2)) == 11, ""); // \ expected-error {{static_assert expression is not an integral constant expression}} \ expected-note {{in call to 'fail(zs[1][0][1][0])'}} @@ -598,8 +599,7 @@ constexpr AggregateInit agg1 = { "hello"[0] }; static_assert(strcmp_ce(&agg1.c, "hello") == 0, ""); static_assert(agg1.n == 0, ""); static_assert(agg1.d == 0.0, ""); -// FIXME: check pointer arithmetic here. -static_assert(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end}} +static_assert(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} static_assert(agg1.arr[0] == 0, ""); static_assert(agg1.arr[4] == 0, ""); static_assert(agg1.arr[5] == 0, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end}} @@ -662,13 +662,13 @@ static_assert(pb1 != pb2, ""); static_assert(pb1 == &bot1, ""); static_assert(pb2 == &bot2, ""); -constexpr Base2 &fail = (Base2&)bot1; // expected-error {{constant expression}} -constexpr Base &fail2 = (Base&)*pb2; // expected-error {{constant expression}} +constexpr Base2 &fail = (Base2&)bot1; // expected-error {{constant expression}} expected-note {{cannot cast object of dynamic type 'const Class::Derived' to type 'Class::Base2'}} +constexpr Base &fail2 = (Base&)*pb2; // expected-error {{constant expression}} expected-note {{cannot cast object of dynamic type 'const Class::Derived' to type 'Class::Base'}} constexpr Base2 &ok2 = (Base2&)bot2; static_assert(&ok2 == &derived, ""); -constexpr Base2 *pfail = (Base2*)pb1; // expected-error {{constant expression}} -constexpr Base *pfail2 = (Base*)&bot2; // expected-error {{constant expression}} +constexpr Base2 *pfail = (Base2*)pb1; // expected-error {{constant expression}} expected-note {{cannot cast object of dynamic type 'const Class::Derived' to type 'Class::Base2'}} +constexpr Base *pfail2 = (Base*)&bot2; // expected-error {{constant expression}} expected-note {{cannot cast object of dynamic type 'const Class::Derived' to type 'Class::Base'}} constexpr Base2 *pok2 = (Base2*)pb2; static_assert(pok2 == &derived, ""); static_assert(&ok2 == pok2, ""); @@ -723,7 +723,7 @@ static_assert(u[0].a == 0, ""); static_assert(u[0].b, ""); // expected-error {{constant expression}} expected-note {{read of member 'b' of union with active member 'a'}} static_assert(u[1].b == 1, ""); static_assert((&u[1].b)[1] == 2, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} -static_assert(*(&(u[1].b) + 1 + 1) == 3, ""); // expected-error {{constant expression}} expected-note {{subexpression}} +static_assert(*(&(u[1].b) + 1 + 1) == 3, ""); // expected-error {{constant expression}} expected-note {{cannot refer to element 2 of non-array object}} static_assert((&(u[1]) + 1 + 1)->b == 3, ""); } @@ -798,7 +798,7 @@ namespace MemberPointer { constexpr T<5> *p17_5 = &t17; constexpr T<13> *p17_13 = (T<13>*)p17_5; - constexpr T<23> *p17_23 = (T<23>*)p17_13; // expected-error {{constant expression}} + constexpr T<23> *p17_23 = (T<23>*)p17_13; // expected-error {{constant expression}} expected-note {{cannot cast object of dynamic type 'T<17>' to type 'T<23>'}} static_assert(&(p17_5->*(int(T<3>::*))deepn) == &t17.n, ""); static_assert(&(p17_13->*deepn) == &t17.n, ""); constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // expected-error {{constant expression}} @@ -832,15 +832,15 @@ namespace ArrayBaseDerived { // pb3 does not point to an array element. constexpr Base *pb4 = pb3 + 1; // ok, one-past-the-end pointer. - constexpr int pb4n = pb4->n; // expected-error {{constant expression}} - constexpr Base *err_pb5 = pb3 + 2; // FIXME: reject this. - constexpr int err_pb5n = err_pb5->n; // expected-error {{constant expression}} - constexpr Base *err_pb2 = pb3 - 1; // FIXME: reject this. - constexpr int err_pb2n = err_pb2->n; // expected-error {{constant expression}} + constexpr int pb4n = pb4->n; // expected-error {{constant expression}} expected-note {{cannot access field of pointer past the end}} + constexpr Base *err_pb5 = pb3 + 2; // expected-error {{constant expression}} expected-note {{cannot refer to element 2}} expected-note {{here}} + constexpr int err_pb5n = err_pb5->n; // expected-error {{constant expression}} expected-note {{initializer of 'err_pb5' is not a constant expression}} + constexpr Base *err_pb2 = pb3 - 1; // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} expected-note {{here}} + constexpr int err_pb2n = err_pb2->n; // expected-error {{constant expression}} expected-note {{initializer of 'err_pb2'}} constexpr Base *pb3a = pb4 - 1; // pb4 does not point to a Derived. - constexpr Derived *err_pd4 = (Derived*)pb4; // expected-error {{constant expression}} + constexpr Derived *err_pd4 = (Derived*)pb4; // expected-error {{constant expression}} expected-note {{cannot access derived class of pointer past the end}} constexpr Derived *pd3a = (Derived*)pb3a; constexpr int pd3n = pd3a->n; @@ -850,10 +850,9 @@ namespace ArrayBaseDerived { constexpr Derived *pd9 = pd6 + 3; constexpr Derived *pd10 = pd6 + 4; constexpr int pd9n = pd9->n; // ok - constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} + constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}} expected-note {{cannot access base class of pointer past the end}} constexpr int pd0n = pd10[-10].n; - // FIXME: check pointer arithmetic here. - constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} + constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}} expected-note {{cannot refer to element -1 of}} constexpr Base *pb9 = pd9; constexpr const int *(Base::*pfb)() const = diff --git a/test/SemaCXX/constexpr-backtrace-limit.cpp b/test/SemaCXX/constexpr-backtrace-limit.cpp index d2923abf20..9c40eedabd 100644 --- a/test/SemaCXX/constexpr-backtrace-limit.cpp +++ b/test/SemaCXX/constexpr-backtrace-limit.cpp @@ -15,14 +15,14 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 2 -fconstexpr-depth 8 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST3 // TEST3: constant expression -// TEST3-NEXT: subexpression +// TEST3-NEXT: reinterpret_cast // TEST3-NEXT: in call to 'recurse(0)' // TEST3-NEXT: skipping 4 calls // TEST3-NEXT: in call to 'recurse(5)' // RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 8 -fconstexpr-depth 8 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST4 // TEST4: constant expression -// TEST4-NEXT: subexpression +// TEST4-NEXT: reinterpret_cast // TEST4-NEXT: in call to 'recurse(0)' // TEST4-NEXT: in call to 'recurse(1)' // TEST4-NEXT: in call to 'recurse(2)' -- 2.40.0