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">;
|
"only the first dimension of an allocated array may have dynamic size">;
|
||||||
def err_new_paren_array_nonconst : Error<
|
def err_new_paren_array_nonconst : Error<
|
||||||
"when type is in parentheses, array cannot have dynamic size">;
|
"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<
|
def err_array_size_not_integral : Error<
|
||||||
"array size expression must have integral or enumerated type, not %0">;
|
"array size expression must have integral or enumerated type, not %0">;
|
||||||
def err_default_init_const : Error<
|
def err_default_init_const : Error<
|
||||||
|
|
|
@ -573,7 +573,13 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const {
|
||||||
if (getOverloadedOperator() != OO_Delete &&
|
if (getOverloadedOperator() != OO_Delete &&
|
||||||
getOverloadedOperator() != OO_Array_Delete)
|
getOverloadedOperator() != OO_Array_Delete)
|
||||||
return false;
|
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:
|
// C++ [basic.stc.dynamic.deallocation]p2:
|
||||||
// If a class T has a member deallocation function named operator delete
|
// If a class T has a member deallocation function named operator delete
|
||||||
// with exactly one parameter, then that function is a usual (non-placement)
|
// 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();
|
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)
|
// FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16)
|
||||||
|
|
||||||
PlacementArgs.release();
|
PlacementArgs.release();
|
||||||
|
@ -819,6 +825,20 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
|
||||||
return false;
|
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
|
/// FindAllocationFunctions - Finds the overloads of operator new and delete
|
||||||
/// that are appropriate for the allocation.
|
/// that are appropriate for the allocation.
|
||||||
bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
||||||
|
@ -835,7 +855,6 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
||||||
// operator new.
|
// operator new.
|
||||||
// 3) The first argument is always size_t. Append the arguments from the
|
// 3) The first argument is always size_t. Append the arguments from the
|
||||||
// placement form.
|
// placement form.
|
||||||
// FIXME: Also find the appropriate delete operator.
|
|
||||||
|
|
||||||
llvm::SmallVector<Expr*, 8> AllocArgs(1 + NumPlaceArgs);
|
llvm::SmallVector<Expr*, 8> AllocArgs(1 + NumPlaceArgs);
|
||||||
// We don't care about the actual value of this argument.
|
// 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;
|
AllocArgs[0] = &Size;
|
||||||
std::copy(PlaceArgs, PlaceArgs + NumPlaceArgs, AllocArgs.begin() + 1);
|
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(
|
DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
|
||||||
IsArray ? OO_Array_New : OO_New);
|
IsArray ? OO_Array_New : OO_New);
|
||||||
|
DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
|
||||||
|
IsArray ? OO_Array_Delete : OO_Delete);
|
||||||
|
|
||||||
if (AllocType->isRecordType() && !UseGlobal) {
|
if (AllocType->isRecordType() && !UseGlobal) {
|
||||||
CXXRecordDecl *Record
|
CXXRecordDecl *Record
|
||||||
= cast<CXXRecordDecl>(AllocType->getAs<RecordType>()->getDecl());
|
= cast<CXXRecordDecl>(AllocType->getAs<RecordType>()->getDecl());
|
||||||
// FIXME: We fail to find inherited overloads.
|
|
||||||
if (FindAllocationOverload(StartLoc, Range, NewName, &AllocArgs[0],
|
if (FindAllocationOverload(StartLoc, Range, NewName, &AllocArgs[0],
|
||||||
AllocArgs.size(), Record, /*AllowMissing=*/true,
|
AllocArgs.size(), Record, /*AllowMissing=*/true,
|
||||||
OperatorNew))
|
OperatorNew))
|
||||||
|
@ -874,6 +901,110 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
||||||
if (NumPlaceArgs > 0)
|
if (NumPlaceArgs > 0)
|
||||||
std::copy(&AllocArgs[1], AllocArgs.end(), PlaceArgs);
|
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;
|
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