diff options
Diffstat (limited to 'lib/libcxxabi/src')
-rw-r--r-- | lib/libcxxabi/src/CMakeLists.txt | 42 | ||||
-rw-r--r-- | lib/libcxxabi/src/cxa_default_handlers.cpp | 19 | ||||
-rw-r--r-- | lib/libcxxabi/src/cxa_demangle.cpp | 8246 | ||||
-rw-r--r-- | lib/libcxxabi/src/cxa_exception.cpp | 9 | ||||
-rw-r--r-- | lib/libcxxabi/src/cxa_handlers.cpp | 22 | ||||
-rw-r--r-- | lib/libcxxabi/src/cxa_personality.cpp | 25 | ||||
-rw-r--r-- | lib/libcxxabi/src/demangle/Compiler.h | 34 | ||||
-rw-r--r-- | lib/libcxxabi/src/demangle/StringView.h | 98 | ||||
-rw-r--r-- | lib/libcxxabi/src/demangle/Utility.h | 191 | ||||
-rw-r--r-- | lib/libcxxabi/src/fallback_malloc.cpp | 3 | ||||
-rw-r--r-- | lib/libcxxabi/src/include/atomic_support.h | 181 | ||||
-rw-r--r-- | lib/libcxxabi/src/include/refstring.h | 9 | ||||
-rw-r--r-- | lib/libcxxabi/src/private_typeinfo.cpp | 69 | ||||
-rw-r--r-- | lib/libcxxabi/src/stdlib_exception.cpp | 1 | ||||
-rw-r--r-- | lib/libcxxabi/src/stdlib_new_delete.cpp | 1 |
15 files changed, 4128 insertions, 4822 deletions
diff --git a/lib/libcxxabi/src/CMakeLists.txt b/lib/libcxxabi/src/CMakeLists.txt index 240f6d0d103..776c5129433 100644 --- a/lib/libcxxabi/src/CMakeLists.txt +++ b/lib/libcxxabi/src/CMakeLists.txt @@ -61,12 +61,19 @@ if (LIBCXXABI_USE_LLVM_UNWINDER) # Prefer using the in-tree version of libunwind, either shared or static. If # none are found fall back to using -lunwind. # FIXME: Is it correct to prefer the static version of libunwind? - if (NOT LIBCXXABI_ENABLE_STATIC_UNWINDER AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) - list(APPEND LIBCXXABI_LIBRARIES unwind_shared) - elseif (LIBCXXABI_ENABLE_STATIC_UNWINDER AND (TARGET unwind_static OR HAVE_LIBUNWIND)) - list(APPEND LIBCXXABI_LIBRARIES unwind_static) + if (NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind_shared) + elseif (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_static OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind_static) else() - list(APPEND LIBCXXABI_LIBRARIES unwind) + list(APPEND LIBCXXABI_SHARED_LIBRARIES unwind) + endif() + if (NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) + list(APPEND LIBCXXABI_STATIC_LIBRARIES unwind_shared) + elseif (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY AND (TARGET unwind_static OR HAVE_LIBUNWIND)) + # We handle this by directly merging libunwind objects into libc++abi. + else() + list(APPEND LIBCXXABI_STATIC_LIBRARIES unwind) endif() else() add_library_flags_if(LIBCXXABI_HAS_GCC_S_LIB gcc_s) @@ -122,12 +129,13 @@ set_target_properties(cxxabi_objects POSITION_INDEPENDENT_CODE ON) -set(LIBCXXABI_TARGETS) - # Build the shared library. if (LIBCXXABI_ENABLE_SHARED) add_library(cxxabi_shared SHARED $<TARGET_OBJECTS:cxxabi_objects>) - target_link_libraries(cxxabi_shared ${LIBCXXABI_LIBRARIES}) + if(COMMAND llvm_setup_rpath) + llvm_setup_rpath(cxxabi_shared) + endif() + target_link_libraries(cxxabi_shared ${LIBCXXABI_LIBRARIES} ${LIBCXXABI_SHARED_LIBRARIES}) set_target_properties(cxxabi_shared PROPERTIES CXX_EXTENSIONS @@ -146,19 +154,22 @@ if (LIBCXXABI_ENABLE_SHARED) "1" VERSION "1.0") - list(APPEND LIBCXXABI_TARGETS "cxxabi_shared") + list(APPEND LIBCXXABI_BUILD_TARGETS "cxxabi_shared") + if (LIBCXXABI_INSTALL_SHARED_LIBRARY) + list(APPEND LIBCXXABI_INSTALL_TARGETS "cxxabi_shared") + endif() endif() # Build the static library. if (LIBCXXABI_ENABLE_STATIC) set(cxxabi_static_sources $<TARGET_OBJECTS:cxxabi_objects>) - if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_ENABLE_STATIC_UNWINDER) + if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY) 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}) + target_link_libraries(cxxabi_static ${LIBCXXABI_LIBRARIES} ${LIBCXXABI_STATIC_LIBRARIES}) set_target_properties(cxxabi_static PROPERTIES CXX_EXTENSIONS @@ -173,14 +184,17 @@ if (LIBCXXABI_ENABLE_STATIC) "c++abi" POSITION_INDEPENDENT_CODE ON) - list(APPEND LIBCXXABI_TARGETS "cxxabi_static") + list(APPEND LIBCXXABI_BUILD_TARGETS "cxxabi_static") + if (LIBCXXABI_INSTALL_STATIC_LIBRARY) + list(APPEND LIBCXXABI_INSTALL_TARGETS "cxxabi_static") + endif() endif() # Add a meta-target for both libraries. -add_custom_target(cxxabi DEPENDS ${LIBCXXABI_TARGETS}) +add_custom_target(cxxabi DEPENDS ${LIBCXXABI_BUILD_TARGETS}) if (LIBCXXABI_INSTALL_LIBRARY) - install(TARGETS ${LIBCXXABI_TARGETS} + install(TARGETS ${LIBCXXABI_INSTALL_TARGETS} LIBRARY DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi ARCHIVE DESTINATION ${LIBCXXABI_INSTALL_PREFIX}lib${LIBCXXABI_LIBDIR_SUFFIX} COMPONENT cxxabi ) diff --git a/lib/libcxxabi/src/cxa_default_handlers.cpp b/lib/libcxxabi/src/cxa_default_handlers.cpp index f2e36b4bc83..0fa169f0640 100644 --- a/lib/libcxxabi/src/cxa_default_handlers.cpp +++ b/lib/libcxxabi/src/cxa_default_handlers.cpp @@ -18,7 +18,9 @@ #include "cxa_handlers.hpp" #include "cxa_exception.hpp" #include "private_typeinfo.h" +#include "include/atomic_support.h" +#if !defined(LIBCXXABI_SILENT_TERMINATE) static const char* cause = "uncaught"; __attribute__((noreturn)) @@ -84,7 +86,6 @@ static void demangling_unexpected_handler() 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 @@ -101,10 +102,6 @@ std::terminate_handler __cxa_terminate_handler = default_terminate_handler; _LIBCXXABI_DATA_VIS std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler; -// In the future these will become: -// std::atomic<std::terminate_handler> __cxa_terminate_handler(default_terminate_handler); -// std::atomic<std::unexpected_handler> __cxa_unexpected_handler(default_unexpected_handler); - namespace std { @@ -113,10 +110,8 @@ set_unexpected(unexpected_handler func) _NOEXCEPT { if (func == 0) func = default_unexpected_handler; - return __atomic_exchange_n(&__cxa_unexpected_handler, func, - __ATOMIC_ACQ_REL); -// Using of C++11 atomics this should be rewritten -// return __cxa_unexpected_handler.exchange(func, memory_order_acq_rel); + return __libcpp_atomic_exchange(&__cxa_unexpected_handler, func, + _AO_Acq_Rel); } terminate_handler @@ -124,10 +119,8 @@ set_terminate(terminate_handler func) _NOEXCEPT { if (func == 0) func = default_terminate_handler; - return __atomic_exchange_n(&__cxa_terminate_handler, func, - __ATOMIC_ACQ_REL); -// Using of C++11 atomics this should be rewritten -// return __cxa_terminate_handler.exchange(func, memory_order_acq_rel); + return __libcpp_atomic_exchange(&__cxa_terminate_handler, func, + _AO_Acq_Rel); } } diff --git a/lib/libcxxabi/src/cxa_demangle.cpp b/lib/libcxxabi/src/cxa_demangle.cpp index 9cfd99607bf..1007d7efaef 100644 --- a/lib/libcxxabi/src/cxa_demangle.cpp +++ b/lib/libcxxabi/src/cxa_demangle.cpp @@ -9,168 +9,32 @@ // FIXME: (possibly) incomplete list of features that clang mangles that this // file does not yet support: -// - enable_if attribute -// - decomposition declarations // - C++ modules TS #define _LIBCPP_NO_EXCEPTIONS #include "__cxxabi_config.h" -#include <vector> -#include <algorithm> -#include <numeric> +#include "demangle/Compiler.h" +#include "demangle/StringView.h" +#include "demangle/Utility.h" + +#include <cassert> +#include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> -#include <cctype> - -#ifdef _MSC_VER -// snprintf is implemented in VS 2015 -#if _MSC_VER < 1900 -#define snprintf _snprintf_s -#endif -#endif - -namespace __cxxabiv1 -{ - -namespace -{ - -enum -{ - unknown_error = -4, - invalid_args = -3, - invalid_mangled_name, - memory_alloc_failure, - success -}; - -class StringView { - const char *First; - const char *Last; - -public: - template <size_t N> - StringView(const char (&Str)[N]) : First(Str), Last(Str + N - 1) {} - StringView(const char *First_, const char *Last_) : First(First_), Last(Last_) {} - StringView() : First(nullptr), Last(nullptr) {} - - StringView substr(size_t From, size_t To) { - if (To >= size()) - To = size() - 1; - if (From >= size()) - From = size() - 1; - return StringView(First + From, First + To); - } - - StringView dropFront(size_t N) const { - if (N >= size()) - N = size() - 1; - return StringView(First + N, Last); - } - - bool startsWith(StringView Str) const { - if (Str.size() > size()) - return false; - return std::equal(Str.begin(), Str.end(), begin()); - } - - const char &operator[](size_t Idx) const { return *(begin() + Idx); } - - const char *begin() const { return First; } - const char *end() const { return Last; } - size_t size() const { return static_cast<size_t>(Last - First); } -}; - -bool operator==(const StringView &LHS, const StringView &RHS) { - return LHS.size() == RHS.size() && - std::equal(LHS.begin(), LHS.end(), RHS.begin()); -} - -// Stream that AST nodes write their string representation into after the AST -// has been parsed. -class OutputStream { - char *Buffer; - size_t CurrentPosition; - size_t BufferCapacity; - - // Ensure there is at least n more positions in buffer. - void grow(size_t N) { - if (N + CurrentPosition >= BufferCapacity) { - BufferCapacity *= 2; - if (BufferCapacity < N + CurrentPosition) - BufferCapacity = N + CurrentPosition; - Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity)); - } - } - -public: - OutputStream(char *StartBuf, size_t Size) - : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} - - OutputStream &operator+=(StringView R) { - size_t Size = R.size(); - if (Size == 0) - return *this; - grow(Size); - memmove(Buffer + CurrentPosition, R.begin(), Size); - CurrentPosition += Size; - return *this; - } - - OutputStream &operator+=(char C) { - grow(1); - Buffer[CurrentPosition++] = C; - 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()); - } - - char back() const { - return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0'; - } - - bool empty() const { return CurrentPosition == 0; } +#include <numeric> +#include <utility> +#include <vector> - char *getBuffer() { return Buffer; } - char *getBufferEnd() { return Buffer + CurrentPosition - 1; } - size_t getBufferCapacity() { return BufferCapacity; } +namespace { +enum : int { + demangle_unknown_error = -4, + demangle_invalid_args = -3, + demangle_invalid_mangled_name = -2, + demangle_memory_alloc_failure = -1, + demangle_success = 0, }; // Base class of all AST nodes. The AST is built by the parser, then is @@ -178,30 +42,37 @@ public: class Node { public: enum Kind : unsigned char { + KNodeArrayNode, KDotSuffix, KVendorExtQualType, KQualType, KConversionOperatorType, KPostfixQualifiedType, + KElaboratedTypeSpefType, KNameType, KAbiTagAttr, + KEnableIfAttr, KObjCProtoName, KPointerType, - KLValueReferenceType, - KRValueReferenceType, + KReferenceType, KPointerToMemberType, KArrayType, KFunctionType, - KTopLevelFunctionDecl, - KFunctionQualType, - KFunctionRefQualType, + KNoexceptSpec, + KDynamicExceptionSpec, + KFunctionEncoding, KLiteralOperator, KSpecialName, KCtorVtableSpecialName, KQualifiedName, - KEmptyName, + KNestedName, + KLocalName, KVectorType, - KTemplateParams, + KParameterPack, + KTemplateArgumentPack, + KParameterPackExpansion, + KTemplateArgs, + KForwardTemplateReference, KNameWithTemplateArgs, KGlobalQualifiedName, KStdQualifiedName, @@ -210,32 +81,70 @@ public: KCtorDtorName, KDtorName, KUnnamedTypeName, - KLambdaTypeName, + KClosureTypeName, + KStructuredBindingName, KExpr, + KBracedExpr, + KBracedRangeExpr, }; - const Kind K; + Kind K; -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; + /// 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, }; -public: - Node(Kind K_, bool HasRHS_ = false, bool HasFunction_ = false, - bool HasArray_ = false) - : K(K_), HasRHSComponent(HasRHS_), HasFunction(HasFunction_), - HasArray(HasArray_) {} + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache; + + /// 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_, Cache RHSComponentCache_ = Cache::No, + Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) + : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), + FunctionCache(FunctionCache_) {} - bool hasRHSComponent() const { return HasRHSComponent; } - bool hasArray() const { return HasArray; } - bool hasFunction() const { return HasFunction; } + 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); + } - void print(OutputStream &s) const { - printLeft(s); - if (hasRHSComponent()) - printRight(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; } + + // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to + // get at a node that actually represents some concrete syntax. + virtual const Node *getSyntaxNode(OutputStream &) const { + return this; + } + + void print(OutputStream &S) const { + printLeft(S); + if (RHSComponentCache != Cache::No) + printRight(S); } // Print the "left" side of this Node into OutputStream. @@ -244,13 +153,24 @@ public: // Print the "right". This distinction is necessary to represent C++ types // that appear on the RHS of their subtype, such as arrays or functions. // Since most types don't have such a component, provide a default - // implemenation. + // implementation. virtual void printRight(OutputStream &) const {} virtual StringView getBaseName() const { return StringView(); } // 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 { @@ -258,22 +178,47 @@ class NodeArray { 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; + size_t BeforeComma = S.getCurrentPosition(); + if (!FirstElement) + S += ", "; + size_t AfterComma = S.getCurrentPosition(); Elements[Idx]->print(S); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == S.getCurrentPosition()) { + S.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; } } }; +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + void printLeft(OutputStream &S) const override { + Array.printWithComma(S); + } +}; + class DotSuffix final : public Node { const Node *Prefix; const StringView Suffix; @@ -291,20 +236,24 @@ public: }; 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(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 { @@ -334,14 +283,19 @@ protected: public: QualType(Node *Child_, Qualifiers Quals_) - : Node(KQualType, Child_->hasRHSComponent(), Child_->hasFunction(), - Child_->hasArray()), + : Node(KQualType, 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); @@ -355,7 +309,8 @@ class ConversionOperatorType final : public Node { const Node *Ty; public: - ConversionOperatorType(Node *Ty_) : Node(KConversionOperatorType), Ty(Ty_) {} + ConversionOperatorType(Node *Ty_) + : Node(KConversionOperatorType), Ty(Ty_) {} void printLeft(OutputStream &S) const override { S += "operator "; @@ -375,8 +330,6 @@ public: Ty->printLeft(s); s += Postfix; } - - void printRight(OutputStream &S) const override { Ty->printRight(S); } }; class NameType final : public Node { @@ -391,12 +344,28 @@ public: void printLeft(OutputStream &s) const override { s += Name; } }; -class AbiTagAttr final : public Node { - const Node* Base; - StringView Tag; +class ElaboratedTypeSpefType : public Node { + StringView Kind; + Node *Child; public: - AbiTagAttr(const Node* Base_, StringView Tag_) - : Node(KAbiTagAttr), Base(Base_), Tag(Tag_) {} + ElaboratedTypeSpefType(StringView Kind_, Node *Child_) + : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} + + void printLeft(OutputStream &S) const override { + S += Kind; + S += ' '; + Child->print(S); + } +}; + +struct AbiTagAttr : Node { + Node *Base; + StringView Tag; + + AbiTagAttr(Node* Base_, StringView Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, + Base_->ArrayCache, Base_->FunctionCache), + Base(Base_), Tag(Tag_) {} void printLeft(OutputStream &S) const override { Base->printLeft(S); @@ -406,25 +375,38 @@ public: } }; +class EnableIfAttr : public Node { + NodeArray Conditions; +public: + EnableIfAttr(NodeArray Conditions_) + : Node(KEnableIfAttr), Conditions(Conditions_) {} + + void printLeft(OutputStream &S) const override { + S += " [enable_if:"; + Conditions.printWithComma(S); + 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 += ">"; } }; @@ -434,82 +416,99 @@ class PointerType final : public Node { public: PointerType(Node *Pointee_) - : Node(KPointerType, Pointee_->hasRHSComponent()), Pointee(Pointee_) {} + : Node(KPointerType, 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); } } }; -class LValueReferenceType final : public Node { - const Node *Pointee; - -public: - LValueReferenceType(Node *Pointee_) - : Node(KLValueReferenceType, Pointee_->hasRHSComponent()), - Pointee(Pointee_) {} - - void printLeft(OutputStream &s) const override { - Pointee->printLeft(s); - if (Pointee->hasArray()) - s += " "; - if (Pointee->hasArray() || Pointee->hasFunction()) - s += "(&"; - else - s += "&"; - } - void printRight(OutputStream &s) const override { - if (Pointee->hasArray() || Pointee->hasFunction()) - s += ")"; - Pointee->printRight(s); - } +enum class ReferenceKind { + LValue, + RValue, }; -class RValueReferenceType final : public Node { +// Represents either a LValue or an RValue reference type. +class ReferenceType : public Node { const Node *Pointee; + ReferenceKind RK; + + mutable bool Printing = false; + + // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The + // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any + // other combination collapses to a lvalue ref. + std::pair<ReferenceKind, const Node *> collapse(OutputStream &S) const { + auto SoFar = std::make_pair(RK, Pointee); + for (;;) { + const Node *SN = SoFar.second->getSyntaxNode(S); + if (SN->getKind() != KReferenceType) + break; + auto *RT = static_cast<const ReferenceType *>(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + } + return SoFar; + } public: - RValueReferenceType(Node *Pointee_) - : Node(KRValueReferenceType, Pointee_->hasRHSComponent()), - Pointee(Pointee_) {} + ReferenceType(Node *Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), RK(RK_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + return Pointee->hasRHSComponent(S); + } void printLeft(OutputStream &s) const override { - Pointee->printLeft(s); - if (Pointee->hasArray()) + if (Printing) + return; + SwapAndRestore<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(s); + Collapsed.second->printLeft(s); + if (Collapsed.second->hasArray(s)) s += " "; - if (Pointee->hasArray() || Pointee->hasFunction()) - s += "(&&"; - else - s += "&&"; - } + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) + s += "("; + s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } void printRight(OutputStream &s) const override { - if (Pointee->hasArray() || Pointee->hasFunction()) + if (Printing) + return; + SwapAndRestore<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(s); + if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) s += ")"; - Pointee->printRight(s); + Collapsed.second->printRight(s); } }; @@ -519,12 +518,16 @@ class PointerToMemberType final : public Node { public: PointerToMemberType(Node *ClassType_, Node *MemberType_) - : Node(KPointerToMemberType, MemberType_->hasRHSComponent()), + : Node(KPointerToMemberType, 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 += " "; @@ -533,7 +536,7 @@ public: } void printRight(OutputStream &s) const override { - if (MemberType->hasArray() || MemberType->hasFunction()) + if (MemberType->hasArray(s) || MemberType->hasFunction(s)) s += ")"; MemberType->printRight(s); } @@ -581,10 +584,20 @@ class ArrayType final : public Node { public: ArrayType(Node *Base_, NodeOrString Dimension_) - : Node(KArrayType, true, false, true), Base(Base_), Dimension(Dimension_) {} + : Node(KArrayType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes), + Base(Base_), Dimension(Dimension_) {} // Incomplete array type. - ArrayType(Node *Base_) : Node(KArrayType, true, false, true), Base(Base_) {} + ArrayType(Node *Base_) + : Node(KArrayType, + /*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); } @@ -604,12 +617,23 @@ public: 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_) {} - - // Handle C++'s ... quirky decl grammer by using the left & right + FunctionType(Node *Ret_, NodeArray Params_, Qualifiers CVQuals_, + FunctionRefQual RefQual_, Node *ExceptionSpec_) + : Node(KFunctionType, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_), + ExceptionSpec(ExceptionSpec_) {} + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + // Handle C++'s ... quirky decl grammar by using the left & right // distinction. Consider: // int (*f(float))(char) {} // f is a function that takes a float and returns a pointer to a function @@ -623,26 +647,85 @@ public: 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 { - const Node *Ret; - const Node *Name; +class NoexceptSpec : public Node { + Node *E; +public: + NoexceptSpec(Node *E_) : Node(KNoexceptSpec), 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_) {} + + void printLeft(OutputStream &S) const override { + S += "throw("; + Types.printWithComma(S); + S += ')'; + } +}; + +class FunctionEncoding final : public Node { + Node *Ret; + Node *Name; NodeArray Params; + Node *Attrs; + 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_, + Node *Attrs_, Qualifiers CVQuals_, FunctionRefQual RefQual_) + : Node(KFunctionEncoding, + /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes), + Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_), + CVQuals(CVQuals_), RefQual(RefQual_) {} + + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + Node *getReturnType() const { return Ret; } + + bool hasRHSComponentSlow(OutputStream &) const override { return true; } + bool hasFunctionSlow(OutputStream &) const override { return true; } + + 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); @@ -650,61 +733,25 @@ public: 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, -}; - -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_) {} + if (CVQuals & QualConst) + S += " const"; + if (CVQuals & QualVolatile) + S += " volatile"; + if (CVQuals & QualRestrict) + S += " restrict"; - 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); - } + if (Attrs != nullptr) + Attrs->print(S); } }; @@ -725,7 +772,7 @@ class SpecialName final : public Node { const Node *Child; public: - SpecialName(StringView Special_, Node *Child_) + SpecialName(StringView Special_, Node* Child_) : Node(KSpecialName), Special(Special_), Child(Child_) {} void printLeft(OutputStream &S) const override { @@ -740,8 +787,8 @@ class CtorVtableSpecialName final : public Node { public: CtorVtableSpecialName(Node *FirstType_, Node *SecondType_) - : Node(KCtorVtableSpecialName), FirstType(FirstType_), - SecondType(SecondType_) {} + : Node(KCtorVtableSpecialName), + FirstType(FirstType_), SecondType(SecondType_) {} void printLeft(OutputStream &S) const override { S += "construction vtable for "; @@ -751,41 +798,54 @@ public: } }; +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qual->print(S); + S += "::"; + Name->print(S); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + void printLeft(OutputStream &S) const override { + Encoding->print(S); + S += "::"; + Entity->print(S); + } +}; + class QualifiedName final : public Node { // qualifier::name const Node *Qualifier; const Node *Name; - mutable OutputStream::StreamStringView Cache; - public: - QualifiedName(Node *Qualifier_, Node *Name_) + QualifiedName(Node* Qualifier_, Node* Name_) : Node(KQualifiedName), 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); } }; -class EmptyName : public Node { -public: - EmptyName() : Node(KEmptyName) {} - void printLeft(OutputStream &) const override {} -}; - class VectorType final : public Node { const Node *BaseType; const NodeOrString Dimension; @@ -796,8 +856,8 @@ public: : Node(KVectorType), BaseType(nullptr), Dimension(Dimension_), IsPixel(true) {} VectorType(Node *BaseType_, NodeOrString Dimension_) - : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_), - IsPixel(false) {} + : Node(KVectorType), BaseType(BaseType_), + Dimension(Dimension_), IsPixel(false) {} void printLeft(OutputStream &S) const override { if (IsPixel) { @@ -816,38 +876,215 @@ public: } }; -class TemplateParams final : public Node { - NodeArray Params; +/// 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; + + // Setup OutputStream for a pack expansion unless we're already expanding one. + void initializePackExpansion(OutputStream &S) const { + if (S.CurrentPackMax == std::numeric_limits<unsigned>::max()) { + S.CurrentPackMax = static_cast<unsigned>(Data.size()); + S.CurrentPackIndex = 0; + } + } + +public: + ParameterPack(NodeArray Data_) : Node(KParameterPack), 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 { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; + } + + void printLeft(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printLeft(S); + } + void printRight(OutputStream &S) const override { + initializePackExpansion(S); + size_t Idx = S.CurrentPackIndex; + if (Idx < Data.size()) + Data[Idx]->printRight(S); + } +}; + +/// A variadic template argument. This node represents an occurrence 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_) {} + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputStream &S) const override { + Elements.printWithComma(S); + } +}; - mutable OutputStream::StreamStringView Cache; +/// 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: - TemplateParams(NodeArray Params_) : Node(KTemplateParams), Params(Params_) {} + ParameterPackExpansion(Node* Child_) + : Node(KParameterPackExpansion), Child(Child_) {} + + const Node *getChild() const { return Child; } void printLeft(OutputStream &S) const override { - if (!Cache.empty()) { - S += Cache; + constexpr unsigned Max = std::numeric_limits<unsigned>::max(); + SwapAndRestore<unsigned> SavePackIdx(S.CurrentPackIndex, Max); + SwapAndRestore<unsigned> SavePackMax(S.CurrentPackMax, Max); + size_t StreamPos = S.getCurrentPosition(); + + // Print the first element in the pack. If Child contains a ParameterPack, + // it will set up S.CurrentPackMax and print the first element. + Child->print(S); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a <function-param>. + if (S.CurrentPackMax == Max) { + S += "..."; return; } - OutputStream::StreamPosition Start = S.getCurrentPosition(); + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (S.CurrentPackMax == 0) { + S.setCurrentPosition(StreamPos); + return; + } + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { + S += ", "; + S.CurrentPackIndex = I; + Child->print(S); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + NodeArray getParams() { return Params; } + + void printLeft(OutputStream &S) const override { S += "<"; - Params.printWithSeperator(S, ", "); + Params.printWithComma(S); if (S.back() == '>') S += " "; S += ">"; + } +}; - Cache = S.makeStringViewFromPastPosition(Start); +struct ForwardTemplateReference : Node { + size_t Index; + Node *Ref = nullptr; + + // If we're currently printing this node. It is possible (though invalid) for + // a forward template reference to refer to itself via a substitution. This + // creates a cyclic AST, which will stack overflow printing. To fix this, bail + // out if more than one print* function is active. + mutable bool Printing = false; + + ForwardTemplateReference(size_t Index_) + : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown, + Cache::Unknown), + Index(Index_) {} + + bool hasRHSComponentSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore<bool> SavePrinting(Printing, true); + return Ref->hasRHSComponent(S); + } + bool hasArraySlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore<bool> SavePrinting(Printing, true); + return Ref->hasArray(S); + } + bool hasFunctionSlow(OutputStream &S) const override { + if (Printing) + return false; + SwapAndRestore<bool> SavePrinting(Printing, true); + return Ref->hasFunction(S); + } + const Node *getSyntaxNode(OutputStream &S) const override { + if (Printing) + return this; + SwapAndRestore<bool> SavePrinting(Printing, true); + return Ref->getSyntaxNode(S); + } + + void printLeft(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore<bool> SavePrinting(Printing, true); + Ref->printLeft(S); + } + void printRight(OutputStream &S) const override { + if (Printing) + return; + SwapAndRestore<bool> SavePrinting(Printing, true); + Ref->printRight(S); } }; -class NameWithTemplateArgs final : public Node { +struct NameWithTemplateArgs : Node { // name<template_args> Node *Name; Node *TemplateArgs; -public: NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} @@ -863,7 +1100,8 @@ class GlobalQualifiedName final : public Node { Node *Child; public: - GlobalQualifiedName(Node *Child_) : Node(KGlobalQualifiedName), Child(Child_) {} + GlobalQualifiedName(Node* Child_) + : Node(KGlobalQualifiedName), Child(Child_) {} StringView getBaseName() const override { return Child->getBaseName(); } @@ -873,10 +1111,9 @@ public: } }; -class StdQualifiedName final : public Node { +struct StdQualifiedName : Node { Node *Child; -public: StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} StringView getBaseName() const override { return Child->getBaseName(); } @@ -1034,27 +1271,40 @@ public: } }; -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_) {} void printLeft(OutputStream &S) const override { S += "\'lambda"; S += Count; S += "\'("; - Params.printWithSeperator(S, ", "); + Params.printWithComma(S); S += ")"; } }; +class StructuredBindingName : public Node { + NodeArray Bindings; +public: + StructuredBindingName(NodeArray Bindings_) + : Node(KStructuredBindingName), Bindings(Bindings_) {} + + void printLeft(OutputStream &S) const override { + S += '['; + Bindings.printWithComma(S); + S += ']'; + } +}; + // -- Expression Nodes -- struct Expr : public Node { - Expr() : Node(KExpr) {} + Expr(Kind K = KExpr) : Node(K) {} }; class BinaryExpr : public Expr { @@ -1190,14 +1440,15 @@ public: }; 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 += ")"; } }; @@ -1212,7 +1463,7 @@ public: void printLeft(OutputStream &S) const override { Callee->print(S); S += "("; - Args.printWithSeperator(S, ", "); + Args.printWithComma(S); S += ")"; } }; @@ -1227,8 +1478,8 @@ class NewExpr : public Expr { 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_) {} void printLeft(OutputStream &S) const override { if (IsGlobal) @@ -1236,17 +1487,19 @@ public: 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 += ")"; } + } }; @@ -1296,33 +1549,127 @@ public: } }; -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_) {} 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: + InitListExpr(Node *Ty_, NodeArray Inits_) : Ty(Ty_), Inits(Inits_) {} + void printLeft(OutputStream &S) const override { + if (Ty) + Ty->print(S); + S += '{'; + Inits.printWithComma(S); + S += '}'; + } +}; + +class BracedExpr : public Expr { + Node *Elem; + Node *Init; + bool IsArray; public: - ConversionExpr(NodeArray Expressions_, NodeArray Types_) - : Expressions(Expressions_), Types(Types_) {} + BracedExpr(Node *Elem_, Node *Init_, bool IsArray_) + : Expr(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {} void printLeft(OutputStream &S) const override { - S += "("; - Expressions.printWithSeperator(S, ", "); - S += ")("; - Types.printWithSeperator(S, ", "); - S += ")"; + 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); + } +}; + +struct FoldExpr : Expr { + Node *Pack, *Init; + StringView OperatorName; + bool IsLeftFold; + + FoldExpr(bool IsLeftFold_, StringView OperatorName_, Node *Pack_, Node *Init_) + : Pack(Pack_), Init(Init_), OperatorName(OperatorName_), + IsLeftFold(IsLeftFold_) {} + + void printLeft(OutputStream &S) const override { + auto PrintPack = [&] { + S += '('; + ParameterPackExpansion(Pack).print(S); + S += ')'; + }; + + S += '('; + + if (IsLeftFold) { + // init op ... op pack + if (Init != nullptr) { + Init->print(S); + S += ' '; + S += OperatorName; + S += ' '; + } + // ... op pack + S += "... "; + S += OperatorName; + S += ' '; + PrintPack(); + } else { // !IsLeftFold + // pack op ... + PrintPack(); + S += ' '; + S += OperatorName; + S += " ..."; + // pack op ... op init + if (Init != nullptr) { + S += ' '; + S += OperatorName; + S += ' '; + Init->print(S); + } + } + S += ')'; } }; @@ -1355,7 +1702,8 @@ class IntegerCastExpr : public Expr { StringView Integer; public: - IntegerCastExpr(Node *Ty_, StringView Integer_) : Ty(Ty_), Integer(Integer_) {} + IntegerCastExpr(Node *Ty_, StringView Integer_) + : Ty(Ty_), Integer(Integer_) {} void printLeft(OutputStream &S) const override { S += "("; @@ -1438,17 +1786,21 @@ class BumpPointerAllocator { static constexpr size_t AllocSize = 4096; static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); - alignas(16) char InitialBuffer[AllocSize]; + alignas(long double) char InitialBuffer[AllocSize]; BlockMeta* BlockList = nullptr; void grow() { - char* NewMeta = new char[AllocSize]; + char* NewMeta = static_cast<char *>(std::malloc(AllocSize)); + if (NewMeta == nullptr) + std::terminate(); BlockList = new (NewMeta) BlockMeta{BlockList, 0}; } void* allocateMassive(size_t NBytes) { NBytes += sizeof(BlockMeta); - BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(new char[NBytes]); + BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; return static_cast<void*>(NewMeta + 1); } @@ -1469,14 +1821,17 @@ public: BlockList->Current - N); } - ~BumpPointerAllocator() { + void reset() { while (BlockList) { BlockMeta* Tmp = BlockList; BlockList = BlockList->Next; if (reinterpret_cast<char*>(Tmp) != InitialBuffer) - delete[] reinterpret_cast<char*>(Tmp); + std::free(Tmp); } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; } + + ~BumpPointerAllocator() { reset(); } }; template <class T, size_t N> @@ -1501,10 +1856,15 @@ class PODSmallVector { size_t S = size(); if (isInline()) { auto* Tmp = static_cast<T*>(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); std::copy(First, Last, Tmp); First = Tmp; - } else + } else { First = static_cast<T*>(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } Last = First + S; Cap = First + NewCap; } @@ -1593,1083 +1953,852 @@ public: } }; -// 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 collapses 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; + + // Set of unresolved forward <template-param> references. These can occur in a + // conversion operator's type, and are resolved in the enclosing <encoding>. + PODSmallVector<ForwardTemplateReference *, 4> ForwardTemplateRefs; + + bool TryToParseTemplateArgs = true; + bool PermitForwardTemplateReferences = false; + bool ParsingLambdaParams = false; + + BumpPointerAllocator ASTAllocator; + + Db(const char *First_, const char *Last_) : First(First_), Last(Last_) {} + + void reset(const char *First_, const char *Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + ParsingLambdaParams = false; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + ASTAllocator.reset(); } - // 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 T, class... Args> T *make(Args &&... args) { + return new (ASTAllocator.allocate(sizeof(T))) + T(std::forward<Args>(args)...); } - // Remove the last pack from the table. - void popPack() { - unsigned Last = PackIndices.back(); - PackIndices.pop_back(); - Substitutions.dropBack(Last); + 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); } - // For use in a range-for loop. - struct NodeRange { - Node** First; - Node** Last; - Node** begin() { return First; } - Node** end() { return Last; } - }; - - // 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}; + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = + makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; } - size_t size() const { return PackIndices.size(); } - bool empty() const { return PackIndices.empty(); } - void clear() { - Substitutions.clear(); - PackIndices.clear(); + bool consumeIf(StringView S) { + if (StringView(First, Last).startsWith(S)) { + First += S.size(); + return true; + } + return false; } -}; -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; - - // 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; - - // 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; - - Qualifiers CV = QualNone; - FunctionRefQual RefQuals = FrefQualNone; - unsigned EncodingDepth = 0; - bool ParsedCtorDtorCV = false; - bool TagTemplates = true; - bool FixForwardReferences = false; - bool TryToParseTemplateArgs = true; - - BumpPointerAllocator ASTAllocator; - - template <class T, class... Args> T* make(Args&& ...args) - { - return new (ASTAllocator.allocate(sizeof(T))) - T(std::forward<Args>(args)...); + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; } + return false; + } - 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); - } + char consume() { return First != Last ? *First++ : '\0'; } - 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); + char look(unsigned Lookahead = 0) { + if (static_cast<size_t>(Last - First) <= Lookahead) + return '\0'; + return First[Lookahead]; + } -// <number> ::= [n] <non-negative decimal integer> + 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(bool TagTemplates = false); + 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(); + Node *parseFoldExpr(); + + /// 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; + size_t ForwardTemplateRefsBegin; + + NameState(Db *Enclosing) + : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {} + }; -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; - } - } + bool resolveForwardTemplateRefs(NameState &State) { + size_t I = State.ForwardTemplateRefsBegin; + size_t E = ForwardTemplateRefs.size(); + for (; I < E; ++I) { + size_t Idx = ForwardTemplateRefs[I]->Index; + if (Idx >= TemplateParams.size()) + return true; + ForwardTemplateRefs[I]->Ref = TemplateParams[Idx]; } - return first; -} - -template <class Float> -struct FloatData; + ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin); + return false; + } -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"; + /// 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); + + 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(); }; -constexpr const char* FloatData<float>::spec; +const char* parse_discriminator(const char* first, const char* last); -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"; -}; +// <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 + + if (look() == 'N') + return parseNestedName(State); + if (look() == 'Z') + return parseLocalName(State); + + // ::= <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(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make<NameWithTemplateArgs>(S, TA); + } -constexpr const char* FloatData<double>::spec; + Node *N = parseUnscopedName(State); + if (N == nullptr) + return nullptr; + // ::= <unscoped-template-name> <template-args> + if (look() == 'I') { + Subs.push_back(N); + Node *TA = parseTemplateArgs(State != nullptr); + if (TA == nullptr) + return nullptr; + if (State) State->EndsWithTemplateArgs = true; + return make<NameWithTemplateArgs>(N, TA); + } + // ::= <unscoped-name> + return N; +} -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"; -}; +// <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; + + if (consumeIf('s')) { + First = parse_discriminator(First, Last); + return make<LocalName>(Encoding, make<NameType>("string literal")); + } -constexpr const char* FloatData<long double>::spec; + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) + return nullptr; + Node *N = parseName(State); + if (N == nullptr) + return nullptr; + return make<LocalName>(Encoding, N); + } -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; - } - if (*t == 'E') - { - db.Names.push_back( - db.make<FloatExpr<Float>>(StringView(first, t))); - first = t + 1; - } - return first; + Node *Entity = parseName(State); + if (Entity == nullptr) + return nullptr; + First = parse_discriminator(First, Last); + return make<LocalName>(Encoding, Entity); } -// <positive length number> ::= [0-9]* -const char* -parse_positive_integer(const char* first, const char* last, size_t* out) -{ - 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; - } - *out = n; - first = t; - } - } - return first; +// <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); } -// extension -// <abi-tag-seq> ::= <abi-tag>* -// <abi-tag> ::= B <positive length number> <identifier> -const char* -parse_abi_tag_seq(const char* first, const char* last, Db& db) -{ - while (first != last && *first == 'B' && first+1 != last) - { - size_t length; - const char* t = parse_positive_integer(first+1, last, &length); - if (t == first+1) - return first; - if (static_cast<size_t>(last - t) < length || db.Names.empty()) - return first; - db.Names.back() = db.make<AbiTagAttr>( - db.Names.back(), StringView(t, t + length)); - first = t + length; - } - return first; +// <unqualified-name> ::= <operator-name> [abi-tags] +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <unnamed-type-name> +// ::= 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 if (consumeIf("DC")) { + size_t BindingsBegin = Names.size(); + do { + Node *Binding = parseSourceName(State); + if (Binding == nullptr) + return nullptr; + Names.push_back(Binding); + } while (!consumeIf('E')); + Result = make<StructuredBindingName>(popTrailingNodeArray(BindingsBegin)); + } else + Result = parseOperatorName(State); + if (Result != nullptr) + Result = parseAbiTags(Result); + return Result; } -// <source-name> ::= <positive length number> <identifier> [<abi-tag-seq>] -const char* -parse_source_name(const char* first, const char* last, Db& db) -{ - if (first != last) - { - size_t length; - const char* t = parse_positive_integer(first, last, &length); - if (t == first) - return first; - if (static_cast<size_t>(last - t) >= length) - { - StringView r(t, t + length); - 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 + length; - first = parse_abi_tag_seq(first, last, db); - } +// <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; + SwapAndRestore<bool> SwapParams(ParsingLambdaParams, true); + 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); } - return first; + StringView Count = parseNumber(); + if (!consumeIf('_')) + return nullptr; + return make<ClosureTypeName>(Params, Count); + } + return nullptr; } -// <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; - } - } - } - return first; +// <source-name> ::= <positive length number> <identifier> +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); } -// <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; - } +// <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; -} - -// <CV-Qualifiers> ::= [r] [V] [K] - -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; - } + 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': { + First += 2; + SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' <type> could have a <template-param> that refers to some + // <template-arg>s further ahead in the mangled name. + SwapAndRestore<bool> SavePermit(PermitForwardTemplateReferences, + PermitForwardTemplateReferences || + State != nullptr); + Node* Ty = parseType(); + if (Ty == nullptr) + return nullptr; + if (State) State->CtorDtorConversion = true; + return make<ConversionOperatorType>(Ty); } - 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; - } - } - } } - 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; - } - } + 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 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; - } - } + 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 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; - } - } + return nullptr; + case 'g': + switch (look(1)) { + case 'e': + First += 2; + return make<NameType>("operator>="); + case 't': + First += 2; + return make<NameType>("operator>"); } - return first; -} - -// sc <type> <expression> # static_cast<type> (expression) - -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 nullptr; + case 'i': + if (look(1) == 'x') { + First += 2; + return make<NameType>("operator[]"); } - return first; -} - -// 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 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); } - return first; + 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; } -// st <type> # sizeof (a type) +// <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; + } + } -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; - } + 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(State) == nullptr) + return nullptr; } - return first; -} + return make<CtorDtorName>(SoFar, false); + } -// sz <expr> # sizeof (a expression) + 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); + } -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; + return nullptr; } -// sZ <template-param> # size of a parameter pack +// <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 +// +// <data-member-prefix> := <member source-name> [<template-args>] M +// +// <template-prefix> ::= <prefix> <template unqualified-name> +// ::= <template-param> +// ::= <substitution> +Node *Db::parseNestedName(NameState *State) { + if (!consumeIf('N')) + return nullptr; + + 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<NestedName>(SoFar, Comp); + else SoFar = Comp; + if (State) State->EndsWithTemplateArgs = false; + }; -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; -} + if (consumeIf("St")) + SoFar = make<NameType>("std"); -// <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 + while (!consumeIf('E')) { + consumeIf('L'); // extension -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; - } - } - } + // <data-member-prefix> := <member source-name> [<template-args>] M + if (consumeIf('M')) { + if (SoFar == nullptr) + return nullptr; + continue; } - 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; - } + // ::= <template-param> + if (look() == 'T') { + Node *TP = parseTemplateParam(); + if (TP == nullptr) + return nullptr; + PushComponent(TP); + Subs.push_back(SoFar); + continue; } - return first; -} -// te <expression> # typeid (expression) -// ti <type> # typeid (type) + // ::= <template-prefix> <template-args> + if (look() == 'I') { + Node *TA = parseTemplateArgs(State != nullptr); + if (TA == nullptr || SoFar == nullptr) + return nullptr; + SoFar = make<NameWithTemplateArgs>(SoFar, TA); + if (State) State->EndsWithTemplateArgs = true; + Subs.push_back(SoFar); + continue; + } -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; - } + // ::= <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; -} -// tw <expression> # throw expression + // ::= <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; + } -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; - } + // Parse an <unqualified-name> thats actually a <ctor-dtor-name>. + if (look() == 'C' || (look() == 'D' && look(1) != 'C')) { + 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; -} -// ds <expression> <expression> # expr.*expr + // ::= <prefix> <unqualified-name> + Node *N = parseUnqualifiedName(State); + if (N == nullptr) + return nullptr; + PushComponent(N); + Subs.push_back(SoFar); + } -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; + if (SoFar == nullptr || Subs.empty()) + return nullptr; + + 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 @@ -2679,1984 +2808,1076 @@ parse_destructor_name(const char* first, const char* last, Db& db) // ::= on <operator-name> <template-args> # unresolved operator template-id // ::= dn <destructor-name> # destructor or pseudo-destructor; // # e.g. ~X or ~X<N-1> - -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; -} - -// <unresolved-qualifier-level> ::= <simple-id> - -const char* -parse_unresolved_qualifier_level(const char* first, const char* last, Db& db) -{ - return parse_simple_id(first, last, db); +Node *Db::parseBaseUnresolvedName() { + if (std::isdigit(look())) + return parseSimpleId(); + + if (consumeIf("dn")) + return parseDestructorName(); + + consumeIf("on"); + + 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> // extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> // ::= [gs] <base-unresolved-name> # x or (with "gs") ::x -// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> +// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> // # A::x, N::y, A<T>::z; "gs" means leading "::" // ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x // 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> - -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; - } - } - } +// +// <unresolved-qualifier-level> ::= <simple-id> +Node *Db::parseUnresolvedName() { + Node *SoFar = nullptr; + + // 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; -} - -// 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; - } - } + while (!consumeIf('E')) { + Node *Qual = parseSimpleId(); + if (Qual == nullptr) + return nullptr; + SoFar = make<QualifiedName>(SoFar, Qual); } - return first; -} -// cl <expression>+ E # call + Node *Base = parseBaseUnresolvedName(); + if (Base == nullptr) + return nullptr; + return make<QualifiedName>(SoFar, Base); + } -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; - } - return first; -} + bool Global = consumeIf("gs"); -// [gs] nw <expression>* _ <type> E # new (expr-list) type -// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) -// [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 + // [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; + } -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; - } + // [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); } - return first; -} + } -// cv <type> <expression> # conversion with one argument -// cv <type> _ <expression>* E # conversion with a different number of arguments + assert(SoFar != nullptr); -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; - } - } - return first; + Node *Base = parseBaseUnresolvedName(); + if (Base == nullptr) + return nullptr; + return make<QualifiedName>(SoFar, Base); } -// 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; +// <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; } -// <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; +// <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); } -// <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; +// <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; } -// <array-type> ::= A <positive dimension number> _ <element type> -// ::= A [<dimension expression>] _ <element type> +StringView Db::parseBareSourceName() { + size_t Int = 0; + if (parsePositiveInteger(&Int) || numLeft() < Int) + return StringView(); + StringView R(First, First + Int); + First += Int; + return R; +} -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; - } - } - } +// <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)); + } -// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) -// ::= DT <expression> E # decltype of an expression (C++0x) + consumeIf("Dx"); // transaction safe -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; - } + 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; } - return first; + 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> +// <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); + } -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; + 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()); } -// <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; +// <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, ")"); } -// <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 -// extension ::= <operator-name> <abi-tag-seq> -const char* -parse_operator_name(const char* first, const char* last, Db& db) -{ - const char* original_first = first; - 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; - } - } +// <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 (original_first != first) - first = parse_abi_tag_seq(first, last, db); + 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); + } - return first; + Node *Ty = parseType(); + if (Ty == nullptr) + return nullptr; + return make<ArrayType>(Ty); } -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; +// <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); } -// <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) -// ::= L <mangled-name> E # external name +// <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; +} -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; - } - } - } - } +// <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); } - return first; + + 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; } -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; +// <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; } - 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 # ? -// extension ::= <ctor-dtor-name> <abi-tag-seq> -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; - first = parse_abi_tag_seq(first, last, db); - 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; - first = parse_abi_tag_seq(first, last, db); - db.ParsedCtorDtorCV = true; - break; - } - 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); } - return first; -} + 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<ReferenceType>(Ref, ReferenceKind::LValue); + break; + } + // ::= O <type> # r-value reference (C++11) + case 'O': { + ++First; + Node *Ref = parseType(); + if (Ref == nullptr) + return nullptr; + Result = make<ReferenceType>(Ref, ReferenceKind::RValue); + 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; -// <unnamed-type-name> ::= Ut [<nonnegative number>] _ [<abi-tag-seq>] -// ::= <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; - first = parse_abi_tag_seq(first, last, db); - } - 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; - } + // 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; } - return first; + _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; } -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <unnamed-type-name> +Node *Db::parsePrefixExpr(StringView Kind) { + Node *E = parseExpr(); + if (E == nullptr) + return nullptr; + return make<PrefixExpr>(Kind, E); +} -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; +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); } -// <unscoped-name> ::= <unqualified-name> -// ::= St <unqualified-name> # ::std:: -// extension ::= StL<unqualified-name> +Node *Db::parseIntegerLiteral(StringView Lit) { + StringView Tmp = parseNumber(true); + if (!Tmp.empty() && consumeIf('E')) + return make<IntegerExpr>(Lit, Tmp); + return nullptr; +} -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; +// <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; } -// at <type> # alignof (a type) +// <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; +} -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; - } +// [gs] nw <expression>* _ <type> E # new (expr-list) type +// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// [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 +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); } -// az <expression> # alignof (a expression) +// 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_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; - } + 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); + } + + Node *E[1] = {parseExpr()}; + if (E[0] == nullptr) + return nullptr; + return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1)); } -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; +// <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") +// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name +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; + } + } } -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; +// <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 first; + 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); + } + 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 parseExpr(); } -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; +// (not yet in the spec) +// <fold-expr> ::= fL <binary-operator-name> <expression> <expression> +// ::= fR <binary-operator-name> <expression> <expression> +// ::= fl <binary-operator-name> <expression> +// ::= fr <binary-operator-name> <expression> +Node *Db::parseFoldExpr() { + if (!consumeIf('f')) + return nullptr; + + char FoldKind = look(); + bool IsLeftFold, HasInitializer; + HasInitializer = FoldKind == 'L' || FoldKind == 'R'; + if (FoldKind == 'l' || FoldKind == 'L') + IsLeftFold = true; + else if (FoldKind == 'r' || FoldKind == 'R') + IsLeftFold = false; + else + return nullptr; + ++First; + + // FIXME: This map is duplicated in parseOperatorName and parseExpr. + StringView OperatorName; + if (consumeIf("aa")) OperatorName = "&&"; + else if (consumeIf("an")) OperatorName = "&"; + else if (consumeIf("aN")) OperatorName = "&="; + else if (consumeIf("aS")) OperatorName = "="; + else if (consumeIf("cm")) OperatorName = ","; + else if (consumeIf("ds")) OperatorName = ".*"; + else if (consumeIf("dv")) OperatorName = "/"; + else if (consumeIf("dV")) OperatorName = "/="; + else if (consumeIf("eo")) OperatorName = "^"; + else if (consumeIf("eO")) OperatorName = "^="; + else if (consumeIf("eq")) OperatorName = "=="; + else if (consumeIf("ge")) OperatorName = ">="; + else if (consumeIf("gt")) OperatorName = ">"; + else if (consumeIf("le")) OperatorName = "<="; + else if (consumeIf("ls")) OperatorName = "<<"; + else if (consumeIf("lS")) OperatorName = "<<="; + else if (consumeIf("lt")) OperatorName = "<"; + else if (consumeIf("mi")) OperatorName = "-"; + else if (consumeIf("mI")) OperatorName = "-="; + else if (consumeIf("ml")) OperatorName = "*"; + else if (consumeIf("mL")) OperatorName = "*="; + else if (consumeIf("ne")) OperatorName = "!="; + else if (consumeIf("oo")) OperatorName = "||"; + else if (consumeIf("or")) OperatorName = "|"; + else if (consumeIf("oR")) OperatorName = "|="; + else if (consumeIf("pl")) OperatorName = "+"; + else if (consumeIf("pL")) OperatorName = "+="; + else if (consumeIf("rm")) OperatorName = "%"; + else if (consumeIf("rM")) OperatorName = "%="; + else if (consumeIf("rs")) OperatorName = ">>"; + else if (consumeIf("rS")) OperatorName = ">>="; + else return nullptr; + + Node *Pack = parseExpr(), *Init = nullptr; + if (Pack == nullptr) + return nullptr; + if (HasInitializer) { + Init = parseExpr(); + if (Init == nullptr) + return nullptr; + } + + if (IsLeftFold && Init) + std::swap(Pack, Init); + + return make<FoldExpr>(IsLeftFold, OperatorName, Pack, Init); } // <expression> ::= <unary operator-name> <expression> @@ -4691,1031 +3912,502 @@ parse_binary_expression(const char* first, const char* last, StringView op, Db& // ::= ds <expression> <expression> # expr.*expr // ::= sZ <template-param> # size of a parameter pack // ::= sZ <function-param> # size of a function parameter pack +// ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template // ::= sp <expression> # pack expansion // ::= tw <expression> # throw expression // ::= tr # throw with no operand (rethrow) // ::= <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> - -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); - } +Node *Db::parseExpr() { + bool Global = consumeIf("gs"); + if (numLeft() < 2) + return nullptr; + + switch (*First) { + case 'L': + return parseExprPrimary(); + case 'T': + return parseTemplateParam(); + case 'f': { + // Disambiguate a fold expression from a <function-param>. + if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2)))) + return parseFunctionParam(); + return parseFoldExpr(); + } + case 'a': + switch (First[1]) { + case 'a': + 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; -} - -// <template-arg> ::= <type> # type or template -// ::= X <expression> E # expression -// ::= <expr-primary> # simple expressions -// ::= J <template-arg>* E # argument pack -// ::= LZ <encoding> E # extension - -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; - } + case 'z': { + First += 2; + Node *Ty = parseExpr(); + if (Ty == nullptr) + return nullptr; + return make<EnclosingExpr>("alignof (", Ty, ")"); } - return first; -} - -// <template-args> ::= I <template-arg>* E -// extension, the abi says <template-arg>+ - -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); - - 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); } - 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 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); } - return first; -} - -// <discriminator> := _ <non-negative number> # when number < 10 -// := __ <non-negative number> _ # when number >= 10 -// extension := decimal-digit+ # at the end of string - -const char* -parse_discriminator(const char* first, const char* last) -{ - // parse but ignore discriminator - if (first != last) - { - if (*first == '_') - { - const char* t1 = first+1; - if (t1 != last) - { - if (std::isdigit(*t1)) - first = t1+1; - else if (*t1 == '_') - { - for (++t1; t1 != last && std::isdigit(*t1); ++t1) - ; - if (t1 != last && *t1 == '_') - first = t1 + 1; - } - } - } - else if (std::isdigit(*first)) - { - const char* t1 = first+1; - for (; t1 != last && std::isdigit(*t1); ++t1) - ; - if (t1 == last) - first = last; - } + // 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)); } - 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; - } - } + case 'm': + First += 2; + return parseBinaryExpr(","); + case 'o': + First += 2; + return parsePrefixExpr("~"); + case 'v': + return parseConversionExpr(); } - 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 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); } - return first; + 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; + case 'P': { + First += 2; + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Arg = parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); + } + return make<EnclosingExpr>( + "sizeof... (", make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin)), + ")"); + } + } + return nullptr; + case 't': + 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; } // <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; +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; } // <special-name> ::= TV <type> # virtual table @@ -5732,493 +4424,575 @@ parse_call_offset(const char* first, const char* last) // # No <type> // ::= TW <object name> # Thread-local wrapper // ::= TH <object name> # Thread-local initialization +// ::= GR <object name> _ # First temporary +// ::= GR <object name> <seq-id> _ # Subsequent temporaries // 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); + } + // 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 + // GR <object name> _ # First temporary + // GR <object name> <seq-id> _ # Subsequent temporaries + case 'R': { + First += 2; + Node *Name = parseName(); + if (Name == nullptr) + return nullptr; + size_t Count; + bool ParsedSeqId = !parseSeqId(&Count); + if (!consumeIf('_') && ParsedSeqId) + return nullptr; + return make<SpecialName>("reference temporary for ", Name); + } + } + } + return nullptr; +} -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; - } +// <encoding> ::= <function name> <bare-function-type> +// ::= <data name> +// ::= <special-name> +Node *Db::parseEncoding() { + 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(this); + Node *Name = parseName(&NameInfo); + if (Name == nullptr) + return nullptr; + + if (resolveForwardTemplateRefs(NameInfo)) + return nullptr; + + if (IsEndOfEncoding()) + return Name; + + Node *Attrs = nullptr; + if (consumeIf("Ua9enable_ifI")) { + size_t BeforeArgs = Names.size(); + while (!consumeIf('E')) { + Node *Arg = parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); } - return first; + Attrs = make<EnableIfAttr>(popTrailingNodeArray(BeforeArgs)); + } + + Node *ReturnType = nullptr; + if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) { + ReturnType = parseType(); + if (ReturnType == nullptr) + return nullptr; + } + + if (consumeIf('v')) + return make<FunctionEncoding>(ReturnType, Name, NodeArray(), + Attrs, 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), + Attrs, NameInfo.CVQualifiers, + NameInfo.ReferenceQualifier); } -template <class T> -class save_value +template <class Float> +struct FloatData; + +template <> +struct FloatData<float> { - T& restore_; - T original_value_; -public: - save_value(T& restore) - : restore_(restore), - original_value_(restore) - {} + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; - ~save_value() - { - restore_ = std::move(original_value_); - } +constexpr const char* FloatData<float>::spec; - save_value(const save_value&) = delete; - save_value& operator=(const save_value&) = delete; +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"; }; -// <encoding> ::= <function name> <bare-function-type> -// ::= <data name> -// ::= <special-name> +constexpr const char* FloatData<double>::spec; -const char* -parse_encoding(const char* first, const char* last, Db& db) +template <> +struct FloatData<long double> { - 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; +#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); } -// _block_invoke -// _block_invoke<decimal-digit>+ -// _block_invoke_<decimal-digit>+ +// <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; + } +} -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; +// <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; } - return first; + // 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]; } -// extension -// <dot-suffix> := .<anything and everything> +// <template-param> ::= T_ # first template parameter +// ::= T <parameter-2 non-negative number> _ +Node *Db::parseTemplateParam() { + if (!consumeIf('T')) + return nullptr; + + size_t Index = 0; + if (!consumeIf('_')) { + if (parsePositiveInteger(&Index)) + return nullptr; + ++Index; + if (!consumeIf('_')) + return nullptr; + } -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; + // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter list + // are mangled as the corresponding artificial template type parameter. + if (ParsingLambdaParams) + return make<NameType>("auto"); + + // If we're in a context where this <template-param> refers to a + // <template-arg> further ahead in the mangled name (currently just conversion + // operator types), then we should only look it up in the right context. + if (PermitForwardTemplateReferences) { + ForwardTemplateRefs.push_back(make<ForwardTemplateReference>(Index)); + return ForwardTemplateRefs.back(); + } + + if (Index >= TemplateParams.size()) + return nullptr; + 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); } - return first; + 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(); + } } -// <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> -// ::= <type> +// <template-args> ::= I <template-arg>* E +// extension, the abi says <template-arg>+ +Node *Db::parseTemplateArgs(bool TagTemplates) { + if (!consumeIf('I')) + return nullptr; + + // <template-params> refer to the innermost <template-args>. Clear out any + // outer args that we may have inserted into TemplateParams. + if (TagTemplates) + TemplateParams.clear(); + + 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 make<TemplateArgs>(popTrailingNodeArray(ArgsBegin)); +} + +// <discriminator> := _ <non-negative number> # when number < 10 +// := __ <non-negative number> _ # when number >= 10 +// extension := decimal-digit+ # at the end of string -void -demangle(const char* first, const char* last, Db& db, int& status) +const char* +parse_discriminator(const char* first, const char* last) { - if (first >= last) - { - status = invalid_mangled_name; - return; - } - if (*first == '_') + // parse but ignore discriminator + if (first != last) { - if (last - first >= 4) + if (*first == '_') { - 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* t1 = first+1; + if (t1 != last) { - const char* t = parse_encoding(first+4, last, db); - if (t != first+4 && t != last) + if (std::isdigit(*t1)) + first = t1+1; + else if (*t1 == '_') { - const char* t1 = parse_block_invoke(t, last, db); - if (t1 != last) - status = invalid_mangled_name; + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; } - else - status = invalid_mangled_name; } - else - status = invalid_mangled_name; } - else - status = invalid_mangled_name; + else if (std::isdigit(*first)) + { + const char* t1 = first+1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 == last) + first = last; + } } - else - { - const char* t = parse_type(first, last, db); - if (t != last) - status = invalid_mangled_name; + return first; +} + +// <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 (status == success && db.Names.empty()) - status = invalid_mangled_name; + if (numLeft() != 0) + return nullptr; + return Encoding; + } + + if (consumeIf("___Z")) { + Node *Encoding = parseEncoding(); + if (Encoding == nullptr || !consumeIf("_block_invoke")) + return nullptr; + bool RequireNumber = consumeIf('_'); + if (parseNumber().empty() && RequireNumber) + 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; +} + +bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S, + size_t InitSize) { + size_t BufferSize; + if (Buf == nullptr) { + Buf = static_cast<char *>(std::malloc(InitSize)); + if (Buf == nullptr) + return true; + BufferSize = InitSize; + } else + BufferSize = *N; + + S.reset(Buf, BufferSize); + return false; } } // unnamed namespace +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; - } - - 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 (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; - } +__cxa_demangle(const char *MangledName, char *Buf, size_t *N, int *Status) { + if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { + if (Status) + *Status = demangle_invalid_args; + return nullptr; + } - if (internal_status == success) - { - if (!buf) - { - internal_size = 1024; - buf = static_cast<char*>(std::malloc(internal_size)); - } + int InternalStatus = demangle_success; + Db Parser(MangledName, MangledName + std::strlen(MangledName)); + OutputStream S; + + Node *AST = Parser.parse(); + + if (AST == nullptr) + InternalStatus = demangle_invalid_mangled_name; + else if (initializeOutputStream(Buf, N, S, 1024)) + InternalStatus = demangle_memory_alloc_failure; + else { + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + Buf = S.getBuffer(); + } - 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 == demangle_success ? Buf : nullptr; } - } // __cxxabiv1 diff --git a/lib/libcxxabi/src/cxa_exception.cpp b/lib/libcxxabi/src/cxa_exception.cpp index d5230cdc7d9..397427a0299 100644 --- a/lib/libcxxabi/src/cxa_exception.cpp +++ b/lib/libcxxabi/src/cxa_exception.cpp @@ -18,6 +18,7 @@ #include "cxa_exception.hpp" #include "cxa_handlers.hpp" #include "fallback_malloc.h" +#include "include/atomic_support.h" #if __has_feature(address_sanitizer) extern "C" void __asan_handle_no_return(void); @@ -616,7 +617,7 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() { if (thrown_object != NULL ) { __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); - __sync_add_and_fetch(&exception_header->referenceCount, 1); + std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1)); } } @@ -628,12 +629,12 @@ __cxa_increment_exception_refcount(void *thrown_object) throw() { Requires: If thrown_object is not NULL, it is a native exception. */ -void -__cxa_decrement_exception_refcount(void *thrown_object) throw() { +_LIBCXXABI_NO_CFI +void __cxa_decrement_exception_refcount(void *thrown_object) throw() { if (thrown_object != NULL ) { __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); - if (__sync_sub_and_fetch(&exception_header->referenceCount, size_t(1)) == 0) + if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0) { if (NULL != exception_header->exceptionDestructor) exception_header->exceptionDestructor(thrown_object); diff --git a/lib/libcxxabi/src/cxa_handlers.cpp b/lib/libcxxabi/src/cxa_handlers.cpp index 2881a2a65fe..622e93c354e 100644 --- a/lib/libcxxabi/src/cxa_handlers.cpp +++ b/lib/libcxxabi/src/cxa_handlers.cpp @@ -18,6 +18,7 @@ #include "cxa_handlers.hpp" #include "cxa_exception.hpp" #include "private_typeinfo.h" +#include "include/atomic_support.h" namespace std { @@ -25,10 +26,7 @@ namespace std unexpected_handler get_unexpected() _NOEXCEPT { - return __sync_fetch_and_add(&__cxa_unexpected_handler, (unexpected_handler)0); -// The above is safe but overkill on x86 -// Using of C++11 atomics this should be rewritten -// return __cxa_unexpected_handler.load(memory_order_acq); + return __libcpp_atomic_load(&__cxa_unexpected_handler, _AO_Acquire); } void @@ -49,10 +47,7 @@ unexpected() terminate_handler get_terminate() _NOEXCEPT { - return __sync_fetch_and_add(&__cxa_terminate_handler, (terminate_handler)0); -// The above is safe but overkill on x86 -// Using of C++11 atomics this should be rewritten -// return __cxa_terminate_handler.load(memory_order_acq); + return __libcpp_atomic_load(&__cxa_terminate_handler, _AO_Acquire); } void @@ -99,8 +94,6 @@ terminate() _NOEXCEPT __terminate(get_terminate()); } -// In the future this will become: -// std::atomic<std::new_handler> __cxa_new_handler(0); extern "C" { new_handler __cxa_new_handler = 0; } @@ -108,18 +101,13 @@ new_handler __cxa_new_handler = 0; new_handler set_new_handler(new_handler handler) _NOEXCEPT { - return __atomic_exchange_n(&__cxa_new_handler, handler, __ATOMIC_ACQ_REL); -// Using of C++11 atomics this should be rewritten -// return __cxa_new_handler.exchange(handler, memory_order_acq_rel); + return __libcpp_atomic_exchange(&__cxa_new_handler, handler, _AO_Acq_Rel); } new_handler get_new_handler() _NOEXCEPT { - return __sync_fetch_and_add(&__cxa_new_handler, (new_handler)0); -// The above is safe but overkill on x86 -// Using of C++11 atomics this should be rewritten -// return __cxa_new_handler.load(memory_order_acq); + return __libcpp_atomic_load(&__cxa_new_handler, _AO_Acquire); } } // std diff --git a/lib/libcxxabi/src/cxa_personality.cpp b/lib/libcxxabi/src/cxa_personality.cpp index 7f857faf78f..6759d8a71f9 100644 --- a/lib/libcxxabi/src/cxa_personality.cpp +++ b/lib/libcxxabi/src/cxa_personality.cpp @@ -23,6 +23,16 @@ #include "private_typeinfo.h" #include "unwind.h" +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +#include <windows.h> +#include <winnt.h> + +extern "C" EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, + void *, PCONTEXT, + PDISPATCHER_CONTEXT, + _Unwind_Personality_Fn); +#endif + /* Exception Header Layout: @@ -934,12 +944,16 @@ _UA_CLEANUP_PHASE */ #if !defined(_LIBCXXABI_ARM_EHABI) +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +static _Unwind_Reason_Code __gxx_personality_imp +#else _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code #ifdef __USING_SJLJ_EXCEPTIONS__ __gxx_personality_sj0 #else __gxx_personality_v0 #endif +#endif (int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { @@ -1022,6 +1036,17 @@ __gxx_personality_v0 // We were called improperly: neither a phase 1 or phase 2 search return _URC_FATAL_PHASE1_ERROR; } + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +extern "C" EXCEPTION_DISPOSITION +__gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame, + PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) +{ + return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp, + __gxx_personality_imp); +} +#endif + #else extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, diff --git a/lib/libcxxabi/src/demangle/Compiler.h b/lib/libcxxabi/src/demangle/Compiler.h new file mode 100644 index 00000000000..e5f3c72a451 --- /dev/null +++ b/lib/libcxxabi/src/demangle/Compiler.h @@ -0,0 +1,34 @@ +//===--- Compiler.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// This file is contains a subset of macros copied from +// llvm/lib/Demangle/Compiler.h. +//===----------------------------------------------------------------------===// + +#ifndef LIBCXX_DEMANGLE_COMPILER_H +#define LIBCXX_DEMANGLE_COMPILER_H + +#ifdef _MSC_VER +// snprintf is implemented in VS 2015 +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef NDEBUG +#if __has_attribute(noinline) && __has_attribute(used) +#define DUMP_METHOD __attribute__((noinline, used)) +#else +#define DUMP_METHOD +#endif +#endif + +#endif diff --git a/lib/libcxxabi/src/demangle/StringView.h b/lib/libcxxabi/src/demangle/StringView.h new file mode 100644 index 00000000000..986f2defc08 --- /dev/null +++ b/lib/libcxxabi/src/demangle/StringView.h @@ -0,0 +1,98 @@ +//===--- StringView.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// +// This file is copied from llvm/lib/Demangle/StringView.h. +//===----------------------------------------------------------------------===// + +#ifndef LIBCXX_DEMANGLE_STRINGVIEW_H +#define LIBCXX_DEMANGLE_STRINGVIEW_H + +#include <algorithm> +#include <cassert> +#include <cstring> + +namespace { +class StringView { + const char *First; + const char *Last; + +public: + template <size_t N> + StringView(const char (&Str)[N]) : First(Str), Last(Str + N - 1) {} + StringView(const char *First_, const char *Last_) + : First(First_), Last(Last_) {} + StringView(const char *First_, size_t Len) + : First(First_), Last(First_ + Len) {} + StringView(const char *Str) : First(Str), Last(Str + std::strlen(Str)) {} + StringView() : First(nullptr), Last(nullptr) {} + + StringView substr(size_t From) const { + return StringView(begin() + From, size() - From); + } + + StringView substr(size_t From, size_t To) const { + if (To >= size()) + To = size() - 1; + if (From >= size()) + From = size() - 1; + return StringView(First + From, First + To); + } + + StringView dropFront(size_t N = 1) const { + if (N >= size()) + N = size() - 1; + return StringView(First + N, Last); + } + + char front() const { + assert(!empty()); + return *begin(); + } + + char popFront() { + assert(!empty()); + return *First++; + } + + bool consumeFront(char C) { + if (!startsWith(C)) + return false; + *this = dropFront(1); + return true; + } + + bool consumeFront(StringView S) { + if (!startsWith(S)) + return false; + *this = dropFront(S.size()); + return true; + } + + bool startsWith(char C) const { return !empty() && *begin() == C; } + + bool startsWith(StringView Str) const { + if (Str.size() > size()) + return false; + return std::equal(Str.begin(), Str.end(), begin()); + } + + const char &operator[](size_t Idx) const { return *(begin() + Idx); } + + 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; } +}; + +inline bool operator==(const StringView &LHS, const StringView &RHS) { + return LHS.size() == RHS.size() && + std::equal(LHS.begin(), LHS.end(), RHS.begin()); +} +} // namespace + +#endif diff --git a/lib/libcxxabi/src/demangle/Utility.h b/lib/libcxxabi/src/demangle/Utility.h new file mode 100644 index 00000000000..39092436283 --- /dev/null +++ b/lib/libcxxabi/src/demangle/Utility.h @@ -0,0 +1,191 @@ +//===--- Utility.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// +// This file is copied from llvm/lib/Demangle/Utility.h. +//===----------------------------------------------------------------------===// + +#ifndef LIBCXX_DEMANGLE_UTILITY_H +#define LIBCXX_DEMANGLE_UTILITY_H + +#include "StringView.h" + +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <iterator> +#include <limits> + +namespace { +// Stream that AST nodes write their string representation into after the AST +// has been parsed. +class OutputStream { + char *Buffer; + size_t CurrentPosition; + size_t BufferCapacity; + + // Ensure there is at least n more positions in buffer. + void grow(size_t N) { + if (N + CurrentPosition >= BufferCapacity) { + BufferCapacity *= 2; + if (BufferCapacity < N + CurrentPosition) + BufferCapacity = N + CurrentPosition; + Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity)); + if (Buffer == nullptr) + std::terminate(); + } + } + + void writeUnsigned(uint64_t N, bool isNeg = false) { + // Handle special case... + if (N == 0) { + *this << '0'; + return; + } + + char Temp[21]; + char *TempPtr = std::end(Temp); + + while (N) { + *--TempPtr = '0' + char(N % 10); + N /= 10; + } + + // Add negative sign... + if (isNeg) + *--TempPtr = '-'; + this->operator<<(StringView(TempPtr, std::end(Temp))); + } + +public: + OutputStream(char *StartBuf, size_t Size) + : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} + OutputStream() = default; + void reset(char *Buffer_, size_t BufferCapacity_) { + CurrentPosition = 0; + Buffer = Buffer_; + BufferCapacity = BufferCapacity_; + } + + /// Create an OutputStream from a buffer and a size. If either of these are + /// null a buffer is allocated. + static OutputStream create(char *StartBuf, size_t *Size, size_t AllocSize) { + OutputStream Result; + + if (!StartBuf || !Size) { + StartBuf = static_cast<char *>(std::malloc(AllocSize)); + if (StartBuf == nullptr) + std::terminate(); + Size = &AllocSize; + } + + Result.reset(StartBuf, *Size); + return Result; + } + + /// 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(); + unsigned CurrentPackMax = std::numeric_limits<unsigned>::max(); + + OutputStream &operator+=(StringView R) { + size_t Size = R.size(); + if (Size == 0) + return *this; + grow(Size); + std::memmove(Buffer + CurrentPosition, R.begin(), Size); + CurrentPosition += Size; + return *this; + } + + OutputStream &operator+=(char C) { + grow(1); + Buffer[CurrentPosition++] = C; + return *this; + } + + OutputStream &operator<<(StringView R) { return (*this += R); } + + OutputStream &operator<<(char C) { return (*this += C); } + + OutputStream &operator<<(long long N) { + if (N < 0) + writeUnsigned(static_cast<unsigned long long>(-N), true); + else + writeUnsigned(static_cast<unsigned long long>(N)); + return *this; + } + + OutputStream &operator<<(unsigned long long N) { + writeUnsigned(N, false); + return *this; + } + + OutputStream &operator<<(long N) { + return this->operator<<(static_cast<long long>(N)); + } + + OutputStream &operator<<(unsigned long N) { + return this->operator<<(static_cast<unsigned long long>(N)); + } + + OutputStream &operator<<(int N) { + return this->operator<<(static_cast<long long>(N)); + } + + OutputStream &operator<<(unsigned int N) { + return this->operator<<(static_cast<unsigned long long>(N)); + } + + size_t getCurrentPosition() const { return CurrentPosition; } + void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + + char back() const { + return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0'; + } + + bool empty() const { return CurrentPosition == 0; } + + char *getBuffer() { return Buffer; } + char *getBufferEnd() { return Buffer + CurrentPosition - 1; } + size_t getBufferCapacity() { return BufferCapacity; } +}; + +template <class T> class SwapAndRestore { + T &Restore; + T OriginalValue; + bool ShouldRestore = true; + +public: + SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {} + + SwapAndRestore(T &Restore_, T NewVal) + : Restore(Restore_), OriginalValue(Restore) { + Restore = std::move(NewVal); + } + ~SwapAndRestore() { + if (ShouldRestore) + Restore = std::move(OriginalValue); + } + + void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; } + + void restoreNow(bool Force) { + if (!Force && !ShouldRestore) + return; + + Restore = std::move(OriginalValue); + ShouldRestore = false; + } + + SwapAndRestore(const SwapAndRestore &) = delete; + SwapAndRestore &operator=(const SwapAndRestore &) = delete; +}; + +} // namespace + +#endif diff --git a/lib/libcxxabi/src/fallback_malloc.cpp b/lib/libcxxabi/src/fallback_malloc.cpp index 336ad31b017..bec9952596b 100644 --- a/lib/libcxxabi/src/fallback_malloc.cpp +++ b/lib/libcxxabi/src/fallback_malloc.cpp @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +// Define _LIBCPP_BUILDING_LIBRARY to ensure _LIBCPP_HAS_NO_ALIGNED_ALLOCATION +// is only defined when libc aligned allocation is not available. +#define _LIBCPP_BUILDING_LIBRARY #include "fallback_malloc.h" #include <__threading_support> diff --git a/lib/libcxxabi/src/include/atomic_support.h b/lib/libcxxabi/src/include/atomic_support.h new file mode 100644 index 00000000000..96dbd2ccd16 --- /dev/null +++ b/lib/libcxxabi/src/include/atomic_support.h @@ -0,0 +1,181 @@ +//===----------------------------------------------------------------------===//// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===//// + +// FIXME: This file is copied from libcxx/src/include/atomic_support.h. Instead +// of duplicating the file in libc++abi we should require that the libc++ +// sources are available when building libc++abi. + +#ifndef ATOMIC_SUPPORT_H +#define ATOMIC_SUPPORT_H + +#include "__config" +#include "memory" // for __libcpp_relaxed_load + +#if defined(__clang__) && __has_builtin(__atomic_load_n) \ + && __has_builtin(__atomic_store_n) \ + && __has_builtin(__atomic_add_fetch) \ + && __has_builtin(__atomic_exchange_n) \ + && __has_builtin(__atomic_compare_exchange_n) \ + && defined(__ATOMIC_RELAXED) \ + && defined(__ATOMIC_CONSUME) \ + && defined(__ATOMIC_ACQUIRE) \ + && defined(__ATOMIC_RELEASE) \ + && defined(__ATOMIC_ACQ_REL) \ + && defined(__ATOMIC_SEQ_CST) +# define _LIBCXXABI_HAS_ATOMIC_BUILTINS +#elif !defined(__clang__) && defined(_GNUC_VER) && _GNUC_VER >= 407 +# define _LIBCXXABI_HAS_ATOMIC_BUILTINS +#endif + +#if !defined(_LIBCXXABI_HAS_ATOMIC_BUILTINS) && !defined(_LIBCXXABI_HAS_NO_THREADS) +# if defined(_LIBCPP_WARNING) + _LIBCPP_WARNING("Building libc++ without __atomic builtins is unsupported") +# else +# warning Building libc++ without __atomic builtins is unsupported +# endif +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace { + +#if defined(_LIBCXXABI_HAS_ATOMIC_BUILTINS) && !defined(_LIBCXXABI_HAS_NO_THREADS) + +enum __libcpp_atomic_order { + _AO_Relaxed = __ATOMIC_RELAXED, + _AO_Consume = __ATOMIC_CONSUME, + _AO_Acquire = __ATOMIC_ACQUIRE, + _AO_Release = __ATOMIC_RELEASE, + _AO_Acq_Rel = __ATOMIC_ACQ_REL, + _AO_Seq = __ATOMIC_SEQ_CST +}; + +template <class _ValueType, class _FromType> +inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_atomic_store(_ValueType* __dest, _FromType __val, + int __order = _AO_Seq) +{ + __atomic_store_n(__dest, __val, __order); +} + +template <class _ValueType, class _FromType> +inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val) +{ + __atomic_store_n(__dest, __val, _AO_Relaxed); +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_load(_ValueType const* __val, + int __order = _AO_Seq) +{ + return __atomic_load_n(__val, __order); +} + +template <class _ValueType, class _AddType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a, + int __order = _AO_Seq) +{ + return __atomic_add_fetch(__val, __a, __order); +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_exchange(_ValueType* __target, + _ValueType __value, int __order = _AO_Seq) +{ + return __atomic_exchange_n(__target, __value, __order); +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +bool __libcpp_atomic_compare_exchange(_ValueType* __val, + _ValueType* __expected, _ValueType __after, + int __success_order = _AO_Seq, + int __fail_order = _AO_Seq) +{ + return __atomic_compare_exchange_n(__val, __expected, __after, true, + __success_order, __fail_order); +} + +#else // _LIBCPP_HAS_NO_THREADS + +enum __libcpp_atomic_order { + _AO_Relaxed, + _AO_Consume, + _AO_Acquire, + _AO_Release, + _AO_Acq_Rel, + _AO_Seq +}; + +template <class _ValueType, class _FromType> +inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_atomic_store(_ValueType* __dest, _FromType __val, + int = 0) +{ + *__dest = __val; +} + +template <class _ValueType, class _FromType> +inline _LIBCPP_INLINE_VISIBILITY +void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val) +{ + *__dest = __val; +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_load(_ValueType const* __val, + int = 0) +{ + return *__val; +} + +template <class _ValueType, class _AddType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a, + int = 0) +{ + return *__val += __a; +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +_ValueType __libcpp_atomic_exchange(_ValueType* __target, + _ValueType __value, int __order = _AO_Seq) +{ + _ValueType old = *__target; + *__target = __value; + return old; +} + +template <class _ValueType> +inline _LIBCPP_INLINE_VISIBILITY +bool __libcpp_atomic_compare_exchange(_ValueType* __val, + _ValueType* __expected, _ValueType __after, + int = 0, int = 0) +{ + if (*__val == *__expected) { + *__val = __after; + return true; + } + *__expected = *__val; + return false; +} + +#endif // _LIBCPP_HAS_NO_THREADS + +} // end namespace + +_LIBCPP_END_NAMESPACE_STD + +#endif // ATOMIC_SUPPORT_H diff --git a/lib/libcxxabi/src/include/refstring.h b/lib/libcxxabi/src/include/refstring.h index bc131aeb5ae..69f6747e125 100644 --- a/lib/libcxxabi/src/include/refstring.h +++ b/lib/libcxxabi/src/include/refstring.h @@ -22,6 +22,7 @@ #include <dlfcn.h> #include <mach-o/dyld.h> #endif +#include "atomic_support.h" _LIBCPP_BEGIN_NAMESPACE_STD @@ -87,7 +88,7 @@ __libcpp_refstring::__libcpp_refstring(const __libcpp_refstring &s) _NOEXCEPT : __imp_(s.__imp_) { if (__uses_refcount()) - __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1); + __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1); } inline @@ -96,10 +97,10 @@ __libcpp_refstring& __libcpp_refstring::operator=(__libcpp_refstring const& s) _ struct _Rep_base *old_rep = rep_from_data(__imp_); __imp_ = s.__imp_; if (__uses_refcount()) - __sync_add_and_fetch(&rep_from_data(__imp_)->count, 1); + __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1); if (adjust_old_count) { - if (__sync_add_and_fetch(&old_rep->count, count_t(-1)) < 0) + if (__libcpp_atomic_add(&old_rep->count, count_t(-1)) < 0) { ::operator delete(old_rep); } @@ -111,7 +112,7 @@ inline __libcpp_refstring::~__libcpp_refstring() { if (__uses_refcount()) { _Rep_base* rep = rep_from_data(__imp_); - if (__sync_add_and_fetch(&rep->count, count_t(-1)) < 0) { + if (__libcpp_atomic_add(&rep->count, count_t(-1)) < 0) { ::operator delete(rep); } } diff --git a/lib/libcxxabi/src/private_typeinfo.cpp b/lib/libcxxabi/src/private_typeinfo.cpp index ef9466ef610..a0326a188c4 100644 --- a/lib/libcxxabi/src/private_typeinfo.cpp +++ b/lib/libcxxabi/src/private_typeinfo.cpp @@ -859,13 +859,14 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // Record the access path that got us here // If there is more than one dst_type this path doesn't matter. info->path_dynamic_ptr_to_dst_ptr = path_below; + bool does_dst_type_point_to_our_static_type = false; // Only search above here if dst_type derives from static_type, or // if it is unknown if dst_type derives from static_type. if (info->is_dst_type_derived_from_static_type != no) { // Set up flags to record results from all base classes bool is_dst_type_derived_from_static_type = false; - bool does_dst_type_point_to_our_static_type = false; + // We've found a dst_type with a potentially public path to here. // We have to assume the path is public because it may become // public later (if we get back to here with a public path). @@ -909,21 +910,6 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, } } } - if (!does_dst_type_point_to_our_static_type) - { - // We found a dst_type that doesn't point to (static_ptr, static_type) - // So record the address of this dst_ptr and increment the - // count of the number of such dst_types found in the tree. - info->dst_ptr_not_leading_to_static_ptr = current_ptr; - info->number_to_dst_ptr += 1; - // If there exists another dst with a private path to - // (static_ptr, static_type), then the cast from - // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous, - // so stop search. - if (info->number_to_static_ptr == 1 && - info->path_dst_ptr_to_static_ptr == not_public_path) - info->search_done = true; - } // If we found no static_type,s then dst_type doesn't derive // from static_type, else it does. Record this result so that // next time we hit a dst_type we will know not to search above @@ -932,7 +918,22 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, info->is_dst_type_derived_from_static_type = yes; else info->is_dst_type_derived_from_static_type = no; - } + } + if (!does_dst_type_point_to_our_static_type) + { + // We found a dst_type that doesn't point to (static_ptr, static_type) + // So record the address of this dst_ptr and increment the + // count of the number of such dst_types found in the tree. + info->dst_ptr_not_leading_to_static_ptr = current_ptr; + info->number_to_dst_ptr += 1; + // If there exists another dst with a private path to + // (static_ptr, static_type), then the cast from + // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous, + // so stop search. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == not_public_path) + info->search_done = true; + } } } else @@ -1030,13 +1031,13 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, // Record the access path that got us here // If there is more than one dst_type this path doesn't matter. info->path_dynamic_ptr_to_dst_ptr = path_below; + bool does_dst_type_point_to_our_static_type = false; // Only search above here if dst_type derives from static_type, or // if it is unknown if dst_type derives from static_type. if (info->is_dst_type_derived_from_static_type != no) { // Set up flags to record results from all base classes bool is_dst_type_derived_from_static_type = false; - bool does_dst_type_point_to_our_static_type = false; // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; @@ -1047,20 +1048,6 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, if (info->found_our_static_ptr) does_dst_type_point_to_our_static_type = true; } - if (!does_dst_type_point_to_our_static_type) - { - // We found a dst_type that doesn't point to (static_ptr, static_type) - // So record the address of this dst_ptr and increment the - // count of the number of such dst_types found in the tree. - info->dst_ptr_not_leading_to_static_ptr = current_ptr; - info->number_to_dst_ptr += 1; - // If there exists another dst with a private path to - // (static_ptr, static_type), then the cast from - // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous. - if (info->number_to_static_ptr == 1 && - info->path_dst_ptr_to_static_ptr == not_public_path) - info->search_done = true; - } // If we found no static_type,s then dst_type doesn't derive // from static_type, else it does. Record this result so that // next time we hit a dst_type we will know not to search above @@ -1070,6 +1057,20 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, else info->is_dst_type_derived_from_static_type = no; } + if (!does_dst_type_point_to_our_static_type) + { + // We found a dst_type that doesn't point to (static_ptr, static_type) + // So record the address of this dst_ptr and increment the + // count of the number of such dst_types found in the tree. + info->dst_ptr_not_leading_to_static_ptr = current_ptr; + info->number_to_dst_ptr += 1; + // If there exists another dst with a private path to + // (static_ptr, static_type), then the cast from + // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == not_public_path) + info->search_done = true; + } } } else @@ -1181,6 +1182,8 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, info->found_our_static_ptr = false; info->found_any_static_type = false; p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); + found_our_static_ptr |= info->found_our_static_ptr; + found_any_static_type |= info->found_any_static_type; if (++p < e) { do @@ -1210,6 +1213,8 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, info->found_our_static_ptr = false; info->found_any_static_type = false; p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); + found_our_static_ptr |= info->found_our_static_ptr; + found_any_static_type |= info->found_any_static_type; } while (++p < e); } // Restore flags diff --git a/lib/libcxxabi/src/stdlib_exception.cpp b/lib/libcxxabi/src/stdlib_exception.cpp index a8f71ab0ecc..6c0910267d4 100644 --- a/lib/libcxxabi/src/stdlib_exception.cpp +++ b/lib/libcxxabi/src/stdlib_exception.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #define _LIBCPP_BUILDING_LIBRARY -#define _LIBCPP_BUILDING_NEW #include <new> #include <exception> diff --git a/lib/libcxxabi/src/stdlib_new_delete.cpp b/lib/libcxxabi/src/stdlib_new_delete.cpp index 0e85f6ad299..686ad07ccd5 100644 --- a/lib/libcxxabi/src/stdlib_new_delete.cpp +++ b/lib/libcxxabi/src/stdlib_new_delete.cpp @@ -9,7 +9,6 @@ // This file implements the new and delete operators. //===----------------------------------------------------------------------===// -#define _LIBCPP_BUILDING_NEW #define _LIBCPP_BUILDING_LIBRARY #include "__cxxabi_config.h" #include <new> |