Author: rsmith Date: Mon Oct 10 13:54:32 2016 New Revision: 283789 URL: http://llvm.org/viewvc/llvm-project?rev=283789&view=rev Log: Re-commit r283722, reverted in r283750, with a fix for a CUDA-specific use of past-the-end iterator.
Original commit message: P0035R4: Semantic analysis and code generation for C++17 overaligned allocation. Added: cfe/trunk/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp - copied unchanged from r283749, cfe/trunk/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.deallocation/p2.cpp cfe/trunk/test/CXX/expr/expr.unary/expr.delete/p10.cpp - copied unchanged from r283749, cfe/trunk/test/CXX/expr/expr.unary/expr.delete/p10.cpp cfe/trunk/test/CXX/expr/expr.unary/expr.new/p14.cpp - copied unchanged from r283749, cfe/trunk/test/CXX/expr/expr.unary/expr.new/p14.cpp cfe/trunk/test/CodeGenCXX/cxx1z-aligned-allocation.cpp - copied unchanged from r283749, cfe/trunk/test/CodeGenCXX/cxx1z-aligned-allocation.cpp Modified: cfe/trunk/include/clang/AST/ASTContext.h cfe/trunk/include/clang/AST/ExprCXX.h cfe/trunk/include/clang/AST/Type.h cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/Overload.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/AST/ASTContext.cpp cfe/trunk/lib/AST/ASTImporter.cpp cfe/trunk/lib/AST/Decl.cpp cfe/trunk/lib/AST/DeclCXX.cpp cfe/trunk/lib/AST/ExprCXX.cpp cfe/trunk/lib/AST/Type.cpp cfe/trunk/lib/CodeGen/CGExprCXX.cpp cfe/trunk/lib/CodeGen/CodeGenFunction.h cfe/trunk/lib/Sema/SemaCUDA.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/lib/Sema/SemaExprCXX.cpp cfe/trunk/lib/Sema/SemaOverload.cpp cfe/trunk/lib/Serialization/ASTReaderStmt.cpp cfe/trunk/lib/Serialization/ASTWriterStmt.cpp cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp cfe/trunk/test/CXX/special/class.dtor/p9.cpp Modified: cfe/trunk/include/clang/AST/ASTContext.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/ASTContext.h (original) +++ cfe/trunk/include/clang/AST/ASTContext.h Mon Oct 10 13:54:32 2016 @@ -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; Modified: cfe/trunk/include/clang/AST/ExprCXX.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/ExprCXX.h (original) +++ cfe/trunk/include/clang/AST/ExprCXX.h Mon Oct 10 13:54:32 2016 @@ -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. Modified: cfe/trunk/include/clang/AST/Type.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Type.h (original) +++ cfe/trunk/include/clang/AST/Type.h Mon Oct 10 13:54:32 2016 @@ -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) \ Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Oct 10 13:54:32 2016 @@ -6025,6 +6025,10 @@ def err_no_suitable_delete_member_functi "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">; Modified: cfe/trunk/include/clang/Sema/Overload.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Overload.h (original) +++ cfe/trunk/include/clang/Sema/Overload.h Mon Oct 10 13:54:32 2016 @@ -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, Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Mon Oct 10 13:54:32 2016 @@ -4856,14 +4856,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); @@ -4873,7 +4868,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, @@ -9338,6 +9336,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. Modified: cfe/trunk/lib/AST/ASTContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp (original) +++ cfe/trunk/lib/AST/ASTContext.cpp Mon Oct 10 13:54:32 2016 @@ -1570,6 +1570,30 @@ bool ASTContext::isAlignmentRequired(Qua 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()) Modified: cfe/trunk/lib/AST/ASTImporter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTImporter.cpp (original) +++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Oct 10 13:54:32 2016 @@ -6330,6 +6330,7 @@ Expr *ASTNodeImporter::VisitCXXNewExpr(C Importer.getToContext(), CE->isGlobalNew(), OperatorNewDecl, OperatorDeleteDecl, + CE->passAlignment(), CE->doesUsualArrayDeleteWantSize(), PlacementArgs, Importer.Import(CE->getTypeIdParens()), Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Mon Oct 10 13:54:32 2016 @@ -2596,7 +2596,7 @@ bool FunctionDecl::isReplaceableGlobalAl 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::isReplaceableGlobalAl 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 { Modified: cfe/trunk/lib/AST/DeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/DeclCXX.cpp (original) +++ cfe/trunk/lib/AST/DeclCXX.cpp Mon Oct 10 13:54:32 2016 @@ -1577,17 +1577,35 @@ bool CXXMethodDecl::isUsualDeallocationF // 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. Modified: cfe/trunk/lib/AST/ExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprCXX.cpp (original) +++ cfe/trunk/lib/AST/ExprCXX.cpp Mon Oct 10 13:54:32 2016 @@ -62,7 +62,7 @@ SourceLocation CXXScalarValueInitExpr::g // 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 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; Modified: cfe/trunk/lib/AST/Type.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Type.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/AST/Type.cpp (original) +++ cfe/trunk/lib/AST/Type.cpp Mon Oct 10 13:54:32 2016 @@ -2337,6 +2337,15 @@ bool QualType::isCXX11PODType(const ASTC 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()) { Modified: cfe/trunk/lib/CodeGen/CGExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprCXX.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGExprCXX.cpp (original) +++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp Mon Oct 10 13:54:32 2016 @@ -1219,111 +1219,116 @@ RValue CodeGenFunction::EmitBuiltinNewDe 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); } - - 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) {} - - 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)); + // The first argument is always a void*. + ++AI; - CallArgList DeleteArgs; + // Figure out what other parameters we should be implicitly passing. + bool PassSize = false; + bool PassAlignment = false; - // The first argument is always a void*. - FunctionProtoType::param_type_iterator AI = FPT->param_type_begin(); - DeleteArgs.add(RValue::get(Ptr), *AI++); + if (AI != AE && (*AI)->isIntegerType()) { + PassSize = true; + ++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)->isAlignValT()) { + PassAlignment = true; + ++AI; + } - // Pass the rest of the arguments, which must match exactly. - for (unsigned I = 0; I != NumPlacementArgs; ++I) - DeleteArgs.add(getPlacementArgs()[I], *AI++); + assert(AI == AE && "unexpected usual deallocation function parameter"); + return {PassSize, PassAlignment}; +} - // Call 'operator delete'. - EmitNewDeleteCall(CGF, OperatorDelete, FPT, DeleteArgs); - } - }; +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; + }; - /// 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; + 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) {} + 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, DominatingValue<RValue>::saved_type Arg) { + 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++); + DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0)); - // 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++); + // 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(CodeGe 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(CodeGe 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::EmitCXXNew 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::EmitCXXNew // 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::EmitCXXNew } 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; - // We start at 1 here because the first argument (the allocation size) - // has already been emitted. + 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); + } + + // 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::EmitCXXNew 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::EmitCXXNew } 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); + + auto ParamTypeIt = DeleteFTy->param_type_begin(); - QualType ArgTy = DeleteFTy->getParamType(0); + // 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); } }; } Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original) +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Oct 10 13:54:32 2016 @@ -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); Modified: cfe/trunk/lib/Sema/SemaCUDA.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCUDA.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaCUDA.cpp (original) +++ cfe/trunk/lib/Sema/SemaCUDA.cpp Mon Oct 10 13:54:32 2016 @@ -158,6 +158,34 @@ Sema::IdentifyCUDAPreference(const Funct llvm_unreachable("All cases should've been handled by now."); } +void Sema::EraseUnwantedCUDAMatches(const FunctionDecl *Caller, + LookupResult &R) { + if (R.empty() || 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, Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Oct 10 13:54:32 2016 @@ -14368,6 +14368,14 @@ void Sema::ActOnFields(Scope *S, SourceL 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); Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon Oct 10 13:54:32 2016 @@ -6755,7 +6755,7 @@ bool Sema::ShouldDeleteSpecialMember(CXX 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(CXXDestructor 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; @@ -10282,7 +10274,11 @@ CXXDestructorDecl *Sema::DeclareImplicit 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. Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Mon Oct 10 13:54:32 2016 @@ -1321,8 +1321,126 @@ Sema::BuildCXXTypeConstructExpr(TypeSour 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 // 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, boo 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, boo // 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, boo 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, boo } 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 A 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(Sourc // 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(Sourc // 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; - - // 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)) + // 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"); + + // 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(Sourc 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(Sourc // 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(Sourc 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(Sourc 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(Sourc 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; - - Operator = FnDecl; - return false; + 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; + + 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::DeclareGlobalAllocationFuncti 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(Sour Found.suppressDiagnostics(); - SmallVector<DeclAccessPair,4> Matches; - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) { - NamedDecl *ND = (*F)->getUnderlyingDecl(); - - // 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()); - } + bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD)); - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); + // 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); - // 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(Sour } 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(Sour 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 Star // 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 Star 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); Modified: cfe/trunk/lib/Sema/SemaOverload.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaOverload.cpp (original) +++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Oct 10 13:54:32 2016 @@ -10142,16 +10142,17 @@ static void CompleteNonViableCandidate(S /// 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) { Modified: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderStmt.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp (original) +++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp Mon Oct 10 13:54:32 2016 @@ -1410,6 +1410,7 @@ void ASTStmtReader::VisitCXXNewExpr(CXXN 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++]; Modified: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterStmt.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp (original) +++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp Mon Oct 10 13:54:32 2016 @@ -1392,6 +1392,7 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXN 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); Modified: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp (original) +++ cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp Mon Oct 10 13:54:32 2016 @@ -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 Modified: cfe/trunk/test/CXX/special/class.dtor/p9.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/special/class.dtor/p9.cpp?rev=283789&r1=283788&r2=283789&view=diff ============================================================================== --- cfe/trunk/test/CXX/special/class.dtor/p9.cpp (original) +++ cfe/trunk/test/CXX/special/class.dtor/p9.cpp Mon Oct 10 13:54:32 2016 @@ -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... _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits