[cfe-commits] r97209 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/AST/DeclCXX.cpp lib/Sema/SemaExprCXX.cpp test/CXX/expr/expr.unary/expr.new/p19.cpp test/CXX/expr/expr.unary/expr.new/p20-0x.cpp test/CXX/expr/expr.unary/expr.new/p20.cpp

Douglas Gregor dgregor at apple.com
Thu Feb 25 21:06:18 PST 2010


Author: dgregor
Date: Thu Feb 25 23:06:18 2010
New Revision: 97209

URL: http://llvm.org/viewvc/llvm-project?rev=97209&view=rev
Log:
Implement semantic analysis for C++ [expr.new]p18-20, which describe
how we find the operator delete that matches withe operator new we
found in a C++ new-expression.

This will also need CodeGen support. On a happy note, we're now a
"nans" away from building tramp3d-v4.


Added:
    cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp   (with props)
    cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp   (with props)
    cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp   (with props)
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=97209&r1=97208&r2=97209&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Feb 25 23:06:18 2010
@@ -2016,6 +2016,9 @@
   "only the first dimension of an allocated array may have dynamic size">;
 def err_new_paren_array_nonconst : Error<
   "when type is in parentheses, array cannot have dynamic size">;
+def err_placement_new_non_placement_delete : Error<
+  "'new' expression with placement arguments refers to non-placement "
+  "'operator delete'">;
 def err_array_size_not_integral : Error<
   "array size expression must have integral or enumerated type, not %0">;
 def err_default_init_const : Error<

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=97209&r1=97208&r2=97209&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Thu Feb 25 23:06:18 2010
@@ -573,7 +573,13 @@
   if (getOverloadedOperator() != OO_Delete &&
       getOverloadedOperator() != OO_Array_Delete)
     return false;
-  
+
+  // C++ [basic.stc.dynamic.deallocation]p2:
+  //   A template instance is never a usual deallocation function,
+  //   regardless of its signature.
+  if (getPrimaryTemplate())
+    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)

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=97209&r1=97208&r2=97209&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Thu Feb 25 23:06:18 2010
@@ -780,6 +780,12 @@
     ConsArgs = (Expr **)ConvertedConstructorArgs.take();
   }
   
+  // Mark the new and delete operators as referenced.
+  if (OperatorNew)
+    MarkDeclarationReferenced(StartLoc, OperatorNew);
+  if (OperatorDelete)
+    MarkDeclarationReferenced(StartLoc, OperatorDelete);
+
   // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16)
   
   PlacementArgs.release();
@@ -819,6 +825,20 @@
   return false;
 }
 
+/// \brief Determine whether the given function is a non-placement
+/// deallocation function.
+static bool isNonPlacementDeallocationFunction(FunctionDecl *FD) {
+  if (FD->isInvalidDecl())
+    return false;
+
+  if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
+    return Method->isUsualDeallocationFunction();
+
+  return ((FD->getOverloadedOperator() == OO_Delete ||
+           FD->getOverloadedOperator() == OO_Array_Delete) &&
+          FD->getNumParams() == 1);
+}
+
 /// FindAllocationFunctions - Finds the overloads of operator new and delete
 /// that are appropriate for the allocation.
 bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
@@ -835,7 +855,6 @@
   //   operator new.
   // 3) The first argument is always size_t. Append the arguments from the
   //   placement form.
-  // FIXME: Also find the appropriate delete operator.
 
   llvm::SmallVector<Expr*, 8> AllocArgs(1 + NumPlaceArgs);
   // We don't care about the actual value of this argument.
@@ -848,12 +867,20 @@
   AllocArgs[0] = &Size;
   std::copy(PlaceArgs, PlaceArgs + NumPlaceArgs, AllocArgs.begin() + 1);
 
+  // C++ [expr.new]p8:
+  //   If the allocated type is a non-array type, the allocation
+  //   function’s name is operator new and the deallocation function’s
+  //   name is operator delete. If the allocated type is an array
+  //   type, the allocation function’s name is operator new[] and the
+  //   deallocation function’s name is operator delete[].
   DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
                                         IsArray ? OO_Array_New : OO_New);
