diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 5 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 34 | ||||
-rw-r--r-- | lib/CodeGen/CGCXXExpr.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 41 | ||||
-rw-r--r-- | test/SemaCXX/new-delete.cpp | 17 |
5 files changed, 90 insertions, 21 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index e4becf81cc..7ba6d2520d 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -785,6 +785,11 @@ public: return (CD->begin_overridden_methods() != CD->end_overridden_methods()); } + /// \brief Determine whether this is a usual deallocation function + /// (C++ [basic.stc.dynamic.deallocation]p2), which is an overloaded + /// delete or delete[] operator with a particular signature. + bool isUsualDeallocationFunction() const; + const CXXMethodDecl *getCanonicalDecl() const { return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl()); } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index fc04ed401b..d1aef6a5b3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -451,6 +451,40 @@ CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, isStatic, isInline); } +bool CXXMethodDecl::isUsualDeallocationFunction() const { + if (getOverloadedOperator() != OO_Delete && + getOverloadedOperator() != OO_Array_Delete) + return false; + + // C++ [basic.stc.dynamic.deallocation]p2: + // If a class T has a member deallocation function named operator delete + // with exactly one parameter, then that function is a usual (non-placement) + // 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 + // exactly two parameters, the second of which has type std::size_t (18.1), + // then this function is a usual deallocation function. + ASTContext &Context = getASTContext(); + if (getNumParams() != 2 || + !Context.hasSameType(getParamDecl(1)->getType(), Context.getSizeType())) + return false; + + // This function is a usual deallocation function if there are no + // single-parameter deallocation functions of the same kind. + for (DeclContext::lookup_const_result R = getDeclContext()->lookup(getDeclName()); + R.first != R.second; ++R.first) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*R.first)) + if (FD->getNumParams() == 1) + return false; + } + + return true; +} + typedef llvm::DenseMap<const CXXMethodDecl*, std::vector<const CXXMethodDecl *> *> OverriddenMethodsMapTy; diff --git a/lib/CodeGen/CGCXXExpr.cpp b/lib/CodeGen/CGCXXExpr.cpp index 1ee4ceb136..7dd6427752 100644 --- a/lib/CodeGen/CGCXXExpr.cpp +++ b/lib/CodeGen/CGCXXExpr.cpp @@ -239,10 +239,18 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { return; }; - QualType DeleteTy = - E->getArgument()->getType()->getAs<PointerType>()->getPointeeType(); + // Get at the argument before we performed the implicit conversion + // to void*. + const Expr *Arg = E->getArgument(); + while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { + if (ICE->getCastKind() != CastExpr::CK_UserDefinedConversion && + ICE->getType()->isVoidPointerType()) + Arg = ICE->getSubExpr(); + } + + QualType DeleteTy = Arg->getType()->getAs<PointerType>()->getPointeeType(); - llvm::Value *Ptr = EmitScalarExpr(E->getArgument()); + llvm::Value *Ptr = EmitScalarExpr(Arg); // Null check the pointer. llvm::BasicBlock *DeleteNotNull = createBasicBlock("delete.notnull"); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 183bd8f4c9..5846866dc6 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -589,6 +589,7 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, DeclarationName Name, Expr** Args, unsigned NumArgs, DeclContext *Ctx, bool AllowMissing, FunctionDecl *&Operator) { + // FIXME: Change to use LookupQualifiedName! DeclContext::lookup_iterator Alloc, AllocEnd; llvm::tie(Alloc, AllocEnd) = Ctx->lookup(Name); if (Alloc == AllocEnd) { @@ -602,9 +603,13 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, for (; Alloc != AllocEnd; ++Alloc) { // Even member operator new/delete are implicitly treated as // static, so don't use AddMemberCandidate. - if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*Alloc)) + if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*Alloc)) { AddOverloadCandidate(Fn, Args, NumArgs, Candidates, /*SuppressUserConversions=*/false); + continue; + } + + // FIXME: Handle function templates } // Do the resolution. @@ -616,7 +621,7 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, // The first argument is size_t, and the first parameter must be size_t, // too. This is checked on declaration and can be assumed. (It can't be // asserted on, though, since invalid decls are left in there.) - for (unsigned i = 1; i < NumArgs; ++i) { + for (unsigned i = 0; i < NumArgs; ++i) { // FIXME: Passing word to diagnostic. if (PerformCopyInitialization(Args[i], FnDecl->getParamDecl(i)->getType(), @@ -844,27 +849,27 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, << Ex->getSourceRange())) return ExprError(); - // FIXME: This should be shared with the code for finding the delete - // operator in ActOnCXXNew. - IntegerLiteral Size(llvm::APInt::getNullValue( - Context.Target.getPointerWidth(0)), - Context.getSizeType(), - SourceLocation()); - ImplicitCastExpr Cast(Context.getPointerType(Context.VoidTy), - CastExpr::CK_Unknown, &Size, false); - Expr *DeleteArg = &Cast; - DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( ArrayForm ? OO_Array_Delete : OO_Delete); if (Pointee->isRecordType() && !UseGlobal) { CXXRecordDecl *Record = cast<CXXRecordDecl>(Pointee->getAs<RecordType>()->getDecl()); - // FIXME: We fail to find inherited overloads. - if (FindAllocationOverload(StartLoc, SourceRange(), DeleteName, - &DeleteArg, 1, Record, /*AllowMissing=*/true, - OperatorDelete)) - return ExprError(); + + // Try to find operator delete/operator delete[] in class scope. + LookupResult Found = LookupQualifiedName(Record, DeleteName, + LookupOrdinaryName); + // FIXME: Diagnose ambiguity properly + assert(!Found.isAmbiguous() && "Ambiguous delete/delete[] not handled"); + for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); + F != FEnd; ++F) { + if (CXXMethodDecl *Delete = dyn_cast<CXXMethodDecl>(*F)) + if (Delete->isUsualDeallocationFunction()) { + OperatorDelete = Delete; + break; + } + } + if (!Record->hasTrivialDestructor()) if (const CXXDestructorDecl *Dtor = Record->getDestructor(Context)) MarkDeclarationReferenced(StartLoc, @@ -876,7 +881,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, DeclareGlobalNewDelete(); DeclContext *TUDecl = Context.getTranslationUnitDecl(); if (FindAllocationOverload(StartLoc, SourceRange(), DeleteName, - &DeleteArg, 1, TUDecl, /*AllowMissing=*/false, + &Ex, 1, TUDecl, /*AllowMissing=*/false, OperatorDelete)) return ExprError(); } diff --git a/test/SemaCXX/new-delete.cpp b/test/SemaCXX/new-delete.cpp index 50af17d265..087b4643e3 100644 --- a/test/SemaCXX/new-delete.cpp +++ b/test/SemaCXX/new-delete.cpp @@ -115,3 +115,20 @@ void test_delete_conv(X0 x0, X1 x1, X2 x2) { delete x1; delete x2; // expected-error{{ambiguous conversion of delete expression of type 'struct X2' to a pointer}} } + +// PR4782 +class X3 { +public: + static void operator delete(void * mem, unsigned long size); +}; + +class X4 { +public: + static void release(X3 *x); + static void operator delete(void * mem, unsigned long size); +}; + + +void X4::release(X3 *x) { + delete x; +} |