aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/DeclCXX.h5
-rw-r--r--lib/AST/DeclCXX.cpp34
-rw-r--r--lib/CodeGen/CGCXXExpr.cpp14
-rw-r--r--lib/Sema/SemaExprCXX.cpp41
-rw-r--r--test/SemaCXX/new-delete.cpp17
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;
+}