Do a proper recursive lookup when deciding whether a class's usual

deallocation function has a two-argument form.  Store the result of this
check in new[] and delete[] nodes.

Fixes rdar://problem/8913519

llvm-svn: 124373
This commit is contained in:
John McCall 2011-01-27 09:37:56 +00:00
parent 539848dd25
commit 284c48fff6
10 changed files with 248 additions and 81 deletions

View File

@ -992,8 +992,11 @@ class CXXNewExpr : public Expr {
bool Initializer : 1;
// Do we allocate an array? If so, the first SubExpr is the size expression.
bool Array : 1;
// If this is an array allocation, does the usual deallocation
// function for the allocated type want to know the allocated size?
bool UsualArrayDeleteWantsSize : 1;
// The number of placement new arguments.
unsigned NumPlacementArgs : 15;
unsigned NumPlacementArgs : 14;
// The number of constructor arguments. This may be 1 even for non-class
// types; use the pseudo copy constructor.
unsigned NumConstructorArgs : 14;
@ -1029,8 +1032,8 @@ public:
SourceRange TypeIdParens,
Expr *arraySize, CXXConstructorDecl *constructor, bool initializer,
Expr **constructorArgs, unsigned numConsArgs,
FunctionDecl *operatorDelete, QualType ty,
TypeSourceInfo *AllocatedTypeInfo,
FunctionDecl *operatorDelete, bool usualArrayDeleteWantsSize,
QualType ty, TypeSourceInfo *AllocatedTypeInfo,
SourceLocation startLoc, SourceLocation endLoc,
SourceLocation constructorLParen,
SourceLocation constructorRParen);
@ -1082,9 +1085,14 @@ public:
SourceRange getTypeIdParens() const { return TypeIdParens; }
bool isGlobalNew() const { return GlobalNew; }
void setGlobalNew(bool V) { GlobalNew = V; }
bool hasInitializer() const { return Initializer; }
void setHasInitializer(bool V) { Initializer = V; }
/// Answers whether the usual array deallocation function for the
/// allocated type expects the size of the allocation as a
/// parameter.
bool doesUsualArrayDeleteWantSize() const {
return UsualArrayDeleteWantsSize;
}
unsigned getNumConstructorArgs() const { return NumConstructorArgs; }
@ -1169,6 +1177,9 @@ class CXXDeleteExpr : public Expr {
// to pointer-to-array type (ArrayFormAsWritten will be false while ArrayForm
// will be true).
bool ArrayFormAsWritten : 1;
// Does the usual deallocation function for the element type require
// a size_t argument?
bool UsualArrayDeleteWantsSize : 1;
// Points to the operator delete overload that is used. Could be a member.
FunctionDecl *OperatorDelete;
// The pointer expression to be deleted.
@ -1177,12 +1188,13 @@ class CXXDeleteExpr : public Expr {
SourceLocation Loc;
public:
CXXDeleteExpr(QualType ty, bool globalDelete, bool arrayForm,
bool arrayFormAsWritten, FunctionDecl *operatorDelete,
Expr *arg, SourceLocation loc)
bool arrayFormAsWritten, bool usualArrayDeleteWantsSize,
FunctionDecl *operatorDelete, Expr *arg, SourceLocation loc)
: Expr(CXXDeleteExprClass, ty, VK_RValue, OK_Ordinary, false, false,
arg->containsUnexpandedParameterPack()),
GlobalDelete(globalDelete),
ArrayForm(arrayForm), ArrayFormAsWritten(arrayFormAsWritten),
UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize),
OperatorDelete(operatorDelete), Argument(arg), Loc(loc) { }
explicit CXXDeleteExpr(EmptyShell Shell)
: Expr(CXXDeleteExprClass, Shell), OperatorDelete(0), Argument(0) { }
@ -1191,6 +1203,14 @@ public:
bool isArrayForm() const { return ArrayForm; }
bool isArrayFormAsWritten() const { return ArrayFormAsWritten; }
/// Answers whether the usual array deallocation function for the
/// allocated type expects the size of the allocation as a
/// parameter. This can be true even if the actual deallocation
/// function that we're using doesn't want a size.
bool doesUsualArrayDeleteWantSize() const {
return UsualArrayDeleteWantsSize;
}
FunctionDecl *getOperatorDelete() const { return OperatorDelete; }
Expr *getArgument() { return cast<Expr>(Argument); }

