When implicitly declaring operators new, new[], delete, and delete[],

give them the appropriate exception specifications. This,
unfortunately, requires us to maintain and/or implicitly generate
handles to namespace "std" and the class "std::bad_alloc". However,
every other approach I've come up with was more hackish, and this
standard requirement itself is quite the hack.

Fixes PR4829.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81939 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Douglas Gregor 2009-09-15 22:30:29 +00:00
Родитель 291fbde2da
Коммит 7adb10fa31
7 изменённых файлов: 126 добавлений и 25 удалений

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

@ -193,11 +193,11 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
ExternalSource(0), CurContext(0), PreDeclaratorDC(0),
CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()),
StdNamespace(0), StdBadAlloc(0),
GlobalNewDeleteDeclared(false), ExprEvalContext(PotentiallyEvaluated),
CompleteTranslationUnit(CompleteTranslationUnit),
NumSFINAEErrors(0), CurrentInstantiationScope(0) {
StdNamespace = 0;
TUScope = 0;
if (getLangOptions().CPlusPlus)
FieldCollector.reset(new CXXFieldCollector());

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

@ -305,10 +305,13 @@ public:
/// For example, user-defined classes, built-in "id" type, etc.
Scope *TUScope;
/// The C++ "std" namespace, where the standard library resides. Cached here
/// by GetStdNamespace
/// \brief The C++ "std" namespace, where the standard library resides.
NamespaceDecl *StdNamespace;
/// \brief The C++ "std::bad_alloc" class, which is defined by the C++
/// standard library.
CXXRecordDecl *StdBadAlloc;
/// A flag to remember whether the implicit forms of operator new and delete
/// have been declared.
bool GlobalNewDeleteDeclared;
@ -1337,8 +1340,6 @@ public:
void WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethod,
ObjCMethodDecl *IntfMethod);
NamespaceDecl *GetStdNamespace();
bool isPropertyReadonly(ObjCPropertyDecl *PropertyDecl,
ObjCInterfaceDecl *IDecl);

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

@ -521,18 +521,6 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned bid,
return New;
}
/// GetStdNamespace - This method gets the C++ "std" namespace. This is where
/// everything from the standard library is defined.
NamespaceDecl *Sema::GetStdNamespace() {
if (!StdNamespace) {
IdentifierInfo *StdIdent = &PP.getIdentifierTable().get("std");
DeclContext *Global = Context.getTranslationUnitDecl();
Decl *Std = LookupQualifiedName(Global, StdIdent, LookupNamespaceName);
StdNamespace = dyn_cast_or_null<NamespaceDecl>(Std);
}
return StdNamespace;
}
/// MergeTypeDefDecl - We just parsed a typedef 'New' which has the
/// same name and scope as a previous declaration 'Old'. Figure out
/// how to resolve this situation, merging decls or emitting
@ -3990,7 +3978,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
DeclContext *SearchDC = CurContext;
DeclContext *DC = CurContext;
NamedDecl *PrevDecl = 0;
bool isStdBadAlloc = false;
bool Invalid = false;
if (Name && SS.isNotEmpty()) {
@ -4067,6 +4055,19 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
PrevDecl = 0;
}
if (getLangOptions().CPlusPlus && Name && DC && StdNamespace &&
DC->Equals(StdNamespace) && Name->isStr("bad_alloc")) {
// This is a declaration of or a reference to "std::bad_alloc".
isStdBadAlloc = true;
if (!PrevDecl && StdBadAlloc) {
// std::bad_alloc has been implicitly declared (but made invisible to
// name lookup). Fill in this implicit declaration as the previous
// declaration, so that the declarations get chained appropriately.
PrevDecl = StdBadAlloc;
}
}
if (PrevDecl) {
// Check whether the previous declaration is usable.
(void)DiagnoseUseOfDecl(PrevDecl, NameLoc);
@ -4253,11 +4254,14 @@ CreateNewDecl:
// FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.:
// struct X { int A; } D; D should chain to X.
if (getLangOptions().CPlusPlus)
if (getLangOptions().CPlusPlus) {
// FIXME: Look for a way to use RecordDecl for simple structs.
New = CXXRecordDecl::Create(Context, Kind, SearchDC, Loc, Name, KWLoc,
cast_or_null<CXXRecordDecl>(PrevDecl));
else
if (isStdBadAlloc && (!StdBadAlloc || StdBadAlloc->isImplicit()))
StdBadAlloc = cast<CXXRecordDecl>(New);
} else
New = RecordDecl::Create(Context, Kind, SearchDC, Loc, Name, KWLoc,
cast_or_null<RecordDecl>(PrevDecl));
}

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

