From 15bd5353553fa6bc8cd2bcc134915f249da5d70b Mon Sep 17 00:00:00 2001 From: Richard Smith <richard-llvm@metafoo.co.uk> Date: Mon, 10 Oct 2016 06:42:31 +0000 Subject: [PATCH] P0035R4: Semantic analysis and code generation for C++17 overaligned allocation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@283722 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 5 + include/clang/AST/ExprCXX.h | 16 +- include/clang/AST/Type.h | 3 +- include/clang/Basic/DiagnosticSemaKinds.td | 4 + include/clang/Sema/Overload.h | 4 +- include/clang/Sema/Sema.h | 11 +- lib/AST/ASTContext.cpp | 24 + lib/AST/ASTImporter.cpp | 1 + lib/AST/Decl.cpp | 48 +- lib/AST/DeclCXX.cpp | 26 +- lib/AST/ExprCXX.cpp | 5 +- lib/AST/Type.cpp | 9 + lib/CodeGen/CGExprCXX.cpp | 387 +++++---- lib/CodeGen/CodeGenFunction.h | 3 +- lib/Sema/SemaCUDA.cpp | 28 + lib/Sema/SemaDecl.cpp | 8 + lib/Sema/SemaDeclCXX.cpp | 26 +- lib/Sema/SemaExprCXX.cpp | 752 +++++++++++------- lib/Sema/SemaOverload.cpp | 11 +- lib/Serialization/ASTReaderStmt.cpp | 1 + lib/Serialization/ASTWriterStmt.cpp | 1 + .../basic.stc.dynamic.deallocation/p2.cpp | 19 + test/CXX/expr/expr.unary/expr.delete/p10.cpp | 25 + test/CXX/expr/expr.unary/expr.new/p14.cpp | 69 ++ test/CXX/expr/expr.unary/expr.new/p20-0x.cpp | 57 +- test/CXX/special/class.dtor/p9.cpp | 19 +- test/CodeGenCXX/cxx1z-aligned-allocation.cpp | 206 +++++ 27 files changed, 1238 insertions(+), 530 deletions(-) create mode 100644 test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp create mode 100644 test/CXX/expr/expr.unary/expr.delete/p10.cpp create mode 100644 test/CXX/expr/expr.unary/expr.new/p14.cpp create mode 100644 test/CodeGenCXX/cxx1z-aligned-allocation.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 7c0d19c56f9..507d98b797a 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1881,6 +1881,11 @@ public: unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; } unsigned getTypeAlign(const Type *T) const { return getTypeInfo(T).Align; } + /// \brief Return the ABI-specified alignment of a type, in bits, or 0 if + /// the type is incomplete and we cannot determine the alignment (for + /// example, from alignment attributes). + unsigned getTypeAlignIfKnown(QualType T) const; + /// \brief Return the ABI-specified alignment of a (complete) type \p T, in /// characters. CharUnits getTypeAlignInChars(QualType T) const; diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 3de5cc921aa..af45b161f6b 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1838,11 +1838,13 @@ class CXXNewExpr : public Expr { unsigned GlobalNew : 1; /// Do we allocate an array? If so, the first SubExpr is the size expression. unsigned Array : 1; + /// Should the alignment be passed to the allocation function? + unsigned PassAlignment : 1; /// If this is an array allocation, does the usual deallocation /// function for the allocated type want to know the allocated size? unsigned UsualArrayDeleteWantsSize : 1; /// The number of placement new arguments. - unsigned NumPlacementArgs : 13; + unsigned NumPlacementArgs : 26; /// What kind of initializer do we have? Could be none, parens, or braces. /// In storage, we distinguish between "none, and no initializer expr", and /// "none, but an implicit initializer expr". @@ -1858,8 +1860,8 @@ public: }; CXXNewExpr(const ASTContext &C, bool globalNew, FunctionDecl *operatorNew, - FunctionDecl *operatorDelete, bool usualArrayDeleteWantsSize, - ArrayRef<Expr*> placementArgs, + FunctionDecl *operatorDelete, bool PassAlignment, + bool usualArrayDeleteWantsSize, ArrayRef<Expr*> placementArgs, SourceRange typeIdParens, Expr *arraySize, InitializationStyle initializationStyle, Expr *initializer, QualType ty, TypeSourceInfo *AllocatedTypeInfo, @@ -1947,10 +1949,16 @@ public: } /// \brief Returns the CXXConstructExpr from this new-expression, or null. - const CXXConstructExpr* getConstructExpr() const { + const CXXConstructExpr *getConstructExpr() const { return dyn_cast_or_null<CXXConstructExpr>(getInitializer()); } + /// Indicates whether the required alignment should be implicitly passed to + /// the allocation function. + bool passAlignment() const { + return PassAlignment; + } + /// Answers whether the usual array deallocation function for the /// allocated type expects the size of the allocation as a /// parameter. diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 54435502c25..fb1b50f7aac 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -1729,7 +1729,8 @@ public: bool isObjCARCBridgableType() const; bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter - bool isNullPtrType() const; // C++0x nullptr_t + bool isNullPtrType() const; // C++11 std::nullptr_t + bool isAlignValT() const; // C++17 std::align_val_t bool isAtomicType() const; // C11 _Atomic() #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bd634a6b3ac..59199aea7b8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6025,6 +6025,10 @@ def err_no_suitable_delete_member_function_found : Error< "no suitable member %0 in %1">; def err_ambiguous_suitable_delete_member_function_found : Error< "multiple suitable %0 functions in %1">; +def warn_ambiguous_suitable_delete_function_found : Warning< + "multiple suitable %0 functions for %1; no 'operator delete' function " + "will be invoked if initialization throws an exception">, + InGroup<DiagGroup<"ambiguous-delete">>; def note_member_declared_here : Note< "member %0 declared here">; def err_decrement_bool : Error<"cannot decrement expression of type bool">; diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 3bcd327df94..1c2eb927895 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -795,7 +795,9 @@ namespace clang { OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args, StringRef Opc = "", - SourceLocation Loc = SourceLocation()); + SourceLocation Loc = SourceLocation(), + llvm::function_ref<bool(OverloadCandidate&)> Filter = + [](OverloadCandidate&) { return true; }); }; bool isBetterOverloadCandidate(Sema &S, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8bd3916e0d8..adf7b07f14b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4855,14 +4855,9 @@ public: SourceRange R); bool FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, bool UseGlobal, QualType AllocType, bool IsArray, - MultiExprArg PlaceArgs, + bool &PassAlignment, MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete); - bool FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, - DeclarationName Name, MultiExprArg Args, - DeclContext *Ctx, - bool AllowMissing, FunctionDecl *&Operator, - bool Diagnose = true); void DeclareGlobalNewDelete(); void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return, ArrayRef<QualType> Params); @@ -4872,7 +4867,10 @@ public: bool Diagnose = true); FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc, bool CanProvideSize, + bool Overaligned, DeclarationName Name); + FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc, + CXXRecordDecl *RD); /// ActOnCXXDelete - Parsed a C++ 'delete' expression ExprResult ActOnCXXDelete(SourceLocation StartLoc, @@ -9337,6 +9335,7 @@ public: void EraseUnwantedCUDAMatches( const FunctionDecl *Caller, SmallVectorImpl<std::pair<DeclAccessPair, FunctionDecl *>> &Matches); + void EraseUnwantedCUDAMatches(const FunctionDecl *Caller, LookupResult &R); /// Given a implicit special member, infer its CUDA target from the /// calls it needs to make to underlying base/field special members. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index dc99d4dabd4..6b840b9e475 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1572,6 +1572,30 @@ bool ASTContext::isAlignmentRequired(QualType T) const { return isAlignmentRequired(T.getTypePtr()); } +unsigned ASTContext::getTypeAlignIfKnown(QualType T) const { + // An alignment on a typedef overrides anything else. + if (auto *TT = T->getAs<TypedefType>()) + if (unsigned Align = TT->getDecl()->getMaxAlignment()) + return Align; + + // If we have an (array of) complete type, we're done. + T = getBaseElementType(T); + if (!T->isIncompleteType()) + return getTypeAlign(T); + + // If we had an array type, its element type might be a typedef + // type with an alignment attribute. + if (auto *TT = T->getAs<TypedefType>()) + if (unsigned Align = TT->getDecl()->getMaxAlignment()) + return Align; + + // Otherwise, see if the declaration of the type had an attribute. + if (auto *TT = T->getAs<TagType>()) + return TT->getDecl()->getMaxAlignment(); + + return 0; +} + TypeInfo ASTContext::getTypeInfo(const Type *T) const { TypeInfoMap::iterator I = MemoizedTypeInfo.find(T); if (I != MemoizedTypeInfo.end()) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index d03de30d1b4..9e17c0c3de5 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -6330,6 +6330,7 @@ Expr *ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *CE) { Importer.getToContext(), CE->isGlobalNew(), OperatorNewDecl, OperatorDeleteDecl, + CE->passAlignment(), CE->doesUsualArrayDeleteWantSize(), PlacementArgs, Importer.Import(CE->getTypeIdParens()), diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fec7df0ffb7..e330e31d47f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2596,7 +2596,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const { return false; const auto *FPT = getType()->castAs<FunctionProtoType>(); - if (FPT->getNumParams() == 0 || FPT->getNumParams() > 2 || FPT->isVariadic()) + if (FPT->getNumParams() == 0 || FPT->getNumParams() > 3 || FPT->isVariadic()) return false; // If this is a single-parameter function, it must be a replaceable global @@ -2604,20 +2604,42 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const { if (FPT->getNumParams() == 1) return true; - // Otherwise, we're looking for a second parameter whose type is - // 'const std::nothrow_t &', or, in C++1y, 'std::size_t'. - QualType Ty = FPT->getParamType(1); + unsigned Params = 1; + QualType Ty = FPT->getParamType(Params); ASTContext &Ctx = getASTContext(); + + auto Consume = [&] { + ++Params; + Ty = Params < FPT->getNumParams() ? FPT->getParamType(Params) : QualType(); + }; + + // In C++14, the next parameter can be a 'std::size_t' for sized delete. + bool IsSizedDelete = false; if (Ctx.getLangOpts().SizedDeallocation && - Ctx.hasSameType(Ty, Ctx.getSizeType())) - return true; - if (!Ty->isReferenceType()) - return false; - Ty = Ty->getPointeeType(); - if (Ty.getCVRQualifiers() != Qualifiers::Const) - return false; - const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); - return RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace(); + (getDeclName().getCXXOverloadedOperator() == OO_Delete || + getDeclName().getCXXOverloadedOperator() == OO_Array_Delete) && + Ctx.hasSameType(Ty, Ctx.getSizeType())) { + IsSizedDelete = true; + Consume(); + } + + // In C++17, the next parameter can be a 'std::align_val_t' for aligned + // new/delete. + if (Ctx.getLangOpts().AlignedAllocation && !Ty.isNull() && Ty->isAlignValT()) + Consume(); + + // Finally, if this is not a sized delete, the final parameter can + // be a 'const std::nothrow_t&'. + if (!IsSizedDelete && !Ty.isNull() && Ty->isReferenceType()) { + Ty = Ty->getPointeeType(); + if (Ty.getCVRQualifiers() != Qualifiers::Const) + return false; + const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace()) + Consume(); + } + + return Params == FPT->getNumParams(); } LanguageLinkage FunctionDecl::getLanguageLinkage() const { diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index d7472fcc358..97f75a02052 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1577,17 +1577,35 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const { // deallocation function. [...] if (getNumParams() == 1) return true; + unsigned UsualParams = 1; - // C++ [basic.stc.dynamic.deallocation]p2: + // C++ <=14 [basic.stc.dynamic.deallocation]p2: // [...] If class T does not declare such an operator delete but does // declare a member deallocation function named operator delete with // exactly two parameters, the second of which has type std::size_t (18.1), // then this function is a usual deallocation function. + // + // C++17 says a usual deallocation function is one with the signature + // (void* [, size_t] [, std::align_val_t] [, ...]) + // and all such functions are usual deallocation functions. It's not clear + // that allowing varargs functions was intentional. ASTContext &Context = getASTContext(); - if (getNumParams() != 2 || - !Context.hasSameUnqualifiedType(getParamDecl(1)->getType(), - Context.getSizeType())) + if (UsualParams < getNumParams() && + Context.hasSameUnqualifiedType(getParamDecl(UsualParams)->getType(), + Context.getSizeType())) + ++UsualParams; + + if (UsualParams < getNumParams() && + getParamDecl(UsualParams)->getType()->isAlignValT()) + ++UsualParams; + + if (UsualParams != getNumParams()) return false; + + // In C++17 onwards, all potential usual deallocation functions are actual + // usual deallocation functions. + if (Context.getLangOpts().AlignedAllocation) + return true; // This function is a usual deallocation function if there are no // single-parameter deallocation functions of the same kind. diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index a13033d4746..3efb5b1cbc1 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -62,7 +62,7 @@ SourceLocation CXXScalarValueInitExpr::getLocStart() const { // CXXNewExpr CXXNewExpr::CXXNewExpr(const ASTContext &C, bool globalNew, FunctionDecl *operatorNew, FunctionDecl *operatorDelete, - bool usualArrayDeleteWantsSize, + bool PassAlignment, bool usualArrayDeleteWantsSize, ArrayRef<Expr*> placementArgs, SourceRange typeIdParens, Expr *arraySize, InitializationStyle initializationStyle, @@ -76,7 +76,8 @@ CXXNewExpr::CXXNewExpr(const ASTContext &C, bool globalNew, SubExprs(nullptr), OperatorNew(operatorNew), OperatorDelete(operatorDelete), AllocatedTypeInfo(allocatedTypeInfo), TypeIdParens(typeIdParens), Range(Range), DirectInitRange(directInitRange), - GlobalNew(globalNew), UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) { + GlobalNew(globalNew), PassAlignment(PassAlignment), + UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) { assert((initializer != nullptr || initializationStyle == NoInit) && "Only NoInit can have no initializer."); StoredInitializationStyle = initializer ? initializationStyle + 1 : 0; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 4aa07568dc7..113974c4b60 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2337,6 +2337,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const { return false; } +bool Type::isAlignValT() const { + if (auto *ET = getAs<EnumType>()) { + auto *II = ET->getDecl()->getIdentifier(); + if (II && II->isStr("align_val_t") && ET->getDecl()->isInStdNamespace()) + return true; + } + return false; +} + bool Type::isPromotableIntegerType() const { if (const BuiltinType *BT = getAs<BuiltinType>()) switch (BT->getKind()) { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index e022663788a..ccab9f08d63 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -1219,111 +1219,116 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, llvm_unreachable("predeclared global operator new/delete is missing"); } -namespace { - /// A cleanup to call the given 'operator delete' function upon - /// abnormal exit from a new expression. - class CallDeleteDuringNew final : public EHScopeStack::Cleanup { - size_t NumPlacementArgs; - const FunctionDecl *OperatorDelete; - llvm::Value *Ptr; - llvm::Value *AllocSize; +static std::pair<bool, bool> +shouldPassSizeAndAlignToUsualDelete(const FunctionProtoType *FPT) { + auto AI = FPT->param_type_begin(), AE = FPT->param_type_end(); - RValue *getPlacementArgs() { return reinterpret_cast<RValue*>(this+1); } + // The first argument is always a void*. + ++AI; - public: - static size_t getExtraSize(size_t NumPlacementArgs) { - return NumPlacementArgs * sizeof(RValue); - } - - CallDeleteDuringNew(size_t NumPlacementArgs, - const FunctionDecl *OperatorDelete, - llvm::Value *Ptr, - llvm::Value *AllocSize) - : NumPlacementArgs(NumPlacementArgs), OperatorDelete(OperatorDelete), - Ptr(Ptr), AllocSize(AllocSize) {} + // Figure out what other parameters we should be implicitly passing. + bool PassSize = false; + bool PassAlignment = false; - void setPlacementArg(unsigned I, RValue Arg) { - assert(I < NumPlacementArgs && "index out of range"); - getPlacementArgs()[I] = Arg; - } - - void Emit(CodeGenFunction &CGF, Flags flags) override { - const FunctionProtoType *FPT - = OperatorDelete->getType()->getAs<FunctionProtoType>(); - assert(FPT->getNumParams() == NumPlacementArgs + 1 || - (FPT->getNumParams() == 2 && NumPlacementArgs == 0)); - - CallArgList DeleteArgs; - - // The first argument is always a void*. - FunctionProtoType::param_type_iterator AI = FPT->param_type_begin(); - DeleteArgs.add(RValue::get(Ptr), *AI++); - - // A member 'operator delete' can take an extra 'size_t' argument. - if (FPT->getNumParams() == NumPlacementArgs + 2) - DeleteArgs.add(RValue::get(AllocSize), *AI++); + if (AI != AE && (*AI)->isIntegerType()) { + PassSize = true; + ++AI; + } - // Pass the rest of the arguments, which must match exactly. - for (unsigned I = 0; I != NumPlacementArgs; ++I) - DeleteArgs.add(getPlacementArgs()[I], *AI++); + if (AI != AE && (*AI)->isAlignValT()) { + PassAlignment = true; + ++AI; + } - // Call 'operator delete'. - EmitNewDeleteCall(CGF, OperatorDelete, FPT, DeleteArgs); - } - }; + assert(AI == AE && "unexpected usual deallocation function parameter"); + return {PassSize, PassAlignment}; +} - /// A cleanup to call the given 'operator delete' function upon - /// abnormal exit from a new expression when the new expression is - /// conditional. - class CallDeleteDuringConditionalNew final : public EHScopeStack::Cleanup { - size_t NumPlacementArgs; +namespace { + /// A cleanup to call the given 'operator delete' function upon abnormal + /// exit from a new expression. Templated on a traits type that deals with + /// ensuring that the arguments dominate the cleanup if necessary. + template<typename Traits> + class CallDeleteDuringNew final : public EHScopeStack::Cleanup { + /// Type used to hold llvm::Value*s. + typedef typename Traits::ValueTy ValueTy; + /// Type used to hold RValues. + typedef typename Traits::RValueTy RValueTy; + struct PlacementArg { + RValueTy ArgValue; + QualType ArgType; + }; + + unsigned NumPlacementArgs : 31; + unsigned PassAlignmentToPlacementDelete : 1; const FunctionDecl *OperatorDelete; - DominatingValue<RValue>::saved_type Ptr; - DominatingValue<RValue>::saved_type AllocSize; + ValueTy Ptr; + ValueTy AllocSize; + CharUnits AllocAlign; - DominatingValue<RValue>::saved_type *getPlacementArgs() { - return reinterpret_cast<DominatingValue<RValue>::saved_type*>(this+1); + PlacementArg *getPlacementArgs() { + return reinterpret_cast<PlacementArg *>(this + 1); } public: static size_t getExtraSize(size_t NumPlacementArgs) { - return NumPlacementArgs * sizeof(DominatingValue<RValue>::saved_type); + return NumPlacementArgs * sizeof(PlacementArg); } - CallDeleteDuringConditionalNew(size_t NumPlacementArgs, - const FunctionDecl *OperatorDelete, - DominatingValue<RValue>::saved_type Ptr, - DominatingValue<RValue>::saved_type AllocSize) - : NumPlacementArgs(NumPlacementArgs), OperatorDelete(OperatorDelete), - Ptr(Ptr), AllocSize(AllocSize) {} - - void setPlacementArg(unsigned I, DominatingValue<RValue>::saved_type Arg) { + CallDeleteDuringNew(size_t NumPlacementArgs, + const FunctionDecl *OperatorDelete, ValueTy Ptr, + ValueTy AllocSize, bool PassAlignmentToPlacementDelete, + CharUnits AllocAlign) + : NumPlacementArgs(NumPlacementArgs), + PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete), + OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize), + AllocAlign(AllocAlign) {} + + void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) { assert(I < NumPlacementArgs && "index out of range"); - getPlacementArgs()[I] = Arg; + getPlacementArgs()[I] = {Arg, Type}; } void Emit(CodeGenFunction &CGF, Flags flags) override { - const FunctionProtoType *FPT - = OperatorDelete->getType()->getAs<FunctionProtoType>(); - assert(FPT->getNumParams() == NumPlacementArgs + 1 || - (FPT->getNumParams() == 2 && NumPlacementArgs == 0)); - + const FunctionProtoType *FPT = + OperatorDelete->getType()->getAs<FunctionProtoType>(); CallArgList DeleteArgs; // The first argument is always a void*. - FunctionProtoType::param_type_iterator AI = FPT->param_type_begin(); - DeleteArgs.add(Ptr.restore(CGF), *AI++); - - // A member 'operator delete' can take an extra 'size_t' argument. - if (FPT->getNumParams() == NumPlacementArgs + 2) { - RValue RV = AllocSize.restore(CGF); - DeleteArgs.add(RV, *AI++); + DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0)); + + // Figure out what other parameters we should be implicitly passing. + bool PassSize = false; + bool PassAlignment = false; + if (NumPlacementArgs) { + // A placement deallocation function is implicitly passed an alignment + // if the placement allocation function was, but is never passed a size. + PassAlignment = PassAlignmentToPlacementDelete; + } else { + // For a non-placement new-expression, 'operator delete' can take a + // size and/or an alignment if it has the right parameters. + std::tie(PassSize, PassAlignment) = + shouldPassSizeAndAlignToUsualDelete(FPT); } + // The second argument can be a std::size_t (for non-placement delete). + if (PassSize) + DeleteArgs.add(Traits::get(CGF, AllocSize), + CGF.getContext().getSizeType()); + + // The next (second or third) argument can be a std::align_val_t, which + // is an enum whose underlying type is std::size_t. + // FIXME: Use the right type as the parameter type. Note that in a call + // to operator delete(size_t, ...), we may not have it available. + if (PassAlignment) + DeleteArgs.add(RValue::get(llvm::ConstantInt::get( + CGF.SizeTy, AllocAlign.getQuantity())), + CGF.getContext().getSizeType()); + // Pass the rest of the arguments, which must match exactly. for (unsigned I = 0; I != NumPlacementArgs; ++I) { - RValue RV = getPlacementArgs()[I].restore(CGF); - DeleteArgs.add(RV, *AI++); + auto Arg = getPlacementArgs()[I]; + DeleteArgs.add(Traits::get(CGF, Arg.ArgValue), Arg.ArgType); } // Call 'operator delete'. @@ -1338,18 +1343,34 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF, const CXXNewExpr *E, Address NewPtr, llvm::Value *AllocSize, + CharUnits AllocAlign, const CallArgList &NewArgs) { + unsigned NumNonPlacementArgs = E->passAlignment() ? 2 : 1; + // If we're not inside a conditional branch, then the cleanup will // dominate and we can do the easier (and more efficient) thing. if (!CGF.isInConditionalBranch()) { - CallDeleteDuringNew *Cleanup = CGF.EHStack - .pushCleanupWithExtra<CallDeleteDuringNew>(EHCleanup, - E->getNumPlacementArgs(), - E->getOperatorDelete(), - NewPtr.getPointer(), - AllocSize); - for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) - Cleanup->setPlacementArg(I, NewArgs[I+1].RV); + struct DirectCleanupTraits { + typedef llvm::Value *ValueTy; + typedef RValue RValueTy; + static RValue get(CodeGenFunction &, ValueTy V) { return RValue::get(V); } + static RValue get(CodeGenFunction &, RValueTy V) { return V; } + }; + + typedef CallDeleteDuringNew<DirectCleanupTraits> DirectCleanup; + + DirectCleanup *Cleanup = CGF.EHStack + .pushCleanupWithExtra<DirectCleanup>(EHCleanup, + E->getNumPlacementArgs(), + E->getOperatorDelete(), + NewPtr.getPointer(), + AllocSize, + E->passAlignment(), + AllocAlign); + for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) { + auto &Arg = NewArgs[I + NumNonPlacementArgs]; + Cleanup->setPlacementArg(I, Arg.RV, Arg.Ty); + } return; } @@ -1360,15 +1381,28 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF, DominatingValue<RValue>::saved_type SavedAllocSize = DominatingValue<RValue>::save(CGF, RValue::get(AllocSize)); - CallDeleteDuringConditionalNew *Cleanup = CGF.EHStack - .pushCleanupWithExtra<CallDeleteDuringConditionalNew>(EHCleanup, - E->getNumPlacementArgs(), - E->getOperatorDelete(), - SavedNewPtr, - SavedAllocSize); - for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) - Cleanup->setPlacementArg(I, - DominatingValue<RValue>::save(CGF, NewArgs[I+1].RV)); + struct ConditionalCleanupTraits { + typedef DominatingValue<RValue>::saved_type ValueTy; + typedef DominatingValue<RValue>::saved_type RValueTy; + static RValue get(CodeGenFunction &CGF, ValueTy V) { + return V.restore(CGF); + } + }; + typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup; + + ConditionalCleanup *Cleanup = CGF.EHStack + .pushCleanupWithExtra<ConditionalCleanup>(EHCleanup, + E->getNumPlacementArgs(), + E->getOperatorDelete(), + SavedNewPtr, + SavedAllocSize, + E->passAlignment(), + AllocAlign); + for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) { + auto &Arg = NewArgs[I + NumNonPlacementArgs]; + Cleanup->setPlacementArg(I, DominatingValue<RValue>::save(CGF, Arg.RV), + Arg.Ty); + } CGF.initFullExprCleanup(); } @@ -1397,6 +1431,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { llvm::Value *allocSize = EmitCXXNewAllocSize(*this, E, minElements, numElements, allocSizeWithoutCookie); + CharUnits allocAlign = getContext().getTypeAlignInChars(allocType); // Emit the allocation call. If the allocator is a global placement // operator, just "inline" it directly. @@ -1412,10 +1447,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // The pointer expression will, in many cases, be an opaque void*. // In these cases, discard the computed alignment and use the // formal alignment of the allocated type. - if (alignSource != AlignmentSource::Decl) { - allocation = Address(allocation.getPointer(), - getContext().getTypeAlignInChars(allocType)); - } + if (alignSource != AlignmentSource::Decl) + allocation = Address(allocation.getPointer(), allocAlign); // Set up allocatorArgs for the call to operator delete if it's not // the reserved global operator. @@ -1428,28 +1461,55 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { } else { const FunctionProtoType *allocatorType = allocator->getType()->castAs<FunctionProtoType>(); + unsigned ParamsToSkip = 0; // The allocation size is the first argument. QualType sizeType = getContext().getSizeType(); allocatorArgs.add(RValue::get(allocSize), sizeType); + ++ParamsToSkip; + + if (allocSize != allocSizeWithoutCookie) { + CharUnits cookieAlign = getSizeAlign(); // FIXME: Ask the ABI. + allocAlign = std::max(allocAlign, cookieAlign); + } + + // The allocation alignment may be passed as the second argument. + if (E->passAlignment()) { + QualType AlignValT = sizeType; + if (allocatorType->getNumParams() > 1) { + AlignValT = allocatorType->getParamType(1); + assert(getContext().hasSameUnqualifiedType( + AlignValT->castAs<EnumType>()->getDecl()->getIntegerType(), + sizeType) && + "wrong type for alignment parameter"); + ++ParamsToSkip; + } else { + // Corner case, passing alignment to 'operator new(size_t, ...)'. + assert(allocator->isVariadic() && "can't pass alignment to allocator"); + } + allocatorArgs.add( + RValue::get(llvm::ConstantInt::get(SizeTy, allocAlign.getQuantity())), + AlignValT); + } - // We start at 1 here because the first argument (the allocation size) - // has already been emitted. + // FIXME: Why do we not pass a CalleeDecl here? EmitCallArgs(allocatorArgs, allocatorType, E->placement_arguments(), - /* CalleeDecl */ nullptr, - /*ParamsToSkip*/ 1); + /*CalleeDecl*/nullptr, /*ParamsToSkip*/ParamsToSkip); RValue RV = EmitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs); - // For now, only assume that the allocation function returns - // something satisfactorily aligned for the element type, plus - // the cookie if we have one. - CharUnits allocationAlign = - getContext().getTypeAlignInChars(allocType); - if (allocSize != allocSizeWithoutCookie) { - CharUnits cookieAlign = getSizeAlign(); // FIXME? - allocationAlign = std::max(allocationAlign, cookieAlign); + // If this was a call to a global replaceable allocation function that does + // not take an alignment argument, the allocator is known to produce + // storage that's suitably aligned for any object that fits, up to a known + // threshold. Otherwise assume it's suitably aligned for the allocated type. + CharUnits allocationAlign = allocAlign; + if (!E->passAlignment() && + allocator->isReplaceableGlobalAllocationFunction()) { + unsigned AllocatorAlign = llvm::PowerOf2Floor(std::min<uint64_t>( + Target.getNewAlign(), getContext().getTypeSize(allocType))); + allocationAlign = std::max( + allocationAlign, getContext().toCharUnitsFromBits(AllocatorAlign)); } allocation = Address(RV.getScalarVal(), allocationAlign); @@ -1488,7 +1548,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { llvm::Instruction *cleanupDominator = nullptr; if (E->getOperatorDelete() && !E->getOperatorDelete()->isReservedGlobalPlacementOperator()) { - EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocatorArgs); + EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocAlign, + allocatorArgs); operatorDeleteCleanup = EHStack.stable_begin(); cleanupDominator = Builder.CreateUnreachable(); } @@ -1550,31 +1611,58 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { } void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD, - llvm::Value *Ptr, - QualType DeleteTy) { - assert(DeleteFD->getOverloadedOperator() == OO_Delete); + llvm::Value *Ptr, QualType DeleteTy, + llvm::Value *NumElements, + CharUnits CookieSize) { + assert((!NumElements && CookieSize.isZero()) || + DeleteFD->getOverloadedOperator() == OO_Array_Delete); const FunctionProtoType *DeleteFTy = DeleteFD->getType()->getAs<FunctionProtoType>(); CallArgList DeleteArgs; - // Check if we need to pass the size to the delete operator. - llvm::Value *Size = nullptr; - QualType SizeTy; - if (DeleteFTy->getNumParams() == 2) { - SizeTy = DeleteFTy->getParamType(1); - CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy); - Size = llvm::ConstantInt::get(ConvertType(SizeTy), - DeleteTypeSize.getQuantity()); - } + std::pair<bool, bool> PassSizeAndAlign = + shouldPassSizeAndAlignToUsualDelete(DeleteFTy); - QualType ArgTy = DeleteFTy->getParamType(0); + auto ParamTypeIt = DeleteFTy->param_type_begin(); + + // Pass the pointer itself. + QualType ArgTy = *ParamTypeIt++; llvm::Value *DeletePtr = Builder.CreateBitCast(Ptr, ConvertType(ArgTy)); DeleteArgs.add(RValue::get(DeletePtr), ArgTy); - if (Size) - DeleteArgs.add(RValue::get(Size), SizeTy); + // Pass the size if the delete function has a size_t parameter. + if (PassSizeAndAlign.first) { + QualType SizeType = *ParamTypeIt++; + CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy); + llvm::Value *Size = llvm::ConstantInt::get(ConvertType(SizeType), + DeleteTypeSize.getQuantity()); + + // For array new, multiply by the number of elements. + if (NumElements) + Size = Builder.CreateMul(Size, NumElements); + + // If there is a cookie, add the cookie size. + if (!CookieSize.isZero()) + Size = Builder.CreateAdd( + Size, llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity())); + + DeleteArgs.add(RValue::get(Size), SizeType); + } + + // Pass the alignment if the delete function has an align_val_t parameter. + if (PassSizeAndAlign.second) { + QualType AlignValType = *ParamTypeIt++; + CharUnits DeleteTypeAlign = getContext().toCharUnitsFromBits( + getContext().getTypeAlignIfKnown(DeleteTy)); + llvm::Value *Align = llvm::ConstantInt::get(ConvertType(AlignValType), + DeleteTypeAlign.getQuantity()); + DeleteArgs.add(RValue::get(Align), AlignValType); + } + + assert(ParamTypeIt == DeleteFTy->param_type_end() && + "unknown parameter to usual delete function"); // Emit the call to delete. EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs); @@ -1678,45 +1766,8 @@ namespace { ElementType(ElementType), CookieSize(CookieSize) {} void Emit(CodeGenFunction &CGF, Flags flags) override { - const FunctionProtoType *DeleteFTy = - OperatorDelete->getType()->getAs<FunctionProtoType>(); - assert(DeleteFTy->getNumParams() == 1 || DeleteFTy->getNumParams() == 2); - - CallArgList Args; - - // Pass the pointer as the first argument. - QualType VoidPtrTy = DeleteFTy->getParamType(0); - llvm::Value *DeletePtr - = CGF.Builder.CreateBitCast(Ptr, CGF.ConvertType(VoidPtrTy)); - Args.add(RValue::get(DeletePtr), VoidPtrTy); - - // Pass the original requested size as the second argument. - if (DeleteFTy->getNumParams() == 2) { - QualType size_t = DeleteFTy->getParamType(1); - llvm::IntegerType *SizeTy - = cast<llvm::IntegerType>(CGF.ConvertType(size_t)); - - CharUnits ElementTypeSize = - CGF.CGM.getContext().getTypeSizeInChars(ElementType); - - // The size of an element, multiplied by the number of elements. - llvm::Value *Size - = llvm::ConstantInt::get(SizeTy, ElementTypeSize.getQuantity()); - if (NumElements) - Size = CGF.Builder.CreateMul(Size, NumElements); - - // Plus the size of the cookie if applicable. - if (!CookieSize.isZero()) { - llvm::Value *CookieSizeV - = llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity()); - Size = CGF.Builder.CreateAdd(Size, CookieSizeV); - } - - Args.add(RValue::get(Size), size_t); - } - - // Emit the call to delete. - EmitNewDeleteCall(CGF, OperatorDelete, DeleteFTy, Args); + CGF.EmitDeleteCall(OperatorDelete, Ptr, ElementType, NumElements, + CookieSize); } }; } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 4e149e65dcd..eac759fc826 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2033,7 +2033,8 @@ public: void EmitCXXDeleteExpr(const CXXDeleteExpr *E); void EmitDeleteCall(const FunctionDecl *DeleteFD, llvm::Value *Ptr, - QualType DeleteTy); + QualType DeleteTy, llvm::Value *NumElements = nullptr, + CharUnits CookieSize = CharUnits()); RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, const Expr *Arg, bool IsDelete); diff --git a/lib/Sema/SemaCUDA.cpp b/lib/Sema/SemaCUDA.cpp index d6c0606674e..827eb02bcd6 100644 --- a/lib/Sema/SemaCUDA.cpp +++ b/lib/Sema/SemaCUDA.cpp @@ -158,6 +158,34 @@ Sema::IdentifyCUDAPreference(const FunctionDecl *Caller, llvm_unreachable("All cases should've been handled by now."); } +void Sema::EraseUnwantedCUDAMatches(const FunctionDecl *Caller, + LookupResult &R) { + if (R.isSingleResult()) + return; + + // Gets the CUDA function preference for a call from Caller to Match. + auto GetCFP = [&](const NamedDecl *D) { + if (auto *Callee = dyn_cast<FunctionDecl>(D->getUnderlyingDecl())) + return IdentifyCUDAPreference(Caller, Callee); + return CFP_Never; + }; + + // Find the best call preference among the functions in R. + CUDAFunctionPreference BestCFP = GetCFP(*std::max_element( + R.begin(), R.end(), [&](const NamedDecl *D1, const NamedDecl *D2) { + return GetCFP(D1) < GetCFP(D2); + })); + + // Erase all functions with lower priority. + auto Filter = R.makeFilter(); + while (Filter.hasNext()) { + auto *Callee = dyn_cast<FunctionDecl>(Filter.next()->getUnderlyingDecl()); + if (Callee && GetCFP(Callee) < BestCFP) + Filter.erase(); + } + Filter.done(); +} + template <typename T> static void EraseUnwantedCUDAMatchesImpl( Sema &S, const FunctionDecl *Caller, llvm::SmallVectorImpl<T> &Matches, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 32201c99d3d..7baa85a6464 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -14368,6 +14368,14 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (!Completed) Record->completeDefinition(); + // We may have deferred checking for a deleted destructor. Check now. + if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(Record)) { + auto *Dtor = CXXRecord->getDestructor(); + if (Dtor && Dtor->isImplicit() && + ShouldDeleteSpecialMember(Dtor, CXXDestructor)) + SetDeclDeleted(Dtor, CXXRecord->getLocation()); + } + if (Record->hasAttrs()) { CheckAlignasUnderalignment(Record); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 0f7f0ffa75a..36a0338d4ae 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6755,7 +6755,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name, - OperatorDelete, false)) { + OperatorDelete, /*Diagnose*/false)) { if (Diagnose) Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete); return true; @@ -7695,19 +7695,11 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) { Loc = RD->getLocation(); // If we have a virtual destructor, look up the deallocation function - FunctionDecl *OperatorDelete = nullptr; - DeclarationName Name = - Context.DeclarationNames.getCXXOperatorName(OO_Delete); - if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete)) - return true; - // If there's no class-specific operator delete, look up the global - // non-array delete. - if (!OperatorDelete) - OperatorDelete = FindUsualDeallocationFunction(Loc, true, Name); - - MarkFunctionReferenced(Loc, OperatorDelete); - - Destructor->setOperatorDelete(OperatorDelete); + if (FunctionDecl *OperatorDelete = + FindDeallocationFunctionForDestructor(Loc, RD)) { + MarkFunctionReferenced(Loc, OperatorDelete); + Destructor->setOperatorDelete(OperatorDelete); + } } return false; @@ -10280,7 +10272,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { Scope *S = getScopeForContext(ClassDecl); CheckImplicitSpecialMemberDeclaration(S, Destructor); - if (ShouldDeleteSpecialMember(Destructor, CXXDestructor)) + // We can't check whether an implicit destructor is deleted before we complete + // the definition of the class, because its validity depends on the alignment + // of the class. We'll check this from ActOnFields once the class is complete. + if (ClassDecl->isCompleteDefinition() && + ShouldDeleteSpecialMember(Destructor, CXXDestructor)) SetDeclDeleted(Destructor, ClassLoc); // Introduce this destructor into its scope. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index ad102f8d203..9c5dba5a38a 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1321,8 +1321,126 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, return Result; } -/// doesUsualArrayDeleteWantSize - Answers whether the usual -/// operator delete[] for the given type has a size_t parameter. +/// \brief Determine whether the given function is a non-placement +/// deallocation function. +static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { + if (FD->isInvalidDecl()) + return false; + + if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) + return Method->isUsualDeallocationFunction(); + + if (FD->getOverloadedOperator() != OO_Delete && + FD->getOverloadedOperator() != OO_Array_Delete) + return false; + + unsigned UsualParams = 1; + + if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() && + S.Context.hasSameUnqualifiedType( + FD->getParamDecl(UsualParams)->getType(), + S.Context.getSizeType())) + ++UsualParams; + + if (S.getLangOpts().AlignedAllocation && UsualParams < FD->getNumParams() && + S.Context.hasSameUnqualifiedType( + FD->getParamDecl(UsualParams)->getType(), + S.Context.getTypeDeclType(S.getStdAlignValT()))) + ++UsualParams; + + return UsualParams == FD->getNumParams(); +} + +namespace { + struct UsualDeallocFnInfo { + UsualDeallocFnInfo() : Found(), FD(nullptr) {} + UsualDeallocFnInfo(DeclAccessPair Found) + : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())), + HasSizeT(false), HasAlignValT(false) { + // A function template declaration is never a usual deallocation function. + if (!FD) + return; + if (FD->getNumParams() == 3) + HasAlignValT = HasSizeT = true; + else if (FD->getNumParams() == 2) { + HasSizeT = FD->getParamDecl(1)->getType()->isIntegerType(); + HasAlignValT = !HasSizeT; + } + } + + operator bool() const { return FD; } + + DeclAccessPair Found; + FunctionDecl *FD; + bool HasSizeT, HasAlignValT; + }; +} + +/// Determine whether a type has new-extended alignment. This may be called when +/// the type is incomplete (for a delete-expression with an incomplete pointee +/// type), in which case it will conservatively return false if the alignment is +/// not known. +static bool hasNewExtendedAlignment(Sema &S, QualType AllocType) { + return S.getLangOpts().AlignedAllocation && + S.getASTContext().getTypeAlignIfKnown(AllocType) > + S.getASTContext().getTargetInfo().getNewAlign(); +} + +/// Select the correct "usual" deallocation function to use from a selection of +/// deallocation functions (either global or class-scope). +static UsualDeallocFnInfo resolveDeallocationOverload( + Sema &S, LookupResult &R, bool WantSize, bool WantAlign, + llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) { + UsualDeallocFnInfo Best; + + // For CUDA, rank callability above anything else when ordering usual + // deallocation functions. + // FIXME: We should probably instead rank this between alignment (which + // affects correctness) and size (which is just an optimization). + if (S.getLangOpts().CUDA) + S.EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(S.CurContext), R); + + for (auto I = R.begin(), E = R.end(); I != E; ++I) { + UsualDeallocFnInfo Info(I.getPair()); + if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD)) + continue; + + if (!Best) { + Best = Info; + if (BestFns) + BestFns->push_back(Info); + continue; + } + + // C++17 [expr.delete]p10: + // If the type has new-extended alignment, a function with a parameter of + // type std::align_val_t is preferred; otherwise a function without such a + // parameter is preferred + if (Best.HasAlignValT == WantAlign && Info.HasAlignValT != WantAlign) + continue; + + if (Best.HasAlignValT == Info.HasAlignValT && + Best.HasSizeT == WantSize && Info.HasSizeT != WantSize) + continue; + + // If more than one preferred function is found, all non-preferred + // functions are eliminated from further consideration. + if (BestFns && (Best.HasAlignValT != Info.HasAlignValT || + Best.HasSizeT != Info.HasSizeT)) + BestFns->clear(); + + Best = Info; + if (BestFns) + BestFns->push_back(Info); + } + + return Best; +} + +/// Determine whether a given type is a class for which 'delete[]' would call +/// a member 'operator delete[]' with a 'size_t' parameter. This implies that +/// we need to store the array size (even if the type is +/// trivially-destructible). static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, QualType allocType) { const RecordType *record = @@ -1346,35 +1464,13 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, // on this thing, so it doesn't matter if we allocate extra space or not. if (ops.isAmbiguous()) return false; - LookupResult::Filter filter = ops.makeFilter(); - while (filter.hasNext()) { - NamedDecl *del = filter.next()->getUnderlyingDecl(); - - // C++0x [basic.stc.dynamic.deallocation]p2: - // A template instance is never a usual deallocation function, - // regardless of its signature. - if (isa<FunctionTemplateDecl>(del)) { - filter.erase(); - continue; - } - - // C++0x [basic.stc.dynamic.deallocation]p2: - // If class T does not declare [an operator delete[] with one - // parameter] but does declare a member deallocation function - // named operator delete[] with exactly two parameters, the - // second of which has type std::size_t, then this function - // is a usual deallocation function. - if (!cast<CXXMethodDecl>(del)->isUsualDeallocationFunction()) { - filter.erase(); - continue; - } - } - filter.done(); - - if (!ops.isSingleResult()) return false; - - const FunctionDecl *del = cast<FunctionDecl>(ops.getFoundDecl()); - return (del->getNumParams() == 2); + // C++17 [expr.delete]p10: + // If the deallocation functions have class scope, the one without a + // parameter of type std::size_t is selected. + auto Best = resolveDeallocationOverload( + S, ops, /*WantSize*/false, + /*WantAlign*/hasNewExtendedAlignment(S, allocType)); + return Best && Best.HasSizeT; } /// \brief Parsed a C++ 'new' expression (C++ 5.3.4). @@ -1730,21 +1826,26 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, FunctionDecl *OperatorNew = nullptr; FunctionDecl *OperatorDelete = nullptr; + unsigned Alignment = + AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType); + unsigned NewAlignment = Context.getTargetInfo().getNewAlign(); + bool PassAlignment = getLangOpts().AlignedAllocation && + Alignment > NewAlignment; if (!AllocType->isDependentType() && !Expr::hasAnyTypeDependentArguments(PlacementArgs) && FindAllocationFunctions(StartLoc, SourceRange(PlacementLParen, PlacementRParen), - UseGlobal, AllocType, ArraySize, PlacementArgs, - OperatorNew, OperatorDelete)) + UseGlobal, AllocType, ArraySize, PassAlignment, + PlacementArgs, OperatorNew, OperatorDelete)) return ExprError(); // If this is an array allocation, compute whether the usual array // deallocation function for the type has a size_t parameter. bool UsualArrayDeleteWantsSize = false; if (ArraySize && !AllocType->isDependentType()) - UsualArrayDeleteWantsSize - = doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType); + UsualArrayDeleteWantsSize = + doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType); SmallVector<Expr *, 8> AllPlaceArgs; if (OperatorNew) { @@ -1755,9 +1856,11 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // We've already converted the placement args, just fill in any default // arguments. Skip the first parameter because we don't have a corresponding - // argument. - if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, 1, - PlacementArgs, AllPlaceArgs, CallType)) + // argument. Skip the second parameter too if we're passing in the + // alignment; we've already filled it in. + if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, + PassAlignment ? 2 : 1, PlacementArgs, + AllPlaceArgs, CallType)) return ExprError(); if (!AllPlaceArgs.empty()) @@ -1767,21 +1870,18 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, DiagnoseSentinelCalls(OperatorNew, PlacementLParen, PlacementArgs); // FIXME: Missing call to CheckFunctionCall or equivalent - } - // Warn if the type is over-aligned and is being allocated by global operator - // new. - if (PlacementArgs.empty() && OperatorNew && - (OperatorNew->isImplicit() || - (OperatorNew->getLocStart().isValid() && - getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) { - if (unsigned Align = Context.getPreferredTypeAlign(AllocType.getTypePtr())){ - unsigned SuitableAlign = Context.getTargetInfo().getSuitableAlign(); - if (Align > SuitableAlign) + // Warn if the type is over-aligned and is being allocated by (unaligned) + // global operator new. + if (PlacementArgs.empty() && !PassAlignment && + (OperatorNew->isImplicit() || + (OperatorNew->getLocStart().isValid() && + getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) { + if (Alignment > NewAlignment) Diag(StartLoc, diag::warn_overaligned_type) << AllocType - << unsigned(Align / Context.getCharWidth()) - << unsigned(SuitableAlign / Context.getCharWidth()); + << unsigned(Alignment / Context.getCharWidth()) + << unsigned(NewAlignment / Context.getCharWidth()); } } @@ -1880,7 +1980,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, } return new (Context) - CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, + CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, PassAlignment, UsualArrayDeleteWantsSize, PlacementArgs, TypeIdParens, ArraySize, initStyle, Initializer, ResultType, AllocTypeInfo, Range, DirectInitRange); @@ -1923,32 +2023,128 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, return false; } -/// \brief Determine whether the given function is a non-placement -/// deallocation function. -static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { - if (FD->isInvalidDecl()) - return false; +static bool +resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range, + SmallVectorImpl<Expr *> &Args, bool &PassAlignment, + FunctionDecl *&Operator, + OverloadCandidateSet *AlignedCandidates = nullptr, + Expr *AlignArg = nullptr) { + OverloadCandidateSet Candidates(R.getNameLoc(), + OverloadCandidateSet::CSK_Normal); + for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end(); + Alloc != AllocEnd; ++Alloc) { + // Even member operator new/delete are implicitly treated as + // static, so don't use AddMemberCandidate. + NamedDecl *D = (*Alloc)->getUnderlyingDecl(); - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) - return Method->isUsualDeallocationFunction(); + if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { + S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), + /*ExplicitTemplateArgs=*/nullptr, Args, + Candidates, + /*SuppressUserConversions=*/false); + continue; + } - if (FD->getOverloadedOperator() != OO_Delete && - FD->getOverloadedOperator() != OO_Array_Delete) + FunctionDecl *Fn = cast<FunctionDecl>(D); + S.AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates, + /*SuppressUserConversions=*/false); + } + + // Do the resolution. + OverloadCandidateSet::iterator Best; + switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) { + case OR_Success: { + // Got one! + FunctionDecl *FnDecl = Best->Function; + if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.getNamingClass(), + Best->FoundDecl) == Sema::AR_inaccessible) + return true; + + Operator = FnDecl; return false; + } - if (FD->getNumParams() == 1) + case OR_No_Viable_Function: + // C++17 [expr.new]p13: + // If no matching function is found and the allocated object type has + // new-extended alignment, the alignment argument is removed from the + // argument list, and overload resolution is performed again. + if (PassAlignment) { + PassAlignment = false; + AlignArg = Args[1]; + Args.erase(Args.begin() + 1); + return resolveAllocationOverload(S, R, Range, Args, PassAlignment, + Operator, &Candidates, AlignArg); + } + + // MSVC will fall back on trying to find a matching global operator new + // if operator new[] cannot be found. Also, MSVC will leak by not + // generating a call to operator delete or operator delete[], but we + // will not replicate that bug. + // FIXME: Find out how this interacts with the std::align_val_t fallback + // once MSVC implements it. + if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New && + S.Context.getLangOpts().MSVCCompat) { + R.clear(); + R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New)); + S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl()); + // FIXME: This will give bad diagnostics pointing at the wrong functions. + return resolveAllocationOverload(S, R, Range, Args, PassAlignment, + Operator, nullptr); + } + + S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call) + << R.getLookupName() << Range; + + // If we have aligned candidates, only note the align_val_t candidates + // from AlignedCandidates and the non-align_val_t candidates from + // Candidates. + if (AlignedCandidates) { + auto IsAligned = [](OverloadCandidate &C) { + return C.Function->getNumParams() > 1 && + C.Function->getParamDecl(1)->getType()->isAlignValT(); + }; + auto IsUnaligned = [&](OverloadCandidate &C) { return !IsAligned(C); }; + + // This was an overaligned allocation, so list the aligned candidates + // first. + Args.insert(Args.begin() + 1, AlignArg); + AlignedCandidates->NoteCandidates(S, OCD_AllCandidates, Args, "", + R.getNameLoc(), IsAligned); + Args.erase(Args.begin() + 1); + Candidates.NoteCandidates(S, OCD_AllCandidates, Args, "", R.getNameLoc(), + IsUnaligned); + } else { + Candidates.NoteCandidates(S, OCD_AllCandidates, Args); + } return true; - return S.getLangOpts().SizedDeallocation && FD->getNumParams() == 2 && - S.Context.hasSameUnqualifiedType(FD->getParamDecl(1)->getType(), - S.Context.getSizeType()); + case OR_Ambiguous: + S.Diag(R.getNameLoc(), diag::err_ovl_ambiguous_call) + << R.getLookupName() << Range; + Candidates.NoteCandidates(S, OCD_ViableCandidates, Args); + return true; + + case OR_Deleted: { + S.Diag(R.getNameLoc(), diag::err_ovl_deleted_call) + << Best->Function->isDeleted() + << R.getLookupName() + << S.getDeletedOrUnavailableSuffix(Best->Function) + << Range; + Candidates.NoteCandidates(S, OCD_AllCandidates, Args); + return true; + } + } + llvm_unreachable("Unreachable, bad result from BestViableFunction"); } + /// FindAllocationFunctions - Finds the overloads of operator new and delete /// that are appropriate for the allocation. bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, bool UseGlobal, QualType AllocType, - bool IsArray, MultiExprArg PlaceArgs, + bool IsArray, bool &PassAlignment, + MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete) { // --- Choosing an allocation function --- @@ -1960,16 +2156,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // 3) The first argument is always size_t. Append the arguments from the // placement form. - SmallVector<Expr*, 8> AllocArgs(1 + PlaceArgs.size()); - // We don't care about the actual value of this argument. + SmallVector<Expr*, 8> AllocArgs; + AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size()); + + // We don't care about the actual value of these arguments. // FIXME: Should the Sema create the expression and embed it in the syntax // tree? Or should the consumer just recalculate the value? + // FIXME: Using a dummy value will interact poorly with attribute enable_if. IntegerLiteral Size(Context, llvm::APInt::getNullValue( Context.getTargetInfo().getPointerWidth(0)), Context.getSizeType(), SourceLocation()); - AllocArgs[0] = &Size; - std::copy(PlaceArgs.begin(), PlaceArgs.end(), AllocArgs.begin() + 1); + AllocArgs.push_back(&Size); + + QualType AlignValT = Context.VoidTy; + if (PassAlignment) { + DeclareGlobalNewDelete(); + AlignValT = Context.getTypeDeclType(getStdAlignValT()); + } + CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation()); + if (PassAlignment) + AllocArgs.push_back(&Align); + + AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end()); // C++ [expr.new]p8: // If the allocated type is a non-array type, the allocation @@ -1978,50 +2187,57 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // type, the allocation function's name is operator new[] and the // deallocation function's name is operator delete[]. DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName( - IsArray ? OO_Array_New : OO_New); - DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( - IsArray ? OO_Array_Delete : OO_Delete); + IsArray ? OO_Array_New : OO_New); QualType AllocElemType = Context.getBaseElementType(AllocType); - if (AllocElemType->isRecordType() && !UseGlobal) { - CXXRecordDecl *Record - = cast<CXXRecordDecl>(AllocElemType->getAs<RecordType>()->getDecl()); - if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, Record, - /*AllowMissing=*/true, OperatorNew)) + // Find the allocation function. + { + LookupResult R(*this, NewName, StartLoc, LookupOrdinaryName); + + // C++1z [expr.new]p9: + // If the new-expression begins with a unary :: operator, the allocation + // function's name is looked up in the global scope. Otherwise, if the + // allocated type is a class type T or array thereof, the allocation + // function's name is looked up in the scope of T. + if (AllocElemType->isRecordType() && !UseGlobal) + LookupQualifiedName(R, AllocElemType->getAsCXXRecordDecl()); + + // We can see ambiguity here if the allocation function is found in + // multiple base classes. + if (R.isAmbiguous()) return true; - } - if (!OperatorNew) { - // Didn't find a member overload. Look for a global one. - DeclareGlobalNewDelete(); - DeclContext *TUDecl = Context.getTranslationUnitDecl(); - bool FallbackEnabled = IsArray && Context.getLangOpts().MSVCCompat; - if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl, - /*AllowMissing=*/FallbackEnabled, OperatorNew, - /*Diagnose=*/!FallbackEnabled)) { - if (!FallbackEnabled) - return true; + // If this lookup fails to find the name, or if the allocated type is not + // a class type, the allocation function's name is looked up in the + // global scope. + if (R.empty()) + LookupQualifiedName(R, Context.getTranslationUnitDecl()); + + assert(!R.empty() && "implicitly declared allocation functions not found"); + assert(!R.isAmbiguous() && "global allocation functions are ambiguous"); - // MSVC will fall back on trying to find a matching global operator new - // if operator new[] cannot be found. Also, MSVC will leak by not - // generating a call to operator delete or operator delete[], but we - // will not replicate that bug. - NewName = Context.DeclarationNames.getCXXOperatorName(OO_New); - DeleteName = Context.DeclarationNames.getCXXOperatorName(OO_Delete); - if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl, - /*AllowMissing=*/false, OperatorNew)) + // We do our own custom access checks below. + R.suppressDiagnostics(); + + if (resolveAllocationOverload(*this, R, Range, AllocArgs, PassAlignment, + OperatorNew)) return true; - } } - // We don't need an operator delete if we're running under - // -fno-exceptions. + // We don't need an operator delete if we're running under -fno-exceptions. if (!getLangOpts().Exceptions) { OperatorDelete = nullptr; return false; } + // Note, the name of OperatorNew might have been changed from array to + // non-array by resolveAllocationOverload. + DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( + OperatorNew->getDeclName().getCXXOverloadedOperator() == OO_Array_New + ? OO_Array_Delete + : OO_Delete); + // C++ [expr.new]p19: // // If the new-expression begins with a unary :: operator, the @@ -2040,6 +2256,7 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (FoundDelete.isAmbiguous()) return true; // FIXME: clean up expressions? + bool FoundGlobalDelete = FoundDelete.empty(); if (FoundDelete.empty()) { DeclareGlobalNewDelete(); LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl()); @@ -2054,7 +2271,16 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // we had explicit placement arguments. This matters for things like // struct A { void *operator new(size_t, int = 0); ... }; // A *a = new A() - bool isPlacementNew = (!PlaceArgs.empty() || OperatorNew->param_size() != 1); + // + // We don't have any definition for what a "placement allocation function" + // is, but we assume it's any allocation function whose + // parameter-declaration-clause is anything other than (size_t). + // + // FIXME: Should (size_t, std::align_val_t) also be considered non-placement? + // This affects whether an exception from the constructor of an overaligned + // type uses the sized or non-sized form of aligned operator delete. + bool isPlacementNew = !PlaceArgs.empty() || OperatorNew->param_size() != 1 || + OperatorNew->isVariadic(); if (isPlacementNew) { // C++ [expr.new]p20: @@ -2080,7 +2306,9 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, ArgTypes.push_back(Proto->getParamType(I)); FunctionProtoType::ExtProtoInfo EPI; + // FIXME: This is not part of the standard's rule. EPI.Variadic = Proto->isVariadic(); + EPI.ExceptionSpec.Type = EST_BasicNoexcept; ExpectedFunctionType = Context.getFunctionType(Context.VoidTy, ArgTypes, EPI); @@ -2104,35 +2332,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Context.hasSameType(Fn->getType(), ExpectedFunctionType)) Matches.push_back(std::make_pair(D.getPair(), Fn)); } - } else { - // C++ [expr.new]p20: - // [...] Any non-placement deallocation function matches a - // non-placement allocation function. [...] - for (LookupResult::iterator D = FoundDelete.begin(), - DEnd = FoundDelete.end(); - D != DEnd; ++D) { - if (FunctionDecl *Fn = dyn_cast<FunctionDecl>((*D)->getUnderlyingDecl())) - if (isNonPlacementDeallocationFunction(*this, Fn)) - Matches.push_back(std::make_pair(D.getPair(), Fn)); - } + if (getLangOpts().CUDA) + EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); + } else { // C++1y [expr.new]p22: // For a non-placement allocation function, the normal deallocation // function lookup is used - // C++1y [expr.delete]p?: - // If [...] deallocation function lookup finds both a usual deallocation - // function with only a pointer parameter and a usual deallocation - // function with both a pointer parameter and a size parameter, then the - // selected deallocation function shall be the one with two parameters. - // Otherwise, the selected deallocation function shall be the function - // with one parameter. - if (getLangOpts().SizedDeallocation && Matches.size() == 2) { - if (Matches[0].second->getNumParams() == 1) - Matches.erase(Matches.begin()); - else - Matches.erase(Matches.begin() + 1); - assert(Matches[0].second->getNumParams() == 2 && - "found an unexpected usual deallocation function"); + // + // Per [expr.delete]p10, this lookup prefers a member operator delete + // without a size_t argument, but prefers a non-member operator delete + // with a size_t where possible (which it always is in this case). + llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns; + UsualDeallocFnInfo Selected = resolveDeallocationOverload( + *this, FoundDelete, /*WantSize*/ FoundGlobalDelete, + /*WantAlign*/ hasNewExtendedAlignment(*this, AllocElemType), + &BestDeallocFns); + if (Selected) + Matches.push_back(std::make_pair(Selected.Found, Selected.FD)); + else { + // If we failed to select an operator, all remaining functions are viable + // but ambiguous. + for (auto Fn : BestDeallocFns) + Matches.push_back(std::make_pair(Fn.Found, Fn.FD)); } } @@ -2143,130 +2365,58 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Matches.size() == 1) { OperatorDelete = Matches[0].second; - // C++0x [expr.new]p20: - // If the lookup finds the two-parameter form of a usual - // deallocation function (3.7.4.2) and that function, considered + // C++1z [expr.new]p23: + // If the lookup finds a usual deallocation function (3.7.4.2) + // with a parameter of type std::size_t and that function, considered // as a placement deallocation function, would have been // selected as a match for the allocation function, the program // is ill-formed. - if (!PlaceArgs.empty() && getLangOpts().CPlusPlus11 && + if (getLangOpts().CPlusPlus11 && isPlacementNew && isNonPlacementDeallocationFunction(*this, OperatorDelete)) { - Diag(StartLoc, diag::err_placement_new_non_placement_delete) - << SourceRange(PlaceArgs.front()->getLocStart(), - PlaceArgs.back()->getLocEnd()); - if (!OperatorDelete->isImplicit()) - Diag(OperatorDelete->getLocation(), diag::note_previous_decl) - << DeleteName; - } else { - CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(), - Matches[0].first); - } - } - - return false; -} - -/// \brief Find an fitting overload for the allocation function -/// in the specified scope. -/// -/// \param StartLoc The location of the 'new' token. -/// \param Range The range of the placement arguments. -/// \param Name The name of the function ('operator new' or 'operator new[]'). -/// \param Args The placement arguments specified. -/// \param Ctx The scope in which we should search; either a class scope or the -/// translation unit. -/// \param AllowMissing If \c true, report an error if we can't find any -/// allocation functions. Otherwise, succeed but don't fill in \p -/// Operator. -/// \param Operator Filled in with the found allocation function. Unchanged if -/// no allocation function was found. -/// \param Diagnose If \c true, issue errors if the allocation function is not -/// usable. -bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, - DeclarationName Name, MultiExprArg Args, - DeclContext *Ctx, - bool AllowMissing, FunctionDecl *&Operator, - bool Diagnose) { - LookupResult R(*this, Name, StartLoc, LookupOrdinaryName); - LookupQualifiedName(R, Ctx); - if (R.empty()) { - if (AllowMissing || !Diagnose) - return false; - return Diag(StartLoc, diag::err_ovl_no_viable_function_in_call) - << Name << Range; - } - - if (R.isAmbiguous()) - return true; - - R.suppressDiagnostics(); - - OverloadCandidateSet Candidates(StartLoc, OverloadCandidateSet::CSK_Normal); - for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end(); - Alloc != AllocEnd; ++Alloc) { - // Even member operator new/delete are implicitly treated as - // static, so don't use AddMemberCandidate. - NamedDecl *D = (*Alloc)->getUnderlyingDecl(); + UsualDeallocFnInfo Info(DeclAccessPair::make(OperatorDelete, AS_public)); + // Core issue, per mail to core reflector, 2016-10-09: + // If this is a member operator delete, and there is a corresponding + // non-sized member operator delete, this isn't /really/ a sized + // deallocation function, it just happens to have a size_t parameter. + bool IsSizedDelete = Info.HasSizeT; + if (IsSizedDelete && !FoundGlobalDelete) { + auto NonSizedDelete = + resolveDeallocationOverload(*this, FoundDelete, /*WantSize*/false, + /*WantAlign*/Info.HasAlignValT); + if (NonSizedDelete && !NonSizedDelete.HasSizeT && + NonSizedDelete.HasAlignValT == Info.HasAlignValT) + IsSizedDelete = false; + } - if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { - AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), - /*ExplicitTemplateArgs=*/nullptr, - Args, Candidates, - /*SuppressUserConversions=*/false); - continue; + if (IsSizedDelete) { + SourceRange R = PlaceArgs.empty() + ? SourceRange() + : SourceRange(PlaceArgs.front()->getLocStart(), + PlaceArgs.back()->getLocEnd()); + Diag(StartLoc, diag::err_placement_new_non_placement_delete) << R; + if (!OperatorDelete->isImplicit()) + Diag(OperatorDelete->getLocation(), diag::note_previous_decl) + << DeleteName; + } } - FunctionDecl *Fn = cast<FunctionDecl>(D); - AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates, - /*SuppressUserConversions=*/false); - } - - // Do the resolution. - OverloadCandidateSet::iterator Best; - switch (Candidates.BestViableFunction(*this, StartLoc, Best)) { - case OR_Success: { - // Got one! - FunctionDecl *FnDecl = Best->Function; - if (CheckAllocationAccess(StartLoc, Range, R.getNamingClass(), - Best->FoundDecl, Diagnose) == AR_inaccessible) - return true; + CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(), + Matches[0].first); + } else if (!Matches.empty()) { + // We found multiple suitable operators. Per [expr.new]p20, that means we + // call no 'operator delete' function, but we should at least warn the user. + // FIXME: Suppress this warning if the construction cannot throw. + Diag(StartLoc, diag::warn_ambiguous_suitable_delete_function_found) + << DeleteName << AllocElemType; - Operator = FnDecl; - return false; + for (auto &Match : Matches) + Diag(Match.second->getLocation(), + diag::note_member_declared_here) << DeleteName; } - case OR_No_Viable_Function: - if (Diagnose) { - Diag(StartLoc, diag::err_ovl_no_viable_function_in_call) - << Name << Range; - Candidates.NoteCandidates(*this, OCD_AllCandidates, Args); - } - return true; - - case OR_Ambiguous: - if (Diagnose) { - Diag(StartLoc, diag::err_ovl_ambiguous_call) - << Name << Range; - Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args); - } - return true; - - case OR_Deleted: { - if (Diagnose) { - Diag(StartLoc, diag::err_ovl_deleted_call) - << Best->Function->isDeleted() - << Name - << getDeletedOrUnavailableSuffix(Best->Function) - << Range; - Candidates.NoteCandidates(*this, OCD_AllCandidates, Args); - } - return true; - } - } - llvm_unreachable("Unreachable, bad result from BestViableFunction"); + return false; } - /// DeclareGlobalNewDelete - Declare the global forms of operator new and /// delete. These are: /// @code @@ -2460,52 +2610,43 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, bool CanProvideSize, + bool Overaligned, DeclarationName Name) { DeclareGlobalNewDelete(); LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName); LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl()); - // C++ [expr.new]p20: - // [...] Any non-placement deallocation function matches a - // non-placement allocation function. [...] - llvm::SmallVector<FunctionDecl*, 2> Matches; - for (LookupResult::iterator D = FoundDelete.begin(), - DEnd = FoundDelete.end(); - D != DEnd; ++D) { - if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*D)) - if (isNonPlacementDeallocationFunction(*this, Fn)) - Matches.push_back(Fn); - } - - // C++1y [expr.delete]p?: - // If the type is complete and deallocation function lookup finds both a - // usual deallocation function with only a pointer parameter and a usual - // deallocation function with both a pointer parameter and a size - // parameter, then the selected deallocation function shall be the one - // with two parameters. Otherwise, the selected deallocation function - // shall be the function with one parameter. - if (getLangOpts().SizedDeallocation && Matches.size() == 2) { - unsigned NumArgs = CanProvideSize ? 2 : 1; - if (Matches[0]->getNumParams() != NumArgs) - Matches.erase(Matches.begin()); - else - Matches.erase(Matches.begin() + 1); - assert(Matches[0]->getNumParams() == NumArgs && - "found an unexpected usual deallocation function"); - } + // FIXME: It's possible for this to result in ambiguity, through a + // user-declared variadic operator delete or the enable_if attribute. We + // should probably not consider those cases to be usual deallocation + // functions. But for now we just make an arbitrary choice in that case. + auto Result = resolveDeallocationOverload(*this, FoundDelete, CanProvideSize, + Overaligned); + assert(Result.FD && "operator delete missing from global scope?"); + return Result.FD; +} - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); +FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc, + CXXRecordDecl *RD) { + DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); - assert(Matches.size() == 1 && - "unexpectedly have multiple usual deallocation functions"); - return Matches.front(); + FunctionDecl *OperatorDelete = nullptr; + if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete)) + return nullptr; + if (OperatorDelete) + return OperatorDelete; + + // If there's no class-specific operator delete, look up the global + // non-array delete. + return FindUsualDeallocationFunction( + Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)), + Name); } bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, - FunctionDecl* &Operator, bool Diagnose) { + FunctionDecl *&Operator, bool Diagnose) { LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName); // Try to find operator delete/operator delete[] in class scope. LookupQualifiedName(Found, RD); @@ -2515,27 +2656,20 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Found.suppressDiagnostics(); - SmallVector<DeclAccessPair,4> Matches; - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) { - NamedDecl *ND = (*F)->getUnderlyingDecl(); + bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD)); - // Ignore template operator delete members from the check for a usual - // deallocation function. - if (isa<FunctionTemplateDecl>(ND)) - continue; - - if (cast<CXXMethodDecl>(ND)->isUsualDeallocationFunction()) - Matches.push_back(F.getPair()); - } + // C++17 [expr.delete]p10: + // If the deallocation functions have class scope, the one without a + // parameter of type std::size_t is selected. + llvm::SmallVector<UsualDeallocFnInfo, 4> Matches; + resolveDeallocationOverload(*this, Found, /*WantSize*/ false, + /*WantAlign*/ Overaligned, &Matches); - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); - - // There's exactly one suitable operator; pick it. + // If we could find an overload, use it. if (Matches.size() == 1) { - Operator = cast<CXXMethodDecl>(Matches[0]->getUnderlyingDecl()); + Operator = cast<CXXMethodDecl>(Matches[0].FD); + // FIXME: DiagnoseUseOfDecl? if (Operator->isDeleted()) { if (Diagnose) { Diag(StartLoc, diag::err_deleted_function_use); @@ -2545,21 +2679,21 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, } if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(), - Matches[0], Diagnose) == AR_inaccessible) + Matches[0].Found, Diagnose) == AR_inaccessible) return true; return false; + } - // We found multiple suitable operators; complain about the ambiguity. - } else if (!Matches.empty()) { + // We found multiple suitable operators; complain about the ambiguity. + // FIXME: The standard doesn't say to do this; it appears that the intent + // is that this should never happen. + if (!Matches.empty()) { if (Diagnose) { Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found) << Name << RD; - - for (SmallVectorImpl<DeclAccessPair>::iterator - F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), - diag::note_member_declared_here) << Name; + for (auto &Match : Matches) + Diag(Match.FD->getLocation(), diag::note_member_declared_here) << Name; } return true; } @@ -2571,9 +2705,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Diag(StartLoc, diag::err_no_suitable_delete_member_function_found) << Name << RD; - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), + for (NamedDecl *D : Found) + Diag(D->getUnderlyingDecl()->getLocation(), diag::note_member_declared_here) << Name; } return true; @@ -2984,7 +3117,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, // Otherwise, the usual operator delete[] should be the // function we just found. else if (OperatorDelete && isa<CXXMethodDecl>(OperatorDelete)) - UsualArrayDeleteWantsSize = (OperatorDelete->getNumParams() == 2); + UsualArrayDeleteWantsSize = + UsualDeallocFnInfo( + DeclAccessPair::make(OperatorDelete, AS_public)) + .HasSizeT; } if (!PointeeRD->hasIrrelevantDestructor()) @@ -3001,13 +3137,17 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, SourceLocation()); } - if (!OperatorDelete) + if (!OperatorDelete) { + bool IsComplete = isCompleteType(StartLoc, Pointee); + bool CanProvideSize = + IsComplete && (!ArrayForm || UsualArrayDeleteWantsSize || + Pointee.isDestructedType()); + bool Overaligned = hasNewExtendedAlignment(*this, Pointee); + // Look for a global declaration. - OperatorDelete = FindUsualDeallocationFunction( - StartLoc, isCompleteType(StartLoc, Pointee) && - (!ArrayForm || UsualArrayDeleteWantsSize || - Pointee.isDestructedType()), - DeleteName); + OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize, + Overaligned, DeleteName); + } MarkFunctionReferenced(StartLoc, OperatorDelete); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 7ca85a907ea..082a6f98ef2 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -10142,16 +10142,17 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, /// PrintOverloadCandidates - When overload resolution fails, prints /// diagnostic messages containing the candidates in the candidate /// set. -void OverloadCandidateSet::NoteCandidates(Sema &S, - OverloadCandidateDisplayKind OCD, - ArrayRef<Expr *> Args, - StringRef Opc, - SourceLocation OpLoc) { +void OverloadCandidateSet::NoteCandidates( + Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args, + StringRef Opc, SourceLocation OpLoc, + llvm::function_ref<bool(OverloadCandidate &)> Filter) { // Sort the candidates by viability and position. Sorting directly would // be prohibitive, so we make a set of pointers and sort those. SmallVector<OverloadCandidate*, 32> Cands; if (OCD == OCD_AllCandidates) Cands.reserve(size()); for (iterator Cand = begin(), LastCand = end(); Cand != LastCand; ++Cand) { + if (!Filter(*Cand)) + continue; if (Cand->Viable) Cands.push_back(Cand); else if (OCD == OCD_AllCandidates) { diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 28d83c7e81f..a87a054f116 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1410,6 +1410,7 @@ void ASTStmtReader::VisitCXXNewExpr(CXXNewExpr *E) { VisitExpr(E); E->GlobalNew = Record[Idx++]; bool isArray = Record[Idx++]; + E->PassAlignment = Record[Idx++]; E->UsualArrayDeleteWantsSize = Record[Idx++]; unsigned NumPlacementArgs = Record[Idx++]; E->StoredInitializationStyle = Record[Idx++]; diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 8e7cb64e50a..ddb69d966a7 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1392,6 +1392,7 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) { VisitExpr(E); Record.push_back(E->isGlobalNew()); Record.push_back(E->isArray()); + Record.push_back(E->passAlignment()); Record.push_back(E->doesUsualArrayDeleteWantSize()); Record.push_back(E->getNumPlacementArgs()); Record.push_back(E->StoredInitializationStyle); diff --git a/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp b/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp new file mode 100644 index 00000000000..9e3210c6650 --- /dev/null +++ b/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -std=c++1z -fsized-deallocation -fexceptions -verify %s + +using size_t = decltype(sizeof(0)); + +namespace std { enum class align_val_t : size_t {}; } + +// p2 says "A template instance is never a usual deallocation function, +// regardless of its signature." We (and every other implementation) assume +// this means "A function template specialization [...]" +template<typename...Ts> struct A { + void *operator new(size_t); + void operator delete(void*, Ts...) = delete; // expected-note 4{{deleted}} +}; + +auto *a1 = new A<>; // expected-error {{deleted}} +auto *a2 = new A<size_t>; // expected-error {{deleted}} +auto *a3 = new A<std::align_val_t>; // expected-error {{deleted}} +auto *a4 = new A<size_t, std::align_val_t>; // expected-error {{deleted}} +auto *a5 = new A<std::align_val_t, size_t>; // ok, not usual diff --git a/test/CXX/expr/expr.unary/expr.delete/p10.cpp b/test/CXX/expr/expr.unary/expr.delete/p10.cpp new file mode 100644 index 00000000000..aad2747dd32 --- /dev/null +++ b/test/CXX/expr/expr.unary/expr.delete/p10.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +using size_t = decltype(sizeof(0)); +namespace std { enum class align_val_t : size_t {}; } + +// Aligned version is preferred over unaligned version, +// unsized version is preferred over sized version. +template<unsigned Align> +struct alignas(Align) A { + void operator delete(void*); + void operator delete(void*, std::align_val_t) = delete; // expected-note {{here}} + + void operator delete(void*, size_t) = delete; + void operator delete(void*, size_t, std::align_val_t) = delete; +}; +void f(A<__STDCPP_DEFAULT_NEW_ALIGNMENT__> *p) { delete p; } +void f(A<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2> *p) { delete p; } // expected-error {{deleted}} + +template<unsigned Align> +struct alignas(Align) B { + void operator delete(void*, size_t); + void operator delete(void*, size_t, std::align_val_t) = delete; // expected-note {{here}} +}; +void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__> *p) { delete p; } +void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2> *p) { delete p; } // expected-error {{deleted}} diff --git a/test/CXX/expr/expr.unary/expr.new/p14.cpp b/test/CXX/expr/expr.unary/expr.new/p14.cpp new file mode 100644 index 00000000000..6537cdcfeaf --- /dev/null +++ b/test/CXX/expr/expr.unary/expr.new/p14.cpp @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -std=c++1z -fsized-deallocation -fexceptions %s -verify + +using size_t = decltype(sizeof(0)); +namespace std { enum class align_val_t : size_t {}; } + +struct Arg {} arg; + +// If the type is aligned, first try with an alignment argument and then +// without. If not, never consider supplying an alignment. + +template<unsigned Align, typename ...Ts> +struct alignas(Align) Unaligned { + void *operator new(size_t, Ts...) = delete; // expected-note 4{{deleted}} +}; +auto *ua = new Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{deleted}} +auto *ub = new Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}} +auto *uap = new (arg) Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{deleted}} +auto *ubp = new (arg) Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}} + +template<unsigned Align, typename ...Ts> +struct alignas(Align) Aligned { + void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 2{{deleted}} expected-note 2{{not viable}} +}; +auto *aa = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{no matching}} +auto *ab = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}} +auto *aap = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{no matching}} +auto *abp = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}} + +// If both are available, we prefer the aligned version for an overaligned +// type, and only use the unaligned version for a non-overaligned type. + +template<unsigned Align, typename ...Ts> +struct alignas(Align) Both1 { + void *operator new(size_t, Ts...); // expected-note 2{{not viable}} + void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 2{{deleted}} +}; +template<unsigned Align, typename ...Ts> +struct alignas(Align) Both2 { + void *operator new(size_t, Ts...) = delete; // expected-note 2{{deleted}} + void *operator new(size_t, std::align_val_t, Ts...); // expected-note 2{{not viable}} +}; +auto *b1a = new Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; +auto *b1b = new Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}} +auto *b2a = new Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{deleted}} +auto *b2b = new Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; +auto *b1ap = new (arg) Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; +auto *b1bp = new (arg) Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}} +auto *b2ap = new (arg) Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{deleted}} +auto *b2bp = new (arg) Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; + +// Note that the aligned form can select a function with a parameter different +// from std::align_val_t. + +struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) WeirdAlignedAlloc1 { + void *operator new(size_t, ...) = delete; // expected-note 2{{deleted}} +}; +auto *waa1 = new WeirdAlignedAlloc1; // expected-error {{deleted}} +auto *waa1p = new (arg) WeirdAlignedAlloc1; // expected-error {{deleted}} + +struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) WeirdAlignedAlloc2 { + template<typename ...T> + void *operator new(size_t, T...) { + using U = void(T...); // expected-note 2{{previous}} + using U = void; // expected-error {{different types ('void' vs 'void (std::align_val_t)')}} \ + expected-error {{different types ('void' vs 'void (std::align_val_t, Arg)')}} + } +}; +auto *waa2 = new WeirdAlignedAlloc2; // expected-note {{instantiation of}} +auto *waa2p = new (arg) WeirdAlignedAlloc2; // expected-note {{instantiation of}} diff --git a/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp b/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp index eca1ec79019..13676a8a07c 100644 --- a/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp +++ b/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp @@ -1,6 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fexceptions %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -fexceptions %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -fexceptions %s typedef __SIZE_TYPE__ size_t; +namespace std { enum class align_val_t : size_t {}; } + struct S { // Placement allocation function: static void* operator new(size_t, size_t); @@ -9,5 +13,56 @@ struct S { }; void testS() { - S* p = new (0) S; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}} + S* p = new (0) S; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}} +} + +struct T { + // Placement allocation function: + static void* operator new(size_t, size_t); + // Usual (non-placement) deallocation function: + static void operator delete(void*); + // Placement deallocation function: + static void operator delete(void*, size_t); +}; + +void testT() { + T* p = new (0) T; // ok +} + +#if __cplusplus > 201402L +struct U { + // Placement allocation function: + static void* operator new(size_t, size_t, std::align_val_t); + // Placement deallocation function: + static void operator delete(void*, size_t, std::align_val_t); // expected-note{{declared here}} +}; + +void testU() { + U* p = new (0, std::align_val_t(0)) U; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}} +} + +struct V { + // Placement allocation function: + static void* operator new(size_t, size_t, std::align_val_t); + // Usual (non-placement) deallocation function: + static void operator delete(void*, std::align_val_t); + // Placement deallocation function: + static void operator delete(void*, size_t, std::align_val_t); +}; + +void testV() { + V* p = new (0, std::align_val_t(0)) V; +} + +struct W { + // Placement allocation function: + static void* operator new(size_t, size_t, std::align_val_t); + // Usual (non-placement) deallocation functions: + static void operator delete(void*); + static void operator delete(void*, size_t, std::align_val_t); // expected-note {{declared here}} +}; + +void testW() { + W* p = new (0, std::align_val_t(0)) W; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}} } +#endif diff --git a/test/CXX/special/class.dtor/p9.cpp b/test/CXX/special/class.dtor/p9.cpp index cfde48b0aab..4c6fbf43437 100644 --- a/test/CXX/special/class.dtor/p9.cpp +++ b/test/CXX/special/class.dtor/p9.cpp @@ -31,13 +31,13 @@ namespace test0 { namespace test1 { class A { public: - static void operator delete(void *p) {}; // expected-note {{member 'operator delete' declared here}} + static void operator delete(void *p) {}; virtual ~A(); }; class B : protected A { public: - static void operator delete(void *, size_t) {}; // expected-note {{member 'operator delete' declared here}} + static void operator delete(void *, size_t) {}; ~B(); }; @@ -49,7 +49,20 @@ namespace test1 { ~C(); }; - C::~C() {} // expected-error {{multiple suitable 'operator delete' functions in 'C'}} + // We assume that the intent is to treat C::operator delete(void*, size_t) as + // /not/ being a usual deallocation function, as it would be if it were + // declared with in C directly. + C::~C() {} + + struct D { + void operator delete(void*); // expected-note {{member 'operator delete' declared here}} + void operator delete(void*, ...); // expected-note {{member 'operator delete' declared here}} + virtual ~D(); + }; + // FIXME: The standard doesn't say this is ill-formed, but presumably either + // it should be or the variadic operator delete should not be a usual + // deallocation function. + D::~D() {} // expected-error {{multiple suitable 'operator delete' functions in 'D'}} } // ...at the point of definition of a virtual destructor... diff --git a/test/CodeGenCXX/cxx1z-aligned-allocation.cpp b/test/CodeGenCXX/cxx1z-aligned-allocation.cpp new file mode 100644 index 00000000000..437597d963b --- /dev/null +++ b/test/CodeGenCXX/cxx1z-aligned-allocation.cpp @@ -0,0 +1,206 @@ +// Check that delete exprs call aligned (de)allocation functions if +// -faligned-allocation is passed in both C++11 and C++14. +// RUN: %clang_cc1 -std=c++11 -fexceptions -fsized-deallocation -faligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++14 -fexceptions -fsized-deallocation -faligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++1z -fexceptions -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s + +// Check that we don't used aligned (de)allocation without -faligned-allocation or C++1z. +// RUN: %clang_cc1 -std=c++14 -DUNALIGNED -fexceptions %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNALIGNED +// RUN: %clang_cc1 -std=c++1z -DUNALIGNED -fexceptions -fno-aligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNALIGNED + +// CHECK-UNALIGNED-NOT: _Znwm_St11align_val_t +// CHECK-UNALIGNED-NOT: _Znam_St11align_val_t +// CHECK-UNALIGNED-NOT: _ZdlPv_St11align_val_t +// CHECK-UNALIGNED-NOT: _ZdaPv_St11align_val_t +// CHECK-UNALIGNED-NOT: _ZdlPvm_St11align_val_t +// CHECK-UNALIGNED-NOT: _ZdaPvm_St11align_val_t + +typedef decltype(sizeof(0)) size_t; +namespace std { enum class align_val_t : size_t {}; } + +#define OVERALIGNED alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) + +// Global new and delete. +// ====================== +struct OVERALIGNED A { A(); int n[128]; }; + +// CHECK-LABEL: define {{.*}} @_Z2a0v() +// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t(i64 512, i64 32) +// CHECK: call void @_ZdlPvSt11align_val_t(i8* %[[ALLOC]], i64 32) +void *a0() { return new A; } + +// CHECK-LABEL: define {{.*}} @_Z2a1l( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZnamSt11align_val_t(i64 %{{.*}}, i64 32) +// No array cookie. +// CHECK-NOT: store +// CHECK: invoke void @_ZN1AC1Ev( +// CHECK: call void @_ZdaPvSt11align_val_t(i8* %[[ALLOC]], i64 32) +void *a1(long n) { return new A[n]; } + +// CHECK-LABEL: define {{.*}} @_Z2a2P1A( +// CHECK: call void @_ZdlPvmSt11align_val_t(i8* %{{.*}}, i64 512, i64 32) #9 +void a2(A *p) { delete p; } + +// CHECK-LABEL: define {{.*}} @_Z2a3P1A( +// CHECK: call void @_ZdaPvSt11align_val_t(i8* %{{.*}}, i64 32) #9 +void a3(A *p) { delete[] p; } + + +// Class-specific usual new and delete. +// ==================================== +struct OVERALIGNED B { + B(); + // These are just a distraction. We should ignore them. + void *operator new(size_t); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + void *operator new(size_t, std::align_val_t); + void operator delete(void*, std::align_val_t); + void operator delete[](void*, std::align_val_t); + + int n[128]; +}; + +// CHECK-LABEL: define {{.*}} @_Z2b0v() +// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1BnwEmSt11align_val_t(i64 512, i64 32) +// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %[[ALLOC]], i64 32) +void *b0() { return new B; } + +// CHECK-LABEL: define {{.*}} @_Z2b1l( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZnamSt11align_val_t(i64 %{{.*}}, i64 32) +// No array cookie. +// CHECK-NOT: store +// CHECK: invoke void @_ZN1BC1Ev( +// CHECK: call void @_ZN1BdaEPvSt11align_val_t(i8* %[[ALLOC]], i64 32) +void *b1(long n) { return new B[n]; } + +// CHECK-LABEL: define {{.*}} @_Z2b2P1B( +// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %{{.*}}, i64 32) +void b2(B *p) { delete p; } + +// CHECK-LABEL: define {{.*}} @_Z2b3P1B( +// CHECK: call void @_ZN1BdaEPvSt11align_val_t(i8* %{{.*}}, i64 32) +void b3(B *p) { delete[] p; } + +struct OVERALIGNED C { + C(); + void *operator new[](size_t, std::align_val_t); + void operator delete[](void*, size_t, std::align_val_t); + + // It doesn't matter that we have an unaligned operator delete[] that doesn't + // want the size. What matters is that the aligned one does. + void operator delete[](void*); +}; + +// This one has an array cookie. +// CHECK-LABEL: define {{.*}} @_Z2b4l( +// CHECK: call {{.*}} @llvm.umul.with.overflow{{.*}}i64 32 +// CHECK: call {{.*}} @llvm.uadd.with.overflow{{.*}}i64 32 +// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1CnaEmSt11align_val_t(i64 %{{.*}}, i64 32) +// CHECK: store +// CHECK: call void @_ZN1CC1Ev( +// +// Note, we're still calling a placement allocation function, and there is no +// matching placement operator delete. =( +// FIXME: This seems broken. +// CHECK-NOT: call void @_ZN1CdaEPvmSt11align_val_t( +#ifndef UNALIGNED +void *b4(long n) { return new C[n]; } +#endif + +// CHECK-LABEL: define {{.*}} @_Z2b5P1C( +// CHECK: mul i64{{.*}} 32 +// CHECK: add i64{{.*}} 32 +// CHECK: call void @_ZN1CdaEPvmSt11align_val_t( +void b5(C *p) { delete[] p; } + + +// Global placement new. +// ===================== + +struct Q { int n; } q; +void *operator new(size_t, Q); +void *operator new(size_t, std::align_val_t, Q); +void operator delete(void*, Q); +void operator delete(void*, std::align_val_t, Q); + +// CHECK-LABEL: define {{.*}} @_Z2c0v( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t1Q(i64 512, i64 32, i32 % +// CHECK: call void @_ZdlPvSt11align_val_t1Q(i8* %[[ALLOC]], i64 32, i32 % +void *c0() { return new (q) A; } + + +// Class-specific placement new. +// ============================= + +struct OVERALIGNED D { + D(); + void *operator new(size_t, Q); + void *operator new(size_t, std::align_val_t, Q); + void operator delete(void*, Q); + void operator delete(void*, std::align_val_t, Q); +}; + +// CHECK-LABEL: define {{.*}} @_Z2d0v( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1DnwEmSt11align_val_t1Q(i64 32, i64 32, i32 % +// CHECK: call void @_ZN1DdlEPvSt11align_val_t1Q(i8* %[[ALLOC]], i64 32, i32 % +void *d0() { return new (q) D; } + + +// Calling aligned new with placement syntax. +// ========================================== + +#ifndef UNALIGNED +// CHECK-LABEL: define {{.*}} @_Z2e0v( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t(i64 512, i64 5) +// CHECK: call void @_ZdlPvSt11align_val_t(i8* %[[ALLOC]], i64 5) +void *e0() { return new (std::align_val_t(5)) A; } + +// CHECK-LABEL: define {{.*}} @_Z2e1v( +// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1BnwEmSt11align_val_t(i64 512, i64 5) +// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %[[ALLOC]], i64 5) +void *e1() { return new (std::align_val_t(5)) B; } +#endif + +// Variadic placement/non-placement allocation functions. +// ====================================================== + +struct OVERALIGNED F { + F(); + void *operator new(size_t, ...); + void operator delete(void*, ...); + int n[128]; +}; + +// CHECK-LABEL: define {{.*}} @_Z2f0v( +// CHECK: %[[ALLOC:.*]] = call i8* (i64, ...) @_ZN1FnwEmz(i64 512, i64 32) +// Non-placement allocation function, uses normal deallocation lookup which +// cares about whether a parameter has type std::align_val_t. +// CHECK: call void (i8*, ...) @_ZN1FdlEPvz(i8* %[[ALLOC]]) +void *f0() { return new F; } + +// CHECK-LABEL: define {{.*}} @_Z2f1v( +// CHECK: %[[ALLOC:.*]] = call i8* (i64, ...) @_ZN1FnwEmz(i64 512, i64 32, i32 % +// Placement allocation function, uses placement deallocation matching, which +// passes same arguments and therefore includes alignment. +// CHECK: call void (i8*, ...) @_ZN1FdlEPvz(i8* %[[ALLOC]], i64 32, i32 % +void *f1() { return new (q) F; } + +struct OVERALIGNED G { + G(); + void *operator new(size_t, std::align_val_t, ...); + void operator delete(void*, std::align_val_t, ...); + int n[128]; +}; +#ifndef UNALIGNED +// CHECK-LABEL: define {{.*}} @_Z2g0v +// CHECK: %[[ALLOC:.*]] = call i8* (i64, i64, ...) @_ZN1GnwEmSt11align_val_tz(i64 512, i64 32) +// CHECK: call void (i8*, i64, ...) @_ZN1GdlEPvSt11align_val_tz(i8* %[[ALLOC]], i64 32) +void *g0() { return new G; } + +// CHECK-LABEL: define {{.*}} @_Z2g1v +// CHECK: %[[ALLOC:.*]] = call i8* (i64, i64, ...) @_ZN1GnwEmSt11align_val_tz(i64 512, i64 32, i32 % +// CHECK: call void (i8*, i64, ...) @_ZN1GdlEPvSt11align_val_tz(i8* %[[ALLOC]], i64 32, i32 % +void *g1() { return new (q) G; } +#endif -- GitLab