r221955 - PR21565 Add an egregious hack to support broken libstdc++ headers that declare

Richard Smith richard-llvm at metafoo.co.uk
Thu Nov 13 16:37:56 PST 2014


Author: rsmith
Date: Thu Nov 13 18:37:55 2014
New Revision: 221955

URL: http://llvm.org/viewvc/llvm-project?rev=221955&view=rev
Log:
PR21565 Add an egregious hack to support broken libstdc++ headers that declare
a member named 'swap' and then expect unqualified lookup for the name 'swap' in
its exception specification to find anything else.

Without delay-parsed exception specifications, this was ill-formed (NDR) by
[basic.scope.class]p1, rule 2. With delay-parsed exception specifications, the
call to 'swap' unambiguously finds the function being declared, which then
fails because the arguments don't work for that function.

Added:
    cfe/trunk/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
      - copied unchanged from r221954, cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp
    cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
Removed:
    cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp
Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Sema/SemaExceptionSpec.cpp

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=221955&r1=221954&r2=221955&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Nov 13 18:37:55 2014
@@ -4109,6 +4109,10 @@ public:
                                    SmallVectorImpl<QualType> &Exceptions,
                                    FunctionProtoType::ExceptionSpecInfo &ESI);
 
+  /// \brief Determine if we're in a case where we need to (incorrectly) eagerly
+  /// parse an exception specification to work around a libstdc++ bug.
+  bool isLibstdcxxEagerExceptionSpecHack(const Declarator &D);
+
   /// \brief Add an exception-specification to the given member function
   /// (or member function template). The exception-specification was parsed
   /// after the method itself was declared.

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=221955&r1=221954&r2=221955&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Thu Nov 13 18:37:55 2014
@@ -5281,7 +5281,8 @@ void Parser::ParseFunctionDeclarator(Dec
 
       // Parse exception-specification[opt].
       bool Delayed = D.isFirstDeclarationOfMember() &&
-                     D.isFunctionDeclaratorAFunctionDeclaration();
+                     D.isFunctionDeclaratorAFunctionDeclaration() &&
+                     !Actions.isLibstdcxxEagerExceptionSpecHack(D);
       ESpecType = tryParseExceptionSpecification(Delayed,
                                                  ESpecRange,
                                                  DynamicExceptions,

Modified: cfe/trunk/lib/Sema/SemaExceptionSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExceptionSpec.cpp?rev=221955&r1=221954&r2=221955&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExceptionSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExceptionSpec.cpp Thu Nov 13 18:37:55 2014
@@ -35,6 +35,33 @@ static const FunctionProtoType *GetUnder
   return T->getAs<FunctionProtoType>();
 }
 
+/// HACK: libstdc++ has a bug where it shadows std::swap with a member
+/// swap function then tries to call std::swap unqualified from the exception
+/// specification of that function. This function detects whether we're in
+/// such a case and turns off delay-parsing of exception specifications.
+bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
+  auto *RD = dyn_cast<CXXRecordDecl>(CurContext);
+
+  // All the problem cases are member functions named "swap" within class
+  // templates declared directly within namespace std.
+  if (!RD || RD->getEnclosingNamespaceContext() != getStdNamespace() ||
+      !RD->getIdentifier() || !RD->getDescribedClassTemplate() ||
+      !D.getIdentifier() || !D.getIdentifier()->isStr("swap"))
+    return false;
+
+  // Only apply this hack within a system header.
+  if (!Context.getSourceManager().isInSystemHeader(D.getLocStart()))
+    return false;
+
+  return llvm::StringSwitch<bool>(RD->getIdentifier()->getName())
+      .Case("array", true)
+      .Case("pair", true)
+      .Case("priority_queue", true)
+      .Case("stack", true)
+      .Case("queue", true)
+      .Default(false);
+}
+
 /// CheckSpecifiedExceptionType - Check if the given type is valid in an
 /// exception specification. Incomplete types, or pointers to incomplete types
 /// other than void are not allowed.

Removed: cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp?rev=221954&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp (removed)
@@ -1,23 +0,0 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wsystem-headers %s
-
-// libstdc++4.6 in debug mode has explicit default constructors.
-// stlport has this for all containers.
-#ifdef BE_THE_HEADER
-#pragma clang system_header
-namespace std {
-namespace __debug {
-template <class T>
-class vector {
-public:
-  explicit vector() {} // expected-warning{{should not be explicit}}
-};
-}
-}
-#else
-
-#define BE_THE_HEADER
-#include __FILE__
-
-struct { int a, b; std::__debug::vector<int> c; } e[] = { {1, 1} }; // expected-note{{used in initialization here}}
-
-#endif

Added: cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp?rev=221955&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp (added)
+++ cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp Thu Nov 13 18:37:55 2014
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions
+
+// This is a test for an egregious hack in Clang that works around
+// an issue with GCC's <utility> implementation. std::pair::swap
+// has an exception specification that makes an unqualified call to
+// swap. This is invalid, because it ends up calling itself with
+// the wrong number of arguments.
+
+#ifdef BE_THE_HEADER
+
+#pragma GCC system_header
+namespace std {
+  template<typename T> void swap(T &, T &);
+
+  template<typename A, typename B> struct pair {
+    void swap(pair &other) noexcept(noexcept(swap(*this, other)));
+  };
+}
+
+#else
+
+#define BE_THE_HEADER
+#include __FILE__
+
+struct X {};
+using PX = std::pair<X, X>;
+using PI = std::pair<int, int>;
+void swap(PX &, PX &) noexcept;
+PX px;
+PI pi;
+
+static_assert(noexcept(px.swap(px)), "");
+static_assert(!noexcept(pi.swap(pi)), "");
+
+namespace sad {
+  template<typename T> void swap(T &, T &);
+
+  template<typename A, typename B> struct pair {
+    void swap(pair &other) noexcept(noexcept(swap(*this, other))); // expected-error {{too many arguments}} expected-note {{declared here}}
+  };
+
+  pair<int, int> pi;
+
+  static_assert(!noexcept(pi.swap(pi)), ""); // expected-note {{in instantiation of}}
+}
+
+#endif





More information about the cfe-commits mailing list