]> granicus.if.org Git - clang/commitdiff
Work around an annoying, non-standard optimization in the glibc
authorDouglas Gregor <dgregor@apple.com>
Fri, 12 Feb 2010 07:32:17 +0000 (07:32 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 12 Feb 2010 07:32:17 +0000 (07:32 +0000)
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

lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExceptionSpec.cpp
test/SemaCXX/Inputs/lit.local.cfg [new file with mode: 0644]
test/SemaCXX/Inputs/malloc.h [new file with mode: 0644]
test/SemaCXX/builtin-exception-spec.cpp [new file with mode: 0644]

index 02ffb8ccb44b1ebb69b42ab6c4acea9df82f57b0..8db3eb1713202aae83eb7645deccd628bbd0a921 100644 (file)
@@ -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,
index 00bc453e84086a4a4e9edbc7af28e1548321c7a2..a062724ee4125f461b9ff14955cb0ca07b7f697b 100644 (file)
@@ -356,9 +356,7 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) {
     }
   }
 
-  if (CheckEquivalentExceptionSpec(
-          Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
-          New->getType()->getAs<FunctionProtoType>(), New->getLocation()))
+  if (CheckEquivalentExceptionSpec(Old, New))
     Invalid = true;
 
   return Invalid;
index d0718d020b2bc7b1c95cbf349dcd980d8ecdcc15..9be411b552adf0a999a9704bfb754f3fb3825c83 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #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);
diff --git a/test/SemaCXX/Inputs/lit.local.cfg b/test/SemaCXX/Inputs/lit.local.cfg
new file mode 100644 (file)
index 0000000..e6f55ee
--- /dev/null
@@ -0,0 +1 @@
+config.suffixes = []
diff --git a/test/SemaCXX/Inputs/malloc.h b/test/SemaCXX/Inputs/malloc.h
new file mode 100644 (file)
index 0000000..c54d621
--- /dev/null
@@ -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 (file)
index 0000000..324d20e
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify %s
+#include <malloc.h>
+
+extern "C" {
+void *malloc(__SIZE_TYPE__);
+}