From: Richard Smith Date: Thu, 29 Sep 2016 22:49:46 +0000 (+0000) Subject: P0035R4: add std::align_val_t overloads of operator new/delete in C++17 mode. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bec0fbe74a1a3757ec2693605175a0019c6509b3;p=clang P0035R4: add std::align_val_t overloads of operator new/delete in C++17 mode. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@282800 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8459a39244..a95a97712f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -721,6 +721,10 @@ public: /// standard library. LazyDeclPtr StdBadAlloc; + /// \brief The C++ "std::align_val_t" enum class, which is defined by the C++ + /// standard library. + LazyDeclPtr StdAlignValT; + /// \brief The C++ "std::initializer_list" template, which is defined in /// \. ClassTemplateDecl *StdInitializerList; @@ -4237,6 +4241,7 @@ public: NamespaceDecl *getOrCreateStdNamespace(); CXXRecordDecl *getStdBadAlloc() const; + EnumDecl *getStdAlignValT() const; /// \brief Tests whether Ty is an instance of std::initializer_list and, if /// it is and Element is not NULL, assigns the element type to Element. @@ -4838,8 +4843,7 @@ public: bool Diagnose = true); void DeclareGlobalNewDelete(); void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return, - QualType Param1, - QualType Param2 = QualType()); + ArrayRef Params); bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, FunctionDecl* &Operator, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5256eda24b..d7472fcc35 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1577,7 +1577,7 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const { // deallocation function. [...] if (getNumParams() == 1) return true; - + // C++ [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 diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index cfb1f99b07..cf3ea30bd2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -12535,6 +12535,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, DeclContext *SearchDC = CurContext; DeclContext *DC = CurContext; bool isStdBadAlloc = false; + bool isStdAlignValT = false; RedeclarationKind Redecl = ForRedeclaration; if (TUK == TUK_Friend || TUK == TUK_Reference) @@ -12689,15 +12690,20 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } if (getLangOpts().CPlusPlus && Name && DC && StdNamespace && - DC->Equals(getStdNamespace()) && Name->isStr("bad_alloc")) { - // This is a declaration of or a reference to "std::bad_alloc". - isStdBadAlloc = true; + DC->Equals(getStdNamespace())) { + if (Name->isStr("bad_alloc")) { + // This is a declaration of or a reference to "std::bad_alloc". + isStdBadAlloc = true; - if (Previous.empty() && StdBadAlloc) { - // std::bad_alloc has been implicitly declared (but made invisible to - // name lookup). Fill in this implicit declaration as the previous + // If std::bad_alloc has been implicitly declared (but made invisible to + // name lookup), fill in this implicit declaration as the previous // declaration, so that the declarations get chained appropriately. - Previous.addDecl(getStdBadAlloc()); + if (Previous.empty() && StdBadAlloc) + Previous.addDecl(getStdBadAlloc()); + } else if (Name->isStr("align_val_t")) { + isStdAlignValT = true; + if (Previous.empty() && StdAlignValT) + Previous.addDecl(getStdAlignValT()); } } @@ -13089,6 +13095,10 @@ CreateNewDecl: New = EnumDecl::Create(Context, SearchDC, KWLoc, Loc, Name, cast_or_null(PrevDecl), ScopedEnum, ScopedEnumUsesClassTag, !EnumUnderlying.isNull()); + + if (isStdAlignValT && (!StdAlignValT || getStdAlignValT()->isImplicit())) + StdAlignValT = cast(New); + // If this is an undefined enum, warn. if (TUK != TUK_Definition && !Invalid) { TagDecl *Def; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index eb8c3d2cc8..6c952e0182 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -8266,6 +8266,10 @@ CXXRecordDecl *Sema::getStdBadAlloc() const { StdBadAlloc.get(Context.getExternalSource())); } +EnumDecl *Sema::getStdAlignValT() const { + return cast_or_null(StdAlignValT.get(Context.getExternalSource())); +} + NamespaceDecl *Sema::getStdNamespace() const { return cast_or_null( StdNamespace.get(Context.getExternalSource())); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 42888badd2..237bdbf89c 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2340,41 +2340,61 @@ void Sema::DeclareGlobalNewDelete() { nullptr); getStdBadAlloc()->setImplicit(true); } + if (!StdAlignValT && getLangOpts().CPlusPlus1z) { + // The "std::align_val_t" enum class has not yet been declared, so build it + // implicitly. + auto *AlignValT = EnumDecl::Create( + Context, getOrCreateStdNamespace(), SourceLocation(), SourceLocation(), + &PP.getIdentifierTable().get("align_val_t"), nullptr, true, true, true); + AlignValT->setIntegerType(Context.getSizeType()); + AlignValT->setPromotionType(Context.getSizeType()); + AlignValT->setImplicit(true); + StdAlignValT = AlignValT; + } GlobalNewDeleteDeclared = true; QualType VoidPtr = Context.getPointerType(Context.VoidTy); QualType SizeT = Context.getSizeType(); - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_New), - VoidPtr, SizeT, QualType()); - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_Array_New), - VoidPtr, SizeT, QualType()); - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_Delete), - Context.VoidTy, VoidPtr); - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete), - Context.VoidTy, VoidPtr); - if (getLangOpts().SizedDeallocation) { - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_Delete), - Context.VoidTy, VoidPtr, Context.getSizeType()); - DeclareGlobalAllocationFunction( - Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete), - Context.VoidTy, VoidPtr, Context.getSizeType()); - } + auto DeclareGlobalAllocationFunctions = [&](OverloadedOperatorKind Kind, + QualType Return, QualType Param) { + llvm::SmallVector Params; + Params.push_back(Param); + + // Create up to four variants of the function (sized/aligned). + bool HasSizedVariant = getLangOpts().SizedDeallocation && + (Kind == OO_Delete || Kind == OO_Array_Delete); + bool HasAlignedVariant = getLangOpts().CPlusPlus1z; + for (int Sized = 0; Sized <= HasSizedVariant; ++Sized) { + if (Sized) + Params.push_back(SizeT); + + for (int Aligned = 0; Aligned <= HasAlignedVariant; ++Aligned) { + if (Aligned) + Params.push_back(Context.getTypeDeclType(getStdAlignValT())); + + DeclareGlobalAllocationFunction( + Context.DeclarationNames.getCXXOperatorName(Kind), Return, Params); + + if (Aligned) + Params.pop_back(); + } + } + }; + + DeclareGlobalAllocationFunctions(OO_New, VoidPtr, SizeT); + DeclareGlobalAllocationFunctions(OO_Array_New, VoidPtr, SizeT); + DeclareGlobalAllocationFunctions(OO_Delete, Context.VoidTy, VoidPtr); + DeclareGlobalAllocationFunctions(OO_Array_Delete, Context.VoidTy, VoidPtr); } /// DeclareGlobalAllocationFunction - Declares a single implicit global /// allocation function if it doesn't already exist. void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return, - QualType Param1, QualType Param2) { + ArrayRef Params) { DeclContext *GlobalCtx = Context.getTranslationUnitDecl(); - unsigned NumParams = Param2.isNull() ? 1 : 2; // Check if this function is already declared. DeclContext::lookup_result R = GlobalCtx->lookup(Name); @@ -2383,18 +2403,12 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, // Only look at non-template functions, as it is the predefined, // non-templated allocation function we are trying to declare here. if (FunctionDecl *Func = dyn_cast(*Alloc)) { - if (Func->getNumParams() == NumParams) { - QualType InitialParam1Type = - Context.getCanonicalType(Func->getParamDecl(0) - ->getType().getUnqualifiedType()); - QualType InitialParam2Type = - NumParams == 2 - ? Context.getCanonicalType(Func->getParamDecl(1) - ->getType().getUnqualifiedType()) - : QualType(); - // FIXME: Do we need to check for default arguments here? - if (InitialParam1Type == Param1 && - (NumParams == 1 || InitialParam2Type == Param2)) { + if (Func->getNumParams() == Params.size()) { + llvm::SmallVector FuncParams; + for (auto *P : Func->parameters()) + FuncParams.push_back( + Context.getCanonicalType(P->getType().getUnqualifiedType())); + if (llvm::makeArrayRef(FuncParams) == Params) { // Make the function visible to name lookup, even if we found it in // an unimported module. It either is an implicitly-declared global // allocation function, or is suppressing that function. @@ -2423,10 +2437,7 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, getLangOpts().CPlusPlus11 ? EST_BasicNoexcept : EST_DynamicNone; } - QualType Params[] = { Param1, Param2 }; - - QualType FnType = Context.getFunctionType( - Return, llvm::makeArrayRef(Params, NumParams), EPI); + QualType FnType = Context.getFunctionType(Return, Params, EPI); FunctionDecl *Alloc = FunctionDecl::Create(Context, GlobalCtx, SourceLocation(), SourceLocation(), Name, @@ -2437,15 +2448,14 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, Alloc->addAttr(VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default)); - ParmVarDecl *ParamDecls[2]; - for (unsigned I = 0; I != NumParams; ++I) { - ParamDecls[I] = ParmVarDecl::Create(Context, Alloc, SourceLocation(), - SourceLocation(), nullptr, - Params[I], /*TInfo=*/nullptr, - SC_None, nullptr); - ParamDecls[I]->setImplicit(); + llvm::SmallVector ParamDecls; + for (QualType T : Params) { + ParamDecls.push_back( + ParmVarDecl::Create(Context, Alloc, SourceLocation(), SourceLocation(), + nullptr, T, /*TInfo=*/nullptr, SC_None, nullptr)); + ParamDecls.back()->setImplicit(); } - Alloc->setParams(llvm::makeArrayRef(ParamDecls, NumParams)); + Alloc->setParams(ParamDecls); Context.getTranslationUnitDecl()->addDecl(Alloc); IdResolver.tryAddTopLevelDecl(Alloc, Name); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 9b51f3dae5..6ae825b970 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3045,7 +3045,7 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { break; case SEMA_DECL_REFS: - if (Record.size() != 2) { + if (Record.size() != 3) { Error("Invalid SEMA_DECL_REFS block"); return Failure; } @@ -7104,12 +7104,14 @@ void ASTReader::UpdateSema() { // Load the offsets of the declarations that Sema references. // They will be lazily deserialized when needed. if (!SemaDeclRefs.empty()) { - assert(SemaDeclRefs.size() % 2 == 0); - for (unsigned I = 0; I != SemaDeclRefs.size(); I += 2) { + assert(SemaDeclRefs.size() % 3 == 0); + for (unsigned I = 0; I != SemaDeclRefs.size(); I += 3) { if (!SemaObj->StdNamespace) SemaObj->StdNamespace = SemaDeclRefs[I]; if (!SemaObj->StdBadAlloc) SemaObj->StdBadAlloc = SemaDeclRefs[I+1]; + if (!SemaObj->StdAlignValT) + SemaObj->StdAlignValT = SemaDeclRefs[I+2]; } SemaDeclRefs.clear(); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0866e3a81c..253fab2a36 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4363,9 +4363,10 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // Build a record containing some declaration references. RecordData SemaDeclRefs; - if (SemaRef.StdNamespace || SemaRef.StdBadAlloc) { + if (SemaRef.StdNamespace || SemaRef.StdBadAlloc || SemaRef.StdAlignValT) { AddDeclRef(SemaRef.getStdNamespace(), SemaDeclRefs); AddDeclRef(SemaRef.getStdBadAlloc(), SemaDeclRefs); + AddDeclRef(SemaRef.getStdAlignValT(), SemaDeclRefs); } RecordData CUDASpecialDeclRefs; diff --git a/test/CXX/drs/dr5xx.cpp b/test/CXX/drs/dr5xx.cpp index e0bab57e52..6461712cf0 100644 --- a/test/CXX/drs/dr5xx.cpp +++ b/test/CXX/drs/dr5xx.cpp @@ -8,6 +8,12 @@ // with -verify. __extension__ typedef __SIZE_TYPE__ size_t; void *operator new(size_t); // expected-error 0-1{{missing exception spec}} expected-note{{candidate}} +#if __cplusplus > 201402L +namespace std { + enum class align_val_t : size_t {}; +} +void *operator new(size_t, std::align_val_t); // expected-note{{candidate}} +#endif namespace dr500 { // dr500: dup 372 class D; diff --git a/test/PCH/cxx1z-aligned-alloc.cpp b/test/PCH/cxx1z-aligned-alloc.cpp new file mode 100644 index 0000000000..c49b910e85 --- /dev/null +++ b/test/PCH/cxx1z-aligned-alloc.cpp @@ -0,0 +1,35 @@ +// No PCH: +// RUN: %clang_cc1 -pedantic -fsized-deallocation -std=c++1z -include %s -verify %s +// +// With PCH: +// RUN: %clang_cc1 -pedantic -fsized-deallocation -std=c++1z -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic -fsized-deallocation -std=c++1z -include-pch %t -verify %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +using size_t = decltype(sizeof(0)); + +// Call the overaligned form of 'operator new'. +struct alignas(256) Q { int n; }; +void *f() { return new Q; } + +// Extract the std::align_val_t type from the implicit declaration of operator delete. +template +AlignValT extract(void (*)(void*, size_t, AlignValT)); +using T = decltype(extract(&operator delete)); + +#else + +// ok, calls aligned allocation via placement syntax +void *q = new (T{16}) Q; + +namespace std { + enum class align_val_t : size_t {}; +} + +using T = std::align_val_t; // ok, same type + +#endif