View File

@ -111,7 +111,8 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew,
SourceRange TypeIdParens, Expr *arraySize,
CXXConstructorDecl *constructor, bool initializer,
Expr **constructorArgs, unsigned numConsArgs,
FunctionDecl *operatorDelete, QualType ty,
FunctionDecl *operatorDelete,
bool usualArrayDeleteWantsSize, QualType ty,
TypeSourceInfo *AllocatedTypeInfo,
SourceLocation startLoc, SourceLocation endLoc,
SourceLocation constructorLParen,
@ -119,8 +120,9 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew,
: Expr(CXXNewExprClass, ty, VK_RValue, OK_Ordinary,
ty->isDependentType(), ty->isDependentType(),
ty->containsUnexpandedParameterPack()),
GlobalNew(globalNew),
Initializer(initializer), SubExprs(0), OperatorNew(operatorNew),
GlobalNew(globalNew), Initializer(initializer),
UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize),
SubExprs(0), OperatorNew(operatorNew),
OperatorDelete(operatorDelete), Constructor(constructor),
AllocatedTypeInfo(AllocatedTypeInfo), TypeIdParens(TypeIdParens),
StartLoc(startLoc), EndLoc(endLoc), ConstructorLParen(constructorLParen),

View File

@ -142,13 +142,14 @@ void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
CGF.EmitReturnOfRValue(RV, ResultType);
}
CharUnits CGCXXABI::GetArrayCookieSize(QualType ElementType) {
CharUnits CGCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
return CharUnits::Zero();
}
llvm::Value *CGCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
// Should never be called.
ErrorUnsupportedABI(CGF, "array cookie initialization");
@ -156,7 +157,8 @@ llvm::Value *CGCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
}
void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
QualType ElementType, llvm::Value *&NumElements,
const CXXDeleteExpr *expr, QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize) {
ErrorUnsupportedABI(CGF, "array cookie reading");

View File

@ -189,7 +189,7 @@ public:
///
/// \param ElementType - the allocated type of the expression,
/// i.e. the pointee type of the expression result type
virtual CharUnits GetArrayCookieSize(QualType ElementType);
virtual CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
/// Initialize the array cookie for the given allocation.
///
@ -202,6 +202,7 @@ public:
virtual llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
/// Reads the array cookie associated with the given pointer,
@ -218,6 +219,7 @@ public:
/// \param CookieSize - an out parameter which will be initialized
/// with the size of the cookie, or zero if there is no cookie
virtual void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);

View File

@ -395,7 +395,7 @@ static CharUnits CalculateCookiePadding(CodeGenFunction &CGF,
if (IsPlacementOperatorNewArray(CGF.getContext(), OperatorNew))
return CharUnits::Zero();
return CGF.CGM.getCXXABI().GetArrayCookieSize(E->getAllocatedType());
return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
}
static llvm::Value *EmitCXXNewAllocSize(ASTContext &Context,
@ -1065,7 +1065,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
if (AllocSize != AllocSizeWithoutCookie) {
assert(E->isArray());
NewPtr = CGM.getCXXABI().InitializeArrayCookie(CGF, NewPtr, NumElements,
AllocType);
E, AllocType);
}
// If there's an operator delete, enter a cleanup to call it if an
@ -1271,18 +1271,19 @@ namespace {
/// Emit the code for deleting an array of objects.
static void EmitArrayDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
const CXXDeleteExpr *E,
llvm::Value *Ptr,
QualType ElementType) {
llvm::Value *NumElements = 0;
llvm::Value *AllocatedPtr = 0;
CharUnits CookieSize;
CGF.CGM.getCXXABI().ReadArrayCookie(CGF, Ptr, ElementType,
CGF.CGM.getCXXABI().ReadArrayCookie(CGF, Ptr, E, ElementType,
NumElements, AllocatedPtr, CookieSize);
assert(AllocatedPtr && "ReadArrayCookie didn't set AllocatedPtr");
// Make sure that we call delete even if one of the dtors throws.
const FunctionDecl *OperatorDelete = E->getOperatorDelete();
CGF.EHStack.pushCleanup<CallArrayDelete>(NormalAndEHCleanup,
AllocatedPtr, OperatorDelete,
NumElements, ElementType,
@ -1352,7 +1353,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
cast<llvm::PointerType>(Ptr->getType())->getElementType());
if (E->isArrayForm()) {
EmitArrayDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy);
EmitArrayDelete(*this, E, Ptr, DeleteTy);
} else {
EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy);
}

