Overload resolution for the operator new function. Member version is still untested.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60503 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Sebastian Redl 2008-12-03 20:26:15 +00:00
Родитель cd25c13ade
Коммит b5a57a69e5
4 изменённых файлов: 255 добавлений и 11 удалений

Просмотреть файл

@ -116,7 +116,8 @@ void Sema::ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) {
Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer)
: PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
SourceMgr(PP.getSourceManager()), CurContext(0), PreDeclaratorDC(0),
CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()) {
CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()),
GlobalNewDeleteDeclared(false) {
// Get IdentifierInfo objects for known functions for which we
// do extra checking.

Просмотреть файл

@ -203,6 +203,10 @@ public:
/// by GetStdNamespace
NamespaceDecl *StdNamespace;
/// A flag to remember whether the implicit forms of operator new and delete
/// have been declared.
bool GlobalNewDeleteDeclared;
/// ObjCMethodList - a linked list of methods with different signatures.
struct ObjCMethodList {
ObjCMethodDecl *Method;
@ -831,6 +835,14 @@ public:
ExprTy **ConstructorArgs, unsigned NumConsArgs,
SourceLocation ConstructorRParen);
bool CheckAllocatedType(QualType AllocType, const Declarator &D);
bool FindAllocationFunctions(SourceLocation StartLoc, bool UseGlobal,
QualType AllocType, bool IsArray,
Expr **PlaceArgs, unsigned NumPlaceArgs,
FunctionDecl *&OperatorNew,
FunctionDecl *&OperatorDelete);
void DeclareGlobalNewDelete();
void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return,
QualType Argument);
/// ActOnCXXDelete - Parsed a C++ 'delete' expression
virtual ExprResult ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,

Просмотреть файл

