Merge commit f26c8d18976
* upstream svn@323906
Test: git diff f26c8d18976 == git diff 05ba328 c9dec13
Change-Id: If992e433888af8b631b54abd4dc81258d9462e89
diff --git a/.arcconfig b/.arcconfig
index 0c5cf4e..08cd16e 100644
--- a/.arcconfig
+++ b/.arcconfig
@@ -1,4 +1,4 @@
{
- "project_id" : "libcxxabi",
+ "repository.callsign" : "CXXA",
"conduit_uri" : "https://reviews.llvm.org/"
}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 456250e..35eb130 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@
project(libcxxabi CXX C)
set(PACKAGE_NAME libcxxabi)
- set(PACKAGE_VERSION 6.0.0svn)
+ set(PACKAGE_VERSION 7.0.0svn)
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org")
@@ -69,6 +69,7 @@
option(LIBCXXABI_INCLUDE_TESTS "Generate build targets for the libc++abi unit tests." ${LLVM_INCLUDE_TESTS})
set(LIBCXXABI_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING
"Define suffix of library directory name (32/64)")
+option(LIBCXXABI_INSTALL_LIBRARY "Install the libc++abi library." ON)
set(LIBCXXABI_TARGET_TRIPLE "" CACHE STRING "Target triple for cross compiling.")
set(LIBCXXABI_GCC_TOOLCHAIN "" CACHE PATH "GCC toolchain for cross compiling.")
set(LIBCXXABI_SYSROOT "" CACHE PATH "Sysroot for cross compiling.")
@@ -114,6 +115,7 @@
${CMAKE_BINARY_DIR}/${LIBCXXABI_LIBCXX_INCLUDES}
${LIBCXXABI_LIBCXX_INCLUDE_DIRS}
${LLVM_INCLUDE_DIR}/c++/v1
+ NO_CMAKE_FIND_ROOT_PATH
)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXXABI_LIBCXX_INCLUDES}" CACHE PATH
@@ -126,6 +128,7 @@
${LIBCXXABI_LIBCXX_INCLUDES}/../
${LIBCXXABI_LIBCXX_SRC_DIRS}
NO_DEFAULT_PATH
+ NO_CMAKE_FIND_ROOT_PATH
)
if (LIBCXXABI_LIBCXX_PATH STREQUAL "LIBCXXABI_LIBCXX_PATH-NOTFOUND")
@@ -424,6 +427,7 @@
${LLVM_MAIN_SRC_DIR}/projects/libunwind/include
${LLVM_MAIN_SRC_DIR}/runtimes/libunwind/include
NO_DEFAULT_PATH
+ NO_CMAKE_FIND_ROOT_PATH
)
if (LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL STREQUAL "LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL-NOTFOUND")
@@ -439,18 +443,21 @@
# soname, etc...
add_subdirectory(src)
-if (NOT LIBCXXABI_INCLUDE_TESTS OR (LIBCXXABI_STANDALONE_BUILD AND NOT LIBCXXABI_ENABLE_SHARED))
- # We can't reasonably test the system C++ library with a static libc++abi.
- # We either need to be able to replace libc++abi at run time (with a shared
- # libc++abi), or we need to be able to replace the C++ runtime (with a non-
- # standalone build).
- message(WARNING "The libc++abi tests aren't valid when libc++abi is built "
- "standalone (i.e. outside of llvm/projects/libcxxabi ) and "
- "is built without a shared library. Either build a shared "
- "library, build libc++abi at the same time as you build "
- "libc++, or do without testing. No check target will be "
- "available!")
-else()
- add_subdirectory(test)
- add_subdirectory(fuzz)
+if (LIBCXXABI_INCLUDE_TESTS)
+ if (LIBCXXABI_STANDALONE_BUILD AND NOT LIBCXXABI_ENABLE_SHARED)
+ # We can't reasonably test the system C++ library with a static
+ # libc++abi. We either need to be able to replace libc++abi at
+ # run time (with a shared libc++abi), or we need to be able to
+ # replace the C++ runtime (with a non- standalone build).
+ message(WARNING "The libc++abi tests aren't valid when libc++abi "
+ "is built standalone (i.e. outside of "
+ "llvm/projects/libcxxabi ) and is built without "
+ "a shared library. Either build a shared "
+ "library, build libc++abi at the same time as "
+ "you build libc++, or do without testing. No "
+ "check target will be available!")
+ else()
+ add_subdirectory(test)
+ add_subdirectory(fuzz)
+ endif()
endif()
diff --git a/CREDITS.TXT b/CREDITS.TXT
index 9c910fc..fbb6cbf 100644
--- a/CREDITS.TXT
+++ b/CREDITS.TXT
@@ -58,7 +58,7 @@
D: Minor patches and fixes
N: Jon Roelofs
-E: jonathan@codesourcery.com
+E: jroelofs@jroelofs.com
D: ARM EHABI Unwind & Exception Handling, Bare-metal
N: Nico Weber
diff --git a/cmake/Modules/HandleOutOfTreeLLVM.cmake b/cmake/Modules/HandleOutOfTreeLLVM.cmake
index 8e74208..ed0b527 100644
--- a/cmake/Modules/HandleOutOfTreeLLVM.cmake
+++ b/cmake/Modules/HandleOutOfTreeLLVM.cmake
@@ -107,14 +107,22 @@
set(LLVM_ENABLE_SPHINX OFF)
endif()
- # Required LIT Configuration ------------------------------------------------
- # Define the default arguments to use with 'lit', and an option for the user
- # to override.
- set(LIT_ARGS_DEFAULT "-sv --show-xfail --show-unsupported")
- if (MSVC OR XCODE)
- set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
+ # In a standalone build, we don't have llvm to automatically generate the
+ # llvm-lit script for us. So we need to provide an explicit directory that
+ # the configurator should write the script into.
+ set(LLVM_LIT_OUTPUT_DIR "${libcxxabi_BINARY_DIR}/bin")
+
+ if (LLVM_INCLUDE_TESTS)
+ # Required LIT Configuration ------------------------------------------------
+ # Define the default arguments to use with 'lit', and an option for the user
+ # to override.
+ set(LLVM_EXTERNAL_LIT "${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py")
+ set(LIT_ARGS_DEFAULT "-sv --show-xfail --show-unsupported")
+ if (MSVC OR XCODE)
+ set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
+ endif()
+ set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
endif()
- set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
# Required doc configuration
if (LLVM_ENABLE_SPHINX)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index adcc412..5a929cf 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -127,6 +127,9 @@
# Build the shared library.
if (LIBCXXABI_ENABLE_SHARED)
add_library(cxxabi_shared SHARED $<TARGET_OBJECTS:cxxabi_objects>)
+ if(COMMAND llvm_setup_rpath)
+ llvm_setup_rpath(cxxabi_shared)
+ endif()
target_link_libraries(cxxabi_shared ${LIBCXXABI_LIBRARIES})
set_target_properties(cxxabi_shared
PROPERTIES
@@ -151,7 +154,13 @@
# Build the static library.
if (LIBCXXABI_ENABLE_STATIC)
- add_library(cxxabi_static STATIC $<TARGET_OBJECTS:cxxabi_objects>)
+ set(cxxabi_static_sources $<TARGET_OBJECTS:cxxabi_objects>)
+ if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_ENABLE_STATIC_UNWINDER)
+ if (TARGET unwind_static OR HAVE_LIBUNWIND)
+ list(APPEND cxxabi_static_sources $<TARGET_OBJECTS:unwind_objects>)
+ endif()
+ endif()
+ add_library(cxxabi_static STATIC ${cxxabi_static_sources})
target_link_libraries(cxxabi_static ${LIBCXXABI_LIBRARIES})
set_target_properties(cxxabi_static
PROPERTIES
@@ -173,17 +182,25 @@
# Add a meta-target for both libraries.
add_custom_target(cxxabi DEPENDS ${LIBCXXABI_TARGETS})
-install(TARGETS ${LIBCXXABI_TARGETS}
- LIBRARY DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi
- ARCHIVE DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi
- )
+if (LIBCXXABI_INSTALL_LIBRARY)
+ install(TARGETS ${LIBCXXABI_TARGETS}
+ LIBRARY DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi
+ ARCHIVE DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi
+ )
+endif()
-if (NOT CMAKE_CONFIGURATION_TYPES)
+if (NOT CMAKE_CONFIGURATION_TYPES AND LIBCXXABI_INSTALL_LIBRARY)
add_custom_target(install-cxxabi
DEPENDS cxxabi
COMMAND "${CMAKE_COMMAND}"
-DCMAKE_INSTALL_COMPONENT=cxxabi
-P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake")
+ add_custom_target(install-cxxabi-stripped
+ DEPENDS cxxabi
+ COMMAND "${CMAKE_COMMAND}"
+ -DCMAKE_INSTALL_COMPONENT=cxxabi
+ -DCMAKE_INSTALL_DO_STRIP=1
+ -P "${LIBCXXABI_BINARY_DIR}/cmake_install.cmake")
# TODO: This is a legacy target name and should be removed at some point.
add_custom_target(install-libcxxabi DEPENDS install-cxxabi)
diff --git a/src/cxa_default_handlers.cpp b/src/cxa_default_handlers.cpp
index f2e36b4..8231139 100644
--- a/src/cxa_default_handlers.cpp
+++ b/src/cxa_default_handlers.cpp
@@ -19,6 +19,7 @@
#include "cxa_exception.hpp"
#include "private_typeinfo.h"
+#if !defined(LIBCXXABI_SILENT_TERMINATE)
static const char* cause = "uncaught";
__attribute__((noreturn))
@@ -84,7 +85,6 @@
std::terminate();
}
-#if !defined(LIBCXXABI_SILENT_TERMINATE)
static std::terminate_handler default_terminate_handler = demangling_terminate_handler;
static std::terminate_handler default_unexpected_handler = demangling_unexpected_handler;
#else
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index 7d17318..d0362dc 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -7,6 +7,12 @@
//
//===----------------------------------------------------------------------===//
+// FIXME: (possibly) incomplete list of features that clang mangles that this
+// file does not yet support:
+// - enable_if attribute
+// - C++ modules TS
+// - All C++14 and C++17 features
+
#define _LIBCPP_NO_EXCEPTIONS
#include "__cxxabi_config.h"
@@ -14,6 +20,7 @@
#include <vector>
#include <algorithm>
#include <numeric>
+#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -26,20 +33,15 @@
#endif
#endif
-namespace __cxxabiv1
-{
+#ifndef NDEBUG
+#if __has_attribute(noinline) && __has_attribute(used)
+#define DUMP_METHOD __attribute__((noinline,used))
+#else
+#define DUMP_METHOD
+#endif
+#endif
-namespace
-{
-
-enum
-{
- unknown_error = -4,
- invalid_args = -3,
- invalid_mangled_name,
- memory_alloc_failure,
- success
-};
+namespace {
class StringView {
const char *First;
@@ -76,6 +78,7 @@
const char *begin() const { return First; }
const char *end() const { return Last; }
size_t size() const { return static_cast<size_t>(Last - First); }
+ bool empty() const { return First == Last; }
};
bool operator==(const StringView &LHS, const StringView &RHS) {
@@ -104,6 +107,10 @@
OutputStream(char *StartBuf, size_t Size)
: Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
+ /// If a ParameterPackExpansion (or similar type) is encountered, the offset
+ /// into the pack that we're currently printing.
+ unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
+
OutputStream &operator+=(StringView R) {
size_t Size = R.size();
if (Size == 0)
@@ -120,41 +127,7 @@
return *this;
}
- // Offset of position in buffer, used for building stream_string_view.
- typedef unsigned StreamPosition;
-
- // StringView into a stream, used for caching the ast nodes.
- class StreamStringView {
- StreamPosition First, Last;
-
- friend class OutputStream;
-
- public:
- StreamStringView() : First(0), Last(0) {}
-
- StreamStringView(StreamPosition First_, StreamPosition Last_)
- : First(First_), Last(Last_) {}
-
- bool empty() const { return First == Last; }
- };
-
- OutputStream &operator+=(StreamStringView &s) {
- size_t Sz = static_cast<size_t>(s.Last - s.First);
- if (Sz == 0)
- return *this;
- grow(Sz);
- memmove(Buffer + CurrentPosition, Buffer + s.First, Sz);
- CurrentPosition += Sz;
- return *this;
- }
-
- StreamPosition getCurrentPosition() const {
- return static_cast<StreamPosition>(CurrentPosition);
- }
-
- StreamStringView makeStringViewFromPastPosition(StreamPosition Pos) {
- return StreamStringView(Pos, getCurrentPosition());
- }
+ size_t getCurrentPosition() const { return CurrentPosition; };
char back() const {
return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
@@ -167,6 +140,21 @@
size_t getBufferCapacity() { return BufferCapacity; }
};
+template <class T>
+class SwapAndRestore {
+ T &Restore;
+ T OriginalValue;
+public:
+ SwapAndRestore(T& Restore_, T NewVal)
+ : Restore(Restore_), OriginalValue(Restore) {
+ Restore = std::move(NewVal);
+ }
+ ~SwapAndRestore() { Restore = std::move(OriginalValue); }
+
+ SwapAndRestore(const SwapAndRestore &) = delete;
+ SwapAndRestore &operator=(const SwapAndRestore &) = delete;
+};
+
// Base class of all AST nodes. The AST is built by the parser, then is
// traversed by the printLeft/Right functions to produce a demangled string.
class Node {
@@ -177,7 +165,9 @@
KQualType,
KConversionOperatorType,
KPostfixQualifiedType,
+ KElaboratedTypeSpefType,
KNameType,
+ KAbiTagAttr,
KObjCProtoName,
KPointerType,
KLValueReferenceType,
@@ -185,16 +175,19 @@
KPointerToMemberType,
KArrayType,
KFunctionType,
- KTopLevelFunctionDecl,
- KFunctionQualType,
- KFunctionRefQualType,
+ KNoexceptSpec,
+ KDynamicExceptionSpec,
+ KFunctionEncoding,
KLiteralOperator,
KSpecialName,
KCtorVtableSpecialName,
KQualifiedName,
KEmptyName,
KVectorType,
- KTemplateParams,
+ KParameterPack,
+ KTemplateArgumentPack,
+ KParameterPackExpansion,
+ KTemplateArgs,
KNameWithTemplateArgs,
KGlobalQualifiedName,
KStdQualifiedName,
@@ -203,32 +196,77 @@
KCtorDtorName,
KDtorName,
KUnnamedTypeName,
- KLambdaTypeName,
+ KClosureTypeName,
KExpr,
+ KBracedExpr,
+ KBracedRangeExpr,
};
- const Kind K;
+ static constexpr unsigned NoParameterPack =
+ std::numeric_limits<unsigned>::max();
+ unsigned ParameterPackSize = NoParameterPack;
-private:
- // If this Node has any RHS part, potentally many Nodes further down.
- const unsigned HasRHSComponent : 1;
- const unsigned HasFunction : 1;
- const unsigned HasArray : 1;
+ Kind K;
-public:
- Node(Kind K_, bool HasRHS_ = false, bool HasFunction_ = false,
- bool HasArray_ = false)
- : K(K_), HasRHSComponent(HasRHS_), HasFunction(HasFunction_),
- HasArray(HasArray_) {}
+ /// Three-way bool to track a cached value. Unknown is possible if this node
+ /// has an unexpanded parameter pack below it that may affect this cache.
+ enum class Cache : unsigned char { Yes, No, Unknown, };
- bool hasRHSComponent() const { return HasRHSComponent; }
- bool hasArray() const { return HasArray; }
- bool hasFunction() const { return HasFunction; }
+ /// Tracks if this node has a component on its right side, in which case we
+ /// need to call printRight.
+ Cache RHSComponentCache;
- void print(OutputStream &s) const {
- printLeft(s);
- if (hasRHSComponent())
- printRight(s);
+ /// Track if this node is a (possibly qualified) array type. This can affect
+ /// how we format the output string.
+ Cache ArrayCache;
+
+ /// Track if this node is a (possibly qualified) function type. This can
+ /// affect how we format the output string.
+ Cache FunctionCache;
+
+ Node(Kind K_, unsigned ParameterPackSize_ = NoParameterPack,
+ Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No,
+ Cache FunctionCache_ = Cache::No)
+ : ParameterPackSize(ParameterPackSize_), K(K_),
+ RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_),
+ FunctionCache(FunctionCache_) {}
+
+ bool containsUnexpandedParameterPack() const {
+ return ParameterPackSize != NoParameterPack;
+ }
+
+ bool hasRHSComponent(OutputStream &S) const {
+ if (RHSComponentCache != Cache::Unknown)
+ return RHSComponentCache == Cache::Yes;
+ return hasRHSComponentSlow(S);
+ }
+
+ bool hasArray(OutputStream &S) const {
+ if (ArrayCache != Cache::Unknown)
+ return ArrayCache == Cache::Yes;
+ return hasArraySlow(S);
+ }
+
+ bool hasFunction(OutputStream &S) const {
+ if (FunctionCache != Cache::Unknown)
+ return FunctionCache == Cache::Yes;
+ return hasFunctionSlow(S);
+ }
+
+ Kind getKind() const { return K; }
+
+ virtual bool hasRHSComponentSlow(OutputStream &) const { return false; }
+ virtual bool hasArraySlow(OutputStream &) const { return false; }
+ virtual bool hasFunctionSlow(OutputStream &) const { return false; }
+
+ /// If this node is a pack expansion that expands to 0 elements. This can have
+ /// an effect on how we should format the output.
+ bool isEmptyPackExpansion() const;
+
+ void print(OutputStream &S) const {
+ printLeft(S);
+ if (RHSComponentCache != Cache::No)
+ printRight(S);
}
// Print the "left" side of this Node into OutputStream.
@@ -244,6 +282,17 @@
// Silence compiler warnings, this dtor will never be called.
virtual ~Node() = default;
+
+#ifndef NDEBUG
+ DUMP_METHOD void dump() const {
+ char *Buffer = static_cast<char*>(std::malloc(1024));
+ OutputStream S(Buffer, 1024);
+ print(S);
+ S += '\0';
+ printf("Symbol dump for %p: %s\n", (const void*)this, S.getBuffer());
+ std::free(S.getBuffer());
+ }
+#endif
};
class NodeArray {
@@ -251,17 +300,26 @@
size_t NumElements;
public:
- NodeArray() : NumElements(0) {}
+ NodeArray() : Elements(nullptr), NumElements(0) {}
NodeArray(Node **Elements_, size_t NumElements_)
: Elements(Elements_), NumElements(NumElements_) {}
bool empty() const { return NumElements == 0; }
size_t size() const { return NumElements; }
- void printWithSeperator(OutputStream &S, StringView Seperator) const {
+ Node **begin() const { return Elements; }
+ Node **end() const { return Elements + NumElements; }
+
+ Node *operator[](size_t Idx) const { return Elements[Idx]; }
+
+ void printWithComma(OutputStream &S) const {
+ bool FirstElement = true;
for (size_t Idx = 0; Idx != NumElements; ++Idx) {
- if (Idx)
- S += Seperator;
+ if (Elements[Idx]->isEmptyPackExpansion())
+ continue;
+ if (!FirstElement)
+ S += ", ";
+ FirstElement = false;
Elements[Idx]->print(S);
}
}
@@ -284,20 +342,25 @@
};
class VendorExtQualType final : public Node {
- const Node *Ext;
const Node *Ty;
+ StringView Ext;
public:
- VendorExtQualType(Node *Ext_, Node *Ty_)
- : Node(KVendorExtQualType), Ext(Ext_), Ty(Ty_) {}
+ VendorExtQualType(Node *Ty_, StringView Ext_)
+ : Node(KVendorExtQualType, Ty_->ParameterPackSize),
+ Ty(Ty_), Ext(Ext_) {}
void printLeft(OutputStream &S) const override {
- Ext->print(S);
+ Ty->print(S);
S += " ";
- Ty->printLeft(S);
+ S += Ext;
}
+};
- void printRight(OutputStream &S) const override { Ty->printRight(S); }
+enum FunctionRefQual : unsigned char {
+ FrefQualNone,
+ FrefQualLValue,
+ FrefQualRValue,
};
enum Qualifiers {
@@ -327,14 +390,19 @@
public:
QualType(Node *Child_, Qualifiers Quals_)
- : Node(KQualType, Child_->hasRHSComponent(), Child_->hasFunction(),
- Child_->hasArray()),
+ : Node(KQualType, Child_->ParameterPackSize, Child_->RHSComponentCache,
+ Child_->ArrayCache, Child_->FunctionCache),
Quals(Quals_), Child(Child_) {}
- QualType(Node::Kind ChildKind_, Node *Child_, Qualifiers Quals_)
- : Node(ChildKind_, Child_->hasRHSComponent(), Child_->hasFunction(),
- Child_->hasArray()),
- Quals(Quals_), Child(Child_) {}
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ return Child->hasRHSComponent(S);
+ }
+ bool hasArraySlow(OutputStream &S) const override {
+ return Child->hasArray(S);
+ }
+ bool hasFunctionSlow(OutputStream &S) const override {
+ return Child->hasFunction(S);
+ }
void printLeft(OutputStream &S) const override {
Child->printLeft(S);
@@ -348,7 +416,8 @@
const Node *Ty;
public:
- ConversionOperatorType(Node *Ty_) : Node(KConversionOperatorType), Ty(Ty_) {}
+ ConversionOperatorType(Node *Ty_)
+ : Node(KConversionOperatorType, Ty_->ParameterPackSize), Ty(Ty_) {}
void printLeft(OutputStream &S) const override {
S += "operator ";
@@ -362,14 +431,13 @@
public:
PostfixQualifiedType(Node *Ty_, StringView Postfix_)
- : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {}
+ : Node(KPostfixQualifiedType, Ty_->ParameterPackSize),
+ Ty(Ty_), Postfix(Postfix_) {}
void printLeft(OutputStream &s) const override {
Ty->printLeft(s);
s += Postfix;
}
-
- void printRight(OutputStream &S) const override { Ty->printRight(S); }
};
class NameType final : public Node {
@@ -384,25 +452,58 @@
void printLeft(OutputStream &s) const override { s += Name; }
};
+class ElaboratedTypeSpefType : public Node {
+ StringView Kind;
+ Node *Child;
+public:
+ ElaboratedTypeSpefType(StringView Kind_, Node *Child_)
+ : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {
+ ParameterPackSize = Child->ParameterPackSize;
+ }
+
+ void printLeft(OutputStream &S) const override {
+ S += Kind;
+ S += ' ';
+ Child->print(S);
+ }
+};
+
+class AbiTagAttr final : public Node {
+ const Node* Base;
+ StringView Tag;
+public:
+ AbiTagAttr(const Node* Base_, StringView Tag_)
+ : Node(KAbiTagAttr, Base_->ParameterPackSize, Base_->RHSComponentCache,
+ Base_->ArrayCache, Base_->FunctionCache),
+ Base(Base_), Tag(Tag_) {}
+
+ void printLeft(OutputStream &S) const override {
+ Base->printLeft(S);
+ S += "[abi:";
+ S += Tag;
+ S += "]";
+ }
+};
+
class ObjCProtoName : public Node {
Node *Ty;
- Node *Protocol;
+ StringView Protocol;
friend class PointerType;
public:
- ObjCProtoName(Node *Ty_, Node *Protocol_)
+ ObjCProtoName(Node *Ty_, StringView Protocol_)
: Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {}
bool isObjCObject() const {
- return Ty->K == KNameType &&
+ return Ty->getKind() == KNameType &&
static_cast<NameType *>(Ty)->getName() == "objc_object";
}
void printLeft(OutputStream &S) const override {
- Ty->printLeft(S);
+ Ty->print(S);
S += "<";
- Protocol->printLeft(S);
+ S += Protocol;
S += ">";
}
};
@@ -412,30 +513,36 @@
public:
PointerType(Node *Pointee_)
- : Node(KPointerType, Pointee_->hasRHSComponent()), Pointee(Pointee_) {}
+ : Node(KPointerType, Pointee_->ParameterPackSize,
+ Pointee_->RHSComponentCache),
+ Pointee(Pointee_) {}
+
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ return Pointee->hasRHSComponent(S);
+ }
void printLeft(OutputStream &s) const override {
// We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
- if (Pointee->K != KObjCProtoName ||
+ if (Pointee->getKind() != KObjCProtoName ||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
Pointee->printLeft(s);
- if (Pointee->hasArray())
+ if (Pointee->hasArray(s))
s += " ";
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += "(";
s += "*";
} else {
const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee);
s += "id<";
- objcProto->Protocol->print(s);
+ s += objcProto->Protocol;
s += ">";
}
}
void printRight(OutputStream &s) const override {
- if (Pointee->K != KObjCProtoName ||
+ if (Pointee->getKind() != KObjCProtoName ||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += ")";
Pointee->printRight(s);
}
@@ -447,20 +554,25 @@
public:
LValueReferenceType(Node *Pointee_)
- : Node(KLValueReferenceType, Pointee_->hasRHSComponent()),
+ : Node(KLValueReferenceType, Pointee_->ParameterPackSize,
+ Pointee_->RHSComponentCache),
Pointee(Pointee_) {}
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ return Pointee->hasRHSComponent(S);
+ }
+
void printLeft(OutputStream &s) const override {
Pointee->printLeft(s);
- if (Pointee->hasArray())
+ if (Pointee->hasArray(s))
s += " ";
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += "(&";
else
s += "&";
}
void printRight(OutputStream &s) const override {
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += ")";
Pointee->printRight(s);
}
@@ -471,21 +583,26 @@
public:
RValueReferenceType(Node *Pointee_)
- : Node(KRValueReferenceType, Pointee_->hasRHSComponent()),
+ : Node(KRValueReferenceType, Pointee_->ParameterPackSize,
+ Pointee_->RHSComponentCache),
Pointee(Pointee_) {}
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ return Pointee->hasRHSComponent(S);
+ }
+
void printLeft(OutputStream &s) const override {
Pointee->printLeft(s);
- if (Pointee->hasArray())
+ if (Pointee->hasArray(s))
s += " ";
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += "(&&";
else
s += "&&";
}
void printRight(OutputStream &s) const override {
- if (Pointee->hasArray() || Pointee->hasFunction())
+ if (Pointee->hasArray(s) || Pointee->hasFunction(s))
s += ")";
Pointee->printRight(s);
}
@@ -497,12 +614,19 @@
public:
PointerToMemberType(Node *ClassType_, Node *MemberType_)
- : Node(KPointerToMemberType, MemberType_->hasRHSComponent()),
+ : Node(KPointerToMemberType,
+ std::min(MemberType_->ParameterPackSize,
+ ClassType_->ParameterPackSize),
+ MemberType_->RHSComponentCache),
ClassType(ClassType_), MemberType(MemberType_) {}
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ return MemberType->hasRHSComponent(S);
+ }
+
void printLeft(OutputStream &s) const override {
MemberType->printLeft(s);
- if (MemberType->hasArray() || MemberType->hasFunction())
+ if (MemberType->hasArray(s) || MemberType->hasFunction(s))
s += "(";
else
s += " ";
@@ -511,7 +635,7 @@
}
void printRight(OutputStream &s) const override {
- if (MemberType->hasArray() || MemberType->hasFunction())
+ if (MemberType->hasArray(s) || MemberType->hasFunction(s))
s += ")";
MemberType->printRight(s);
}
@@ -559,10 +683,24 @@
public:
ArrayType(Node *Base_, NodeOrString Dimension_)
- : Node(KArrayType, true, false, true), Base(Base_), Dimension(Dimension_) {}
+ : Node(KArrayType, Base_->ParameterPackSize,
+ /*RHSComponentCache=*/Cache::Yes,
+ /*ArrayCache=*/Cache::Yes),
+ Base(Base_), Dimension(Dimension_) {
+ if (Dimension.isNode())
+ ParameterPackSize =
+ std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize);
+ }
// Incomplete array type.
- ArrayType(Node *Base_) : Node(KArrayType, true, false, true), Base(Base_) {}
+ ArrayType(Node *Base_)
+ : Node(KArrayType, Base_->ParameterPackSize,
+ /*RHSComponentCache=*/Cache::Yes,
+ /*ArrayCache=*/Cache::Yes),
+ Base(Base_) {}
+
+ bool hasRHSComponentSlow(OutputStream &) const override { return true; }
+ bool hasArraySlow(OutputStream &) const override { return true; }
void printLeft(OutputStream &S) const override { Base->printLeft(S); }
@@ -582,10 +720,27 @@
class FunctionType final : public Node {
Node *Ret;
NodeArray Params;
+ Qualifiers CVQuals;
+ FunctionRefQual RefQual;
+ Node *ExceptionSpec;
public:
- FunctionType(Node *Ret_, NodeArray Params_)
- : Node(KFunctionType, true, true), Ret(Ret_), Params(Params_) {}
+ FunctionType(Node *Ret_, NodeArray Params_, Qualifiers CVQuals_,
+ FunctionRefQual RefQual_, Node *ExceptionSpec_)
+ : Node(KFunctionType, Ret_->ParameterPackSize,
+ /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
+ /*FunctionCache=*/Cache::Yes),
+ Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_),
+ ExceptionSpec(ExceptionSpec_) {
+ for (Node *P : Params)
+ ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
+ if (ExceptionSpec != nullptr)
+ ParameterPackSize =
+ std::min(ParameterPackSize, ExceptionSpec->ParameterPackSize);
+ }
+
+ bool hasRHSComponentSlow(OutputStream &) const override { return true; }
+ bool hasFunctionSlow(OutputStream &) const override { return true; }
// Handle C++'s ... quirky decl grammer by using the left & right
// distinction. Consider:
@@ -601,26 +756,87 @@
void printRight(OutputStream &S) const override {
S += "(";
- Params.printWithSeperator(S, ", ");
+ Params.printWithComma(S);
S += ")";
Ret->printRight(S);
+
+ if (CVQuals & QualConst)
+ S += " const";
+ if (CVQuals & QualVolatile)
+ S += " volatile";
+ if (CVQuals & QualRestrict)
+ S += " restrict";
+
+ if (RefQual == FrefQualLValue)
+ S += " &";
+ else if (RefQual == FrefQualRValue)
+ S += " &&";
+
+ if (ExceptionSpec != nullptr) {
+ S += ' ';
+ ExceptionSpec->print(S);
+ }
}
};
-class TopLevelFunctionDecl final : public Node {
+class NoexceptSpec : public Node {
+ Node *E;
+public:
+ NoexceptSpec(Node *E_) : Node(KNoexceptSpec, E_->ParameterPackSize), E(E_) {}
+
+ void printLeft(OutputStream &S) const override {
+ S += "noexcept(";
+ E->print(S);
+ S += ")";
+ }
+};
+
+class DynamicExceptionSpec : public Node {
+ NodeArray Types;
+public:
+ DynamicExceptionSpec(NodeArray Types_)
+ : Node(KDynamicExceptionSpec), Types(Types_) {
+ for (Node *T : Types)
+ ParameterPackSize = std::min(ParameterPackSize, T->ParameterPackSize);
+ }
+
+ void printLeft(OutputStream &S) const override {
+ S += "throw(";
+ Types.printWithComma(S);
+ S += ')';
+ }
+};
+
+class FunctionEncoding final : public Node {
const Node *Ret;
const Node *Name;
NodeArray Params;
+ Qualifiers CVQuals;
+ FunctionRefQual RefQual;
public:
- TopLevelFunctionDecl(Node *Ret_, Node *Name_, NodeArray Params_)
- : Node(KTopLevelFunctionDecl, true, true), Ret(Ret_), Name(Name_),
- Params(Params_) {}
+ FunctionEncoding(Node *Ret_, Node *Name_, NodeArray Params_,
+ Qualifiers CVQuals_, FunctionRefQual RefQual_)
+ : Node(KFunctionEncoding, NoParameterPack,
+ /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
+ /*FunctionCache=*/Cache::Yes),
+ Ret(Ret_), Name(Name_), Params(Params_), CVQuals(CVQuals_),
+ RefQual(RefQual_) {
+ for (Node *P : Params)
+ ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
+ if (Ret)
+ ParameterPackSize = std::min(ParameterPackSize, Ret->ParameterPackSize);
+ }
+
+ bool hasRHSComponentSlow(OutputStream &) const override { return true; }
+ bool hasFunctionSlow(OutputStream &) const override { return true; }
+
+ Node *getName() { return const_cast<Node *>(Name); }
void printLeft(OutputStream &S) const override {
if (Ret) {
Ret->printLeft(S);
- if (!Ret->hasRHSComponent())
+ if (!Ret->hasRHSComponent(S))
S += " ";
}
Name->print(S);
@@ -628,69 +844,31 @@
void printRight(OutputStream &S) const override {
S += "(";
- Params.printWithSeperator(S, ", ");
+ Params.printWithComma(S);
S += ")";
if (Ret)
Ret->printRight(S);
- }
-};
-enum FunctionRefQual : unsigned char {
- FrefQualNone,
- FrefQualLValue,
- FrefQualRValue,
-};
+ if (CVQuals & QualConst)
+ S += " const";
+ if (CVQuals & QualVolatile)
+ S += " volatile";
+ if (CVQuals & QualRestrict)
+ S += " restrict";
-class FunctionRefQualType : public Node {
- Node *Fn;
- FunctionRefQual Quals;
-
- friend class FunctionQualType;
-
-public:
- FunctionRefQualType(Node *Fn_, FunctionRefQual Quals_)
- : Node(KFunctionRefQualType, true, true), Fn(Fn_), Quals(Quals_) {}
-
- void printQuals(OutputStream &S) const {
- if (Quals == FrefQualLValue)
+ if (RefQual == FrefQualLValue)
S += " &";
- else
+ else if (RefQual == FrefQualRValue)
S += " &&";
}
-
- void printLeft(OutputStream &S) const override { Fn->printLeft(S); }
-
- void printRight(OutputStream &S) const override {
- Fn->printRight(S);
- printQuals(S);
- }
-};
-
-class FunctionQualType final : public QualType {
-public:
- FunctionQualType(Node *Child_, Qualifiers Quals_)
- : QualType(KFunctionQualType, Child_, Quals_) {}
-
- void printLeft(OutputStream &S) const override { Child->printLeft(S); }
-
- void printRight(OutputStream &S) const override {
- if (Child->K == KFunctionRefQualType) {
- auto *RefQuals = static_cast<const FunctionRefQualType *>(Child);
- RefQuals->Fn->printRight(S);
- printQuals(S);
- RefQuals->printQuals(S);
- } else {
- Child->printRight(S);
- printQuals(S);
- }
- }
};
class LiteralOperator : public Node {
const Node *OpName;
public:
- LiteralOperator(Node *OpName_) : Node(KLiteralOperator), OpName(OpName_) {}
+ LiteralOperator(Node *OpName_)
+ : Node(KLiteralOperator, OpName_->ParameterPackSize), OpName(OpName_) {}
void printLeft(OutputStream &S) const override {
S += "operator\"\" ";
@@ -703,8 +881,9 @@
const Node *Child;
public:
- SpecialName(StringView Special_, Node *Child_)
- : Node(KSpecialName), Special(Special_), Child(Child_) {}
+ SpecialName(StringView Special_, Node* Child_)
+ : Node(KSpecialName, Child_->ParameterPackSize), Special(Special_),
+ Child(Child_) {}
void printLeft(OutputStream &S) const override {
S += Special;
@@ -718,8 +897,9 @@
public:
CtorVtableSpecialName(Node *FirstType_, Node *SecondType_)
- : Node(KCtorVtableSpecialName), FirstType(FirstType_),
- SecondType(SecondType_) {}
+ : Node(KCtorVtableSpecialName, std::min(FirstType_->ParameterPackSize,
+ SecondType_->ParameterPackSize)),
+ FirstType(FirstType_), SecondType(SecondType_) {}
void printLeft(OutputStream &S) const override {
S += "construction vtable for ";
@@ -734,27 +914,18 @@
const Node *Qualifier;
const Node *Name;
- mutable OutputStream::StreamStringView Cache;
-
public:
- QualifiedName(Node *Qualifier_, Node *Name_)
- : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {}
+ QualifiedName(Node* Qualifier_, Node* Name_)
+ : Node(KQualifiedName,
+ std::min(Qualifier_->ParameterPackSize, Name_->ParameterPackSize)),
+ Qualifier(Qualifier_), Name(Name_) {}
StringView getBaseName() const override { return Name->getBaseName(); }
void printLeft(OutputStream &S) const override {
- if (!Cache.empty()) {
- S += Cache;
- return;
- }
-
- OutputStream::StreamPosition Start = S.getCurrentPosition();
- if (Qualifier->K != KEmptyName) {
- Qualifier->print(S);
- S += "::";
- }
+ Qualifier->print(S);
+ S += "::";
Name->print(S);
- Cache = S.makeStringViewFromPastPosition(Start);
}
};
@@ -772,10 +943,17 @@
public:
VectorType(NodeOrString Dimension_)
: Node(KVectorType), BaseType(nullptr), Dimension(Dimension_),
- IsPixel(true) {}
+ IsPixel(true) {
+ if (Dimension.isNode())
+ ParameterPackSize = Dimension.asNode()->ParameterPackSize;
+ }
VectorType(Node *BaseType_, NodeOrString Dimension_)
- : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_),
- IsPixel(false) {}
+ : Node(KVectorType, BaseType_->ParameterPackSize), BaseType(BaseType_),
+ Dimension(Dimension_), IsPixel(false) {
+ if (Dimension.isNode())
+ ParameterPackSize =
+ std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
if (IsPixel) {
@@ -794,29 +972,149 @@
}
};
-class TemplateParams final : public Node {
- NodeArray Params;
-
- mutable OutputStream::StreamStringView Cache;
-
+/// An unexpanded parameter pack (either in the expression or type context). If
+/// this AST is correct, this node will have a ParameterPackExpansion node above
+/// it.
+///
+/// This node is created when some <template-args> are found that apply to an
+/// <encoding>, and is stored in the TemplateParams table. In order for this to
+/// appear in the final AST, it has to referenced via a <template-param> (ie,
+/// T_).
+class ParameterPack final : public Node {
+ NodeArray Data;
public:
- TemplateParams(NodeArray Params_) : Node(KTemplateParams), Params(Params_) {}
+ ParameterPack(NodeArray Data_)
+ : Node(KParameterPack, static_cast<unsigned>(Data_.size())), Data(Data_) {
+ ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown;
+ if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
+ return P->ArrayCache == Cache::No;
+ }))
+ ArrayCache = Cache::No;
+ if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
+ return P->FunctionCache == Cache::No;
+ }))
+ FunctionCache = Cache::No;
+ if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
+ return P->RHSComponentCache == Cache::No;
+ }))
+ RHSComponentCache = Cache::No;
+ }
+
+ bool hasRHSComponentSlow(OutputStream &S) const override {
+ size_t Idx = S.CurrentPackIndex;
+ return Idx < Data.size() && Data[Idx]->hasRHSComponent(S);
+ }
+ bool hasArraySlow(OutputStream &S) const override {
+ size_t Idx = S.CurrentPackIndex;
+ return Idx < Data.size() && Data[Idx]->hasArray(S);
+ }
+ bool hasFunctionSlow(OutputStream &S) const override {
+ size_t Idx = S.CurrentPackIndex;
+ return Idx < Data.size() && Data[Idx]->hasFunction(S);
+ }
void printLeft(OutputStream &S) const override {
- if (!Cache.empty()) {
- S += Cache;
+ size_t Idx = S.CurrentPackIndex;
+ if (Idx < Data.size())
+ Data[Idx]->printLeft(S);
+ }
+ void printRight(OutputStream &S) const override {
+ size_t Idx = S.CurrentPackIndex;
+ if (Idx < Data.size())
+ Data[Idx]->printRight(S);
+ }
+};
+
+/// A variadic template argument. This node represents an occurance of
+/// J<something>E in some <template-args>. It isn't itself unexpanded, unless
+/// one of it's Elements is. The parser inserts a ParameterPack into the
+/// TemplateParams table if the <template-args> this pack belongs to apply to an
+/// <encoding>.
+class TemplateArgumentPack final : public Node {
+ NodeArray Elements;
+public:
+ TemplateArgumentPack(NodeArray Elements_)
+ : Node(KTemplateArgumentPack), Elements(Elements_) {
+ for (Node *E : Elements)
+ ParameterPackSize = std::min(E->ParameterPackSize, ParameterPackSize);
+ }
+
+ NodeArray getElements() const { return Elements; }
+
+ void printLeft(OutputStream &S) const override {
+ Elements.printWithComma(S);
+ }
+};
+
+/// A pack expansion. Below this node, there are some unexpanded ParameterPacks
+/// which each have Child->ParameterPackSize elements.
+class ParameterPackExpansion final : public Node {
+ const Node *Child;
+
+public:
+ ParameterPackExpansion(Node* Child_)
+ : Node(KParameterPackExpansion), Child(Child_) {}
+
+ const Node *getChild() const { return Child; }
+
+ void printLeft(OutputStream &S) const override {
+ unsigned PackSize = Child->ParameterPackSize;
+ if (PackSize == NoParameterPack) {
+ Child->print(S);
+ S += "...";
return;
}
- OutputStream::StreamPosition Start = S.getCurrentPosition();
+ SwapAndRestore<unsigned> SavePackIndex(S.CurrentPackIndex, 0);
+ for (unsigned I = 0; I != PackSize; ++I) {
+ if (I != 0)
+ S += ", ";
+ S.CurrentPackIndex = I;
+ Child->print(S);
+ }
+ }
+};
+inline bool Node::isEmptyPackExpansion() const {
+ if (getKind() == KParameterPackExpansion) {
+ auto *AsPack = static_cast<const ParameterPackExpansion *>(this);
+ return AsPack->getChild()->isEmptyPackExpansion();
+ }
+ if (getKind() == KTemplateArgumentPack) {
+ auto *AsTemplateArg = static_cast<const TemplateArgumentPack *>(this);
+ for (Node *E : AsTemplateArg->getElements())
+ if (!E->isEmptyPackExpansion())
+ return false;
+ return true;
+ }
+ return ParameterPackSize == 0;
+}
+
+class TemplateArgs final : public Node {
+ NodeArray Params;
+
+public:
+ TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {
+ for (Node *P : Params)
+ ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
+ }
+
+ NodeArray getParams() { return Params; }
+
+ void printLeft(OutputStream &S) const override {
S += "<";
- Params.printWithSeperator(S, ", ");
+ bool FirstElement = true;
+ for (size_t Idx = 0, E = Params.size(); Idx != E; ++Idx) {
+ if (Params[Idx]->isEmptyPackExpansion())
+ continue;
+ if (!FirstElement)
+ S += ", ";
+ FirstElement = false;
+ Params[Idx]->print(S);
+ }
if (S.back() == '>')
S += " ";
S += ">";
-
- Cache = S.makeStringViewFromPastPosition(Start);
}
};
@@ -827,7 +1125,9 @@
public:
NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_)
- : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {}
+ : Node(KNameWithTemplateArgs, std::min(Name_->ParameterPackSize,
+ TemplateArgs_->ParameterPackSize)),
+ Name(Name_), TemplateArgs(TemplateArgs_) {}
StringView getBaseName() const override { return Name->getBaseName(); }
@@ -841,7 +1141,8 @@
Node *Child;
public:
- GlobalQualifiedName(Node *Child_) : Node(KGlobalQualifiedName), Child(Child_) {}
+ GlobalQualifiedName(Node* Child_)
+ : Node(KGlobalQualifiedName, Child_->ParameterPackSize), Child(Child_) {}
StringView getBaseName() const override { return Child->getBaseName(); }
@@ -855,7 +1156,8 @@
Node *Child;
public:
- StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {}
+ StdQualifiedName(Node *Child_)
+ : Node(KStdQualifiedName, Child_->ParameterPackSize), Child(Child_) {}
StringView getBaseName() const override { return Child->getBaseName(); }
@@ -978,7 +1280,8 @@
public:
CtorDtorName(Node *Basename_, bool IsDtor_)
- : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_) {}
+ : Node(KCtorDtorName, Basename_->ParameterPackSize),
+ Basename(Basename_), IsDtor(IsDtor_) {}
void printLeft(OutputStream &S) const override {
if (IsDtor)
@@ -991,7 +1294,9 @@
const Node *Base;
public:
- DtorName(Node *Base_) : Node(KDtorName), Base(Base_) {}
+ DtorName(Node *Base_) : Node(KDtorName), Base(Base_) {
+ ParameterPackSize = Base->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += "~";
@@ -1012,19 +1317,22 @@
}
};
-class LambdaTypeName : public Node {
+class ClosureTypeName : public Node {
NodeArray Params;
StringView Count;
public:
- LambdaTypeName(NodeArray Params_, StringView Count_)
- : Node(KLambdaTypeName), Params(Params_), Count(Count_) {}
+ ClosureTypeName(NodeArray Params_, StringView Count_)
+ : Node(KClosureTypeName), Params(Params_), Count(Count_) {
+ for (Node *P : Params)
+ ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
S += "\'lambda";
S += Count;
S += "\'(";
- Params.printWithSeperator(S, ", ");
+ Params.printWithComma(S);
S += ")";
}
};
@@ -1032,7 +1340,7 @@
// -- Expression Nodes --
struct Expr : public Node {
- Expr() : Node(KExpr) {}
+ Expr(Kind K = KExpr) : Node(K) {}
};
class BinaryExpr : public Expr {
@@ -1042,7 +1350,10 @@
public:
BinaryExpr(Node *LHS_, StringView InfixOperator_, Node *RHS_)
- : LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {}
+ : LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {
+ ParameterPackSize =
+ std::min(LHS->ParameterPackSize, RHS->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
// might be a template argument expression, then we need to disambiguate
@@ -1068,7 +1379,10 @@
const Node *Op2;
public:
- ArraySubscriptExpr(Node *Op1_, Node *Op2_) : Op1(Op1_), Op2(Op2_) {}
+ ArraySubscriptExpr(Node *Op1_, Node *Op2_) : Op1(Op1_), Op2(Op2_) {
+ ParameterPackSize =
+ std::min(Op1->ParameterPackSize, Op2->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
S += "(";
@@ -1085,7 +1399,9 @@
public:
PostfixExpr(Node *Child_, StringView Operand_)
- : Child(Child_), Operand(Operand_) {}
+ : Child(Child_), Operand(Operand_) {
+ ParameterPackSize = Child->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += "(";
@@ -1102,7 +1418,11 @@
public:
ConditionalExpr(Node *Cond_, Node *Then_, Node *Else_)
- : Cond(Cond_), Then(Then_), Else(Else_) {}
+ : Cond(Cond_), Then(Then_), Else(Else_) {
+ ParameterPackSize =
+ std::min(Cond->ParameterPackSize,
+ std::min(Then->ParameterPackSize, Else->ParameterPackSize));
+ }
void printLeft(OutputStream &S) const override {
S += "(";
@@ -1122,7 +1442,10 @@
public:
MemberExpr(Node *LHS_, StringView Kind_, Node *RHS_)
- : LHS(LHS_), Kind(Kind_), RHS(RHS_) {}
+ : LHS(LHS_), Kind(Kind_), RHS(RHS_) {
+ ParameterPackSize =
+ std::min(LHS->ParameterPackSize, RHS->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
LHS->print(S);
@@ -1138,7 +1461,9 @@
public:
EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_)
- : Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {}
+ : Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {
+ ParameterPackSize = Infix->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += Prefix;
@@ -1155,7 +1480,10 @@
public:
CastExpr(StringView CastKind_, Node *To_, Node *From_)
- : CastKind(CastKind_), To(To_), From(From_) {}
+ : CastKind(CastKind_), To(To_), From(From_) {
+ ParameterPackSize =
+ std::min(To->ParameterPackSize, From->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
S += CastKind;
@@ -1168,14 +1496,15 @@
};
class SizeofParamPackExpr : public Expr {
- NodeArray Args;
+ Node *Pack;
public:
- SizeofParamPackExpr(NodeArray Args_) : Args(Args_) {}
+ SizeofParamPackExpr(Node *Pack_) : Pack(Pack_) {}
void printLeft(OutputStream &S) const override {
S += "sizeof...(";
- Args.printWithSeperator(S, ", ");
+ ParameterPackExpansion PPE(Pack);
+ PPE.printLeft(S);
S += ")";
}
};
@@ -1185,12 +1514,16 @@
NodeArray Args;
public:
- CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) {}
+ CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) {
+ for (Node *P : Args)
+ ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
+ ParameterPackSize = std::min(ParameterPackSize, Callee->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
Callee->print(S);
S += "(";
- Args.printWithSeperator(S, ", ");
+ Args.printWithComma(S);
S += ")";
}
};
@@ -1205,8 +1538,15 @@
public:
NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_,
bool IsArray_)
- : ExprList(ExprList_), Type(Type_), InitList(InitList_), IsGlobal(IsGlobal_),
- IsArray(IsArray_) {}
+ : ExprList(ExprList_), Type(Type_), InitList(InitList_),
+ IsGlobal(IsGlobal_), IsArray(IsArray_) {
+ for (Node *E : ExprList)
+ ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize);
+ for (Node *I : InitList)
+ ParameterPackSize = std::min(ParameterPackSize, I->ParameterPackSize);
+ if (Type)
+ ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
if (IsGlobal)
@@ -1214,17 +1554,19 @@
S += "new";
if (IsArray)
S += "[]";
+ S += ' ';
if (!ExprList.empty()) {
S += "(";
- ExprList.printWithSeperator(S, ", ");
+ ExprList.printWithComma(S);
S += ")";
}
Type->print(S);
if (!InitList.empty()) {
S += "(";
- InitList.printWithSeperator(S, ", ");
+ InitList.printWithComma(S);
S += ")";
}
+
}
};
@@ -1235,7 +1577,9 @@
public:
DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_)
- : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {}
+ : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {
+ ParameterPackSize = Op->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
if (IsGlobal)
@@ -1252,7 +1596,9 @@
Node *Child;
public:
- PrefixExpr(StringView Prefix_, Node *Child_) : Prefix(Prefix_), Child(Child_) {}
+ PrefixExpr(StringView Prefix_, Node *Child_) : Prefix(Prefix_), Child(Child_) {
+ ParameterPackSize = Child->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += Prefix;
@@ -1274,33 +1620,88 @@
}
};
-class ExprList : public Expr {
- NodeArray SubExprs;
+class ConversionExpr : public Expr {
+ const Node *Type;
+ NodeArray Expressions;
public:
- ExprList(NodeArray SubExprs_) : SubExprs(SubExprs_) {}
+ ConversionExpr(const Node *Type_, NodeArray Expressions_)
+ : Type(Type_), Expressions(Expressions_) {
+ for (Node *E : Expressions)
+ ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize);
+ ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
S += "(";
- SubExprs.printWithSeperator(S, ", ");
+ Type->print(S);
+ S += ")(";
+ Expressions.printWithComma(S);
S += ")";
}
};
-class ConversionExpr : public Expr {
- NodeArray Expressions;
- NodeArray Types;
-
+class InitListExpr : public Expr {
+ Node *Ty;
+ NodeArray Inits;
public:
- ConversionExpr(NodeArray Expressions_, NodeArray Types_)
- : Expressions(Expressions_), Types(Types_) {}
+ InitListExpr(Node *Ty_, NodeArray Inits_)
+ : Ty(Ty_), Inits(Inits_) {
+ if (Ty)
+ ParameterPackSize = Ty->ParameterPackSize;
+ for (Node *I : Inits)
+ ParameterPackSize = std::min(I->ParameterPackSize, ParameterPackSize);
+ }
void printLeft(OutputStream &S) const override {
- S += "(";
- Expressions.printWithSeperator(S, ", ");
- S += ")(";
- Types.printWithSeperator(S, ", ");
- S += ")";
+ if (Ty)
+ Ty->print(S);
+ S += '{';
+ Inits.printWithComma(S);
+ S += '}';
+ }
+};
+
+class BracedExpr : public Expr {
+ Node *Elem;
+ Node *Init;
+ bool IsArray;
+public:
+ BracedExpr(Node *Elem_, Node *Init_, bool IsArray_)
+ : Expr(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {}
+
+ void printLeft(OutputStream &S) const override {
+ if (IsArray) {
+ S += '[';
+ Elem->print(S);
+ S += ']';
+ } else {
+ S += '.';
+ Elem->print(S);
+ }
+ if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)
+ S += " = ";
+ Init->print(S);
+ }
+};
+
+class BracedRangeExpr : public Expr {
+ Node *First;
+ Node *Last;
+ Node *Init;
+public:
+ BracedRangeExpr(Node *First_, Node *Last_, Node *Init_)
+ : Expr(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {}
+
+ void printLeft(OutputStream &S) const override {
+ S += '[';
+ First->print(S);
+ S += " ... ";
+ Last->print(S);
+ S += ']';
+ if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)
+ S += " = ";
+ Init->print(S);
}
};
@@ -1308,7 +1709,9 @@
const Node *Op;
public:
- ThrowExpr(Node *Op_) : Op(Op_) {}
+ ThrowExpr(Node *Op_) : Op(Op_) {
+ ParameterPackSize = Op->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += "throw ";
@@ -1333,7 +1736,9 @@
StringView Integer;
public:
- IntegerCastExpr(Node *Ty_, StringView Integer_) : Ty(Ty_), Integer(Integer_) {}
+ IntegerCastExpr(Node *Ty_, StringView Integer_) : Ty(Ty_), Integer(Integer_) {
+ ParameterPackSize = Ty->ParameterPackSize;
+ }
void printLeft(OutputStream &S) const override {
S += "(";
@@ -1571,1046 +1976,794 @@
}
};
-// Substitution table. This type is used to track the substitutions that are
-// known by the parser.
-template <size_t Size>
-class SubstitutionTable {
- // Substitutions hold the actual entries in the table, and PackIndices tells
- // us which entries are members of which pack. For example, if the
- // substitutions we're tracking are: {int, {float, FooBar}, char}, with
- // {float, FooBar} being a parameter pack, we represent the substitutions as:
- // Substitutions: int, float, FooBar, char
- // PackIndices: 0, 1, 3
- // So, PackIndicies[I] holds the offset of the begin of the Ith pack, and
- // PackIndices[I + 1] holds the offset of the end.
- PODSmallVector<Node*, Size> Substitutions;
- PODSmallVector<unsigned, Size> PackIndices;
+struct Db {
+ const char *First;
+ const char *Last;
-public:
- // Add a substitution that represents a single name to the table. This is
- // modeled as a parameter pack with just one element.
- void pushSubstitution(Node* Entry) {
- pushPack();
- pushSubstitutionIntoPack(Entry);
+ // Name stack, this is used by the parser to hold temporary names that were
+ // parsed. The parser colapses multiple names into new nodes to construct
+ // the AST. Once the parser is finished, names.size() == 1.
+ PODSmallVector<Node *, 32> Names;
+
+ // Substitution table. Itanium supports name substitutions as a means of
+ // compression. The string "S42_" refers to the 44nd entry (base-36) in this
+ // table.
+ PODSmallVector<Node *, 32> Subs;
+
+ // Template parameter table. Like the above, but referenced like "T42_".
+ // This has a smaller size compared to Subs and Names because it can be
+ // stored on the stack.
+ PODSmallVector<Node *, 8> TemplateParams;
+
+ unsigned EncodingDepth = 0;
+ bool TagTemplates = true;
+ bool FixForwardReferences = false;
+ bool TryToParseTemplateArgs = true;
+
+ BumpPointerAllocator ASTAllocator;
+
+ Db(const char *First_, const char *Last_) : First(First_), Last(Last_) {}
+
+ template <class T, class... Args> T *make(Args &&... args) {
+ return new (ASTAllocator.allocate(sizeof(T)))
+ T(std::forward<Args>(args)...);
}
- // Add a new empty pack to the table. Subsequent calls to
- // pushSubstitutionIntoPack() will add to this pack.
- void pushPack() {
- PackIndices.push_back(static_cast<unsigned>(Substitutions.size()));
- }
- void pushSubstitutionIntoPack(Node* Entry) {
- assert(!PackIndices.empty() && "No pack to push substitution into!");
- Substitutions.push_back(Entry);
+ template <class It> NodeArray makeNodeArray(It begin, It end) {
+ size_t sz = static_cast<size_t>(end - begin);
+ void *mem = ASTAllocator.allocate(sizeof(Node *) * sz);
+ Node **data = new (mem) Node *[sz];
+ std::copy(begin, end, data);
+ return NodeArray(data, sz);
}
- // Remove the last pack from the table.
- void popPack() {
- unsigned Last = PackIndices.back();
- PackIndices.pop_back();
- Substitutions.dropBack(Last);
+ NodeArray popTrailingNodeArray(size_t FromPosition) {
+ assert(FromPosition <= Names.size());
+ NodeArray res =
+ makeNodeArray(Names.begin() + (long)FromPosition, Names.end());
+ Names.dropBack(FromPosition);
+ return res;
}
- // For use in a range-for loop.
- struct NodeRange {
- Node** First;
- Node** Last;
- Node** begin() { return First; }
- Node** end() { return Last; }
+ bool consumeIf(StringView S) {
+ if (StringView(First, Last).startsWith(S)) {
+ First += S.size();
+ return true;
+ }
+ return false;
+ }
+
+ bool consumeIf(char C) {
+ if (First != Last && *First == C) {
+ ++First;
+ return true;
+ }
+ return false;
+ }
+
+ char consume() { return First != Last ? *First++ : '\0'; }
+
+ char look(unsigned Lookahead = 0) {
+ if (static_cast<size_t>(Last - First) <= Lookahead)
+ return '\0';
+ return First[Lookahead];
+ }
+
+ size_t numLeft() const { return static_cast<size_t>(Last - First); }
+
+ StringView parseNumber(bool AllowNegative = false);
+ Qualifiers parseCVQualifiers();
+ bool parsePositiveInteger(size_t *Out);
+ StringView parseBareSourceName();
+
+ bool parseSeqId(size_t *Out);
+ Node *parseSubstitution();
+ Node *parseTemplateParam();
+ Node *parseTemplateArgs();
+ Node *parseTemplateArg();
+
+ /// Parse the <expr> production.
+ Node *parseExpr();
+ Node *parsePrefixExpr(StringView Kind);
+ Node *parseBinaryExpr(StringView Kind);
+ Node *parseIntegerLiteral(StringView Lit);
+ Node *parseExprPrimary();
+ template <class Float> Node *parseFloatingLiteral();
+ Node *parseFunctionParam();
+ Node *parseNewExpr();
+ Node *parseConversionExpr();
+ Node *parseBracedExpr();
+
+ /// Parse the <type> production.
+ Node *parseType();
+ Node *parseFunctionType();
+ Node *parseVectorType();
+ Node *parseDecltype();
+ Node *parseArrayType();
+ Node *parsePointerToMemberType();
+ Node *parseClassEnumType();
+ Node *parseQualifiedType();
+
+ Node *parseEncoding();
+ bool parseCallOffset();
+ Node *parseSpecialName();
+
+ /// Holds some extra information about a <name> that is being parsed. This
+ /// information is only pertinent if the <name> refers to an <encoding>.
+ struct NameState {
+ bool CtorDtorConversion = false;
+ bool EndsWithTemplateArgs = false;
+ Qualifiers CVQualifiers = QualNone;
+ FunctionRefQual ReferenceQualifier = FrefQualNone;
};
- // Retrieve the Nth substitution. This is represented as a range, as the
- // substitution could be referring to a parameter pack.
- NodeRange nthSubstitution(size_t N) {
- assert(PackIndices[N] <= Substitutions.size());
- // The Nth parameter pack starts at offset PackIndices[N], and ends at
- // PackIndices[N + 1].
- Node** Begin = Substitutions.begin() + PackIndices[N];
- Node** End;
- if (N + 1 != PackIndices.size()) {
- assert(PackIndices[N + 1] <= Substitutions.size());
- End = Substitutions.begin() + PackIndices[N + 1];
- } else
- End = Substitutions.end();
- assert(Begin <= End);
- return NodeRange{Begin, End};
- }
+ /// Parse the <name> production>
+ Node *parseName(NameState *State = nullptr);
+ Node *parseLocalName(NameState *State);
+ Node *parseOperatorName(NameState *State);
+ Node *parseUnqualifiedName(NameState *State);
+ Node *parseUnnamedTypeName(NameState *State);
+ Node *parseSourceName(NameState *State);
+ Node *parseUnscopedName(NameState *State);
+ Node *parseNestedName(NameState *State);
+ Node *parseCtorDtorName(Node *&SoFar, NameState *State);
- size_t size() const { return PackIndices.size(); }
- bool empty() const { return PackIndices.empty(); }
- void clear() {
- Substitutions.clear();
- PackIndices.clear();
- }
+ Node *parseAbiTags(Node *N);
+
+ /// Parse the <unresolved-name> production.
+ Node *parseUnresolvedName();
+ Node *parseSimpleId();
+ Node *parseBaseUnresolvedName();
+ Node *parseUnresolvedType();
+ Node *parseDestructorName();
+
+ /// Top-level entry point into the parser.
+ Node *parse();
};
-struct Db
-{
- // Name stack, this is used by the parser to hold temporary names that were
- // parsed. The parser colapses multiple names into new nodes to construct
- // the AST. Once the parser is finished, names.size() == 1.
- PODSmallVector<Node*, 32> Names;
+const char* parse_discriminator(const char* first, const char* last);
- // Substitution table. Itanium supports name substitutions as a means of
- // compression. The string "S42_" refers to the 42nd entry in this table.
- SubstitutionTable<32> Subs;
+// <name> ::= <nested-name> // N
+// ::= <local-name> # See Scope Encoding below // Z
+// ::= <unscoped-template-name> <template-args>
+// ::= <unscoped-name>
+//
+// <unscoped-template-name> ::= <unscoped-name>
+// ::= <substitution>
+Node *Db::parseName(NameState *State) {
+ consumeIf('L'); // extension
- // Template parameter table. Like the above, but referenced like "T42_".
- // This has a smaller size compared to Subs and Names because it can be
- // stored on the stack.
- SubstitutionTable<4> TemplateParams;
+ if (look() == 'N')
+ return parseNestedName(State);
+ if (look() == 'Z')
+ return parseLocalName(State);
- Qualifiers CV = QualNone;
- FunctionRefQual RefQuals = FrefQualNone;
- unsigned EncodingDepth = 0;
- bool ParsedCtorDtorCV = false;
- bool TagTemplates = true;
- bool FixForwardReferences = false;
- bool TryToParseTemplateArgs = true;
+ // ::= <unscoped-template-name> <template-args>
+ if (look() == 'S' && look(1) != 't') {
+ Node *S = parseSubstitution();
+ if (S == nullptr)
+ return nullptr;
+ if (look() != 'I')
+ return nullptr;
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ if (State) State->EndsWithTemplateArgs = true;
+ return make<NameWithTemplateArgs>(S, TA);
+ }
- BumpPointerAllocator ASTAllocator;
-
- template <class T, class... Args> T* make(Args&& ...args)
- {
- return new (ASTAllocator.allocate(sizeof(T)))
- T(std::forward<Args>(args)...);
- }
-
- template <class It> NodeArray makeNodeArray(It begin, It end)
- {
- size_t sz = static_cast<size_t>(end - begin);
- void* mem = ASTAllocator.allocate(sizeof(Node*) * sz);
- Node** data = new (mem) Node*[sz];
- std::copy(begin, end, data);
- return NodeArray(data, sz);
- }
-
- NodeArray popTrailingNodeArray(size_t FromPosition)
- {
- assert(FromPosition <= Names.size());
- NodeArray res = makeNodeArray(
- Names.begin() + (long)FromPosition, Names.end());
- Names.dropBack(FromPosition);
- return res;
- }
-};
-
-const char* parse_type(const char* first, const char* last, Db& db);
-const char* parse_encoding(const char* first, const char* last, Db& db);
-const char* parse_name(const char* first, const char* last, Db& db,
- bool* ends_with_template_args = 0);
-const char* parse_expression(const char* first, const char* last, Db& db);
-const char* parse_template_args(const char* first, const char* last, Db& db);
-const char* parse_operator_name(const char* first, const char* last, Db& db);
-const char* parse_unqualified_name(const char* first, const char* last, Db& db);
-const char* parse_decltype(const char* first, const char* last, Db& db);
-
-// <number> ::= [n] <non-negative decimal integer>
-
-const char*
-parse_number(const char* first, const char* last)
-{
- if (first != last)
- {
- const char* t = first;
- if (*t == 'n')
- ++t;
- if (t != last)
- {
- if (*t == '0')
- {
- first = t+1;
- }
- else if ('1' <= *t && *t <= '9')
- {
- first = t+1;
- while (first != last && std::isdigit(*first))
- ++first;
- }
- }
- }
- return first;
+ Node *N = parseUnscopedName(State);
+ if (N == nullptr)
+ return nullptr;
+ // ::= <unscoped-template-name> <template-args>
+ if (look() == 'I') {
+ Subs.push_back(N);
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ if (State) State->EndsWithTemplateArgs = true;
+ return make<NameWithTemplateArgs>(N, TA);
+ }
+ // ::= <unscoped-name>
+ return N;
}
-template <class Float>
-struct FloatData;
+// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
+// := Z <function encoding> E s [<discriminator>]
+// := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
+Node *Db::parseLocalName(NameState *State) {
+ if (!consumeIf('Z'))
+ return nullptr;
+ Node *Encoding = parseEncoding();
+ if (Encoding == nullptr || !consumeIf('E'))
+ return nullptr;
-template <>
-struct FloatData<float>
-{
- static const size_t mangled_size = 8;
- static const size_t max_demangled_size = 24;
- static constexpr const char* spec = "%af";
-};
+ if (consumeIf('s')) {
+ First = parse_discriminator(First, Last);
+ return make<QualifiedName>(Encoding, make<NameType>("string literal"));
+ }
-constexpr const char* FloatData<float>::spec;
+ if (consumeIf('d')) {
+ parseNumber(true);
+ if (!consumeIf('_'))
+ return nullptr;
+ Node *N = parseName(State);
+ if (N == nullptr)
+ return nullptr;
+ return make<QualifiedName>(Encoding, N);
+ }
-template <>
-struct FloatData<double>
-{
- static const size_t mangled_size = 16;
- static const size_t max_demangled_size = 32;
- static constexpr const char* spec = "%a";
-};
+ Node *Entity = parseName(State);
+ if (Entity == nullptr)
+ return nullptr;
+ First = parse_discriminator(First, Last);
+ return make<QualifiedName>(Encoding, Entity);
+}
-constexpr const char* FloatData<double>::spec;
+// <unscoped-name> ::= <unqualified-name>
+// ::= St <unqualified-name> # ::std::
+// extension ::= StL<unqualified-name>
+Node *Db::parseUnscopedName(NameState *State) {
+ if (consumeIf("StL") || consumeIf("St")) {
+ Node *R = parseUnqualifiedName(State);
+ if (R == nullptr)
+ return nullptr;
+ return make<StdQualifiedName>(R);
+ }
+ return parseUnqualifiedName(State);
+}
-template <>
-struct FloatData<long double>
-{
-#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
- defined(__wasm__)
- static const size_t mangled_size = 32;
-#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
- static const size_t mangled_size = 16;
-#else
- static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms
-#endif
- static const size_t max_demangled_size = 40;
- static constexpr const char* spec = "%LaL";
-};
+// <unqualified-name> ::= <operator-name> [abi-tags]
+// ::= <ctor-dtor-name>
+// ::= <source-name>
+// ::= <unnamed-type-name>
+// FIXME: ::= DC <source-name>+ E # structured binding declaration
+Node *Db::parseUnqualifiedName(NameState *State) {
+ // <ctor-dtor-name>s are special-cased in parseNestedName().
+ Node *Result;
+ if (look() == 'U')
+ Result = parseUnnamedTypeName(State);
+ else if (look() >= '1' && look() <= '9')
+ Result = parseSourceName(State);
+ else
+ Result = parseOperatorName(State);
+ if (Result != nullptr)
+ Result = parseAbiTags(Result);
+ return Result;
+}
-constexpr const char* FloatData<long double>::spec;
-
-template <class Float>
-const char*
-parse_floating_number(const char* first, const char* last, Db& db)
-{
- const size_t N = FloatData<Float>::mangled_size;
- if (static_cast<std::size_t>(last - first) <= N)
- return first;
- last = first + N;
- const char* t = first;
- for (; t != last; ++t)
- {
- if (!isxdigit(*t))
- return first;
+// <unnamed-type-name> ::= Ut [<nonnegative number>] _
+// ::= <closure-type-name>
+//
+// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
+//
+// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters
+Node *Db::parseUnnamedTypeName(NameState *) {
+ if (consumeIf("Ut")) {
+ StringView Count = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ return make<UnnamedTypeName>(Count);
+ }
+ if (consumeIf("Ul")) {
+ NodeArray Params;
+ if (!consumeIf("vE")) {
+ size_t ParamsBegin = Names.size();
+ do {
+ Node *P = parseType();
+ if (P == nullptr)
+ return nullptr;
+ Names.push_back(P);
+ } while (!consumeIf('E'));
+ Params = popTrailingNodeArray(ParamsBegin);
}
- if (*t == 'E')
- {
- db.Names.push_back(
- db.make<FloatExpr<Float>>(StringView(first, t)));
- first = t + 1;
- }
- return first;
+ StringView Count = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ return make<ClosureTypeName>(Params, Count);
+ }
+ return nullptr;
}
// <source-name> ::= <positive length number> <identifier>
-
-const char*
-parse_source_name(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- char c = *first;
- if (isdigit(c) && first+1 != last)
- {
- const char* t = first+1;
- size_t n = static_cast<size_t>(c - '0');
- for (c = *t; isdigit(c); c = *t)
- {
- n = n * 10 + static_cast<size_t>(c - '0');
- if (++t == last)
- return first;
- }
- if (static_cast<size_t>(last - t) >= n)
- {
- StringView r(t, t + n);
- if (r.substr(0, 10) == "_GLOBAL__N")
- db.Names.push_back(db.make<NameType>("(anonymous namespace)"));
- else
- db.Names.push_back(db.make<NameType>(r));
- first = t + n;
- }
- }
- }
- return first;
+Node *Db::parseSourceName(NameState *) {
+ size_t Length = 0;
+ if (parsePositiveInteger(&Length))
+ return nullptr;
+ if (numLeft() < Length || Length == 0)
+ return nullptr;
+ StringView Name(First, First + Length);
+ First += Length;
+ if (Name.startsWith("_GLOBAL__N"))
+ return make<NameType>("(anonymous namespace)");
+ return make<NameType>(Name);
}
-// <substitution> ::= S <seq-id> _
-// ::= S_
-// <substitution> ::= Sa # ::std::allocator
-// <substitution> ::= Sb # ::std::basic_string
-// <substitution> ::= Ss # ::std::basic_string < char,
-// ::std::char_traits<char>,
-// ::std::allocator<char> >
-// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> >
-// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> >
-// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
-
-const char*
-parse_substitution(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- if (*first == 'S')
- {
- switch (first[1])
- {
- case 'a':
- db.Names.push_back(
- db.make<SpecialSubstitution>(
- SpecialSubKind::allocator));
- first += 2;
- break;
- case 'b':
- db.Names.push_back(
- db.make<SpecialSubstitution>(SpecialSubKind::basic_string));
- first += 2;
- break;
- case 's':
- db.Names.push_back(
- db.make<SpecialSubstitution>(
- SpecialSubKind::string));
- first += 2;
- break;
- case 'i':
- db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::istream));
- first += 2;
- break;
- case 'o':
- db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::ostream));
- first += 2;
- break;
- case 'd':
- db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::iostream));
- first += 2;
- break;
- case '_':
- if (!db.Subs.empty())
- {
- for (Node* n : db.Subs.nthSubstitution(0))
- db.Names.push_back(n);
- first += 2;
- }
- break;
- default:
- if (std::isdigit(first[1]) || std::isupper(first[1]))
- {
- size_t sub = 0;
- const char* t = first+1;
- if (std::isdigit(*t))
- sub = static_cast<size_t>(*t - '0');
- else
- sub = static_cast<size_t>(*t - 'A') + 10;
- for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t)
- {
- sub *= 36;
- if (std::isdigit(*t))
- sub += static_cast<size_t>(*t - '0');
- else
- sub += static_cast<size_t>(*t - 'A') + 10;
- }
- if (t == last || *t != '_')
- return first;
- ++sub;
- if (sub < db.Subs.size())
- {
- for (Node* n : db.Subs.nthSubstitution(sub))
- db.Names.push_back(n);
- first = t+1;
- }
- }
- break;
- }
- }
+// <operator-name> ::= aa # &&
+// ::= ad # & (unary)
+// ::= an # &
+// ::= aN # &=
+// ::= aS # =
+// ::= cl # ()
+// ::= cm # ,
+// ::= co # ~
+// ::= cv <type> # (cast)
+// ::= da # delete[]
+// ::= de # * (unary)
+// ::= dl # delete
+// ::= dv # /
+// ::= dV # /=
+// ::= eo # ^
+// ::= eO # ^=
+// ::= eq # ==
+// ::= ge # >=
+// ::= gt # >
+// ::= ix # []
+// ::= le # <=
+// ::= li <source-name> # operator ""
+// ::= ls # <<
+// ::= lS # <<=
+// ::= lt # <
+// ::= mi # -
+// ::= mI # -=
+// ::= ml # *
+// ::= mL # *=
+// ::= mm # -- (postfix in <expression> context)
+// ::= na # new[]
+// ::= ne # !=
+// ::= ng # - (unary)
+// ::= nt # !
+// ::= nw # new
+// ::= oo # ||
+// ::= or # |
+// ::= oR # |=
+// ::= pm # ->*
+// ::= pl # +
+// ::= pL # +=
+// ::= pp # ++ (postfix in <expression> context)
+// ::= ps # + (unary)
+// ::= pt # ->
+// ::= qu # ?
+// ::= rm # %
+// ::= rM # %=
+// ::= rs # >>
+// ::= rS # >>=
+// ::= ss # <=> C++2a
+// ::= v <digit> <source-name> # vendor extended operator
+Node *Db::parseOperatorName(NameState *State) {
+ switch (look()) {
+ case 'a':
+ switch (look(1)) {
+ case 'a':
+ First += 2;
+ return make<NameType>("operator&&");
+ case 'd':
+ case 'n':
+ First += 2;
+ return make<NameType>("operator&");
+ case 'N':
+ First += 2;
+ return make<NameType>("operator&=");
+ case 'S':
+ First += 2;
+ return make<NameType>("operator=");
}
- return first;
+ return nullptr;
+ case 'c':
+ switch (look(1)) {
+ case 'l':
+ First += 2;
+ return make<NameType>("operator()");
+ case 'm':
+ First += 2;
+ return make<NameType>("operator,");
+ case 'o':
+ First += 2;
+ return make<NameType>("operator~");
+ // ::= cv <type> # (cast)
+ case 'v': {
+ SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false);
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ if (State) State->CtorDtorConversion = true;
+ return make<ConversionOperatorType>(Ty);
+ }
+ }
+ return nullptr;
+ case 'd':
+ switch (look(1)) {
+ case 'a':
+ First += 2;
+ return make<NameType>("operator delete[]");
+ case 'e':
+ First += 2;
+ return make<NameType>("operator*");
+ case 'l':
+ First += 2;
+ return make<NameType>("operator delete");
+ case 'v':
+ First += 2;
+ return make<NameType>("operator/");
+ case 'V':
+ First += 2;
+ return make<NameType>("operator/=");
+ }
+ return nullptr;
+ case 'e':
+ switch (look(1)) {
+ case 'o':
+ First += 2;
+ return make<NameType>("operator^");
+ case 'O':
+ First += 2;
+ return make<NameType>("operator^=");
+ case 'q':
+ First += 2;
+ return make<NameType>("operator==");
+ }
+ return nullptr;
+ case 'g':
+ switch (look(1)) {
+ case 'e':
+ First += 2;
+ return make<NameType>("operator>=");
+ case 't':
+ First += 2;
+ return make<NameType>("operator>");
+ }
+ return nullptr;
+ case 'i':
+ if (look(1) == 'x') {
+ First += 2;
+ return make<NameType>("operator[]");
+ }
+ return nullptr;
+ case 'l':
+ switch (look(1)) {
+ case 'e':
+ First += 2;
+ return make<NameType>("operator<=");
+ // ::= li <source-name> # operator ""
+ case 'i': {
+ First += 2;
+ Node *SN = parseSourceName(State);
+ if (SN == nullptr)
+ return nullptr;
+ return make<LiteralOperator>(SN);
+ }
+ case 's':
+ First += 2;
+ return make<NameType>("operator<<");
+ case 'S':
+ First += 2;
+ return make<NameType>("operator<<=");
+ case 't':
+ First += 2;
+ return make<NameType>("operator<");
+ }
+ return nullptr;
+ case 'm':
+ switch (look(1)) {
+ case 'i':
+ First += 2;
+ return make<NameType>("operator-");
+ case 'I':
+ First += 2;
+ return make<NameType>("operator-=");
+ case 'l':
+ First += 2;
+ return make<NameType>("operator*");
+ case 'L':
+ First += 2;
+ return make<NameType>("operator*=");
+ case 'm':
+ First += 2;
+ return make<NameType>("operator--");
+ }
+ return nullptr;
+ case 'n':
+ switch (look(1)) {
+ case 'a':
+ First += 2;
+ return make<NameType>("operator new[]");
+ case 'e':
+ First += 2;
+ return make<NameType>("operator!=");
+ case 'g':
+ First += 2;
+ return make<NameType>("operator-");
+ case 't':
+ First += 2;
+ return make<NameType>("operator!");
+ case 'w':
+ First += 2;
+ return make<NameType>("operator new");
+ }
+ return nullptr;
+ case 'o':
+ switch (look(1)) {
+ case 'o':
+ First += 2;
+ return make<NameType>("operator||");
+ case 'r':
+ First += 2;
+ return make<NameType>("operator|");
+ case 'R':
+ First += 2;
+ return make<NameType>("operator|=");
+ }
+ return nullptr;
+ case 'p':
+ switch (look(1)) {
+ case 'm':
+ First += 2;
+ return make<NameType>("operator->*");
+ case 'l':
+ First += 2;
+ return make<NameType>("operator+");
+ case 'L':
+ First += 2;
+ return make<NameType>("operator+=");
+ case 'p':
+ First += 2;
+ return make<NameType>("operator++");
+ case 's':
+ First += 2;
+ return make<NameType>("operator+");
+ case 't':
+ First += 2;
+ return make<NameType>("operator->");
+ }
+ return nullptr;
+ case 'q':
+ if (look(1) == 'u') {
+ First += 2;
+ return make<NameType>("operator?");
+ }
+ return nullptr;
+ case 'r':
+ switch (look(1)) {
+ case 'm':
+ First += 2;
+ return make<NameType>("operator%");
+ case 'M':
+ First += 2;
+ return make<NameType>("operator%=");
+ case 's':
+ First += 2;
+ return make<NameType>("operator>>");
+ case 'S':
+ First += 2;
+ return make<NameType>("operator>>=");
+ }
+ return nullptr;
+ case 's':
+ if (look(1) == 's') {
+ First += 2;
+ return make<NameType>("operator<=>");
+ }
+ return nullptr;
+ // ::= v <digit> <source-name> # vendor extended operator
+ case 'v':
+ if (std::isdigit(look(1))) {
+ First += 2;
+ Node *SN = parseSourceName(State);
+ if (SN == nullptr)
+ return nullptr;
+ return make<ConversionOperatorType>(SN);
+ }
+ return nullptr;
+ }
+ return nullptr;
}
-// <builtin-type> ::= v # void
-// ::= w # wchar_t
-// ::= b # bool
-// ::= c # char
-// ::= a # signed char
-// ::= h # unsigned char
-// ::= s # short
-// ::= t # unsigned short
-// ::= i # int
-// ::= j # unsigned int
-// ::= l # long
-// ::= m # unsigned long
-// ::= x # long long, __int64
-// ::= y # unsigned long long, __int64
-// ::= n # __int128
-// ::= o # unsigned __int128
-// ::= f # float
-// ::= d # double
-// ::= e # long double, __float80
-// ::= g # __float128
-// ::= z # ellipsis
-// ::= Dd # IEEE 754r decimal floating point (64 bits)
-// ::= De # IEEE 754r decimal floating point (128 bits)
-// ::= Df # IEEE 754r decimal floating point (32 bits)
-// ::= Dh # IEEE 754r half-precision floating point (16 bits)
-// ::= Di # char32_t
-// ::= Ds # char16_t
-// ::= Da # auto (in dependent new-expressions)
-// ::= Dc # decltype(auto)
-// ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
-// ::= u <source-name> # vendor extended type
-
-const char*
-parse_builtin_type(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- switch (*first)
- {
- case 'v':
- db.Names.push_back(db.make<NameType>("void"));
- ++first;
- break;
- case 'w':
- db.Names.push_back(db.make<NameType>("wchar_t"));
- ++first;
- break;
- case 'b':
- db.Names.push_back(db.make<NameType>("bool"));
- ++first;
- break;
- case 'c':
- db.Names.push_back(db.make<NameType>("char"));
- ++first;
- break;
- case 'a':
- db.Names.push_back(db.make<NameType>("signed char"));
- ++first;
- break;
- case 'h':
- db.Names.push_back(db.make<NameType>("unsigned char"));
- ++first;
- break;
- case 's':
- db.Names.push_back(db.make<NameType>("short"));
- ++first;
- break;
- case 't':
- db.Names.push_back(db.make<NameType>("unsigned short"));
- ++first;
- break;
- case 'i':
- db.Names.push_back(db.make<NameType>("int"));
- ++first;
- break;
- case 'j':
- db.Names.push_back(db.make<NameType>("unsigned int"));
- ++first;
- break;
- case 'l':
- db.Names.push_back(db.make<NameType>("long"));
- ++first;
- break;
- case 'm':
- db.Names.push_back(db.make<NameType>("unsigned long"));
- ++first;
- break;
- case 'x':
- db.Names.push_back(db.make<NameType>("long long"));
- ++first;
- break;
- case 'y':
- db.Names.push_back(db.make<NameType>("unsigned long long"));
- ++first;
- break;
- case 'n':
- db.Names.push_back(db.make<NameType>("__int128"));
- ++first;
- break;
- case 'o':
- db.Names.push_back(db.make<NameType>("unsigned __int128"));
- ++first;
- break;
- case 'f':
- db.Names.push_back(db.make<NameType>("float"));
- ++first;
- break;
- case 'd':
- db.Names.push_back(db.make<NameType>("double"));
- ++first;
- break;
- case 'e':
- db.Names.push_back(db.make<NameType>("long double"));
- ++first;
- break;
- case 'g':
- db.Names.push_back(db.make<NameType>("__float128"));
- ++first;
- break;
- case 'z':
- db.Names.push_back(db.make<NameType>("..."));
- ++first;
- break;
- case 'u':
- {
- const char*t = parse_source_name(first+1, last, db);
- if (t != first+1)
- first = t;
- }
- break;
- case 'D':
- if (first+1 != last)
- {
- switch (first[1])
- {
- case 'd':
- db.Names.push_back(db.make<NameType>("decimal64"));
- first += 2;
- break;
- case 'e':
- db.Names.push_back(db.make<NameType>("decimal128"));
- first += 2;
- break;
- case 'f':
- db.Names.push_back(db.make<NameType>("decimal32"));
- first += 2;
- break;
- case 'h':
- db.Names.push_back(db.make<NameType>("decimal16"));
- first += 2;
- break;
- case 'i':
- db.Names.push_back(db.make<NameType>("char32_t"));
- first += 2;
- break;
- case 's':
- db.Names.push_back(db.make<NameType>("char16_t"));
- first += 2;
- break;
- case 'a':
- db.Names.push_back(db.make<NameType>("auto"));
- first += 2;
- break;
- case 'c':
- db.Names.push_back(db.make<NameType>("decltype(auto)"));
- first += 2;
- break;
- case 'n':
- db.Names.push_back(db.make<NameType>("std::nullptr_t"));
- first += 2;
- break;
- }
- }
- break;
- }
+// <ctor-dtor-name> ::= C1 # complete object constructor
+// ::= C2 # base object constructor
+// ::= C3 # complete object allocating constructor
+// extension ::= C5 # ?
+// ::= D0 # deleting destructor
+// ::= D1 # complete object destructor
+// ::= D2 # base object destructor
+// extension ::= D5 # ?
+Node *Db::parseCtorDtorName(Node *&SoFar, NameState *State) {
+ if (SoFar->K == Node::KSpecialSubstitution) {
+ auto SSK = static_cast<SpecialSubstitution *>(SoFar)->SSK;
+ switch (SSK) {
+ case SpecialSubKind::string:
+ case SpecialSubKind::istream:
+ case SpecialSubKind::ostream:
+ case SpecialSubKind::iostream:
+ SoFar = make<ExpandedSpecialSubstitution>(SSK);
+ default:
+ break;
}
- return first;
+ }
+
+ if (consumeIf('C')) {
+ bool IsInherited = consumeIf('I');
+ if (look() != '1' && look() != '2' && look() != '3' && look() != '5')
+ return nullptr;
+ ++First;
+ if (State) State->CtorDtorConversion = true;
+ if (IsInherited) {
+ if (parseName() == nullptr)
+ return nullptr;
+ }
+ return make<CtorDtorName>(SoFar, false);
+ }
+
+ if (look() == 'D' &&
+ (look(1) == '0' || look(1) == '1' || look(1) == '2' || look(1) == '5')) {
+ First += 2;
+ if (State) State->CtorDtorConversion = true;
+ return make<CtorDtorName>(SoFar, true);
+ }
+
+ return nullptr;
}
-// <CV-Qualifiers> ::= [r] [V] [K]
+// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
+// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
+//
+// <prefix> ::= <prefix> <unqualified-name>
+// ::= <template-prefix> <template-args>
+// ::= <template-param>
+// ::= <decltype>
+// ::= # empty
+// ::= <substitution>
+// ::= <prefix> <data-member-prefix>
+// extension ::= L
+//
+// <template-prefix> ::= <prefix> <template unqualified-name>
+// ::= <template-param>
+// ::= <substitution>
+Node *Db::parseNestedName(NameState *State) {
+ if (!consumeIf('N'))
+ return nullptr;
-const char*
-parse_cv_qualifiers(const char* first, const char* last, Qualifiers& cv)
-{
- cv = QualNone;
- if (first != last)
- {
- if (*first == 'r')
- {
- addQualifiers(cv, QualRestrict);
- ++first;
- }
- if (*first == 'V')
- {
- addQualifiers(cv, QualVolatile);
- ++first;
- }
- if (*first == 'K')
- {
- addQualifiers(cv, QualConst);
- ++first;
- }
+ Qualifiers CVTmp = parseCVQualifiers();
+ if (State) State->CVQualifiers = CVTmp;
+
+ if (consumeIf('O')) {
+ if (State) State->ReferenceQualifier = FrefQualRValue;
+ } else if (consumeIf('R')) {
+ if (State) State->ReferenceQualifier = FrefQualLValue;
+ } else
+ if (State) State->ReferenceQualifier = FrefQualNone;
+
+ Node *SoFar = nullptr;
+ auto PushComponent = [&](Node *Comp) {
+ if (SoFar) SoFar = make<QualifiedName>(SoFar, Comp);
+ else SoFar = Comp;
+ if (State) State->EndsWithTemplateArgs = false;
+ };
+
+ if (consumeIf("St"))
+ SoFar = make<NameType>("std");
+
+ while (!consumeIf('E')) {
+ consumeIf('L'); // extension
+
+ // ::= <template-param>
+ if (look() == 'T') {
+ Node *TP = parseTemplateParam();
+ if (TP == nullptr)
+ return nullptr;
+ PushComponent(TP);
+ Subs.push_back(SoFar);
+ continue;
}
- return first;
-}
-// <template-param> ::= T_ # first template parameter
-// ::= T <parameter-2 non-negative number> _
-
-const char*
-parse_template_param(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- if (*first == 'T')
- {
- if (first[1] == '_')
- {
- if (!db.TemplateParams.empty())
- {
- for (Node *t : db.TemplateParams.nthSubstitution(0))
- db.Names.push_back(t);
- first += 2;
- }
- else
- {
- db.Names.push_back(db.make<NameType>("T_"));
- first += 2;
- db.FixForwardReferences = true;
- }
- }
- else if (isdigit(first[1]))
- {
- const char* t = first+1;
- size_t sub = static_cast<size_t>(*t - '0');
- for (++t; t != last && isdigit(*t); ++t)
- {
- sub *= 10;
- sub += static_cast<size_t>(*t - '0');
- }
- if (t == last || *t != '_')
- return first;
- ++sub;
- if (sub < db.TemplateParams.size())
- {
- for (Node *temp : db.TemplateParams.nthSubstitution(sub))
- db.Names.push_back(temp);
- first = t+1;
- }
- else
- {
- db.Names.push_back(
- db.make<NameType>(StringView(first, t + 1)));
- first = t+1;
- db.FixForwardReferences = true;
- }
- }
- }
+ // ::= <template-prefix> <template-args>
+ if (look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr || SoFar == nullptr)
+ return nullptr;
+ SoFar = make<NameWithTemplateArgs>(SoFar, TA);
+ if (State) State->EndsWithTemplateArgs = true;
+ Subs.push_back(SoFar);
+ continue;
}
- return first;
-}
-// cc <type> <expression> # const_cast<type> (expression)
-
-const char*
-parse_const_cast_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'c' && first[1] == 'c')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto from_expr = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<CastExpr>(
- "const_cast", db.Names.back(), from_expr);
- first = t1;
- }
- }
+ // ::= <decltype>
+ if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
+ Node *DT = parseDecltype();
+ if (DT == nullptr)
+ return nullptr;
+ PushComponent(DT);
+ Subs.push_back(SoFar);
+ continue;
}
- return first;
-}
-// dc <type> <expression> # dynamic_cast<type> (expression)
-
-const char*
-parse_dynamic_cast_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'd' && first[1] == 'c')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto from_expr = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<CastExpr>(
- "dynamic_cast", db.Names.back(), from_expr);
- first = t1;
- }
- }
+ // ::= <substitution>
+ if (look() == 'S' && look(1) != 't') {
+ Node *S = parseSubstitution();
+ if (S == nullptr)
+ return nullptr;
+ PushComponent(S);
+ if (SoFar != S)
+ Subs.push_back(S);
+ continue;
}
- return first;
-}
-// rc <type> <expression> # reinterpret_cast<type> (expression)
-
-const char*
-parse_reinterpret_cast_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'r' && first[1] == 'c')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto from_expr = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<CastExpr>(
- "reinterpret_cast", db.Names.back(), from_expr);
- first = t1;
- }
- }
+ // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
+ if (look() == 'C' || look() == 'D') {
+ if (SoFar == nullptr)
+ return nullptr;
+ Node *CtorDtor = parseCtorDtorName(SoFar, State);
+ if (CtorDtor == nullptr)
+ return nullptr;
+ PushComponent(CtorDtor);
+ SoFar = parseAbiTags(SoFar);
+ if (SoFar == nullptr)
+ return nullptr;
+ Subs.push_back(SoFar);
+ continue;
}
- return first;
-}
-// sc <type> <expression> # static_cast<type> (expression)
+ // ::= <prefix> <unqualified-name>
+ Node *N = parseUnqualifiedName(State);
+ if (N == nullptr)
+ return nullptr;
+ PushComponent(N);
+ Subs.push_back(SoFar);
+ }
-const char*
-parse_static_cast_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 'c')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto from_expr = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<CastExpr>(
- "static_cast", db.Names.back(), from_expr);
- first = t1;
- }
- }
- }
- return first;
-}
+ if (SoFar == nullptr || Subs.empty())
+ return nullptr;
-// sp <expression> # pack expansion
-
-const char*
-parse_pack_expansion(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 'p')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- first = t;
- }
- return first;
-}
-
-// st <type> # sizeof (a type)
-
-const char*
-parse_sizeof_type_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 't')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<EnclosingExpr>(
- "sizeof (", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-// sz <expr> # sizeof (a expression)
-
-const char*
-parse_sizeof_expr_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 'z')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<EnclosingExpr>(
- "sizeof (", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-// sZ <template-param> # size of a parameter pack
-
-const char*
-parse_sizeof_param_pack_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T')
- {
- size_t k0 = db.Names.size();
- const char* t = parse_template_param(first+2, last, db);
- size_t k1 = db.Names.size();
- if (t != first+2 && k0 <= k1)
- {
- Node* sizeof_expr = db.make<SizeofParamPackExpr>(
- db.popTrailingNodeArray(k0));
- db.Names.push_back(sizeof_expr);
- first = t;
- }
- }
- return first;
-}
-
-// <function-param> ::= fp <top-level CV-Qualifiers> _ # L == 0, first parameter
-// ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
-// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter
-// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters
-
-const char*
-parse_function_param(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && *first == 'f')
- {
- if (first[1] == 'p')
- {
- Qualifiers cv;
- const char* t = parse_cv_qualifiers(first+2, last, cv);
- const char* t1 = parse_number(t, last);
- if (t1 != last && *t1 == '_')
- {
- db.Names.push_back(
- db.make<FunctionParam>(StringView(t, t1)));
- first = t1+1;
- }
- }
- else if (first[1] == 'L')
- {
- Qualifiers cv;
- const char* t0 = parse_number(first+2, last);
- if (t0 != last && *t0 == 'p')
- {
- ++t0;
- const char* t = parse_cv_qualifiers(t0, last, cv);
- const char* t1 = parse_number(t, last);
- if (t1 != last && *t1 == '_')
- {
- db.Names.push_back(
- db.make<FunctionParam>(StringView(t, t1)));
- first = t1+1;
- }
- }
- }
- }
- return first;
-}
-
-// sZ <function-param> # size of a function parameter pack
-
-const char*
-parse_sizeof_function_param_pack_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f')
- {
- const char* t = parse_function_param(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<EnclosingExpr>(
- "sizeof...(", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-// te <expression> # typeid (expression)
-// ti <type> # typeid (type)
-
-const char*
-parse_typeid_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i'))
- {
- const char* t;
- if (first[1] == 'e')
- t = parse_expression(first+2, last, db);
- else
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<EnclosingExpr>(
- "typeid(", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-// tw <expression> # throw expression
-
-const char*
-parse_throw_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 't' && first[1] == 'w')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<ThrowExpr>(db.Names.back());
- first = t;
- }
- }
- return first;
-}
-
-// ds <expression> <expression> # expr.*expr
-
-const char*
-parse_dot_star_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'd' && first[1] == 's')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto rhs_expr = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<MemberExpr>(
- db.Names.back(), ".*", rhs_expr);
- first = t1;
- }
- }
- }
- return first;
+ Subs.pop_back();
+ return SoFar;
}
// <simple-id> ::= <source-name> [ <template-args> ]
+Node *Db::parseSimpleId() {
+ Node *SN = parseSourceName(/*NameState=*/nullptr);
+ if (SN == nullptr)
+ return nullptr;
+ if (look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ return make<NameWithTemplateArgs>(SN, TA);
+ }
+ return SN;
+}
-const char*
-parse_simple_id(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- const char* t = parse_source_name(first, last, db);
- if (t != first)
- {
- const char* t1 = parse_template_args(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<NameWithTemplateArgs>(db.Names.back(), args);
- }
- first = t1;
- }
- else
- first = t;
- }
- return first;
+// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
+// ::= <simple-id> # e.g., ~A<2*N>
+Node *Db::parseDestructorName() {
+ Node *Result;
+ if (std::isdigit(look()))
+ Result = parseSimpleId();
+ else
+ Result = parseUnresolvedType();
+ if (Result == nullptr)
+ return nullptr;
+ return make<DtorName>(Result);
}
// <unresolved-type> ::= <template-param>
// ::= <decltype>
// ::= <substitution>
-
-const char*
-parse_unresolved_type(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- const char* t = first;
- switch (*first)
- {
- case 'T':
- {
- size_t k0 = db.Names.size();
- t = parse_template_param(first, last, db);
- size_t k1 = db.Names.size();
- if (t != first && k1 == k0 + 1)
- {
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- else
- {
- for (; k1 != k0; --k1)
- db.Names.pop_back();
- }
- break;
- }
- case 'D':
- t = parse_decltype(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- break;
- case 'S':
- t = parse_substitution(first, last, db);
- if (t != first)
- first = t;
- else
- {
- if (last - first > 2 && first[1] == 't')
- {
- t = parse_unqualified_name(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<StdQualifiedName>(db.Names.back());
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- }
- }
- break;
- }
- }
- return first;
-}
-
-// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
-// ::= <simple-id> # e.g., ~A<2*N>
-
-const char*
-parse_destructor_name(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- const char* t = parse_unresolved_type(first, last, db);
- if (t == first)
- t = parse_simple_id(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<DtorName>(db.Names.back());
- first = t;
- }
- }
- return first;
+Node *Db::parseUnresolvedType() {
+ if (look() == 'T') {
+ Node *TP = parseTemplateParam();
+ if (TP == nullptr)
+ return nullptr;
+ Subs.push_back(TP);
+ return TP;
+ }
+ if (look() == 'D') {
+ Node *DT = parseDecltype();
+ if (DT == nullptr)
+ return nullptr;
+ Subs.push_back(DT);
+ return DT;
+ }
+ return parseSubstitution();
}
// <base-unresolved-name> ::= <simple-id> # unresolved name
@@ -2620,73 +2773,25 @@
// ::= on <operator-name> <template-args> # unresolved operator template-id
// ::= dn <destructor-name> # destructor or pseudo-destructor;
// # e.g. ~X or ~X<N-1>
+Node *Db::parseBaseUnresolvedName() {
+ if (std::isdigit(look()))
+ return parseSimpleId();
-const char*
-parse_base_unresolved_name(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n')
- {
- if (first[0] == 'o')
- {
- const char* t = parse_operator_name(first+2, last, db);
- if (t != first+2)
- {
- first = parse_template_args(t, last, db);
- if (first != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<NameWithTemplateArgs>(
- db.Names.back(), args);
- }
- }
- }
- else
- {
- const char* t = parse_destructor_name(first+2, last, db);
- if (t != first+2)
- first = t;
- }
- }
- else
- {
- const char* t = parse_simple_id(first, last, db);
- if (t == first)
- {
- t = parse_operator_name(first, last, db);
- if (t != first)
- {
- first = parse_template_args(t, last, db);
- if (first != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<NameWithTemplateArgs>(
- db.Names.back(), args);
- }
- }
- }
- else
- first = t;
- }
- }
- return first;
-}
+ if (consumeIf("dn"))
+ return parseDestructorName();
-// <unresolved-qualifier-level> ::= <simple-id>
+ consumeIf("on");
-const char*
-parse_unresolved_qualifier_level(const char* first, const char* last, Db& db)
-{
- return parse_simple_id(first, last, db);
+ Node *Oper = parseOperatorName(/*NameState=*/nullptr);
+ if (Oper == nullptr)
+ return nullptr;
+ if (look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ return make<NameWithTemplateArgs>(Oper, TA);
+ }
+ return Oper;
}
// <unresolved-name>
@@ -2698,220 +2803,767 @@
// extension ::= sr <unresolved-type> <template-args> <base-unresolved-name>
// # T::N::x /decltype(p)::N::x
// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+//
+// <unresolved-qualifier-level> ::= <simple-id>
+Node *Db::parseUnresolvedName() {
+ Node *SoFar = nullptr;
-const char*
-parse_unresolved_name(const char* first, const char* last, Db& db)
-{
- if (last - first > 2)
- {
- const char* t = first;
- bool global = false;
- if (t[0] == 'g' && t[1] == 's')
- {
- global = true;
- t += 2;
- }
- const char* t2 = parse_base_unresolved_name(t, last, db);
- if (t2 != t)
- {
- if (global)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<GlobalQualifiedName>(db.Names.back());
- }
- first = t2;
- }
- else if (last - t > 2 && t[0] == 's' && t[1] == 'r')
- {
- if (t[2] == 'N')
- {
- t += 3;
- const char* t1 = parse_unresolved_type(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- t1 = parse_template_args(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<NameWithTemplateArgs>(
- db.Names.back(), args);
- t = t1;
- if (t == last)
- {
- db.Names.pop_back();
- return first;
- }
- }
- while (*t != 'E')
- {
- t1 = parse_unresolved_qualifier_level(t, last, db);
- if (t1 == t || t1 == last || db.Names.size() < 2)
- return first;
- auto s = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), s);
- t = t1;
- }
- ++t;
- t1 = parse_base_unresolved_name(t, last, db);
- if (t1 == t)
- {
- if (!db.Names.empty())
- db.Names.pop_back();
- return first;
- }
- if (db.Names.size() < 2)
- return first;
- auto s = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), s);
- first = t1;
- }
- else
- {
- t += 2;
- const char* t1 = parse_unresolved_type(t, last, db);
- if (t1 != t)
- {
- t = t1;
- t1 = parse_template_args(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<NameWithTemplateArgs>(
- db.Names.back(), args);
- t = t1;
- }
- t1 = parse_base_unresolved_name(t, last, db);
- if (t1 == t)
- {
- if (!db.Names.empty())
- db.Names.pop_back();
- return first;
- }
- if (db.Names.size() < 2)
- return first;
- auto s = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), s);
- first = t1;
- }
- else
- {
- t1 = parse_unresolved_qualifier_level(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- if (global)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<GlobalQualifiedName>(
- db.Names.back());
- }
- while (*t != 'E')
- {
- t1 = parse_unresolved_qualifier_level(t, last, db);
- if (t1 == t || t1 == last || db.Names.size() < 2)
- return first;
- auto s = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<QualifiedName>(
- db.Names.back(), s);
- t = t1;
- }
- ++t;
- t1 = parse_base_unresolved_name(t, last, db);
- if (t1 == t)
- {
- if (!db.Names.empty())
- db.Names.pop_back();
- return first;
- }
- if (db.Names.size() < 2)
- return first;
- auto s = db.Names.back();
- db.Names.pop_back();
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), s);
- first = t1;
- }
- }
- }
+ // srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>
+ // srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+ if (consumeIf("srN")) {
+ SoFar = parseUnresolvedType();
+ if (SoFar == nullptr)
+ return nullptr;
+
+ if (look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ SoFar = make<NameWithTemplateArgs>(SoFar, TA);
}
- return first;
+
+ while (!consumeIf('E')) {
+ Node *Qual = parseSimpleId();
+ if (Qual == nullptr)
+ return nullptr;
+ SoFar = make<QualifiedName>(SoFar, Qual);
+ }
+
+ Node *Base = parseBaseUnresolvedName();
+ if (Base == nullptr)
+ return nullptr;
+ return make<QualifiedName>(SoFar, Base);
+ }
+
+ bool Global = consumeIf("gs");
+
+ // [gs] <base-unresolved-name> # x or (with "gs") ::x
+ if (!consumeIf("sr")) {
+ SoFar = parseBaseUnresolvedName();
+ if (SoFar == nullptr)
+ return nullptr;
+ if (Global)
+ SoFar = make<GlobalQualifiedName>(SoFar);
+ return SoFar;
+ }
+
+ // [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+ if (std::isdigit(look())) {
+ do {
+ Node *Qual = parseSimpleId();
+ if (Qual == nullptr)
+ return nullptr;
+ if (SoFar)
+ SoFar = make<QualifiedName>(SoFar, Qual);
+ else if (Global)
+ SoFar = make<GlobalQualifiedName>(Qual);
+ else
+ SoFar = Qual;
+ } while (!consumeIf('E'));
+ }
+ // sr <unresolved-type> <base-unresolved-name>
+ // sr <unresolved-type> <template-args> <base-unresolved-name>
+ else {
+ SoFar = parseUnresolvedType();
+ if (SoFar == nullptr)
+ return nullptr;
+
+ if (look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ SoFar = make<NameWithTemplateArgs>(SoFar, TA);
+ }
+ }
+
+ assert(SoFar != nullptr);
+
+ Node *Base = parseBaseUnresolvedName();
+ if (Base == nullptr)
+ return nullptr;
+ return make<QualifiedName>(SoFar, Base);
}
-// dt <expression> <unresolved-name> # expr.name
-
-const char*
-parse_dot_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'd' && first[1] == 't')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_unresolved_name(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<MemberExpr>(db.Names.back(), ".", name);
- first = t1;
- }
- }
- }
- return first;
+// <abi-tags> ::= <abi-tag> [<abi-tags>]
+// <abi-tag> ::= B <source-name>
+Node *Db::parseAbiTags(Node *N) {
+ while (consumeIf('B')) {
+ StringView SN = parseBareSourceName();
+ if (SN.empty())
+ return nullptr;
+ N = make<AbiTagAttr>(N, SN);
+ }
+ return N;
}
-// cl <expression>+ E # call
+// <number> ::= [n] <non-negative decimal integer>
+StringView Db::parseNumber(bool AllowNegative) {
+ const char *Tmp = First;
+ if (AllowNegative)
+ consumeIf('n');
+ if (numLeft() == 0 || !std::isdigit(*First))
+ return StringView();
+ while (numLeft() != 0 && std::isdigit(*First))
+ ++First;
+ return StringView(Tmp, First);
+}
-const char*
-parse_call_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 4 && first[0] == 'c' && first[1] == 'l')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t == last || t == first + 2 || db.Names.empty())
- return first;
- Node* callee = db.Names.back();
- db.Names.pop_back();
- size_t args_begin = db.Names.size();
- while (*t != 'E')
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 == last || t1 == t)
- return first;
- t = t1;
- }
- if (db.Names.size() < args_begin)
- return first;
- ++t;
- CallExpr* the_call = db.make<CallExpr>(
- callee, db.popTrailingNodeArray(args_begin));
- db.Names.push_back(the_call);
- first = t;
+// <positive length number> ::= [0-9]*
+bool Db::parsePositiveInteger(size_t *Out) {
+ *Out = 0;
+ if (look() < '0' || look() > '9')
+ return true;
+ while (look() >= '0' && look() <= '9') {
+ *Out *= 10;
+ *Out += static_cast<size_t>(consume() - '0');
+ }
+ return false;
+}
+
+StringView Db::parseBareSourceName() {
+ size_t Int = 0;
+ if (parsePositiveInteger(&Int) || numLeft() < Int)
+ return StringView();
+ StringView R(First, First + Int);
+ First += Int;
+ return R;
+}
+
+// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
+//
+// <exception-spec> ::= Do # non-throwing exception-specification (e.g., noexcept, throw())
+// ::= DO <expression> E # computed (instantiation-dependent) noexcept
+// ::= Dw <type>+ E # dynamic exception specification with instantiation-dependent types
+//
+// <ref-qualifier> ::= R # & ref-qualifier
+// <ref-qualifier> ::= O # && ref-qualifier
+Node *Db::parseFunctionType() {
+ Qualifiers CVQuals = parseCVQualifiers();
+
+ Node *ExceptionSpec = nullptr;
+ if (consumeIf("Do")) {
+ ExceptionSpec = make<NameType>("noexcept");
+ } else if (consumeIf("DO")) {
+ Node *E = parseExpr();
+ if (E == nullptr || !consumeIf('E'))
+ return nullptr;
+ ExceptionSpec = make<NoexceptSpec>(E);
+ } else if (consumeIf("Dw")) {
+ size_t SpecsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *T = parseType();
+ if (T == nullptr)
+ return nullptr;
+ Names.push_back(T);
}
- return first;
+ ExceptionSpec =
+ make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin));
+ }
+
+ consumeIf("Dx"); // transaction safe
+
+ if (!consumeIf('F'))
+ return nullptr;
+ consumeIf('Y'); // extern "C"
+ Node *ReturnType = parseType();
+ if (ReturnType == nullptr)
+ return nullptr;
+
+ FunctionRefQual ReferenceQualifier = FrefQualNone;
+ size_t ParamsBegin = Names.size();
+ while (true) {
+ if (consumeIf('E'))
+ break;
+ if (consumeIf('v'))
+ continue;
+ if (consumeIf("RE")) {
+ ReferenceQualifier = FrefQualLValue;
+ break;
+ }
+ if (consumeIf("OE")) {
+ ReferenceQualifier = FrefQualRValue;
+ break;
+ }
+ Node *T = parseType();
+ if (T == nullptr)
+ return nullptr;
+ Names.push_back(T);
+ }
+
+ NodeArray Params = popTrailingNodeArray(ParamsBegin);
+ return make<FunctionType>(ReturnType, Params, CVQuals,
+ ReferenceQualifier, ExceptionSpec);
+}
+
+// extension:
+// <vector-type> ::= Dv <positive dimension number> _ <extended element type>
+// ::= Dv [<dimension expression>] _ <element type>
+// <extended element type> ::= <element type>
+// ::= p # AltiVec vector pixel
+Node *Db::parseVectorType() {
+ if (!consumeIf("Dv"))
+ return nullptr;
+ if (look() >= '1' && look() <= '9') {
+ StringView DimensionNumber = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ if (consumeIf('p'))
+ return make<VectorType>(DimensionNumber);
+ Node *ElemType = parseType();
+ if (ElemType == nullptr)
+ return nullptr;
+ return make<VectorType>(ElemType, DimensionNumber);
+ }
+
+ if (!consumeIf('_')) {
+ Node *DimExpr = parseExpr();
+ if (!DimExpr)
+ return nullptr;
+ if (!consumeIf('_'))
+ return nullptr;
+ Node *ElemType = parseType();
+ if (!ElemType)
+ return nullptr;
+ return make<VectorType>(ElemType, DimExpr);
+ }
+ Node *ElemType = parseType();
+ if (!ElemType)
+ return nullptr;
+ return make<VectorType>(ElemType, StringView());
+}
+
+// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x)
+// ::= DT <expression> E # decltype of an expression (C++0x)
+Node *Db::parseDecltype() {
+ if (!consumeIf('D'))
+ return nullptr;
+ if (!consumeIf('t') && !consumeIf('T'))
+ return nullptr;
+ Node *E = parseExpr();
+ if (E == nullptr)
+ return nullptr;
+ if (!consumeIf('E'))
+ return nullptr;
+ return make<EnclosingExpr>("decltype(", E, ")");
+}
+
+// <array-type> ::= A <positive dimension number> _ <element type>
+// ::= A [<dimension expression>] _ <element type>
+Node *Db::parseArrayType() {
+ if (!consumeIf('A'))
+ return nullptr;
+
+ if (std::isdigit(look())) {
+ StringView Dimension = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<ArrayType>(Ty, Dimension);
+ }
+
+ if (!consumeIf('_')) {
+ Node *DimExpr = parseExpr();
+ if (DimExpr == nullptr)
+ return nullptr;
+ if (!consumeIf('_'))
+ return nullptr;
+ Node *ElementType = parseType();
+ if (ElementType == nullptr)
+ return nullptr;
+ return make<ArrayType>(ElementType, DimExpr);
+ }
+
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<ArrayType>(Ty);
+}
+
+// <pointer-to-member-type> ::= M <class type> <member type>
+Node *Db::parsePointerToMemberType() {
+ if (!consumeIf('M'))
+ return nullptr;
+ Node *ClassType = parseType();
+ if (ClassType == nullptr)
+ return nullptr;
+ Node *MemberType = parseType();
+ if (MemberType == nullptr)
+ return nullptr;
+ return make<PointerToMemberType>(ClassType, MemberType);
+}
+
+// <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier
+// ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class'
+// ::= Tu <name> # dependent elaborated type specifier using 'union'
+// ::= Te <name> # dependent elaborated type specifier using 'enum'
+Node *Db::parseClassEnumType() {
+ StringView ElabSpef;
+ if (consumeIf("Ts"))
+ ElabSpef = "struct";
+ else if (consumeIf("Tu"))
+ ElabSpef = "union";
+ else if (consumeIf("Te"))
+ ElabSpef = "enum";
+
+ Node *Name = parseName();
+ if (Name == nullptr)
+ return nullptr;
+
+ if (!ElabSpef.empty())
+ return make<ElaboratedTypeSpefType>(ElabSpef, Name);
+
+ return Name;
+}
+
+// <qualified-type> ::= <qualifiers> <type>
+// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>
+// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier
+Node *Db::parseQualifiedType() {
+ if (consumeIf('U')) {
+ StringView Qual = parseBareSourceName();
+ if (Qual.empty())
+ return nullptr;
+
+ // FIXME parse the optional <template-args> here!
+
+ // extension ::= U <objc-name> <objc-type> # objc-type<identifier>
+ if (Qual.startsWith("objcproto")) {
+ StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto"));
+ StringView Proto;
+ {
+ SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()),
+ SaveLast(Last, ProtoSourceName.end());
+ Proto = parseBareSourceName();
+ }
+ if (Proto.empty())
+ return nullptr;
+ Node *Child = parseQualifiedType();
+ if (Child == nullptr)
+ return nullptr;
+ return make<ObjCProtoName>(Child, Proto);
+ }
+
+ Node *Child = parseQualifiedType();
+ if (Child == nullptr)
+ return nullptr;
+ return make<VendorExtQualType>(Child, Qual);
+ }
+
+ Qualifiers Quals = parseCVQualifiers();
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ if (Quals != QualNone)
+ Ty = make<QualType>(Ty, Quals);
+ return Ty;
+}
+
+// <type> ::= <builtin-type>
+// ::= <qualified-type>
+// ::= <function-type>
+// ::= <class-enum-type>
+// ::= <array-type>
+// ::= <pointer-to-member-type>
+// ::= <template-param>
+// ::= <template-template-param> <template-args>
+// ::= <decltype>
+// ::= P <type> # pointer
+// ::= R <type> # l-value reference
+// ::= O <type> # r-value reference (C++11)
+// ::= C <type> # complex pair (C99)
+// ::= G <type> # imaginary (C99)
+// ::= <substitution> # See Compression below
+// extension ::= U <objc-name> <objc-type> # objc-type<identifier>
+// extension ::= <vector-type> # <vector-type> starts with Dv
+//
+// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1
+// <objc-type> ::= <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
+Node *Db::parseType() {
+ Node *Result = nullptr;
+
+ switch (look()) {
+ // ::= <qualified-type>
+ case 'r':
+ case 'V':
+ case 'K': {
+ unsigned AfterQuals = 0;
+ if (look(AfterQuals) == 'r') ++AfterQuals;
+ if (look(AfterQuals) == 'V') ++AfterQuals;
+ if (look(AfterQuals) == 'K') ++AfterQuals;
+
+ if (look(AfterQuals) == 'F' ||
+ (look(AfterQuals) == 'D' &&
+ (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' ||
+ look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) {
+ Result = parseFunctionType();
+ break;
+ }
+ _LIBCPP_FALLTHROUGH();
+ }
+ case 'U': {
+ Result = parseQualifiedType();
+ break;
+ }
+ // <builtin-type> ::= v # void
+ case 'v':
+ ++First;
+ return make<NameType>("void");
+ // ::= w # wchar_t
+ case 'w':
+ ++First;
+ return make<NameType>("wchar_t");
+ // ::= b # bool
+ case 'b':
+ ++First;
+ return make<NameType>("bool");
+ // ::= c # char
+ case 'c':
+ ++First;
+ return make<NameType>("char");
+ // ::= a # signed char
+ case 'a':
+ ++First;
+ return make<NameType>("signed char");
+ // ::= h # unsigned char
+ case 'h':
+ ++First;
+ return make<NameType>("unsigned char");
+ // ::= s # short
+ case 's':
+ ++First;
+ return make<NameType>("short");
+ // ::= t # unsigned short
+ case 't':
+ ++First;
+ return make<NameType>("unsigned short");
+ // ::= i # int
+ case 'i':
+ ++First;
+ return make<NameType>("int");
+ // ::= j # unsigned int
+ case 'j':
+ ++First;
+ return make<NameType>("unsigned int");
+ // ::= l # long
+ case 'l':
+ ++First;
+ return make<NameType>("long");
+ // ::= m # unsigned long
+ case 'm':
+ ++First;
+ return make<NameType>("unsigned long");
+ // ::= x # long long, __int64
+ case 'x':
+ ++First;
+ return make<NameType>("long long");
+ // ::= y # unsigned long long, __int64
+ case 'y':
+ ++First;
+ return make<NameType>("unsigned long long");
+ // ::= n # __int128
+ case 'n':
+ ++First;
+ return make<NameType>("__int128");
+ // ::= o # unsigned __int128
+ case 'o':
+ ++First;
+ return make<NameType>("unsigned __int128");
+ // ::= f # float
+ case 'f':
+ ++First;
+ return make<NameType>("float");
+ // ::= d # double
+ case 'd':
+ ++First;
+ return make<NameType>("double");
+ // ::= e # long double, __float80
+ case 'e':
+ ++First;
+ return make<NameType>("long double");
+ // ::= g # __float128
+ case 'g':
+ ++First;
+ return make<NameType>("__float128");
+ // ::= z # ellipsis
+ case 'z':
+ ++First;
+ return make<NameType>("...");
+
+ // <builtin-type> ::= u <source-name> # vendor extended type
+ case 'u': {
+ ++First;
+ StringView Res = parseBareSourceName();
+ if (Res.empty())
+ return nullptr;
+ return make<NameType>(Res);
+ }
+ case 'D':
+ switch (look(1)) {
+ // ::= Dd # IEEE 754r decimal floating point (64 bits)
+ case 'd':
+ First += 2;
+ return make<NameType>("decimal64");
+ // ::= De # IEEE 754r decimal floating point (128 bits)
+ case 'e':
+ First += 2;
+ return make<NameType>("decimal128");
+ // ::= Df # IEEE 754r decimal floating point (32 bits)
+ case 'f':
+ First += 2;
+ return make<NameType>("decimal32");
+ // ::= Dh # IEEE 754r half-precision floating point (16 bits)
+ case 'h':
+ First += 2;
+ return make<NameType>("decimal16");
+ // ::= Di # char32_t
+ case 'i':
+ First += 2;
+ return make<NameType>("char32_t");
+ // ::= Ds # char16_t
+ case 's':
+ First += 2;
+ return make<NameType>("char16_t");
+ // ::= Da # auto (in dependent new-expressions)
+ case 'a':
+ First += 2;
+ return make<NameType>("auto");
+ // ::= Dc # decltype(auto)
+ case 'c':
+ First += 2;
+ return make<NameType>("decltype(auto)");
+ // ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
+ case 'n':
+ First += 2;
+ return make<NameType>("std::nullptr_t");
+
+ // ::= <decltype>
+ case 't':
+ case 'T': {
+ Result = parseDecltype();
+ break;
+ }
+ // extension ::= <vector-type> # <vector-type> starts with Dv
+ case 'v': {
+ Result = parseVectorType();
+ break;
+ }
+ // ::= Dp <type> # pack expansion (C++0x)
+ case 'p': {
+ First += 2;
+ Node *Child = parseType();
+ if (!Child)
+ return nullptr;
+ Result = make<ParameterPackExpansion>(Child);
+ break;
+ }
+ // Exception specifier on a function type.
+ case 'o':
+ case 'O':
+ case 'w':
+ // Transaction safe function type.
+ case 'x':
+ Result = parseFunctionType();
+ break;
+ }
+ break;
+ // ::= <function-type>
+ case 'F': {
+ Result = parseFunctionType();
+ break;
+ }
+ // ::= <array-type>
+ case 'A': {
+ Result = parseArrayType();
+ break;
+ }
+ // ::= <pointer-to-member-type>
+ case 'M': {
+ Result = parsePointerToMemberType();
+ break;
+ }
+ // ::= <template-param>
+ case 'T': {
+ // This could be an elaborate type specifier on a <class-enum-type>.
+ if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') {
+ Result = parseClassEnumType();
+ break;
+ }
+
+ Result = parseTemplateParam();
+ if (Result == nullptr)
+ return nullptr;
+
+ // Result could be either of:
+ // <type> ::= <template-param>
+ // <type> ::= <template-template-param> <template-args>
+ //
+ // <template-template-param> ::= <template-param>
+ // ::= <substitution>
+ //
+ // If this is followed by some <template-args>, and we're permitted to
+ // parse them, take the second production.
+
+ if (TryToParseTemplateArgs && look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ Result = make<NameWithTemplateArgs>(Result, TA);
+ }
+ break;
+ }
+ // ::= P <type> # pointer
+ case 'P': {
+ ++First;
+ Node *Ptr = parseType();
+ if (Ptr == nullptr)
+ return nullptr;
+ Result = make<PointerType>(Ptr);
+ break;
+ }
+ // ::= R <type> # l-value reference
+ case 'R': {
+ ++First;
+ Node *Ref = parseType();
+ if (Ref == nullptr)
+ return nullptr;
+ Result = make<LValueReferenceType>(Ref);
+ break;
+ }
+ // ::= O <type> # r-value reference (C++11)
+ case 'O': {
+ ++First;
+ Node *Ref = parseType();
+ if (Ref == nullptr)
+ return nullptr;
+ Result = make<RValueReferenceType>(Ref);
+ break;
+ }
+ // ::= C <type> # complex pair (C99)
+ case 'C': {
+ ++First;
+ Node *P = parseType();
+ if (P == nullptr)
+ return nullptr;
+ Result = make<PostfixQualifiedType>(P, " complex");
+ break;
+ }
+ // ::= G <type> # imaginary (C99)
+ case 'G': {
+ ++First;
+ Node *P = parseType();
+ if (P == nullptr)
+ return P;
+ Result = make<PostfixQualifiedType>(P, " imaginary");
+ break;
+ }
+ // ::= <substitution> # See Compression below
+ case 'S': {
+ if (look(1) && look(1) != 't') {
+ Node *Sub = parseSubstitution();
+ if (Sub == nullptr)
+ return nullptr;
+
+ // Sub could be either of:
+ // <type> ::= <substitution>
+ // <type> ::= <template-template-param> <template-args>
+ //
+ // <template-template-param> ::= <template-param>
+ // ::= <substitution>
+ //
+ // If this is followed by some <template-args>, and we're permitted to
+ // parse them, take the second production.
+
+ if (TryToParseTemplateArgs && look() == 'I') {
+ Node *TA = parseTemplateArgs();
+ if (TA == nullptr)
+ return nullptr;
+ Result = make<NameWithTemplateArgs>(Sub, TA);
+ break;
+ }
+
+ // If all we parsed was a substitution, don't re-insert into the
+ // substitution table.
+ return Sub;
+ }
+ _LIBCPP_FALLTHROUGH();
+ }
+ // ::= <class-enum-type>
+ default: {
+ Result = parseClassEnumType();
+ break;
+ }
+ }
+
+ // If we parsed a type, insert it into the substitution table. Note that all
+ // <builtin-type>s and <substitution>s have already bailed out, because they
+ // don't get substitutions.
+ if (Result != nullptr)
+ Subs.push_back(Result);
+ return Result;
+}
+
+Node *Db::parsePrefixExpr(StringView Kind) {
+ Node *E = parseExpr();
+ if (E == nullptr)
+ return nullptr;
+ return make<PrefixExpr>(Kind, E);
+}
+
+Node *Db::parseBinaryExpr(StringView Kind) {
+ Node *LHS = parseExpr();
+ if (LHS == nullptr)
+ return nullptr;
+ Node *RHS = parseExpr();
+ if (RHS == nullptr)
+ return nullptr;
+ return make<BinaryExpr>(LHS, Kind, RHS);
+}
+
+Node *Db::parseIntegerLiteral(StringView Lit) {
+ StringView Tmp = parseNumber(true);
+ if (!Tmp.empty() && consumeIf('E'))
+ return make<IntegerExpr>(Lit, Tmp);
+ return nullptr;
+}
+
+// <CV-Qualifiers> ::= [r] [V] [K]
+Qualifiers Db::parseCVQualifiers() {
+ Qualifiers CVR = QualNone;
+ if (consumeIf('r'))
+ addQualifiers(CVR, QualRestrict);
+ if (consumeIf('V'))
+ addQualifiers(CVR, QualVolatile);
+ if (consumeIf('K'))
+ addQualifiers(CVR, QualConst);
+ return CVR;
+}
+
+// <function-param> ::= fp <top-level CV-Qualifiers> _ # L == 0, first parameter
+// ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
+// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter
+// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters
+Node *Db::parseFunctionParam() {
+ if (consumeIf("fp")) {
+ parseCVQualifiers();
+ StringView Num = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ return make<FunctionParam>(Num);
+ }
+ if (consumeIf("fL")) {
+ if (parseNumber().empty())
+ return nullptr;
+ if (!consumeIf('p'))
+ return nullptr;
+ parseCVQualifiers();
+ StringView Num = parseNumber();
+ if (!consumeIf('_'))
+ return nullptr;
+ return make<FunctionParam>(Num);
+ }
+ return nullptr;
}
// [gs] nw <expression>* _ <type> E # new (expr-list) type
@@ -2919,1678 +3571,208 @@
// [gs] na <expression>* _ <type> E # new[] (expr-list) type
// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
// <initializer> ::= pi <expression>* E # parenthesized initialization
-
-const char*
-parse_new_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 4)
- {
- const char* t = first;
- bool parsed_gs = false;
- if (t[0] == 'g' && t[1] == 's')
- {
- t += 2;
- parsed_gs = true;
- }
- if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a'))
- {
- bool is_array = t[1] == 'a';
- t += 2;
- if (t == last)
- return first;
- size_t first_expr_in_list = db.Names.size();
- NodeArray ExprList, init_list;
- while (*t != '_')
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- }
- if (first_expr_in_list > db.Names.size())
- return first;
- ExprList = db.popTrailingNodeArray(first_expr_in_list);
- ++t;
- const char* t1 = parse_type(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- bool has_init = false;
- if (last - t >= 3 && t[0] == 'p' && t[1] == 'i')
- {
- t += 2;
- has_init = true;
- size_t init_list_begin = db.Names.size();
- while (*t != 'E')
- {
- t1 = parse_expression(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- }
- if (init_list_begin > db.Names.size())
- return first;
- init_list = db.popTrailingNodeArray(init_list_begin);
- }
- if (*t != 'E' || db.Names.empty())
- return first;
- auto type = db.Names.back();
- db.Names.pop_back();
- db.Names.push_back(
- db.make<NewExpr>(ExprList, type, init_list,
- parsed_gs, is_array));
- first = t+1;
- }
+Node *Db::parseNewExpr() {
+ bool Global = consumeIf("gs");
+ bool IsArray = look(1) == 'a';
+ if (!consumeIf("nw") && !consumeIf("na"))
+ return nullptr;
+ size_t Exprs = Names.size();
+ while (!consumeIf('_')) {
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return nullptr;
+ Names.push_back(Ex);
+ }
+ NodeArray ExprList = popTrailingNodeArray(Exprs);
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return Ty;
+ if (consumeIf("pi")) {
+ size_t InitsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *Init = parseExpr();
+ if (Init == nullptr)
+ return Init;
+ Names.push_back(Init);
}
- return first;
+ NodeArray Inits = popTrailingNodeArray(InitsBegin);
+ return make<NewExpr>(ExprList, Ty, Inits, Global, IsArray);
+ } else if (!consumeIf('E'))
+ return nullptr;
+ return make<NewExpr>(ExprList, Ty, NodeArray(), Global, IsArray);
}
// cv <type> <expression> # conversion with one argument
// cv <type> _ <expression>* E # conversion with a different number of arguments
+Node *Db::parseConversionExpr() {
+ if (!consumeIf("cv"))
+ return nullptr;
+ Node *Ty;
+ {
+ SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false);
+ Ty = parseType();
+ }
-const char*
-parse_conversion_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'c' && first[1] == 'v')
- {
- bool TryToParseTemplateArgs = db.TryToParseTemplateArgs;
- db.TryToParseTemplateArgs = false;
- size_t type_begin = db.Names.size();
- const char* t = parse_type(first+2, last, db);
- db.TryToParseTemplateArgs = TryToParseTemplateArgs;
- if (t != first+2 && t != last)
- {
- size_t expr_list_begin = db.Names.size();
- if (*t != '_')
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 == t)
- return first;
- t = t1;
- }
- else
- {
- ++t;
- if (t == last)
- return first;
- if (*t != 'E')
- {
- while (*t != 'E')
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 == t || t1 == last)
- return first;
- t = t1;
- }
- }
- ++t;
- }
- if (db.Names.size() < expr_list_begin ||
- type_begin > expr_list_begin)
- return first;
- NodeArray expressions = db.makeNodeArray(
- db.Names.begin() + (long)expr_list_begin, db.Names.end());
- NodeArray types = db.makeNodeArray(
- db.Names.begin() + (long)type_begin,
- db.Names.begin() + (long)expr_list_begin);
- auto* conv_expr = db.make<ConversionExpr>(
- types, expressions);
- db.Names.dropBack(type_begin);
- db.Names.push_back(conv_expr);
- first = t;
- }
+ if (Ty == nullptr)
+ return nullptr;
+
+ if (consumeIf('_')) {
+ size_t ExprsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *E = parseExpr();
+ if (E == nullptr)
+ return E;
+ Names.push_back(E);
}
- return first;
-}
+ NodeArray Exprs = popTrailingNodeArray(ExprsBegin);
+ return make<ConversionExpr>(Ty, Exprs);
+ }
-// pt <expression> <expression> # expr->name
-
-const char*
-parse_arrow_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'p' && first[1] == 't')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- const char* t1 = parse_expression(t, last, db);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto tmp = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<MemberExpr>(
- db.Names.back(), "->", tmp);
- first = t1;
- }
- }
- }
- return first;
-}
-
-// <ref-qualifier> ::= R # & ref-qualifier
-// <ref-qualifier> ::= O # && ref-qualifier
-
-// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
-
-const char*
-parse_function_type(const char* first, const char* last, Db& db)
-{
- if (first != last && *first == 'F')
- {
- const char* t = first+1;
- if (t != last)
- {
- if (*t == 'Y')
- {
- /* extern "C" */
- if (++t == last)
- return first;
- }
- const char* t1 = parse_type(t, last, db);
- if (t1 != t && !db.Names.empty())
- {
- Node* ret_type = db.Names.back();
- db.Names.pop_back();
- size_t params_begin = db.Names.size();
- t = t1;
- FunctionRefQual RefQuals = FrefQualNone;
- while (true)
- {
- if (t == last)
- {
- if (!db.Names.empty())
- db.Names.pop_back();
- return first;
- }
- if (*t == 'E')
- {
- ++t;
- break;
- }
- if (*t == 'v')
- {
- ++t;
- continue;
- }
- if (*t == 'R' && t+1 != last && t[1] == 'E')
- {
- RefQuals = FrefQualLValue;
- ++t;
- continue;
- }
- if (*t == 'O' && t+1 != last && t[1] == 'E')
- {
- RefQuals = FrefQualRValue;
- ++t;
- continue;
- }
- size_t k0 = db.Names.size();
- t1 = parse_type(t, last, db);
- size_t k1 = db.Names.size();
- if (t1 == t || t1 == last || k1 < k0)
- return first;
- t = t1;
- }
- if (db.Names.empty() || params_begin > db.Names.size())
- return first;
- Node* fty = db.make<FunctionType>(
- ret_type, db.popTrailingNodeArray(params_begin));
- if (RefQuals)
- fty = db.make<FunctionRefQualType>(fty, RefQuals);
- db.Names.push_back(fty);
- first = t;
- }
- }
- }
- return first;
-}
-
-// <pointer-to-member-type> ::= M <class type> <member type>
-
-const char*
-parse_pointer_to_member_type(const char* first, const char* last, Db& db)
-{
- if (first != last && *first == 'M')
- {
- const char* t = parse_type(first+1, last, db);
- if (t != first+1)
- {
- const char* t2 = parse_type(t, last, db);
- if (t2 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto func = std::move(db.Names.back());
- db.Names.pop_back();
- auto ClassType = std::move(db.Names.back());
- db.Names.back() =
- db.make<PointerToMemberType>(ClassType, func);
- first = t2;
- }
- }
- }
- return first;
-}
-
-// <array-type> ::= A <positive dimension number> _ <element type>
-// ::= A [<dimension expression>] _ <element type>
-
-const char*
-parse_array_type(const char* first, const char* last, Db& db)
-{
- if (first != last && *first == 'A' && first+1 != last)
- {
- if (first[1] == '_')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<ArrayType>(db.Names.back());
- first = t;
- }
- }
- else if ('1' <= first[1] && first[1] <= '9')
- {
- const char* t = parse_number(first+1, last);
- if (t != last && *t == '_')
- {
- const char* t2 = parse_type(t+1, last, db);
- if (t2 != t+1)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<ArrayType>(db.Names.back(),
- StringView(first + 1, t));
- first = t2;
- }
- }
- }
- else
- {
- const char* t = parse_expression(first+1, last, db);
- if (t != first+1 && t != last && *t == '_')
- {
- const char* t2 = parse_type(++t, last, db);
- if (t2 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto base_type = std::move(db.Names.back());
- db.Names.pop_back();
- auto dimension_expr = std::move(db.Names.back());
- db.Names.back() =
- db.make<ArrayType>(base_type, dimension_expr);
- first = t2;
- }
- }
- }
- }
- return first;
-}
-
-// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x)
-// ::= DT <expression> E # decltype of an expression (C++0x)
-
-const char*
-parse_decltype(const char* first, const char* last, Db& db)
-{
- if (last - first >= 4 && first[0] == 'D')
- {
- switch (first[1])
- {
- case 't':
- case 'T':
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2 && t != last && *t == 'E')
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<EnclosingExpr>(
- "decltype(", db.Names.back(), ")");
- first = t+1;
- }
- }
- break;
- }
- }
- return first;
-}
-
-// extension:
-// <vector-type> ::= Dv <positive dimension number> _
-// <extended element type>
-// ::= Dv [<dimension expression>] _ <element type>
-// <extended element type> ::= <element type>
-// ::= p # AltiVec vector pixel
-
-const char*
-parse_vector_type(const char* first, const char* last, Db& db)
-{
- if (last - first > 3 && first[0] == 'D' && first[1] == 'v')
- {
- if ('1' <= first[2] && first[2] <= '9')
- {
- const char* t = parse_number(first+2, last);
- if (t == last || *t != '_')
- return first;
- const char* num = first + 2;
- size_t sz = static_cast<size_t>(t - num);
- if (++t != last)
- {
- if (*t != 'p')
- {
- const char* t1 = parse_type(t, last, db);
- if (t1 != t)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<VectorType>(db.Names.back(),
- StringView(num, num + sz));
- first = t1;
- }
- }
- else
- {
- ++t;
- db.Names.push_back(
- db.make<VectorType>(StringView(num, num + sz)));
- first = t;
- }
- }
- }
- else
- {
- Node* num = nullptr;
- const char* t1 = first+2;
- if (*t1 != '_')
- {
- const char* t = parse_expression(t1, last, db);
- if (t != t1)
- {
- if (db.Names.empty())
- return first;
- num = db.Names.back();
- db.Names.pop_back();
- t1 = t;
- }
- }
- if (t1 != last && *t1 == '_' && ++t1 != last)
- {
- const char* t = parse_type(t1, last, db);
- if (t != t1)
- {
- if (db.Names.empty())
- return first;
- if (num)
- db.Names.back() =
- db.make<VectorType>(db.Names.back(), num);
- else
- db.Names.back() =
- db.make<VectorType>(db.Names.back(), StringView());
- first = t;
- } else if (num)
- db.Names.push_back(num);
- }
- }
- }
- return first;
-}
-
-// <type> ::= <builtin-type>
-// ::= <function-type>
-// ::= <class-enum-type>
-// ::= <array-type>
-// ::= <pointer-to-member-type>
-// ::= <template-param>
-// ::= <template-template-param> <template-args>
-// ::= <decltype>
-// ::= <substitution>
-// ::= <CV-Qualifiers> <type>
-// ::= P <type> # pointer-to
-// ::= R <type> # reference-to
-// ::= O <type> # rvalue reference-to (C++0x)
-// ::= C <type> # complex pair (C 2000)
-// ::= G <type> # imaginary (C 2000)
-// ::= Dp <type> # pack expansion (C++0x)
-// ::= U <source-name> <type> # vendor extended type qualifier
-// extension := U <objc-name> <objc-type> # objc-type<identifier>
-// extension := <vector-type> # <vector-type> starts with Dv
-
-// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1
-// <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
-
-const char*
-parse_type(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- switch (*first)
- {
- case 'r':
- case 'V':
- case 'K':
- {
- Qualifiers cv = QualNone;
- const char* t = parse_cv_qualifiers(first, last, cv);
- if (t != first)
- {
- bool is_function = *t == 'F';
- size_t k0 = db.Names.size();
- const char* t1 = parse_type(t, last, db);
- size_t k1 = db.Names.size();
- if (t1 != t)
- {
- if (is_function)
- db.Subs.popPack();
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- {
- if (cv) {
- if (is_function)
- db.Names[k] = db.make<FunctionQualType>(
- db.Names[k], cv);
- else
- db.Names[k] =
- db.make<QualType>(db.Names[k], cv);
- }
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- }
- first = t1;
- }
- }
- }
- break;
- default:
- {
- const char* t = parse_builtin_type(first, last, db);
- if (t != first)
- {
- first = t;
- }
- else
- {
- switch (*first)
- {
- case 'A':
- t = parse_array_type(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- first = t;
- db.Subs.pushSubstitution(db.Names.back());
- }
- break;
- case 'C':
- t = parse_type(first+1, last, db);
- if (t != first+1)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<PostfixQualifiedType>(
- db.Names.back(), " complex");
- first = t;
- db.Subs.pushSubstitution(db.Names.back());
- }
- break;
- case 'F':
- t = parse_function_type(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- first = t;
- db.Subs.pushSubstitution(db.Names.back());
- }
- break;
- case 'G':
- t = parse_type(first+1, last, db);
- if (t != first+1)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<PostfixQualifiedType>(
- db.Names.back(), " imaginary");
- first = t;
- db.Subs.pushSubstitution(db.Names.back());
- }
- break;
- case 'M':
- t = parse_pointer_to_member_type(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- first = t;
- db.Subs.pushSubstitution(db.Names.back());
- }
- break;
- case 'O':
- {
- size_t k0 = db.Names.size();
- t = parse_type(first+1, last, db);
- size_t k1 = db.Names.size();
- if (t != first+1)
- {
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- {
- db.Names[k] =
- db.make<RValueReferenceType>(db.Names[k]);
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- }
- first = t;
- }
- break;
- }
- case 'P':
- {
- size_t k0 = db.Names.size();
- t = parse_type(first+1, last, db);
- size_t k1 = db.Names.size();
- if (t != first+1)
- {
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- {
- db.Names[k] = db.make<PointerType>(db.Names[k]);
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- }
- first = t;
- }
- break;
- }
- case 'R':
- {
- size_t k0 = db.Names.size();
- t = parse_type(first+1, last, db);
- size_t k1 = db.Names.size();
- if (t != first+1)
- {
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- {
- db.Names[k] =
- db.make<LValueReferenceType>(db.Names[k]);
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- }
- first = t;
- }
- break;
- }
- case 'T':
- {
- size_t k0 = db.Names.size();
- t = parse_template_param(first, last, db);
- size_t k1 = db.Names.size();
- if (t != first)
- {
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- if (db.TryToParseTemplateArgs && k1 == k0+1)
- {
- const char* t1 = parse_template_args(t, last, db);
- if (t1 != t)
- {
- auto args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<
- NameWithTemplateArgs>(
- db.Names.back(), args);
- db.Subs.pushSubstitution(db.Names.back());
- t = t1;
- }
- }
- first = t;
- }
- break;
- }
- case 'U':
- if (first+1 != last)
- {
- t = parse_source_name(first+1, last, db);
- if (t != first+1)
- {
- const char* t2 = parse_type(t, last, db);
- if (t2 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto type = db.Names.back();
- db.Names.pop_back();
- if (db.Names.back()->K != Node::KNameType ||
- !static_cast<NameType*>(db.Names.back())->getName().startsWith("objcproto"))
- {
- db.Names.back() = db.make<VendorExtQualType>(type, db.Names.back());
- }
- else
- {
- auto* proto = static_cast<NameType*>(db.Names.back());
- db.Names.pop_back();
- t = parse_source_name(proto->getName().begin() + 9, proto->getName().end(), db);
- if (t != proto->getName().begin() + 9)
- {
- db.Names.back() = db.make<ObjCProtoName>(type, db.Names.back());
- }
- else
- {
- db.Names.push_back(db.make<VendorExtQualType>(type, proto));
- }
- }
- db.Subs.pushSubstitution(db.Names.back());
- first = t2;
- }
- }
- }
- break;
- case 'S':
- if (first+1 != last && first[1] == 't')
- {
- t = parse_name(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- }
- else
- {
- t = parse_substitution(first, last, db);
- if (t != first)
- {
- first = t;
- // Parsed a substitution. If the substitution is a
- // <template-param> it might be followed by <template-args>.
- if (db.TryToParseTemplateArgs)
- {
- t = parse_template_args(first, last, db);
- if (t != first)
- {
- if (db.Names.size() < 2)
- return first;
- auto template_args = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<
- NameWithTemplateArgs>(
- db.Names.back(), template_args);
- // Need to create substitution for <template-template-param> <template-args>
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- }
- }
- }
- break;
- case 'D':
- if (first+1 != last)
- {
- switch (first[1])
- {
- case 'p':
- {
- size_t k0 = db.Names.size();
- t = parse_type(first+2, last, db);
- size_t k1 = db.Names.size();
- if (t != first+2)
- {
- db.Subs.pushPack();
- for (size_t k = k0; k < k1; ++k)
- db.Subs.pushSubstitutionIntoPack(db.Names[k]);
- first = t;
- return first;
- }
- break;
- }
- case 't':
- case 'T':
- t = parse_decltype(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- return first;
- }
- break;
- case 'v':
- t = parse_vector_type(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- return first;
- }
- break;
- }
- }
- _LIBCPP_FALLTHROUGH();
- default:
- // must check for builtin-types before class-enum-types to avoid
- // ambiguities with operator-names
- t = parse_builtin_type(first, last, db);
- if (t != first)
- {
- first = t;
- }
- else
- {
- t = parse_name(first, last, db);
- if (t != first)
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- first = t;
- }
- }
- break;
- }
- }
- break;
- }
- }
- }
- return first;
-}
-
-// <operator-name>
-// ::= aa # &&
-// ::= ad # & (unary)
-// ::= an # &
-// ::= aN # &=
-// ::= aS # =
-// ::= cl # ()
-// ::= cm # ,
-// ::= co # ~
-// ::= cv <type> # (cast)
-// ::= da # delete[]
-// ::= de # * (unary)
-// ::= dl # delete
-// ::= dv # /
-// ::= dV # /=
-// ::= eo # ^
-// ::= eO # ^=
-// ::= eq # ==
-// ::= ge # >=
-// ::= gt # >
-// ::= ix # []
-// ::= le # <=
-// ::= li <source-name> # operator ""
-// ::= ls # <<
-// ::= lS # <<=
-// ::= lt # <
-// ::= mi # -
-// ::= mI # -=
-// ::= ml # *
-// ::= mL # *=
-// ::= mm # -- (postfix in <expression> context)
-// ::= na # new[]
-// ::= ne # !=
-// ::= ng # - (unary)
-// ::= nt # !
-// ::= nw # new
-// ::= oo # ||
-// ::= or # |
-// ::= oR # |=
-// ::= pm # ->*
-// ::= pl # +
-// ::= pL # +=
-// ::= pp # ++ (postfix in <expression> context)
-// ::= ps # + (unary)
-// ::= pt # ->
-// ::= qu # ?
-// ::= rm # %
-// ::= rM # %=
-// ::= rs # >>
-// ::= rS # >>=
-// ::= v <digit> <source-name> # vendor extended operator
-
-const char*
-parse_operator_name(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- switch (first[0])
- {
- case 'a':
- switch (first[1])
- {
- case 'a':
- db.Names.push_back(db.make<NameType>("operator&&"));
- first += 2;
- break;
- case 'd':
- case 'n':
- db.Names.push_back(db.make<NameType>("operator&"));
- first += 2;
- break;
- case 'N':
- db.Names.push_back(db.make<NameType>("operator&="));
- first += 2;
- break;
- case 'S':
- db.Names.push_back(db.make<NameType>("operator="));
- first += 2;
- break;
- }
- break;
- case 'c':
- switch (first[1])
- {
- case 'l':
- db.Names.push_back(db.make<NameType>("operator()"));
- first += 2;
- break;
- case 'm':
- db.Names.push_back(db.make<NameType>("operator,"));
- first += 2;
- break;
- case 'o':
- db.Names.push_back(db.make<NameType>("operator~"));
- first += 2;
- break;
- case 'v':
- {
- bool TryToParseTemplateArgs = db.TryToParseTemplateArgs;
- db.TryToParseTemplateArgs = false;
- const char* t = parse_type(first+2, last, db);
- db.TryToParseTemplateArgs = TryToParseTemplateArgs;
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<ConversionOperatorType>(db.Names.back());
- db.ParsedCtorDtorCV = true;
- first = t;
- }
- }
- break;
- }
- break;
- case 'd':
- switch (first[1])
- {
- case 'a':
- db.Names.push_back(db.make<NameType>("operator delete[]"));
- first += 2;
- break;
- case 'e':
- db.Names.push_back(db.make<NameType>("operator*"));
- first += 2;
- break;
- case 'l':
- db.Names.push_back(db.make<NameType>("operator delete"));
- first += 2;
- break;
- case 'v':
- db.Names.push_back(db.make<NameType>("operator/"));
- first += 2;
- break;
- case 'V':
- db.Names.push_back(db.make<NameType>("operator/="));
- first += 2;
- break;
- }
- break;
- case 'e':
- switch (first[1])
- {
- case 'o':
- db.Names.push_back(db.make<NameType>("operator^"));
- first += 2;
- break;
- case 'O':
- db.Names.push_back(db.make<NameType>("operator^="));
- first += 2;
- break;
- case 'q':
- db.Names.push_back(db.make<NameType>("operator=="));
- first += 2;
- break;
- }
- break;
- case 'g':
- switch (first[1])
- {
- case 'e':
- db.Names.push_back(db.make<NameType>("operator>="));
- first += 2;
- break;
- case 't':
- db.Names.push_back(db.make<NameType>("operator>"));
- first += 2;
- break;
- }
- break;
- case 'i':
- if (first[1] == 'x')
- {
- db.Names.push_back(db.make<NameType>("operator[]"));
- first += 2;
- }
- break;
- case 'l':
- switch (first[1])
- {
- case 'e':
- db.Names.push_back(db.make<NameType>("operator<="));
- first += 2;
- break;
- case 'i':
- {
- const char* t = parse_source_name(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<LiteralOperator>(db.Names.back());
- first = t;
- }
- }
- break;
- case 's':
- db.Names.push_back(db.make<NameType>("operator<<"));
- first += 2;
- break;
- case 'S':
- db.Names.push_back(db.make<NameType>("operator<<="));
- first += 2;
- break;
- case 't':
- db.Names.push_back(db.make<NameType>("operator<"));
- first += 2;
- break;
- }
- break;
- case 'm':
- switch (first[1])
- {
- case 'i':
- db.Names.push_back(db.make<NameType>("operator-"));
- first += 2;
- break;
- case 'I':
- db.Names.push_back(db.make<NameType>("operator-="));
- first += 2;
- break;
- case 'l':
- db.Names.push_back(db.make<NameType>("operator*"));
- first += 2;
- break;
- case 'L':
- db.Names.push_back(db.make<NameType>("operator*="));
- first += 2;
- break;
- case 'm':
- db.Names.push_back(db.make<NameType>("operator--"));
- first += 2;
- break;
- }
- break;
- case 'n':
- switch (first[1])
- {
- case 'a':
- db.Names.push_back(db.make<NameType>("operator new[]"));
- first += 2;
- break;
- case 'e':
- db.Names.push_back(db.make<NameType>("operator!="));
- first += 2;
- break;
- case 'g':
- db.Names.push_back(db.make<NameType>("operator-"));
- first += 2;
- break;
- case 't':
- db.Names.push_back(db.make<NameType>("operator!"));
- first += 2;
- break;
- case 'w':
- db.Names.push_back(db.make<NameType>("operator new"));
- first += 2;
- break;
- }
- break;
- case 'o':
- switch (first[1])
- {
- case 'o':
- db.Names.push_back(db.make<NameType>("operator||"));
- first += 2;
- break;
- case 'r':
- db.Names.push_back(db.make<NameType>("operator|"));
- first += 2;
- break;
- case 'R':
- db.Names.push_back(db.make<NameType>("operator|="));
- first += 2;
- break;
- }
- break;
- case 'p':
- switch (first[1])
- {
- case 'm':
- db.Names.push_back(db.make<NameType>("operator->*"));
- first += 2;
- break;
- case 'l':
- db.Names.push_back(db.make<NameType>("operator+"));
- first += 2;
- break;
- case 'L':
- db.Names.push_back(db.make<NameType>("operator+="));
- first += 2;
- break;
- case 'p':
- db.Names.push_back(db.make<NameType>("operator++"));
- first += 2;
- break;
- case 's':
- db.Names.push_back(db.make<NameType>("operator+"));
- first += 2;
- break;
- case 't':
- db.Names.push_back(db.make<NameType>("operator->"));
- first += 2;
- break;
- }
- break;
- case 'q':
- if (first[1] == 'u')
- {
- db.Names.push_back(db.make<NameType>("operator?"));
- first += 2;
- }
- break;
- case 'r':
- switch (first[1])
- {
- case 'm':
- db.Names.push_back(db.make<NameType>("operator%"));
- first += 2;
- break;
- case 'M':
- db.Names.push_back(db.make<NameType>("operator%="));
- first += 2;
- break;
- case 's':
- db.Names.push_back(db.make<NameType>("operator>>"));
- first += 2;
- break;
- case 'S':
- db.Names.push_back(db.make<NameType>("operator>>="));
- first += 2;
- break;
- }
- break;
- case 'v':
- if (std::isdigit(first[1]))
- {
- const char* t = parse_source_name(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<ConversionOperatorType>(db.Names.back());
- first = t;
- }
- }
- break;
- }
- }
- return first;
-}
-
-const char*
-parse_integer_literal(const char* first, const char* last, StringView lit, Db& db)
-{
- const char* t = parse_number(first, last);
- if (t != first && t != last && *t == 'E')
- {
- db.Names.push_back(
- db.make<IntegerExpr>(lit, StringView(first, t)));
- first = t+1;
- }
- return first;
+ Node *E[1] = {parseExpr()};
+ if (E[0] == nullptr)
+ return nullptr;
+ return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1));
}
// <expr-primary> ::= L <type> <value number> E # integer literal
// ::= L <type> <value float> E # floating literal
// ::= L <string type> E # string literal
// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
-// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
+// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
// ::= L <mangled-name> E # external name
-
-const char*
-parse_expr_primary(const char* first, const char* last, Db& db)
-{
- if (last - first >= 4 && *first == 'L')
- {
- switch (first[1])
- {
- case 'w':
- {
- const char* t = parse_integer_literal(first+2, last, "wchar_t", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'b':
- if (first[3] == 'E')
- {
- switch (first[2])
- {
- case '0':
- db.Names.push_back(db.make<BoolExpr>(0));
- first += 4;
- break;
- case '1':
- db.Names.push_back(db.make<BoolExpr>(1));
- first += 4;
- break;
- }
- }
- break;
- case 'c':
- {
- const char* t = parse_integer_literal(first+2, last, "char", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'a':
- {
- const char* t = parse_integer_literal(first+2, last, "signed char", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'h':
- {
- const char* t = parse_integer_literal(first+2, last, "unsigned char", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 's':
- {
- const char* t = parse_integer_literal(first+2, last, "short", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 't':
- {
- const char* t = parse_integer_literal(first+2, last, "unsigned short", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'i':
- {
- const char* t = parse_integer_literal(first+2, last, "", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'j':
- {
- const char* t = parse_integer_literal(first+2, last, "u", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'l':
- {
- const char* t = parse_integer_literal(first+2, last, "l", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'm':
- {
- const char* t = parse_integer_literal(first+2, last, "ul", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'x':
- {
- const char* t = parse_integer_literal(first+2, last, "ll", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'y':
- {
- const char* t = parse_integer_literal(first+2, last, "ull", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'n':
- {
- const char* t = parse_integer_literal(first+2, last, "__int128", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'o':
- {
- const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'f':
- {
- const char* t = parse_floating_number<float>(first+2, last, db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'd':
- {
- const char* t = parse_floating_number<double>(first+2, last, db);
- if (t != first+2)
- first = t;
- }
- break;
- case 'e':
- {
- const char* t = parse_floating_number<long double>(first+2, last, db);
- if (t != first+2)
- first = t;
- }
- break;
- case '_':
- if (first[2] == 'Z')
- {
- const char* t = parse_encoding(first+3, last, db);
- if (t != first+3 && t != last && *t == 'E')
- first = t+1;
- }
- break;
- case 'T':
- // Invalid mangled name per
- // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
- break;
- default:
- {
- // might be named type
- const char* t = parse_type(first+1, last, db);
- if (t != first+1 && t != last)
- {
- if (*t != 'E')
- {
- const char* n = t;
- for (; n != last && isdigit(*n); ++n)
- ;
- if (n != t && n != last && *n == 'E')
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<IntegerCastExpr>(
- db.Names.back(), StringView(t, n));
- first = n+1;
- break;
- }
- }
- else
- {
- first = t+1;
- break;
- }
- }
- }
- }
+Node *Db::parseExprPrimary() {
+ if (!consumeIf('L'))
+ return nullptr;
+ switch (look()) {
+ case 'w':
+ ++First;
+ return parseIntegerLiteral("wchar_t");
+ case 'b':
+ if (consumeIf("b0E"))
+ return make<BoolExpr>(0);
+ if (consumeIf("b1E"))
+ return make<BoolExpr>(1);
+ return nullptr;
+ case 'c':
+ ++First;
+ return parseIntegerLiteral("char");
+ case 'a':
+ ++First;
+ return parseIntegerLiteral("signed char");
+ case 'h':
+ ++First;
+ return parseIntegerLiteral("unsigned char");
+ case 's':
+ ++First;
+ return parseIntegerLiteral("short");
+ case 't':
+ ++First;
+ return parseIntegerLiteral("unsigned short");
+ case 'i':
+ ++First;
+ return parseIntegerLiteral("");
+ case 'j':
+ ++First;
+ return parseIntegerLiteral("u");
+ case 'l':
+ ++First;
+ return parseIntegerLiteral("l");
+ case 'm':
+ ++First;
+ return parseIntegerLiteral("ul");
+ case 'x':
+ ++First;
+ return parseIntegerLiteral("ll");
+ case 'y':
+ ++First;
+ return parseIntegerLiteral("ull");
+ case 'n':
+ ++First;
+ return parseIntegerLiteral("__int128");
+ case 'o':
+ ++First;
+ return parseIntegerLiteral("unsigned __int128");
+ case 'f':
+ ++First;
+ return parseFloatingLiteral<float>();
+ case 'd':
+ ++First;
+ return parseFloatingLiteral<double>();
+ case 'e':
+ ++First;
+ return parseFloatingLiteral<long double>();
+ case '_':
+ if (consumeIf("_Z")) {
+ Node *R = parseEncoding();
+ if (R != nullptr && consumeIf('E'))
+ return R;
}
- return first;
+ return nullptr;
+ case 'T':
+ // Invalid mangled name per
+ // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
+ return nullptr;
+ default: {
+ // might be named type
+ Node *T = parseType();
+ if (T == nullptr)
+ return nullptr;
+ StringView N = parseNumber();
+ if (!N.empty()) {
+ if (!consumeIf('E'))
+ return nullptr;
+ return make<IntegerCastExpr>(T, N);
+ }
+ if (consumeIf('E'))
+ return T;
+ return nullptr;
+ }
+ }
}
-Node* maybe_change_special_sub_name(Node* inp, Db& db)
-{
- if (inp->K != Node::KSpecialSubstitution)
- return inp;
- auto Kind = static_cast<SpecialSubstitution*>(inp)->SSK;
- switch (Kind)
- {
- case SpecialSubKind::string:
- case SpecialSubKind::istream:
- case SpecialSubKind::ostream:
- case SpecialSubKind::iostream:
- return db.make<ExpandedSpecialSubstitution>(Kind);
- default:
- break;
+// <braced-expression> ::= <expression>
+// ::= di <field source-name> <braced-expression> # .name = expr
+// ::= dx <index expression> <braced-expression> # [expr] = expr
+// ::= dX <range begin expression> <range end expression> <braced-expression>
+Node *Db::parseBracedExpr() {
+ if (look() == 'd') {
+ switch (look(1)) {
+ case 'i': {
+ First += 2;
+ Node *Field = parseSourceName(/*NameState=*/nullptr);
+ if (Field == nullptr)
+ return nullptr;
+ Node *Init = parseBracedExpr();
+ if (Init == nullptr)
+ return nullptr;
+ return make<BracedExpr>(Field, Init, /*isArray=*/false);
}
- return inp;
-}
-
-// <ctor-dtor-name> ::= C1 # complete object constructor
-// ::= C2 # base object constructor
-// ::= C3 # complete object allocating constructor
-// extension ::= C5 # ?
-// ::= D0 # deleting destructor
-// ::= D1 # complete object destructor
-// ::= D2 # base object destructor
-// extension ::= D5 # ?
-
-const char*
-parse_ctor_dtor_name(const char* first, const char* last, Db& db)
-{
- if (last-first >= 2 && !db.Names.empty())
- {
- switch (first[0])
- {
- case 'C':
- switch (first[1])
- {
- case '1':
- case '2':
- case '3':
- case '5':
- if (db.Names.empty())
- return first;
- db.Names.back() =
- maybe_change_special_sub_name(db.Names.back(), db);
- db.Names.push_back(
- db.make<CtorDtorName>(db.Names.back(), false));
- first += 2;
- db.ParsedCtorDtorCV = true;
- break;
- }
- break;
- case 'D':
- switch (first[1])
- {
- case '0':
- case '1':
- case '2':
- case '5':
- if (db.Names.empty())
- return first;
- db.Names.push_back(
- db.make<CtorDtorName>(db.Names.back(), true));
- first += 2;
- db.ParsedCtorDtorCV = true;
- break;
- }
- break;
- }
+ case 'x': {
+ First += 2;
+ Node *Index = parseExpr();
+ if (Index == nullptr)
+ return nullptr;
+ Node *Init = parseBracedExpr();
+ if (Init == nullptr)
+ return nullptr;
+ return make<BracedExpr>(Index, Init, /*isArray=*/true);
}
- return first;
-}
-
-// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _
-// ::= <closure-type-name>
-//
-// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
-//
-// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters
-
-const char*
-parse_unnamed_type_name(const char* first, const char* last, Db& db)
-{
- if (last - first > 2 && first[0] == 'U')
- {
- char type = first[1];
- switch (type)
- {
- case 't':
- {
- const char* t0 = first+2;
- if (t0 == last)
- return first;
- StringView count;
- if (std::isdigit(*t0))
- {
- const char* t1 = t0 + 1;
- while (t1 != last && std::isdigit(*t1))
- ++t1;
- count = StringView(t0, t1);
- t0 = t1;
- }
- if (t0 == last || *t0 != '_')
- return first;
- db.Names.push_back(db.make<UnnamedTypeName>(count));
- first = t0 + 1;
- }
- break;
- case 'l':
- {
- size_t begin_pos = db.Names.size();
- const char* t0 = first+2;
- NodeArray lambda_params;
- if (first[2] == 'v')
- {
- ++t0;
- }
- else
- {
- while (true)
- {
- const char* t1 = parse_type(t0, last, db);
- if (t1 == t0)
- break;
- t0 = t1;
- }
- if (db.Names.size() < begin_pos)
- return first;
- lambda_params = db.popTrailingNodeArray(begin_pos);
- }
- if (t0 == last || *t0 != 'E')
- return first;
- ++t0;
- if (t0 == last)
- return first;
- StringView count;
- if (std::isdigit(*t0))
- {
- const char* t1 = t0 + 1;
- while (t1 != last && std::isdigit(*t1))
- ++t1;
- count = StringView(t0, t1);
- t0 = t1;
- }
- if (t0 == last || *t0 != '_')
- return first;
- db.Names.push_back(db.make<LambdaTypeName>(lambda_params, count));
- first = t0 + 1;
- }
- break;
- }
+ case 'X': {
+ First += 2;
+ Node *RangeBegin = parseExpr();
+ if (RangeBegin == nullptr)
+ return nullptr;
+ Node *RangeEnd = parseExpr();
+ if (RangeEnd == nullptr)
+ return nullptr;
+ Node *Init = parseBracedExpr();
+ if (Init == nullptr)
+ return nullptr;
+ return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init);
}
- return first;
-}
-
-// <unqualified-name> ::= <operator-name>
-// ::= <ctor-dtor-name>
-// ::= <source-name>
-// ::= <unnamed-type-name>
-
-const char*
-parse_unqualified_name(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- const char* t;
- switch (*first)
- {
- case 'C':
- case 'D':
- t = parse_ctor_dtor_name(first, last, db);
- if (t != first)
- first = t;
- break;
- case 'U':
- t = parse_unnamed_type_name(first, last, db);
- if (t != first)
- first = t;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- t = parse_source_name(first, last, db);
- if (t != first)
- first = t;
- break;
- default:
- t = parse_operator_name(first, last, db);
- if (t != first)
- first = t;
- break;
- };
}
- return first;
-}
-
-// <unscoped-name> ::= <unqualified-name>
-// ::= St <unqualified-name> # ::std::
-// extension ::= StL<unqualified-name>
-
-const char*
-parse_unscoped_name(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- const char* t0 = first;
- bool St = false;
- if (first[0] == 'S' && first[1] == 't')
- {
- t0 += 2;
- St = true;
- if (t0 != last && *t0 == 'L')
- ++t0;
- }
- const char* t1 = parse_unqualified_name(t0, last, db);
- if (t1 != t0)
- {
- if (St)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<StdQualifiedName>(db.Names.back());
- }
- first = t1;
- }
- }
- return first;
-}
-
-// at <type> # alignof (a type)
-
-const char*
-parse_alignof_type(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'a' && first[1] == 't')
- {
- const char* t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<EnclosingExpr>("alignof (", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-// az <expression> # alignof (a expression)
-
-const char*
-parse_alignof_expr(const char* first, const char* last, Db& db)
-{
- if (last - first >= 3 && first[0] == 'a' && first[1] == 'z')
- {
- const char* t = parse_expression(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<EnclosingExpr>("alignof (", db.Names.back(), ")");
- first = t;
- }
- }
- return first;
-}
-
-const char*
-parse_noexcept_expression(const char* first, const char* last, Db& db)
-{
- const char* t1 = parse_expression(first, last, db);
- if (t1 != first)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<EnclosingExpr>("noexcept (", db.Names.back(), ")");
- first = t1;
- }
- return first;
-}
-
-const char*
-parse_prefix_expression(const char* first, const char* last, StringView op, Db& db)
-{
- const char* t1 = parse_expression(first, last, db);
- if (t1 != first)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<PrefixExpr>(op, db.Names.back());
- first = t1;
- }
- return first;
-}
-
-const char*
-parse_binary_expression(const char* first, const char* last, StringView op, Db& db)
-{
- const char* t1 = parse_expression(first, last, db);
- if (t1 != first)
- {
- const char* t2 = parse_expression(t1, last, db);
- if (t2 != t1)
- {
- if (db.Names.size() < 2)
- return first;
- auto op2 = db.Names.back();
- db.Names.pop_back();
- auto op1 = db.Names.back();
- db.Names.back() = db.make<BinaryExpr>(op1, op, op2);
- first = t2;
- }
- }
- return first;
+ }
+ return parseExpr();
}
// <expression> ::= <unary operator-name> <expression>
@@ -4631,775 +3813,918 @@
// ::= <unresolved-name> # f(p), N::f(p), ::f(p),
// # freestanding dependent name (e.g., T::x),
// # objectless nonstatic member reference
+// ::= fL <binary-operator-name> <expression> <expression>
+// ::= fR <binary-operator-name> <expression> <expression>
+// ::= fl <binary-operator-name> <expression>
+// ::= fr <binary-operator-name> <expression>
// ::= <expr-primary>
+Node *Db::parseExpr() {
+ bool Global = consumeIf("gs");
+ if (numLeft() < 2)
+ return nullptr;
-const char*
-parse_expression(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2)
- {
- const char* t = first;
- bool parsed_gs = false;
- if (last - first >= 4 && t[0] == 'g' && t[1] == 's')
- {
- t += 2;
- parsed_gs = true;
- }
- switch (*t)
- {
- case 'L':
- first = parse_expr_primary(first, last, db);
- break;
- case 'T':
- first = parse_template_param(first, last, db);
- break;
- case 'f':
- first = parse_function_param(first, last, db);
- break;
- case 'a':
- switch (t[1])
- {
- case 'a':
- t = parse_binary_expression(first+2, last, "&&", db);
- if (t != first+2)
- first = t;
- break;
- case 'd':
- t = parse_prefix_expression(first+2, last, "&", db);
- if (t != first+2)
- first = t;
- break;
- case 'n':
- t = parse_binary_expression(first+2, last, "&", db);
- if (t != first+2)
- first = t;
- break;
- case 'N':
- t = parse_binary_expression(first+2, last, "&=", db);
- if (t != first+2)
- first = t;
- break;
- case 'S':
- t = parse_binary_expression(first+2, last, "=", db);
- if (t != first+2)
- first = t;
- break;
- case 't':
- first = parse_alignof_type(first, last, db);
- break;
- case 'z':
- first = parse_alignof_expr(first, last, db);
- break;
- }
- break;
- case 'c':
- switch (t[1])
- {
- case 'c':
- first = parse_const_cast_expr(first, last, db);
- break;
- case 'l':
- first = parse_call_expr(first, last, db);
- break;
- case 'm':
- t = parse_binary_expression(first+2, last, ",", db);
- if (t != first+2)
- first = t;
- break;
- case 'o':
- t = parse_prefix_expression(first+2, last, "~", db);
- if (t != first+2)
- first = t;
- break;
- case 'v':
- first = parse_conversion_expr(first, last, db);
- break;
- }
- break;
- case 'd':
- switch (t[1])
- {
- case 'a':
- {
- const char* t1 = parse_expression(t+2, last, db);
- if (t1 != t+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<DeleteExpr>(
- db.Names.back(), parsed_gs, /*is_array=*/true);
- first = t1;
- }
- }
- break;
- case 'c':
- first = parse_dynamic_cast_expr(first, last, db);
- break;
- case 'e':
- t = parse_prefix_expression(first+2, last, "*", db);
- if (t != first+2)
- first = t;
- break;
- case 'l':
- {
- const char* t1 = parse_expression(t+2, last, db);
- if (t1 != t+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<DeleteExpr>(
- db.Names.back(), parsed_gs, /*is_array=*/false);
- first = t1;
- }
- }
- break;
- case 'n':
- return parse_unresolved_name(first, last, db);
- case 's':
- first = parse_dot_star_expr(first, last, db);
- break;
- case 't':
- first = parse_dot_expr(first, last, db);
- break;
- case 'v':
- t = parse_binary_expression(first+2, last, "/", db);
- if (t != first+2)
- first = t;
- break;
- case 'V':
- t = parse_binary_expression(first+2, last, "/=", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'e':
- switch (t[1])
- {
- case 'o':
- t = parse_binary_expression(first+2, last, "^", db);
- if (t != first+2)
- first = t;
- break;
- case 'O':
- t = parse_binary_expression(first+2, last, "^=", db);
- if (t != first+2)
- first = t;
- break;
- case 'q':
- t = parse_binary_expression(first+2, last, "==", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'g':
- switch (t[1])
- {
- case 'e':
- t = parse_binary_expression(first+2, last, ">=", db);
- if (t != first+2)
- first = t;
- break;
- case 't':
- t = parse_binary_expression(first+2, last, ">", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'i':
- if (t[1] == 'x')
- {
- const char* t1 = parse_expression(first+2, last, db);
- if (t1 != first+2)
- {
- const char* t2 = parse_expression(t1, last, db);
- if (t2 != t1)
- {
- if (db.Names.size() < 2)
- return first;
- auto op2 = db.Names.back();
- db.Names.pop_back();
- auto op1 = db.Names.back();
- db.Names.back() =
- db.make<ArraySubscriptExpr>(op1, op2);
- first = t2;
- }
- else if (!db.Names.empty())
- db.Names.pop_back();
- }
- }
- break;
- case 'l':
- switch (t[1])
- {
- case 'e':
- t = parse_binary_expression(first+2, last, "<=", db);
- if (t != first+2)
- first = t;
- break;
- case 's':
- t = parse_binary_expression(first+2, last, "<<", db);
- if (t != first+2)
- first = t;
- break;
- case 'S':
- t = parse_binary_expression(first+2, last, "<<=", db);
- if (t != first+2)
- first = t;
- break;
- case 't':
- t = parse_binary_expression(first+2, last, "<", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'm':
- switch (t[1])
- {
- case 'i':
- t = parse_binary_expression(first+2, last, "-", db);
- if (t != first+2)
- first = t;
- break;
- case 'I':
- t = parse_binary_expression(first+2, last, "-=", db);
- if (t != first+2)
- first = t;
- break;
- case 'l':
- t = parse_binary_expression(first+2, last, "*", db);
- if (t != first+2)
- first = t;
- break;
- case 'L':
- t = parse_binary_expression(first+2, last, "*=", db);
- if (t != first+2)
- first = t;
- break;
- case 'm':
- if (first+2 != last && first[2] == '_')
- {
- t = parse_prefix_expression(first+3, last, "--", db);
- if (t != first+3)
- first = t;
- }
- else
- {
- const char* t1 = parse_expression(first+2, last, db);
- if (t1 != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<PostfixExpr>(db.Names.back(), "--");
- first = t1;
- }
- }
- break;
- }
- break;
- case 'n':
- switch (t[1])
- {
- case 'a':
- case 'w':
- first = parse_new_expr(first, last, db);
- break;
- case 'e':
- t = parse_binary_expression(first+2, last, "!=", db);
- if (t != first+2)
- first = t;
- break;
- case 'g':
- t = parse_prefix_expression(first+2, last, "-", db);
- if (t != first+2)
- first = t;
- break;
- case 't':
- t = parse_prefix_expression(first+2, last, "!", db);
- if (t != first+2)
- first = t;
- break;
- case 'x':
- t = parse_noexcept_expression(first+2, last, db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'o':
- switch (t[1])
- {
- case 'n':
- return parse_unresolved_name(first, last, db);
- case 'o':
- t = parse_binary_expression(first+2, last, "||", db);
- if (t != first+2)
- first = t;
- break;
- case 'r':
- t = parse_binary_expression(first+2, last, "|", db);
- if (t != first+2)
- first = t;
- break;
- case 'R':
- t = parse_binary_expression(first+2, last, "|=", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 'p':
- switch (t[1])
- {
- case 'm':
- t = parse_binary_expression(first+2, last, "->*", db);
- if (t != first+2)
- first = t;
- break;
- case 'l':
- t = parse_binary_expression(first+2, last, "+", db);
- if (t != first+2)
- first = t;
- break;
- case 'L':
- t = parse_binary_expression(first+2, last, "+=", db);
- if (t != first+2)
- first = t;
- break;
- case 'p':
- if (first+2 != last && first[2] == '_')
- {
- t = parse_prefix_expression(first+3, last, "++", db);
- if (t != first+3)
- first = t;
- }
- else
- {
- const char* t1 = parse_expression(first+2, last, db);
- if (t1 != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<PostfixExpr>(db.Names.back(), "++");
- first = t1;
- }
- }
- break;
- case 's':
- t = parse_prefix_expression(first+2, last, "+", db);
- if (t != first+2)
- first = t;
- break;
- case 't':
- first = parse_arrow_expr(first, last, db);
- break;
- }
- break;
- case 'q':
- if (t[1] == 'u')
- {
- const char* t1 = parse_expression(first+2, last, db);
- if (t1 != first+2)
- {
- const char* t2 = parse_expression(t1, last, db);
- if (t2 != t1)
- {
- const char* t3 = parse_expression(t2, last, db);
- if (t3 != t2)
- {
- if (db.Names.size() < 3)
- return first;
- auto op3 = db.Names.back();
- db.Names.pop_back();
- auto op2 = db.Names.back();
- db.Names.pop_back();
- auto op1 = db.Names.back();
- db.Names.back() =
- db.make<ConditionalExpr>(op1, op2, op3);
- first = t3;
- }
- else
- {
- if (db.Names.size() < 2)
- return first;
- db.Names.pop_back();
- db.Names.pop_back();
- }
- }
- else if (!db.Names.empty())
- db.Names.pop_back();
- }
- }
- break;
- case 'r':
- switch (t[1])
- {
- case 'c':
- first = parse_reinterpret_cast_expr(first, last, db);
- break;
- case 'm':
- t = parse_binary_expression(first+2, last, "%", db);
- if (t != first+2)
- first = t;
- break;
- case 'M':
- t = parse_binary_expression(first+2, last, "%=", db);
- if (t != first+2)
- first = t;
- break;
- case 's':
- t = parse_binary_expression(first+2, last, ">>", db);
- if (t != first+2)
- first = t;
- break;
- case 'S':
- t = parse_binary_expression(first+2, last, ">>=", db);
- if (t != first+2)
- first = t;
- break;
- }
- break;
- case 's':
- switch (t[1])
- {
- case 'c':
- first = parse_static_cast_expr(first, last, db);
- break;
- case 'p':
- first = parse_pack_expansion(first, last, db);
- break;
- case 'r':
- return parse_unresolved_name(first, last, db);
- case 't':
- first = parse_sizeof_type_expr(first, last, db);
- break;
- case 'z':
- first = parse_sizeof_expr_expr(first, last, db);
- break;
- case 'Z':
- if (last - t >= 3)
- {
- switch (t[2])
- {
- case 'T':
- first = parse_sizeof_param_pack_expr(first, last, db);
- break;
- case 'f':
- first = parse_sizeof_function_param_pack_expr(first, last, db);
- break;
- }
- }
- break;
- }
- break;
- case 't':
- switch (t[1])
- {
- case 'e':
- case 'i':
- first = parse_typeid_expr(first, last, db);
- break;
- case 'r':
- db.Names.push_back(db.make<NameType>("throw"));
- first += 2;
- break;
- case 'w':
- first = parse_throw_expr(first, last, db);
- break;
- }
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- return parse_unresolved_name(first, last, db);
- }
+ switch (*First) {
+ case 'L':
+ return parseExprPrimary();
+ case 'T':
+ return parseTemplateParam();
+ case 'f':
+ return parseFunctionParam();
+ case 'a':
+ switch (First[1]) {
+ case 'a':
+ First += 2;
+ return parseBinaryExpr("&&");
+ case 'd':
+ First += 2;
+ return parsePrefixExpr("&");
+ case 'n':
+ First += 2;
+ return parseBinaryExpr("&");
+ case 'N':
+ First += 2;
+ return parseBinaryExpr("&=");
+ case 'S':
+ First += 2;
+ return parseBinaryExpr("=");
+ case 't': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<EnclosingExpr>("alignof (", Ty, ")");
}
- return first;
+ case 'z': {
+ First += 2;
+ Node *Ty = parseExpr();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<EnclosingExpr>("alignof (", Ty, ")");
+ }
+ }
+ return nullptr;
+ case 'c':
+ switch (First[1]) {
+ // cc <type> <expression> # const_cast<type>(expression)
+ case 'c': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return Ty;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<CastExpr>("const_cast", Ty, Ex);
+ }
+ // cl <expression>+ E # call
+ case 'l': {
+ First += 2;
+ Node *Callee = parseExpr();
+ if (Callee == nullptr)
+ return Callee;
+ size_t ExprsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *E = parseExpr();
+ if (E == nullptr)
+ return E;
+ Names.push_back(E);
+ }
+ return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin));
+ }
+ case 'm':
+ First += 2;
+ return parseBinaryExpr(",");
+ case 'o':
+ First += 2;
+ return parsePrefixExpr("~");
+ case 'v':
+ return parseConversionExpr();
+ }
+ return nullptr;
+ case 'd':
+ switch (First[1]) {
+ case 'a': {
+ First += 2;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<DeleteExpr>(Ex, Global, /*is_array=*/true);
+ }
+ case 'c': {
+ First += 2;
+ Node *T = parseType();
+ if (T == nullptr)
+ return T;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<CastExpr>("dynamic_cast", T, Ex);
+ }
+ case 'e':
+ First += 2;
+ return parsePrefixExpr("*");
+ case 'l': {
+ First += 2;
+ Node *E = parseExpr();
+ if (E == nullptr)
+ return E;
+ return make<DeleteExpr>(E, Global, /*is_array=*/false);
+ }
+ case 'n':
+ return parseUnresolvedName();
+ case 's': {
+ First += 2;
+ Node *LHS = parseExpr();
+ if (LHS == nullptr)
+ return nullptr;
+ Node *RHS = parseExpr();
+ if (RHS == nullptr)
+ return nullptr;
+ return make<MemberExpr>(LHS, ".*", RHS);
+ }
+ case 't': {
+ First += 2;
+ Node *LHS = parseExpr();
+ if (LHS == nullptr)
+ return LHS;
+ Node *RHS = parseExpr();
+ if (RHS == nullptr)
+ return nullptr;
+ return make<MemberExpr>(LHS, ".", RHS);
+ }
+ case 'v':
+ First += 2;
+ return parseBinaryExpr("/");
+ case 'V':
+ First += 2;
+ return parseBinaryExpr("/=");
+ }
+ return nullptr;
+ case 'e':
+ switch (First[1]) {
+ case 'o':
+ First += 2;
+ return parseBinaryExpr("^");
+ case 'O':
+ First += 2;
+ return parseBinaryExpr("^=");
+ case 'q':
+ First += 2;
+ return parseBinaryExpr("==");
+ }
+ return nullptr;
+ case 'g':
+ switch (First[1]) {
+ case 'e':
+ First += 2;
+ return parseBinaryExpr(">=");
+ case 't':
+ First += 2;
+ return parseBinaryExpr(">");
+ }
+ return nullptr;
+ case 'i':
+ switch (First[1]) {
+ case 'x': {
+ First += 2;
+ Node *Base = parseExpr();
+ if (Base == nullptr)
+ return nullptr;
+ Node *Index = parseExpr();
+ if (Index == nullptr)
+ return Index;
+ return make<ArraySubscriptExpr>(Base, Index);
+ }
+ case 'l': {
+ First += 2;
+ size_t InitsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *E = parseBracedExpr();
+ if (E == nullptr)
+ return nullptr;
+ Names.push_back(E);
+ }
+ return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin));
+ }
+ }
+ return nullptr;
+ case 'l':
+ switch (First[1]) {
+ case 'e':
+ First += 2;
+ return parseBinaryExpr("<=");
+ case 's':
+ First += 2;
+ return parseBinaryExpr("<<");
+ case 'S':
+ First += 2;
+ return parseBinaryExpr("<<=");
+ case 't':
+ First += 2;
+ return parseBinaryExpr("<");
+ }
+ return nullptr;
+ case 'm':
+ switch (First[1]) {
+ case 'i':
+ First += 2;
+ return parseBinaryExpr("-");
+ case 'I':
+ First += 2;
+ return parseBinaryExpr("-=");
+ case 'l':
+ First += 2;
+ return parseBinaryExpr("*");
+ case 'L':
+ First += 2;
+ return parseBinaryExpr("*=");
+ case 'm':
+ First += 2;
+ if (consumeIf('_'))
+ return parsePrefixExpr("--");
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return nullptr;
+ return make<PostfixExpr>(Ex, "--");
+ }
+ return nullptr;
+ case 'n':
+ switch (First[1]) {
+ case 'a':
+ case 'w':
+ return parseNewExpr();
+ case 'e':
+ First += 2;
+ return parseBinaryExpr("!=");
+ case 'g':
+ First += 2;
+ return parsePrefixExpr("-");
+ case 't':
+ First += 2;
+ return parsePrefixExpr("!");
+ case 'x':
+ First += 2;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<EnclosingExpr>("noexcept (", Ex, ")");
+ }
+ return nullptr;
+ case 'o':
+ switch (First[1]) {
+ case 'n':
+ return parseUnresolvedName();
+ case 'o':
+ First += 2;
+ return parseBinaryExpr("||");
+ case 'r':
+ First += 2;
+ return parseBinaryExpr("|");
+ case 'R':
+ First += 2;
+ return parseBinaryExpr("|=");
+ }
+ return nullptr;
+ case 'p':
+ switch (First[1]) {
+ case 'm':
+ First += 2;
+ return parseBinaryExpr("->*");
+ case 'l':
+ First += 2;
+ return parseBinaryExpr("+");
+ case 'L':
+ First += 2;
+ return parseBinaryExpr("+=");
+ case 'p': {
+ First += 2;
+ if (consumeIf('_'))
+ return parsePrefixExpr("++");
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<PostfixExpr>(Ex, "++");
+ }
+ case 's':
+ First += 2;
+ return parsePrefixExpr("+");
+ case 't': {
+ First += 2;
+ Node *L = parseExpr();
+ if (L == nullptr)
+ return nullptr;
+ Node *R = parseExpr();
+ if (R == nullptr)
+ return nullptr;
+ return make<MemberExpr>(L, "->", R);
+ }
+ }
+ return nullptr;
+ case 'q':
+ if (First[1] == 'u') {
+ First += 2;
+ Node *Cond = parseExpr();
+ if (Cond == nullptr)
+ return nullptr;
+ Node *LHS = parseExpr();
+ if (LHS == nullptr)
+ return nullptr;
+ Node *RHS = parseExpr();
+ if (RHS == nullptr)
+ return nullptr;
+ return make<ConditionalExpr>(Cond, LHS, RHS);
+ }
+ return nullptr;
+ case 'r':
+ switch (First[1]) {
+ case 'c': {
+ First += 2;
+ Node *T = parseType();
+ if (T == nullptr)
+ return T;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<CastExpr>("reinterpret_cast", T, Ex);
+ }
+ case 'm':
+ First += 2;
+ return parseBinaryExpr("%");
+ case 'M':
+ First += 2;
+ return parseBinaryExpr("%=");
+ case 's':
+ First += 2;
+ return parseBinaryExpr(">>");
+ case 'S':
+ First += 2;
+ return parseBinaryExpr(">>=");
+ }
+ return nullptr;
+ case 's':
+ switch (First[1]) {
+ case 'c': {
+ First += 2;
+ Node *T = parseType();
+ if (T == nullptr)
+ return T;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<CastExpr>("static_cast", T, Ex);
+ }
+ case 'p': {
+ First += 2;
+ Node *Child = parseExpr();
+ if (Child == nullptr)
+ return nullptr;
+ return make<ParameterPackExpansion>(Child);
+ }
+ case 'r':
+ return parseUnresolvedName();
+ case 't': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return Ty;
+ return make<EnclosingExpr>("sizeof (", Ty, ")");
+ }
+ case 'z': {
+ First += 2;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<EnclosingExpr>("sizeof (", Ex, ")");
+ }
+ case 'Z':
+ First += 2;
+ if (look() == 'T') {
+ Node *R = parseTemplateParam();
+ if (R == nullptr)
+ return nullptr;
+ return make<SizeofParamPackExpr>(R);
+ } else if (look() == 'f') {
+ Node *FP = parseFunctionParam();
+ if (FP == nullptr)
+ return nullptr;
+ return make<EnclosingExpr>("sizeof...", FP, ")");
+ }
+ return nullptr;
+ }
+ return nullptr;
+ case 't':
+ switch (First[1]) {
+ case 'e': {
+ First += 2;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return Ex;
+ return make<EnclosingExpr>("typeid (", Ex, ")");
+ }
+ case 'i': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return Ty;
+ return make<EnclosingExpr>("typeid (", Ty, ")");
+ }
+ case 'l': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ size_t InitsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *E = parseBracedExpr();
+ if (E == nullptr)
+ return nullptr;
+ Names.push_back(E);
+ }
+ return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin));
+ }
+ case 'r':
+ First += 2;
+ return make<NameType>("throw");
+ case 'w': {
+ First += 2;
+ Node *Ex = parseExpr();
+ if (Ex == nullptr)
+ return nullptr;
+ return make<ThrowExpr>(Ex);
+ }
+ }
+ return nullptr;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return parseUnresolvedName();
+ }
+ return nullptr;
}
-// <template-arg> ::= <type> # type or template
-// ::= X <expression> E # expression
-// ::= <expr-primary> # simple expressions
-// ::= J <template-arg>* E # argument pack
-// ::= LZ <encoding> E # extension
+// <call-offset> ::= h <nv-offset> _
+// ::= v <v-offset> _
+//
+// <nv-offset> ::= <offset number>
+// # non-virtual base override
+//
+// <v-offset> ::= <offset number> _ <virtual offset number>
+// # virtual base override, with vcall offset
+bool Db::parseCallOffset() {
+ // Just scan through the call offset, we never add this information into the
+ // output.
+ if (consumeIf('h'))
+ return parseNumber(true).empty() || !consumeIf('_');
+ if (consumeIf('v'))
+ return parseNumber(true).empty() || !consumeIf('_') ||
+ parseNumber(true).empty() || !consumeIf('_');
+ return true;
+}
-const char*
-parse_template_arg(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- const char* t;
- switch (*first)
- {
- case 'X':
- t = parse_expression(first+1, last, db);
- if (t != first+1)
- {
- if (t != last && *t == 'E')
- first = t+1;
- }
- break;
- case 'J':
- t = first+1;
- if (t == last)
- return first;
- while (*t != 'E')
- {
- const char* t1 = parse_template_arg(t, last, db);
- if (t1 == t)
- return first;
- t = t1;
- }
- first = t+1;
- break;
- case 'L':
- // <expr-primary> or LZ <encoding> E
- if (first+1 != last && first[1] == 'Z')
- {
- t = parse_encoding(first+2, last, db);
- if (t != first+2 && t != last && *t == 'E')
- first = t+1;
- }
- else
- first = parse_expr_primary(first, last, db);
- break;
- default:
- // <type>
- first = parse_type(first, last, db);
- break;
- }
+// <special-name> ::= TV <type> # virtual table
+// ::= TT <type> # VTT structure (construction vtable index)
+// ::= TI <type> # typeinfo structure
+// ::= TS <type> # typeinfo name (null-terminated byte string)
+// ::= Tc <call-offset> <call-offset> <base encoding>
+// # base is the nominal target function of thunk
+// # first call-offset is 'this' adjustment
+// # second call-offset is result adjustment
+// ::= T <call-offset> <base encoding>
+// # base is the nominal target function of thunk
+// ::= GV <object name> # Guard variable for one-time initialization
+// # No <type>
+// ::= TW <object name> # Thread-local wrapper
+// ::= TH <object name> # Thread-local initialization
+// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
+// extension ::= GR <object name> # reference temporary for object
+Node *Db::parseSpecialName() {
+ switch (look()) {
+ case 'T':
+ switch (look(1)) {
+ // TV <type> # virtual table
+ case 'V': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<SpecialName>("vtable for ", Ty);
}
- return first;
+ // TT <type> # VTT structure (construction vtable index)
+ case 'T': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<SpecialName>("VTT for ", Ty);
+ }
+ // TI <type> # typeinfo structure
+ case 'I': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<SpecialName>("typeinfo for ", Ty);
+ }
+ // TS <type> # typeinfo name (null-terminated byte string)
+ case 'S': {
+ First += 2;
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ return make<SpecialName>("typeinfo name for ", Ty);
+ }
+ // Tc <call-offset> <call-offset> <base encoding>
+ case 'c': {
+ First += 2;
+ if (parseCallOffset() || parseCallOffset())
+ return nullptr;
+ Node *Encoding = parseEncoding();
+ if (Encoding == nullptr)
+ return nullptr;
+ return make<SpecialName>("covariant return thunk to ", Encoding);
+ }
+ // extension ::= TC <first type> <number> _ <second type>
+ // # construction vtable for second-in-first
+ case 'C': {
+ First += 2;
+ Node *FirstType = parseType();
+ if (FirstType == nullptr)
+ return nullptr;
+ if (parseNumber(true).empty() || !consumeIf('_'))
+ return nullptr;
+ Node *SecondType = parseType();
+ if (SecondType == nullptr)
+ return nullptr;
+ return make<CtorVtableSpecialName>(SecondType, FirstType);
+ }
+ // TW <object name> # Thread-local wrapper
+ case 'W': {
+ First += 2;
+ Node *Name = parseName();
+ if (Name == nullptr)
+ return nullptr;
+ return make<SpecialName>("thread-local wrapper routine for ", Name);
+ }
+ // TH <object name> # Thread-local initialization
+ case 'H': {
+ First += 2;
+ Node *Name = parseName();
+ if (Name == nullptr)
+ return nullptr;
+ return make<SpecialName>("thread-local initialization routine for ", Name);
+ }
+ // T <call-offset> <base encoding>
+ default: {
+ ++First;
+ bool IsVirt = look() == 'v';
+ if (parseCallOffset())
+ return nullptr;
+ Node *BaseEncoding = parseEncoding();
+ if (BaseEncoding == nullptr)
+ return nullptr;
+ if (IsVirt)
+ return make<SpecialName>("virtual thunk to ", BaseEncoding);
+ else
+ return make<SpecialName>("non-virtual thunk to ", BaseEncoding);
+ }
+ }
+ case 'G':
+ switch (look(1)) {
+ // GV <object name> # Guard variable for one-time initialization
+ case 'V': {
+ First += 2;
+ Node *Name = parseName();
+ if (Name == nullptr)
+ return nullptr;
+ return make<SpecialName>("guard variable for ", Name);
+ }
+ // GR <object name> # reference temporary for object
+ case 'R': {
+ First += 2;
+ Node *Name = parseName();
+ if (Name == nullptr)
+ return nullptr;
+ return make<SpecialName>("reference temporary for ", Name);
+ }
+ }
+ }
+ return nullptr;
+}
+
+// <encoding> ::= <function name> <bare-function-type>
+// ::= <data name>
+// ::= <special-name>
+Node *Db::parseEncoding() {
+ // Always "tag" templates (insert them into Db::TemplateParams) unless we're
+ // doing a second parse to resolve a forward template reference, in which case
+ // we only tag templates if EncodingDepth > 1.
+ // FIXME: This is kinda broken; it would be better to make a forward reference
+ // and patch it all in one pass.
+ SwapAndRestore<bool> SaveTagTemplates(TagTemplates,
+ TagTemplates || EncodingDepth);
+ SwapAndRestore<unsigned> SaveEncodingDepth(EncodingDepth, EncodingDepth + 1);
+
+ if (look() == 'G' || look() == 'T')
+ return parseSpecialName();
+
+ auto IsEndOfEncoding = [&] {
+ // The set of chars that can potentially follow an <encoding> (none of which
+ // can start a <type>). Enumerating these allows us to avoid speculative
+ // parsing.
+ return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';
+ };
+
+ NameState NameInfo;
+ Node *Name = parseName(&NameInfo);
+ if (Name == nullptr || IsEndOfEncoding())
+ return Name;
+
+ TagTemplates = false;
+
+ Node *ReturnType = nullptr;
+ if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) {
+ ReturnType = parseType();
+ if (ReturnType == nullptr)
+ return nullptr;
+ }
+
+ if (consumeIf('v'))
+ return make<FunctionEncoding>(ReturnType, Name, NodeArray(),
+ NameInfo.CVQualifiers,
+ NameInfo.ReferenceQualifier);
+
+ size_t ParamsBegin = Names.size();
+ do {
+ Node *Ty = parseType();
+ if (Ty == nullptr)
+ return nullptr;
+ Names.push_back(Ty);
+ } while (!IsEndOfEncoding());
+
+ return make<FunctionEncoding>(ReturnType, Name,
+ popTrailingNodeArray(ParamsBegin),
+ NameInfo.CVQualifiers,
+ NameInfo.ReferenceQualifier);
+}
+
+template <class Float>
+struct FloatData;
+
+template <>
+struct FloatData<float>
+{
+ static const size_t mangled_size = 8;
+ static const size_t max_demangled_size = 24;
+ static constexpr const char* spec = "%af";
+};
+
+constexpr const char* FloatData<float>::spec;
+
+template <>
+struct FloatData<double>
+{
+ static const size_t mangled_size = 16;
+ static const size_t max_demangled_size = 32;
+ static constexpr const char* spec = "%a";
+};
+
+constexpr const char* FloatData<double>::spec;
+
+template <>
+struct FloatData<long double>
+{
+#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
+ defined(__wasm__)
+ static const size_t mangled_size = 32;
+#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
+ static const size_t mangled_size = 16;
+#else
+ static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms
+#endif
+ static const size_t max_demangled_size = 40;
+ static constexpr const char *spec = "%LaL";
+};
+
+constexpr const char *FloatData<long double>::spec;
+
+template <class Float> Node *Db::parseFloatingLiteral() {
+ const size_t N = FloatData<Float>::mangled_size;
+ if (numLeft() <= N)
+ return nullptr;
+ StringView Data(First, First + N);
+ for (char C : Data)
+ if (!std::isxdigit(C))
+ return nullptr;
+ First += N;
+ if (!consumeIf('E'))
+ return nullptr;
+ return make<FloatExpr<Float>>(Data);
+}
+
+// <seq-id> ::= <0-9A-Z>+
+bool Db::parseSeqId(size_t *Out) {
+ if (!(look() >= '0' && look() <= '9') &&
+ !(look() >= 'A' && look() <= 'Z'))
+ return true;
+
+ size_t Id = 0;
+ while (true) {
+ if (look() >= '0' && look() <= '9') {
+ Id *= 36;
+ Id += static_cast<size_t>(look() - '0');
+ } else if (look() >= 'A' && look() <= 'Z') {
+ Id *= 36;
+ Id += static_cast<size_t>(look() - 'A') + 10;
+ } else {
+ *Out = Id;
+ return false;
+ }
+ ++First;
+ }
+}
+
+// <substitution> ::= S <seq-id> _
+// ::= S_
+// <substitution> ::= Sa # ::std::allocator
+// <substitution> ::= Sb # ::std::basic_string
+// <substitution> ::= Ss # ::std::basic_string < char,
+// ::std::char_traits<char>,
+// ::std::allocator<char> >
+// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> >
+// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> >
+// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
+Node *Db::parseSubstitution() {
+ if (!consumeIf('S'))
+ return nullptr;
+
+ if (std::islower(look())) {
+ Node *SpecialSub;
+ switch (look()) {
+ case 'a':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::allocator);
+ break;
+ case 'b':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::basic_string);
+ break;
+ case 's':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::string);
+ break;
+ case 'i':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::istream);
+ break;
+ case 'o':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::ostream);
+ break;
+ case 'd':
+ ++First;
+ SpecialSub = make<SpecialSubstitution>(SpecialSubKind::iostream);
+ break;
+ default:
+ return nullptr;
+ }
+ // Itanium C++ ABI 5.1.2: If a name that would use a built-in <substitution>
+ // has ABI tags, the tags are appended to the substitution; the result is a
+ // substitutable component.
+ Node *WithTags = parseAbiTags(SpecialSub);
+ if (WithTags != SpecialSub) {
+ Subs.push_back(WithTags);
+ SpecialSub = WithTags;
+ }
+ return SpecialSub;
+ }
+
+ // ::= S_
+ if (consumeIf('_')) {
+ if (Subs.empty())
+ return nullptr;
+ return Subs[0];
+ }
+
+ // ::= S <seq-id> _
+ size_t Index = 0;
+ if (parseSeqId(&Index))
+ return nullptr;
+ ++Index;
+ if (!consumeIf('_') || Index >= Subs.size())
+ return nullptr;
+ return Subs[Index];
+}
+
+// <template-param> ::= T_ # first template parameter
+// ::= T <parameter-2 non-negative number> _
+Node *Db::parseTemplateParam() {
+ if (!consumeIf('T'))
+ return nullptr;
+
+ if (consumeIf('_')) {
+ if (TemplateParams.empty()) {
+ FixForwardReferences = true;
+ return make<NameType>("FORWARD_REFERENCE");
+ }
+ return TemplateParams[0];
+ }
+
+ size_t Index;
+ if (parsePositiveInteger(&Index))
+ return nullptr;
+ ++Index;
+ if (!consumeIf('_'))
+ return nullptr;
+ if (Index >= TemplateParams.size()) {
+ FixForwardReferences = true;
+ return make<NameType>("FORWARD_REFERENCE");
+ }
+ return TemplateParams[Index];
+}
+
+// <template-arg> ::= <type> # type or template
+// ::= X <expression> E # expression
+// ::= <expr-primary> # simple expressions
+// ::= J <template-arg>* E # argument pack
+// ::= LZ <encoding> E # extension
+Node *Db::parseTemplateArg() {
+ switch (look()) {
+ case 'X': {
+ ++First;
+ Node *Arg = parseExpr();
+ if (Arg == nullptr || !consumeIf('E'))
+ return nullptr;
+ return Arg;
+ }
+ case 'J': {
+ ++First;
+ size_t ArgsBegin = Names.size();
+ while (!consumeIf('E')) {
+ Node *Arg = parseTemplateArg();
+ if (Arg == nullptr)
+ return nullptr;
+ Names.push_back(Arg);
+ }
+ NodeArray Args = popTrailingNodeArray(ArgsBegin);
+ return make<TemplateArgumentPack>(Args);
+ }
+ case 'L': {
+ // ::= LZ <encoding> E # extension
+ if (look(1) == 'Z') {
+ First += 2;
+ Node *Arg = parseEncoding();
+ if (Arg == nullptr || !consumeIf('E'))
+ return nullptr;
+ return Arg;
+ }
+ // ::= <expr-primary> # simple expressions
+ return parseExprPrimary();
+ }
+ default:
+ return parseType();
+ }
}
// <template-args> ::= I <template-arg>* E
// extension, the abi says <template-arg>+
+Node *Db::parseTemplateArgs() {
+ if (!consumeIf('I'))
+ return nullptr;
-const char*
-parse_template_args(const char* first, const char* last, Db& db)
-{
- if (last - first >= 2 && *first == 'I')
- {
- if (db.TagTemplates)
- db.TemplateParams.clear();
- const char* t = first+1;
- size_t begin_idx = db.Names.size();
- while (*t != 'E')
- {
- if (db.TagTemplates)
- {
- auto TmpParams = std::move(db.TemplateParams);
- size_t k0 = db.Names.size();
- const char* t1 = parse_template_arg(t, last, db);
- size_t k1 = db.Names.size();
- db.TemplateParams = std::move(TmpParams);
+ // <template-params> refer to the innermost <template-args>. Clear out any
+ // outer args that we may have inserted into TemplateParams.
+ if (TagTemplates)
+ TemplateParams.clear();
- if (t1 == t || t1 == last || k0 > k1)
- return first;
- db.TemplateParams.pushPack();
- for (size_t k = k0; k < k1; ++k)
- db.TemplateParams.pushSubstitutionIntoPack(db.Names[k]);
- t = t1;
- continue;
- }
- size_t k0 = db.Names.size();
- const char* t1 = parse_template_arg(t, last, db);
- size_t k1 = db.Names.size();
- if (t1 == t || t1 == last || k0 > k1)
- return first;
- t = t1;
- }
- if (begin_idx > db.Names.size())
- return first;
- first = t + 1;
- TemplateParams* tp = db.make<TemplateParams>(
- db.popTrailingNodeArray(begin_idx));
- db.Names.push_back(tp);
+ size_t ArgsBegin = Names.size();
+ while (!consumeIf('E')) {
+ if (TagTemplates) {
+ auto OldParams = std::move(TemplateParams);
+ Node *Arg = parseTemplateArg();
+ TemplateParams = std::move(OldParams);
+ if (Arg == nullptr)
+ return nullptr;
+ Names.push_back(Arg);
+ Node *TableEntry = Arg;
+ if (Arg->getKind() == Node::KTemplateArgumentPack) {
+ TableEntry = make<ParameterPack>(
+ static_cast<TemplateArgumentPack*>(TableEntry)->getElements());
+ }
+ TemplateParams.push_back(TableEntry);
+ } else {
+ Node *Arg = parseTemplateArg();
+ if (Arg == nullptr)
+ return nullptr;
+ Names.push_back(Arg);
}
- return first;
-}
-
-// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
-// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
-//
-// <prefix> ::= <prefix> <unqualified-name>
-// ::= <template-prefix> <template-args>
-// ::= <template-param>
-// ::= <decltype>
-// ::= # empty
-// ::= <substitution>
-// ::= <prefix> <data-member-prefix>
-// extension ::= L
-//
-// <template-prefix> ::= <prefix> <template unqualified-name>
-// ::= <template-param>
-// ::= <substitution>
-
-const char*
-parse_nested_name(const char* first, const char* last, Db& db,
- bool* ends_with_template_args)
-{
- if (first != last && *first == 'N')
- {
- Qualifiers cv;
- const char* t0 = parse_cv_qualifiers(first+1, last, cv);
- if (t0 == last)
- return first;
- db.RefQuals = FrefQualNone;
- if (*t0 == 'R')
- {
- db.RefQuals = FrefQualLValue;
- ++t0;
- }
- else if (*t0 == 'O')
- {
- db.RefQuals = FrefQualRValue;
- ++t0;
- }
- db.Names.push_back(db.make<EmptyName>());
- if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't')
- {
- t0 += 2;
- db.Names.back() = db.make<NameType>("std");
- }
- if (t0 == last)
- return first;
- bool pop_subs = false;
- bool component_ends_with_template_args = false;
- while (*t0 != 'E')
- {
- component_ends_with_template_args = false;
- const char* t1;
- switch (*t0)
- {
- case 'S':
- if (t0 + 1 != last && t0[1] == 't')
- goto do_parse_unqualified_name;
- t1 = parse_substitution(t0, last, db);
- if (t1 != t0 && t1 != last)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.back()->K != Node::KEmptyName)
- {
- db.Names.back() = db.make<QualifiedName>(
- db.Names.back(), name);
- db.Subs.pushSubstitution(db.Names.back());
- }
- else
- db.Names.back() = name;
- pop_subs = true;
- t0 = t1;
- }
- else
- return first;
- break;
- case 'T':
- t1 = parse_template_param(t0, last, db);
- if (t1 != t0 && t1 != last)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.back()->K != Node::KEmptyName)
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), name);
- else
- db.Names.back() = name;
- db.Subs.pushSubstitution(db.Names.back());
- pop_subs = true;
- t0 = t1;
- }
- else
- return first;
- break;
- case 'D':
- if (t0 + 1 != last && t0[1] != 't' && t0[1] != 'T')
- goto do_parse_unqualified_name;
- t1 = parse_decltype(t0, last, db);
- if (t1 != t0 && t1 != last)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.back()->K != Node::KEmptyName)
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), name);
- else
- db.Names.back() = name;
- db.Subs.pushSubstitution(db.Names.back());
- pop_subs = true;
- t0 = t1;
- }
- else
- return first;
- break;
- case 'I':
- t1 = parse_template_args(t0, last, db);
- if (t1 != t0 && t1 != last)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- db.Names.back() = db.make<NameWithTemplateArgs>(
- db.Names.back(), name);
- db.Subs.pushSubstitution(db.Names.back());
- t0 = t1;
- component_ends_with_template_args = true;
- }
- else
- return first;
- break;
- case 'L':
- if (++t0 == last)
- return first;
- break;
- default:
- do_parse_unqualified_name:
- t1 = parse_unqualified_name(t0, last, db);
- if (t1 != t0 && t1 != last)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.back()->K != Node::KEmptyName)
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), name);
- else
- db.Names.back() = name;
- db.Subs.pushSubstitution(db.Names.back());
- pop_subs = true;
- t0 = t1;
- }
- else
- return first;
- }
- }
- first = t0 + 1;
- db.CV = cv;
- if (pop_subs && !db.Subs.empty())
- db.Subs.popPack();
- if (ends_with_template_args)
- *ends_with_template_args = component_ends_with_template_args;
- }
- return first;
+ }
+ return make<TemplateArgs>(popTrailingNodeArray(ArgsBegin));
}
// <discriminator> := _ <non-negative number> # when number < 10
@@ -5440,719 +4765,106 @@
return first;
}
-// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
-// := Z <function encoding> E s [<discriminator>]
-// := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
-
-const char*
-parse_local_name(const char* first, const char* last, Db& db,
- bool* ends_with_template_args)
-{
- if (first != last && *first == 'Z')
- {
- const char* t = parse_encoding(first+1, last, db);
- if (t != first+1 && t != last && *t == 'E' && ++t != last)
- {
- switch (*t)
- {
- case 's':
- first = parse_discriminator(t+1, last);
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<QualifiedName>(
- db.Names.back(), db.make<NameType>("string literal"));
- break;
- case 'd':
- if (++t != last)
- {
- const char* t1 = parse_number(t, last);
- if (t1 != last && *t1 == '_')
- {
- t = t1 + 1;
- t1 = parse_name(t, last, db,
- ends_with_template_args);
- if (t1 != t)
- {
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), name);
- first = t1;
- }
- else if (!db.Names.empty())
- db.Names.pop_back();
- }
- }
- break;
- default:
- {
- const char* t1 = parse_name(t, last, db,
- ends_with_template_args);
- if (t1 != t)
- {
- // parse but ignore discriminator
- first = parse_discriminator(t1, last);
- if (db.Names.size() < 2)
- return first;
- auto name = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<QualifiedName>(db.Names.back(), name);
- }
- else if (!db.Names.empty())
- db.Names.pop_back();
- }
- break;
- }
- }
- }
- return first;
-}
-
-// <name> ::= <nested-name> // N
-// ::= <local-name> # See Scope Encoding below // Z
-// ::= <unscoped-template-name> <template-args>
-// ::= <unscoped-name>
-
-// <unscoped-template-name> ::= <unscoped-name>
-// ::= <substitution>
-
-const char*
-parse_name(const char* first, const char* last, Db& db,
- bool* ends_with_template_args)
-{
- if (last - first >= 2)
- {
- const char* t0 = first;
- // extension: ignore L here
- if (*t0 == 'L')
- ++t0;
- switch (*t0)
- {
- case 'N':
- {
- const char* t1 = parse_nested_name(t0, last, db,
- ends_with_template_args);
- if (t1 != t0)
- first = t1;
- break;
- }
- case 'Z':
- {
- const char* t1 = parse_local_name(t0, last, db,
- ends_with_template_args);
- if (t1 != t0)
- first = t1;
- break;
- }
- default:
- {
- const char* t1 = parse_unscoped_name(t0, last, db);
- if (t1 != t0)
- {
- if (t1 != last && *t1 == 'I') // <unscoped-template-name> <template-args>
- {
- if (db.Names.empty())
- return first;
- db.Subs.pushSubstitution(db.Names.back());
- t0 = t1;
- t1 = parse_template_args(t0, last, db);
- if (t1 != t0)
- {
- if (db.Names.size() < 2)
- return first;
- auto tmp = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<NameWithTemplateArgs>(
- db.Names.back(), tmp);
- first = t1;
- if (ends_with_template_args)
- *ends_with_template_args = true;
- }
- }
- else // <unscoped-name>
- first = t1;
- }
- else
- { // try <substitution> <template-args>
- t1 = parse_substitution(t0, last, db);
- if (t1 != t0 && t1 != last && *t1 == 'I')
- {
- t0 = t1;
- t1 = parse_template_args(t0, last, db);
- if (t1 != t0)
- {
- if (db.Names.size() < 2)
- return first;
- auto tmp = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<NameWithTemplateArgs>(
- db.Names.back(), tmp);
- first = t1;
- if (ends_with_template_args)
- *ends_with_template_args = true;
- }
- }
- }
- break;
- }
- }
- }
- return first;
-}
-
-// <call-offset> ::= h <nv-offset> _
-// ::= v <v-offset> _
-//
-// <nv-offset> ::= <offset number>
-// # non-virtual base override
-//
-// <v-offset> ::= <offset number> _ <virtual offset number>
-// # virtual base override, with vcall offset
-
-const char*
-parse_call_offset(const char* first, const char* last)
-{
- if (first != last)
- {
- switch (*first)
- {
- case 'h':
- {
- const char* t = parse_number(first + 1, last);
- if (t != first + 1 && t != last && *t == '_')
- first = t + 1;
- }
- break;
- case 'v':
- {
- const char* t = parse_number(first + 1, last);
- if (t != first + 1 && t != last && *t == '_')
- {
- const char* t2 = parse_number(++t, last);
- if (t2 != t && t2 != last && *t2 == '_')
- first = t2 + 1;
- }
- }
- break;
- }
- }
- return first;
-}
-
-// <special-name> ::= TV <type> # virtual table
-// ::= TT <type> # VTT structure (construction vtable index)
-// ::= TI <type> # typeinfo structure
-// ::= TS <type> # typeinfo name (null-terminated byte string)
-// ::= Tc <call-offset> <call-offset> <base encoding>
-// # base is the nominal target function of thunk
-// # first call-offset is 'this' adjustment
-// # second call-offset is result adjustment
-// ::= T <call-offset> <base encoding>
-// # base is the nominal target function of thunk
-// ::= GV <object name> # Guard variable for one-time initialization
-// # No <type>
-// ::= TW <object name> # Thread-local wrapper
-// ::= TH <object name> # Thread-local initialization
-// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
-// extension ::= GR <object name> # reference temporary for object
-
-const char*
-parse_special_name(const char* first, const char* last, Db& db)
-{
- if (last - first > 2)
- {
- const char* t;
- switch (*first)
- {
- case 'T':
- switch (first[1])
- {
- case 'V':
- // TV <type> # virtual table
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("vtable for ", db.Names.back());
- first = t;
- }
- break;
- case 'T':
- // TT <type> # VTT structure (construction vtable index)
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("VTT for ", db.Names.back());
- first = t;
- }
- break;
- case 'I':
- // TI <type> # typeinfo structure
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("typeinfo for ", db.Names.back());
- first = t;
- }
- break;
- case 'S':
- // TS <type> # typeinfo name (null-terminated byte string)
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("typeinfo name for ", db.Names.back());
- first = t;
- }
- break;
- case 'c':
- // Tc <call-offset> <call-offset> <base encoding>
- {
- const char* t0 = parse_call_offset(first+2, last);
- if (t0 == first+2)
- break;
- const char* t1 = parse_call_offset(t0, last);
- if (t1 == t0)
- break;
- t = parse_encoding(t1, last, db);
- if (t != t1)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("covariant return thunk to ",
- db.Names.back());
- first = t;
- }
- }
- break;
- case 'C':
- // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
- t = parse_type(first+2, last, db);
- if (t != first+2)
- {
- const char* t0 = parse_number(t, last);
- if (t0 != t && t0 != last && *t0 == '_')
- {
- const char* t1 = parse_type(++t0, last, db);
- if (t1 != t0)
- {
- if (db.Names.size() < 2)
- return first;
- auto left = db.Names.back();
- db.Names.pop_back();
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<CtorVtableSpecialName>(
- left, db.Names.back());
- first = t1;
- }
- }
- }
- break;
- case 'W':
- // TW <object name> # Thread-local wrapper
- t = parse_name(first + 2, last, db);
- if (t != first + 2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("thread-local wrapper routine for ",
- db.Names.back());
- first = t;
- }
- break;
- case 'H':
- //TH <object name> # Thread-local initialization
- t = parse_name(first + 2, last, db);
- if (t != first + 2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() = db.make<SpecialName>(
- "thread-local initialization routine for ", db.Names.back());
- first = t;
- }
- break;
- default:
- // T <call-offset> <base encoding>
- {
- const char* t0 = parse_call_offset(first+1, last);
- if (t0 == first+1)
- break;
- t = parse_encoding(t0, last, db);
- if (t != t0)
- {
- if (db.Names.empty())
- return first;
- if (first[1] == 'v')
- {
- db.Names.back() =
- db.make<SpecialName>("virtual thunk to ",
- db.Names.back());
- first = t;
- }
- else
- {
- db.Names.back() =
- db.make<SpecialName>("non-virtual thunk to ",
- db.Names.back());
- first = t;
- }
- }
- }
- break;
- }
- break;
- case 'G':
- switch (first[1])
- {
- case 'V':
- // GV <object name> # Guard variable for one-time initialization
- t = parse_name(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("guard variable for ", db.Names.back());
- first = t;
- }
- break;
- case 'R':
- // extension ::= GR <object name> # reference temporary for object
- t = parse_name(first+2, last, db);
- if (t != first+2)
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("reference temporary for ",
- db.Names.back());
- first = t;
- }
- break;
- }
- break;
- }
- }
- return first;
-}
-
-template <class T>
-class save_value
-{
- T& restore_;
- T original_value_;
-public:
- save_value(T& restore)
- : restore_(restore),
- original_value_(restore)
- {}
-
- ~save_value()
- {
- restore_ = std::move(original_value_);
- }
-
- save_value(const save_value&) = delete;
- save_value& operator=(const save_value&) = delete;
-};
-
-// <encoding> ::= <function name> <bare-function-type>
-// ::= <data name>
-// ::= <special-name>
-
-const char*
-parse_encoding(const char* first, const char* last, Db& db)
-{
- if (first != last)
- {
- save_value<decltype(db.EncodingDepth)> su(db.EncodingDepth);
- ++db.EncodingDepth;
- save_value<decltype(db.TagTemplates)> sb(db.TagTemplates);
- if (db.EncodingDepth > 1)
- db.TagTemplates = true;
- save_value<decltype(db.ParsedCtorDtorCV)> sp(db.ParsedCtorDtorCV);
- db.ParsedCtorDtorCV = false;
- switch (*first)
- {
- case 'G':
- case 'T':
- first = parse_special_name(first, last, db);
- break;
- default:
- {
- bool ends_with_template_args = false;
- const char* t = parse_name(first, last, db,
- &ends_with_template_args);
- if (db.Names.empty())
- return first;
- Qualifiers cv = db.CV;
- FunctionRefQual ref = db.RefQuals;
- if (t != first)
- {
- if (t != last && *t != 'E' && *t != '.')
- {
- save_value<bool> sb2(db.TagTemplates);
- db.TagTemplates = false;
- const char* t2;
- if (db.Names.empty())
- return first;
- if (!db.Names.back())
- return first;
- Node* return_type = nullptr;
- if (!db.ParsedCtorDtorCV && ends_with_template_args)
- {
- t2 = parse_type(t, last, db);
- if (t2 == t)
- return first;
- if (db.Names.size() < 1)
- return first;
- return_type = db.Names.back();
- db.Names.pop_back();
- t = t2;
- }
-
- Node* result = nullptr;
-
- if (t != last && *t == 'v')
- {
- ++t;
- if (db.Names.empty())
- return first;
- Node* name = db.Names.back();
- db.Names.pop_back();
- result = db.make<TopLevelFunctionDecl>(
- return_type, name, NodeArray());
- }
- else
- {
- size_t params_begin = db.Names.size();
- while (true)
- {
- t2 = parse_type(t, last, db);
- if (t2 == t)
- break;
- t = t2;
- }
- if (db.Names.size() < params_begin)
- return first;
- NodeArray params =
- db.popTrailingNodeArray(params_begin);
- if (db.Names.empty())
- return first;
- Node* name = db.Names.back();
- db.Names.pop_back();
- result = db.make<TopLevelFunctionDecl>(
- return_type, name, params);
- }
- if (ref != FrefQualNone)
- result = db.make<FunctionRefQualType>(result, ref);
- if (cv != QualNone)
- result = db.make<FunctionQualType>(result, cv);
- db.Names.push_back(result);
- first = t;
- }
- else
- first = t;
- }
- break;
- }
- }
- }
- return first;
-}
-
-// _block_invoke
-// _block_invoke<decimal-digit>+
-// _block_invoke_<decimal-digit>+
-
-const char*
-parse_block_invoke(const char* first, const char* last, Db& db)
-{
- if (last - first >= 13)
- {
- // FIXME: strcmp?
- const char test[] = "_block_invoke";
- const char* t = first;
- for (int i = 0; i < 13; ++i, ++t)
- {
- if (*t != test[i])
- return first;
- }
- if (t != last)
- {
- if (*t == '_')
- {
- // must have at least 1 decimal digit
- if (++t == last || !std::isdigit(*t))
- return first;
- ++t;
- }
- // parse zero or more digits
- while (t != last && isdigit(*t))
- ++t;
- }
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<SpecialName>("invocation function for block in ",
- db.Names.back());
- first = t;
- }
- return first;
-}
-
-// extension
-// <dot-suffix> := .<anything and everything>
-
-const char*
-parse_dot_suffix(const char* first, const char* last, Db& db)
-{
- if (first != last && *first == '.')
- {
- if (db.Names.empty())
- return first;
- db.Names.back() =
- db.make<DotSuffix>(db.Names.back(), StringView(first, last));
- first = last;
- }
- return first;
-}
-
-// <block-involcaton-function> ___Z<encoding>_block_invoke
-// <block-involcaton-function> ___Z<encoding>_block_invoke<decimal-digit>+
-// <block-involcaton-function> ___Z<encoding>_block_invoke_<decimal-digit>+
-// <mangled-name> ::= _Z<encoding>
+// <mangled-name> ::= _Z <encoding>
// ::= <type>
+// extension ::= ___Z <encoding> _block_invoke
+// extension ::= ___Z <encoding> _block_invoke<decimal-digit>+
+// extension ::= ___Z <encoding> _block_invoke_<decimal-digit>+
+Node *Db::parse() {
+ if (consumeIf("_Z")) {
+ Node *Encoding = parseEncoding();
+ if (Encoding == nullptr)
+ return nullptr;
+ if (look() == '.') {
+ Encoding = make<DotSuffix>(Encoding, StringView(First, Last));
+ First = Last;
+ }
+ if (numLeft() != 0)
+ return nullptr;
+ return Encoding;
+ }
-void
-demangle(const char* first, const char* last, Db& db, int& status)
-{
- if (first >= last)
- {
- status = invalid_mangled_name;
- return;
- }
- if (*first == '_')
- {
- if (last - first >= 4)
- {
- if (first[1] == 'Z')
- {
- const char* t = parse_encoding(first+2, last, db);
- if (t != first+2 && t != last && *t == '.')
- t = parse_dot_suffix(t, last, db);
- if (t != last)
- status = invalid_mangled_name;
- }
- else if (first[1] == '_' && first[2] == '_' && first[3] == 'Z')
- {
- const char* t = parse_encoding(first+4, last, db);
- if (t != first+4 && t != last)
- {
- const char* t1 = parse_block_invoke(t, last, db);
- if (t1 != last)
- status = invalid_mangled_name;
- }
- else
- status = invalid_mangled_name;
- }
- else
- status = invalid_mangled_name;
- }
- else
- status = invalid_mangled_name;
- }
- else
- {
- const char* t = parse_type(first, last, db);
- if (t != last)
- status = invalid_mangled_name;
- }
- if (status == success && db.Names.empty())
- status = invalid_mangled_name;
+ if (consumeIf("___Z")) {
+ Node *Encoding = parseEncoding();
+ if (Encoding == nullptr || !consumeIf("_block_invoke"))
+ return nullptr;
+ consumeIf('_');
+ if (parseNumber().empty())
+ return nullptr;
+ if (numLeft() != 0)
+ return nullptr;
+ return make<SpecialName>("invocation function for block in ", Encoding);
+ }
+
+ Node *Ty = parseType();
+ if (numLeft() != 0)
+ return nullptr;
+ return Ty;
}
-
} // unnamed namespace
+enum {
+ unknown_error = -4,
+ invalid_args = -3,
+ invalid_mangled_name = -2,
+ memory_alloc_failure = -1,
+ success = 0,
+};
+
+namespace __cxxabiv1 {
extern "C" _LIBCXXABI_FUNC_VIS char *
-__cxa_demangle(const char *mangled_name, char *buf, size_t *n, int *status) {
- if (mangled_name == nullptr || (buf != nullptr && n == nullptr))
- {
- if (status)
- *status = invalid_args;
- return nullptr;
+__cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) {
+ if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) {
+ if (Status)
+ *Status = invalid_args;
+ return nullptr;
+ }
+
+ size_t BufSize = Buf != nullptr ? *N : 0;
+ int InternalStatus = success;
+ size_t MangledNameLength = std::strlen(MangledName);
+
+ Db Parser(MangledName, MangledName + MangledNameLength);
+ Node *AST = Parser.parse();
+
+ if (AST == nullptr)
+ InternalStatus = invalid_mangled_name;
+
+ if (InternalStatus == success && Parser.FixForwardReferences &&
+ !Parser.TemplateParams.empty()) {
+ Parser.FixForwardReferences = false;
+ Parser.TagTemplates = false;
+ Parser.Names.clear();
+ Parser.Subs.clear();
+ Parser.First = MangledName;
+ Parser.Last = MangledName + MangledNameLength;
+ AST = Parser.parse();
+ if (AST == nullptr || Parser.FixForwardReferences)
+ InternalStatus = invalid_mangled_name;
+ }
+
+ if (InternalStatus == success && AST->containsUnexpandedParameterPack())
+ InternalStatus = invalid_mangled_name;
+
+ if (InternalStatus == success) {
+ if (Buf == nullptr) {
+ BufSize = 1024;
+ Buf = static_cast<char*>(std::malloc(BufSize));
}
- size_t internal_size = buf != nullptr ? *n : 0;
- Db db;
- int internal_status = success;
- size_t len = std::strlen(mangled_name);
- demangle(mangled_name, mangled_name + len, db,
- internal_status);
+ 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;
+ }
- if (internal_status == success && db.FixForwardReferences &&
- !db.TemplateParams.empty())
- {
- db.FixForwardReferences = false;
- db.TagTemplates = false;
- db.Names.clear();
- db.Subs.clear();
- demangle(mangled_name, mangled_name + len, db, internal_status);
- if (db.FixForwardReferences)
- internal_status = invalid_mangled_name;
- }
-
- if (internal_status == success)
- {
- if (!buf)
- {
- internal_size = 1024;
- buf = static_cast<char*>(std::malloc(internal_size));
- }
-
- if (buf)
- {
- OutputStream s(buf, internal_size);
- db.Names.back()->print(s);
- s += '\0';
- if (n) *n = s.getCurrentPosition();
- buf = s.getBuffer();
- }
- else
- internal_status = memory_alloc_failure;
- }
- else
- buf = nullptr;
- if (status)
- *status = internal_status;
- return buf;
+ if (Status)
+ *Status = InternalStatus;
+ return InternalStatus == success ? Buf : nullptr;
}
-
} // __cxxabiv1
diff --git a/src/cxa_exception.cpp b/src/cxa_exception.cpp
index e6ed093..b77f3a8 100644
--- a/src/cxa_exception.cpp
+++ b/src/cxa_exception.cpp
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS
+
#include "cxxabi.h"
#include <exception> // for std::terminate
@@ -137,6 +139,28 @@
std::__terminate(exception_header->terminateHandler);
}
+// Return the offset of the __cxa_exception header from the start of the
+// allocated buffer. If __cxa_exception's alignment is smaller than the maximum
+// useful alignment for the target machine, padding has to be inserted before
+// the header to ensure the thrown object that follows the header is
+// sufficiently aligned. This happens if _Unwind_exception isn't double-word
+// aligned (on Darwin, for example).
+static size_t get_cxa_exception_offset() {
+ struct S {
+ } __attribute__((aligned));
+
+ // Compute the maximum alignment for the target machine.
+ constexpr size_t alignment = std::alignment_of<S>::value;
+ constexpr size_t excp_size = sizeof(__cxa_exception);
+ constexpr size_t aligned_size =
+ (excp_size + alignment - 1) / alignment * alignment;
+ constexpr size_t offset = aligned_size - excp_size;
+ static_assert((offset == 0 ||
+ std::alignment_of<_Unwind_Exception>::value < alignment),
+ "offset is non-zero only if _Unwind_Exception isn't aligned");
+ return offset;
+}
+
extern "C" {
// Allocate a __cxa_exception object, and zero-fill it.
@@ -146,10 +170,16 @@
// user's exception object.
void *__cxa_allocate_exception(size_t thrown_size) throw() {
size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size);
- __cxa_exception *exception_header =
- static_cast<__cxa_exception *>(__aligned_malloc_with_fallback(actual_size));
- if (NULL == exception_header)
+
+ // Allocate extra space before the __cxa_exception header to ensure the
+ // start of the thrown object is sufficiently aligned.
+ size_t header_offset = get_cxa_exception_offset();
+ char *raw_buffer =
+ (char *)__aligned_malloc_with_fallback(header_offset + actual_size);
+ if (NULL == raw_buffer)
std::terminate();
+ __cxa_exception *exception_header =
+ static_cast<__cxa_exception *>((void *)(raw_buffer + header_offset));
std::memset(exception_header, 0, actual_size);
return thrown_object_from_cxa_exception(exception_header);
}
@@ -157,7 +187,11 @@
// Free a __cxa_exception object allocated with __cxa_allocate_exception.
void __cxa_free_exception(void *thrown_object) throw() {
- __aligned_free_with_fallback(cxa_exception_from_thrown_object(thrown_object));
+ // Compute the size of the padding before the header.
+ size_t header_offset = get_cxa_exception_offset();
+ char *raw_buffer =
+ ((char *)cxa_exception_from_thrown_object(thrown_object)) - header_offset;
+ __aligned_free_with_fallback((void *)raw_buffer);
}
diff --git a/src/cxa_exception.hpp b/src/cxa_exception.hpp
index b9f74e3..c8b0fb1 100644
--- a/src/cxa_exception.hpp
+++ b/src/cxa_exception.hpp
@@ -60,22 +60,7 @@
// previously adding padded to 64-bit align unwindHeader.
size_t referenceCount;
#endif
-
- // This field is annotated with attribute aligned so that the exception
- // object following the field is sufficiently aligned and there is no
- // gap between the field and the exception object. r276215 made a change to
- // annotate _Unwind_Exception in unwind.h with __attribute__((aligned)), but
- // we cannot incorporate the fix on Darwin since it is an ABI-breaking
- // change, which is why we need the attribute on this field.
- //
- // For ARM EHABI, we do not align this field since _Unwind_Exception is an
- // alias of _Unwind_Control_Block, which is not annotated with
- // __attribute__((aligned).
-#if defined(_LIBCXXABI_ARM_EHABI)
_Unwind_Exception unwindHeader;
-#else
- _Unwind_Exception unwindHeader __attribute__((aligned));
-#endif
};
// http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html
@@ -109,14 +94,7 @@
#if !defined(__LP64__) && !defined(_LIBCXXABI_ARM_EHABI)
void* primaryException;
#endif
-
- // See the comment in __cxa_exception as to why this field has attribute
- // aligned.
-#if defined(_LIBCXXABI_ARM_EHABI)
_Unwind_Exception unwindHeader;
-#else
- _Unwind_Exception unwindHeader __attribute__((aligned));
-#endif
};
struct _LIBCXXABI_HIDDEN __cxa_eh_globals {
diff --git a/src/private_typeinfo.cpp b/src/private_typeinfo.cpp
index 08e2b45..ef9466e 100644
--- a/src/private_typeinfo.cpp
+++ b/src/private_typeinfo.cpp
@@ -673,8 +673,8 @@
info.path_dynamic_ptr_to_static_ptr == unknown)
{
syslog(LOG_ERR, "dynamic_cast error 2: One or more of the following type_info's "
- " has hidden visibility. They should all have public visibility. "
- " %s, %s, %s.\n", static_type->name(), dynamic_type->name(),
+ "has hidden visibility. They should all have public visibility. "
+ "%s, %s, %s.\n", static_type->name(), dynamic_type->name(),
dst_type->name());
// Redo the search comparing type_info's using strcmp
info = {dst_type, static_ptr, static_type, src2dst_offset, 0};
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index c1819d7..5e51c44 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -24,10 +24,6 @@
"Executor to use when running tests.")
set(AUTO_GEN_COMMENT "## Autogenerated by libcxxabi configuration.\n# Do not edit!")
-configure_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
- @ONLY)
if (LIBCXXABI_ENABLE_SHARED)
set(LIBCXXABI_TEST_DEPS cxxabi_shared)
@@ -46,6 +42,11 @@
endif()
endif()
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+
add_lit_testsuite(check-cxxabi "Running libcxxabi tests"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${LIBCXXABI_TEST_DEPS}
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in
index 0e40a29..a66c9e4 100644
--- a/test/lit.site.cfg.in
+++ b/test/lit.site.cfg.in
@@ -20,6 +20,8 @@
config.host_triple = "@LLVM_HOST_TRIPLE@"
config.target_triple = "@TARGET_TRIPLE@"
config.use_target = len("@LIBCXXABI_TARGET_TRIPLE@") > 0
+config.sysroot = "@LIBCXXABI_SYSROOT@"
+config.gcc_toolchain = "@LIBCXXABI_GCC_TOOLCHAIN@"
config.cxx_ext_threads = "@LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY@"
# Let the main config do the real work.
diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp
index 18bbbba..b54f8d3 100644
--- a/test/test_demangle.pass.cpp
+++ b/test/test_demangle.pass.cpp
@@ -29585,7 +29585,7 @@
{"_Z1fPU11objcproto1A11objc_object", "f(id<A>)"},
{"_Z1fPKU11objcproto1A7NSArray", "f(NSArray<A> const*)"},
{"_ZNK1AIJ1Z1Y1XEEcv1BIJDpPT_EEIJS2_S1_S0_EEEv", "A<Z, Y, X>::operator B<X*, Y*, Z*><X, Y, Z>() const"},
- {"_ZNK3Ncr6Silver7Utility6detail12CallOnThreadIZ53-[DeploymentSetupController handleManualServerEntry:]E3$_5EclIJEEEDTclclL_ZNS2_4getTIS4_EERT_vEEspclsr3stdE7forwardIT_Efp_EEEDpOSA_", "decltype(-[DeploymentSetupController handleManualServerEntry:]::$_5& Ncr::Silver::Utility::detail::getT<-[DeploymentSetupController handleManualServerEntry:]::$_5>()()(std::forward<-[DeploymentSetupController handleManualServerEntry:]::$_5>(fp))) Ncr::Silver::Utility::detail::CallOnThread<-[DeploymentSetupController handleManualServerEntry:]::$_5>::operator()<>(-[DeploymentSetupController handleManualServerEntry:]::$_5&&) const"},
+ {"_ZNK3Ncr6Silver7Utility6detail12CallOnThreadIZ53-[DeploymentSetupController handleManualServerEntry:]E3$_5EclIJEEEDTclclL_ZNS2_4getTIS4_EERT_vEEspclsr3stdE7forwardIT_Efp_EEEDpOSA_", "decltype(-[DeploymentSetupController handleManualServerEntry:]::$_5& Ncr::Silver::Utility::detail::getT<-[DeploymentSetupController handleManualServerEntry:]::$_5>()()(std::forward<-[DeploymentSetupController handleManualServerEntry:]::$_5>(fp)...)) Ncr::Silver::Utility::detail::CallOnThread<-[DeploymentSetupController handleManualServerEntry:]::$_5>::operator()<>(-[DeploymentSetupController handleManualServerEntry:]::$_5&&...) const"},
{"_Zli2_xy", "operator\"\" _x(unsigned long long)"},
{"_Z1fIiEDcT_", "decltype(auto) f<int>(int)"},
{"_ZZ4testvEN1g3fooE5Point", "test()::g::foo(Point)"},
@@ -29604,7 +29604,118 @@
{"PFvRmOE", "void (*)(unsigned long&) &&"},
{"_ZTW1x", "thread-local wrapper routine for x"},
{"_ZTHN3fooE", "thread-local initialization routine for foo"},
- {"_Z4algoIJiiiEEvZ1gEUlT_E_", "void algo<int, int, int>(g::'lambda'(int, int, int))"},
+ {"_Z4algoIJiiiEEvZ1gEUlDpT_E_", "void algo<int, int, int>(g::'lambda'(int, int, int))"},
+ // attribute abi_tag
+ {"_Z1fB3foov", "f[abi:foo]()"},
+ {"_Z1fB3fooB3barv", "f[abi:foo][abi:bar]()"},
+ {"_ZN1SB5outer1fB5innerEv", "S[abi:outer]::f[abi:inner]()"},
+ {"_ZN1SC2B8ctor_tagEv", "S::S[abi:ctor_tag]()"},
+ {"_ZplB4MERP1SS_", "operator+[abi:MERP](S, S)"},
+
+ {"_Z1fIJifcEEvDp5unaryIT_E", "void f<int, float, char>(unary<int>, unary<float>, unary<char>)"},
+ {"_Z1fIJEJiEEvDpT_DpT0_", "void f<int>(int)"},
+ {"_Z1fIJicEEvDp7MuncherIAstT__S1_E", "void f<int, char>(Muncher<int [sizeof (int)]>, Muncher<char [sizeof (char)]>)"},
+ {"_ZN1SIJifcEE1fIJdjEEEiDp4MerpIJifcT_EE", "int S<int, float, char>::f<double, unsigned int>(Merp<int, float, char, double>, Merp<int, float, char, unsigned int>)"},
+ {"_Z1pIJicEEiDp4MerpIXsZT_EJT_EE", "int p<int, char>(Merp<sizeof...(int, char), int>, Merp<sizeof...(int, char), char>)"},
+
+ // Some expression symbols found in clang's test/CodeGenCXX/mangle-exprs.cpp
+ {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXleT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) <= (4), void>::type*)"},
+ {"_ZN5Casts6cstyleILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::cstyle<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"},
+ {"_ZN5Casts10functionalILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::functional<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"},
+ {"_ZN5Casts7static_ILj4EEEvPN9enable_ifIXleT_scjLi4EEvE4typeE", "void Casts::static_<4u>(enable_if<(4u) <= (static_cast<unsigned int>(4)), void>::type*)"},
+ {"_ZN5Casts12reinterpret_ILj4EiEEvPN9enable_ifIXleT_szrcPT0_Li0EEvE4typeE", "void Casts::reinterpret_<4u, int>(enable_if<(4u) <= (sizeof (reinterpret_cast<int*>(0))), void>::type*)"},
+ {"_ZN5Casts6const_IiXadL_ZNS_1iEEEEEvPN9enable_ifIXleLi0EszccPT_T0_EvE4typeE", "void Casts::const_<int, &(Casts::i)>(enable_if<(0) <= (sizeof (const_cast<int*>(&(Casts::i)))), void>::type*)"},
+ {"_ZN5Casts8dynamic_INS_1SEXadL_ZNS_1sEEEEEvPN9enable_ifIXleLi0EszdcPT_T0_EvE4typeE", "void Casts::dynamic_<Casts::S, &(Casts::s)>(enable_if<(0) <= (sizeof (dynamic_cast<Casts::S*>(&(Casts::s)))), void>::type*)"},
+ {"_ZN5Casts1fILi6EEENS_1TIXT_EEEv", "Casts::T<6> Casts::f<6>()"},
+ {"_ZN5Casts5auto_IiEEvDTnw_DapicvT__EEE", "void Casts::auto_<int>(decltype(new auto((int)())))"},
+ {"_ZN5Casts7scalar_IiEEvDTcmcvT__Ecvi_EE", "void Casts::scalar_<int>(decltype(((int)()) , ((int)())))"},
+ {"_ZN5test11aIsEEDTcl3foocvT__EEES1_", "decltype(foo((short)())) test1::a<short>(short)"},
+ {"_ZN5test21aIPFfvEEEvT_DTclfL0p_EE", "void test2::a<float (*)()>(float (*)(), decltype(fp()))"},
+ {"_ZN5test21bIPFfvEEEDTclfp_EET_", "decltype(fp()) test2::b<float (*)()>(float (*)())"},
+ {"_ZN5test21cIPFfvEEEvT_PFvDTclfL1p_EEE", "void test2::c<float (*)()>(float (*)(), void (*)(decltype(fp())))"},
+ {"_ZN5test21dIPFfvEEEvT_PFDTclfL0p_EEvE", "void test2::d<float (*)()>(float (*)(), decltype(fp()) (*)())"},
+ {"_ZN5test21eIPFfvEEEvPFDTclfp_EET_E", "void test2::e<float (*)()>(decltype(fp()) (*)(float (*)()))"},
+ {"_ZN5test21fIPFfvEEEvPFvT_DTclfL0p_EEE", "void test2::f<float (*)()>(void (*)(float (*)(), decltype(fp())))"},
+ {"_ZN5test21gIPFfvEEEvT_DTclfL0p_EE", "void test2::g<float (*)()>(float (*)(), decltype(fp()))"},
+ {"_ZN5test21hIPFfvEEEvT_DTcvPFDTclfL0p_EEvELi0EE", "void test2::h<float (*)()>(float (*)(), decltype((decltype(fp()) (*)())(0)))"},
+ {"_ZN5test21iIPFfvEEEvDTcvPFDTclfp_EET_ELi0EE", "void test2::i<float (*)()>(decltype((decltype(fp()) (*)(float (*)()))(0)))"},
+ {"_ZZN5test21gIPFfvEEEvT_DTclfL0p_EEE8variable", "void test2::g<float (*)()>(float (*)(), decltype(fp()))::variable"},
+ {"_ZN5test31aINS_1XEMS1_PiEEvT_T0_DTdsfL0p_fL0p0_E", "void test3::a<test3::X, int* test3::X::*>(test3::X, int* test3::X::*, decltype(fp.*fp0))"},
+ {"_ZN5test43tf1INS_1XEEEvDTnw_T_piLi1EEE", "void test4::tf1<test4::X>(decltype(new test4::X(1)))"},
+ {"_ZN5test51aIiEEvDTnxcvT__EE", "void test5::a<int>(decltype(noexcept ((int)())))"},
+ {"_ZN5test62f1IiEEvDTcvT_dtdtL_ZNS_1zEE2ua1iE", "void test6::f1<int>(decltype((int)(test6::z.ua.i)))"},
+ {"_ZN5test62f2IiEEvDTcvT_dtdtL_ZNS_1zEE2ub1iE", "void test6::f2<int>(decltype((int)(test6::z.ub.i)))"},
+ {"_ZN5test62f3IiEEvDTcvT_dtdtL_ZNS_1zEE1s1iE", "void test6::f3<int>(decltype((int)(test6::z.s.i)))"},
+ {"_ZN5test62f4IiEEvDTcvT_dtdtL_ZNS_1zEE4uuss1iE", "void test6::f4<int>(decltype((int)(test6::z.uuss.i)))"},
+ {"_ZN5test62f5IiEEvDTcvT_dtptL_ZNS_2zpEE2ua1iE", "void test6::f5<int>(decltype((int)(test6::zp->ua.i)))"},
+ {"_ZN5test62f6IiEEvDTcvT_dtptL_ZNS_2zpEE2ub1iE", "void test6::f6<int>(decltype((int)(test6::zp->ub.i)))"},
+ {"_ZN5test62f7IiEEvDTcvT_dtptL_ZNS_2zpEE1s1iE", "void test6::f7<int>(decltype((int)(test6::zp->s.i)))"},
+ {"_ZN5test62f8IiEEvDTcvT_dtptL_ZNS_2zpEE4uuss1iE", "void test6::f8<int>(decltype((int)(test6::zp->uuss.i)))"},
+ {"_ZN5test73fD2IiEEDTcmcvNS_1DEL_ZNS_1bEEcvT__EES2_", "decltype(((test7::D)(test7::b)) , ((int)())) test7::fD2<int>(int)"},
+ {"_ZN5test73fT2IiEEDTcvT__EES1_", "decltype((int)()) test7::fT2<int>(int)"},
+ {"_ZN5test73fT4IiEEDTcvT_Li1EES1_", "decltype((int)(1)) test7::fT4<int>(int)"},
+ {"_ZN5test73fT6INS_1BEEEDTcvT__Li1ELi2EEES2_", "decltype((test7::B)(1, 2)) test7::fT6<test7::B>(test7::B)"},
+ {"_ZNK5test81XIiE3barIiEEDTcl3fooIT_EEEv", "decltype(foo<int>()) test8::X<int>::bar<int>() const"},
+
+ // Multiple qualifiers on the same type should all get the same entry in the substitution table.
+ {"_Z1fPU3AS1KiS0_", "f(int const AS1*, int const AS1*)"},
+
+ {"_ZN6test471fINS_1SEEEvPTsNT_1cE", "void test47::f<test47::S>(struct test47::S::c*)"},
+ {"_ZN6test481fINS_1SEEEvPTuNT_1uE", "void test48::f<test48::S>(union test48::S::u*)"},
+ {"_ZN6test451fINS_1SEEEvPTeNT_1eE", "void test45::f<test45::S>(enum test45::S::e*)"},
+
+ // Initializer list expressions
+ {"_ZN5test43tf2INS_1XEEEvDTnw_T_piilLi1EEEE", "void test4::tf2<test4::X>(decltype(new test4::X({1})))"},
+ {"_ZN5test73fA1IiEEDTcmtlNS_1AELi1ELi2EEcvT__EES2_", "decltype((test7::A{1, 2}) , ((int)())) test7::fA1<int>(int)"},
+ {"_ZN5test73fA2IiEEDTcmcvNS_1AEilLi1ELi2EEcvT__EES2_", "decltype(((test7::A)({1, 2})) , ((int)())) test7::fA2<int>(int)"},
+ {"_ZN5test73fB1IiEEDTcmtlNS_1BELi1ELi2EEcvT__EES2_", "decltype((test7::B{1, 2}) , ((int)())) test7::fB1<int>(int)"},
+ {"_ZN5test73fB2IiEEDTcmcvNS_1BEilLi1ELi2EEcvT__EES2_", "decltype(((test7::B)({1, 2})) , ((int)())) test7::fB2<int>(int)"},
+ {"_ZN5test73fC1IiEEDTcmtlNS_1CEilLi1ELi2EEEcvT__EES2_", "decltype((test7::C{{1, 2}}) , ((int)())) test7::fC1<int>(int)"},
+ {"_ZN5test73fC2IiEEDTcmcvNS_1CEilLi1ELi2EEcvT__EES2_", "decltype(((test7::C)({1, 2})) , ((int)())) test7::fC2<int>(int)"},
+ {"_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", "decltype((test7::D{test7::b}) , ((int)())) test7::fD1<int>(int)"},
+ {"_ZN5test73fE1IiEEDTcmtlNS_1EELi1ELi2EEcvT__EES2_", "decltype((test7::E{1, 2}) , ((int)())) test7::fE1<int>(int)"},
+ {"_ZN5test73fE2IiEEDTcmcvNS_1EEilLi1ELi2EEcvT__EES2_", "decltype(((test7::E)({1, 2})) , ((int)())) test7::fE2<int>(int)"},
+ {"_ZN5test73fF1IiEEDTcmtlNS_1FEilLi1ELi2EEEcvT__EES2_", "decltype((test7::F{{1, 2}}) , ((int)())) test7::fF1<int>(int)"},
+ {"_ZN5test73fF2IiEEDTcmcvNS_1FEilLi1ELi2EEcvT__EES2_", "decltype(((test7::F)({1, 2})) , ((int)())) test7::fF2<int>(int)"},
+ {"_ZN5test73fT1IiEEDTtlT_EES1_", "decltype(int{}) test7::fT1<int>(int)"},
+ {"_ZN5test73fT3IiEEDTtlT_Li1EEES1_", "decltype(int{1}) test7::fT3<int>(int)"},
+ {"_ZN5test73fT5INS_1BEEEDTtlT_Li1ELi2EEES2_", "decltype(test7::B{1, 2}) test7::fT5<test7::B>(test7::B)"},
+ {"_ZN5test73fT7INS_1AEEEDTtlT_ilEEES2_", "decltype(test7::A{{}}) test7::fT7<test7::A>(test7::A)"},
+ {"_ZN5test73fT8INS_1AEEEDTcvT_ilEES2_", "decltype((test7::A)({})) test7::fT8<test7::A>(test7::A)"},
+ {"_ZN5test73fT9INS_1AEEEDTtlT_ilLi1EEEES2_", "decltype(test7::A{{1}}) test7::fT9<test7::A>(test7::A)"},
+ {"_ZN5test73fTAINS_1AEEEDTcvT_ilLi1EEES2_", "decltype((test7::A)({1})) test7::fTA<test7::A>(test7::A)"},
+ {"_ZN5test73fTBINS_1CEEEDTtlT_ilLi1ELi2EEEES2_", "decltype(test7::C{{1, 2}}) test7::fTB<test7::C>(test7::C)"},
+ {"_ZN5test73fTCINS_1CEEEDTcvT_ilLi1ELi2EEES2_", "decltype((test7::C)({1, 2})) test7::fTC<test7::C>(test7::C)"},
+
+ // Designated init expressions
+ {"_ZN15designated_init1fINS_1AEEEvDTtlT_di1adi1bdxLi3EdXLi1ELi4ELi9EEE", "void designated_init::f<designated_init::A>(decltype(designated_init::A{.a.b[3][1 ... 4] = 9}))"},
+ {"_Z1fIXtl1Xdi1adi1bdxLi3ELi1EEEE", "f<X{.a.b[3] = 1}>"},
+
+ // Inheriting constructors:
+ {"_ZN1BCI21AEi", "B::B(int)"},
+ {"_ZN1DCI21CIiEET_", "D::D(int)"},
+
+ // Exception specifiers:
+ {"_Z1bPDoFivE", "b(int (*)() noexcept)"},
+ {"_Z1fILb0EEvPDOT_EFivE", "void f<false>(int (*)() noexcept(false))"},
+ {"_Z1fILb1EEvPDOT_EFivE", "void f<true>(int (*)() noexcept(true))"},
+ {"_Z1gIJEEvPDwDpT_EFivE", "void g<>(int (*)() throw())"},
+ {"_Z1gIJfEEvPDwDpT_EFivE", "void g<float>(int (*)() throw(float))"},
+ {"_Z1hIJEEPDwDpT_iEFivEPDwiS1_EFivE", "int (*h<>(int (*)() throw(int)))() throw(int)"},
+ {"_Z1hIJfEEPDwDpT_iEFivEPDwiS1_EFivE", "int (*h<float>(int (*)() throw(int, float)))() throw(float, int)"},
+ {"_Z1iIJEEPDwiDpT_EFivEPS2_", "int (*i<>(int (*)() throw(int)))() throw(int)"},
+ {"_Z1iIJEEPDwiDpT_EFivES3_", "int (*i<>(int (*)() throw(int)))() throw(int)"},
+ {"_Z1iIJfEEPDwiDpT_EFivEPS2_", "int (*i<float>(int (*)() throw(int, float)))() throw(int, float)"},
+ {"_Z1iIJfEEPDwiDpT_EFivES3_", "int (*i<float>(int (*)() throw(int, float)))() throw(int, float)"},
+ {"_Z1pILb1EEiM1SKDOT_EFivRE", "int p<true>(int (S::*)() const & noexcept(true))"},
+ {"_Z1pIJicfEEiM1SVKDwDpT_EFivOE", "int p<int, char, float>(int (S::*)() const volatile && throw(int, char, float))"},
+ {"_Z1pM1SDoFivE", "p(int (S::*)() noexcept)"},
+
+ {"_ZNKR4llvm8OptionalINS_11MCFixupKindEEdeEv", "llvm::Optional<llvm::MCFixupKind>::operator*() const &"},
+ {"_ZZL23isValidCoroutineContextRN5clang4SemaENS_14SourceLocationEN4llvm9StringRefEENK3$_4clEZL23isValidCoroutineContextS1_S2_S4_E15InvalidFuncDiag", "isValidCoroutineContext(clang::Sema&, clang::SourceLocation, llvm::StringRef)::$_4::operator()(isValidCoroutineContext(clang::Sema&, clang::SourceLocation, llvm::StringRef)::InvalidFuncDiag) const"},
+
+ // ABI tags can apply to built-in substitutions.
+ {"_Z1fSsB1XS_", "f(std::string[abi:X], std::string[abi:X])"},
};
const unsigned N = sizeof(cases) / sizeof(cases[0]);
@@ -29712,7 +29823,7 @@
free(buf);
}
-void test2()
+void test_invalid_cases()
{
std::size_t len = 0;
char* buf = nullptr;
@@ -29733,6 +29844,39 @@
free(buf);
}
+const char *xfail_cases[] = {
+ "_Z1fUa9enable_ifIXLi1EEEv", // enable_if attribute
+ "_ZDC2a12a2E", // decomposition decl
+ "_ZW6FooBarE2f3v", // C++ modules TS
+
+ // FIXME: Why does clang generate the "cp" expr?
+ "_ZN5test11bIsEEDTcp3foocvT__EEES1_",
+};
+
+const size_t num_xfails = sizeof(xfail_cases) / sizeof(xfail_cases[0]);
+
+void test_xfail_cases()
+{
+ std::size_t len = 0;
+ char* buf = nullptr;
+ for (std::size_t i = 0; i < num_xfails; ++i)
+ {
+ int status;
+ char* demang = __cxxabiv1::__cxa_demangle(xfail_cases[i], buf, &len, &status);
+ if (status != -2)
+ {
+ std::cout << xfail_cases[i] << " was documented as xfail but passed\n"
+ << "got status = " << status << '\n';
+ assert(status == -2);
+ }
+ else
+ {
+ buf = demang;
+ }
+ }
+ free(buf);
+}
+
void testFPLiterals()
{
std::size_t len = 0;
@@ -29769,7 +29913,8 @@
{
timer t;
test();
- test2();
+ test_invalid_cases();
+ test_xfail_cases();
testFPLiterals();
}
#if 0
diff --git a/test/unittest_demangle.pass.cpp b/test/unittest_demangle.pass.cpp
index 0b4505e..3ec869c 100644
--- a/test/unittest_demangle.pass.cpp
+++ b/test/unittest_demangle.pass.cpp
@@ -82,44 +82,6 @@
}
}
-void testSubstitutionTable() {
- {
- SubstitutionTable<2> Tab;
-
- NameType Names[] = {{"MERP"}, {"MARP"}, {"MAMP"}};
- Tab.pushPack();
- Tab.pushSubstitutionIntoPack(&Names[0]);
- Tab.pushSubstitutionIntoPack(&Names[1]);
- Tab.pushSubstitutionIntoPack(&Names[2]);
-
- int Index = 0;
- for (Node* N : Tab.nthSubstitution(0)) {
- assert(static_cast<NameType*>(N)->getName() == Names[Index].getName());
- ++Index;
- }
- assert(Index == 3);
-
- Tab.popPack();
- assert(Tab.empty() && Tab.size() == 0);
- Tab.pushSubstitution(&Names[0]);
- Tab.pushSubstitution(&Names[1]);
- assert(!Tab.empty() && Tab.size() == 2);
-
- int I = 0;
- for (Node* N : Tab.nthSubstitution(0)) {
- assert(static_cast<NameType*>(N)->getName() == "MERP");
- assert(I == 0);
- ++I;
- }
- for (Node* N : Tab.nthSubstitution(1)) {
- assert(static_cast<NameType*>(N)->getName() == "MARP");
- assert(I == 1);
- ++I;
- }
- }
-}
-
int main() {
testPODSmallVector();
- testSubstitutionTable();
}