From 99439d474e7cb48497a2da4c35f70cdc1d5b153f Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Tue, 15 Mar 2011 19:52:30 +0000 Subject: [PATCH] Implement a hack to work around the changing exception specification of operator new in C++0x. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127688 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 3 ++- lib/Sema/SemaExceptionSpec.cpp | 40 ++++++++++++++++++++++++++++-- test/CXX/except/except.spec/p3.cpp | 11 ++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 51cff56960..394a57ae25 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -758,7 +758,8 @@ public: const FunctionProtoType *New, SourceLocation NewLoc, bool *MissingExceptionSpecification = 0, bool *MissingEmptyExceptionSpecification = 0, - bool AllowNoexceptAllMatchWithNoSpec = false); + bool AllowNoexceptAllMatchWithNoSpec = false, + bool IsOperatorNew = false); bool CheckExceptionSpecSubset( const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Superset, SourceLocation SuperLoc, diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 285f400b07..1d21b9760c 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -96,6 +96,8 @@ bool Sema::CheckDistantExceptionSpec(QualType T) { } bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { + OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator(); + bool IsOperatorNew = OO == OO_New || OO == OO_Array_New; bool MissingExceptionSpecification = false; bool MissingEmptyExceptionSpecification = false; if (!CheckEquivalentExceptionSpec(PDiag(diag::err_mismatched_exception_spec), @@ -106,7 +108,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { New->getLocation(), &MissingExceptionSpecification, &MissingEmptyExceptionSpecification, - /*AllowNoexceptAllMatchWithNoSpec=*/true)) + /*AllowNoexceptAllMatchWithNoSpec=*/true, + IsOperatorNew)) return false; // The failure was something other than an empty exception @@ -272,7 +275,8 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, SourceLocation NewLoc, bool *MissingExceptionSpecification, bool*MissingEmptyExceptionSpecification, - bool AllowNoexceptAllMatchWithNoSpec) { + bool AllowNoexceptAllMatchWithNoSpec, + bool IsOperatorNew) { // Just completely ignore this under -fno-exceptions. if (!getLangOptions().CXXExceptions) return false; @@ -374,6 +378,38 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, if (OldNonThrowing && NewNonThrowing) return false; + // As a special compatibility feature, under C++0x we accept no spec and + // throw(std::bad_alloc) as equivalent for operator new and operator new[]. + // This is because the implicit declaration changed, but old code would break. + if (getLangOptions().CPlusPlus0x && IsOperatorNew) { + const FunctionProtoType *WithExceptions = 0; + if (OldEST == EST_None && NewEST == EST_Dynamic) + WithExceptions = New; + else if (OldEST == EST_Dynamic && NewEST == EST_None) + WithExceptions = Old; + if (WithExceptions && WithExceptions->getNumExceptions() == 1) { + // One has no spec, the other throw(something). If that something is + // std::bad_alloc, all conditions are met. + QualType Exception = *WithExceptions->exception_begin(); + if (CXXRecordDecl *ExRecord = Exception->getAsCXXRecordDecl()) { + IdentifierInfo* Name = ExRecord->getIdentifier(); + if (Name && Name->getName() == "bad_alloc") { + // It's called bad_alloc, but is it in std? + DeclContext* DC = ExRecord->getDeclContext(); + while (DC && !isa(DC)) + DC = DC->getParent(); + if (DC) { + NamespaceDecl* NS = cast(DC); + IdentifierInfo* NSName = NS->getIdentifier(); + if (NSName && NSName->getName() == "std" && + isa(NS->getParent())) + return false; + } + } + } + } + } + // At this point, the only remaining valid case is two matching dynamic // specifications. We return here unless both specifications are dynamic. if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) { diff --git a/test/CXX/except/except.spec/p3.cpp b/test/CXX/except/except.spec/p3.cpp index e751f55ec3..10e027b719 100644 --- a/test/CXX/except/except.spec/p3.cpp +++ b/test/CXX/except/except.spec/p3.cpp @@ -92,3 +92,14 @@ extern void (*r20)() noexcept(false); // expected-error {{does not match}} extern void (*r21)() throw(int); // expected-note {{previous declaration}} extern void (*r21)() noexcept(true); // expected-error {{does not match}} + + +// As a very special workaround, we allow operator new to match no spec +// with a throw(bad_alloc) spec, because C++0x makes an incompatible change +// here. +namespace std { class bad_alloc {}; } +void* operator new(unsigned long) throw(std::bad_alloc); +void* operator new(unsigned long); +void* operator new[](unsigned long) throw(std::bad_alloc); +void* operator new[](unsigned long); + -- 2.40.0