@ -17,6 +17,7 @@
#include "clang/Parse/DeclSpec.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/TargetInfo.h"
using namespace clang;
/// ActOnCXXConversionFunctionExpr - Parse a C++ conversion function
@ -235,19 +236,13 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
}
}
// --- Choosing an allocation function ---
// C++ 5.3.4p8 - 14 & 18
// 1) If UseGlobal is true, only look in the global scope. Else, also look
// in the scope of the allocated class.
// 2) If an array size is given, look for operator new[], else look for
// operator new.
// 3) The first argument is always size_t. Append the arguments from the
// placement form.
// FIXME: Find the correct overload of operator new.
// FIXME: Also find the corresponding overload of operator delete.
FunctionDecl *OperatorNew = 0;
FunctionDecl *OperatorDelete = 0;
Expr **PlaceArgs = (Expr**)PlacementArgs;
if (FindAllocationFunctions(StartLoc, UseGlobal, AllocType, ArraySize,
PlaceArgs, NumPlaceArgs, OperatorNew,
OperatorDelete))
return true;
bool Init = ConstructorLParen.isValid();
// --- Choosing a constructor ---
@ -355,6 +350,235 @@ bool Sema::CheckAllocatedType(QualType AllocType, const Declarator &D)
return false;
}
/// FindAllocationFunctions - Finds the overloads of operator new and delete
/// that are appropriate for the allocation.
bool Sema::FindAllocationFunctions(SourceLocation StartLoc, bool UseGlobal,
QualType AllocType, bool IsArray,
Expr **PlaceArgs, unsigned NumPlaceArgs,
FunctionDecl *&OperatorNew,
FunctionDecl *&OperatorDelete)
{
// --- Choosing an allocation function ---
// C++ 5.3.4p8 - 14 & 18
// 1) If UseGlobal is true, only look in the global scope. Else, also look
// in the scope of the allocated class.
// 2) If an array size is given, look for operator new[], else look for
// 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.
// FIXME: Should the Sema create the expression and embed it in the syntax
// tree? Or should the consumer just recalculate the value?
AllocArgs[0] = new IntegerLiteral(llvm::APInt::getNullValue(
Context.Target.getPointerWidth(0)),
Context.getSizeType(),
SourceLocation());
std::copy(PlaceArgs, PlaceArgs + NumPlaceArgs, AllocArgs.begin() + 1);
DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
IsArray ? OO_Array_New : OO_New);
if (AllocType->isRecordType() && !UseGlobal) {
OverloadCandidateSet MemberNewCandidates;
const CXXRecordType *Record = cast<CXXRecordType>(
AllocType->getAsRecordType());
IdentifierResolver::iterator I =
IdResolver.begin(NewName, Record->getDecl(), /*LookInParentCtx=*/false);
NamedDecl *Decl = (I == IdResolver.end()) ? 0 : *I;
// Member operator new is implicitly treated as static, so don't use
// AddMemberCandidate.
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(Decl))
AddOverloadCandidate(Method, &AllocArgs[0], AllocArgs.size(),
MemberNewCandidates,
/*SuppressUserConversions=*/false);
else if (OverloadedFunctionDecl *Ovl
= dyn_cast_or_null<OverloadedFunctionDecl>(Decl)) {
for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
FEnd = Ovl->function_end();
F != FEnd; ++F) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*F))
AddOverloadCandidate(Method, &AllocArgs[0], AllocArgs.size(),
MemberNewCandidates,
/*SuppressUserConversions=*/false);
}
}
// Do the resolution.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(MemberNewCandidates, Best)) {
case OR_Success: {
// Got one!
FunctionDecl *FnDecl = Best->Function;
// The first argument is size_t, and the first parameter must be size_t,
// too.
for (unsigned i = 1; i < AllocArgs.size(); ++i) {
// FIXME: Passing word to diagnostic.
// This might modify the argument expression, so pass the one in
// PlaceArgs.
if (PerformCopyInitialization(PlaceArgs[i-1],
FnDecl->getParamDecl(i)->getType(),
"passing"))
return true;
}
OperatorNew = FnDecl;
break;
}
case OR_No_Viable_Function:
// No viable function; look something up in the global scope instead.
break;
case OR_Ambiguous:
// FIXME: Bad location information.
Diag(StartLoc, diag::err_ovl_ambiguous_oper) << NewName;
PrintOverloadCandidates(MemberNewCandidates, /*OnlyViable=*/true);
return true;
}
}
if (!OperatorNew) {
// Didn't find a member overload. Look for a global one.
DeclareGlobalNewDelete();
OverloadCandidateSet GlobalNewCandidates;
IdentifierResolver::iterator I =
IdResolver.begin(NewName, Context.getTranslationUnitDecl(),
/*LookInParentCtx=*/false);
NamedDecl *Decl = (I == IdResolver.end()) ? 0 : *I;
if (FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(Decl))
AddOverloadCandidate(Fn, &AllocArgs[0], AllocArgs.size(),
GlobalNewCandidates,
/*SuppressUserConversions=*/false);
else if (OverloadedFunctionDecl *Ovl
= dyn_cast_or_null<OverloadedFunctionDecl>(Decl)) {
for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
FEnd = Ovl->function_end();
F != FEnd; ++F) {
if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*F))
AddOverloadCandidate(Fn, &AllocArgs[0], AllocArgs.size(),
GlobalNewCandidates,
/*SuppressUserConversions=*/false);
}
}
// Do the resolution.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(GlobalNewCandidates, Best)) {
case OR_Success: {
// Got one!
FunctionDecl *FnDecl = Best->Function;
// The first argument is size_t, and the first parameter must be size_t,
// too. This is checked on declaration and can be assumed.
for (unsigned i = 1; i < AllocArgs.size(); ++i) {
// FIXME: Passing word to diagnostic.
// This might modify the argument expression, so pass the one in
// PlaceArgs.
if (PerformCopyInitialization(PlaceArgs[i-1],
FnDecl->getParamDecl(i)->getType(),
"passing"))
return true;
}
OperatorNew = FnDecl;
break;
}
case OR_No_Viable_Function:
// FIXME: Bad location information.
Diag(StartLoc, diag::err_ovl_no_viable_function_in_call)
<< NewName << (unsigned)GlobalNewCandidates.size();
PrintOverloadCandidates(GlobalNewCandidates, /*OnlyViable=*/false);
return true;
case OR_Ambiguous:
// FIXME: Bad location information.
Diag(StartLoc, diag::err_ovl_ambiguous_oper) << NewName;
PrintOverloadCandidates(GlobalNewCandidates, /*OnlyViable=*/true);
return true;
}
}
AllocArgs[0]->Destroy(Context);
return false;
}
/// DeclareGlobalNewDelete - Declare the global forms of operator new and
/// delete. These are:
/// @code
/// void* operator new(std::size_t) throw(std::bad_alloc);
/// void* operator new[](std::size_t) throw(std::bad_alloc);
/// void operator delete(void *) throw();
/// void operator delete[](void *) throw();
/// @endcode
/// Note that the placement and nothrow forms of new are *not* implicitly
/// declared. Their use requires including \<new\>.
void Sema::DeclareGlobalNewDelete()
{
if (GlobalNewDeleteDeclared)
return;
GlobalNewDeleteDeclared = true;
QualType VoidPtr = Context.getPointerType(Context.VoidTy);
QualType SizeT = Context.getSizeType();
// FIXME: Exception specifications are not added.
DeclareGlobalAllocationFunction(
Context.DeclarationNames.getCXXOperatorName(OO_New),
VoidPtr, SizeT);
DeclareGlobalAllocationFunction(
Context.DeclarationNames.getCXXOperatorName(OO_Array_New),
VoidPtr, SizeT);
DeclareGlobalAllocationFunction(
Context.DeclarationNames.getCXXOperatorName(OO_Delete),
Context.VoidTy, VoidPtr);
DeclareGlobalAllocationFunction(
Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete),
Context.VoidTy, VoidPtr);
}
/// DeclareGlobalAllocationFunction - Declares a single implicit global
/// allocation function if it doesn't already exist.
void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
QualType Return, QualType Argument)
{
DeclContext *GlobalCtx = Context.getTranslationUnitDecl();
// Check if this function is already declared.
IdentifierResolver::iterator I = IdResolver.begin(Name, GlobalCtx,
/*CheckParent=*/false);
if (I != IdResolver.end()) {
NamedDecl *Decl = *I;
if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(Decl)) {
// The return type fits. This is checked when the function is declared.
if (Fn->getNumParams() == 1 &&
Context.getCanonicalType(Fn->getParamDecl(0)->getType()) == Argument)
return;
} else if(OverloadedFunctionDecl *Ovl =
dyn_cast<OverloadedFunctionDecl>(Decl)) {
for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
FEnd = Ovl->function_end();
F != FEnd; ++F) {
if ((*F)->getNumParams() == 1 &&
Context.getCanonicalType((*F)->getParamDecl(0)->getType())
== Argument)
return;
}
}
}
QualType FnType = Context.getFunctionType(Return, &Argument, 1, false, 0);
FunctionDecl *Alloc =
FunctionDecl::Create(Context, GlobalCtx, SourceLocation(), Name,
FnType, FunctionDecl::None, false, 0,
SourceLocation());
Alloc->setImplicit();
ParmVarDecl *Param = ParmVarDecl::Create(Context, Alloc, SourceLocation(),
0, Argument, VarDecl::None, 0, 0);
Alloc->setParams(&Param, 1);
PushOnScopeChains(Alloc, TUScope);
}
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
/// or

Просмотреть файл

@ -1,5 +1,7 @@
// RUN: clang -fsyntax-only -verify %s
#include <stddef.h>
struct S // expected-note {{candidate}}
{
S(int, int, double); // expected-note {{candidate}}
@ -8,6 +10,10 @@ struct S // expected-note {{candidate}}
};
struct T;
void* operator new(size_t); // expected-note {{candidate}}
void* operator new(size_t, int*); // expected-note {{candidate}}
void* operator new(size_t, float*); // expected-note {{candidate}}
void good_news()
{
int *pi = new int;
@ -43,6 +49,7 @@ void bad_news(int *ip)
(void)new int[-1]; // expected-error {{array size is negative}}
(void)new int[*(S*)0]; // expected-error {{array size expression must have integral or enumerated type, not 'struct S'}}
(void)::S::new int; // expected-error {{expected unqualified-id}}
(void)new (0, 0) int; // expected-error {{no matching function for call to 'operator new'}}
// Some lacking cases due to lack of sema support.
}