diff options
author | Douglas Gregor <dgregor@apple.com> | 2010-02-12 07:32:17 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2010-02-12 07:32:17 +0000 |
commit | e13ad837709cd7730e18d8af1cf6b7d35a56d6b7 (patch) | |
tree | ca2e49deee5dc33eb0730ebf041c66f26a96d39b /lib/Sema/SemaExceptionSpec.cpp | |
parent | 96058953c72355efb266abe8e333db4f5715dbd2 (diff) |
Work around an annoying, non-standard optimization in the glibc
headers, where malloc (and many other libc functions) are declared
with empty throw specifications, e.g.,
extern void *malloc (__SIZE_TYPE__ __size) throw () __attribute__
((__malloc__)) ;
The C++ standard doesn't seem to allow this, and redeclaring malloc as
the standard permits (as follows) resulted in Clang (rightfully!)
complaining about mis-matched exception specifications.
void *malloc(size_t size);
We work around this by silently propagating an empty throw
specification "throw()" from a function with C linkage declared in a
system header to a redeclaration that has no throw specifier.
Ick.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95969 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaExceptionSpec.cpp')
-rw-r--r-- | lib/Sema/SemaExceptionSpec.cpp | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index d0718d020b..9be411b552 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -12,10 +12,11 @@ //===----------------------------------------------------------------------===// #include "Sema.h" -#include "clang/Basic/Diagnostic.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallPtrSet.h" namespace clang { @@ -92,6 +93,52 @@ bool Sema::CheckDistantExceptionSpec(QualType T) { return FnT->hasExceptionSpec(); } +bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { + bool MissingEmptyExceptionSpecification = false; + if (!CheckEquivalentExceptionSpec(diag::err_mismatched_exception_spec, + diag::note_previous_declaration, + Old->getType()->getAs<FunctionProtoType>(), + Old->getLocation(), + New->getType()->getAs<FunctionProtoType>(), + New->getLocation(), + &MissingEmptyExceptionSpecification)) + return false; + + // The failure was something other than an empty exception + // specification; return an error. + if (!MissingEmptyExceptionSpecification) + return true; + + // The new function declaration is only missing an empty exception + // specification "throw()". If the throw() specification came from a + // function in a system header that has C linkage, just add an empty + // exception specification to the "new" declaration. This is an + // egregious workaround for glibc, which adds throw() specifications + // to many libc functions as an optimization. Unfortunately, that + // optimization isn't permitted by the C++ standard, so we're forced + // to work around it here. + if (isa<FunctionProtoType>(New->getType()) && + Context.getSourceManager().isInSystemHeader(Old->getLocation()) && + Old->isExternC()) { + const FunctionProtoType *NewProto + = cast<FunctionProtoType>(New->getType()); + QualType NewType = Context.getFunctionType(NewProto->getResultType(), + NewProto->arg_type_begin(), + NewProto->getNumArgs(), + NewProto->isVariadic(), + NewProto->getTypeQuals(), + true, false, 0, 0, + NewProto->getNoReturnAttr(), + NewProto->getCallConv()); + New->setType(NewType); + return false; + } + + Diag(New->getLocation(), diag::err_mismatched_exception_spec); + Diag(Old->getLocation(), diag::note_previous_declaration); + return true; +} + /// CheckEquivalentExceptionSpec - Check if the two types have equivalent /// exception specifications. Exception specifications are equivalent if /// they allow exactly the same set of exception types. It does not matter how @@ -111,12 +158,26 @@ bool Sema::CheckEquivalentExceptionSpec( bool Sema::CheckEquivalentExceptionSpec( const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Old, SourceLocation OldLoc, - const FunctionProtoType *New, SourceLocation NewLoc) { + const FunctionProtoType *New, SourceLocation NewLoc, + bool *MissingEmptyExceptionSpecification) { + if (MissingEmptyExceptionSpecification) + *MissingEmptyExceptionSpecification = false; + bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec(); bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec(); if (OldAny && NewAny) return false; if (OldAny || NewAny) { + if (MissingEmptyExceptionSpecification && Old->hasExceptionSpec() && + !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0 && + !New->hasExceptionSpec()) { + // The old type has a throw() exception specification and the + // new type has no exception specification, and the caller asked + // to handle this itself. + *MissingEmptyExceptionSpecification = true; + return true; + } + Diag(NewLoc, DiagID); if (NoteID.getDiagID() != 0) Diag(OldLoc, NoteID); |