diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-11-14 00:37:55 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-11-14 00:37:55 +0000 |
commit | 6403e937d65dbd3c48ff52f1df6eb579f76d53e6 (patch) | |
tree | 8b5742ce3adb89dce49f19144294716dcccec416 /clang | |
parent | 283bc2ed28f423c29ab57a2b491d2ebdb53a4b12 (diff) | |
download | bcm5719-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.h | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 27 | ||||
-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.cpp | 47 |
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 |