@ -2374,6 +2374,21 @@ Sema::DeclPtrTy Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
Diag(PrevDecl->getLocation(), diag::note_previous_definition);
Namespc->setInvalidDecl();
// Continue on to push Namespc as current DeclContext and return it.
} else if (II->isStr("std") &&
CurContext->getLookupContext()->isTranslationUnit()) {
// This is the first "real" definition of the namespace "std", so update
// our cache of the "std" namespace to point at this definition.
if (StdNamespace) {
// We had already defined a dummy namespace "std". Link this new
// namespace definition to the dummy namespace "std".
StdNamespace->setNextNamespace(Namespc);
StdNamespace->setLocation(IdentLoc);
Namespc->setOriginalNamespace(StdNamespace->getOriginalNamespace());
}
// Make our StdNamespace cache point at the first real definition of the
// "std" namespace.
StdNamespace = Namespc;
}
PushOnScopeChains(Namespc, DeclRegionScope);

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

@ -61,8 +61,7 @@ Sema::ActOnCXXOperatorFunctionIdExpr(Scope *S, SourceLocation OperatorLoc,
Action::OwningExprResult
Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
bool isType, void *TyOrExpr, SourceLocation RParenLoc) {
NamespaceDecl *StdNs = GetStdNamespace();
if (!StdNs)
if (!StdNamespace)
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
if (isType)
@ -70,7 +69,8 @@ Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
TyOrExpr = GetTypeFromParser(TyOrExpr).getAsOpaquePtr();
IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info");
Decl *TypeInfoDecl = LookupQualifiedName(StdNs, TypeInfoII, LookupTagName);
Decl *TypeInfoDecl = LookupQualifiedName(StdNamespace, TypeInfoII,
LookupTagName);
RecordDecl *TypeInfoRecordDecl = dyn_cast_or_null<RecordDecl>(TypeInfoDecl);
if (!TypeInfoRecordDecl)
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
@ -661,12 +661,49 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
void Sema::DeclareGlobalNewDelete() {
if (GlobalNewDeleteDeclared)
return;
// C++ [basic.std.dynamic]p2:
// [...] The following allocation and deallocation functions (18.4) are
// implicitly declared in global scope in each translation unit of a
// program
//
// 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();
//
// These implicit declarations introduce only the function names operator
// new, operator new[], operator delete, operator delete[].
//
// Here, we need to refer to std::bad_alloc, so we will implicitly declare
// "std" or "bad_alloc" as necessary to form the exception specification.
// However, we do not make these implicit declarations visible to name
// lookup.
if (!StdNamespace) {
// The "std" namespace has not yet been defined, so build one implicitly.
StdNamespace = NamespaceDecl::Create(Context,
Context.getTranslationUnitDecl(),
SourceLocation(),
&PP.getIdentifierTable().get("std"));
StdNamespace->setImplicit(true);
}
if (!StdBadAlloc) {
// The "std::bad_alloc" class has not yet been declared, so build it
// implicitly.
StdBadAlloc = CXXRecordDecl::Create(Context, TagDecl::TK_class,
StdNamespace,
SourceLocation(),
&PP.getIdentifierTable().get("bad_alloc"),
SourceLocation(), 0);
StdBadAlloc->setImplicit(true);
}
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);
@ -700,7 +737,19 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
}
}
QualType FnType = Context.getFunctionType(Return, &Argument, 1, false, 0);
QualType BadAllocType;
bool HasBadAllocExceptionSpec
= (Name.getCXXOverloadedOperator() == OO_New ||
Name.getCXXOverloadedOperator() == OO_Array_New);
if (HasBadAllocExceptionSpec) {
assert(StdBadAlloc && "Must have std::bad_alloc declared");
BadAllocType = Context.getTypeDeclType(StdBadAlloc);
}
QualType FnType = Context.getFunctionType(Return, &Argument, 1, false, 0,
true, false,
HasBadAllocExceptionSpec? 1 : 0,
&BadAllocType);
FunctionDecl *Alloc =
FunctionDecl::Create(Context, GlobalCtx, SourceLocation(), Name,
FnType, /*DInfo=*/0, FunctionDecl::None, false, true);

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

@ -0,0 +1,7 @@
// RUN: clang -fsyntax-only -verify %s
int *use_new(int N) {
return new int [N];
}
int std = 17;

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

@ -0,0 +1,25 @@
// RUN: clang-cc -fsyntax-only -verify %s
int *use_new(int N) {
if (N == 1)
return new int;
return new int [N];
}
void use_delete(int* ip, int N) {
if (N == 1)
delete ip;
else
delete [] ip;
}
namespace std {
class bad_alloc { };
typedef __SIZE_TYPE__ size_t;
}
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();