View File

@ -47,7 +47,9 @@ protected:
return PtrDiffTy;
}
bool NeedsArrayCookie(QualType ElementType);
bool NeedsArrayCookie(const CXXNewExpr *expr);
bool NeedsArrayCookie(const CXXDeleteExpr *expr,
QualType elementType);
public:
ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) :
@ -105,12 +107,14 @@ public:
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
CharUnits GetArrayCookieSize(QualType ElementType);
CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
@ -140,12 +144,14 @@ public:
void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResTy);
CharUnits GetArrayCookieSize(QualType ElementType);
CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
@ -794,67 +800,49 @@ void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
/************************** Array allocation cookies **************************/
bool ItaniumCXXABI::NeedsArrayCookie(QualType ElementType) {
ElementType = getContext().getBaseElementType(ElementType);
const RecordType *RT = ElementType->getAs<RecordType>();
if (!RT) return false;
const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
// If the class has a non-trivial destructor, it always needs a cookie.
if (!RD->hasTrivialDestructor()) return true;
bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie. Otherwise we don't need a cookie.
const CXXMethodDecl *UsualDeallocationFunction = 0;
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
// Usual deallocation functions of this form are always found on the
// class.
//
// FIXME: what exactly is this code supposed to do if there's an
// ambiguity? That's possible with using declarations.
DeclarationName OpName =
getContext().DeclarationNames.getCXXOperatorName(OO_Array_Delete);
DeclContext::lookup_const_iterator Op, OpEnd;
for (llvm::tie(Op, OpEnd) = RD->lookup(OpName); Op != OpEnd; ++Op) {
const CXXMethodDecl *Delete =
cast<CXXMethodDecl>((*Op)->getUnderlyingDecl());
if (Delete->isUsualDeallocationFunction()) {
UsualDeallocationFunction = Delete;
break;
}
}
// No usual deallocation function, we don't need a cookie.
if (!UsualDeallocationFunction)
return false;
// The usual deallocation function doesn't take a size_t argument,
// so we don't need a cookie.
if (UsualDeallocationFunction->getNumParams() == 1)
return false;
assert(UsualDeallocationFunction->getNumParams() == 2 &&
"Unexpected deallocation function type!");
return true;
// Otherwise, if the class has a non-trivial destructor, it always
// needs a cookie.
const CXXRecordDecl *record =
expr->getAllocatedType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
return (record && !record->hasTrivialDestructor());
}
CharUnits ItaniumCXXABI::GetArrayCookieSize(QualType ElementType) {
if (!NeedsArrayCookie(ElementType))
bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr,
QualType elementType) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
// Otherwise, if the class has a non-trivial destructor, it always
// needs a cookie.
const CXXRecordDecl *record =
elementType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
return (record && !record->hasTrivialDestructor());
}
CharUnits ItaniumCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
if (!NeedsArrayCookie(expr))
return CharUnits::Zero();
// Padding is the maximum of sizeof(size_t) and alignof(ElementType)
// Padding is the maximum of sizeof(size_t) and alignof(elementType)
ASTContext &Ctx = getContext();
return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()),
Ctx.getTypeAlignInChars(ElementType));
Ctx.getTypeAlignInChars(expr->getAllocatedType()));
}
llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
assert(NeedsArrayCookie(ElementType));
assert(NeedsArrayCookie(expr));
unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace();
@ -887,6 +875,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr,
@ -896,7 +885,7 @@ void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
const llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
// If we don't need an array cookie, bail out early.
if (!NeedsArrayCookie(ElementType)) {
if (!NeedsArrayCookie(expr, ElementType)) {
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
NumElements = 0;
CookieSize = CharUnits::Zero();
@ -927,8 +916,8 @@ void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
NumElements = CGF.Builder.CreateLoad(NumElementsPtr);
}
CharUnits ARMCXXABI::GetArrayCookieSize(QualType ElementType) {
if (!NeedsArrayCookie(ElementType))
CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
if (!NeedsArrayCookie(expr))
return CharUnits::Zero();
// On ARM, the cookie is always:
@ -944,8 +933,9 @@ CharUnits ARMCXXABI::GetArrayCookieSize(QualType ElementType) {
llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
assert(NeedsArrayCookie(ElementType));
assert(NeedsArrayCookie(expr));
// NewPtr is a char*.
@ -978,6 +968,7 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr,
@ -987,7 +978,7 @@ void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
const llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
// If we don't need an array cookie, bail out early.
if (!NeedsArrayCookie(ElementType)) {
if (!NeedsArrayCookie(expr, ElementType)) {
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
NumElements = 0;
CookieSize = CharUnits::Zero();

View File

@ -664,6 +664,61 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
return move(Result);
}
/// doesUsualArrayDeleteWantSize - Answers whether the usual
/// operator delete[] for the given type has a size_t parameter.
static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
QualType allocType) {
const RecordType *record =
allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
if (!record) return false;
// Try to find an operator delete[] in class scope.
DeclarationName deleteName =
S.Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete);
LookupResult ops(S, deleteName, loc, Sema::LookupOrdinaryName);
S.LookupQualifiedName(ops, record->getDecl());
// We're just doing this for information.
ops.suppressDiagnostics();
// Very likely: there's no operator delete[].
if (ops.empty()) return false;
// If it's ambiguous, it should be illegal to call operator delete[]
// 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);
}
/// ActOnCXXNew - Parsed a C++ 'new' expression (C++ 5.3.4), as in e.g.:
/// @code new (memory) int[size][4] @endcode
@ -839,6 +894,14 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
UseGlobal, AllocType, ArraySize, PlaceArgs,
NumPlaceArgs, 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);
llvm::SmallVector<Expr *, 8> AllPlaceArgs;
if (OperatorNew) {
// Add default arguments, if any.
@ -938,6 +1001,7 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
PlaceArgs, NumPlaceArgs, TypeIdParens,
ArraySize, Constructor, Init,
ConsArgs, NumConsArgs, OperatorDelete,
UsualArrayDeleteWantsSize,
ResultType, AllocTypeInfo,
StartLoc,
Init ? ConstructorRParen :
@ -1492,6 +1556,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
FunctionDecl *OperatorDelete = 0;
bool ArrayFormAsWritten = ArrayForm;
bool UsualArrayDeleteWantsSize = false;
if (!Ex->isTypeDependent()) {
QualType Type = Ex->getType();
@ -1587,6 +1652,21 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
FindDeallocationFunction(StartLoc, RD, DeleteName, OperatorDelete))
return ExprError();
// If we're allocating an array of records, check whether the
// usual operator delete[] has a size_t parameter.
if (ArrayForm) {
// If the user specifically asked to use the global allocator,
// we'll need to do the lookup into the class.
if (UseGlobal)
UsualArrayDeleteWantsSize =
doesUsualArrayDeleteWantSize(*this, StartLoc, PointeeElem);
// Otherwise, the usual operator delete[] should be the
// function we just found.
else if (isa<CXXMethodDecl>(OperatorDelete))
UsualArrayDeleteWantsSize = (OperatorDelete->getNumParams() == 2);
}
if (!RD->hasTrivialDestructor())
if (CXXDestructorDecl *Dtor = LookupDestructor(RD)) {
MarkDeclarationReferenced(StartLoc,
@ -1606,13 +1686,14 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
}
MarkDeclarationReferenced(StartLoc, OperatorDelete);
// FIXME: Check access and ambiguity of operator delete and destructor.
}
return Owned(new (Context) CXXDeleteExpr(Context.VoidTy, UseGlobal, ArrayForm,
ArrayFormAsWritten, OperatorDelete,
Ex, StartLoc));
ArrayFormAsWritten,
UsualArrayDeleteWantsSize,
OperatorDelete, Ex, StartLoc));
}
/// \brief Check the use of the given variable as a C++ condition in an if,

