forked from OSchip/llvm-project
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:
parent
539848dd25
commit
284c48fff6
|
@ -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); }
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue