forked from OSchip/llvm-project
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. llvm-svn: 97209
This commit is contained in:
parent
044ada532c
commit
6642ca217e
|
@ -2016,6 +2016,9 @@ def err_new_array_nonconst : Error<
|
|||
"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<
|
||||
|
|
|
@ -573,7 +573,13 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const {
|
|||
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)
|
||||
|
|
|
@ -780,6 +780,12 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
|
|||
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 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
|
|||
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 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
// 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 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
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 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -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'}}
|
||||
}
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue