diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -38,8 +38,8 @@ "pure virtual function %q0 called">; def note_constexpr_polymorphic_unknown_dynamic_type : Note< "%select{|||||virtual function called on|dynamic_cast applied to|" - "typeid applied to|destruction of}0 object '%1' whose dynamic type " - "is not constant">; + "typeid applied to|construction of|destruction of}0 object '%1' " + "whose dynamic type is not constant">; def note_constexpr_dynamic_cast_to_reference_failed : Note< "reference dynamic_cast failed: %select{" "static type %1 of operand is a non-public base class of dynamic type %2|" @@ -121,11 +121,12 @@ "evaluation of a call to a 'constexpr' member function">; def note_constexpr_lifetime_ended : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " - "%select{temporary|variable}1 whose lifetime has ended">; + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 %select{temporary|variable}1 whose " + "%plural{8:storage duration|:lifetime}0 has ended">; def note_constexpr_access_uninit : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to||destruction of}0 " "%select{object outside its lifetime|uninitialized object}1 " "is not allowed in a constant expression">; def note_constexpr_use_uninit_reference : Note< @@ -136,18 +137,19 @@ "in a constant expression">; def note_constexpr_access_volatile_type : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "||}0 " + "|||}0 " "volatile-qualified type %1 is not allowed in a constant expression">; def note_constexpr_access_volatile_obj : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "||}0 " + "|||}0 " "volatile %select{temporary|object %2|member %2}1 is not allowed in " "a constant expression">; def note_constexpr_volatile_here : Note< "volatile %select{temporary created|object declared|member declared}0 here">; def note_constexpr_access_mutable : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "mutable member %1 is not allowed in a constant expression">; def note_constexpr_ltor_non_const_int : Note< "read of non-const variable %0 is not allowed in a constant expression">; @@ -157,35 +159,42 @@ "read of incomplete type %0 is not allowed in a constant expression">; def note_constexpr_access_null : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "dereferenced null pointer is not allowed in a constant expression">; def note_constexpr_access_past_end : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "dereferenced one-past-the-end pointer is not allowed " "in a constant expression">; def note_constexpr_access_unsized_array : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "element of array without known bound " "is not allowed in a constant expression">; def note_constexpr_access_inactive_union_member : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|" + "construction of subobject of|destruction of}0 " "member %1 of union with %select{active member %3|no active member}2 " "is not allowed in a constant expression">; def note_constexpr_access_static_temporary : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 temporary " + "member call on|dynamic_cast of|typeid applied to|reconstruction of|" + "destruction of}0 temporary " "is not allowed in a constant expression outside the expression that " "created the temporary">; def note_constexpr_access_unreadable_object : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "object '%1' whose value is not known">; def note_constexpr_access_deleted_object : Note< "%select{read of|read of|assignment to|increment of|decrement of|" - "member call on|dynamic_cast of|typeid applied to|destruction of}0 " + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "heap allocated object that has been deleted">; def note_constexpr_modify_global : Note< "a constant expression cannot modify an object that is visible outside " @@ -255,6 +264,9 @@ def note_constexpr_pseudo_destructor : Note< "pseudo-destructor call is not permitted in constant expressions " "until C++20">; +def note_constexpr_construct_complex_elem : Note< + "construction of individual component of complex number is not yet supported " + "in constant expressions">; def note_constexpr_destroy_complex_elem : Note< "destruction of individual component of complex number is not yet supported " "in constant expressions">; @@ -265,6 +277,8 @@ "call to %select{placement|class-specific}0 %1">; def note_constexpr_new_placement : Note< "this placement new expression is not yet supported in constant expressions">; +def note_constexpr_placement_new_wrong_type : Note< + "placement new would change type of storage from %0 to %1">; def note_constexpr_new_negative : Note< "cannot allocate array; evaluated array bound %0 is negative">; def note_constexpr_new_too_large : Note< @@ -272,6 +286,13 @@ def note_constexpr_new_too_small : Note< "cannot allocate array; evaluated array bound %0 is too small to hold " "%1 explicitly initialized elements">; +def note_constexpr_new_untyped : Note< + "cannot allocate untyped memory in a constant expression; " + "use 'std::allocator::allocate' to allocate memory of type 'T'">; +def note_constexpr_new_not_complete_object_type : Note< + "cannot allocate memory of %select{incomplete|function}0 type %1">; +def note_constexpr_operator_new_bad_size : Note< + "allocated size %0 is not a multiple of size %1 of element type %2">; def note_constexpr_delete_not_heap_alloc : Note< "delete of pointer '%0' that does not point to a heap-allocated object">; def note_constexpr_double_delete : Note< @@ -279,8 +300,12 @@ def note_constexpr_double_destroy : Note< "destruction of object that is already being destroyed">; def note_constexpr_new_delete_mismatch : Note< - "%select{non-|}0array delete used to delete pointer to " - "%select{|non-}0array object of type %1">; + "%plural{2:'delete' used to delete pointer to object " + "allocated with 'std::allocator<...>::allocate'|" + ":%select{non-array delete|array delete|'std::allocator<...>::deallocate'}0 " + "used to delete pointer to " + "%select{array object of type %2|non-array object of type %2|" + "object allocated with 'new'}0}1">; def note_constexpr_delete_subobject : Note< "delete of pointer%select{ to subobject|}1 '%0' " "%select{|that does not point to complete object}1">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -594,6 +594,13 @@ Frame *getCaller() const override { return Caller; } SourceLocation getCallLocation() const override { return CallLoc; } const FunctionDecl *getCallee() const override { return Callee; } + + bool isStdFunction() const { + for (const DeclContext *DC = Callee; DC; DC = DC->getParent()) + if (DC->isStdNamespace()) + return true; + return false; + } }; /// Temporarily override 'this'. @@ -690,6 +697,37 @@ } namespace { + /// A dynamically-allocated heap object. + struct DynAlloc { + /// The value of this heap-allocated object. + APValue Value; + /// The allocating expression; used for diagnostics. Either a CXXNewExpr + /// or a CallExpr (the latter is for direct calls to operator new inside + /// std::allocator::allocate). + const Expr *AllocExpr = nullptr; + + enum Kind { + New, + ArrayNew, + StdAllocator + }; + + /// Get the kind of the allocation. This must match between allocation + /// and deallocation. + Kind getKind() const { + if (auto *NE = dyn_cast(AllocExpr)) + return NE->isArray() ? ArrayNew : New; + assert(isa(AllocExpr)); + return StdAllocator; + } + }; + + struct DynAllocOrder { + bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const { + return L.getIndex() < R.getIndex(); + } + }; + /// EvalInfo - This is a private struct used by the evaluator to capture /// information about a subexpression as it is folded. It retains information /// about the AST context, but also maintains information about the folded @@ -761,20 +799,6 @@ llvm::DenseMap ObjectsUnderConstruction; - /// A dynamically-allocated heap object. - struct DynAlloc { - /// The value of this heap-allocated object. - APValue Value; - /// The allocating expression; used for diagnostics. - const Expr *AllocExpr = nullptr; - }; - - struct DynAllocOrder { - bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const { - return L.getIndex() < R.getIndex(); - } - }; - /// Current heap allocations, along with the location where each was /// allocated. We use std::map here because we need stable addresses /// for the stored APValues. @@ -970,6 +994,39 @@ return Result; } + /// Information about a stack frame for std::allocator::[de]allocate. + struct StdAllocatorCaller { + unsigned FrameIndex; + QualType ElemType; + explicit operator bool() const { return FrameIndex != 0; }; + }; + + StdAllocatorCaller getStdAllocatorCaller(StringRef FnName) const { + for (const CallStackFrame *Call = CurrentCall; Call != &BottomFrame; + Call = Call->Caller) { + const auto *MD = dyn_cast_or_null(Call->Callee); + if (!MD) + continue; + const IdentifierInfo *FnII = MD->getIdentifier(); + if (!FnII || !FnII->isStr(FnName)) + continue; + + const auto *CTSD = + dyn_cast(MD->getParent()); + if (!CTSD) + continue; + + const IdentifierInfo *ClassII = CTSD->getIdentifier(); + const TemplateArgumentList &TAL = CTSD->getTemplateArgs(); + if (CTSD->isInStdNamespace() && ClassII && + ClassII->isStr("allocator") && TAL.size() >= 1 && + TAL[0].getKind() == TemplateArgument::Type) + return {Call->Index, TAL[0].getAsType()}; + } + + return {}; + } + void performLifetimeExtension() { // Disable the cleanups for lifetime-extended temporaries. CleanupStack.erase( @@ -1345,6 +1402,7 @@ case AK_Assign: case AK_Increment: case AK_Decrement: + case AK_Construct: case AK_Destroy: return true; } @@ -1357,7 +1415,7 @@ /// Is this an access per the C++ definition? static bool isFormalAccess(AccessKinds AK) { - return isAnyAccess(AK) && AK != AK_Destroy; + return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy; } namespace { @@ -1453,9 +1511,10 @@ IsNullPtr = false; } - void setNull(QualType PointerTy, uint64_t TargetVal) { + void setNull(ASTContext &Ctx, QualType PointerTy) { Base = (Expr *)nullptr; - Offset = CharUnits::fromQuantity(TargetVal); + Offset = + CharUnits::fromQuantity(Ctx.getTargetNullPointerValue(PointerTy)); InvalidBase = false; Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; @@ -1465,6 +1524,12 @@ set(B, true); } + std::string toString(ASTContext &Ctx, QualType T) const { + APValue Printable; + moveInto(Printable); + return Printable.getAsString(Ctx, T); + } + private: // Check that this LValue is not based on a null pointer. If it is, produce // a diagnostic and mark the designator as invalid. @@ -1905,7 +1970,7 @@ Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here); else if (DynamicAllocLValue DA = Base.dyn_cast()) { // FIXME: Produce a note for dangling pointers too. - if (Optional Alloc = Info.lookupDynamicAlloc(DA)) + if (Optional Alloc = Info.lookupDynamicAlloc(DA)) Info.Note((*Alloc)->AllocExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here); } @@ -3113,8 +3178,9 @@ // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { // Reading an indeterminate value is undefined, but assigning over one is OK. - if (O->isAbsent() || - (O->isIndeterminate() && handler.AccessKind != AK_Assign && + if ((O->isAbsent() && handler.AccessKind != AK_Construct) || + (O->isIndeterminate() && handler.AccessKind != AK_Construct && + handler.AccessKind != AK_Assign && handler.AccessKind != AK_ReadObjectRepresentation)) { if (!Info.checkingPotentialConstantExpression()) Info.FFDiag(E, diag::note_constexpr_access_uninit) @@ -3254,13 +3320,18 @@ const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) { - // FIXME: If O->getUnionValue() is absent, report that there's no - // active union member rather than reporting the prior active union - // member. We'll need to fix nullptr_t to not use APValue() as its - // representation first. - Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) - << handler.AccessKind << Field << !UnionField << UnionField; - return handler.failed(); + if (I == N - 1 && handler.AccessKind == AK_Construct) { + // Placement new onto an inactive union member makes it active. + O->setUnion(Field, APValue()); + } else { + // FIXME: If O->getUnionValue() is absent, report that there's no + // active union member rather than reporting the prior active union + // member. We'll need to fix nullptr_t to not use APValue() as its + // representation first. + Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) + << handler.AccessKind << Field << !UnionField << UnionField; + return handler.failed(); + } } O = &O->getUnionValue(); } else @@ -3560,7 +3631,7 @@ if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) return CompleteObject(); } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast()) { - Optional Alloc = Info.lookupDynamicAlloc(DA); + Optional Alloc = Info.lookupDynamicAlloc(DA); if (!Alloc) { Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK; return CompleteObject(); @@ -5147,8 +5218,7 @@ if (!E->isGLValue()) { // The value of a failed cast to pointer type is the null pointer value // of the required result type. - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Ptr.setNull(E->getType(), TargetVal); + Ptr.setNull(Info.Ctx, E->getType()); return true; } @@ -5878,6 +5948,161 @@ return HandleDestructionImpl(Info, Loc, LV, Value, T); } +/// Perform a call to 'perator new' or to `__builtin_operator_new'. +static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E, + LValue &Result) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) + return false; + + // This is permitted only within a call to std::allocator::allocate. + auto Caller = Info.getStdAllocatorCaller("allocate"); + if (!Caller) { + Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a + ? diag::note_constexpr_new_untyped + : diag::note_constexpr_new); + return false; + } + + QualType ElemType = Caller.ElemType; + if (ElemType->isIncompleteType() || ElemType->isFunctionType()) { + Info.FFDiag(E->getExprLoc(), + diag::note_constexpr_new_not_complete_object_type) + << (ElemType->isIncompleteType() ? 0 : 1) << ElemType; + return false; + } + + APSInt ByteSize; + if (!EvaluateInteger(E->getArg(0), ByteSize, Info)) + return false; + bool IsNothrow = false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) { + EvaluateIgnoredValue(Info, E->getArg(I)); + IsNothrow |= E->getType()->isNothrowT(); + } + + CharUnits ElemSize; + if (!HandleSizeof(Info, E->getExprLoc(), ElemType, ElemSize)) + return false; + APInt Size, Remainder; + APInt ElemSizeAP(ByteSize.getBitWidth(), ElemSize.getQuantity()); + APInt::udivrem(ByteSize, ElemSizeAP, Size, Remainder); + if (Remainder != 0) { + // This likely indicates a bug in the implementation of 'std::allocator'. + Info.FFDiag(E->getExprLoc(), diag::note_constexpr_operator_new_bad_size) + << ByteSize << APSInt(ElemSizeAP, true) << ElemType; + return false; + } + + if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (IsNothrow) { + Result.setNull(Info.Ctx, E->getType()); + return true; + } + + Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true); + return false; + } + + QualType AllocType = + Info.Ctx.getConstantArrayType(ElemType, Size, ArrayType::Normal, 0); + APValue *Val = Info.createHeapAlloc(E, AllocType, Result); + *Val = APValue(APValue::UninitArray(), 0, Size.getZExtValue()); + Result.addArray(Info, E, cast(AllocType)); + return true; +} + +static bool hasVirtualDestructor(QualType T) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual(); + return false; +} + +/// Check that the given object is a suitable pointer to a heap allocation that +/// still exists and is of the right kind for the purpose of a deletion. +/// +/// On success, returns the heap allocation to deallocate. On failure, produces +/// a diagnostic and returns None. +static Optional CheckDeleteKind(EvalInfo &Info, const Expr *E, + const LValue &Pointer, + DynAlloc::Kind DeallocKind) { + auto PointerAsString = [&] { + return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy); + }; + + DynamicAllocLValue DA = Pointer.Base.dyn_cast(); + if (!DA) { + Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc) + << PointerAsString(); + if (Pointer.Base) + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + Optional Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_double_delete); + return None; + } + + QualType AllocType = Pointer.Base.getDynamicAllocType(); + if (DeallocKind != (*Alloc)->getKind()) { + Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) + << DeallocKind << (*Alloc)->getKind() << AllocType; + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + bool Subobject = false; + if (DeallocKind == DynAlloc::New) { + Subobject = Pointer.Designator.MostDerivedPathLength != 0 || + Pointer.Designator.isOnePastTheEnd(); + } else { + Subobject = Pointer.Designator.Entries.size() != 1 || + Pointer.Designator.Entries[0].getAsArrayIndex() != 0; + } + if (Subobject) { + Info.FFDiag(E, diag::note_constexpr_delete_subobject) + << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); + return None; + } + + return Alloc; +} + +// Perform a call to 'operator delete' or '__builtin_operator_delete'. +bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) + return false; + + // This is permitted only within a call to std::allocator::deallocate. + if (!Info.getStdAllocatorCaller("deallocate")) { + Info.FFDiag(E->getExprLoc()); + return true; + } + + LValue Pointer; + if (!EvaluatePointer(E->getArg(0), Pointer, Info)) + return false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) + EvaluateIgnoredValue(Info, E->getArg(I)); + + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) + return true; + + if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator)) + return false; + + Info.HeapAllocs.erase(Pointer.Base.get()); + return true; +} + //===----------------------------------------------------------------------===// // Generic Evaluation //===----------------------------------------------------------------------===// @@ -6700,6 +6925,17 @@ FD = cast(CorrespondingCallOpSpecialization); } else FD = LambdaCallOp; + } else if (FD->isReplaceableGlobalAllocationFunction()) { + if (FD->getDeclName().getCXXOverloadedOperator() == OO_New || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) { + LValue Ptr; + if (!HandleOperatorNewCall(Info, E, Ptr)) + return false; + Ptr.moveInto(Result); + return true; + } else { + return HandleOperatorDeleteCall(Info, E); + } } } else return Error(E); @@ -7565,8 +7801,7 @@ return true; } bool ZeroInitialization(const Expr *E) { - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Result.setNull(E->getType(), TargetVal); + Result.setNull(Info.Ctx, E->getType()); return true; } @@ -7693,12 +7928,22 @@ // permitted in constant expressions in C++11. Bitcasts from cv void* are // also static_casts, but we disallow them as a resolution to DR1312. if (!E->getType()->isVoidPointerType()) { - Result.Designator.setInvalid(); - if (SubExpr->getType()->isVoidPointerType()) - CCEDiag(E, diag::note_constexpr_invalid_cast) - << 3 << SubExpr->getType(); - else - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + if (!Result.InvalidBase && !Result.Designator.Invalid && + !Result.IsNullPtr && + Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx), + E->getType()->getPointeeType()) && + Info.getStdAllocatorCaller("allocate")) { + // Inside a call to std::allocator::allocate and friends, we permit + // casting from void* back to cv1 T* for a pointer that points to a + // cv2 T. + } else { + Result.Designator.setInvalid(); + if (SubExpr->getType()->isVoidPointerType()) + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 3 << SubExpr->getType(); + else + CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + } } if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) ZeroInitialization(E); @@ -7935,6 +8180,8 @@ return true; } + case Builtin::BI__builtin_operator_new: + return HandleOperatorNewCall(Info, E, Result); case Builtin::BI__builtin_launder: return evaluatePointer(E->getArg(0), Result); case Builtin::BIstrchr: @@ -8186,8 +8433,10 @@ } default: - return visitNonBuiltinCallExpr(E); + break; } + + return visitNonBuiltinCallExpr(E); } static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, @@ -8203,14 +8452,23 @@ return false; FunctionDecl *OperatorNew = E->getOperatorNew(); - if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { + + bool IsNothrow = false; + bool IsPlacement = false; + if (OperatorNew->isReservedGlobalPlacementOperator() && + Info.CurrentCall->isStdFunction() && !E->isArray()) { + // FIXME Support array placement new. + assert(E->getNumPlacementArgs() == 1); + if (!EvaluatePointer(E->getPlacementArg(0), Result, Info)) + return false; + if (Result.Designator.Invalid) + return false; + IsPlacement = true; + } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) << isa(OperatorNew) << OperatorNew; return false; - } - - bool IsNothrow = false; - if (E->getNumPlacementArgs()) { + } else if (E->getNumPlacementArgs()) { // The only new-placement list we support is of the form (std::nothrow). // // FIXME: There is no restriction on this, but it's not clear that any @@ -8308,10 +8566,56 @@ "array allocation with non-array new"); } - // Perform the allocation and obtain a pointer to the resulting object. - APValue *Val = Info.createHeapAlloc(E, AllocType, Result); - if (!Val) - return false; + APValue *Val; + if (IsPlacement) { + AccessKinds AK = AK_Construct; + struct FindObjectHandler { + EvalInfo &Info; + const Expr *E; + QualType AllocType; + const AccessKinds AccessKind; + APValue *Value; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + // FIXME: Reject the cases where [basic.life]p8 would not permit the + // old name of the object to be used to name the new object. + if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) { + Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) << + SubobjType << AllocType; + return false; + } + Value = &Subobj; + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + bool found(APFloat &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + } Handler = {Info, E, AllocType, AK, nullptr}; + + CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType); + if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler)) + return false; + + Val = Handler.Value; + + // [basic.life]p1: + // The lifetime of an object o of type T ends when [...] the storage + // which the object occupies is [...] reused by an object that is not + // nested within o (6.6.2). + *Val = APValue(); + } else { + // Perform the allocation and obtain a pointer to the resulting object. + Val = Info.createHeapAlloc(E, AllocType, Result); + if (!Val) + return false; + } if (ResizedArrayILE) { if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, @@ -12838,26 +13142,25 @@ bool VisitCallExpr(const CallExpr *E) { switch (E->getBuiltinCallee()) { - default: - return ExprEvaluatorBaseTy::VisitCallExpr(E); case Builtin::BI__assume: case Builtin::BI__builtin_assume: // The argument is not evaluated! return true; + + case Builtin::BI__builtin_operator_delete: + return HandleOperatorDeleteCall(Info, E); + + default: + break; } + + return ExprEvaluatorBaseTy::VisitCallExpr(E); } bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); }; } // end anonymous namespace -static bool hasVirtualDestructor(QualType T) { - if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) - if (CXXDestructorDecl *DD = RD->getDestructor()) - return DD->isVirtual(); - return false; -} - bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { // We cannot speculatively evaluate a delete expression. if (Info.SpeculativeEvaluationDepth) @@ -12888,49 +13191,12 @@ return true; } - auto PointerAsString = [&] { - APValue Printable; - Pointer.moveInto(Printable); - return Printable.getAsString(Info.Ctx, Arg->getType()); - }; - - DynamicAllocLValue DA = Pointer.Base.dyn_cast(); - if (!DA) { - Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc) - << PointerAsString(); - if (Pointer.Base) - NoteLValueLocation(Info, Pointer.Base); + Optional Alloc = CheckDeleteKind( + Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New); + if (!Alloc) return false; - } QualType AllocType = Pointer.Base.getDynamicAllocType(); - Optional Alloc = Info.lookupDynamicAlloc(DA); - if (!Alloc) { - Info.FFDiag(E, diag::note_constexpr_double_delete); - return false; - } - - if (E->isArrayForm() != AllocType->isConstantArrayType()) { - Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) - << E->isArrayForm() << AllocType; - NoteLValueLocation(Info, Pointer.Base); - return false; - } - - bool Subobject = false; - if (E->isArrayForm()) { - Subobject = Pointer.Designator.Entries.size() != 1 || - Pointer.Designator.Entries[0].getAsArrayIndex() != 0; - } else { - Subobject = Pointer.Designator.MostDerivedPathLength != 0 || - Pointer.Designator.isOnePastTheEnd(); - } - if (Subobject) { - Info.FFDiag(E, diag::note_constexpr_delete_subobject) - << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); - return false; - } - // For the non-array case, the designator must be empty if the static type // does not have a virtual destructor. if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 && @@ -12944,7 +13210,7 @@ (*Alloc)->Value, AllocType)) return false; - if (!Info.HeapAllocs.erase(DA)) { + if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast())) { // The element was already erased. This means the destructor call also // deleted the object. // FIXME: This probably results in undefined behavior before we get this diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h --- a/clang/lib/AST/Interp/State.h +++ b/clang/lib/AST/Interp/State.h @@ -32,6 +32,7 @@ AK_MemberCall, AK_DynamicCast, AK_TypeId, + AK_Construct, AK_Destroy, }; diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -543,6 +543,7 @@ // C++20 features. if (LangOpts.CPlusPlus2a) { Builder.defineMacro("__cpp_conditional_explicit", "201806L"); + Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L"); Builder.defineMacro("__cpp_constinit", "201907L"); } if (LangOpts.Char8) diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -34,6 +34,10 @@ #error "wrong value for __cpp_char8_t" #endif +#if check(constexpr_dynamic_alloc, 0, 0, 0, 0, 201907) +#error "wrong value for __cpp_constexpr_dynamic_alloc" +#endif + #if check(constinit, 0, 0, 0, 0, 201907) #error "wrong value for __cpp_constinit" #endif diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp @@ -0,0 +1,168 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete +// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=operator new" "-DDELETE=operator delete" +// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=::operator new" "-DDELETE=::operator delete" + +constexpr bool alloc_from_user_code() { + void *p = NEW(sizeof(int)); // expected-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator::allocate'}} + DELETE(p); + return true; +} +static_assert(alloc_from_user_code()); // expected-error {{constant expression}} expected-note {{in call}} + +namespace std { + using size_t = decltype(sizeof(0)); + // FIXME: It would be preferable to point these notes at the location of the call to allocator<...>::[de]allocate instead + template struct allocator { + constexpr T *allocate(size_t N) { + return (T*)NEW(sizeof(T) * N); // expected-note 3{{heap allocation}} expected-note {{not deallocated}} + } + constexpr void deallocate(void *p) { + DELETE(p); // expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}} + } + }; +} + +constexpr bool alloc_via_std_allocator() { + std::allocator alloc; + int *p = alloc.allocate(1); + alloc.deallocate(p); + return true; +} +static_assert(alloc_via_std_allocator()); + +template<> struct std::allocator { + constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of function type 'void ()'}} +}; +constexpr void *fn = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +struct Incomplete; +template<> struct std::allocator { + constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of incomplete type 'Incomplete'}} +}; +constexpr void *incomplete = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +struct WrongSize { char x[5]; }; +static_assert(sizeof(WrongSize) == 5); +template<> struct std::allocator { + constexpr void *allocate() { return NEW(7); } // expected-note {{allocated size 7 is not a multiple of size 5 of element type 'WrongSize'}} +}; +constexpr void *wrong_size = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +constexpr bool mismatched(int alloc_kind, int dealloc_kind) { + int *p; + switch (alloc_kind) { + case 0: + p = new int; // expected-note {{heap allocation}} + break; + case 1: + p = new int[1]; // expected-note {{heap allocation}} + break; + case 2: + p = std::allocator().allocate(1); + break; + } + switch (dealloc_kind) { + case 0: + delete p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} + break; + case 1: + delete[] p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} + break; + case 2: + std::allocator().deallocate(p); // expected-note 2{{in call}} + break; + } + return true; +} +static_assert(mismatched(0, 2)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(1, 2)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 0)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 1)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 2)); + +constexpr int *escape = std::allocator().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}} +constexpr int leak = (std::allocator().allocate(3), 0); // expected-error {{constant expression}} +constexpr int no_lifetime_start = (*std::allocator().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}} + +void *operator new(std::size_t, void *p) { return p; } +constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}} + int a; + new (&a) int(42); // expected-note {{call to placement 'operator new'}} + return a == 42; +} + +namespace std { + constexpr bool placement_new_in_stdlib() { + int a; + new (&a) int(42); + return a == 42; + } +} +static_assert(std::placement_new_in_stdlib()); + +namespace std { + template + constexpr void construct_at(void *p, Args &&...args) { + new (p) T((Args&&)args...); // #new + } +} + +constexpr bool call_std_construct_at() { + int *p = std::allocator().allocate(3); + std::construct_at(p, 1); + std::construct_at(p + 1, 2); + std::construct_at(p + 2, 3); + bool good = p[0] + p[1] + p[2] == 6; + std::allocator().deallocate(p); + return good; +} +static_assert(call_std_construct_at()); + +constexpr bool bad_construct_at_type() { + int a; + // expected-note@#new {{placement new would change type of storage from 'int' to 'float'}} + std::construct_at(&a, 1.0f); // expected-note {{in call}} + return true; +} +static_assert(bad_construct_at_type()); // expected-error{{}} expected-note {{in call}} + +constexpr bool bad_construct_at_subobject() { + struct X { int a, b; }; + union A { + int a; + X x; + }; + A a = {1}; + // expected-note@#new {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}} + std::construct_at(&a.x.a, 1); // expected-note {{in call}} + return true; +} +static_assert(bad_construct_at_subobject()); // expected-error{{}} expected-note {{in call}} + +constexpr bool change_union_member() { + union U { + int a; + int b; + }; + U u = {.a = 1}; + std::construct_at(&u.b, 2); + return u.b == 2; +} +static_assert(change_union_member()); + +int external; +// expected-note@#new {{visible outside}} +static_assert((std::construct_at(&external, 1), true)); // expected-error{{}} expected-note {{in call}} + +constexpr int &&temporary = 0; // expected-note {{created here}} +// expected-note@#new {{construction of temporary is not allowed in a constant expression outside the expression that created the temporary}} +static_assert((std::construct_at(&temporary, 1), true)); // expected-error{{}} expected-note {{in call}} + +constexpr bool construct_after_lifetime() { + int *p = new int; + delete p; + // expected-note@#new {{construction of heap allocated object that has been deleted}} + std::construct_at(p); // expected-note {{in call}} + return true; +} +static_assert(construct_after_lifetime()); // expected-error {{}} expected-note {{in call}} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1003,14 +1003,13 @@ P1331R2 - SVN + SVN P1668R1 P0784R7 - Partial Prohibit aggregates with user-declared constructors diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -575,6 +575,10 @@ #endif // _LIBCPP_COMPILER_[CLANG|GCC|MSVC|IBM] +#ifndef __cpp_constexpr_dynamic_alloc +#define _LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC +#endif + #if defined(_LIBCPP_OBJECT_FORMAT_COFF) #ifdef _DLL @@ -987,6 +991,12 @@ # define _LIBCPP_CONSTEXPR_AFTER_CXX17 #endif +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC) +# define _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC constexpr +#else +# define _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC +#endif + // The _LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other // NODISCARD macros to the correct attribute. #if __has_cpp_attribute(nodiscard) || defined(_LIBCPP_COMPILER_MSVC) diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -1565,47 +1565,47 @@ {typedef allocator_traits::other> other;}; #endif // _LIBCPP_CXX03_LANG - _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY + _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static pointer allocate(allocator_type& __a, size_type __n) {return __a.allocate(__n);} - _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY + _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static pointer allocate(allocator_type& __a, size_type __n, const_void_pointer __hint) {return __allocate(__a, __n, __hint, __has_allocate_hint());} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void deallocate(allocator_type& __a, pointer __p, size_type __n) _NOEXCEPT {__a.deallocate(__p, __n);} #ifndef _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void construct(allocator_type& __a, _Tp* __p, _Args&&... __args) {__construct(__has_construct(), __a, __p, _VSTD::forward<_Args>(__args)...);} #else // _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void construct(allocator_type&, _Tp* __p) { ::new ((void*)__p) _Tp(); } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void construct(allocator_type& __a, _Tp* __p, const _A0& __a0) { __construct(__has_construct(), __a, __p, __a0); } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void construct(allocator_type&, _Tp* __p, const _A0& __a0, const _A1& __a1) { ::new ((void*)__p) _Tp(__a0, __a1); } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void construct(allocator_type&, _Tp* __p, const _A0& __a0, const _A1& __a1, const _A2& __a2) { @@ -1614,15 +1614,15 @@ #endif // _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void destroy(allocator_type& __a, _Tp* __p) {__destroy(__has_destroy(), __a, __p);} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static size_type max_size(const allocator_type& __a) _NOEXCEPT {return __max_size(__has_max_size(), __a);} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static allocator_type select_on_container_copy_construction(const allocator_type& __a) {return __select_on_container_copy_construction( @@ -1630,7 +1630,7 @@ __a);} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct_forward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __begin2) @@ -1648,7 +1648,7 @@ } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static typename enable_if < @@ -1657,18 +1657,32 @@ is_trivially_move_constructible<_Tp>::value, void >::type - __construct_forward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2) + __construct_forward_with_exception_guarantees(allocator_type& __a, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2) { ptrdiff_t _Np = __end1 - __begin1; if (_Np > 0) { - _VSTD::memcpy(__begin2, __begin1, _Np * sizeof(_Tp)); - __begin2 += _Np; +#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED + if (__builtin_is_constant_evaluated()) + for (; __begin1 != __end1; ++__begin1, (void) ++__begin2) + construct(__a, _VSTD::__to_raw_pointer(__begin2), +#ifdef _LIBCPP_NO_EXCEPTIONS + _VSTD::move(*__begin1) +#else + _VSTD::move_if_noexcept(*__begin1) +#endif + ); + else +#endif + { + _VSTD::memcpy(__begin2, __begin1, _Np * sizeof(_Tp)); + __begin2 += _Np; + } } } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct_range_forward(allocator_type& __a, _Iter __begin1, _Iter __end1, _Ptr& __begin2) @@ -1680,7 +1694,7 @@ template ::type, class _RawDestTp = typename remove_const<_DestTp>::type> - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static typename enable_if < @@ -1690,18 +1704,26 @@ !__has_construct::value), void >::type - __construct_range_forward(allocator_type&, _SourceTp* __begin1, _SourceTp* __end1, _DestTp*& __begin2) + __construct_range_forward(allocator_type& __a, _SourceTp* __begin1, _SourceTp* __end1, _DestTp*& __begin2) { ptrdiff_t _Np = __end1 - __begin1; if (_Np > 0) { - _VSTD::memcpy(const_cast<_RawDestTp*>(__begin2), __begin1, _Np * sizeof(_DestTp)); - __begin2 += _Np; +#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED + if (__builtin_is_constant_evaluated()) + for (; __begin1 != __end1; ++__begin1, (void) ++__begin2) + construct(__a, _VSTD::__to_raw_pointer(__begin2), *__begin1); + else +#endif + { + _VSTD::memcpy(const_cast<_RawDestTp*>(__begin2), __begin1, _Np * sizeof(_DestTp)); + __begin2 += _Np; + } } } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct_backward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2) @@ -1722,7 +1744,7 @@ } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static typename enable_if < @@ -1731,44 +1753,65 @@ is_trivially_move_constructible<_Tp>::value, void >::type - __construct_backward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __end2) + __construct_backward_with_exception_guarantees(allocator_type& __a, _Tp* __begin1, _Tp* __end1, _Tp*& __end2) { ptrdiff_t _Np = __end1 - __begin1; - __end2 -= _Np; if (_Np > 0) - _VSTD::memcpy(__end2, __begin1, _Np * sizeof(_Tp)); + { +#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED + if (__builtin_is_constant_evaluated()) + { + while (__end1 != __begin1) + { + construct(__a, _VSTD::__to_raw_pointer(__end2 - 1), +#ifdef _LIBCPP_NO_EXCEPTIONS + _VSTD::move(*--__end1) +#else + _VSTD::move_if_noexcept(*--__end1) +#endif + ); + --__end2; + } + } + else +#endif + { + __end2 -= _Np; + _VSTD::memcpy(__end2, __begin1, _Np * sizeof(_Tp)); + } + } } private: - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static pointer __allocate(allocator_type& __a, size_type __n, const_void_pointer __hint, true_type) {return __a.allocate(__n, __hint);} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static pointer __allocate(allocator_type& __a, size_type __n, const_void_pointer, false_type) {return __a.allocate(__n);} #ifndef _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct(true_type, allocator_type& __a, _Tp* __p, _Args&&... __args) {__a.construct(__p, _VSTD::forward<_Args>(__args)...);} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct(false_type, allocator_type&, _Tp* __p, _Args&&... __args) { ::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...); } #else // _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct(true_type, allocator_type& __a, _Tp* __p, const _A0& __a0) {__a.construct(__p, __a0);} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __construct(false_type, allocator_type&, _Tp* __p, const _A0& __a0) { @@ -1777,28 +1820,28 @@ #endif // _LIBCPP_HAS_NO_VARIADICS template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __destroy(true_type, allocator_type& __a, _Tp* __p) {__a.destroy(__p);} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static void __destroy(false_type, allocator_type&, _Tp* __p) { __p->~_Tp(); } - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static size_type __max_size(true_type, const allocator_type& __a) _NOEXCEPT {return __a.max_size();} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static size_type __max_size(false_type, const allocator_type&) _NOEXCEPT {return numeric_limits::max() / sizeof(value_type);} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static allocator_type __select_on_container_copy_construction(true_type, const allocator_type& __a) {return __a.select_on_container_copy_construction();} - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY static allocator_type __select_on_container_copy_construction(false_type, const allocator_type& __a) {return __a;} @@ -1844,7 +1887,7 @@ {return _VSTD::addressof(__x);} _LIBCPP_INLINE_VISIBILITY const_pointer address(const_reference __x) const _NOEXCEPT {return _VSTD::addressof(__x);} - _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY + _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY pointer allocate(size_type __n, allocator::const_pointer = 0) { if (__n > max_size()) @@ -1852,13 +1895,14 @@ " 'n' exceeds maximum supported size"); return static_cast(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp))); } - _LIBCPP_INLINE_VISIBILITY void deallocate(pointer __p, size_type __n) _NOEXCEPT + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY + void deallocate(pointer __p, size_type __n) _NOEXCEPT {_VSTD::__libcpp_deallocate((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));} - _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT + _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT {return size_type(~0) / sizeof(_Tp);} #if !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) && !defined(_LIBCPP_HAS_NO_VARIADICS) template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY void construct(_Up* __p, _Args&&... __args) { @@ -1917,7 +1961,8 @@ ::new((void*)__p) _Tp(__a0, __a1); } #endif // !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) && !defined(_LIBCPP_HAS_NO_VARIADICS) - _LIBCPP_INLINE_VISIBILITY void destroy(pointer __p) {__p->~_Tp();} + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY + void destroy(pointer __p) {__p->~_Tp();} }; template @@ -1946,6 +1991,7 @@ _LIBCPP_INLINE_VISIBILITY const_pointer address(const_reference __x) const _NOEXCEPT {return _VSTD::addressof(__x);} + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY pointer allocate(size_type __n, allocator::const_pointer = 0) { if (__n > max_size()) @@ -1953,6 +1999,7 @@ " 'n' exceeds maximum supported size"); return static_cast(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp))); } + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY void deallocate(pointer __p, size_type __n) _NOEXCEPT {_VSTD::__libcpp_deallocate((void*) const_cast<_Tp *>(__p), __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));} _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT @@ -2022,11 +2069,11 @@ }; template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC bool operator==(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return true;} template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC bool operator!=(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return false;} template diff --git a/libcxx/include/new b/libcxx/include/new --- a/libcxx/include/new +++ b/libcxx/include/new @@ -234,7 +234,8 @@ #endif } -inline _LIBCPP_INLINE_VISIBILITY void *__libcpp_allocate(size_t __size, size_t __align) { +_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY +void *__libcpp_allocate(size_t __size, size_t __align) { #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION if (__is_overaligned_for_new(__align)) { const align_val_t __align_val = static_cast(__align); @@ -255,7 +256,7 @@ } struct _DeallocateCaller { - static inline _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline _LIBCPP_INLINE_VISIBILITY void __do_deallocate_handle_size_align(void *__ptr, size_t __size, size_t __align) { #if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION) ((void)__align); @@ -270,7 +271,7 @@ #endif } - static inline _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline _LIBCPP_INLINE_VISIBILITY void __do_deallocate_handle_align(void *__ptr, size_t __align) { #if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION) ((void)__align); @@ -286,7 +287,7 @@ } private: - static inline void __do_deallocate_handle_size(void *__ptr, size_t __size) { + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_deallocate_handle_size(void *__ptr, size_t __size) { #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION ((void)__size); return __do_call(__ptr); @@ -296,7 +297,7 @@ } #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION - static inline void __do_deallocate_handle_size(void *__ptr, size_t __size, align_val_t __align) { + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_deallocate_handle_size(void *__ptr, size_t __size, align_val_t __align) { #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION ((void)__size); return __do_call(__ptr, __align); @@ -308,7 +309,7 @@ private: template - static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) { + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) { #if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \ defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE) return ::operator delete(__ptr, __a1, __a2); @@ -318,7 +319,7 @@ } template - static inline void __do_call(void *__ptr, _A1 __a1) { + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr, _A1 __a1) { #if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \ defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE) return ::operator delete(__ptr, __a1); @@ -327,7 +328,7 @@ #endif } - static inline void __do_call(void *__ptr) { + _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr) { #ifdef _LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE return ::operator delete(__ptr); #else @@ -336,11 +337,13 @@ } }; -inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) { +_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) { _DeallocateCaller::__do_deallocate_handle_size_align(__ptr, __size, __align); } -inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate_unsized(void* __ptr, size_t __align) { +_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_deallocate_unsized(void* __ptr, size_t __align) { _DeallocateCaller::__do_deallocate_handle_align(__ptr, __align); } diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -218,6 +218,9 @@ # define __cpp_lib_char8_t 201811L # endif // # define __cpp_lib_concepts 201806L +# if !defined(_LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC) +# define __cpp_lib_constexpr_dynamic_alloc 201907L +# endif // # define __cpp_lib_constexpr_misc 201811L // # define __cpp_lib_constexpr_swap_algorithms 201806L # if _LIBCPP_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L