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
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 51cff56..394a57a 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -758,7 +758,8 @@
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 285f400..1d21b97 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -96,6 +96,8 @@
}
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 @@
New->getLocation(),
&MissingExceptionSpecification,
&MissingEmptyExceptionSpecification,
- /*AllowNoexceptAllMatchWithNoSpec=*/true))
+ /*AllowNoexceptAllMatchWithNoSpec=*/true,
+ IsOperatorNew))
return false;
// The failure was something other than an empty exception
@@ -272,7 +275,8 @@
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 @@
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<NamespaceDecl>(DC))
+ DC = DC->getParent();
+ if (DC) {
+ NamespaceDecl* NS = cast<NamespaceDecl>(DC);
+ IdentifierInfo* NSName = NS->getIdentifier();
+ if (NSName && NSName->getName() == "std" &&
+ isa<TranslationUnitDecl>(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 e751f55..10e027b 100644
--- a/test/CXX/except/except.spec/p3.cpp
+++ b/test/CXX/except/except.spec/p3.cpp
@@ -92,3 +92,14 @@
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);
+