diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 6b913fb215..1a9e5fb30e 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -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. diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index cfb221bf40..150b32ec51 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -202,6 +202,10 @@ public: /// The C++ "std" namespace, where the standard library resides. Cached here /// 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 { @@ -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, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index bfc367dd58..1cecb5dc70 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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 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( + 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(Decl)) + AddOverloadCandidate(Method, &AllocArgs[0], AllocArgs.size(), + MemberNewCandidates, + /*SuppressUserConversions=*/false); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null(Decl)) { + for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), + FEnd = Ovl->function_end(); + F != FEnd; ++F) { + if (CXXMethodDecl *Method = dyn_cast(*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(Decl)) + AddOverloadCandidate(Fn, &AllocArgs[0], AllocArgs.size(), + GlobalNewCandidates, + /*SuppressUserConversions=*/false); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null(Decl)) { + for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), + FEnd = Ovl->function_end(); + F != FEnd; ++F) { + if (FunctionDecl *Fn = dyn_cast(*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 \. +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(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(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 diff --git a/test/SemaCXX/new-delete.cpp b/test/SemaCXX/new-delete.cpp index d9b471362f..89c33474bf 100644 --- a/test/SemaCXX/new-delete.cpp +++ b/test/SemaCXX/new-delete.cpp @@ -1,5 +1,7 @@ // RUN: clang -fsyntax-only -verify %s +#include + 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. }