From: Douglas Gregor Date: Fri, 12 Feb 2010 07:32:17 +0000 (+0000) Subject: Work around an annoying, non-standard optimization in the glibc X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e13ad837709cd7730e18d8af1cf6b7d35a56d6b7;p=clang 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 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 02ffb8ccb4..8db3eb1713 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -657,13 +657,15 @@ public: static QualType GetTypeFromParser(TypeTy *Ty, TypeSourceInfo **TInfo = 0); bool CheckSpecifiedExceptionType(QualType T, const SourceRange &Range); bool CheckDistantExceptionSpec(QualType T); + bool CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New); bool CheckEquivalentExceptionSpec( const FunctionProtoType *Old, SourceLocation OldLoc, const FunctionProtoType *New, SourceLocation NewLoc); bool CheckEquivalentExceptionSpec( const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Old, SourceLocation OldLoc, - const FunctionProtoType *New, SourceLocation NewLoc); + const FunctionProtoType *New, SourceLocation NewLoc, + bool *MissingEmptyExceptionSpecification = 0); bool CheckExceptionSpecSubset( const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Superset, SourceLocation SuperLoc, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 00bc453e84..a062724ee4 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -356,9 +356,7 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) { } } - if (CheckEquivalentExceptionSpec( - Old->getType()->getAs(), Old->getLocation(), - New->getType()->getAs(), New->getLocation())) + if (CheckEquivalentExceptionSpec(Old, New)) Invalid = true; return Invalid; 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(), + Old->getLocation(), + New->getType()->getAs(), + 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(New->getType()) && + Context.getSourceManager().isInSystemHeader(Old->getLocation()) && + Old->isExternC()) { + const FunctionProtoType *NewProto + = cast(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); diff --git a/test/SemaCXX/Inputs/lit.local.cfg b/test/SemaCXX/Inputs/lit.local.cfg new file mode 100644 index 0000000000..e6f55eef7a --- /dev/null +++ b/test/SemaCXX/Inputs/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = [] diff --git a/test/SemaCXX/Inputs/malloc.h b/test/SemaCXX/Inputs/malloc.h new file mode 100644 index 0000000000..c54d621501 --- /dev/null +++ b/test/SemaCXX/Inputs/malloc.h @@ -0,0 +1,3 @@ +extern "C" { +extern void *malloc (__SIZE_TYPE__ __size) throw () __attribute__ ((__malloc__)) ; +} diff --git a/test/SemaCXX/builtin-exception-spec.cpp b/test/SemaCXX/builtin-exception-spec.cpp new file mode 100644 index 0000000000..324d20ea6a --- /dev/null +++ b/test/SemaCXX/builtin-exception-spec.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify %s +#include + +extern "C" { +void *malloc(__SIZE_TYPE__); +}