]> granicus.if.org Git - clang/commitdiff
Handle C++ delete expressions when the overloaded delete operator is a
authorDouglas Gregor <dgregor@apple.com>
Tue, 29 Sep 2009 18:16:17 +0000 (18:16 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 29 Sep 2009 18:16:17 +0000 (18:16 +0000)
"usual deallocation function" with two arguments. CodeGen will have to
handle this case specifically, since the value for the second argument
(the size of the allocated object) may have to be computed at run
time.

Fixes the Sema part of PR4782.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83080 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/CodeGen/CGCXXExpr.cpp
lib/Sema/SemaExprCXX.cpp
test/SemaCXX/new-delete.cpp

index e4becf81cc5f6e2d849ed2da39479f54c3424ba9..7ba6d2520d3213df27a133abbd3a10f5e86aa094 100644 (file)
@@ -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());
   }
index fc04ed401bf4badc23af1b8382031b595c32d813..d1aef6a5b340e986ce6d7b0f7760241c47c0bb95 100644 (file)
@@ -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;
index 1ee4ceb13694fab93849afaf7bc359ae64537dce..7dd6427752aeafc3693197cd859d37a9fbf0e820 100644 (file)
@@ -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");
index 183bd8f4c92581d12650b380ef7ff9fe2e80444c..5846866dc67ed42bfae43c8b4f9420d93924fc02 100644 (file)
@@ -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();
     }
index 50af17d2659f2ac65aa23979143584ef226c684e..087b4643e30c9b9ebe69e9088ff3885de5630742 100644 (file)
@@ -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;
+}