[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