summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hines <srhines@google.com>2018-07-09 10:00:10 -0700
committerStephen Hines <srhines@google.com>2018-07-09 10:00:10 -0700
commit3ec3c6396b642d537717f981ba3615286711951f (patch)
tree11e09e6d00958047f684de1d23fc91c223f1e27e
parent77f176fe2ffb0f38e1aea675d68fce882e8e5bcd (diff)
parente0330b61d74b898ee64922d0a3d50c3562f6ddb1 (diff)
downloadlibcxxabi-3ec3c6396b642d537717f981ba3615286711951f.tar.gz
Merge e0330b6 for LLVM update to 333878
Change-Id: I14188f576bffec5ce6096a6671e71a7ec94b1f25
-rw-r--r--CMakeLists.txt6
-rw-r--r--include/__cxxabi_config.h10
-rw-r--r--src/cxa_default_handlers.cpp17
-rw-r--r--src/cxa_demangle.cpp302
-rw-r--r--src/cxa_exception.cpp11
-rw-r--r--src/cxa_handlers.cpp22
-rw-r--r--src/include/atomic_support.h181
-rw-r--r--src/include/refstring.h9
-rw-r--r--src/private_typeinfo.cpp69
-rw-r--r--test/dynamic_cast.pass.cpp154
-rw-r--r--test/libcxxabi/test/config.py5
-rw-r--r--test/test_demangle.pass.cpp11
-rw-r--r--test/test_exception_storage.pass.cpp5
13 files changed, 679 insertions, 123 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 35eb130..1488737 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,6 +99,7 @@ else()
set(LIBCXXABI_LIBCXX_SRC_DIRS
"${LLVM_MAIN_SRC_DIR}/projects/libcxx"
"${LLVM_MAIN_SRC_DIR}/runtimes/libcxx"
+ "${LLVM_MAIN_SRC_DIR}/../libcxx"
)
endif()
@@ -386,6 +387,10 @@ endif()
# Prevent libc++abi from having library dependencies on libc++
add_definitions(-D_LIBCPP_DISABLE_EXTERN_TEMPLATE)
+# Bring back `std::unexpected`, which is removed in C++17, to support
+# pre-C++17.
+add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS)
+
if (MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
@@ -426,6 +431,7 @@ if (LIBCXXABI_USE_LLVM_UNWINDER OR LLVM_NATIVE_ARCH MATCHES ARM)
${CMAKE_BINARY_DIR}/${LIBCXXABI_LIBUNWIND_INCLUDES}
${LLVM_MAIN_SRC_DIR}/projects/libunwind/include
${LLVM_MAIN_SRC_DIR}/runtimes/libunwind/include
+ ${LLVM_MAIN_SRC_DIR}/../libunwind/include
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
diff --git a/include/__cxxabi_config.h b/include/__cxxabi_config.h
index 65b1961..46f5914 100644
--- a/include/__cxxabi_config.h
+++ b/include/__cxxabi_config.h
@@ -60,4 +60,14 @@
#define _LIBCXXABI_WEAK __attribute__((__weak__))
#endif
+#if defined(__clang__)
+#define _LIBCXXABI_COMPILER_CLANG
+#endif
+
+#if __has_attribute(__no_sanitize__) && defined(_LIBCXXABI_COMPILER_CLANG)
+#define _LIBCXXABI_NO_CFI __attribute__((__no_sanitize__("cfi")))
+#else
+#define _LIBCXXABI_NO_CFI
+#endif
+
#endif // ____CXXABI_CONFIG_H
diff --git a/src/cxa_default_handlers.cpp b/src/cxa_default_handlers.cpp
index 8231139..0fa169f 100644
--- a/src/cxa_default_handlers.cpp
+++ b/src/cxa_default_handlers.cpp
@@ -18,6 +18,7 @@
#include "cxa_handlers.hpp"
#include "cxa_exception.hpp"
#include "private_typeinfo.h"
+#include "include/atomic_support.h"
#if !defined(LIBCXXABI_SILENT_TERMINATE)
static const char* cause = "uncaught";
@@ -101,10 +102,6 @@ std::terminate_handler __cxa_terminate_handler = default_terminate_handler;
_LIBCXXABI_DATA_VIS
std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler;
-// In the future these will become:
-// std::atomic<std::terminate_handler> __cxa_terminate_handler(default_terminate_handler);
-// std::atomic<std::unexpected_handler> __cxa_unexpected_handler(default_unexpected_handler);
-
namespace std
{
@@ -113,10 +110,8 @@ set_unexpected(unexpected_handler func) _NOEXCEPT
{
if (func == 0)
func = default_unexpected_handler;
- return __atomic_exchange_n(&__cxa_unexpected_handler, func,
- __ATOMIC_ACQ_REL);
-// Using of C++11 atomics this should be rewritten
-// return __cxa_unexpected_handler.exchange(func, memory_order_acq_rel);
+ return __libcpp_atomic_exchange(&__cxa_unexpected_handler, func,
+ _AO_Acq_Rel);
}
terminate_handler
@@ -124,10 +119,8 @@ set_terminate(terminate_handler func) _NOEXCEPT
{
if (func == 0)
func = default_terminate_handler;
- return __atomic_exchange_n(&__cxa_terminate_handler, func,
- __ATOMIC_ACQ_REL);
-// Using of C++11 atomics this should be rewritten
-// return __cxa_terminate_handler.exchange(func, memory_order_acq_rel);
+ return __libcpp_atomic_exchange(&__cxa_terminate_handler, func,
+ _AO_Acq_Rel);
}
}
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index 9253e83..d70ffdf 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -10,7 +10,6 @@
// FIXME: (possibly) incomplete list of features that clang mangles that this
// file does not yet support:
// - C++ modules TS
-// - All C++14 and C++17 features
#define _LIBCPP_NO_EXCEPTIONS
@@ -105,6 +104,12 @@ class OutputStream {
public:
OutputStream(char *StartBuf, size_t Size)
: Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
+ OutputStream() = default;
+ void reset(char *Buffer_, size_t BufferCapacity_) {
+ CurrentPosition = 0;
+ Buffer = Buffer_;
+ BufferCapacity = BufferCapacity_;
+ }
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
/// into the pack that we're currently printing.
@@ -161,6 +166,7 @@ public:
class Node {
public:
enum Kind : unsigned char {
+ KNodeArrayNode,
KDotSuffix,
KVendorExtQualType,
KQualType,
@@ -184,7 +190,8 @@ public:
KSpecialName,
KCtorVtableSpecialName,
KQualifiedName,
- KEmptyName,
+ KNestedName,
+ KLocalName,
KVectorType,
KParameterPack,
KTemplateArgumentPack,
@@ -323,6 +330,14 @@ public:
}
};
+struct NodeArrayNode : Node {
+ NodeArray Array;
+ NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {}
+ void printLeft(OutputStream &S) const override {
+ Array.printWithComma(S);
+ }
+};
+
class DotSuffix final : public Node {
const Node *Prefix;
const StringView Suffix;
@@ -462,11 +477,11 @@ public:
}
};
-class AbiTagAttr final : public Node {
- const Node* Base;
+struct AbiTagAttr : Node {
+ Node *Base;
StringView Tag;
-public:
- AbiTagAttr(const Node* Base_, StringView Tag_)
+
+ AbiTagAttr(Node* Base_, StringView Tag_)
: Node(KAbiTagAttr, Base_->RHSComponentCache,
Base_->ArrayCache, Base_->FunctionCache),
Base(Base_), Tag(Tag_) {}
@@ -796,8 +811,8 @@ public:
};
class FunctionEncoding final : public Node {
- const Node *Ret;
- const Node *Name;
+ Node *Ret;
+ Node *Name;
NodeArray Params;
Node *Attrs;
Qualifiers CVQuals;
@@ -812,6 +827,11 @@ public:
Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_),
CVQuals(CVQuals_), RefQual(RefQual_) {}
+ Qualifiers getCVQuals() const { return CVQuals; }
+ FunctionRefQual getRefQual() const { return RefQual; }
+ NodeArray getParams() const { return Params; }
+ Node *getReturnType() const { return Ret; }
+
bool hasRHSComponentSlow(OutputStream &) const override { return true; }
bool hasFunctionSlow(OutputStream &) const override { return true; }
@@ -893,6 +913,36 @@ public:
}
};
+struct NestedName : Node {
+ Node *Qual;
+ Node *Name;
+
+ NestedName(Node *Qual_, Node *Name_)
+ : Node(KNestedName), Qual(Qual_), Name(Name_) {}
+
+ StringView getBaseName() const override { return Name->getBaseName(); }
+
+ void printLeft(OutputStream &S) const override {
+ Qual->print(S);
+ S += "::";
+ Name->print(S);
+ }
+};
+
+struct LocalName : Node {
+ Node *Encoding;
+ Node *Entity;
+
+ LocalName(Node *Encoding_, Node *Entity_)
+ : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {}
+
+ void printLeft(OutputStream &S) const override {
+ Encoding->print(S);
+ S += "::";
+ Entity->print(S);
+ }
+};
+
class QualifiedName final : public Node {
// qualifier::name
const Node *Qualifier;
@@ -911,12 +961,6 @@ public:
}
};
-class EmptyName : public Node {
-public:
- EmptyName() : Node(KEmptyName) {}
- void printLeft(OutputStream &) const override {}
-};
-
class VectorType final : public Node {
const Node *BaseType;
const NodeOrString Dimension;
@@ -1140,12 +1184,11 @@ struct ForwardTemplateReference : Node {
}
};
-class NameWithTemplateArgs final : public Node {
+struct NameWithTemplateArgs : Node {
// name<template_args>
Node *Name;
Node *TemplateArgs;
-public:
NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_)
: Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {}
@@ -1172,10 +1215,9 @@ public:
}
};
-class StdQualifiedName final : public Node {
+struct StdQualifiedName : Node {
Node *Child;
-public:
StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {}
StringView getBaseName() const override { return Child->getBaseName(); }
@@ -1686,6 +1728,55 @@ public:
}
};
+struct FoldExpr : Expr {
+ Node *Pack, *Init;
+ StringView OperatorName;
+ bool IsLeftFold;
+
+ FoldExpr(bool IsLeftFold_, StringView OperatorName_, Node *Pack_, Node *Init_)
+ : Pack(Pack_), Init(Init_), OperatorName(OperatorName_),
+ IsLeftFold(IsLeftFold_) {}
+
+ void printLeft(OutputStream &S) const override {
+ auto PrintPack = [&] {
+ S += '(';
+ ParameterPackExpansion(Pack).print(S);
+ S += ')';
+ };
+
+ S += '(';
+
+ if (IsLeftFold) {
+ // init op ... op pack
+ if (Init != nullptr) {
+ Init->print(S);
+ S += ' ';
+ S += OperatorName;
+ S += ' ';
+ }
+ // ... op pack
+ S += "... ";
+ S += OperatorName;
+ S += ' ';
+ PrintPack();
+ } else { // !IsLeftFold
+ // pack op ...
+ PrintPack();
+ S += ' ';
+ S += OperatorName;
+ S += " ...";
+ // pack op ... op init
+ if (Init != nullptr) {
+ S += ' ';
+ S += OperatorName;
+ S += ' ';
+ Init->print(S);
+ }
+ }
+ S += ')';
+ }
+};
+
class ThrowExpr : public Expr {
const Node *Op;
@@ -1830,14 +1921,17 @@ public:
BlockList->Current - N);
}
- ~BumpPointerAllocator() {
+ void reset() {
while (BlockList) {
BlockMeta* Tmp = BlockList;
BlockList = BlockList->Next;
if (reinterpret_cast<char*>(Tmp) != InitialBuffer)
delete[] reinterpret_cast<char*>(Tmp);
}
+ BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};
}
+
+ ~BumpPointerAllocator() { reset(); }
};
template <class T, size_t N>
@@ -1985,6 +2079,18 @@ struct Db {
Db(const char *First_, const char *Last_) : First(First_), Last(Last_) {}
+ void reset(const char *First_, const char *Last_) {
+ First = First_;
+ Last = Last_;
+ Names.clear();
+ Subs.clear();
+ TemplateParams.clear();
+ ParsingLambdaParams = false;
+ TryToParseTemplateArgs = true;
+ PermitForwardTemplateReferences = false;
+ ASTAllocator.reset();
+ }
+
template <class T, class... Args> T *make(Args &&... args) {
return new (ASTAllocator.allocate(sizeof(T)))
T(std::forward<Args>(args)...);
@@ -2054,6 +2160,7 @@ struct Db {
Node *parseNewExpr();
Node *parseConversionExpr();
Node *parseBracedExpr();
+ Node *parseFoldExpr();
/// Parse the <type> production.
Node *parseType();
@@ -2178,7 +2285,7 @@ Node *Db::parseLocalName(NameState *State) {
if (consumeIf('s')) {
First = parse_discriminator(First, Last);
- return make<QualifiedName>(Encoding, make<NameType>("string literal"));
+ return make<LocalName>(Encoding, make<NameType>("string literal"));
}
if (consumeIf('d')) {
@@ -2188,14 +2295,14 @@ Node *Db::parseLocalName(NameState *State) {
Node *N = parseName(State);
if (N == nullptr)
return nullptr;
- return make<QualifiedName>(Encoding, N);
+ return make<LocalName>(Encoding, N);
}
Node *Entity = parseName(State);
if (Entity == nullptr)
return nullptr;
First = parse_discriminator(First, Last);
- return make<QualifiedName>(Encoding, Entity);
+ return make<LocalName>(Encoding, Entity);
}
// <unscoped-name> ::= <unqualified-name>
@@ -2630,6 +2737,8 @@ Node *Db::parseCtorDtorName(Node *&SoFar, NameState *State) {
// ::= <prefix> <data-member-prefix>
// extension ::= L
//
+// <data-member-prefix> := <member source-name> [<template-args>] M
+//
// <template-prefix> ::= <prefix> <template unqualified-name>
// ::= <template-param>
// ::= <substitution>
@@ -2649,7 +2758,7 @@ Node *Db::parseNestedName(NameState *State) {
Node *SoFar = nullptr;
auto PushComponent = [&](Node *Comp) {
- if (SoFar) SoFar = make<QualifiedName>(SoFar, Comp);
+ if (SoFar) SoFar = make<NestedName>(SoFar, Comp);
else SoFar = Comp;
if (State) State->EndsWithTemplateArgs = false;
};
@@ -2660,6 +2769,13 @@ Node *Db::parseNestedName(NameState *State) {
while (!consumeIf('E')) {
consumeIf('L'); // extension
+ // <data-member-prefix> := <member source-name> [<template-args>] M
+ if (consumeIf('M')) {
+ if (SoFar == nullptr)
+ return nullptr;
+ continue;
+ }
+
// ::= <template-param>
if (look() == 'T') {
Node *TP = parseTemplateParam();
@@ -3789,6 +3905,76 @@ Node *Db::parseBracedExpr() {
return parseExpr();
}
+// (not yet in the spec)
+// <fold-expr> ::= fL <binary-operator-name> <expression> <expression>
+// ::= fR <binary-operator-name> <expression> <expression>
+// ::= fl <binary-operator-name> <expression>
+// ::= fr <binary-operator-name> <expression>
+Node *Db::parseFoldExpr() {
+ if (!consumeIf('f'))
+ return nullptr;
+
+ char FoldKind = look();
+ bool IsLeftFold, HasInitializer;
+ HasInitializer = FoldKind == 'L' || FoldKind == 'R';
+ if (FoldKind == 'l' || FoldKind == 'L')
+ IsLeftFold = true;
+ else if (FoldKind == 'r' || FoldKind == 'R')
+ IsLeftFold = false;
+ else
+ return nullptr;
+ ++First;
+
+ // FIXME: This map is duplicated in parseOperatorName and parseExpr.
+ StringView OperatorName;
+ if (consumeIf("aa")) OperatorName = "&&";
+ else if (consumeIf("an")) OperatorName = "&";
+ else if (consumeIf("aN")) OperatorName = "&=";
+ else if (consumeIf("aS")) OperatorName = "=";
+ else if (consumeIf("cm")) OperatorName = ",";
+ else if (consumeIf("ds")) OperatorName = ".*";
+ else if (consumeIf("dv")) OperatorName = "/";
+ else if (consumeIf("dV")) OperatorName = "/=";
+ else if (consumeIf("eo")) OperatorName = "^";
+ else if (consumeIf("eO")) OperatorName = "^=";
+ else if (consumeIf("eq")) OperatorName = "==";
+ else if (consumeIf("ge")) OperatorName = ">=";
+ else if (consumeIf("gt")) OperatorName = ">";
+ else if (consumeIf("le")) OperatorName = "<=";
+ else if (consumeIf("ls")) OperatorName = "<<";
+ else if (consumeIf("lS")) OperatorName = "<<=";
+ else if (consumeIf("lt")) OperatorName = "<";
+ else if (consumeIf("mi")) OperatorName = "-";
+ else if (consumeIf("mI")) OperatorName = "-=";
+ else if (consumeIf("ml")) OperatorName = "*";
+ else if (consumeIf("mL")) OperatorName = "*=";
+ else if (consumeIf("ne")) OperatorName = "!=";
+ else if (consumeIf("oo")) OperatorName = "||";
+ else if (consumeIf("or")) OperatorName = "|";
+ else if (consumeIf("oR")) OperatorName = "|=";
+ else if (consumeIf("pl")) OperatorName = "+";
+ else if (consumeIf("pL")) OperatorName = "+=";
+ else if (consumeIf("rm")) OperatorName = "%";
+ else if (consumeIf("rM")) OperatorName = "%=";
+ else if (consumeIf("rs")) OperatorName = ">>";
+ else if (consumeIf("rS")) OperatorName = ">>=";
+ else return nullptr;
+
+ Node *Pack = parseExpr(), *Init = nullptr;
+ if (Pack == nullptr)
+ return nullptr;
+ if (HasInitializer) {
+ Init = parseExpr();
+ if (Init == nullptr)
+ return nullptr;
+ }
+
+ if (IsLeftFold && Init)
+ std::swap(Pack, Init);
+
+ return make<FoldExpr>(IsLeftFold, OperatorName, Pack, Init);
+}
+
// <expression> ::= <unary operator-name> <expression>
// ::= <binary operator-name> <expression> <expression>
// ::= <ternary operator-name> <expression> <expression> <expression>
@@ -3821,6 +4007,7 @@ Node *Db::parseBracedExpr() {
// ::= ds <expression> <expression> # expr.*expr
// ::= sZ <template-param> # size of a parameter pack
// ::= sZ <function-param> # size of a function parameter pack
+// ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template
// ::= sp <expression> # pack expansion
// ::= tw <expression> # throw expression
// ::= tr # throw with no operand (rethrow)
@@ -3842,8 +4029,12 @@ Node *Db::parseExpr() {
return parseExprPrimary();
case 'T':
return parseTemplateParam();
- case 'f':
- return parseFunctionParam();
+ case 'f': {
+ // Disambiguate a fold expression from a <function-param>.
+ if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2))))
+ return parseFunctionParam();
+ return parseFoldExpr();
+ }
case 'a':
switch (First[1]) {
case 'a':
@@ -4221,9 +4412,22 @@ Node *Db::parseExpr() {
Node *FP = parseFunctionParam();
if (FP == nullptr)
return nullptr;
- return make<EnclosingExpr>("sizeof...", FP, ")");
+ return make<EnclosingExpr>("sizeof... (", FP, ")");
}
return nullptr;
+ case 'P': {
+ First += 2;
+ size_t ArgsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *Arg = parseTemplateArg();
+ if (Arg == nullptr)
+ return nullptr;
+ Names.push_back(Arg);
+ }
+ return make<EnclosingExpr>(
+ "sizeof... (", make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin)),
+ ")");
+ }
}
return nullptr;
case 't':
@@ -4836,6 +5040,22 @@ Node *Db::parse() {
return nullptr;
return Ty;
}
+
+bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S,
+ size_t InitSize) {
+ size_t BufferSize;
+ if (Buf == nullptr) {
+ Buf = static_cast<char *>(std::malloc(InitSize));
+ if (Buf == nullptr)
+ return true;
+ BufferSize = InitSize;
+ } else
+ BufferSize = *N;
+
+ S.reset(Buf, BufferSize);
+ return false;
+}
+
} // unnamed namespace
enum {
@@ -4855,33 +5075,23 @@ __cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) {
return nullptr;
}
- size_t BufSize = Buf != nullptr ? *N : 0;
int InternalStatus = success;
- size_t MangledNameLength = std::strlen(MangledName);
+ Db Parser(MangledName, MangledName + std::strlen(MangledName));
+ OutputStream S;
- Db Parser(MangledName, MangledName + MangledNameLength);
Node *AST = Parser.parse();
if (AST == nullptr)
InternalStatus = invalid_mangled_name;
-
- if (InternalStatus == success) {
+ else if (initializeOutputStream(Buf, N, S, 1024))
+ InternalStatus = memory_alloc_failure;
+ else {
assert(Parser.ForwardTemplateRefs.empty());
-
- if (Buf == nullptr) {
- BufSize = 1024;
- Buf = static_cast<char*>(std::malloc(BufSize));
- }
-
- if (Buf) {
- OutputStream Stream(Buf, BufSize);
- AST->print(Stream);
- Stream += '\0';
- if (N != nullptr)
- *N = Stream.getCurrentPosition();
- Buf = Stream.getBuffer();
- } else
- InternalStatus = memory_alloc_failure;
+ AST->print(S);
+ S += '\0';
+ if (N != nullptr)
+ *N = S.getCurrentPosition();
+ Buf = S.getBuffer();
}
if (Status)
diff --git a/src/cxa_exception.cpp b/src/cxa_exception.cpp
index b77f3a8..397427a 100644
--- a/src/cxa_exception.cpp
+++ b/src/cxa_exception.cpp
@@ -11,8 +11,6 @@
//
//===----------------------------------------------------------------------===//
-#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS
-
#include "cxxabi.h"
#include <exception> // for std::terminate
@@ -20,6 +18,7 @@
#include "cxa_exception.hpp"
#include "cxa_handlers.hpp"
#include "fallback_malloc.h"
+#include "include/atomic_support.h"
#if __has_feature(address_sanitizer)
extern "C" void __asan_handle_no_return(void);
@@ -618,7 +617,7 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() {
if (thrown_object != NULL )
{
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
- __sync_add_and_fetch(&exception_header->referenceCount, 1);
+ std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1));
}
}
@@ -630,12 +629,12 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() {
Requires: If thrown_object is not NULL, it is a native exception.
*/
-void
-__cxa_decrement_exception_refcount(void *thrown_object) throw() {
+_LIBCXXABI_NO_CFI
+void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
if (thrown_object != NULL )
{
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
- if (__sync_sub_and_fetch(&exception_header->referenceCount, size_t(1)) == 0)
+ if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0)
{
if (NULL != exception_header->exceptionDestructor)
exception_header->exceptionDestructor(thrown_object);
diff --git a/src/cxa_handlers.cpp b/src/cxa_handlers.cpp
index 2881a2a..622e93c 100644
--- a/src/cxa_handlers.cpp
+++ b/src/cxa_handlers.cpp
@@ -18,6 +18,7 @@
#include "cxa_handlers.hpp"
#include "cxa_exception.hpp"
#include "private_typeinfo.h"
+#include "include/atomic_support.h"
namespace std
{
@@ -25,10 +26,7 @@ namespace std
unexpected_handler
get_unexpected() _NOEXCEPT
{
- return __sync_fetch_and_add(&__cxa_unexpected_handler, (unexpected_handler)0);
-// The above is safe but overkill on x86
-// Using of C++11 atomics this should be rewritten
-// return __cxa_unexpected_handler.load(memory_order_acq);
+ return __libcpp_atomic_load(&__cxa_unexpected_handler, _AO_Acquire);
}
void
@@ -49,10 +47,7 @@ unexpected()
terminate_handler
get_terminate() _NOEXCEPT
{
- return __sync_fetch_and_add(&__cxa_terminate_handler, (terminate_handler)0);
-// The above is safe but overkill on x86
-// Using of C++11 atomics this should be rewritten
-// return __cxa_terminate_handler.load(memory_order_acq);
+ return __libcpp_atomic_load(&__cxa_terminate_handler, _AO_Acquire);
}
void
@@ -99,8 +94,6 @@ terminate() _NOEXCEPT
__terminate(get_terminate());
}
-// In the future this will become:
-// std::atomic<std::new_handler> __cxa_new_handler(0);
extern "C" {
new_handler __cxa_new_handler = 0;
}
@@ -108,18 +101,13 @@ new_handler __cxa_new_handler = 0;
new_handler
set_new_handler(new_handler handler) _NOEXCEPT
{
- return __atomic_exchange_n(&__cxa_new_handler, handler, __ATOMIC_ACQ_REL);
-// Using of C++11 atomics this should be rewritten
-// return __cxa_new_handler.exchange(handler, memory_order_acq_rel);
+ return __libcpp_atomic_exchange(&__cxa_new_handler, handler, _AO_Acq_Rel);
}
new_handler
get_new_handler() _NOEXCEPT
{
- return __sync_fetch_and_add(&__cxa_new_handler, (new_handler)0);
-// The above is safe but overkill on x86
-// Using of C++11 atomics this should be rewritten
-// return __cxa_new_handler.load(memory_order_acq);
+ return __libcpp_atomic_load(&__cxa_new_handler, _AO_Acquire);
}
} // std
diff --git a/src/include/atomic_support.h b/src/include/atomic_support.h
new file mode 100644
index 0000000..96dbd2c
--- /dev/null
+++ b/src/include/atomic_support.h
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===////
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===////
+
+// FIXME: This file is copied from libcxx/src/include/atomic_support.h. Instead
+// of duplicating the file in libc++abi we should require that the libc++
+// sources are available when building libc++abi.
+
+#ifndef ATOMIC_SUPPORT_H
+#define ATOMIC_SUPPORT_H
+
+#include "__config"
+#include "memory" // for __libcpp_relaxed_load
+
+#if defined(__clang__) && __has_builtin(__atomic_load_n) \
+ && __has_builtin(__atomic_store_n) \
+ && __has_builtin(__atomic_add_fetch) \
+ && __has_builtin(__atomic_exchange_n) \
+ && __has_builtin(__atomic_compare_exchange_n) \
+ && defined(__ATOMIC_RELAXED) \
+ && defined(__ATOMIC_CONSUME) \
+ && defined(__ATOMIC_ACQUIRE) \
+ && defined(__ATOMIC_RELEASE) \
+ && defined(__ATOMIC_ACQ_REL) \
+ && defined(__ATOMIC_SEQ_CST)
+# define _LIBCXXABI_HAS_ATOMIC_BUILTINS
+#elif !defined(__clang__) && defined(_GNUC_VER) && _GNUC_VER >= 407
+# define _LIBCXXABI_HAS_ATOMIC_BUILTINS
+#endif
+
+#if !defined(_LIBCXXABI_HAS_ATOMIC_BUILTINS) && !defined(_LIBCXXABI_HAS_NO_THREADS)
+# if defined(_LIBCPP_WARNING)
+ _LIBCPP_WARNING("Building libc++ without __atomic builtins is unsupported")
+# else
+# warning Building libc++ without __atomic builtins is unsupported
+# endif
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace {
+
+#if defined(_LIBCXXABI_HAS_ATOMIC_BUILTINS) && !defined(_LIBCXXABI_HAS_NO_THREADS)
+
+enum __libcpp_atomic_order {
+ _AO_Relaxed = __ATOMIC_RELAXED,
+ _AO_Consume = __ATOMIC_CONSUME,
+ _AO_Acquire = __ATOMIC_ACQUIRE,
+ _AO_Release = __ATOMIC_RELEASE,
+ _AO_Acq_Rel = __ATOMIC_ACQ_REL,
+ _AO_Seq = __ATOMIC_SEQ_CST
+};
+
+template <class _ValueType, class _FromType>
+inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_atomic_store(_ValueType* __dest, _FromType __val,
+ int __order = _AO_Seq)
+{
+ __atomic_store_n(__dest, __val, __order);
+}
+
+template <class _ValueType, class _FromType>
+inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val)
+{
+ __atomic_store_n(__dest, __val, _AO_Relaxed);
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_load(_ValueType const* __val,
+ int __order = _AO_Seq)
+{
+ return __atomic_load_n(__val, __order);
+}
+
+template <class _ValueType, class _AddType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a,
+ int __order = _AO_Seq)
+{
+ return __atomic_add_fetch(__val, __a, __order);
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_exchange(_ValueType* __target,
+ _ValueType __value, int __order = _AO_Seq)
+{
+ return __atomic_exchange_n(__target, __value, __order);
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+bool __libcpp_atomic_compare_exchange(_ValueType* __val,
+ _ValueType* __expected, _ValueType __after,
+ int __success_order = _AO_Seq,
+ int __fail_order = _AO_Seq)
+{
+ return __atomic_compare_exchange_n(__val, __expected, __after, true,
+ __success_order, __fail_order);
+}
+
+#else // _LIBCPP_HAS_NO_THREADS
+
+enum __libcpp_atomic_order {
+ _AO_Relaxed,
+ _AO_Consume,
+ _AO_Acquire,
+ _AO_Release,
+ _AO_Acq_Rel,
+ _AO_Seq
+};
+
+template <class _ValueType, class _FromType>
+inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_atomic_store(_ValueType* __dest, _FromType __val,
+ int = 0)
+{
+ *__dest = __val;
+}
+
+template <class _ValueType, class _FromType>
+inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val)
+{
+ *__dest = __val;
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_load(_ValueType const* __val,
+ int = 0)
+{
+ return *__val;
+}
+
+template <class _ValueType, class _AddType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a,
+ int = 0)
+{
+ return *__val += __a;
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+_ValueType __libcpp_atomic_exchange(_ValueType* __target,
+ _ValueType __value, int __order = _AO_Seq)
+{
+ _ValueType old = *__target;
+ *__target = __value;
+ return old;
+}
+
+template <class _ValueType>
+inline _LIBCPP_INLINE_VISIBILITY
+bool __libcpp_atomic_compare_exchange(_ValueType* __val,
+ _ValueType* __expected, _ValueType __after,
+ int = 0, int = 0)
+{
+ if (*__val == *__expected) {
+ *__val = __after;
+ return true;
+ }
+ *__expected = *__val;
+ return false;
+}
+
+#endif // _LIBCPP_HAS_NO_THREADS
+
+} // end namespace
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // ATOMIC_SUPPORT_H
diff --git a/src/include/refstring.h b/src/include/refstring.h
index bc131ae..69f6747 100644
--- a/src/include/refstring.h
+++ b/src/include/refstring.h
@@ -22,6 +22,7 @@
#include <dlfcn.h>
#include <mach-o/dyld.h>
#endif
+#include "atomic_support.h"
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -87,7 +88,7 @@ __libcpp_refstring::__libcpp_refstring(const __libcpp_refstring &s) _NOEXCEPT
: __imp_(s.__imp_)
{
if (__uses_refcount())
- __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1);
+ __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1);
}
inline
@@ -96,10 +97,10 @@ __libcpp_refstring& __libcpp_refstring::operator=(__libcpp_refstring const& s) _
struct _Rep_base *old_rep = rep_from_data(__imp_);
__imp_ = s.__imp_;
if (__uses_refcount())
- __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1);
+ __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1);
if (adjust_old_count)
{
- if (__sync_add_and_fetch(&old_rep->count, count_t(-1)) < 0)
+ if (__libcpp_atomic_add(&old_rep->count, count_t(-1)) < 0)
{
::operator delete(old_rep);
}
@@ -111,7 +112,7 @@ inline
__libcpp_refstring::~__libcpp_refstring() {
if (__uses_refcount()) {
_Rep_base* rep = rep_from_data(__imp_);
- if (__sync_add_and_fetch(&rep->count, count_t(-1)) < 0) {
+ if (__libcpp_atomic_add(&rep->count, count_t(-1)) < 0) {
::operator delete(rep);
}
}
diff --git a/src/private_typeinfo.cpp b/src/private_typeinfo.cpp
index ef9466e..a0326a1 100644
--- a/src/private_typeinfo.cpp
+++ b/src/private_typeinfo.cpp
@@ -859,13 +859,14 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info,
// Record the access path that got us here
// If there is more than one dst_type this path doesn't matter.
info->path_dynamic_ptr_to_dst_ptr = path_below;
+ bool does_dst_type_point_to_our_static_type = false;
// Only search above here if dst_type derives from static_type, or
// if it is unknown if dst_type derives from static_type.
if (info->is_dst_type_derived_from_static_type != no)
{
// Set up flags to record results from all base classes
bool is_dst_type_derived_from_static_type = false;
- bool does_dst_type_point_to_our_static_type = false;
+
// We've found a dst_type with a potentially public path to here.
// We have to assume the path is public because it may become
// public later (if we get back to here with a public path).
@@ -909,21 +910,6 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info,
}
}
}
- if (!does_dst_type_point_to_our_static_type)
- {
- // We found a dst_type that doesn't point to (static_ptr, static_type)
- // So record the address of this dst_ptr and increment the
- // count of the number of such dst_types found in the tree.
- info->dst_ptr_not_leading_to_static_ptr = current_ptr;
- info->number_to_dst_ptr += 1;
- // If there exists another dst with a private path to
- // (static_ptr, static_type), then the cast from
- // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous,
- // so stop search.
- if (info->number_to_static_ptr == 1 &&
- info->path_dst_ptr_to_static_ptr == not_public_path)
- info->search_done = true;
- }
// If we found no static_type,s then dst_type doesn't derive
// from static_type, else it does. Record this result so that
// next time we hit a dst_type we will know not to search above
@@ -932,7 +918,22 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info,
info->is_dst_type_derived_from_static_type = yes;
else
info->is_dst_type_derived_from_static_type = no;
- }
+ }
+ if (!does_dst_type_point_to_our_static_type)
+ {
+ // We found a dst_type that doesn't point to (static_ptr, static_type)
+ // So record the address of this dst_ptr and increment the
+ // count of the number of such dst_types found in the tree.
+ info->dst_ptr_not_leading_to_static_ptr = current_ptr;
+ info->number_to_dst_ptr += 1;
+ // If there exists another dst with a private path to
+ // (static_ptr, static_type), then the cast from
+ // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous,
+ // so stop search.
+ if (info->number_to_static_ptr == 1 &&
+ info->path_dst_ptr_to_static_ptr == not_public_path)
+ info->search_done = true;
+ }
}
}
else
@@ -1030,13 +1031,13 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info,
// Record the access path that got us here
// If there is more than one dst_type this path doesn't matter.
info->path_dynamic_ptr_to_dst_ptr = path_below;
+ bool does_dst_type_point_to_our_static_type = false;
// Only search above here if dst_type derives from static_type, or
// if it is unknown if dst_type derives from static_type.
if (info->is_dst_type_derived_from_static_type != no)
{
// Set up flags to record results from all base classes
bool is_dst_type_derived_from_static_type = false;
- bool does_dst_type_point_to_our_static_type = false;
// Zero out found flags
info->found_our_static_ptr = false;
info->found_any_static_type = false;
@@ -1047,20 +1048,6 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info,
if (info->found_our_static_ptr)
does_dst_type_point_to_our_static_type = true;
}
- if (!does_dst_type_point_to_our_static_type)
- {
- // We found a dst_type that doesn't point to (static_ptr, static_type)
- // So record the address of this dst_ptr and increment the
- // count of the number of such dst_types found in the tree.
- info->dst_ptr_not_leading_to_static_ptr = current_ptr;
- info->number_to_dst_ptr += 1;
- // If there exists another dst with a private path to
- // (static_ptr, static_type), then the cast from
- // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous.
- if (info->number_to_static_ptr == 1 &&
- info->path_dst_ptr_to_static_ptr == not_public_path)
- info->search_done = true;
- }
// If we found no static_type,s then dst_type doesn't derive
// from static_type, else it does. Record this result so that
// next time we hit a dst_type we will know not to search above
@@ -1070,6 +1057,20 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info,
else
info->is_dst_type_derived_from_static_type = no;
}
+ if (!does_dst_type_point_to_our_static_type)
+ {
+ // We found a dst_type that doesn't point to (static_ptr, static_type)
+ // So record the address of this dst_ptr and increment the
+ // count of the number of such dst_types found in the tree.
+ info->dst_ptr_not_leading_to_static_ptr = current_ptr;
+ info->number_to_dst_ptr += 1;
+ // If there exists another dst with a private path to
+ // (static_ptr, static_type), then the cast from
+ // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous.
+ if (info->number_to_static_ptr == 1 &&
+ info->path_dst_ptr_to_static_ptr == not_public_path)
+ info->search_done = true;
+ }
}
}
else
@@ -1181,6 +1182,8 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info,
info->found_our_static_ptr = false;
info->found_any_static_type = false;
p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp);
+ found_our_static_ptr |= info->found_our_static_ptr;
+ found_any_static_type |= info->found_any_static_type;
if (++p < e)
{
do
@@ -1210,6 +1213,8 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info,
info->found_our_static_ptr = false;
info->found_any_static_type = false;
p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp);
+ found_our_static_ptr |= info->found_our_static_ptr;
+ found_any_static_type |= info->found_any_static_type;
} while (++p < e);
}
// Restore flags
diff --git a/test/dynamic_cast.pass.cpp b/test/dynamic_cast.pass.cpp
new file mode 100644
index 0000000..5288f7c
--- /dev/null
+++ b/test/dynamic_cast.pass.cpp
@@ -0,0 +1,154 @@
+//===------------------------- dynamic_cast.pass.cpp ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cassert>
+
+// This test explicitly tests dynamic cast with types that have inaccessible
+// bases.
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Winaccessible-base"
+#endif
+
+typedef char Pad1[43981];
+typedef char Pad2[34981];
+typedef char Pad3[93481];
+typedef char Pad4[13489];
+typedef char Pad5[81349];
+typedef char Pad6[34819];
+typedef char Pad7[3489];
+
+namespace t1
+{
+
+// PR33425
+struct C3 { virtual ~C3() {} Pad1 _; };
+struct C5 : protected virtual C3 { Pad2 _; };
+struct C6 : virtual C5 { Pad3 _; };
+struct C7 : virtual C3 { Pad4 _; };
+struct C9 : C6, C7 { Pad5 _; };
+
+C9 c9;
+C3 *c3 = &c9;
+
+void test()
+{
+ assert(dynamic_cast<C3*>(c3) == static_cast<C3*>(&c9));
+ assert(dynamic_cast<C5*>(c3) == static_cast<C5*>(&c9));
+ assert(dynamic_cast<C6*>(c3) == static_cast<C6*>(&c9));
+ assert(dynamic_cast<C7*>(c3) == static_cast<C7*>(&c9));
+ assert(dynamic_cast<C9*>(c3) == static_cast<C9*>(&c9));
+}
+
+} // t1
+
+namespace t2
+{
+
+// PR33425
+struct Src { virtual ~Src() {} Pad1 _; };
+struct Mask : protected virtual Src { Pad2 _; };
+struct Dest : Mask { Pad3 _; };
+struct Root : Dest, virtual Src { Pad4 _; };
+
+Root root;
+Src *src = &root;
+
+void test()
+{
+ assert(dynamic_cast<Src*>(src) == static_cast<Src*>(&root));
+ assert(dynamic_cast<Mask*>(src) == static_cast<Mask*>(&root));
+ assert(dynamic_cast<Dest*>(src) == static_cast<Dest*>(&root));
+ assert(dynamic_cast<Root*>(src) == static_cast<Root*>(&root));
+}
+
+} // t2
+
+namespace t3
+{
+
+// PR33487
+struct Class1 { virtual ~Class1() {} Pad1 _; };
+struct Shared : virtual Class1 { Pad2 _; };
+struct Class6 : virtual Shared { Pad3 _; };
+struct Left : Class6 { Pad4 _; };
+struct Right : Class6 { Pad5 _; };
+struct Main : Left, Right { Pad6 _; };
+
+Main m;
+Class1 *c1 = &m;
+
+void test()
+{
+ assert(dynamic_cast<Class1*>(c1) == static_cast<Class1*>(&m));
+ assert(dynamic_cast<Shared*>(c1) == static_cast<Shared*>(&m));
+ assert(dynamic_cast<Class6*>(c1) == 0);
+ assert(dynamic_cast<Left*>(c1) == static_cast<Left*>(&m));
+ assert(dynamic_cast<Right*>(c1) == static_cast<Right*>(&m));
+ assert(dynamic_cast<Main*>(c1) == static_cast<Main*>(&m));
+}
+
+} // t3
+
+namespace t4
+{
+
+// PR33439
+struct C2 { virtual ~C2() {} Pad1 _; };
+struct C3 { virtual ~C3() {} Pad2 _; };
+struct C4 : C3 { Pad3 _; };
+struct C8 : C2, virtual C4 { Pad4 _; };
+struct C9 : C4, C8 { Pad5 _; };
+
+C9 c9;
+C2 *c2 = &c9;
+
+void test()
+{
+ assert(dynamic_cast<C2*>(c2) == static_cast<C2*>(&c9));
+ assert(dynamic_cast<C3*>(c2) == 0);
+ assert(dynamic_cast<C4*>(c2) == 0);
+ assert(dynamic_cast<C8*>(c2) == static_cast<C8*>(&c9));
+ assert(dynamic_cast<C9*>(c2) == static_cast<C9*>(&c9));
+}
+
+} // t4
+
+namespace t5
+{
+
+// PR33439
+struct Dummy { virtual ~Dummy() {} Pad1 _; };
+struct Src { virtual ~Src() {} Pad2 _; };
+struct Dest : Dummy { Pad3 _; };
+struct A1 : Dest { Pad4 _; };
+struct A2 : Dest { Pad5 _; };
+struct Root : Src, A1, A2 { Pad6 _; };
+
+Root root;
+Src *src = &root;
+
+void test()
+{
+ assert(dynamic_cast<Dummy*>(src) == 0);
+ assert(dynamic_cast<Src*>(src) == static_cast<Src*>(&root));
+ assert(dynamic_cast<Dest*>(src) == 0);
+ assert(dynamic_cast<A1*>(src) == static_cast<A1*>(&root));
+ assert(dynamic_cast<A2*>(src) == static_cast<A2*>(&root));
+}
+
+} // t5
+
+int main()
+{
+ t1::test();
+ t2::test();
+ t3::test();
+ t4::test();
+ t5::test();
+}
diff --git a/test/libcxxabi/test/config.py b/test/libcxxabi/test/config.py
index d24fc5b..f294c8b 100644
--- a/test/libcxxabi/test/config.py
+++ b/test/libcxxabi/test/config.py
@@ -49,7 +49,10 @@ class Configuration(LibcxxConfiguration):
self.config.available_features.add('libcxxabi-has-system-unwinder')
def configure_compile_flags(self):
- self.cxx.compile_flags += ['-DLIBCXXABI_NO_TIMER']
+ self.cxx.compile_flags += [
+ '-DLIBCXXABI_NO_TIMER',
+ '-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS',
+ ]
if self.get_lit_bool('enable_exceptions', True):
self.cxx.compile_flags += ['-funwind-tables']
else:
diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp
index 98921c2..ea7a0fa 100644
--- a/test/test_demangle.pass.cpp
+++ b/test/test_demangle.pass.cpp
@@ -29738,6 +29738,17 @@ const char* cases[][2] =
{"_ZN5OuterI4MarpEcvT_I4MerpEEv", "Outer<Marp>::operator Merp<Merp>()"},
{"_ZZN5OuterI4MarpEcv7MuncherIJT_T0_DpT1_EEI4MerpS0_JicfEEEvEN1ScvS9_Ev", "Outer<Marp>::operator Muncher<Merp, Marp, int, char, float><Merp, Marp, int, char, float>()::S::operator Merp()"},
{"_ZN1Scv7MuncherIJDpPT_EEIJFivEA_iEEEv", "S::operator Muncher<int (*)(), int (*) []><int (), int []>()"},
+
+ {"_Z2f8IiJ8identityIiES0_IfEEEvRAsPiDpT0_T_DpNS3_4typeEE_i", "void f8<int, identity<int>, identity<float> >(int (&) [sizeof... (int, identity<int>, identity<float>, int, identity<int>::type, identity<float>::type)])"},
+
+ {"_ZNK13StaticMembersIfE1xMUlvE_clEv", "StaticMembers<float>::x::'lambda'()::operator()() const"},
+ {"_ZNK10inline_varMUlvE_clEv", "inline_var::'lambda'()::operator()() const"},
+ // C++1z fold expressions:
+ {"_Z6foldl1IJLi1ELi2ELi3EEEv1AIXfLplLi1ET_EE", "void foldl1<1, 2, 3>(A<(1 + ... + (1, 2, 3))>)"},
+ {"_Z6foldr1IJLi1ELi2ELi3EEEv1AIXfRplT_Li1EEE", "void foldr1<1, 2, 3>(A<((1, 2, 3) + ... + 1)>)"},
+ {"_Z5foldlIJLi1ELi2ELi3EEEv1AIXflplT_EE", "void foldl<1, 2, 3>(A<(... + (1, 2, 3))>)"},
+ {"_Z5foldrIJLi1ELi2ELi3EEEv1AIXfrplT_EE", "void foldr<1, 2, 3>(A<((1, 2, 3) + ...)>)"},
+ {"_ZN7PartialIJLi1ELi2EEE5foldrIJLi3ELi4EEEEv1AIXplLi1EplLi2EfRplT_plLi1EplLi2EfrplT_EE", "void Partial<1, 2>::foldr<3, 4>(A<(1) + ((2) + (((3, 4) + ... + (1) + ((2) + (((3, 4) + ...))))))>)"},
};
const unsigned N = sizeof(cases) / sizeof(cases[0]);
diff --git a/test/test_exception_storage.pass.cpp b/test/test_exception_storage.pass.cpp
index 49c9b5d..ef2524f 100644
--- a/test/test_exception_storage.pass.cpp
+++ b/test/test_exception_storage.pass.cpp
@@ -7,11 +7,6 @@
//
//===----------------------------------------------------------------------===//
-// FIXME: cxa_exception.hpp directly references `std::unexpected` and friends.
-// This breaks this test when compiled in C++17. For now fix this by manually
-// re-enabling the STL functions.
-#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS
-
#include <cstdlib>
#include <algorithm>
#include <iostream>