Skip to content

Commit

Permalink
P0035R4: add std::align_val_t overloads of operator new/delete in C++…
Browse files Browse the repository at this point in the history
…17 mode.

llvm-svn: 282800
  • Loading branch information
zygoloid committed Sep 29, 2016
1 parent e979fd1 commit 96269c5
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 60 deletions.
8 changes: 6 additions & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,10 @@ class Sema {
/// 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
/// \<initializer_list>.
ClassTemplateDecl *StdInitializerList;
Expand Down Expand Up @@ -4237,6 +4241,7 @@ class Sema {
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.
Expand Down Expand Up @@ -4838,8 +4843,7 @@ class Sema {
bool Diagnose = true);
void DeclareGlobalNewDelete();
void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return,
QualType Param1,
QualType Param2 = QualType());
ArrayRef<QualType> Params);

bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
DeclarationName Name, FunctionDecl* &Operator,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 17 additions & 7 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -13089,6 +13095,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
New = EnumDecl::Create(Context, SearchDC, KWLoc, Loc, Name,
cast_or_null<EnumDecl>(PrevDecl), ScopedEnum,
ScopedEnumUsesClassTag, !EnumUnderlying.isNull());

if (isStdAlignValT && (!StdAlignValT || getStdAlignValT()->isImplicit()))
StdAlignValT = cast<EnumDecl>(New);

// If this is an undefined enum, warn.
if (TUK != TUK_Definition && !Invalid) {
TagDecl *Def;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8266,6 +8266,10 @@ CXXRecordDecl *Sema::getStdBadAlloc() const {
StdBadAlloc.get(Context.getExternalSource()));
}

EnumDecl *Sema::getStdAlignValT() const {
return cast_or_null<EnumDecl>(StdAlignValT.get(Context.getExternalSource()));
}

NamespaceDecl *Sema::getStdNamespace() const {
return cast_or_null<NamespaceDecl>(
StdNamespace.get(Context.getExternalSource()));
Expand Down
102 changes: 56 additions & 46 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<QualType, 3> 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<QualType> 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);
Expand All @@ -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<FunctionDecl>(*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<QualType, 3> 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.
Expand Down Expand Up @@ -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,
Expand All @@ -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<ParmVarDecl*, 3> 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);
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions clang/test/CXX/drs/dr5xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
35 changes: 35 additions & 0 deletions clang/test/PCH/cxx1z-aligned-alloc.cpp
Original file line number Diff line number Diff line change
@@ -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<typename AlignValT>
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

0 comments on commit 96269c5

Please sign in to comment.