+  DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
+                                        IsArray ? OO_Array_Delete : OO_Delete);
+
   if (AllocType->isRecordType() && !UseGlobal) {
     CXXRecordDecl *Record
       = cast<CXXRecordDecl>(AllocType->getAs<RecordType>()->getDecl());
-    // FIXME: We fail to find inherited overloads.
     if (FindAllocationOverload(StartLoc, Range, NewName, &AllocArgs[0],
                           AllocArgs.size(), Record, /*AllowMissing=*/true,
                           OperatorNew))
@@ -874,6 +901,110 @@
   if (NumPlaceArgs > 0)
     std::copy(&AllocArgs[1], AllocArgs.end(), PlaceArgs);
 
+  // C++ [expr.new]p19:
+  //
+  //   If the new-expression begins with a unary :: operator, the
+  //   deallocation function’s name is looked up in the global
+  //   scope. Otherwise, if the allocated type is a class type T or an
+  //   array thereof, the deallocation function’s name is looked up in
+  //   the scope of T. If this lookup fails to find the name, or if
+  //   the allocated type is not a class type or array thereof, the
+  //   deallocation function’s name is looked up in the global scope.
+  LookupResult FoundDelete(*this, DeleteName, StartLoc, LookupOrdinaryName);
+  if (AllocType->isRecordType() && !UseGlobal) {
+    CXXRecordDecl *RD
+      = cast<CXXRecordDecl>(AllocType->getAs<RecordType>()->getDecl());
+    LookupQualifiedName(FoundDelete, RD);
+  }
+
+  if (FoundDelete.empty()) {
+    DeclareGlobalNewDelete();
+    LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
+  }
+
+  FoundDelete.suppressDiagnostics();
+  llvm::SmallVector<NamedDecl *, 4> Matches;
+  if (NumPlaceArgs > 1) {
+    // C++ [expr.new]p20:
+    //   A declaration of a placement deallocation function matches the
+    //   declaration of a placement allocation function if it has the
+    //   same number of parameters and, after parameter transformations
+    //   (8.3.5), all parameter types except the first are
+    //   identical. [...]
+    // 
+    // To perform this comparison, we compute the function type that
+    // the deallocation function should have, and use that type both
+    // for template argument deduction and for comparison purposes.
+    QualType ExpectedFunctionType;
+    {
+      const FunctionProtoType *Proto
+        = OperatorNew->getType()->getAs<FunctionProtoType>();
+      llvm::SmallVector<QualType, 4> ArgTypes;
+      ArgTypes.push_back(Context.VoidPtrTy); 
+      for (unsigned I = 1, N = Proto->getNumArgs(); I < N; ++I)
+        ArgTypes.push_back(Proto->getArgType(I));
+
+      ExpectedFunctionType
+        = Context.getFunctionType(Context.VoidTy, ArgTypes.data(),
+                                  ArgTypes.size(),
+                                  Proto->isVariadic(),
+                                  0, false, false, 0, 0, false, CC_Default);
+    }
+
+    for (LookupResult::iterator D = FoundDelete.begin(), 
+                             DEnd = FoundDelete.end();
+         D != DEnd; ++D) {
+      FunctionDecl *Fn = 0;
+      if (FunctionTemplateDecl *FnTmpl 
+            = dyn_cast<FunctionTemplateDecl>((*D)->getUnderlyingDecl())) {
+        // Perform template argument deduction to try to match the
+        // expected function type.
+        TemplateDeductionInfo Info(Context, StartLoc);
+        if (DeduceTemplateArguments(FnTmpl, 0, ExpectedFunctionType, Fn, Info))
+          continue;
+      } else
+        Fn = cast<FunctionDecl>((*D)->getUnderlyingDecl());
+
+      if (Context.hasSameType(Fn->getType(), ExpectedFunctionType))
+        Matches.push_back(Fn);
+    }
+  } else {
+    // C++ [expr.new]p20:
+    //   [...] Any non-placement deallocation function matches a
+    //   non-placement allocation function. [...]
+    for (LookupResult::iterator D = FoundDelete.begin(), 
+                             DEnd = FoundDelete.end();
+         D != DEnd; ++D) {
+      if (FunctionDecl *Fn = dyn_cast<FunctionDecl>((*D)->getUnderlyingDecl()))
+        if (isNonPlacementDeallocationFunction(Fn))
+          Matches.push_back(*D);
+    }
+  }
+
+  // C++ [expr.new]p20:
+  //   [...] If the lookup finds a single matching deallocation
+  //   function, that function will be called; otherwise, no
+  //   deallocation function will be called.
+  if (Matches.size() == 1) {
+    // FIXME: Drops access, using-declaration info!
+    OperatorDelete = cast<FunctionDecl>(Matches[0]->getUnderlyingDecl());
+
+    // C++0x [expr.new]p20:
+    //   If the lookup finds the two-parameter form of a usual
+    //   deallocation function (3.7.4.2) and that function, considered
+    //   as a placement deallocation function, would have been
+    //   selected as a match for the allocation function, the program
+    //   is ill-formed.
+    if (NumPlaceArgs && getLangOptions().CPlusPlus0x &&
+        isNonPlacementDeallocationFunction(OperatorDelete)) {
+      Diag(StartLoc, diag::err_placement_new_non_placement_delete)
+        << SourceRange(PlaceArgs[0]->getLocStart(), 
+                       PlaceArgs[NumPlaceArgs - 1]->getLocEnd());
+      Diag(OperatorDelete->getLocation(), diag::note_previous_decl)
+        << DeleteName;
+    }
+  }
+
   return false;
 }
 

