summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2014-11-14 00:37:55 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2014-11-14 00:37:55 +0000
commit6403e937d65dbd3c48ff52f1df6eb579f76d53e6 (patch)
tree8b5742ce3adb89dce49f19144294716dcccec416 /clang
parent283bc2ed28f423c29ab57a2b491d2ebdb53a4b12 (diff)
downloadbcm5719-llvm-6403e937d65dbd3c48ff52f1df6eb579f76d53e6.tar.gz
bcm5719-llvm-6403e937d65dbd3c48ff52f1df6eb579f76d53e6.zip
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. llvm-svn: 221955
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/Sema/Sema.h4
-rw-r--r--clang/lib/Parse/ParseDecl.cpp3
-rw-r--r--clang/lib/Sema/SemaExceptionSpec.cpp27
-rw-r--r--clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp (renamed from clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp)0
-rw-r--r--clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp47
5 files changed, 80 insertions, 1 deletions
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e5bdce4e6c9..a7d8a241acc 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -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.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8fbe7833a1f..2e30c52df93 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5281,7 +5281,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
// Parse exception-specification[opt].
bool Delayed = D.isFirstDeclarationOfMember() &&
- D.isFunctionDeclaratorAFunctionDeclaration();
+ D.isFunctionDeclaratorAFunctionDeclaration() &&
+ !Actions.isLibstdcxxEagerExceptionSpecHack(D);
ESpecType = tryParseExceptionSpecification(Delayed,
ESpecRange,
DynamicExceptions,
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index c35de6b8eda..7175c016734 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -35,6 +35,33 @@ static const FunctionProtoType *GetUnderlyingFunction(QualType T)
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.
diff --git a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp b/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
index 774745777c1..774745777c1 100644
--- a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist-system-header.cpp
+++ b/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
diff --git a/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
new file mode 100644
index 00000000000..8c7c782c32b
--- /dev/null
+++ b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
@@ -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
OpenPOWER on IntegriCloud