View File

@ -1106,8 +1106,9 @@ void ASTStmtReader::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) {
void ASTStmtReader::VisitCXXNewExpr(CXXNewExpr *E) {
VisitExpr(E);
E->setGlobalNew(Record[Idx++]);
E->setHasInitializer(Record[Idx++]);
E->GlobalNew = Record[Idx++];
E->Initializer = Record[Idx++];
E->UsualArrayDeleteWantsSize = Record[Idx++];
bool isArray = Record[Idx++];
unsigned NumPlacementArgs = Record[Idx++];
unsigned NumCtorArgs = Record[Idx++];
@ -1140,6 +1141,7 @@ void ASTStmtReader::VisitCXXDeleteExpr(CXXDeleteExpr *E) {
E->GlobalDelete = Record[Idx++];
E->ArrayForm = Record[Idx++];
E->ArrayFormAsWritten = Record[Idx++];
E->UsualArrayDeleteWantsSize = Record[Idx++];
E->OperatorDelete = cast_or_null<FunctionDecl>(Reader.GetDecl(Record[Idx++]));
E->Argument = Reader.ReadSubExpr();
E->Loc = ReadSourceLocation(Record, Idx);

View File

@ -1101,6 +1101,7 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
VisitExpr(E);
Record.push_back(E->isGlobalNew());
Record.push_back(E->hasInitializer());
Record.push_back(E->doesUsualArrayDeleteWantSize());
Record.push_back(E->isArray());
Record.push_back(E->getNumPlacementArgs());
Record.push_back(E->getNumConstructorArgs());
@ -1125,6 +1126,7 @@ void ASTStmtWriter::VisitCXXDeleteExpr(CXXDeleteExpr *E) {
Record.push_back(E->isGlobalDelete());
Record.push_back(E->isArrayForm());
Record.push_back(E->isArrayFormAsWritten());
Record.push_back(E->doesUsualArrayDeleteWantSize());
Writer.AddDeclRef(E->getOperatorDelete(), Record);
Writer.AddStmt(E->getArgument());
Writer.AddSourceLocation(E->getSourceRange().getBegin(), Record);

View File

@ -1,6 +1,70 @@
// RUN: %clang_cc1 -triple i686-pc-linux-gnu %s -o - -emit-llvm -verify | FileCheck %s
struct A { void operator delete(void*,__typeof(sizeof(int))); int x; };
void a(A* x) { delete x; }
typedef __typeof(sizeof(int)) size_t;
// CHECK: call void @_ZN1AdlEPvj(i8* %{{.*}}, i32 4)
namespace test1 {
struct A { void operator delete(void*,size_t); int x; };
// CHECK: define void @_ZN5test11aEPNS_1AE(
void a(A *x) {
// CHECK: load
// CHECK-NEXT: icmp eq {{.*}}, null
// CHECK-NEXT: br i1
// CHECK: call void @_ZN5test11AdlEPvj(i8* %{{.*}}, i32 4)
delete x;
}
}
// Check that we make cookies for the two-arg delete even when using
// the global allocator and deallocator.
namespace test2 {
struct A {
int x;
void *operator new[](size_t);
void operator delete[](void *, size_t);
};
// CHECK: define [[A:%.*]]* @_ZN5test24testEv()
A *test() {
// CHECK: [[NEW:%.*]] = call noalias i8* @_Znaj(i32 44)
// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[NEW]] to i32*
// CHECK-NEXT: store i32 10, i32* [[T0]]
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[NEW]], i64 4
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[A]]*
// CHECK-NEXT: ret [[A]]* [[T2]]
return ::new A[10];
}
// CHECK: define void @_ZN5test24testEPNS_1AE(
void test(A *p) {
// CHECK: [[P:%.*]] = alloca [[A]]*, align 4
// CHECK-NEXT: store [[A]]* {{%.*}}, [[A]]** [[P]], align 4
// CHECK-NEXT: [[T0:%.*]] = load [[A]]** [[P]], align 4
// CHECK-NEXT: [[T1:%.*]] = icmp eq [[A]]* [[T0]], null
// CHECK-NEXT: br i1 [[T1]],
// CHECK: [[T2:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 -4
// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i32*
// CHECK-NEXT: [[T5:%.*]] = load i32* [[T4]]
// CHECK-NEXT: call void @_ZdaPv(i8* [[T3]])
// CHECK-NEXT: br label
::delete[] p;
}
}
// rdar://problem/8913519
namespace test3 {
struct A {
int x;
void operator delete[](void *, size_t);
};
struct B : A {};
// CHECK: define void @_ZN5test34testEv()
void test() {
// CHECK: call noalias i8* @_Znaj(i32 24)
// CHECK-NEXT: bitcast
// CHECK-NEXT: store i32 5
(void) new B[5];
}
}