зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
291fbde2da
Коммит
7adb10fa31
|
@ -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();
|
Загрузка…
Ссылка в новой задаче