From: John McCall Date: Thu, 27 Jan 2011 09:37:56 +0000 (+0000) Subject: Do a proper recursive lookup when deciding whether a class's usual X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6ec278d1a354517e20f13a877481453ee7940c78;p=clang 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124373 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index f6bbfbf361..addf35a1bc 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -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(Argument); } diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index b4e8a4ec20..7cd714ba3f 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -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), diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 627df43a28..7fb1ffde2d 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -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"); diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 8bc1385d7f..547f98ac21 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -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); diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 7177a64e74..4f1d1764dc 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -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(NormalAndEHCleanup, AllocatedPtr, OperatorDelete, NumElements, ElementType, @@ -1352,7 +1353,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { cast(Ptr->getType())->getElementType()); if (E->isArrayForm()) { - EmitArrayDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy); + EmitArrayDelete(*this, E, Ptr, DeleteTy); } else { EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy); } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index edc03b1bd1..ef5455c286 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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(); - if (!RT) return false; - - const CXXRecordDecl *RD = cast(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. + if (expr->doesUsualArrayDeleteWantSize()) + 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()); +} +bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr, + QualType elementType) { // 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; - - // 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((*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; + // 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(QualType ElementType) { - if (!NeedsArrayCookie(ElementType)) +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(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(); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 53679d370c..8581c6f649 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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(); + 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(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(del)->isUsualDeallocationFunction()) { + filter.erase(); + continue; + } + } + filter.done(); + + if (!ops.isSingleResult()) return false; + + const FunctionDecl *del = cast(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 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(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, diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 5fec238ab8..79b68e63a8 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -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(Reader.GetDecl(Record[Idx++])); E->Argument = Reader.ReadSubExpr(); E->Loc = ReadSourceLocation(Record, Idx); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index e03a780fad..3962439ac5 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -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); diff --git a/test/CodeGenCXX/delete-two-arg.cpp b/test/CodeGenCXX/delete-two-arg.cpp index 5358747fed..b82e9ba63d 100644 --- a/test/CodeGenCXX/delete-two-arg.cpp +++ b/test/CodeGenCXX/delete-two-arg.cpp @@ -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]; + } +}