Added: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp?rev=97209&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp (added)
+++ cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp Thu Feb 25 23:06:18 2010
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+typedef __SIZE_TYPE__ size_t;
+
+// Operator delete template for placement new with global lookup
+template<int I>
+struct X0 {
+  X0();
+
+  static void* operator new(size_t) {
+    return I; // expected-error{{cannot initialize}}
+  }
+
+  static void operator delete(void*) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X0() {
+  // Using the global operator new suppresses the search for a
+  // operator delete in the class.
+  ::new X0<2>;
+
+  new X0<3>; // expected-note 2{{instantiation}}
+}
+
+// Operator delete template for placement new[] with global lookup
+template<int I>
+struct X1 {
+  X1();
+
+  static void* operator new[](size_t) {
+    return I; // expected-error{{cannot initialize}}
+  }
+
+  static void operator delete[](void*) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X1() {
+  // Using the global operator new suppresses the search for a
+  // operator delete in the class.
+  ::new X1<2> [17];
+
+  new X1<3> [17]; // expected-note 2{{instantiation}}
+}

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p19.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp?rev=97209&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp (added)
+++ cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp Thu Feb 25 23:06:18 2010
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
+typedef __SIZE_TYPE__ size_t;
+
+struct S {
+  // Placement allocation function:
+  static void* operator new(size_t, size_t);
+  // Usual (non-placement) deallocation function:
+  static void operator delete(void*, size_t); // expected-note{{declared here}}
+};
+
+void testS() {
+  S* p = new (0) S;	// expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}}
+}

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20-0x.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp?rev=97209&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp (added)
+++ cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp Thu Feb 25 23:06:18 2010
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+typedef __SIZE_TYPE__ size_t;
+
+// Overloaded operator delete with two arguments
+template<int I>
+struct X0 {
+  X0();
+  static void* operator new(size_t);
+  static void operator delete(void*, size_t) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X0() {
+  new X0<1>; // expected-note{{instantiation}}
+}
+
+// Overloaded operator delete with one argument
+template<int I>
+struct X1 {
+  X1();
+
+  static void* operator new(size_t);
+  static void operator delete(void*) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X1() {
+  new X1<1>; // expected-note{{instantiation}}
+}
+
+// Overloaded operator delete for placement new
+template<int I>
+struct X2 {
+  X2();
+
+  static void* operator new(size_t, double, double);
+  static void* operator new(size_t, int, int);
+
+  static void operator delete(void*, const int, int) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+
+  static void operator delete(void*, double, double);
+};
+
+void test_X2() {
+  new (0, 0) X2<1>; // expected-note{{instantiation}}
+}
+
+// Operator delete template for placement new
+struct X3 {
+  X3();
+
+  static void* operator new(size_t, double, double);
+
+  template<typename T>
+  static void operator delete(void*, T x, T) {
+    double *dp = &x;
+    int *ip = &x; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X3() {
+  new (0, 0) X3; // expected-note{{instantiation}}
+}
+
+// Operator delete template for placement new in global scope.
+struct X4 {
+  X4();
+  static void* operator new(size_t, double, double);
+};
+
+template<typename T>
+void operator delete(void*, T x, T) {
+  double *dp = &x;
+  int *ip = &x; // expected-error{{cannot initialize}}
+}
+
+void test_X4() {
+  new (0, 0) X4; // expected-note{{instantiation}}
+}
+
+// Useless operator delete hides global operator delete template.
+struct X5 {
+  X5();
+  static void* operator new(size_t, double, double);
+  void operator delete(void*, double*, double*);
+};
+
+void test_X5() {
+  new (0, 0) X5; // okay, we found X5::operator delete but didn't pick it
+}
+
+// Operator delete template for placement new
+template<int I>
+struct X6 {
+  X6();
+
+  static void* operator new(size_t) {
+    return I; // expected-error{{cannot initialize}}
+  }
+
+  static void operator delete(void*) {
+    int *ip = I; // expected-error{{cannot initialize}}
+  }
+};
+
+void test_X6() {
+  new X6<3>; // expected-note 2{{instantiation}}
+}
+
+void *operator new(size_t, double, double, double);
+
+template<typename T>
+void operator delete(void*, T x, T, T) {
+  double *dp = &x;
+  int *ip = &x; // expected-error{{cannot initialize}}
+}
+void test_int_new() {
+  new (1.0, 1.0, 1.0) int; // expected-note{{instantiation}}
+}
+
+// We don't need an operator delete if the type has a trivial
+// constructor, since we know that constructor cannot throw.
+// FIXME: Is this within the standard? Seems fishy, but both EDG+GCC do it.
+#if 0
+template<int I>
+struct X7 {
+  static void* operator new(size_t);
+  static void operator delete(void*, size_t) {
+    int *ip = I; // okay, since it isn't instantiated.
+  }
+};
+
+void test_X7() {
+  new X7<1>;
+}
+#endif
+

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/expr/expr.unary/expr.new/p20.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list