[llvm-dev] RFC: Cleaning up the Itanium demangler

Erik Pilkington via llvm-dev llvm-dev at lists.llvm.org
Wed Jun 21 16:42:40 PDT 2017


Hello all,
The itanium demangler in libcxxabi (and also, llvm/lib/Demangle) is 
really slow. This is largely because the textual representation of the 
symbol that is being demangled is held in a std::string, and 
manipulations done during parsing are done on that string. The demangler 
is always concatenating strings and inserting into the middle of 
strings, which is terrible. The fact that the parsing logic and the 
string manipulation/formatting logic is interleaved also makes the 
demangler pretty ugly. Another problem was that the demangler used a lot 
stack space, and has a bunch of stack overflows filed against it.

I've been working on fixing this by parsing first into an AST structure, 
and then traversing that AST to produce a demangled string. This 
provides a significant performance improvement and also make the 
demangler somewhat more clean. Attached you should find a patch to this 
effect. This patch is still very much a work in progress, but currently 
passes the libcxxabi test suite and demangles all the symbols in LLVM 
identically to the current demangler. It also provides a significant 
performance improvement: it demangles the symbols in LLVM about 3.7 
times faster than the current demangler. Also, separating the formatting 
code from the parser reduces stack usage (the activation frame for 
parse_type reduced from 416 to 144 bytes on my machine). The stack usage 
is still pretty bad, but this helps with some of it.

Does anyone have any early feedback on the patch? Does this seem like a 
good direction for the demangler?

As far as future plans for this file, I have a few more refactorings and 
performance improvements that I'd like to get through. After that, it 
might be interesting to try to replace the FastDemangle.cpp demangler in 
LLDB with this, to restore the one true demangler in the source tree. 
The FastDemangler.cpp is only partially completed, and calls out to 
ItaniumDemangle.cpp in llvm (which is a copy of cxa_demangle.cpp) if it 
fails to parse the symbol.

Any thoughts here would be appreciated!
Thanks,
Erik
-------------- next part --------------
commit e526dffc812b303734fbce5644f2d2ae4c4ca3dc
Author: Erik Pilkington <erik.pilkington at gmail.com>
Date:   Thu Jun 15 13:23:08 2017 -0700

    demangle tree

diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index a2c384e..495e62e 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -1,5070 +1,6381 @@
 //===-------------------------- cxa_demangle.cpp --------------------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
 // This file is dual licensed under the MIT and the University of Illinois Open
 // Source Licenses. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 
 #define _LIBCPP_NO_EXCEPTIONS
 
 #include "__cxxabi_config.h"
 
 #include <vector>
 #include <algorithm>
 #include <string>
 #include <numeric>
 #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 string_ref
+{
+    const char *first, *last;
+public:
+    template <size_t N> string_ref(const char (&str)[N])
+            : first(str), last(str + N - 1) {}
+    string_ref(const char* first, const char* last)
+            : first(first), last(last) {}
+    string_ref() : first(nullptr), last(nullptr) {}
+
+    string_ref substr(size_t from, size_t to) {
+        if (to >= size()) to = size() - 1;
+        if (from >= size()) from = size() - 1;
+        return string_ref(first + from, first + to);
+    }
+
+    string_ref drop_front(size_t n) const {
+        if (n >= size()) n = size() - 1;
+        return string_ref(first + n, last);
+    }
+
+    bool starts_with(string_ref 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 string_ref& lhs, const string_ref& rhs)
+{
+    return lhs.size() == rhs.size() &&
+        std::equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+class stream
+{
+    char* buf;
+    size_t pos;
+    size_t cap;
+
+    void grow(size_t n) {
+        if (n + pos >= cap)
+            buf = static_cast<char*>(std::realloc(buf, cap *= 2));
+    }
+
+    friend class stream_string_ref;
+
+public:
+    stream(char* start_buf, size_t sz)
+            : buf(start_buf), pos(0), cap(sz) {}
+
+    stream& operator+=(string_ref r) {
+        size_t sz = r.size();
+        grow(sz);
+        memmove(buf + pos, r.begin(), sz);
+        pos += sz;
+        return *this;
+    }
+
+    stream& operator+=(char c) {
+        grow(1);
+        buf[pos++] = c;
+        return *this;
+    }
+
+    char back() const { return pos ? buf[pos - 1] : '\0'; }
+    bool empty() const { return pos == 0; }
+
+    char* get_buf() { return buf; }
+    char* get_buf_end() { return buf + pos - 1; }
+    size_t get_cap() { return cap; }
+
+    unsigned get_pos() const { return static_cast<unsigned>(pos); }
+};
+
+class stream_string_ref {
+    unsigned first, last;
+public:
+    stream_string_ref() : first(0), last(0) {}
+    stream_string_ref(unsigned first, unsigned last)
+            : first(first), last(last) {}
+
+    bool empty() const { return first == last; }
+
+    void print(stream& s)
+    {
+        s.grow(last - first);
+        s += string_ref(s.buf + first, s.buf + last);
+    }
+};
+
+struct node_meta
+{
+    // If this node has any rhs part, potentally nested.
+    unsigned has_rhs_component : 1;
+
+    unsigned has_array : 1;
+    unsigned has_function : 1;
+
+    // Quick RTTI, just for types that need it.
+    unsigned is_qual_type : 1;
+    unsigned is_template_param_list : 1;
+    unsigned is_special_substitution : 1;
+    unsigned is_name_type : 1;
+    unsigned is_objcproto_name : 1;
+
+    node_meta() { memset(this, 0, sizeof(*this)); }
+};
+
+class node
+{
+    node_meta meta;
+
+public:
+    node() = default;
+    node(node_meta meta) : meta(meta) {} // 5 meta line.
+
+    // unsigned get_max_size() const { return meta.max_size; }
+    bool has_array() const { return meta.has_array; }
+    void has_array(bool value) {
+        meta.has_array = value;
+        meta.has_rhs_component |= value;
+    }
+
+    bool has_function() const { return meta.has_function; }
+    void has_function(bool value) {
+        meta.has_function = value;
+        meta.has_rhs_component |= value;
+    }
+
+    bool has_rhs_component() const { return meta.has_rhs_component; }
+    void has_rhs_component(bool value) { meta.has_rhs_component = value; }
+
+    void is_template_param_list(bool value)
+    { meta.is_template_param_list = value; }
+    bool is_template_param_list() { return meta.is_template_param_list; }
+
+    bool is_special_substitution() { return meta.is_special_substitution; }
+    void is_special_substitution(bool value)
+    { meta.is_special_substitution = value; }
+
+    void is_qual_type(bool value) { meta.is_qual_type = value; }
+    bool is_qual_type() const { return meta.is_qual_type; }
+
+    void is_name_type(bool value) { meta.is_name_type = value; }
+    bool is_name_type() const { return meta.is_name_type; }
+
+    void is_objcproto_name(bool value) { meta.is_objcproto_name = value; }
+    bool is_objcproto_name() { return meta.is_objcproto_name; }
+
+    node_meta get_meta() const { return meta; }
+
+    void print(stream& s) const {
+        print_left(s);
+        if (has_rhs_component())
+            print_right(s);
+    }
+    virtual void print_left(stream& s) const = 0;
+    virtual void print_right(stream&) const {}
+
+    virtual string_ref get_base_name() const { return string_ref(); }
+
+    // Silence compiler warnings, this dtor will never be called.
+    virtual ~node() {}
+};
+
+class dot_suffix final : public node
+{
+    node* prefix;
+    string_ref suffix;
+public:
+    dot_suffix(node* prefix, string_ref suffix)
+            : prefix(prefix), suffix(suffix) {}
+
+    void print_left(stream& s) const override
+    {
+        prefix->print(s);
+        s += " (";
+        s += suffix;
+        s += ")";
+    }
+};
+
+class type : public node
+{
+public:
+    using node::node;
+};
+
+class vendor_ext_qual_type final : public type
+{
+    node* ext;
+    node* ty;
+public:
+    // FIXME: handle objcproto
+
+    vendor_ext_qual_type(node* ext, node* ty)
+            : ext(ext), ty(ty) {}
+
+    void print_left(stream& s) const override
+    {
+        ext->print(s);
+        s += " ";
+        ty->print_left(s);
+    }
+
+    void print_right(stream& s) const override
+    {
+        ty->print_right(s);
+    }
+};
+
+class qual_type final : public type {
+    unsigned qualifiers;
+    node* child;
+    mutable stream_string_ref cache;
+
+public:
+    qual_type(node* child, unsigned qualifiers)
+            : qualifiers(qualifiers), child(child) {
+        has_array(child->has_array());
+        has_function(child->has_function());
+        has_rhs_component(child->has_rhs_component());
+        is_qual_type(true);
+    }
+
+    void add_qualifiers(unsigned q2) { qualifiers |= q2; }
+
+    void print_left(stream& s) const override {
+        child->print_left(s);
+        if (!child->has_function()) {
+            if (qualifiers & 1)
+                s += " const";
+            if (qualifiers & 2)
+                s += " volatile";
+            if (qualifiers & 4)
+                s += " restrict";
+            if (qualifiers & 8)
+                s += " &";
+            if (qualifiers & 16)
+                s += " &&";
+        }
+    }
+
+    void print_right(stream& s) const override {
+        child->print_right(s);
+        if (child->has_function()) {
+            if (qualifiers & 1)
+                s += " const";
+            if (qualifiers & 2)
+                s += " volatile";
+            if (qualifiers & 4)
+                s += " restrict";
+            if (qualifiers & 8)
+                s += " &";
+            if (qualifiers & 16)
+                s += " &&";
+        }
+    }
+};
+
+class conversion_operator_type final : public type
+{
+    node* ty;
+
+public:
+    conversion_operator_type(node* ty) : ty(ty) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "operator ";
+        ty->print(s);
+    }
+};
+
+class postfix_qualified_type final : public type
+{
+    node* ty;
+    string_ref postfix;
+public:
+    postfix_qualified_type(node* ty, string_ref postfix)
+            : ty(ty), postfix(postfix) {}
+
+    void print_left(stream& s) const override
+    {
+        ty->print_left(s);
+        s += postfix;
+    }
+
+    void print_right(stream& s) const override
+    {
+        ty->print_right(s);
+    }
+};
+
+class pointer_type final : public type
+{
+    node* pointee;
+
+public:
+    pointer_type(node* pointee) : pointee(pointee) {
+        has_rhs_component(pointee->has_rhs_component());
+    }
+
+    // FIXME: formatting for objc_object<...> to id
+
+    void print_left(stream& s) const override
+    {
+        pointee->print_left(s);
+        if (pointee->has_array())
+            s += " ";
+        if (pointee->has_array() || pointee->has_function())
+            s += "(";
+        s += "*";
+    }
+
+    void print_right(stream& s) const override
+    {
+        if (pointee->has_array() || pointee->has_function())
+            s += ")";
+        pointee->print_right(s);
+    }
+};
+
+class lvalue_reference_type final : public type {
+    const node* pointee;
+
+public:
+    lvalue_reference_type(node* pointee) : pointee(pointee) {
+        has_rhs_component(pointee->has_rhs_component());
+    }
+
+    void print_left(stream& s) const override
+    {
+        pointee->print_left(s);
+        if (pointee->has_array())
+            s += " ";
+        if (pointee->has_array() || pointee->has_function())
+            s += "(&";
+        else
+            s += "&";
+    }
+    void print_right(stream& s) const override
+    {
+        if (pointee->has_array() || pointee->has_function())
+            s += ")";
+        pointee->print_right(s);
+    }
+};
+
+class rvalue_reference_type final : public type
+{
+    const node* pointee;
+public:
+    rvalue_reference_type(node* pointee) : pointee(pointee) {
+        has_rhs_component(pointee->has_rhs_component());
+    }
+
+    // FIXME: Better formatting
+    void print_left(stream& s) const override
+    {
+        pointee->print_left(s);
+        if (pointee->has_array())
+            s += " ";
+        if (pointee->has_array() || pointee->has_function())
+            s += "(&&";
+        else
+            s += "&&";
+    }
+    void print_right(stream& s) const override
+    {
+        if (pointee->has_array() || pointee->has_function())
+            s += ")";
+        pointee->print_right(s);
+    }
+};
+
+class pointer_to_member_type final : public type
+{
+    const node* class_type;
+    const node* member_type;
+
+public:
+    pointer_to_member_type(node* class_type, node* member_type)
+            : class_type(class_type), member_type(member_type) {
+        has_rhs_component(member_type->has_rhs_component());
+    }
+
+    void print_left(stream& s) const override
+    {
+        member_type->print_left(s);
+        if (member_type->has_array() || member_type->has_function())
+            s += "(";
+        else
+            s += " ";
+        class_type->print(s);
+        s += "::*";
+    }
+
+    void print_right(stream& s) const override
+    {
+        if (member_type->has_array() || member_type->has_function())
+            s += ")";
+        member_type->print_right(s);
+    }
+};
+
+struct node_or_string
+{
+    const void* first;
+    const void* second;
+
+public:
+    /* implicit */ node_or_string(string_ref str)
+    {
+        const char* first_char = str.begin();
+        const char* second_char = str.end();
+        if (second_char == nullptr) {
+            assert(first_char == second_char);
+            ++first_char, ++second_char;
+        }
+        first = static_cast<const void*>(first_char);
+        second = static_cast<const void*>(second_char);
+    }
+    /* implicit */ node_or_string(node* n)
+            : first(static_cast<const void*>(n)), second(nullptr) {}
+    node_or_string() : first(nullptr), second(nullptr) {}
+
+    bool is_string() const { return second && first; }
+    bool is_node() const { return first && !second; }
+    bool is_empty() const { return !first && !second; }
+
+    string_ref as_string() const
+    {
+        assert(is_string());
+        return string_ref(static_cast<const char*>(first),
+                          static_cast<const char*>(second));
+    }
+
+    const node* as_node() const
+    {
+        assert(is_node());
+        return static_cast<const node*>(first);
+    }
+};
+
+class array_type final : public type {
+    node* base;
+    node_or_string dimension;
+
+public:
+    array_type(node* base, node_or_string dimension)
+            : base(base), dimension(dimension)
+    {
+        has_array(true);
+    }
+
+    // Incomplete array type.
+    array_type(node* base) : base(base)
+    {
+        has_array(true);
+    }
+
+    void print_left(stream& s) const override {
+        base->print_left(s);
+    }
+
+    void print_right(stream& s) const override
+    {
+        if (s.back() != ']')
+            s += " ";
+        s += "[";
+        if (dimension.is_string())
+            s += dimension.as_string();
+        else if (dimension.is_node())
+            dimension.as_node()->print(s);
+        s += "]";
+        base->print_right(s);
+    }
+};
+
+class function_type final : public type {
+    unsigned nparams;
+    node** params;
+    node* ret;
+public:
+    function_type(node* ret, unsigned nparams, node** params)
+            : nparams(nparams), params(params), ret(ret) {
+        has_function(true);
+    }
+
+    // int (*f)(float)[10]
+    // here, ret = [int, [10]], params = [(float), ]
+    void print_left(stream& s) const override
+    {
+        ret->print_left(s);
+        s += " ";
+    }
+
+    void print_right(stream& s) const override
+    {
+        s += "(";
+        for (size_t i = 0; i < nparams; ++i)
+        {
+            if (i) s += ", ";
+            params[i]->print(s);
+        }
+        s += ")";
+        ret->print_right(s);
+    }
+};
+
+class top_level_function_decl final : public node
+{
+    const unsigned nparams;
+    node** params;
+    const node* ret;
+    const node* name;
+public:
+    top_level_function_decl(node* ret, node* name, unsigned nparams,
+                            node** params)
+            : nparams(nparams), params(params), ret(ret), name(name) {
+        has_function(true);
+    }
+
+    void print_left(stream& s) const override
+    {
+        if (ret) {
+            ret->print_left(s);
+            if (!ret->has_rhs_component())
+                s += " ";
+        }
+
+        name->print(s);
+    }
+
+    void print_right(stream& s) const override
+    {
+        s += "(";
+        for (size_t i = 0; i < nparams; ++i)
+        {
+            if (i) s += ", ";
+            params[i]->print(s);
+        }
+        s += ")";
+        if (ret) ret->print_right(s);
+    }
+};
+
+class string_type final : public type {
+    string_ref as_str;
+
+public:
+    string_type(string_ref as_str) : as_str(as_str) {}
+    void print_left(stream& s) const override { s += as_str; }
+};
+
+class name_type final : public type
+{
+    const string_ref name;
+public:
+    name_type(string_ref name) : name(name)
+    {
+        is_name_type(true);
+    }
+
+    string_ref get_name() const { return name; }
+    string_ref get_base_name() const override { return name; }
+
+    void print_left(stream& s) const override
+    {
+        s += name;
+    }
+};
+
+class objcproto_name : public node
+{
+    node* ty;
+    node* protocol;
+    bool is_pointer;
+public:
+    objcproto_name(node* ty, node* protocol) : ty(ty), protocol(protocol), is_pointer(false)
+    {
+        is_objcproto_name(true);
+    }
+
+    bool should_set_is_pointer()
+    {
+        return ty->is_name_type() && static_cast<name_type*>(ty)->get_name() == "objc_object";
+    }
+    void set_is_pointer() { is_pointer = true; }
+
+    void print_left(stream& s) const override
+    {
+        if (is_pointer)
+            s += "id";
+        else
+            ty->print_left(s);
+        s += "<";
+        protocol->print_left(s);
+        s += ">";
+    }
+};
+
+class special_name final : public type
+{
+    const string_ref special;
+    const node* child;
+public:
+    special_name(string_ref special, node* child)
+            : special(special), child(child) {}
+
+    void print_left(stream& s) const override
+    {
+        s += special;
+        child->print(s);
+    }
+};
+
+class ctor_vtable_special_name final : public type
+{
+    node* first_type;
+    node* second_type;
+public:
+    ctor_vtable_special_name(node* first_type, node* second_type)
+            : first_type(first_type), second_type(second_type) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "construction vtable for ";
+        first_type->print(s);
+        s += "-in-";
+        second_type->print(s);
+    }
+};
+
+class qualified_name final : public type
+{
+    // qualifier::name
+    node* qualifier;
+    node* name;
+
+    mutable stream_string_ref cache;
+
+public:
+    qualified_name(node* qualifier, node* name)
+            : qualifier(qualifier), name(name) {}
+
+    string_ref get_base_name() const override {
+        return name->get_base_name();
+    }
+
+    void print_left(stream& s) const override
+    {
+        if (!cache.empty())
+        {
+            cache.print(s);
+            return;
+        }
+        unsigned start = s.get_pos();
+        qualifier->print(s);
+        s += "::";
+        name->print(s);
+        cache = stream_string_ref(start, s.get_pos());
+    }
+};
+
+class vector_type final : public type
+{
+    node* base_type;
+    node_or_string dimension;
+    bool is_pixel;
+
+public:
+    vector_type(node_or_string dimension)
+            : base_type(nullptr), dimension(dimension), is_pixel(true) {}
+    vector_type(node* base_type, node_or_string dimension,
+                bool is_pixel = false)
+        : base_type(base_type), dimension(dimension), is_pixel(is_pixel) {}
+
+    void print_left(stream& s) const override
+    {
+        if (is_pixel) {
+            s += "pixel vector[";
+            s += dimension.as_string();
+            s += "]";
+        } else {
+            base_type->print(s);
+            s += " vector[";
+            if (dimension.is_node())
+                dimension.as_node()->print(s);
+            else if (dimension.is_string())
+                s += dimension.as_string();
+            s += "]";
+        }
+    }
+};
+
+class template_params final : public node
+{
+    size_t nparams;
+    node** params;
+
+    mutable stream_string_ref cache;
+
+public:
+    template_params(size_t nparams, node** params)
+            : nparams(nparams), params(params) {
+        is_template_param_list(true);
+    }
+
+    void print_left(stream& s) const override
+    {
+        if (!cache.empty())
+        {
+            cache.print(s);
+            return;
+        }
+
+        unsigned start = s.get_pos();
+        s += "<";
+        for (size_t i = 0; i < nparams; ++i)
+        {
+            if (i) s += ", ";
+            params[i]->print(s);
+        }
+
+        if (s.back() == '>')
+            s += " ";
+
+        s += ">";
+        cache = stream_string_ref(start, s.get_pos());
+    }
+};
+
+class name_with_template_args final : public node
+{
+    node* name;
+    node* template_args;
+
+    mutable stream_string_ref cache;
+
+public:
+    name_with_template_args(node* name, node* template_args)
+            : name(name), template_args(template_args) {}
+
+    string_ref get_base_name() const override { return name->get_base_name(); }
+
+    void print_left(stream& s) const override
+    {
+        if (!cache.empty())
+        {
+            cache.print(s);
+            return;
+        }
+        unsigned start = s.get_pos();
+        name->print(s);
+        template_args->print(s);
+        cache = stream_string_ref(start, s.get_pos());
+    }
+};
+
+class global_qualified_name final : public node {
+    node* child;
+public:
+    global_qualified_name(node* child) : child(child) {}
+
+    string_ref get_base_name() const override
+    {
+        return child->get_base_name();
+    }
+
+    void print_left(stream& s) const override
+    {
+        s += "::";
+        child->print(s);
+    }
+};
+
+enum class special_sub_kind {
+    allocator,
+    basic_string,
+    string,
+    istream,
+    ostream,
+    iostream,
+};
+
+class expanded_special_substitution final : public node
+{
+    special_sub_kind ssk;
+
+public:
+    expanded_special_substitution(special_sub_kind ssk) : ssk(ssk) {}
+
+    string_ref get_base_name() const override
+    {
+        switch (ssk)
+        {
+        case special_sub_kind::allocator:
+            return string_ref("allocator");
+        case special_sub_kind::basic_string:
+            return string_ref("basic_string");
+        case special_sub_kind::string:
+            return string_ref("basic_string");
+        case special_sub_kind::istream:
+            return string_ref("basic_istream");
+        case special_sub_kind::ostream:
+            return string_ref("basic_ostream");
+        case special_sub_kind::iostream:
+            return string_ref("basic_iostream");
+        }
+    }
+
+    void print_left(stream& s) const override
+    {
+        switch (ssk)
+        {
+        case special_sub_kind::allocator:
+            s += "std::basic_string<char, std::char_traits<char>, std::allocator<char> >";
+            break;
+        case special_sub_kind::basic_string:
+        case special_sub_kind::string:
+            s += "std::basic_string<char, std::char_traits<char>, std::allocator<char> >";
+            break;
+        case special_sub_kind::istream:
+            s += "std::basic_istream<char, std::char_traits<char> >";
+            break;
+        case special_sub_kind::ostream:
+            s += "std::basic_ostream<char, std::char_traits<char> >";
+            break;
+        case special_sub_kind::iostream:
+            s += "std::basic_iostream<char, std::char_traits<char> >";
+            break;
+        }
+    }
+};
+
+class special_substitution final : public node
+{
+public:
+    special_sub_kind k;
+
+    special_substitution(special_sub_kind k) : k(k)
+    {
+        is_special_substitution(true);
+    }
+
+    string_ref get_name_for_ctor_dtor_name()
+    {
+        switch (k) {
+        case special_sub_kind::allocator:
+            return string_ref("allocator");
+        case special_sub_kind::string:
+        case special_sub_kind::basic_string:
+            return string_ref("std::basic_string<char, std::char_traits<char>, "
+                              "std::allocator<char> >");
+          return string_ref("string");
+        case special_sub_kind::istream:
+            return string_ref("istream");
+        case special_sub_kind::ostream:
+            return string_ref("ostream");
+        case special_sub_kind::iostream:
+            return string_ref("iostream");
+        }
+    }
+
+    string_ref get_base_name() const override
+    {
+        switch (k) {
+        case special_sub_kind::allocator:
+            return string_ref("allocator");
+        case special_sub_kind::basic_string:
+            return string_ref("basic_string");
+        case special_sub_kind::string:
+            return string_ref("string");
+        case special_sub_kind::istream:
+            return string_ref("istream");
+        case special_sub_kind::ostream:
+            return string_ref("ostream");
+        case special_sub_kind::iostream:
+            return string_ref("iostream");
+        }
+    }
+
+    void print_left(stream& s) const override
+    {
+        switch (k) {
+        case special_sub_kind::allocator:
+            s += "std::allocator";
+            break;
+        case special_sub_kind::basic_string:
+            s += "std::basic_string";
+            break;
+        case special_sub_kind::string:
+            s += "std::string";
+            break;
+        case special_sub_kind::istream:
+            s += "std::istream";
+            break;
+        case special_sub_kind::ostream:
+            s += "std::ostream";
+            break;
+        case special_sub_kind::iostream:
+            s += "std::iostream";
+            break;
+        }
+    }
+};
+
+class ctor_dtor_name final : public node
+{
+    node* basename;
+    bool is_dtor;
+
+public:
+    ctor_dtor_name(node* basename, bool is_dtor)
+            : basename(basename), is_dtor(is_dtor) {}
+
+    void print_left(stream& s) const override
+    {
+        if (is_dtor)
+            s += "~";
+        string_ref name;
+        if (basename->is_special_substitution())
+            name = static_cast<special_substitution*>(basename)->
+                get_name_for_ctor_dtor_name();
+        else
+            name = basename->get_base_name();
+        s += name;
+    }
+};
+
+// -- Expression nodes --
+
+class expr : public node
+{
+};
+
+class binary_expr : public expr
+{
+    const node* lhs;
+    const string_ref infix_operator;
+    const node* rhs;
+public:
+    binary_expr(node* lhs, string_ref infix_operator, node* rhs)
+            : lhs(lhs), infix_operator(infix_operator), rhs(rhs) {}
+
+    void print_left(stream& s) const override
+    {
+        // might be a template argument expression, then we need to disambiguate
+        // with parens.
+        if (infix_operator == ">")
+            s += "(";
+
+        s += "(";
+        lhs->print(s);
+        s += ") ";
+        s += infix_operator;
+        s += " (";
+        rhs->print(s);
+        s += ")";
+
+        if (infix_operator == ">")
+            s += ")";
+    }
+};
+
+class at_expr : public expr
+{
+    node* op1;
+    node* op2;
+public:
+    at_expr(node* op1, node* op2) : op1(op1), op2(op2) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "(";
+        op1->print(s);
+        s += ")[";
+        op2->print(s);
+        s += "]";
+    }
+};
+
+class postfix_expr : public expr
+{
+    node* child;
+    string_ref operand;
+public:
+    postfix_expr(node* child, string_ref operand)
+            : child(child), operand(operand) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "(";
+        child->print(s);
+        s += ")";
+        s += operand;
+    }
+};
+
+class conditional_expr : public expr
+{
+    node* cond;
+    node* then;
+    node* _else;
+public:
+    conditional_expr(node* cond, node* then, node* _else)
+            : cond(cond), then(then), _else(_else) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "(";
+        cond->print(s);
+        s += ") ? (";
+        then->print(s);
+        s += ") : (";
+        _else->print(s);
+        s += ")";
+    }
+};
+
+class member_expr : public expr
+{
+    node* lhs;
+    string_ref kind;
+    node* rhs;
+public:
+    member_expr(node* lhs, string_ref kind, node* rhs)
+            : lhs(lhs), kind(kind), rhs(rhs) {}
+
+    void print_left(stream& s) const override
+    {
+        lhs->print(s);
+        s += kind;
+        rhs->print(s);
+    }
+};
+
+
+class enclosing_expr : public expr
+{
+    const string_ref prefix;
+    const node* infix;
+    const string_ref postfix;
+
+public:
+    enclosing_expr(string_ref prefix, node* infix, string_ref postfix)
+            : prefix(prefix), infix(infix), postfix(postfix) {}
+
+    void print_left(stream& s) const override
+    {
+        s += prefix;
+        infix->print(s);
+        s += postfix;
+    }
+};
+
+class cast_expr : public expr
+{
+    const node* to;
+    const string_ref cast_kind;
+    const node* from;
+public:
+    cast_expr(string_ref cast_kind, node* to, node* from)
+            : to(to), cast_kind(cast_kind), from(from) {}
+
+    void print_left(stream& s) const override
+    {
+        s += cast_kind;
+        s += "<";
+        to->print_left(s);
+        s += ">(";
+        from->print_left(s);
+        s += ")";
+    }
+};
+
+class call_like_expr : public expr
+{
+    const string_ref callee;
+    const size_t nargs;
+    node** args;
+public:
+    call_like_expr(string_ref callee, size_t nargs, node** args)
+            : callee(callee), nargs(nargs), args(args) {}
+
+    void print_left(stream& s) const override
+    {
+        s += callee;
+        s += "(";
+        for (size_t i = 0; i < nargs; ++i)
+        {
+            if (i) s += ", ";
+            args[i]->print(s);
+        }
+        s += ")";
+    }
+};
+
+class call_expr : public expr
+{
+    const node* callee;
+    const size_t nargs;
+    node** args;
+public:
+    call_expr(node* callee, size_t nargs, node** args)
+            : callee(callee), nargs(nargs), args(args) {}
+
+    void print_left(stream& s) const override
+    {
+        callee->print(s);
+        s += "(";
+        for (size_t i = 0; i < nargs; ++i)
+        {
+            if (i) s += ", ";
+            args[i]->print(s);
+        }
+        s += ")";
+    }
+};
+
+class string_then_expr : public expr
+{
+    string_ref prefix;
+    node* child;
+public:
+    string_then_expr(string_ref prefix, node* child)
+            : prefix(prefix), child(child) {}
+
+    void print_left(stream& s) const override
+    {
+        s += prefix;
+        child->print(s);
+    }
+};
+
+class prefix_expr : public expr
+{
+    string_ref prefix;
+    node* child;
+
+public:
+    prefix_expr(string_ref prefix, node* child)
+            : prefix(prefix), child(child) {}
+
+    void print_left(stream& s) const override
+    {
+        s += prefix;
+        s += "(";
+        child->print(s);
+        s += ")";
+    }
+};
+
+class function_param : public expr
+{
+    string_ref number;
+public:
+    function_param(string_ref number) : number(number) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "fp";
+        s += number;
+    }
+};
+
+class expr_list : public expr
+{
+    size_t nchildren;
+    node** children;
+
+public:
+    expr_list(size_t nchildren, node** children)
+            : nchildren(nchildren), children(children) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "(";
+        for (size_t i = 0; i < nchildren; ++i)
+        {
+            if (i) s += ", ";
+            children[i]->print(s);
+        }
+        s += ")";
+    }
+};
+
+// FIXME: We should replace all uses of these with actual nodes.
+inline namespace FIXME {
+class prefix_type : public type
+{
+    string_ref prefix;
+    node* child;
+public:
+    prefix_type(string_ref prefix, node* child)
+            : prefix(prefix), child(child) {}
+
+    void print_left(stream& s) const override
+    {
+        s += prefix;
+        child->print(s);
+    }
+};
+
+class then_expr : public expr {
+    node* lhs;
+    node* rhs;
+public:
+    then_expr(node* lhs, node* rhs)
+            : lhs(lhs), rhs(rhs) {}
+
+    void print_left(stream& s) const override
+    {
+        lhs->print(s);
+        rhs->print(s);
+    }
+};
+
+class unnamed_type : public type
+{
+    string_ref count;
+public:
+    unnamed_type(string_ref count) : count(count) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "'unnamed";
+        s += count;
+        s += "\'";
+    }
+};
+
+class lambda_type : public type
+{
+    size_t nparams;
+    node** params;
+    string_ref count;
+public:
+    lambda_type(size_t nparams, node** params, string_ref count)
+            : nparams(nparams), params(params), count(count) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "\'lambda";
+        s += count;
+        s += "\'(";
+        for (size_t i = 0; i < nparams; ++i)
+        {
+            if (i) s += ", ";
+            params[i]->print(s);
+        }
+        s += ")";
+    }
+};
+
+struct string_pair : public node
+{
+    string_ref first;
+    string_ref second;
+
+public:
+    string_pair(string_ref first, string_ref second)
+            : first(first), second(second) {}
+
+    void print_left(stream& s) const override
+    {
+        s += first;
+        s += second;
+    }
+};
+
+} // namespace FIXME
+
+class bool_expr : public expr
+{
+    bool value;
+public:
+    bool_expr(bool value) : value(value) {}
+
+    void print_left(stream& s) const override
+    {
+        s += value ? string_ref("true") : string_ref("false");
+    }
+};
+
+class something_fixme_expr : public expr
+{
+    node* ty;
+    string_ref integer;
+public:
+    something_fixme_expr(node* ty, string_ref integer)
+            : ty(ty), integer(integer) {}
+
+    void print_left(stream& s) const override
+    {
+        s += "(";
+        ty->print(s);
+        s += ")";
+        s += integer;
+    }
+};
+
+class integer_expr : public expr
+{
+    string_ref type;
+    string_ref value;
+public:
+    integer_expr(string_ref type, string_ref value)
+            : type(type), value(value) {}
+
+    void print_left(stream& s) const override
+    {
+        if (type.size() > 3) {
+            s += "(";
+            s += type;
+            s += ")";
+        }
+
+        if (value[0] == 'n') {
+            s += "-";
+            s += value.drop_front(1);
+        } else
+            s += value;
+
+        if (type.size() <= 3) {
+            s += type;
+        }
+    }
+};
+
+template <class Float>
+struct float_data;
+
+template <class Float>
+class float_expr : public expr
+{
+    const string_ref contents;
+public:
+    float_expr(string_ref contents) : contents(contents) {}
+
+    // FIXME: clean up this
+    void print_left(stream& s) const override
+    {
+        const char* first = contents.begin();
+        const char* last = contents.end() + 1;
+
+        const size_t N = float_data<Float>::mangled_size;
+        if (static_cast<std::size_t>(last - first) > N)
+        {
+            last = first + N;
+            union
+            {
+                Float value;
+                char buf[sizeof(Float)];
+            };
+            const char* t = first;
+            char* e = buf;
+            for (; t != last; ++t, ++e)
+            {
+                unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0') :
+                    static_cast<unsigned>(*t - 'a' + 10);
+                ++t;
+                unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0') :
+                    static_cast<unsigned>(*t - 'a' + 10);
+                *e = static_cast<char>((d1 << 4) + d0);
+            }
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+            std::reverse(buf, e);
+#endif
+            char num[float_data<Float>::max_demangled_size] = {0};
+            int n = snprintf(num, sizeof(num), float_data<Float>::spec, value);
+            s += string_ref(num, num + n);
+        }
+    }
+};
+
+template <std::size_t N>
+class arena
+{
+    static const std::size_t alignment = 16;
+    alignas(alignment) char buf_[N];
+    char* ptr_;
+
+    std::size_t 
+    align_up(std::size_t n) noexcept
+        {return (n + (alignment-1)) & ~(alignment-1);}
+
+    bool
+    pointer_in_buffer(char* p) noexcept
+        {return buf_ <= p && p <= buf_ + N;}
+
+public:
+    arena() noexcept : ptr_(buf_) {}
+    ~arena() {ptr_ = nullptr;}
+    arena(const arena&) = delete;
+    arena& operator=(const arena&) = delete;
+
+    char* allocate(std::size_t n);
+    void deallocate(char* p, std::size_t n) noexcept;
+
+    static constexpr std::size_t size() {return N;}
+    std::size_t used() const {return static_cast<std::size_t>(ptr_ - buf_);}
+    void reset() {ptr_ = buf_;}
+};
+
+template <std::size_t N>
+char*
+arena<N>::allocate(std::size_t n)
+{
+    n = align_up(n);
+    if (static_cast<std::size_t>(buf_ + N - ptr_) >= n)
+    {
+        char* r = ptr_;
+        ptr_ += n;
+        return r;
+    }
+    return static_cast<char*>(std::malloc(n));
+}
+
+template <std::size_t N>
+void
+arena<N>::deallocate(char* p, std::size_t n) noexcept
+{
+    if (pointer_in_buffer(p))
+    {
+        n = align_up(n);
+        if (p + n == ptr_)
+            ptr_ = p;
+    }
+    else
+        std::free(p);
+}
+
+template <class T, std::size_t N>
+class short_alloc
+{
+    arena<N>& a_;
+public:
+    typedef T value_type;
+
+public:
+    template <class _Up> struct rebind {typedef short_alloc<_Up, N> other;};
+
+    short_alloc(arena<N>& a) noexcept : a_(a) {}
+    template <class U>
+        short_alloc(const short_alloc<U, N>& a) noexcept
+            : a_(a.a_) {}
+    short_alloc(const short_alloc&) = default;
+    short_alloc& operator=(const short_alloc&) = delete;
+
+    T* allocate(std::size_t n)
+    {
+        return reinterpret_cast<T*>(a_.allocate(n*sizeof(T)));
+    }
+    void deallocate(T* p, std::size_t n) noexcept
+    {
+        a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
+    }
+
+    template <class T1, std::size_t N1, class U, std::size_t M>
+    friend
+    bool
+    operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept;
+
+    template <class U, std::size_t M> friend class short_alloc;
+};
+
+template <class T, std::size_t N, class U, std::size_t M>
+inline
+bool
+operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
+{
+    return N == M && &x.a_ == &y.a_;
+}
+
+template <class T, std::size_t N, class U, std::size_t M>
+inline
+bool
+operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
+{
+    return !(x == y);
+}
+
+template <class T>
+class malloc_alloc
+{
+public:
+    typedef T value_type;
+    typedef T& reference;
+    typedef const T& const_reference;
+    typedef T* pointer;
+    typedef const T* const_pointer;
+    typedef std::size_t size_type;
+    typedef std::ptrdiff_t difference_type;
+
+    malloc_alloc() = default;
+    template <class U> malloc_alloc(const malloc_alloc<U>&) noexcept {}
+
+    T* allocate(std::size_t n)
+    {
+        return static_cast<T*>(std::malloc(n*sizeof(T)));
+    }
+    void deallocate(T* p, std::size_t) noexcept
+    {
+        std::free(p);
+    }
+
+    template <class U> struct rebind { using other = malloc_alloc<U>; };
+    template <class U, class... Args>
+    void construct(U* p, Args&&... args)
+    {
+        ::new ((void*)p) U(std::forward<Args>(args)...);
+    }
+    void destroy(T* p)
+    {
+        p->~T();
+    }
+};
+
+template <class T, class U>
+inline
+bool
+operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) noexcept
+{
+    return true;
+}
+
+template <class T, class U>
+inline
+bool
+operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) noexcept
+{
+    return !(x == y);
+}
+
+const size_t bs = 4 * 1024;
+template <class T> using Alloc = short_alloc<T, bs>;
+template <class T> using Vector = std::vector<T, Alloc<T>>;
+
+struct bptr
+{
+    static constexpr size_t alloc_size = 2048;
+
+    struct meta
+    {
+        meta* next;
+        unsigned current;
+    };
+
+    meta* buf = nullptr;
+
+    void grow()
+    {
+        char* new_meta = new char[alloc_size];
+        buf = new (new_meta) meta{buf, 0};
+    }
+
+public:
+    bptr() { grow(); }
+
+    void* allocate(size_t n) {
+        assert(n % sizeof(void *) == 0);
+        if (n + buf->current > (alloc_size - sizeof(meta)))
+            grow();
+        buf->current += n;
+        return static_cast<void*>(
+            reinterpret_cast<char*>(buf + 1) + buf->current - n);
+    }
+
+    ~bptr() {
+        while (buf) {
+            meta* tmp = buf;
+            buf = buf->next;
+            delete tmp;
+        }
+    }
+};
+
+struct Db
+{
+    typedef std::basic_string<char, std::char_traits<char>,
+                              malloc_alloc<char>> String;
+
+    typedef Vector<node*> sub_type;
+
+    // typedef Vector<string_pair<String>> sub_type;
+    typedef Vector<sub_type> template_param_type;
+    sub_type names;
+    template_param_type subs;
+    Vector<template_param_type> template_param;
+    unsigned cv = 0;
+    unsigned ref = 0;
+    unsigned encoding_depth = 0;
+    bool parsed_ctor_dtor_cv = false;
+    bool tag_templates = true;
+    bool fix_forward_references = false;
+    bool try_to_parse_template_args = true;
+
+    bptr allocator;
+
+    template <size_t N>
+    Db(arena<N>& ar) :
+        names(ar),
+        subs(0, names, ar),
+        template_param(0, subs, ar)
+    {}
+
+    template <class T, class... Args> T* make(Args&& ...args)
+    {
+        // return new T(std::forward<Args>(args)...);
+        return new (allocator.allocate(sizeof(T)))
+            T(std::forward<Args>(args)...);
+    }
+
+    template <class T, class It> T* make_array(It begin, It end)
+    {
+        void* mem =
+            allocator.allocate(sizeof(T) * (static_cast<size_t>(end - begin)));
+        T* data = new (mem) T[static_cast<size_t>(end - begin)];
+        // T* data = new T[static_cast<size_t>(end - begin)];
+        std::copy(begin, end, data);
+        // return data;
+        return data;
+    }
+};
+
+
 template <class C>
     const char* parse_type(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_encoding(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_name(const char* first, const char* last, C& db,
                            bool* ends_with_template_args = 0);
 template <class C>
     const char* parse_expression(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_template_args(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_operator_name(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_unqualified_name(const char* first, const char* last, C& db);
 template <class C>
     const char* parse_decltype(const char* first, const char* last, C& db);
 
 template <class C>
 void
 print_stack(const C& db)
 {
     fprintf(stderr, "---------\n");
     fprintf(stderr, "names:\n");
     for (auto& s : db.names)
         fprintf(stderr, "{%s#%s}\n", s.first.c_str(), s.second.c_str());
     int i = -1;
     fprintf(stderr, "subs:\n");
     for (auto& v : db.subs)
     {
         if (i >= 0)
             fprintf(stderr, "S%i_ = {", i);
         else
             fprintf(stderr, "S_  = {");
         for (auto& s : v)
             fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str());
         fprintf(stderr, "}\n");
         ++i;
     }
     fprintf(stderr, "template_param:\n");
     for (auto& t : db.template_param)
     {
         fprintf(stderr, "--\n");
         i = -1;
         for (auto& v : t)
         {
             if (i >= 0)
                 fprintf(stderr, "T%i_ = {", i);
             else
                 fprintf(stderr, "T_  = {");
             for (auto& s : v)
                 fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str());
             fprintf(stderr, "}\n");
             ++i;
         }
     }
     fprintf(stderr, "---------\n\n");
 }
 
 template <class C>
 void
 print_state(const char* msg, const char* first, const char* last, const C& db)
 {
     fprintf(stderr, "%s: ", msg);
     for (; first != last; ++first)
         fprintf(stderr, "%c", *first);
     fprintf(stderr, "\n");
     print_stack(db);
 }
 
 // <number> ::= [n] <non-negative decimal integer>
 
 const char*
 parse_number(const char* first, const char* last)
 {
     if (first != last)
     {
         const char* t = first;
         if (*t == 'n')
             ++t;
         if (t != last)
         {
             if (*t == '0')
             {
                 first = t+1;
             }
             else if ('1' <= *t && *t <= '9')
             {
                 first = t+1;
                 while (first != last && std::isdigit(*first))
                     ++first;
             }
         }
     }
     return first;
 }
 
 template <class Float>
 struct float_data;
 
 template <>
 struct float_data<float>
 {
     static const size_t mangled_size = 8;
     static const size_t max_demangled_size = 24;
     static constexpr const char* spec = "%af";
 };
 
 constexpr const char* float_data<float>::spec;
 
 template <>
 struct float_data<double>
 {
     static const size_t mangled_size = 16;
     static const size_t max_demangled_size = 32;
     static constexpr const char* spec = "%a";
 };
 
 constexpr const char* float_data<double>::spec;
 
 template <>
 struct float_data<long double>
 {
 #if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
     defined(__wasm__)
     static const size_t mangled_size = 32;
 #elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
     static const size_t mangled_size = 16;
 #else
     static const size_t mangled_size = 20;  // May need to be adjusted to 16 or 24 on other platforms
 #endif
     static const size_t max_demangled_size = 40;
     static constexpr const char* spec = "%LaL";
 };
 
 constexpr const char* float_data<long double>::spec;
 
-template <class Float, class C>
+template <class Float>
 const char*
-parse_floating_number(const char* first, const char* last, C& db)
+parse_floating_number(const char* first, const char* last, Db& db)
 {
     const size_t N = float_data<Float>::mangled_size;
-    if (static_cast<std::size_t>(last - first) > N)
+    if (static_cast<std::size_t>(last - first) <= N)
+        return first;
+    last = first + N;
+    const char* t = first;
+    for (; t != last; ++t)
     {
-        last = first + N;
-        union
-        {
-            Float value;
-            char buf[sizeof(Float)];
-        };
-        const char* t = first;
-        char* e = buf;
-        for (; t != last; ++t, ++e)
-        {
-            if (!isxdigit(*t))
-                return first;
-            unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0') :
-                                        static_cast<unsigned>(*t - 'a' + 10);
-            ++t;
-            unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0') :
-                                        static_cast<unsigned>(*t - 'a' + 10);
-            *e = static_cast<char>((d1 << 4) + d0);
-        }
-        if (*t == 'E')
-        {
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-            std::reverse(buf, e);
-#endif
-            char num[float_data<Float>::max_demangled_size] = {0};
-            int n = snprintf(num, sizeof(num), float_data<Float>::spec, value);
-            if (static_cast<std::size_t>(n) >= sizeof(num))
-                return first;
-            db.names.push_back(typename C::String(num, static_cast<std::size_t>(n)));
-            first = t+1;
-        }
+        if (!isxdigit(*t))
+            return first;
+    }
+    if (*t == 'E')
+    {
+        db.names.push_back(
+            db.make<float_expr<Float>>(string_ref(first, t)));
+        first = t + 1;
     }
     return first;
 }
 
 // <source-name> ::= <positive length number> <identifier>
 
 template <class C>
 const char*
 parse_source_name(const char* first, const char* last, C& db)
 {
     if (first != last)
     {
         char c = *first;
         if (isdigit(c) && first+1 != last)
         {
             const char* t = first+1;
             size_t n = static_cast<size_t>(c - '0');
             for (c = *t; isdigit(c); c = *t)
             {
                 n = n * 10 + static_cast<size_t>(c - '0');
                 if (++t == last)
                     return first;
             }
             if (static_cast<size_t>(last - t) >= n)
             {
-                typename C::String r(t, n);
+                string_ref r(t, t + n);
                 if (r.substr(0, 10) == "_GLOBAL__N")
-                    db.names.push_back("(anonymous namespace)");
+                    db.names.push_back(db.template make<name_type>("(anonymous namespace)"));
                 else
-                    db.names.push_back(std::move(r));
+                    db.names.push_back(db.template make<name_type>(r));
                 first = t + n;
             }
         }
     }
     return first;
 }
 
 // <substitution> ::= S <seq-id> _
 //                ::= S_
 // <substitution> ::= Sa # ::std::allocator
 // <substitution> ::= Sb # ::std::basic_string
 // <substitution> ::= Ss # ::std::basic_string < char,
 //                                               ::std::char_traits<char>,
 //                                               ::std::allocator<char> >
 // <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >
 // <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >
 // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
 
 template <class C>
 const char*
 parse_substitution(const char* first, const char* last, C& db)
 {
     if (last - first >= 2)
     {
         if (*first == 'S')
         {
             switch (first[1])
             {
             case 'a':
-                db.names.push_back("std::allocator");
+                db.names.push_back(
+                    db.template make<special_substitution>(
+                        special_sub_kind::allocator));
                 first += 2;
                 break;
             case 'b':
-                db.names.push_back("std::basic_string");
+                db.names.push_back(db.template make<name_type>("std::basic_string"));
                 first += 2;
                 break;
             case 's':
-                db.names.push_back("std::string");
+                db.names.push_back(
+                    db.template make<special_substitution>(
+                        special_sub_kind::string));
                 first += 2;
                 break;
             case 'i':
-                db.names.push_back("std::istream");
+                db.names.push_back(db.template make<special_substitution>(special_sub_kind::istream));
                 first += 2;
                 break;
             case 'o':
-                db.names.push_back("std::ostream");
+                db.names.push_back(db.template make<special_substitution>(special_sub_kind::ostream));
                 first += 2;
                 break;
             case 'd':
-                db.names.push_back("std::iostream");
+                db.names.push_back(db.template make<special_substitution>(special_sub_kind::iostream));
                 first += 2;
                 break;
             case '_':
                 if (!db.subs.empty())
                 {
                     for (const auto& n : db.subs.front())
                         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 (const auto& n : db.subs[sub])
                             db.names.push_back(n);
                         first = t+1;
                     }
                 }
                 break;
             }
         }
     }
     return first;
 }
 
 // <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
 
 template <class C>
 const char*
 parse_builtin_type(const char* first, const char* last, C& db)
 {
     if (first != last)
     {
         switch (*first)
         {
         case 'v':
-            db.names.push_back("void");
+            db.names.push_back(db.template make<name_type>("void"));
             ++first;
             break;
         case 'w':
-            db.names.push_back("wchar_t");
+            db.names.push_back(db.template make<name_type>("wchar_t"));
             ++first;
             break;
         case 'b':
-            db.names.push_back("bool");
+            db.names.push_back(db.template make<name_type>("bool"));
             ++first;
             break;
         case 'c':
-            db.names.push_back("char");
+            db.names.push_back(db.template make<name_type>("char"));
             ++first;
             break;
         case 'a':
-            db.names.push_back("signed char");
+            db.names.push_back(db.template make<name_type>("signed char"));
             ++first;
             break;
         case 'h':
-            db.names.push_back("unsigned char");
+            db.names.push_back(db.template make<name_type>("unsigned char"));
             ++first;
             break;
         case 's':
-            db.names.push_back("short");
+            db.names.push_back(db.template make<name_type>("short"));
             ++first;
             break;
         case 't':
-            db.names.push_back("unsigned short");
+            db.names.push_back(db.template make<name_type>("unsigned short"));
             ++first;
             break;
         case 'i':
-            db.names.push_back("int");
+            db.names.push_back(db.template make<name_type>("int"));
             ++first;
             break;
         case 'j':
-            db.names.push_back("unsigned int");
+            db.names.push_back(db.template make<name_type>("unsigned int"));
             ++first;
             break;
         case 'l':
-            db.names.push_back("long");
+            db.names.push_back(db.template make<name_type>("long"));
             ++first;
             break;
         case 'm':
-            db.names.push_back("unsigned long");
+            db.names.push_back(db.template make<name_type>("unsigned long"));
             ++first;
             break;
         case 'x':
-            db.names.push_back("long long");
+            db.names.push_back(db.template make<name_type>("long long"));
             ++first;
             break;
         case 'y':
-            db.names.push_back("unsigned long long");
+            db.names.push_back(db.template make<name_type>("unsigned long long"));
             ++first;
             break;
         case 'n':
-            db.names.push_back("__int128");
+            db.names.push_back(db.template make<name_type>("__int128"));
             ++first;
             break;
         case 'o':
-            db.names.push_back("unsigned __int128");
+            db.names.push_back(db.template make<name_type>("unsigned __int128"));
             ++first;
             break;
         case 'f':
-            db.names.push_back("float");
+            db.names.push_back(db.template make<name_type>("float"));
             ++first;
             break;
         case 'd':
-            db.names.push_back("double");
+            db.names.push_back(db.template make<name_type>("double"));
             ++first;
             break;
         case 'e':
-            db.names.push_back("long double");
+            db.names.push_back(db.template make<name_type>("long double"));
             ++first;
             break;
         case 'g':
-            db.names.push_back("__float128");
+            db.names.push_back(db.template make<name_type>("__float128"));
             ++first;
             break;
         case 'z':
-            db.names.push_back("...");
+            db.names.push_back(db.template make<name_type>("..."));
             ++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("decimal64");
+                    db.names.push_back(db.template make<name_type>("decimal64"));
                     first += 2;
                     break;
                 case 'e':
-                    db.names.push_back("decimal128");
+                    db.names.push_back(db.template make<name_type>("decimal128"));
                     first += 2;
                     break;
                 case 'f':
-                    db.names.push_back("decimal32");
+                    db.names.push_back(db.template make<name_type>("decimal32"));
                     first += 2;
                     break;
                 case 'h':
-                    db.names.push_back("decimal16");
+                    db.names.push_back(db.template make<name_type>("decimal16"));
                     first += 2;
                     break;
                 case 'i':
-                    db.names.push_back("char32_t");
+                    db.names.push_back(db.template make<name_type>("char32_t"));
                     first += 2;
                     break;
                 case 's':
-                    db.names.push_back("char16_t");
+                    db.names.push_back(db.template make<name_type>("char16_t"));
                     first += 2;
                     break;
                 case 'a':
-                    db.names.push_back("auto");
+                    db.names.push_back(db.template make<name_type>("auto"));
                     first += 2;
                     break;
                 case 'c':
-                    db.names.push_back("decltype(auto)");
+                    db.names.push_back(db.template make<name_type>("decltype(auto)"));
                     first += 2;
                     break;
                 case 'n':
-                    db.names.push_back("std::nullptr_t");
+                    db.names.push_back(db.template make<name_type>("std::nullptr_t"));
                     first += 2;
                     break;
                 }
             }
             break;
         }
     }
     return first;
 }
 
 // <CV-qualifiers> ::= [r] [V] [K]
 
 const char*
 parse_cv_qualifiers(const char* first, const char* last, unsigned& cv)
 {
     cv = 0;
     if (first != last)
     {
         if (*first == 'r')
         {
             cv |= 4;
             ++first;
         }
         if (*first == 'V')
         {
             cv |= 2;
             ++first;
         }
         if (*first == 'K')
         {
             cv |= 1;
             ++first;
         }
     }
     return first;
 }
 
 // <template-param> ::= T_    # first template parameter
 //                  ::= T <parameter-2 non-negative number> _
 
 template <class C>
 const char*
 parse_template_param(const char* first, const char* last, C& db)
 {
     if (last - first >= 2)
     {
         if (*first == 'T')
         {
             if (first[1] == '_')
             {
                 if (db.template_param.empty())
                     return first;
                 if (!db.template_param.back().empty())
                 {
                     for (auto& t : db.template_param.back().front())
                         db.names.push_back(t);
                     first += 2;
                 }
                 else
                 {
-                    db.names.push_back("T_");
+                    db.names.push_back(db.template make<name_type>("T_"));
                     first += 2;
                     db.fix_forward_references = 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 != '_' || db.template_param.empty())
                     return first;
                 ++sub;
                 if (sub < db.template_param.back().size())
                 {
                     for (auto& temp : db.template_param.back()[sub])
                         db.names.push_back(temp);
                     first = t+1;
                 }
                 else
                 {
-                    db.names.push_back(typename C::String(first, t+1));
+                    db.names.push_back(
+                        db.template make<name_type>(string_ref(first, t + 1)));
                     first = t+1;
                     db.fix_forward_references = true;
                 }
             }
         }
     }
     return first;
 }
 
 // cc <type> <expression>                               # const_cast<type> (expression)
 
 template <class C>
 const char*
 parse_const_cast_expr(const char* first, const char* last, C& 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 expr = db.names.back().move_full();
+                auto from_expr = db.names.back();
                 db.names.pop_back();
                 if (db.names.empty())
                     return first;
-                db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")";
+                db.names.back() = db.template make<cast_expr>(
+                    "const_cast", db.names.back(), from_expr);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // dc <type> <expression>                               # dynamic_cast<type> (expression)
 
 template <class C>
 const char*
 parse_dynamic_cast_expr(const char* first, const char* last, C& 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 expr = db.names.back().move_full();
+                auto from_expr = db.names.back();
                 db.names.pop_back();
                 if (db.names.empty())
                     return first;
-                db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")";
+                db.names.back() = db.template make<cast_expr>(
+                    "dynamic_cast", db.names.back(), from_expr);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // rc <type> <expression>                               # reinterpret_cast<type> (expression)
 
 template <class C>
 const char*
 parse_reinterpret_cast_expr(const char* first, const char* last, C& 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 expr = db.names.back().move_full();
+                auto from_expr = db.names.back();
                 db.names.pop_back();
                 if (db.names.empty())
                     return first;
-                db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")";
+                db.names.back() = db.template make<cast_expr>(
+                    "reinterpret_cast", db.names.back(), from_expr);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // sc <type> <expression>                               # static_cast<type> (expression)
 
 template <class C>
 const char*
 parse_static_cast_expr(const char* first, const char* last, C& 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 expr = db.names.back().move_full();
+                auto from_expr = db.names.back();
                 db.names.pop_back();
-                db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")";
+                db.names.back() = db.template make<cast_expr>(
+                    "static_cast", db.names.back(), from_expr);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // sp <expression>                                  # pack expansion
 
 template <class C>
 const char*
 parse_pack_expansion(const char* first, const char* last, C& db)
 {
     if (last - first >= 3 && first[0] == 's' && first[1] == 'p')
     {
         const char* t = parse_expression(first+2, last, db);
         if (t != first+2)
             first = t;
     }
     return first;
 }
 
 // st <type>                                            # sizeof (a type)
 
 template <class C>
 const char*
 parse_sizeof_type_expr(const char* first, const char* last, C& 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() = "sizeof (" + db.names.back().move_full() + ")";
+            db.names.back() = db.template make<enclosing_expr>(
+                "sizeof (", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 // sz <expr>                                            # sizeof (a expression)
 
 template <class C>
 const char*
 parse_sizeof_expr_expr(const char* first, const char* last, C& 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() = "sizeof (" + db.names.back().move_full() + ")";
+            db.names.back() = db.template make<enclosing_expr>(
+                "sizeof (", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 // sZ <template-param>                                  # size of a parameter pack
 
 template <class C>
 const char*
 parse_sizeof_param_pack_expr(const char* first, const char* last, C& 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)
         {
-            typename C::String tmp("sizeof...(");
-            size_t k = k0;
-            if (k != k1)
-            {
-                tmp += db.names[k].move_full();
-                for (++k; k != k1; ++k)
-                    tmp += ", " + db.names[k].move_full();
-            }
-            tmp += ")";
-            for (; k1 != k0; --k1)
-                db.names.pop_back();
-            db.names.push_back(std::move(tmp));
+            node* sizeof_expr = db.template make<call_like_expr>(
+                "sizeof...", k1 - k0, db.template make_array<node*>(db.names.begin() + (long)k0,
+                                                           db.names.end()));
+            db.names.erase(db.names.begin() + (long)k0, db.names.end());
+            db.names.push_back(sizeof_expr);
             first = t;
         }
     }
     return first;
 }
 
 // <function-param> ::= fp <top-level CV-qualifiers> _                                     # L == 0, first parameter
 //                  ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _   # L == 0, second and later parameters
 //                  ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _         # L > 0, first parameter
 //                  ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _   # L > 0, second and later parameters
 
 template <class C>
 const char*
 parse_function_param(const char* first, const char* last, C& db)
 {
     if (last - first >= 3 && *first == 'f')
     {
         if (first[1] == 'p')
         {
             unsigned 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("fp" + typename C::String(t, t1));
+                db.names.push_back(
+                    db.template make<function_param>(string_ref(t, t1)));
                 first = t1+1;
             }
         }
         else if (first[1] == 'L')
         {
             unsigned 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("fp" + typename C::String(t, t1));
+                    db.names.push_back(
+                        db.template make<function_param>(string_ref(t, t1)));
                     first = t1+1;
                 }
             }
         }
     }
     return first;
 }
 
 // sZ <function-param>                                  # size of a function parameter pack
 
 template <class C>
 const char*
 parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& 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() = "sizeof...(" + db.names.back().move_full() + ")";
+            db.names.back() = db.template make<enclosing_expr>(
+                "sizeof...(", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 // te <expression>                                      # typeid (expression)
 // ti <type>                                            # typeid (type)
 
 template <class C>
 const char*
 parse_typeid_expr(const char* first, const char* last, C& 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() = "typeid(" + db.names.back().move_full() + ")";
+            db.names.back() = db.template make<enclosing_expr>(
+                "typeid(", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 // tw <expression>                                      # throw expression
 
 template <class C>
 const char*
 parse_throw_expr(const char* first, const char* last, C& 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() = "throw " + db.names.back().move_full();
+            db.names.back() = db.template make<string_then_expr>("throw ", db.names.back());
             first = t;
         }
     }
     return first;
 }
 
 // ds <expression> <expression>                         # expr.*expr
 
 template <class C>
 const char*
 parse_dot_star_expr(const char* first, const char* last, C& 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 expr = db.names.back().move_full();
+                auto rhs_expr = db.names.back();
                 db.names.pop_back();
-                db.names.back().first += ".*" + expr;
+                db.names.back() = db.template make<member_expr>(
+                    db.names.back(), ".*", rhs_expr);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // <simple-id> ::= <source-name> [ <template-args> ]
 
 template <class C>
 const char*
 parse_simple_id(const char* first, const char* last, C& 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().move_full();
+                auto args = db.names.back();
                 db.names.pop_back();
-                db.names.back().first += std::move(args);
+                db.names.back() =
+                    db.template make<name_with_template_args>(db.names.back(), args);
             }
             first = t1;
         }
         else
             first = t;
     }
     return first;
 }
 
 // <unresolved-type> ::= <template-param>
 //                   ::= <decltype>
 //                   ::= <substitution>
 
 template <class C>
 const char*
 parse_unresolved_type(const char* first, const char* last, C& 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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                 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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                 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().first.insert(0, "std::");
+                        // FIXME: we should model this as a nested_name_type
+                        db.names.back() = db.template make<prefix_type>("std::", db.names.back());
                         db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         first = t;
                     }
                 }
             }
             break;
        }
     }
     return first;
 }
 
 // <destructor-name> ::= <unresolved-type>                               # e.g., ~T or ~decltype(f())
 //                   ::= <simple-id>                                     # e.g., ~A<2*N>
 
 template <class C>
 const char*
 parse_destructor_name(const char* first, const char* last, C& 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().first.insert(0, "~");
+            db.names.back() = db.template make<prefix_type>("~", db.names.back());
             first = t;
         }
     }
     return first;
 }
 
 // <base-unresolved-name> ::= <simple-id>                                # unresolved name
 //          extension     ::= <operator-name>                            # unresolved operator-function-id
 //          extension     ::= <operator-name> <template-args>            # unresolved operator template-id
 //                        ::= on <operator-name>                         # unresolved operator-function-id
 //                        ::= on <operator-name> <template-args>         # unresolved operator template-id
 //                        ::= dn <destructor-name>                       # destructor or pseudo-destructor;
 //                                                                         # e.g. ~X or ~X<N-1>
 
 template <class C>
 const char*
 parse_base_unresolved_name(const char* first, const char* last, C& 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().move_full();
+                        auto args = db.names.back();
                         db.names.pop_back();
-                        db.names.back().first += std::move(args);
+                        db.names.back() =
+                            db.template make<name_with_template_args>(
+                                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().move_full();
+                        auto args = db.names.back();
                         db.names.pop_back();
-                        db.names.back().first += std::move(args);
+                        db.names.back() =
+                            db.template make<name_with_template_args>(
+                                db.names.back(), args);
                     }
                 }
             }
             else
                 first = t;
         }
     }
     return first;
 }
 
 // <unresolved-qualifier-level> ::= <simple-id>
 
 template <class C>
 const char*
 parse_unresolved_qualifier_level(const char* first, const char* last, C& db)
 {
     return parse_simple_id(first, last, db);
 }
 
 // <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>  
 //                                                                       # 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>
 
 template <class C>
 const char*
 parse_unresolved_name(const char* first, const char* last, C& 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().first.insert(0, "::");
+                db.names.back() = db.template make<prefix_type>("::", 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().move_full();
+                    auto args = db.names.back();
                     db.names.pop_back();
-                    db.names.back().first += std::move(args);
+                    db.names.back() = db.template make<name_with_template_args>(
+                        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().move_full();
+                    auto s = db.names.back();
                     db.names.pop_back();
-                    db.names.back().first += "::" + std::move(s);
+                    db.names.back() =
+                        db.template make<qualified_name>(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().move_full();
+                auto s = db.names.back();
                 db.names.pop_back();
-                db.names.back().first += "::" + std::move(s);
+                db.names.back() =
+                    db.template make<qualified_name>(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().move_full();
+                        auto args = db.names.back();
                         db.names.pop_back();
-                        db.names.back().first += std::move(args);
+                        db.names.back() =
+                            db.template make<name_with_template_args>(
+                                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().move_full();
+                    auto s = db.names.back();
                     db.names.pop_back();
-                    db.names.back().first += "::" + std::move(s);
+                    db.names.back() =
+                        db.template make<qualified_name>(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().first.insert(0, "::");
+                        db.names.back() =
+                            db.template make<global_qualified_name>(
+                                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().move_full();
+                        auto s = db.names.back();
                         db.names.pop_back();
-                        db.names.back().first += "::" + std::move(s);
+                        db.names.back() = db.template make<qualified_name>(
+                            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().move_full();
+                    auto s = db.names.back();
                     db.names.pop_back();
-                    db.names.back().first += "::" + std::move(s);
+                    db.names.back() =
+                        db.template make<qualified_name>(db.names.back(), s);
                     first = t1;
                 }
             }
         }
     }
     return first;
 }
 
 // dt <expression> <unresolved-name>                    # expr.name
 
 template <class C>
 const char*
 parse_dot_expr(const char* first, const char* last, C& 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().move_full();
+                auto name = db.names.back();
                 db.names.pop_back();
                 if (db.names.empty())
                     return first;
-                db.names.back().first += "." + name;
+                db.names.back() = db.template make<member_expr>(db.names.back(), ".", name);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 // cl <expression>+ E                                   # call
 
 template <class C>
 const char*
 parse_call_expr(const char* first, const char* last, C& db)
 {
     if (last - first >= 4 && first[0] == 'c' && first[1] == 'l')
     {
         const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
+        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')
         {
-            if (t == last)
-                return first;
-            if (db.names.empty())
-                return first;
-            db.names.back().first += db.names.back().second;
-            db.names.back().second = typename C::String();
-            db.names.back().first.append("(");
-            bool first_expr = true;
-            while (*t != 'E')
-            {
-                const char* t1 = parse_expression(t, last, db);
-                if (t1 == t || t1 == last)
-                    return first;
-                if (db.names.empty())
-                    return first;
-                auto tmp = db.names.back().move_full();
-                db.names.pop_back();
-                if (!tmp.empty())
-                {
-                    if (db.names.empty())
-                        return first;
-                    if (!first_expr)
-                    {
-                        db.names.back().first.append(", ");
-                        first_expr = false;
-                    }
-                    db.names.back().first.append(tmp);
-                }
-                t = t1;
-            }
-            ++t;
-            if (db.names.empty())
+            const char* t1 = parse_expression(t, last, db);
+            if (t1 == last || t1 == t)
                 return first;
-            db.names.back().first.append(")");
-            first = t;
+            t = t1;
         }
+        if (db.names.size() < args_begin)
+            return first;
+        ++t;
+        call_expr* the_call = db.template make<call_expr>(
+            callee, db.names.size() - args_begin,
+            db.template make_array<node*>(
+                db.names.begin() + (long)args_begin,
+                db.names.end()));
+        db.names.erase(db.names.begin() + (long)args_begin, db.names.end());
+        db.names.push_back(the_call);
+        first = t;
     }
     return first;
 }
 
 // [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
 
 template <class C>
 const char*
 parse_new_expr(const char* first, const char* last, C& db)
 {
+    // FIXME: we never insert a "new"!
+
     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;
             bool has_expr_list = false;
             bool first_expr = true;
             while (*t != '_')
             {
                 const char* t1 = parse_expression(t, last, db);
                 if (t1 == t || t1 == last)
                     return first;
                 has_expr_list = true;
                 if (!first_expr)
                 {
                     if (db.names.empty())
                         return first;
-                    auto tmp = db.names.back().move_full();
+                    auto tmp = db.names.back();
                     db.names.pop_back();
-                    if (!tmp.empty())
+                    // if (!tmp.empty())
                     {
                         if (db.names.empty())
                             return first;
-                        db.names.back().first.append(", ");
-                        db.names.back().first.append(tmp);
+                        db.names.back() = db.template make<then_expr>(
+                            db.names.back(),
+                            db.template make<string_then_expr>(", ", tmp));
                         first_expr = false;
                     }
                 }
                 t = t1;
             }
             ++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;
                 first_expr = true;
                 while (*t != 'E')
                 {
                     t1 = parse_expression(t, last, db);
                     if (t1 == t || t1 == last)
                         return first;
                     if (!first_expr)
                     {
                         if (db.names.empty())
                             return first;
-                        auto tmp = db.names.back().move_full();
+                        auto tmp = db.names.back();
                         db.names.pop_back();
-                        if (!tmp.empty())
+                        // if (!tmp.empty())
                         {
                             if (db.names.empty())
                                 return first;
-                            db.names.back().first.append(", ");
-                            db.names.back().first.append(tmp);
+                            db.names.back() = db.template make<then_expr>(
+                                db.names.back(),
+                                db.template make<string_then_expr>(", ", tmp));
                             first_expr = false;
                         }
                     }
                     t = t1;
                 }
             }
             if (*t != 'E')
                 return first;
-            typename C::String init_list;
+            node* init_list = nullptr;
             if (has_init)
             {
                 if (db.names.empty())
                     return first;
-                init_list = db.names.back().move_full();
+                init_list = db.names.back();
                 db.names.pop_back();
             }
             if (db.names.empty())
                 return first;
-            auto type = db.names.back().move_full();
+            auto type = db.names.back();
             db.names.pop_back();
-            typename C::String expr_list;
+            node* expr_list = nullptr;
             if (has_expr_list)
             {
                 if (db.names.empty())
                     return first;
-                expr_list = db.names.back().move_full();
+                expr_list = db.names.back();
                 db.names.pop_back();
             }
-            typename C::String r;
+            // FIXME: This is terrible
+            node* r = db.template make<string_type>("");
             if (parsed_gs)
-                r = "::";
+                r = db.template make<then_expr>(r, db.template make<string_type>("::"));
             if (is_array)
-                r += "[] ";
+                r = db.template make<then_expr>(r, db.template make<string_type>("[] "));
             else
-                r += " ";
+                r = db.template make<then_expr>(r, db.template make<string_type>(" "));
             if (has_expr_list)
-                r += "(" + expr_list + ") ";
-            r += type;
+                r = db.template make<then_expr>(
+                    r, db.template make<enclosing_expr>("(", expr_list, ") "));
+            r = db.template make<then_expr>(r, type);
             if (has_init)
-                r += " (" + init_list + ")";
-            db.names.push_back(std::move(r));
+                r = db.template make<then_expr>(
+                    r, db.template make<enclosing_expr>("(", init_list, ")"));
+            db.names.push_back(r);
             first = t+1;
         }
     }
     return first;
 }
 
 // cv <type> <expression>                               # conversion with one argument
 // cv <type> _ <expression>* E                          # conversion with a different number of arguments
 
 template <class C>
 const char*
 parse_conversion_expr(const char* first, const char* last, C& db)
 {
     if (last - first >= 3 && first[0] == 'c' && first[1] == 'v')
     {
         bool try_to_parse_template_args = db.try_to_parse_template_args;
         db.try_to_parse_template_args = false;
+        size_t type_begin = db.names.size();
         const char* t = parse_type(first+2, last, db);
         db.try_to_parse_template_args = try_to_parse_template_args;
         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')
-                    db.names.emplace_back();
-                else
+                if (*t != 'E')
                 {
-                    bool first_expr = true;
                     while (*t != 'E')
                     {
                         const char* t1 = parse_expression(t, last, db);
                         if (t1 == t || t1 == last)
                             return first;
-                        if (!first_expr)
-                        {
-                            if (db.names.empty())
-                                return first;
-                            auto tmp = db.names.back().move_full();
-                            db.names.pop_back();
-                            if (!tmp.empty())
-                            {
-                                if (db.names.empty())
-                                    return first;
-                                db.names.back().first.append(", ");
-                                db.names.back().first.append(tmp);
-                                first_expr = false;
-                            }
-                        }
                         t = t1;
                     }
                 }
                 ++t;
             }
-            if (db.names.size() < 2)
+            if (db.names.size() < expr_list_begin)
                 return first;
-            auto tmp = db.names.back().move_full();
-            db.names.pop_back();
-            db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")";
+            node** expressions = db.template make_array<node*>(
+                db.names.begin() + (long)expr_list_begin, db.names.end());
+            node** types = db.template make_array<node*>(
+                db.names.begin() + (long)type_begin,
+                db.names.begin() + (long)expr_list_begin);
+            auto* conv_expr = db.template make<then_expr>(
+                db.template make<expr_list>(expr_list_begin - type_begin, types),
+                db.template make<expr_list>(db.names.size() - expr_list_begin,
+                                   expressions));
+            db.names.erase(db.names.begin() + (long)type_begin, db.names.end());
+            db.names.push_back(conv_expr);
             first = t;
         }
     }
     return first;
 }
 
 // pt <expression> <expression>                    # expr->name
 
 template <class C>
 const char*
 parse_arrow_expr(const char* first, const char* last, C& 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().move_full();
+                auto tmp = db.names.back();
                 db.names.pop_back();
-                db.names.back().first += "->";
-                db.names.back().first += tmp;
+                db.names.back() = db.template make<member_expr>(
+                    db.names.back(), "->", tmp);
                 first = t1;
             }
         }
     }
     return first;
 }
 
 //  <ref-qualifier> ::= R                   # & ref-qualifier
 //  <ref-qualifier> ::= O                   # && ref-qualifier
 
 // <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
 
 template <class C>
 const char*
 parse_function_type(const char* first, const char* last, C& 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)
+            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;
-                typename C::String sig("(");
                 int ref_qual = 0;
                 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')
                     {
                         ref_qual = 1;
                         ++t;
                         continue;
                     }
                     if (*t == 'O' && t+1 != last && t[1] == 'E')
                     {
                         ref_qual = 2;
                         ++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)
+                    if (t1 == t || t1 == last || k1 < k0)
                         return first;
-                    for (size_t k = k0; k < k1; ++k)
-                    {
-                        if (sig.size() > 1)
-                            sig += ", ";
-                        sig += db.names[k].move_full();
-                    }
-                    for (size_t k = k0; k < k1; ++k)
-                        db.names.pop_back();
                     t = t1;
                 }
-                sig += ")";
-                switch (ref_qual)
-                {
-                case 1:
-                    sig += " &";
-                    break;
-                case 2:
-                    sig += " &&";
-                    break;
-                }
                 if (db.names.empty())
                     return first;
-                db.names.back().first += " ";
-                db.names.back().second.insert(0, sig);
+                node** params = db.template make_array<node*>(
+                    db.names.begin() + (long)params_begin, db.names.end());
+                node* fty = db.template make<function_type>(
+                    ret_type,
+                    static_cast<unsigned>(db.names.size() - params_begin),
+                    params);
+                db.names.erase(
+                    db.names.begin() + (long)params_begin, db.names.end());
+
+                if (ref_qual) {
+                    qual_type* qty = db.template make<qual_type>(fty, 0u);
+                    if (ref_qual == 1)
+                        qty->add_qualifiers(8);
+                    else if (ref_qual == 2)
+                        qty->add_qualifiers(16);
+                    fty = qty;
+                }
+                db.names.push_back(fty);
                 first = t;
             }
         }
     }
     return first;
 }
 
 // <pointer-to-member-type> ::= M <class type> <member type>
 
 template <class C>
 const char*
 parse_pointer_to_member_type(const char* first, const char* last, C& 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 class_type = std::move(db.names.back());
-                if (!func.second.empty() && func.second.front() == '(')
-                {
-                    db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*";
-                    db.names.back().second = ")" + std::move(func.second);
-                }
-                else
-                {
-                    db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*";
-                    db.names.back().second = std::move(func.second);
-                }
+                db.names.back() =
+                    db.template make<pointer_to_member_type>(class_type, func);
                 first = t2;
             }
         }
     }
     return first;
 }
 
 // <array-type> ::= A <positive dimension number> _ <element type>
 //              ::= A [<dimension expression>] _ <element type>
 
 template <class C>
 const char*
 parse_array_type(const char* first, const char* last, C& 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;
-                if (db.names.back().second.substr(0, 2) == " [")
-                    db.names.back().second.erase(0, 1);
-                db.names.back().second.insert(0, " []");
+                db.names.back() = db.template make<array_type>(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;
-                    if (db.names.back().second.substr(0, 2) == " [")
-                        db.names.back().second.erase(0, 1);
-                    db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]");
+                    db.names.back() =
+                        db.template make<array_type>(db.names.back(),
+                                            string_ref(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 type = std::move(db.names.back());
+                    auto base_type = std::move(db.names.back());
                     db.names.pop_back();
-                    auto expr = std::move(db.names.back());
-                    db.names.back().first = std::move(type.first);
-                    if (type.second.substr(0, 2) == " [")
-                        type.second.erase(0, 1);
-                    db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second);
+                    auto dimension_expr = std::move(db.names.back());
+                    db.names.back() =
+                        db.template make<array_type>(base_type, dimension_expr);
                     first = t2;
                 }
             }
         }
     }
     return first;
 }
 
 // <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)
 //             ::= DT <expression> E  # decltype of an expression (C++0x)
 
 template <class C>
 const char*
 parse_decltype(const char* first, const char* last, C& 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() = "decltype(" + db.names.back().move_full() + ")";
+                    db.names.back() = db.template make<enclosing_expr>(
+                        "decltype(", db.names.back(), ")");
                     first = t+1;
                 }
             }
             break;
         }
     }
     return first;
 }
 
 // extension:
 // <vector-type>           ::= Dv <positive dimension number> _
 //                                    <extended element type>
 //                         ::= Dv [<dimension expression>] _ <element type>
 // <extended element type> ::= <element type>
 //                         ::= p # AltiVec vector pixel
 
 template <class C>
 const char*
 parse_vector_type(const char* first, const char* last, C& 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().first += " vector[" + typename C::String(num, sz) + "]";
+                        db.names.back() =
+                            db.template make<vector_type>(db.names.back(),
+                                                 string_ref(num, num + sz));
                         first = t1;
                     }
                 }
                 else
                 {
                     ++t;
-                    db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]");
+                    db.names.push_back(
+                        db.template make<vector_type>(string_ref(num, num + sz)));
                     first = t;
                 }
             }
         }
         else
         {
-            typename C::String num;
+            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().move_full();
+                    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;
-                    db.names.back().first += " vector[" + num + "]";
+                    if (num)
+                        db.names.back() =
+                            db.template make<vector_type>(db.names.back(), num);
+                    else
+                        db.names.back() =
+                            db.template make<vector_type>(db.names.back());
                     first = t;
-                }
+                } else if (num)
+                    db.names.push_back(num);
             }
         }
     }
     return first;
 }
 
 // <type> ::= <builtin-type>
 //        ::= <function-type>
 //        ::= <class-enum-type>
 //        ::= <array-type>
 //        ::= <pointer-to-member-type>
 //        ::= <template-param>
 //        ::= <template-template-param> <template-args>
 //        ::= <decltype>
 //        ::= <substitution>
 //        ::= <CV-qualifiers> <type>
 //        ::= P <type>        # pointer-to
 //        ::= R <type>        # reference-to
 //        ::= O <type>        # rvalue reference-to (C++0x)
 //        ::= C <type>        # complex pair (C 2000)
 //        ::= G <type>        # imaginary (C 2000)
 //        ::= Dp <type>       # pack expansion (C++0x)
 //        ::= U <source-name> <type>  # vendor extended type qualifier
 // extension := U <objc-name> <objc-type>  # objc-type<identifier>
 // extension := <vector-type> # <vector-type> starts with Dv
 
 // <objc-name> ::= <k0 number> objcproto <k1 number> <identifier>  # k0 = 9 + <number of digits in k1> + k1
 // <objc-type> := <source-name>  # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
 
 template <class C>
 const char*
 parse_type(const char* first, const char* last, C& db)
 {
     if (first != last)
     {
         switch (*first)
         {
             case 'r':
             case 'V':
             case 'K':
               {
                 unsigned cv = 0;
                 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.pop_back();
                         db.subs.emplace_back(db.names.get_allocator());
                         for (size_t k = k0; k < k1; ++k)
                         {
-                            if (is_function)
-                            {
-                                size_t p = db.names[k].second.size();
-                                if (db.names[k].second[p - 2] == '&' &&
-                                    db.names[k].second[p - 1] == '&')
-                                    p -= 2;
-                                else if (db.names[k].second.back() == '&')
-                                    p -= 1;
-                                if (cv & 1)
-                                {
-                                    db.names[k].second.insert(p, " const");
-                                    p += 6;
-                                }
-                                if (cv & 2)
-                                {
-                                    db.names[k].second.insert(p, " volatile");
-                                    p += 9;
-                                }
-                                if (cv & 4)
-                                    db.names[k].second.insert(p, " restrict");
-                            }
-                            else
-                            {
-                                if (cv & 1)
-                                    db.names[k].first.append(" const");
-                                if (cv & 2)
-                                    db.names[k].first.append(" volatile");
-                                if (cv & 4)
-                                    db.names[k].first.append(" restrict");
+                            if (cv) {
+                                if (db.names[k]->is_qual_type())
+                                    static_cast<qual_type*>(db.names[k])->add_qualifiers(cv);
+                                else
+                                    db.names[k] =
+                                        db.template make<qual_type>(db.names[k], cv);
                             }
                             db.subs.back().push_back(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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         }
                         break;
                     case 'C':
                         t = parse_type(first+1, last, db);
                         if (t != first+1)
                         {
                             if (db.names.empty())
                                 return first;
-                            db.names.back().first.append(" complex");
+                            db.names.back() = db.template make<postfix_qualified_type>(
+                                db.names.back(), " complex");
                             first = t;
                             db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         }
                         break;
                     case 'F':
                         t = parse_function_type(first, last, db);
                         if (t != first)
                         {
                             if (db.names.empty())
                                 return first;
                             first = t;
                             db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         }
                         break;
                     case 'G':
                         t = parse_type(first+1, last, db);
                         if (t != first+1)
                         {
                             if (db.names.empty())
                                 return first;
-                            db.names.back().first.append(" imaginary");
+                            db.names.back() = db.template make<postfix_qualified_type>(
+                                db.names.back(), " imaginary");
                             first = t;
                             db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         }
                         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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                         }
                         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.emplace_back(db.names.get_allocator());
                             for (size_t k = k0; k < k1; ++k)
                             {
-                                if (db.names[k].second.substr(0, 2) == " [")
-                                {
-                                    db.names[k].first += " (";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                else if (!db.names[k].second.empty() &&
-                                          db.names[k].second.front() == '(')
-                                {
-                                    db.names[k].first += "(";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                db.names[k].first.append("&&");
+                                db.names[k] =
+                                    db.template make<rvalue_reference_type>(db.names[k]);
                                 db.subs.back().push_back(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.emplace_back(db.names.get_allocator());
                             for (size_t k = k0; k < k1; ++k)
                             {
-                                if (db.names[k].second.substr(0, 2) == " [")
-                                {
-                                    db.names[k].first += " (";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                else if (!db.names[k].second.empty() &&
-                                          db.names[k].second.front() == '(')
-                                {
-                                    db.names[k].first += "(";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<")
-                                {
-                                    db.names[k].first.append("*");
-                                }
+                                if (db.names[k]->is_objcproto_name() &&
+                                    static_cast<objcproto_name*>(db.names[k])->should_set_is_pointer())
+                                    static_cast<objcproto_name*>(db.names[k])->set_is_pointer();
                                 else
-                                {
-                                    db.names[k].first.replace(0, 11, "id");
-                                }
+                                    db.names[k] = db.template make<pointer_type>(db.names[k]);
                                 db.subs.back().push_back(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.emplace_back(db.names.get_allocator());
                             for (size_t k = k0; k < k1; ++k)
                             {
-                                if (db.names[k].second.substr(0, 2) == " [")
-                                {
-                                    db.names[k].first += " (";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                else if (!db.names[k].second.empty() &&
-                                          db.names[k].second.front() == '(')
-                                {
-                                    db.names[k].first += "(";
-                                    db.names[k].second.insert(0, ")");
-                                }
-                                db.names[k].first.append("&");
+                                db.names[k] =
+                                    db.template make<lvalue_reference_type>(db.names[k]);
                                 db.subs.back().push_back(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.emplace_back(db.names.get_allocator());
                             for (size_t k = k0; k < k1; ++k)
                                 db.subs.back().push_back(db.names[k]);
                             if (db.try_to_parse_template_args && k1 == k0+1)
                             {
                                 const char* t1 = parse_template_args(t, last, db);
                                 if (t1 != t)
                                 {
-                                    auto args = db.names.back().move_full();
+                                    auto args = db.names.back();
                                     db.names.pop_back();
-                                    db.names.back().first += std::move(args);
-                                    db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
+                                    db.names.back() = db.template make<
+                                        name_with_template_args>(
+                                        db.names.back(), args);
+                                    db.subs.push_back(typename C::sub_type(
+                                        1, db.names.back(),
+                                        db.names.get_allocator()));
                                     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().move_full();
+                                    auto type = db.names.back();
                                     db.names.pop_back();
-                                    if (db.names.back().first.substr(0, 9) != "objcproto")
+                                    if (!db.names.back()->is_name_type() ||
+                                        !static_cast<name_type*>(db.names.back())->get_name().starts_with("objcproto"))
                                     {
-                                        db.names.back() = type + " " + db.names.back().move_full();
+                                        db.names.back() = db.template make<vendor_ext_qual_type>(type, db.names.back());
                                     }
                                     else
                                     {
-                                        auto proto = db.names.back().move_full();
+                                        auto* proto = static_cast<name_type*>(db.names.back());
                                         db.names.pop_back();
-                                        t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db);
-                                        if (t != proto.data() + 9)
+                                        t = parse_source_name(proto->get_name().begin() + 9, proto->get_name().end(), db);
+                                        if (t != proto->get_name().begin() + 9)
                                         {
-                                            db.names.back() = type + "<" + db.names.back().move_full() + ">";
+                                            db.names.back() = db.template make<objcproto_name>(type, db.names.back());
                                         }
                                         else
                                         {
-                                            db.names.push_back(type + " " + proto);
+                                            db.names.push_back(db.template make<vendor_ext_qual_type>(type, proto));
                                         }
                                     }
                                     db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                     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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                 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>.
                                 t = parse_template_args(first, last, db);
                                 if (t != first)
                                 {
                                     if (db.names.size() < 2)
                                         return first;
-                                    auto template_args = db.names.back().move_full();
+                                    auto template_args = db.names.back();
                                     db.names.pop_back();
-                                    db.names.back().first += template_args;
+                                    db.names.back() = db.template make<
+                                        name_with_template_args>(
+                                        db.names.back(), template_args);
                                     // Need to create substitution for <template-template-param> <template-args>
                                     db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                     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.emplace_back(db.names.get_allocator());
                                     for (size_t k = k0; k < k1; ++k)
                                         db.subs.back().push_back(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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                     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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                     first = t;
                                     return first;
                                 }
                                 break;
                             }
                         }
                         [[gnu::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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                                 first = t;
                             }
                         }
                         break;
                     }
               }
                 break;
             }
         }
     }
     return first;
 }
 
 //   <operator-name>
 //                   ::= aa    # &&            
 //                   ::= ad    # & (unary)     
 //                   ::= an    # &             
 //                   ::= aN    # &=            
 //                   ::= aS    # =             
 //                   ::= cl    # ()            
 //                   ::= cm    # ,             
 //                   ::= co    # ~             
 //                   ::= cv <type>    # (cast)        
 //                   ::= da    # delete[]      
 //                   ::= de    # * (unary)     
 //                   ::= dl    # delete        
 //                   ::= dv    # /             
 //                   ::= dV    # /=            
 //                   ::= eo    # ^             
 //                   ::= eO    # ^=            
 //                   ::= eq    # ==            
 //                   ::= ge    # >=            
 //                   ::= gt    # >             
 //                   ::= ix    # []            
 //                   ::= le    # <=            
 //                   ::= li <source-name>  # operator ""
 //                   ::= ls    # <<            
 //                   ::= lS    # <<=           
 //                   ::= lt    # <             
 //                   ::= mi    # -             
 //                   ::= mI    # -=            
 //                   ::= ml    # *             
 //                   ::= mL    # *=            
 //                   ::= mm    # -- (postfix in <expression> context)           
 //                   ::= na    # new[]
 //                   ::= ne    # !=            
 //                   ::= ng    # - (unary)     
 //                   ::= nt    # !             
 //                   ::= nw    # new           
 //                   ::= oo    # ||            
 //                   ::= or    # |             
 //                   ::= oR    # |=            
 //                   ::= pm    # ->*           
 //                   ::= pl    # +             
 //                   ::= pL    # +=            
 //                   ::= pp    # ++ (postfix in <expression> context)
 //                   ::= ps    # + (unary)
 //                   ::= pt    # ->            
 //                   ::= qu    # ?             
 //                   ::= rm    # %             
 //                   ::= rM    # %=            
 //                   ::= rs    # >>            
 //                   ::= rS    # >>=           
 //                   ::= v <digit> <source-name>        # vendor extended operator
 
 template <class C>
 const char*
 parse_operator_name(const char* first, const char* last, C& db)
 {
     if (last - first >= 2)
     {
         switch (first[0])
         {
         case 'a':
             switch (first[1])
             {
             case 'a':
-                db.names.push_back("operator&&");
+                db.names.push_back(db.template make<name_type>("operator&&"));
                 first += 2;
                 break;
             case 'd':
             case 'n':
-                db.names.push_back("operator&");
+                db.names.push_back(db.template make<name_type>("operator&"));
                 first += 2;
                 break;
             case 'N':
-                db.names.push_back("operator&=");
+                db.names.push_back(db.template make<name_type>("operator&="));
                 first += 2;
                 break;
             case 'S':
-                db.names.push_back("operator=");
+                db.names.push_back(db.template make<name_type>("operator="));
                 first += 2;
                 break;
             }
             break;
         case 'c':
             switch (first[1])
             {
             case 'l':
-                db.names.push_back("operator()");
+                db.names.push_back(db.template make<name_type>("operator()"));
                 first += 2;
                 break;
             case 'm':
-                db.names.push_back("operator,");
+                db.names.push_back(db.template make<name_type>("operator,"));
                 first += 2;
                 break;
             case 'o':
-                db.names.push_back("operator~");
+                db.names.push_back(db.template make<name_type>("operator~"));
                 first += 2;
                 break;
             case 'v':
                 {
                     bool try_to_parse_template_args = db.try_to_parse_template_args;
                     db.try_to_parse_template_args = false;
                     const char* t = parse_type(first+2, last, db);
                     db.try_to_parse_template_args = try_to_parse_template_args;
                     if (t != first+2)
                     {
                         if (db.names.empty())
                             return first;
-                        db.names.back().first.insert(0, "operator ");
+                        db.names.back() =
+                            db.template make<conversion_operator_type>(db.names.back());
                         db.parsed_ctor_dtor_cv = true;
                         first = t;
                     }
                 }
                 break;
             }
             break;
         case 'd':
             switch (first[1])
             {
             case 'a':
-                db.names.push_back("operator delete[]");
+                db.names.push_back(db.template make<name_type>("operator delete[]"));
                 first += 2;
                 break;
             case 'e':
-                db.names.push_back("operator*");
+                db.names.push_back(db.template make<name_type>("operator*"));
                 first += 2;
                 break;
             case 'l':
-                db.names.push_back("operator delete");
+                db.names.push_back(db.template make<name_type>("operator delete"));
                 first += 2;
                 break;
             case 'v':
-                db.names.push_back("operator/");
+                db.names.push_back(db.template make<name_type>("operator/"));
                 first += 2;
                 break;
             case 'V':
-                db.names.push_back("operator/=");
+                db.names.push_back(db.template make<name_type>("operator/="));
                 first += 2;
                 break;
             }
             break;
         case 'e':
             switch (first[1])
             {
             case 'o':
-                db.names.push_back("operator^");
+                db.names.push_back(db.template make<name_type>("operator^"));
                 first += 2;
                 break;
             case 'O':
-                db.names.push_back("operator^=");
+                db.names.push_back(db.template make<name_type>("operator^="));
                 first += 2;
                 break;
             case 'q':
-                db.names.push_back("operator==");
+                db.names.push_back(db.template make<name_type>("operator=="));
                 first += 2;
                 break;
             }
             break;
         case 'g':
             switch (first[1])
             {
             case 'e':
-                db.names.push_back("operator>=");
+                db.names.push_back(db.template make<name_type>("operator>="));
                 first += 2;
                 break;
             case 't':
-                db.names.push_back("operator>");
+                db.names.push_back(db.template make<name_type>("operator>"));
                 first += 2;
                 break;
             }
             break;
         case 'i':
             if (first[1] == 'x')
             {
-                db.names.push_back("operator[]");
+                db.names.push_back(db.template make<name_type>("operator[]"));
                 first += 2;
             }
             break;
         case 'l':
             switch (first[1])
             {
             case 'e':
-                db.names.push_back("operator<=");
+                db.names.push_back(db.template make<name_type>("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().first.insert(0, "operator\"\" ");
+                        // FIXME
+                        db.names.back() = db.template make<string_then_expr>("operator\"\" ", db.names.back());
                         first = t;
                     }
                 }
                 break;
             case 's':
-                db.names.push_back("operator<<");
+                db.names.push_back(db.template make<name_type>("operator<<"));
                 first += 2;
                 break;
             case 'S':
-                db.names.push_back("operator<<=");
+                db.names.push_back(db.template make<name_type>("operator<<="));
                 first += 2;
                 break;
             case 't':
-                db.names.push_back("operator<");
+                db.names.push_back(db.template make<name_type>("operator<"));
                 first += 2;
                 break;
             }
             break;
         case 'm':
             switch (first[1])
             {
             case 'i':
-                db.names.push_back("operator-");
+                db.names.push_back(db.template make<name_type>("operator-"));
                 first += 2;
                 break;
             case 'I':
-                db.names.push_back("operator-=");
+                db.names.push_back(db.template make<name_type>("operator-="));
                 first += 2;
                 break;
             case 'l':
-                db.names.push_back("operator*");
+                db.names.push_back(db.template make<name_type>("operator*"));
                 first += 2;
                 break;
             case 'L':
-                db.names.push_back("operator*=");
+                db.names.push_back(db.template make<name_type>("operator*="));
                 first += 2;
                 break;
             case 'm':
-                db.names.push_back("operator--");
+                db.names.push_back(db.template make<name_type>("operator--"));
                 first += 2;
                 break;
             }
             break;
         case 'n':
             switch (first[1])
             {
             case 'a':
-                db.names.push_back("operator new[]");
+                db.names.push_back(db.template make<name_type>("operator new[]"));
                 first += 2;
                 break;
             case 'e':
-                db.names.push_back("operator!=");
+                db.names.push_back(db.template make<name_type>("operator!="));
                 first += 2;
                 break;
             case 'g':
-                db.names.push_back("operator-");
+                db.names.push_back(db.template make<name_type>("operator-"));
                 first += 2;
                 break;
             case 't':
-                db.names.push_back("operator!");
+                db.names.push_back(db.template make<name_type>("operator!"));
                 first += 2;
                 break;
             case 'w':
-                db.names.push_back("operator new");
+                db.names.push_back(db.template make<name_type>("operator new"));
                 first += 2;
                 break;
             }
             break;
         case 'o':
             switch (first[1])
             {
             case 'o':
-                db.names.push_back("operator||");
+                db.names.push_back(db.template make<name_type>("operator||"));
                 first += 2;
                 break;
             case 'r':
-                db.names.push_back("operator|");
+                db.names.push_back(db.template make<name_type>("operator|"));
                 first += 2;
                 break;
             case 'R':
-                db.names.push_back("operator|=");
+                db.names.push_back(db.template make<name_type>("operator|="));
                 first += 2;
                 break;
             }
             break;
         case 'p':
             switch (first[1])
             {
             case 'm':
-                db.names.push_back("operator->*");
+                db.names.push_back(db.template make<name_type>("operator->*"));
                 first += 2;
                 break;
             case 'l':
-                db.names.push_back("operator+");
+                db.names.push_back(db.template make<name_type>("operator+"));
                 first += 2;
                 break;
             case 'L':
-                db.names.push_back("operator+=");
+                db.names.push_back(db.template make<name_type>("operator+="));
                 first += 2;
                 break;
             case 'p':
-                db.names.push_back("operator++");
+                db.names.push_back(db.template make<name_type>("operator++"));
                 first += 2;
                 break;
             case 's':
-                db.names.push_back("operator+");
+                db.names.push_back(db.template make<name_type>("operator+"));
                 first += 2;
                 break;
             case 't':
-                db.names.push_back("operator->");
+                db.names.push_back(db.template make<name_type>("operator->"));
                 first += 2;
                 break;
             }
             break;
         case 'q':
             if (first[1] == 'u')
             {
-                db.names.push_back("operator?");
+                db.names.push_back(db.template make<name_type>("operator?"));
                 first += 2;
             }
             break;
         case 'r':
             switch (first[1])
             {
             case 'm':
-                db.names.push_back("operator%");
+                db.names.push_back(db.template make<name_type>("operator%"));
                 first += 2;
                 break;
             case 'M':
-                db.names.push_back("operator%=");
+                db.names.push_back(db.template make<name_type>("operator%="));
                 first += 2;
                 break;
             case 's':
-                db.names.push_back("operator>>");
+                db.names.push_back(db.template make<name_type>("operator>>"));
                 first += 2;
                 break;
             case 'S':
-                db.names.push_back("operator>>=");
+                db.names.push_back(db.template make<name_type>("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().first.insert(0, "operator ");
+                    db.names.back() =
+                        db.template make<conversion_operator_type>(db.names.back());
                     first = t;
                 }
             }
             break;
         }
     }
     return first;
 }
 
 template <class C>
 const char*
-parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db)
+parse_integer_literal(const char* first, const char* last, string_ref lit, C& db)
 {
     const char* t = parse_number(first, last);
     if (t != first && t != last && *t == 'E')
     {
-        if (lit.size() > 3)
-            db.names.push_back("(" + lit + ")");
-        else
-            db.names.emplace_back();
-        if (*first == 'n')
-        {
-            db.names.back().first += '-';
-            ++first;
-        }
-        db.names.back().first.append(first, t);
-        if (lit.size() <= 3)
-            db.names.back().first += lit;
+        db.names.push_back(
+            db.template make<integer_expr>(lit, string_ref(first, t)));
         first = t+1;
     }
     return first;
 }
 
 // <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
 
 template <class C>
 const char*
 parse_expr_primary(const char* first, const char* last, C& 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("false");
+                    db.names.push_back(db.template make<bool_expr>(0));
                     first += 4;
                     break;
                 case '1':
-                    db.names.push_back("true");
+                    db.names.push_back(db.template make<bool_expr>(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.names.back().move_full() + ")" + typename C::String(t, n);
+                            db.names.back() = db.template make<something_fixme_expr>(
+                                db.names.back(), string_ref(t, n));
                             first = n+1;
                             break;
                         }
                     }
                     else
                     {
                         first = t+1;
                         break;
                     }
                 }
             }
         }
     }
     return first;
 }
 
-template <class String>
-String
-base_name(String& s)
+node* maybe_change_special_sub_name(node* inp, Db& db)
 {
-    if (s.empty())
-        return s;
-    if (s == "std::string")
-    {
-        s = "std::basic_string<char, std::char_traits<char>, std::allocator<char> >";
-        return "basic_string";
-    }
-    if (s == "std::istream")
-    {
-        s = "std::basic_istream<char, std::char_traits<char> >";
-        return "basic_istream";
-    }
-    if (s == "std::ostream")
-    {
-        s = "std::basic_ostream<char, std::char_traits<char> >";
-        return "basic_ostream";
-    }
-    if (s == "std::iostream")
-    {
-        s = "std::basic_iostream<char, std::char_traits<char> >";
-        return "basic_iostream";
-    }
-    const char* const pf = s.data();
-    const char* pe = pf + s.size();
-    if (pe[-1] == '>')
-    {
-        unsigned c = 1;
-        while (true)
-        {
-            if (--pe == pf)
-                return String();
-            if (pe[-1] == '<')
-            {
-                if (--c == 0)
-                {
-                    --pe;
-                    break;
-                }
-            }
-            else if (pe[-1] == '>')
-                ++c;
-        }
-    }
-    if (pe - pf <= 1)
-      return String();
-    const char* p0 = pe - 1;
-    for (; p0 != pf; --p0)
+    if (!inp->is_special_substitution())
+        return inp;
+    auto kind = static_cast<special_substitution*>(inp)->k;
+    switch (kind)
     {
-        if (*p0 == ':')
-        {
-            ++p0;
-            break;
-        }
-        if (!isalpha(*p0) && !isdigit(*p0) && *p0 != '_')
-        {
-            return String();
-        }
+    case special_sub_kind::string:
+    case special_sub_kind::istream:
+    case special_sub_kind::ostream:
+    case special_sub_kind::iostream:
+        return db.make<expanded_special_substitution>(kind);
+    default:
+        break;
     }
-    return String(p0, pe);
+    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    # ?
 
 template <class C>
 const char*
 parse_ctor_dtor_name(const char* first, const char* last, C& 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.push_back(base_name(db.names.back().first));
+                db.names.back() =
+                    maybe_change_special_sub_name(db.names.back(), db);
+                db.names.push_back(
+                    db.template make<ctor_dtor_name>(db.names.back(), false));
                 first += 2;
                 db.parsed_ctor_dtor_cv = 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("~" + base_name(db.names.back().first));
+                db.names.push_back(
+                    db.template make<ctor_dtor_name>(db.names.back(), true));
                 first += 2;
                 db.parsed_ctor_dtor_cv = true;
                 break;
             }
             break;
         }
     }
     return first;
 }
 
 // <unnamed-type-name> ::= Ut [ <nonnegative number> ] _
 //                     ::= <closure-type-name>
 // 
 // <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ 
 // 
 // <lambda-sig> ::= <parameter type>+  # Parameter types or "v" if the lambda has no parameters
 
 template <class C>
 const char*
 parse_unnamed_type_name(const char* first, const char* last, C& db)
 {
     if (last - first > 2 && first[0] == 'U')
     {
         char type = first[1];
         switch (type)
         {
         case 't':
           {
-            db.names.push_back(typename C::String("'unnamed"));
             const char* t0 = first+2;
             if (t0 == last)
-            {
-                db.names.pop_back();
                 return first;
-            }
+            string_ref count;
             if (std::isdigit(*t0))
             {
                 const char* t1 = t0 + 1;
                 while (t1 != last && std::isdigit(*t1))
                     ++t1;
-                db.names.back().first.append(t0, t1);
+                count = string_ref(t0, t1);
                 t0 = t1;
             }
-            db.names.back().first.push_back('\'');
             if (t0 == last || *t0 != '_')
-            {
-                db.names.pop_back();
                 return first;
-            }
+            db.names.push_back(db.template make<unnamed_type>(count));
             first = t0 + 1;
           }
             break;
         case 'l':
           {
-            size_t lambda_pos = db.names.size();
-            db.names.push_back(typename C::String("'lambda'("));
+            size_t begin_pos = db.names.size();
             const char* t0 = first+2;
+            size_t nlambda_params = 0;
+            node** lambda_params = nullptr;
             if (first[2] == 'v')
             {
-                db.names.back().first += ')';
                 ++t0;
             }
             else
             {
-                bool is_first_it = true;
                 while (true)
                 {
-                    long k0 = static_cast<long>(db.names.size());
                     const char* t1 = parse_type(t0, last, db);
-                    long k1 = static_cast<long>(db.names.size());
                     if (t1 == t0)
                         break;
-                    if (k0 >= k1)
-                        return first;
-                    // If the call to parse_type above found a pack expansion
-                    // substitution, then multiple names could have been
-                    // inserted into the name table. Walk through the names,
-                    // appending each onto the lambda's parameter list.
-                    std::for_each(db.names.begin() + k0, db.names.begin() + k1,
-                                  [&](typename C::sub_type::value_type &pair) {
-                                      if (pair.empty())
-                                          return;
-                                      auto &lambda = db.names[lambda_pos].first;
-                                      if (!is_first_it)
-                                          lambda.append(", ");
-                                      is_first_it = false;
-                                      lambda.append(pair.move_full());
-                                  });
-                    db.names.erase(db.names.begin() + k0, db.names.end());
                     t0 = t1;
                 }
-                if (is_first_it)
-                {
-                    if (!db.names.empty())
-                        db.names.pop_back();
+                if (db.names.size() <= begin_pos)
                     return first;
-                }
-                if (db.names.empty() || db.names.size() - 1 != lambda_pos)
-                  return first;
-                db.names.back().first.append(")");
+                lambda_params =
+                    db.template make_array<node*>(db.names.begin() + (long)begin_pos,
+                                                  db.names.end());
+                nlambda_params = db.names.size() - begin_pos;
+                db.names.erase(db.names.begin() + (long)begin_pos, db.names.end());
             }
             if (t0 == last || *t0 != 'E')
-            {
-              if (!db.names.empty())
-                db.names.pop_back();
-              return first;
-            }
+                return first;
             ++t0;
             if (t0 == last)
-            {
-                if(!db.names.empty())
-                  db.names.pop_back();
                 return first;
-            }
+            string_ref count;
             if (std::isdigit(*t0))
             {
                 const char* t1 = t0 + 1;
                 while (t1 != last && std::isdigit(*t1))
                     ++t1;
-                db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1);
+                count = string_ref(t0, t1);
                 t0 = t1;
             }
             if (t0 == last || *t0 != '_')
-            {
-                if(!db.names.empty())
-                  db.names.pop_back();
                 return first;
-            }
+            db.names.push_back(
+                db.template make<lambda_type>(nlambda_params, lambda_params, count));
             first = t0 + 1;
           }
             break;
         }
     }
     return first;
 }
 
 // <unqualified-name> ::= <operator-name>
 //                    ::= <ctor-dtor-name>
 //                    ::= <source-name>   
 //                    ::= <unnamed-type-name>
 
 template <class C>
 const char*
 parse_unqualified_name(const char* first, const char* last, C& db)
 {
     if (first != last)
     {
         const char* t;
         switch (*first)
         {
         case 'C':
         case 'D':
             t = parse_ctor_dtor_name(first, last, db);
             if (t != first)
                 first = t;
             break;
         case 'U':
             t = parse_unnamed_type_name(first, last, db);
             if (t != first)
                 first = t;
             break;
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
             t = parse_source_name(first, last, db);
             if (t != first)
                 first = t;
             break;
         default:
             t = parse_operator_name(first, last, db);
             if (t != first)
                 first = t;
             break;
         };
     }
     return first;
 }
 
 // <unscoped-name> ::= <unqualified-name>
 //                 ::= St <unqualified-name>   # ::std::
 // extension       ::= StL<unqualified-name>
 
 template <class C>
 const char*
 parse_unscoped_name(const char* first, const char* last, C& 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().first.insert(0, "std::");
+                db.names.back() =
+                    db.template make<prefix_type>("std::", db.names.back());
             }
             first = t1;
         }
     }
     return first;
 }
 
 // at <type>                                            # alignof (a type)
 
 template <class C>
 const char*
 parse_alignof_type(const char* first, const char* last, C& 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().first = "alignof (" + db.names.back().move_full() + ")";
+            db.names.back() =
+                db.template make<enclosing_expr>("alignof (", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 // az <expression>                                            # alignof (a expression)
 
 template <class C>
 const char*
 parse_alignof_expr(const char* first, const char* last, C& 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().first = "alignof (" + db.names.back().move_full() + ")";
+            db.names.back() =
+                db.template make<enclosing_expr>("alignof (", db.names.back(), ")");
             first = t;
         }
     }
     return first;
 }
 
 template <class C>
 const char*
 parse_noexcept_expression(const char* first, const char* last, C& db)
 {
     const char* t1 = parse_expression(first, last, db);
     if (t1 != first)
     {
         if (db.names.empty())
             return first;
-        db.names.back().first =  "noexcept (" + db.names.back().move_full() + ")";
+        db.names.back() =
+            db.template make<enclosing_expr>("noexcept (", db.names.back(), ")");
         first = t1;
     }
     return first;
 }
 
 template <class C>
 const char*
-parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db)
+parse_prefix_expression(const char* first, const char* last, string_ref op, C& db)
 {
     const char* t1 = parse_expression(first, last, db);
     if (t1 != first)
     {
         if (db.names.empty())
             return first;
-        db.names.back().first =  op + "(" + db.names.back().move_full() + ")";
+        db.names.back() = db.template make<prefix_expr>(op, db.names.back());
         first = t1;
     }
     return first;
 }
 
 template <class C>
 const char*
-parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db)
+parse_binary_expression(const char* first, const char* last, string_ref op, C& 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().move_full();
+            auto op2 = db.names.back();
             db.names.pop_back();
-            auto op1 = db.names.back().move_full();
-            auto& nm = db.names.back().first;
-            nm.clear();
-            if (op == ">")
-                nm += '(';
-            nm += "(" + op1 + ") " + op + " (" + op2 + ")";
-            if (op == ">")
-                nm += ')';
+            auto op1 = db.names.back();
+            db.names.back() = db.template make<binary_expr>(op1, op, op2);
             first = t2;
         }
         else if(!db.names.empty())
             db.names.pop_back();
     }
     return first;
 }
 
 // <expression> ::= <unary operator-name> <expression>
 //              ::= <binary operator-name> <expression> <expression>
 //              ::= <ternary operator-name> <expression> <expression> <expression>
 //              ::= cl <expression>+ E                                   # call
 //              ::= cv <type> <expression>                               # conversion with one argument
 //              ::= cv <type> _ <expression>* E                          # conversion with a different number of arguments
 //              ::= [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)
 //              ::= [gs] dl <expression>                                 # delete expression
 //              ::= [gs] da <expression>                                 # delete[] expression
 //              ::= pp_ <expression>                                     # prefix ++
 //              ::= mm_ <expression>                                     # prefix --
 //              ::= ti <type>                                            # typeid (type)
 //              ::= te <expression>                                      # typeid (expression)
 //              ::= dc <type> <expression>                               # dynamic_cast<type> (expression)
 //              ::= sc <type> <expression>                               # static_cast<type> (expression)
 //              ::= cc <type> <expression>                               # const_cast<type> (expression)
 //              ::= rc <type> <expression>                               # reinterpret_cast<type> (expression)
 //              ::= st <type>                                            # sizeof (a type)
 //              ::= sz <expression>                                      # sizeof (an expression)
 //              ::= at <type>                                            # alignof (a type)
 //              ::= az <expression>                                      # alignof (an expression)
 //              ::= nx <expression>                                      # noexcept (expression)
 //              ::= <template-param>
 //              ::= <function-param>
 //              ::= dt <expression> <unresolved-name>                    # expr.name
 //              ::= pt <expression> <unresolved-name>                    # expr->name
 //              ::= ds <expression> <expression>                         # expr.*expr
 //              ::= sZ <template-param>                                  # size of a parameter pack
 //              ::= sZ <function-param>                                  # size of a function parameter pack
 //              ::= 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
 //              ::= <expr-primary>
 
 template <class C>
 const char*
 parse_expression(const char* first, const char* last, C& 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().first = (parsed_gs ? typename C::String("::") : typename C::String()) +
-                                          "delete[] " + db.names.back().move_full();
+                        if (parsed_gs) {
+                            db.names.back() =
+                                db.template make<string_then_expr>("::delete[] ",
+                                                          db.names.back());
+                        } else {
+                            db.names.back() =
+                                db.template make<string_then_expr>("delete[] ",
+                                                                   db.names.back());
+                        }
                         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().first = (parsed_gs ? typename C::String("::") : typename C::String()) +
-                                          "delete " + db.names.back().move_full();
+                        if (parsed_gs) {
+                            db.names.back() =
+                                db.template make<string_then_expr>("::delete ", db.names.back());
+                        } else {
+                            db.names.back() =
+                                db.template make<string_then_expr>("delete ", db.names.back());
+                        }
                         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().move_full();
+                        auto op2 = db.names.back();
                         db.names.pop_back();
-                        auto op1 = db.names.back().move_full();
-                        db.names.back() = "(" + op1 + ")[" + op2 + "]";
+                        auto op1 = db.names.back();
+                        db.names.back() = db.template make<at_expr>(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.names.back().move_full() + ")--";
+                        db.names.back() =
+                            db.template make<postfix_expr>(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.names.back().move_full() + ")++";
+                        db.names.back() =
+                            db.template make<postfix_expr>(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().move_full();
+                            auto op3 = db.names.back();
                             db.names.pop_back();
-                            auto op2 = db.names.back().move_full();
+                            auto op2 = db.names.back();
                             db.names.pop_back();
-                            auto op1 = db.names.back().move_full();
-                            db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")";
+                            auto op1 = db.names.back();
+                            db.names.back() =
+                                db.template make<conditional_expr>(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("throw");
+                // FIXME
+                db.names.push_back(db.template make<name_type>("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);
         }
     }
     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
 
 template <class C>
 const char*
 parse_template_arg(const char* first, const char* last, C& 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;
         }
     }
     return first;
 }
 
 // <template-args> ::= I <template-arg>* E
 //     extension, the abi says <template-arg>+
 
 template <class C>
 const char*
 parse_template_args(const char* first, const char* last, C& db)
 {
     if (last - first >= 2 && *first == 'I')
     {
         if (db.tag_templates)
             db.template_param.back().clear();
         const char* t = first+1;
-        typename C::String args("<");
+        size_t begin_idx = db.names.size();
         while (*t != 'E')
         {
             if (db.tag_templates)
                 db.template_param.emplace_back(db.names.get_allocator());
             size_t k0 = db.names.size();
             const char* t1 = parse_template_arg(t, last, db);
             size_t k1 = db.names.size();
             if (db.tag_templates)
                 db.template_param.pop_back();
             if (t1 == t || t1 == last)
                 return first;
             if (db.tag_templates)
             {
                 db.template_param.back().emplace_back(db.names.get_allocator());
                 for (size_t k = k0; k < k1; ++k)
                     db.template_param.back().back().push_back(db.names[k]);
             }
-            for (size_t k = k0; k < k1; ++k)
-            {
-                if (args.size() > 1)
-                    args += ", ";
-                args += db.names[k].move_full();
-            }
-            for (; k1 > k0; --k1)
-                if (!db.names.empty())
-                    db.names.pop_back();
             t = t1;
         }
         first = t + 1;
-        if (args.back() != '>')
-            args += ">";
-        else
-            args += " >";
-        db.names.push_back(std::move(args));
-        
+        template_params* tp =
+            db.template make<template_params>(
+                db.names.size() - begin_idx,
+                db.template make_array<node*>(db.names.begin() + (long)begin_idx, db.names.end()));
+        db.names.erase(db.names.begin() + (long)begin_idx, db.names.end());
+        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>
 
 template <class C>
 const char*
 parse_nested_name(const char* first, const char* last, C& db,
                   bool* ends_with_template_args)
 {
     if (first != last && *first == 'N')
     {
         unsigned cv;
         const char* t0 = parse_cv_qualifiers(first+1, last, cv);
         if (t0 == last)
             return first;
         db.ref = 0;
         if (*t0 == 'R')
         {
             db.ref = 1;
             ++t0;
         }
         else if (*t0 == 'O')
         {
             db.ref = 2;
             ++t0;
         }
         db.names.emplace_back();
         if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't')
         {
             t0 += 2;
-            db.names.back().first = "std";
+            db.names.back() = db.template make<string_type>("std");
         }
         if (t0 == last)
         {
             db.names.pop_back();
             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)
                 {
-                    auto name = db.names.back().move_full();
+                    auto name = db.names.back();
                     db.names.pop_back();
                     if (db.names.empty())
                         return first;
-                    if (!db.names.back().first.empty())
+                    if (db.names.back())
                     {
-                        db.names.back().first += "::" + name;
+                        db.names.back() = db.template make<qualified_name>(
+                            db.names.back(), name);
                         db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                     }
                     else
-                        db.names.back().first = name;
+                        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)
                 {
-                    auto name = db.names.back().move_full();
+                    auto name = db.names.back();
                     db.names.pop_back();
                     if (db.names.empty())
                         return first;
-                    if (!db.names.back().first.empty())
-                        db.names.back().first += "::" + name;
+                    if (db.names.back())
+                        db.names.back() =
+                            db.template make<qualified_name>(db.names.back(), name);
                     else
-                        db.names.back().first = name;
+                        db.names.back() = name;
                     db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                     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)
                 {
-                    auto name = db.names.back().move_full();
+                    auto name = db.names.back();
                     db.names.pop_back();
                     if (db.names.empty())
                         return first;
-                    if (!db.names.back().first.empty())
-                        db.names.back().first += "::" + name;
+                    if (db.names.back())
+                        db.names.back() =
+                            db.template make<qualified_name>(db.names.back(), name);
                     else
-                        db.names.back().first = name;
+                        db.names.back() = name;
                     db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                     pop_subs = true;
                     t0 = t1;
                 }
                 else
                     return first;
                 break;
             case 'I':
                 t1 = parse_template_args(t0, last, db);
                 if (t1 != t0 && t1 != last)
                 {
-                    auto name = db.names.back().move_full();
+                    auto name = db.names.back();
                     db.names.pop_back();
                     if (db.names.empty())
                         return first;
-                    db.names.back().first += name;
-                    db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
+                    db.names.back() = db.template make<name_with_template_args>(
+                        db.names.back(), name);
+                    db.subs.push_back(typename C::sub_type(
+                        1, db.names.back(), db.names.get_allocator()));
                     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)
                 {
-                    auto name = db.names.back().move_full();
+                    auto name = db.names.back();
                     db.names.pop_back();
                     if (db.names.empty())
                         return first;
-                    if (!db.names.back().first.empty())
-                        db.names.back().first += "::" + name;
+                    if (db.names.back())
+                        db.names.back() =
+                            db.template make<qualified_name>(db.names.back(), name);
                     else
-                        db.names.back().first = name;
+                        db.names.back() = name;
                     db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                     pop_subs = true;
                     t0 = t1;
                 }
                 else
                     return first;
             }
         }
         first = t0 + 1;
         db.cv = cv;
         if (pop_subs && !db.subs.empty())
             db.subs.pop_back();
         if (ends_with_template_args)
             *ends_with_template_args = component_ends_with_template_args;
     }
     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;
         }
     }
     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>
 
 template <class C>
 const char*
 parse_local_name(const char* first, const char* last, C& 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().first.append("::string literal");
+                db.names.back() = db.template make<qualified_name>(
+                    db.names.back(), db.template make<name_type>("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().move_full();
+                            auto name = db.names.back();
                             db.names.pop_back();
                             if (db.names.empty())
                                 return first;
-                            db.names.back().first.append("::");
-                            db.names.back().first.append(name);
+                            db.names.back() =
+                                db.template make<qualified_name>(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().move_full();
+                        auto name = db.names.back();
                         db.names.pop_back();
                         if (db.names.empty())
                             return first;
-                        db.names.back().first.append("::");
-                        db.names.back().first.append(name);
+                        db.names.back() =
+                            db.template make<qualified_name>(db.names.back(), name);
                     }
                     else if (!db.names.empty())
                         db.names.pop_back();
                 }
                 break;
             }
         }
     }
     return first;
 }
 
 // <name> ::= <nested-name> // N
 //        ::= <local-name> # See Scope Encoding below  // Z
 //        ::= <unscoped-template-name> <template-args>
 //        ::= <unscoped-name>
 
 // <unscoped-template-name> ::= <unscoped-name>
 //                          ::= <substitution>
 
 template <class C>
 const char*
 parse_name(const char* first, const char* last, C& 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.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator()));
                     t0 = t1;
                     t1 = parse_template_args(t0, last, db);
                     if (t1 != t0)
                     {
                         if (db.names.size() < 2)
                             return first;
-                        auto tmp = db.names.back().move_full();
+                        auto tmp = db.names.back();
                         db.names.pop_back();
                         if (db.names.empty())
                             return first;
-                        db.names.back().first += tmp;
+                        db.names.back() =
+                            db.template make<name_with_template_args>(
+                                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().move_full();
+                        auto tmp = db.names.back();
                         db.names.pop_back();
                         if (db.names.empty())
                             return first;
-                        db.names.back().first += tmp;
+                        db.names.back() =
+                            db.template make<name_with_template_args>(
+                                db.names.back(), tmp);
                         first = t1;
                         if (ends_with_template_args)
                             *ends_with_template_args = true;
                     }
                 }
             }
             break;
           }
         }
     }
     return first;
 }
 
 // <call-offset> ::= h <nv-offset> _
 //               ::= v <v-offset> _
 // 
 // <nv-offset> ::= <offset number>
 //               # non-virtual base override
 // 
 // <v-offset>  ::= <offset number> _ <virtual offset number>
 //               # virtual base override, with vcall offset
 
 const char*
 parse_call_offset(const char* first, const char* last)
 {
     if (first != last)
     {
         switch (*first)
         {
         case 'h':
             {
             const char* t = parse_number(first + 1, last);
             if (t != first + 1 && t != last && *t == '_')
                 first = t + 1;
             }
             break;
         case 'v':
             {
             const char* t = parse_number(first + 1, last);
             if (t != first + 1 && t != last && *t == '_')
             {
                 const char* t2 = parse_number(++t, last);
                 if (t2 != t && t2 != last && *t2 == '_')
                     first = t2 + 1;
             }
             }
             break;
         }
     }
     return first;
 }
 
 // <special-name> ::= TV <type>    # virtual table
 //                ::= TT <type>    # VTT structure (construction vtable index)
 //                ::= TI <type>    # typeinfo structure
 //                ::= TS <type>    # typeinfo name (null-terminated byte string)
 //                ::= Tc <call-offset> <call-offset> <base encoding>
 //                    # base is the nominal target function of thunk
 //                    # first call-offset is 'this' adjustment
 //                    # second call-offset is result adjustment
 //                ::= T <call-offset> <base encoding>
 //                    # base is the nominal target function of thunk
 //                ::= GV <object name> # Guard variable for one-time initialization
 //                                     # No <type>
 //                ::= TW <object name> # Thread-local wrapper
 //                ::= TH <object name> # Thread-local initialization
 //      extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
 //      extension ::= GR <object name> # reference temporary for object
 
 template <class C>
 const char*
 parse_special_name(const char* first, const char* last, C& 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().first.insert(0, "vtable for ");
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "VTT for ");
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "typeinfo for ");
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "typeinfo name for ");
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "covariant return thunk to ");
+                    db.names.back() =
+                        db.template make<special_name>("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().move_full();
+                            auto left = db.names.back();
                             db.names.pop_back();
                             if (db.names.empty())
                                 return first;
-                            db.names.back().first = "construction vtable for " +
-                                                    std::move(left) + "-in-" +
-                                                    db.names.back().move_full();
+                            db.names.back() = db.template make<ctor_vtable_special_name>(
+                                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().first.insert(0, "thread-local wrapper routine for ");
+                        return first;
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "thread-local initialization routine for ");
+                        return first;
+                    db.names.back() = db.template make<special_name>(
+                        "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().first.insert(0, "virtual thunk to ");
+                        db.names.back() =
+                            db.template make<special_name>("virtual thunk to ",
+                                                  db.names.back());
                         first = t;
                     }
                     else
                     {
-                        db.names.back().first.insert(0, "non-virtual thunk to ");
+                        db.names.back() =
+                            db.template make<special_name>("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().first.insert(0, "guard variable for ");
+                    db.names.back() =
+                        db.template make<special_name>("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().first.insert(0, "reference temporary for ");
+                    db.names.back() =
+                        db.template make<special_name>("reference temporary for ",
+                                              db.names.back());
                     first = t;
                 }
                 break;
             }
             break;
         }
     }
     return first;
 }
 
 template <class T>
 class save_value
 {
     T& restore_;
     T original_value_;
 public:
     save_value(T& restore)
         : restore_(restore),
           original_value_(restore)
         {}
 
     ~save_value()
     {
         restore_ = std::move(original_value_);
     }
 
     save_value(const save_value&) = delete;
     save_value& operator=(const save_value&) = delete;
 };
 
 // <encoding> ::= <function name> <bare-function-type>
 //            ::= <data name>
 //            ::= <special-name>
 
 template <class C>
 const char*
 parse_encoding(const char* first, const char* last, C& db)
 {
     if (first != last)
     {
         save_value<decltype(db.encoding_depth)> su(db.encoding_depth);
         ++db.encoding_depth;
         save_value<decltype(db.tag_templates)> sb(db.tag_templates);
         if (db.encoding_depth > 1)
             db.tag_templates = true;
         save_value<decltype(db.parsed_ctor_dtor_cv)> sp(db.parsed_ctor_dtor_cv);
         db.parsed_ctor_dtor_cv = 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;
             unsigned cv = db.cv;
             unsigned ref = db.ref;
             if (t != first)
             {
                 if (t != last && *t != 'E' && *t != '.')
                 {
                     save_value<bool> sb2(db.tag_templates);
                     db.tag_templates = false;
                     const char* t2;
-                    typename C::String ret2;
-                    if (db.names.empty())
-                        return first;
-                    const typename C::String& nm = db.names.back().first;
-                    if (nm.empty())
-                        return first;
+                    // if (db.names.empty())
+                    //     return first;
+                    // if (!db.names.back())
+                    //     return first;
+                    node* return_type = nullptr;
                     if (!db.parsed_ctor_dtor_cv && ends_with_template_args)
                     {
                         t2 = parse_type(t, last, db);
                         if (t2 == t)
                             return first;
-                        if (db.names.size() < 2)
+                        if (db.names.size() < 1)
                             return first;
-                        auto ret1 = std::move(db.names.back().first);
-                        ret2 = std::move(db.names.back().second);
-                        if (ret2.empty())
-                            ret1 += ' ';
+                        return_type = db.names.back();
                         db.names.pop_back();
-                        if (db.names.empty())
-                            return first;
-
-                        db.names.back().first.insert(0, ret1);
+                        // auto ret1 = std::move(db.names.back().first);
+                        // ret2 = std::move(db.names.back().second);
+                        // if (ret2.empty())
+                        //     ret1 += ' ';
+                        // if (db.names.empty())
+                        //     return first;
+
+                        // db.names.back().first.insert(0, ret1);
                         t = t2;
                     }
-                    db.names.back().first += '(';
+
+                    top_level_function_decl* result = nullptr;
+
+                    // db.names.back().first += '(';
                     if (t != last && *t == 'v')
                     {
                         ++t;
+                        result = db.template make<top_level_function_decl>(return_type, db.names.back(), 0u, nullptr);
+                        db.names.pop_back();
                     }
                     else
                     {
-                        bool first_arg = true;
+                        size_t params_begin = db.names.size();
                         while (true)
                         {
-                            size_t k0 = db.names.size();
                             t2 = parse_type(t, last, db);
-                            size_t k1 = db.names.size();
                             if (t2 == t)
                                 break;
-                            if (k1 > k0)
-                            {
-                                typename C::String tmp;
-                                for (size_t k = k0; k < k1; ++k)
-                                {
-                                    if (!tmp.empty())
-                                        tmp += ", ";
-                                    tmp += db.names[k].move_full();
-                                }
-                                for (size_t k = k0; k < k1; ++k) {
-                                    if (db.names.empty())
-                                        return first;
-                                    db.names.pop_back();
-                                }
-                                if (!tmp.empty())
-                                {
-                                    if (db.names.empty())
-                                        return first;
-                                    if (!first_arg)
-                                        db.names.back().first += ", ";
-                                    else
-                                        first_arg = false;
-                                    db.names.back().first += tmp;
-                                }
-                            }
                             t = t2;
                         }
+                        unsigned nparams = static_cast<unsigned>(db.names.size() - params_begin);
+                        node** params = db.template make_array<node*>(
+                            db.names.begin() + (long)params_begin,
+                            db.names.end());
+                        db.names.erase(db.names.begin() + (long)params_begin,
+                                       db.names.end());
+                        result =
+                            db.template make<top_level_function_decl>(
+                                return_type, db.names.back(), nparams, params);
+                        db.names.pop_back();
                     }
-                    if (db.names.empty())
-                        return first;
-                    db.names.back().first += ')';
-                    if (cv & 1)
-                        db.names.back().first.append(" const");
-                    if (cv & 2)
-                        db.names.back().first.append(" volatile");
-                    if (cv & 4)
-                        db.names.back().first.append(" restrict");
-                    if (ref == 1)
-                        db.names.back().first.append(" &");
-                    else if (ref == 2)
-                        db.names.back().first.append(" &&");
-                    db.names.back().first += ret2;
+                    db.names.push_back(result);
+                    // db.names.back().first += ')';
+                    if (cv || ref) {
+                        if (ref == 1)
+                            cv |= 8;
+                        if (ref == 2)
+                            cv |= 16;
+                        db.names.back() =
+                            db.template make<qual_type>(db.names.back(), cv);
+                    }
+                    // db.names.back().first += ret2;
                     first = t;
                 }
                 else
                     first = t;
             }
             break;
           }
         }
     }
     return first;
 }
 
 // _block_invoke
 // _block_invoke<decimal-digit>+
 // _block_invoke_<decimal-digit>+
 
 template <class C>
 const char*
 parse_block_invoke(const char* first, const char* last, C& 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().first.insert(0, "invocation function for block in ");
+        db.names.back() =
+            db.template make<special_name>("invocation function for block in ",
+                                  db.names.back());
         first = t;
     }
     return first;
 }
 
 // extension
 // <dot-suffix> := .<anything and everything>
 
 template <class C>
 const char*
 parse_dot_suffix(const char* first, const char* last, C& db)
 {
     if (first != last && *first == '.')
     {
         if (db.names.empty())
             return first;
-        db.names.back().first += " (" + typename C::String(first, last) + ")";
+        db.names.back() =
+            db.template make<dot_suffix>(db.names.back(), string_ref(first, last));
         first = last;
     }
     return first;
 }
 
 // <block-involcaton-function> ___Z<encoding>_block_invoke
 // <block-involcaton-function> ___Z<encoding>_block_invoke<decimal-digit>+
 // <block-involcaton-function> ___Z<encoding>_block_invoke_<decimal-digit>+
 // <mangled-name> ::= _Z<encoding>
 //                ::= <type>
 
 template <class C>
 void
 demangle(const char* first, const char* last, C& db, int& status)
 {
     if (first >= last)
     {
         status = invalid_mangled_name;
         return;
     }
     if (*first == '_')
     {
         if (last - first >= 4)
         {
             if (first[1] == 'Z')
             {
                 const char* t = parse_encoding(first+2, last, db);
                 if (t != first+2 && t != last && *t == '.')
                     t = parse_dot_suffix(t, last, db);
                 if (t != last)
                     status = invalid_mangled_name;
             }
             else if (first[1] == '_' && first[2] == '_' && first[3] == 'Z')
             {
                 const char* t = parse_encoding(first+4, last, db);
                 if (t != first+4 && t != last)
                 {
                     const char* t1 = parse_block_invoke(t, last, db);
                     if (t1 != last)
                         status = invalid_mangled_name;
                 }
                 else
                     status = invalid_mangled_name;
             }
             else
                 status = invalid_mangled_name;
         }
         else
             status = invalid_mangled_name;
     }
     else
     {
         const char* t = parse_type(first, last, db);
         if (t != last)
             status = invalid_mangled_name;
     }
     if (status == success && db.names.empty())
         status = invalid_mangled_name;
 }
 
-template <std::size_t N>
-class arena
-{
-    static const std::size_t alignment = 16;
-    alignas(alignment) char buf_[N];
-    char* ptr_;
-
-    std::size_t 
-    align_up(std::size_t n) noexcept
-        {return (n + (alignment-1)) & ~(alignment-1);}
-
-    bool
-    pointer_in_buffer(char* p) noexcept
-        {return buf_ <= p && p <= buf_ + N;}
-
-public:
-    arena() noexcept : ptr_(buf_) {}
-    ~arena() {ptr_ = nullptr;}
-    arena(const arena&) = delete;
-    arena& operator=(const arena&) = delete;
-
-    char* allocate(std::size_t n);
-    void deallocate(char* p, std::size_t n) noexcept;
-
-    static constexpr std::size_t size() {return N;}
-    std::size_t used() const {return static_cast<std::size_t>(ptr_ - buf_);}
-    void reset() {ptr_ = buf_;}
-};
-
-template <std::size_t N>
-char*
-arena<N>::allocate(std::size_t n)
-{
-    n = align_up(n);
-    if (static_cast<std::size_t>(buf_ + N - ptr_) >= n)
-    {
-        char* r = ptr_;
-        ptr_ += n;
-        return r;
-    }
-    return static_cast<char*>(std::malloc(n));
-}
-
-template <std::size_t N>
-void
-arena<N>::deallocate(char* p, std::size_t n) noexcept
-{
-    if (pointer_in_buffer(p))
-    {
-        n = align_up(n);
-        if (p + n == ptr_)
-            ptr_ = p;
-    }
-    else
-        std::free(p);
-}
-
-template <class T, std::size_t N>
-class short_alloc
-{
-    arena<N>& a_;
-public:
-    typedef T value_type;
-
-public:
-    template <class _Up> struct rebind {typedef short_alloc<_Up, N> other;};
-
-    short_alloc(arena<N>& a) noexcept : a_(a) {}
-    template <class U>
-        short_alloc(const short_alloc<U, N>& a) noexcept
-            : a_(a.a_) {}
-    short_alloc(const short_alloc&) = default;
-    short_alloc& operator=(const short_alloc&) = delete;
-
-    T* allocate(std::size_t n)
-    {
-        return reinterpret_cast<T*>(a_.allocate(n*sizeof(T)));
-    }
-    void deallocate(T* p, std::size_t n) noexcept
-    {
-        a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
-    }
-
-    template <class T1, std::size_t N1, class U, std::size_t M>
-    friend
-    bool
-    operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept;
-
-    template <class U, std::size_t M> friend class short_alloc;
-};
-
-template <class T, std::size_t N, class U, std::size_t M>
-inline
-bool
-operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
-{
-    return N == M && &x.a_ == &y.a_;
-}
-
-template <class T, std::size_t N, class U, std::size_t M>
-inline
-bool
-operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
-{
-    return !(x == y);
-}
-
-template <class T>
-class malloc_alloc
-{
-public:
-    typedef T value_type;
-    typedef T& reference;
-    typedef const T& const_reference;
-    typedef T* pointer;
-    typedef const T* const_pointer;
-    typedef std::size_t size_type;
-    typedef std::ptrdiff_t difference_type;
-
-    malloc_alloc() = default;
-    template <class U> malloc_alloc(const malloc_alloc<U>&) noexcept {}
-
-    T* allocate(std::size_t n)
-    {
-        return static_cast<T*>(std::malloc(n*sizeof(T)));
-    }
-    void deallocate(T* p, std::size_t) noexcept
-    {
-        std::free(p);
-    }
-
-    template <class U> struct rebind { using other = malloc_alloc<U>; };
-    template <class U, class... Args>
-    void construct(U* p, Args&&... args)
-    {
-        ::new ((void*)p) U(std::forward<Args>(args)...);
-    }
-    void destroy(T* p)
-    {
-        p->~T();
-    }
-};
-
-template <class T, class U>
-inline
-bool
-operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) noexcept
-{
-    return true;
-}
-
-template <class T, class U>
-inline
-bool
-operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) noexcept
-{
-    return !(x == y);
-}
-
-const size_t bs = 4 * 1024;
-template <class T> using Alloc = short_alloc<T, bs>;
-template <class T> using Vector = std::vector<T, Alloc<T>>;
-
-template <class StrT>
-struct string_pair
-{
-    StrT first;
-    StrT second;
-
-    string_pair() = default;
-    string_pair(StrT f) : first(std::move(f)) {}
-    string_pair(StrT f, StrT s)
-        : first(std::move(f)), second(std::move(s)) {}
-    template <size_t N>
-        string_pair(const char (&s)[N]) : first(s, N-1) {}
-
-    size_t size() const {return first.size() + second.size();}
-    bool empty() const { return first.empty() && second.empty(); }
-    StrT full() const {return first + second;}
-    StrT move_full() {return std::move(first) + std::move(second);}
-};
-
-struct Db
-{
-    typedef std::basic_string<char, std::char_traits<char>,
-                              malloc_alloc<char>> String;
-    typedef Vector<string_pair<String>> sub_type;
-    typedef Vector<sub_type> template_param_type;
-    sub_type names;
-    template_param_type subs;
-    Vector<template_param_type> template_param;
-    unsigned cv = 0;
-    unsigned ref = 0;
-    unsigned encoding_depth = 0;
-    bool parsed_ctor_dtor_cv = false;
-    bool tag_templates = true;
-    bool fix_forward_references = false;
-    bool try_to_parse_template_args = true;
-
-    template <size_t N>
-    Db(arena<N>& ar) :
-        names(ar),
-        subs(0, names, ar),
-        template_param(0, subs, ar)
-    {}
-};
-
 }  // unnamed namespace
 
-extern "C" _LIBCXXABI_FUNC_VIS char *
+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;
     arena<bs> a;
     Db db(a);
     db.template_param.emplace_back(a);
     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.fix_forward_references &&
            !db.template_param.empty() && !db.template_param.front().empty())
     {
         db.fix_forward_references = false;
         db.tag_templates = false;
         db.names.clear();
         db.subs.clear();
         demangle(mangled_name, mangled_name + len, db, internal_status);
         if (db.fix_forward_references)
             internal_status = invalid_mangled_name;
     }
     if (internal_status == success)
     {
-        size_t sz = db.names.back().size() + 1;
-        if (sz > internal_size)
-        {
-            char* newbuf = static_cast<char*>(std::realloc(buf, sz));
-            if (newbuf == nullptr)
-            {
-                internal_status = memory_alloc_failure;
-                buf = nullptr;
-            }
-            else
-            {
-                buf = newbuf;
-                if (n != nullptr)
-                    *n = sz;
-            }
-        }
+        // size_t sz = db.names.back().size() + 1;
+        // if (sz > internal_size)
+        // {
+        //     char* newbuf = static_cast<char*>(std::realloc(buf, sz));
+        //     if (newbuf == nullptr)
+        //     {
+        //         internal_status = memory_alloc_failure;
+        //         buf = nullptr;
+        //     }
+        //     else
+        //     {
+        //         buf = newbuf;
+        //         if (n != nullptr)
+        //             *n = sz;
+        //     }
+        // }
+        if (!buf) buf = (char*)malloc(1024),internal_size=1024;
         if (buf != nullptr)
         {
-            db.names.back().first += db.names.back().second;
-            std::memcpy(buf, db.names.back().first.data(), sz-1);
-            buf[sz-1] = char(0);
+            stream s(buf, internal_size);
+            db.names.back()->print(s);
+            s += '\0';
+            buf = s.get_buf();
+            if (n) *n = s.get_cap();
+            // db.names.back().first += db.names.back().second;
+            // std::memcpy(buf, db.names.back().first.data(), sz-1);
+            // buf[sz-1] = char(0);
         }
     }
     else
         buf = nullptr;
     if (status)
         *status = internal_status;
     return buf;
 }
 
 }  // __cxxabiv1
diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp
index 451f5f9..65c9059 100644
--- a/test/test_demangle.pass.cpp
+++ b/test/test_demangle.pass.cpp
@@ -29103,698 +29103,702 @@ const char* cases[][2] =
     {"_ZL16nFriendTemplates", "nFriendTemplates"},
     {"_ZL13nLinkageSpecs", "nLinkageSpecs"},
     {"_ZL11nNamespaces", "nNamespaces"},
     {"_ZL16nNamespaceAliass", "nNamespaceAliass"},
     {"_ZL21nObjCCompatibleAliass", "nObjCCompatibleAliass"},
     {"_ZL14nObjCCategorys", "nObjCCategorys"},
     {"_ZL18nObjCCategoryImpls", "nObjCCategoryImpls"},
     {"_ZL20nObjCImplementations", "nObjCImplementations"},
     {"_ZL15nObjCInterfaces", "nObjCInterfaces"},
     {"_ZL14nObjCProtocols", "nObjCProtocols"},
     {"_ZL12nObjCMethods", "nObjCMethods"},
     {"_ZL14nObjCPropertys", "nObjCPropertys"},
     {"_ZL15nClassTemplates", "nClassTemplates"},
     {"_ZL18nFunctionTemplates", "nFunctionTemplates"},
     {"_ZL22nTemplateTemplateParms", "nTemplateTemplateParms"},
     {"_ZL6nEnums", "nEnums"},
     {"_ZL8nRecords", "nRecords"},
     {"_ZL11nCXXRecords", "nCXXRecords"},
     {"_ZL29nClassTemplateSpecializations", "nClassTemplateSpecializations"},
     {"_ZL36nClassTemplatePartialSpecializations", "nClassTemplatePartialSpecializations"},
     {"_ZL18nTemplateTypeParms", "nTemplateTypeParms"},
     {"_ZL9nTypedefs", "nTypedefs"},
     {"_ZL25nUnresolvedUsingTypenames", "nUnresolvedUsingTypenames"},
     {"_ZL7nUsings", "nUsings"},
     {"_ZL16nUsingDirectives", "nUsingDirectives"},
     {"_ZL13nUsingShadows", "nUsingShadows"},
     {"_ZL7nFields", "nFields"},
     {"_ZL17nObjCAtDefsFields", "nObjCAtDefsFields"},
     {"_ZL10nObjCIvars", "nObjCIvars"},
     {"_ZL10nFunctions", "nFunctions"},
     {"_ZL11nCXXMethods", "nCXXMethods"},
     {"_ZL16nCXXConstructors", "nCXXConstructors"},
     {"_ZL15nCXXConversions", "nCXXConversions"},
     {"_ZL15nCXXDestructors", "nCXXDestructors"},
     {"_ZL21nNonTypeTemplateParms", "nNonTypeTemplateParms"},
     {"_ZL5nVars", "nVars"},
     {"_ZL15nImplicitParams", "nImplicitParams"},
     {"_ZL9nParmVars", "nParmVars"},
     {"_ZL14nEnumConstants", "nEnumConstants"},
     {"_ZL15nIndirectFields", "nIndirectFields"},
     {"_ZL22nUnresolvedUsingValues", "nUnresolvedUsingValues"},
     {"_ZL11nObjCClasss", "nObjCClasss"},
     {"_ZL21nObjCForwardProtocols", "nObjCForwardProtocols"},
     {"_ZL18nObjCPropertyImpls", "nObjCPropertyImpls"},
     {"_ZL14nStaticAsserts", "nStaticAsserts"},
     {"_ZL17nTranslationUnits", "nTranslationUnits"},
     {"_ZL13StmtClassInfo", "StmtClassInfo"},
     {"_ZL11InlineLimit", "InlineLimit"},
     {"_ZL13HintThreshold", "HintThreshold"},
     {"_ZL13Disable256Bit", "Disable256Bit"},
     {"_ZL8NoFusing", "NoFusing"},
     {"_ZL17PrintFailedFusing", "PrintFailedFusing"},
     {"_ZL16ReMatPICStubLoad", "ReMatPICStubLoad"},
     {"_ZL19JITCompilerFunction", "JITCompilerFunction"},
     {"_ZL15AsmWriterFlavor", "AsmWriterFlavor"},
     {"_ZN4llvm12_GLOBAL__N_1L6CCRVTsE", "llvm::(anonymous namespace)::CCRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L14CONTROL_REGVTsE", "llvm::(anonymous namespace)::CONTROL_REGVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12DEBUG_REGVTsE", "llvm::(anonymous namespace)::DEBUG_REGVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7FR32VTsE", "llvm::(anonymous namespace)::FR32VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7FR64VTsE", "llvm::(anonymous namespace)::FR64VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7GR16VTsE", "llvm::(anonymous namespace)::GR16VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR16_ABCDVTsE", "llvm::(anonymous namespace)::GR16_ABCDVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR16_NOREXVTsE", "llvm::(anonymous namespace)::GR16_NOREXVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7GR32VTsE", "llvm::(anonymous namespace)::GR32VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR32_ABCDVTsE", "llvm::(anonymous namespace)::GR32_ABCDVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L10GR32_ADVTsE", "llvm::(anonymous namespace)::GR32_ADVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR32_NOREXVTsE", "llvm::(anonymous namespace)::GR32_NOREXVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR32_NOSPVTsE", "llvm::(anonymous namespace)::GR32_NOSPVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L10GR32_TCVTsE", "llvm::(anonymous namespace)::GR32_TCVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7GR64VTsE", "llvm::(anonymous namespace)::GR64VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR64_ABCDVTsE", "llvm::(anonymous namespace)::GR64_ABCDVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR64_NOREXVTsE", "llvm::(anonymous namespace)::GR64_NOREXVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L18GR64_NOREX_NOSPVTsE", "llvm::(anonymous namespace)::GR64_NOREX_NOSPVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR64_NOSPVTsE", "llvm::(anonymous namespace)::GR64_NOSPVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L10GR64_TCVTsE", "llvm::(anonymous namespace)::GR64_TCVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR64_TCW64VTsE", "llvm::(anonymous namespace)::GR64_TCW64VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6GR8VTsE", "llvm::(anonymous namespace)::GR8VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR8_ABCD_HVTsE", "llvm::(anonymous namespace)::GR8_ABCD_HVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L13GR8_ABCD_LVTsE", "llvm::(anonymous namespace)::GR8_ABCD_LVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12GR8_NOREXVTsE", "llvm::(anonymous namespace)::GR8_NOREXVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8RFP32VTsE", "llvm::(anonymous namespace)::RFP32VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8RFP64VTsE", "llvm::(anonymous namespace)::RFP64VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8RFP80VTsE", "llvm::(anonymous namespace)::RFP80VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6RSTVTsE", "llvm::(anonymous namespace)::RSTVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L14SEGMENT_REGVTsE", "llvm::(anonymous namespace)::SEGMENT_REGVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8VR128VTsE", "llvm::(anonymous namespace)::VR128VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8VR256VTsE", "llvm::(anonymous namespace)::VR256VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7VR64VTsE", "llvm::(anonymous namespace)::VR64VTs"},
     {"_ZL14EnableARM3Addr", "EnableARM3Addr"},
     {"_ZL20ForceAllBaseRegAlloc", "ForceAllBaseRegAlloc"},
     {"_ZL21EnableLocalStackAlloc", "EnableLocalStackAlloc"},
     {"_ZL17EnableBasePointer", "EnableBasePointer"},
     {"_ZN4llvm12_GLOBAL__N_1L6CCRVTsE", "llvm::(anonymous namespace)::CCRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6DPRVTsE", "llvm::(anonymous namespace)::DPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8DPR_8VTsE", "llvm::(anonymous namespace)::DPR_8VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L11DPR_VFP2VTsE", "llvm::(anonymous namespace)::DPR_VFP2VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6GPRVTsE", "llvm::(anonymous namespace)::GPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6QPRVTsE", "llvm::(anonymous namespace)::QPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8QPR_8VTsE", "llvm::(anonymous namespace)::QPR_8VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L11QPR_VFP2VTsE", "llvm::(anonymous namespace)::QPR_VFP2VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7QQPRVTsE", "llvm::(anonymous namespace)::QQPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L12QQPR_VFP2VTsE", "llvm::(anonymous namespace)::QQPR_VFP2VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L9QQQQPRVTsE", "llvm::(anonymous namespace)::QQQQPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L6SPRVTsE", "llvm::(anonymous namespace)::SPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8SPR_8VTsE", "llvm::(anonymous namespace)::SPR_8VTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7rGPRVTsE", "llvm::(anonymous namespace)::rGPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L7tGPRVTsE", "llvm::(anonymous namespace)::tGPRVTs"},
     {"_ZN4llvm12_GLOBAL__N_1L8tcGPRVTsE", "llvm::(anonymous namespace)::tcGPRVTs"},
     {"_ZL21AdjustJumpTableBlocks", "AdjustJumpTableBlocks"},
     {"_ZL18DisableARMFastISel", "DisableARMFastISel"},
     {"_ZL16DisableShifterOp", "DisableShifterOp"},
     {"_ZL15CheckVMLxHazard", "CheckVMLxHazard"},
     {"_ZL18EnableARMTailCalls", "EnableARMTailCalls"},
     {"_ZL15ARMInterworking", "ARMInterworking"},
     {"_ZL19JITCompilerFunction", "JITCompilerFunction"},
     {"_ZL9ReserveR9", "ReserveR9"},
     {"_ZL13DarwinUseMOVT", "DarwinUseMOVT"},
     {"_ZL11StrictAlign", "StrictAlign"},
     {"_ZL9ExpandMLx", "ExpandMLx"},
     {"_ZL11ForceExapnd", "ForceExapnd"},
     {"_ZL11ExpandLimit", "ExpandLimit"},
     {"_ZL10OldT2IfCvt", "OldT2IfCvt"},
     {"_ZL11ReduceLimit", "ReduceLimit"},
     {"_ZL16ReduceLimit2Addr", "ReduceLimit2Addr"},
     {"_ZL15ReduceLimitLdSt", "ReduceLimitLdSt"},
     {"_ZN12_GLOBAL__N_1L10CombinerAAE", "(anonymous namespace)::CombinerAA"},
     {"_ZN12_GLOBAL__N_1L16CombinerGlobalAAE", "(anonymous namespace)::CombinerGlobalAA"},
     {"_ZL21EnableExpensiveChecks", "EnableExpensiveChecks"},
     {"_ZL18tdListDAGScheduler", "tdListDAGScheduler"},
     {"_ZL20burrListDAGScheduler", "burrListDAGScheduler"},
     {"_ZL20tdrListrDAGScheduler", "tdrListrDAGScheduler"},
     {"_ZL22sourceListDAGScheduler", "sourceListDAGScheduler"},
     {"_ZL22hybridListDAGScheduler", "hybridListDAGScheduler"},
     {"_ZL19ILPListDAGScheduler", "ILPListDAGScheduler"},
     {"_ZL18DisableSchedCycles", "DisableSchedCycles"},
     {"_ZL4EVTs", "EVTs"},
     {"_ZL13SimpleVTArray", "SimpleVTArray"},
     {"_ZL7VTMutex", "VTMutex"},
     {"_ZL16LimitFPPrecision", "LimitFPPrecision"},
     {"_ZL19LimitFloatPrecision", "LimitFloatPrecision"},
     {"_ZL17MaxParallelChains", "MaxParallelChains"},
     {"_ZL21EnableFastISelVerbose", "EnableFastISelVerbose"},
     {"_ZL19EnableFastISelAbort", "EnableFastISelAbort"},
     {"_ZL11ISHeuristic", "ISHeuristic"},
     {"_ZL23defaultListDAGScheduler", "defaultListDAGScheduler"},
     {"_ZL13PrintDbgScope", "PrintDbgScope"},
     {"_ZL24DisableDebugInfoPrinting", "DisableDebugInfoPrinting"},
     {"_ZL16UnknownLocations", "UnknownLocations"},
     {"_ZL8DebugDiv", "DebugDiv"},
     {"_ZL8DebugMod", "DebugMod"},
     {"_ZL19FlagEnableTailMerge", "FlagEnableTailMerge"},
     {"_ZL18TailMergeThreshold", "TailMergeThreshold"},
     {"_ZL13TailMergeSize", "TailMergeSize"},
     {"_ZL15ViewEdgeBundles", "ViewEdgeBundles"},
     {"_ZL12IfCvtFnStart", "IfCvtFnStart"},
     {"_ZL11IfCvtFnStop", "IfCvtFnStop"},
     {"_ZL10IfCvtLimit", "IfCvtLimit"},
     {"_ZL13DisableSimple", "DisableSimple"},
     {"_ZL14DisableSimpleF", "DisableSimpleF"},
     {"_ZL15DisableTriangle", "DisableTriangle"},
     {"_ZL16DisableTriangleR", "DisableTriangleR"},
     {"_ZL16DisableTriangleF", "DisableTriangleF"},
     {"_ZL17DisableTriangleFR", "DisableTriangleFR"},
     {"_ZL14DisableDiamond", "DisableDiamond"},
     {"_ZL15IfCvtBranchFold", "IfCvtBranchFold"},
     {"_ZL12VerifySpills", "VerifySpills"},
     {"_ZL13DisablePostRA", "DisablePostRA"},
     {"_ZL17DisableBranchFold", "DisableBranchFold"},
     {"_ZL20DisableTailDuplicate", "DisableTailDuplicate"},
     {"_ZL19DisableEarlyTailDup", "DisableEarlyTailDup"},
     {"_ZL16DisableCodePlace", "DisableCodePlace"},
     {"_ZL10DisableSSC", "DisableSSC"},
     {"_ZL18DisableMachineLICM", "DisableMachineLICM"},
     {"_ZL24DisablePostRAMachineLICM", "DisablePostRAMachineLICM"},
     {"_ZL18DisableMachineSink", "DisableMachineSink"},
     {"_ZL10DisableLSR", "DisableLSR"},
     {"_ZL10DisableCGP", "DisableCGP"},
     {"_ZL8PrintLSR", "PrintLSR"},
     {"_ZL14PrintISelInput", "PrintISelInput"},
     {"_ZL11PrintGCInfo", "PrintGCInfo"},
     {"_ZL14ShowMCEncoding", "ShowMCEncoding"},
     {"_ZL10ShowMCInst", "ShowMCInst"},
     {"_ZL15EnableMCLogging", "EnableMCLogging"},
     {"_ZL17VerifyMachineCode", "VerifyMachineCode"},
     {"_ZL10AsmVerbose", "AsmVerbose"},
     {"_ZL20EnableFastISelOption", "EnableFastISelOption"},
     {"_ZL17EnableSplitGEPGVN", "EnableSplitGEPGVN"},
     {"_ZL9EnableLDV", "EnableLDV"},
     {"_ZL12DisableReMat", "DisableReMat"},
     {"_ZL10SplitEdges", "SplitEdges"},
     {"_ZL15defaultRegAlloc", "defaultRegAlloc"},
     {"_ZL8RegAlloc", "RegAlloc"},
     {"_ZL10Aggressive", "Aggressive"},
     {"_ZL15DisablePeephole", "DisablePeephole"},
     {"_ZL21EnablePostRAScheduler", "EnablePostRAScheduler"},
     {"_ZL21EnableAntiDepBreaking", "EnableAntiDepBreaking"},
     {"_ZL8DebugDiv", "DebugDiv"},
     {"_ZL8DebugMod", "DebugMod"},
     {"_ZL13PreSplitLimit", "PreSplitLimit"},
     {"_ZL14DeadSplitLimit", "DeadSplitLimit"},
     {"_ZL16RestoreFoldLimit", "RestoreFoldLimit"},
     {"_ZN12_GLOBAL__N_1L10PSVGlobalsE", "(anonymous namespace)::PSVGlobals"},
     {"_ZL12fastRegAlloc", "fastRegAlloc"},
     {"_ZL12NewHeuristic", "NewHeuristic"},
     {"_ZL17PreSplitIntervals", "PreSplitIntervals"},
     {"_ZL16TrivCoalesceEnds", "TrivCoalesceEnds"},
     {"_ZL18linearscanRegAlloc", "linearscanRegAlloc"},
     {"_ZN12_GLOBAL__N_1L19NumRecentlyUsedRegsE", "(anonymous namespace)::NumRecentlyUsedRegs"},
     {"_ZL14ShrinkWrapping", "ShrinkWrapping"},
     {"_ZL14ShrinkWrapFunc", "ShrinkWrapFunc"},
     {"_ZL19ShrinkWrapDebugging", "ShrinkWrapDebugging"},
     {"_ZL13EnableJoining", "EnableJoining"},
     {"_ZL21DisableCrossClassJoin", "DisableCrossClassJoin"},
     {"_ZL19DisablePhysicalJoin", "DisablePhysicalJoin"},
     {"_ZL16VerifyCoalescing", "VerifyCoalescing"},
     {"_ZL10spillerOpt", "spillerOpt"},
     {"_ZL13SSPBufferSize", "SSPBufferSize"},
     {"_ZL14DisableSharing", "DisableSharing"},
     {"_ZL16ColorWithRegsOpt", "ColorWithRegsOpt"},
     {"_ZL8DCELimit", "DCELimit"},
     {"_ZL17TailDuplicateSize", "TailDuplicateSize"},
     {"_ZL13TailDupVerify", "TailDupVerify"},
     {"_ZL12TailDupLimit", "TailDupLimit"},
     {"_ZL23DisableHazardRecognizer", "DisableHazardRecognizer"},
     {"_ZL11RewriterOpt", "RewriterOpt"},
     {"_ZL14ScheduleSpills", "ScheduleSpills"},
     {"_ZL17CriticalEdgeSplit", "CriticalEdgeSplit"},
     {"_ZL9EnablePRE", "EnablePRE"},
     {"_ZL13EnableLoadPRE", "EnableLoadPRE"},
     {"_ZL9Threshold", "Threshold"},
     {"_ZL16DisablePromotion", "DisablePromotion"},
     {"_ZL15UnrollThreshold", "UnrollThreshold"},
     {"_ZL11UnrollCount", "UnrollCount"},
     {"_ZL18UnrollAllowPartial", "UnrollAllowPartial"},
     {"_ZL9Threshold", "Threshold"},
     {"_ZL18ExpensiveEHSupport", "ExpensiveEHSupport"},
     {"_ZL6DupRet", "DupRet"},
     {"_ZL13MaxIterations", "MaxIterations"},
     {"_ZL14VerifyLoopInfo", "VerifyLoopInfo"},
     {"_ZL15VerifyLoopInfoX", "VerifyLoopInfoX"},
     {"_ZL23MaxBruteForceIterations", "MaxBruteForceIterations"},
     {"_ZL10EnableTBAA", "EnableTBAA"},
     {"_ZL9PrintCode", "PrintCode"},
     {"_ZL13DisableFPElim", "DisableFPElim"},
     {"_ZL20DisableFPElimNonLeaf", "DisableFPElimNonLeaf"},
     {"_ZL22DisableExcessPrecision", "DisableExcessPrecision"},
     {"_ZL11EnableFPMAD", "EnableFPMAD"},
     {"_ZL18EnableUnsafeFPMath", "EnableUnsafeFPMath"},
     {"_ZL18EnableNoInfsFPMath", "EnableNoInfsFPMath"},
     {"_ZL18EnableNoNaNsFPMath", "EnableNoNaNsFPMath"},
     {"_ZL38EnableHonorSignDependentRoundingFPMath", "EnableHonorSignDependentRoundingFPMath"},
     {"_ZL22GenerateSoftFloatCalls", "GenerateSoftFloatCalls"},
     {"_ZL16FloatABIForCalls", "FloatABIForCalls"},
     {"_ZL19DontPlaceZerosInBSS", "DontPlaceZerosInBSS"},
     {"_ZL26EnableJITExceptionHandling", "EnableJITExceptionHandling"},
     {"_ZL16EmitJitDebugInfo", "EmitJitDebugInfo"},
     {"_ZL22EmitJitDebugInfoToDisk", "EmitJitDebugInfoToDisk"},
     {"_ZL18EnableUnwindTables", "EnableUnwindTables"},
     {"_ZL18DefRelocationModel", "DefRelocationModel"},
     {"_ZL12DefCodeModel", "DefCodeModel"},
     {"_ZL27EnableGuaranteedTailCallOpt", "EnableGuaranteedTailCallOpt"},
     {"_ZL22OverrideStackAlignment", "OverrideStackAlignment"},
     {"_ZL18EnableRealignStack", "EnableRealignStack"},
     {"_ZL19DisableSwitchTables", "DisableSwitchTables"},
     {"_ZL19EnableStrongPHIElim", "EnableStrongPHIElim"},
     {"_ZL12DataSections", "DataSections"},
     {"_ZL16FunctionSections", "FunctionSections"},
     {"_ZL15AttributesLists", "AttributesLists"},
     {"_ZN4llvmL7ALMutexE", "llvm::ALMutex"},
     {"_ZL13VerifyDomInfo", "VerifyDomInfo"},
     {"_ZL14VerifyDomInfoX", "VerifyDomInfoX"},
     {"_ZL6GCLock", "GCLock"},
     {"_ZL7GCNames", "GCNames"},
     {"_ZL10GCNamePool", "GCNamePool"},
     {"_ZL13GlobalContext", "GlobalContext"},
     {"_ZN4llvmL13PassDebuggingE", "llvm::PassDebugging"},
     {"_ZN4llvmL11PrintBeforeE", "llvm::PrintBefore"},
     {"_ZN4llvmL10PrintAfterE", "llvm::PrintAfter"},
     {"_ZN4llvmL14PrintBeforeAllE", "llvm::PrintBeforeAll"},
     {"_ZN4llvmL13PrintAfterAllE", "llvm::PrintAfterAll"},
     {"_ZN12_GLOBAL__N_1L15TimingInfoMutexE", "(anonymous namespace)::TimingInfoMutex"},
     {"_ZL12EnableTiming", "EnableTiming"},
     {"_ZL11TheTimeInfo", "TheTimeInfo"},
     {"_ZL15PassRegistryObj", "PassRegistryObj"},
     {"_ZL4Lock", "Lock"},
     {"_ZL8MoreHelp", "MoreHelp"},
     {"_ZL20RegisteredOptionList", "RegisteredOptionList"},
     {"_ZL15ProgramOverview", "ProgramOverview"},
     {"_ZL13NormalPrinter", "NormalPrinter"},
     {"_ZL13HiddenPrinter", "HiddenPrinter"},
     {"_ZL3HOp", "HOp"},
     {"_ZL4HHOp", "HHOp"},
     {"_ZL6VersOp", "VersOp"},
     {"_ZL22VersionPrinterInstance", "VersionPrinterInstance"},
     {"_ZL22OverrideVersionPrinter", "OverrideVersionPrinter"},
     {"_ZN12_GLOBAL__N_1L14CurrentContextE", "(anonymous namespace)::CurrentContext"},
     {"_ZL25gCrashRecoveryContexMutex", "gCrashRecoveryContexMutex"},
     {"_ZL11PrevActions", "PrevActions"},
     {"_ZL5Dummy", "Dummy"},
     {"_ZL15ExplicitSymbols", "ExplicitSymbols"},
     {"_ZL13OpenedHandles", "OpenedHandles"},
     {"_ZL12ErrorHandler", "ErrorHandler"},
     {"_ZL20ErrorHandlerUserData", "ErrorHandlerUserData"},
     {"_ZL10StaticList", "StaticList"},
     {"_ZN12_GLOBAL__N_1L7successE", "(anonymous namespace)::success"},
     {"_ZN12_GLOBAL__N_1L10separatorsE", "(anonymous namespace)::separators"},
     {"_ZN12_GLOBAL__N_1L7successE", "(anonymous namespace)::success"},
     {"_ZL20PrettyStackTraceHead", "PrettyStackTraceHead"},
     {"_ZL12SignalsMutex", "SignalsMutex"},
     {"_ZL13FilesToRemove", "FilesToRemove"},
     {"_ZL14CallBacksToRun", "CallBacksToRun"},
     {"_ZL17InterruptFunction", "InterruptFunction"},
     {"_ZL20NumRegisteredSignals", "NumRegisteredSignals"},
     {"_ZL20RegisteredSignalInfo", "RegisteredSignalInfo"},
     {"_ZL7Enabled", "Enabled"},
     {"_ZL8StatInfo", "StatInfo"},
     {"_ZL8StatLock", "StatLock"},
     {"_ZL11FirstTarget", "FirstTarget"},
     {"_ZL11global_lock", "global_lock"},
     {"_ZL28LibSupportInfoOutputFilename", "LibSupportInfoOutputFilename"},
     {"_ZL9TimerLock", "TimerLock"},
     {"_ZN12_GLOBAL__N_1L10TrackSpaceE", "(anonymous namespace)::TrackSpace"},
     {"_ZN12_GLOBAL__N_1L18InfoOutputFilenameE", "(anonymous namespace)::InfoOutputFilename"},
     {"_ZL12ActiveTimers", "ActiveTimers"},
     {"_ZL11NamedTimers", "NamedTimers"},
     {"_ZL18NamedGroupedTimers", "NamedGroupedTimers"},
     {"_ZL14TimerGroupList", "TimerGroupList"},
     {"_ZL17DefaultTimerGroup", "DefaultTimerGroup"},
     {"_ZL16NotUnderValgrind", "NotUnderValgrind"},
     {"_ZN8Blizzard6Memory12voidp_returncvPT_IcEEv", "Blizzard::Memory::voidp_return::operator char*<char>()"},
     {"_Z2f0PU3AS1c", "f0(char AS1*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXleT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) <= (4), void>::type*)"},
     {"_ZN5Casts6cstyleILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::cstyle<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXaaT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) && (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXaNT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) &= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXaST_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) = (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXstT_EvE4typeE", "void Casts::implicit<4u>(enable_if<sizeof (4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXszT_EvE4typeE", "void Casts::implicit<4u>(enable_if<sizeof (4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXatT_EvE4typeE", "void Casts::implicit<4u>(enable_if<alignof (4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXazT_EvE4typeE", "void Casts::implicit<4u>(enable_if<alignof (4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXcmT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) , (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXcoT_EvE4typeE", "void Casts::implicit<4u>(enable_if<~(4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXdeT_EvE4typeE", "void Casts::implicit<4u>(enable_if<*(4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXdvT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) / (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXdVT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) /= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXeoT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) ^ (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXeOT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) ^= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXeqT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) == (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXgeT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) >= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXgtT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<((4u) > (4)), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXlsT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) << (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXlST_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) <<= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXltT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) < (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXmiT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) - (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXmIT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) -= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXmlT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) * (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXmLT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) *= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXmmT_EvE4typeE", "void Casts::implicit<4u>(enable_if<(4u)--, void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXneT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) != (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXngT_EvE4typeE", "void Casts::implicit<4u>(enable_if<-(4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXntT_EvE4typeE", "void Casts::implicit<4u>(enable_if<!(4u), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXooT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) || (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXorT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) | (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXoRT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) |= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXpmT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) ->* (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXplT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) + (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXpLT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) += (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXppT_EvE4typeE", "void Casts::implicit<4u>(enable_if<(4u)++, void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXpsT_EvE4typeE", "void Casts::implicit<4u>(enable_if<+(4u), void>::type*)"},
 //    {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXptT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) -> (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXquT_Li4ELi5EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) ? (4) : (5), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXrmT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) % (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXrMT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) %= (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXrsT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) >> (4), void>::type*)"},
     {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXrST_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) >>= (4), void>::type*)"},
     {"_Z1fPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP1XS13_S12_S11_S10_SZ_SY_SX_SW_SV_SU_ST_SS_SR_SQ_SP_SO_SN_SM_SL_SK_SJ_SI_SH_SG_SF_SE_SD_SC_SB_SA_S9_S8_S7_S6_S5_S4_S3_S2_S1_S0_S_", "f(X****************************************, X****************************************, X***************************************, X**************************************, X*************************************, X************************************, X***********************************, X**********************************, X*********************************, X********************************, X*******************************, X******************************, X*****************************, X****************************, X***************************, X**************************, X*************************, X************************, X***********************, X**********************, X*********************, X********************, X*******************, X******************, X*****************, X****************, X***************, X**************, X*************, X************, X***********, X**********, X*********, X********, X*******, X******, X*****, X****, X***, X**, X*, X)"},
     {"_ZZN1J1KEvENK1C1FEv", "J::K()::C::F() const"},
     {"_ZZNVK1J1KEvENK1C1FEv", "J::K() const volatile::C::F() const"},
     {"_Z1fM1AKFvvE", "f(void (A::*)() const)"},
     {"_ZNR1X1fEv", "X::f() &"},
     {"_ZNKO1X1hEv", "X::h() const &&"},
 //    {"_Z1fM1XVKFivEMS_VFivEMS_KOFivE", "f(int (X::*)() const volatile, int (X::*)() volatile, int (X::*)() const &&)"},
 //    {"_Z1fM1XRFivEMS_OFivEMS_KOFivE", "f(int (X::*)() &, int (X::*)() &&, int (X::*)() const &&)"},
     {"_ZN5test12f0ENS_1TILZNS_1xEEEE", "test1::f0(test1::T<test1::x>)"},
     {"_ZN5test12f1ENS_2t1ILZNS_2f0EfEEE", "test1::f1(test1::t1<test1::f0(float)>)"},
     {"_ZN5test22f1ENS_2t1IXadL_ZNS_2f0EfEEEE", "test2::f1(test2::t1<&(test2::f0(float))>)"},
     {"_ZN5test32f1ENS_2t1ILZ8test3_f0EEE", "test3::f1(test3::t1<test3_f0>)"},
     {"_ZN5test42f1ENS_2t1IXadL_Z8test4_f0EEEE", "test4::f1(test4::t1<&(test4_f0)>)"},
     {"_ZN5test52f1ENS_2t1ILZ8test5_f0EEE", "test5::f1(test5::t1<test5_f0>)"},
     {"_ZN5test52f2ENS_2t2ILZ4mainEEE", "test5::f2(test5::t2<main>)"},
     {"_ZN5test52f2ENS_2t2ILZ4mainEEE", "test5::f2(test5::t2<main>)"},
     {"_ZN5test62f0ENS_1TIXadL_ZNS_1A3im0EfEEEE", "test6::f0(test6::T<&(test6::A::im0(float))>)"},
     {"_ZN5test71XIiEC1IdEEPT_PNS_5int_cIXplL_ZNS_4metaIiE5valueEEsrNS6_IS3_EE5valueEE4typeE", "test7::X<int>::X<double>(double*, test7::int_c<(test7::meta<int>::value) + (test7::meta<double>::value)>::type*)"},
     {"_ZN5test81fIiEEvNS_5int_cIXsrNS_4metaIT_E4typeE5valueEEE", "void test8::f<int>(test8::int_c<test8::meta<int>::type::value>)"},
     {"_ZN5test91fIiNS_1XEEENS_9supermetaIT_E5applyIT0_E4typeEv", "test9::supermeta<int>::apply<test9::X>::type test9::f<int, test9::X>()"},
     {"_ZN6test101fIidEENS_1XIT_E10definitionIT0_EES2_S5_", "test10::X<int>::definition<double> test10::f<int, double>(int, double)"},
     {"_ZZ2f5vE1a", "f5()::a"},
     {"_ZZ2f6vE1b", "f6()::b"},
     {"_ZNV3$_35test9Ev", "$_3::test9() volatile"},
     {"_ZN5Test8I3$_2EC1ES0_", "Test8<$_2>::Test8($_2)"},
     {"_Z3fooIZN3BarC1EvE3$_0EvT_", "void foo<Bar::Bar()::$_0>(Bar::Bar()::$_0)"},
     {"_ZGVZN1N1gEvE1a", "guard variable for N::g()::a"},
     {"_ZplRK1YRA100_P1X", "operator+(Y const&, X* (&) [100])"},
     {"_Z1fno", "f(__int128, unsigned __int128)"},
     {"_Z1fM1SKFvvE", "f(void (S::*)() const)"},
     {"_Z3ft7IiEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv", "__enable_if<__is_scalar_type<int>::__value, void>::__type ft7<int>()"},
     {"_Z3ft7IPvEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv", "__enable_if<__is_scalar_type<void*>::__value, void>::__type ft7<void*>()"},
     {"_ZN6PR57968__fill_aIiEENS_11__enable_ifIXntsrNS_11__is_scalarIT_EE7__valueEvE6__typeEv", "PR5796::__enable_if<!(PR5796::__is_scalar<int>::__value), void>::__type PR5796::__fill_a<int>()"},
     {"_ZN11Expressions2f1ILi1EEEvPAplngT_Li2E_i", "void Expressions::f1<1>(int (*) [(-(1)) + (2)])"},
     {"_ZN11Expressions2f2ILi1EEEvPApsT__i", "void Expressions::f2<1>(int (*) [+(1)])"},
     {"_ZN11Expressions2f3ILi1EEEvPAplT_T__i", "void Expressions::f3<1>(int (*) [(1) + (1)])"},
     {"_ZN11Expressions2f4ILi1EEEvPAplplLi2ET_T__i", "void Expressions::f4<1>(int (*) [((2) + (1)) + (1)])"},
     {"_ZN3OpsplERKS_", "Ops::operator+(Ops const&)"},
     {"_ZN6PR58615AllocIcNS_6PolicyINS_1PELb1EEEE8allocateEiPKv", "PR5861::Alloc<char, PR5861::Policy<PR5861::P, true> >::allocate(int, void const*)"},
     {"_ZN5test01fIdEEvT_RAszcl3ovlcvS1__EE_c", "void test0::f<double>(double, char (&) [sizeof (ovl((double)()))])"},
     {"_ZN5test01fIiEEvT_RAszcl3ovlcvS1__EE_c", "void test0::f<int>(int, char (&) [sizeof (ovl((int)()))])"},
     {"_ZN5test01jINS_1AEEEvRAszdtcvT__E6buffer_c", "void test0::j<test0::A>(char (&) [sizeof ((test0::A)().buffer)])"},
     {"_ZN5test11fINS_1XEiEEvT_IT0_E", "void test1::f<test1::X, int>(test1::X<int>)"},
     {"_ZN5test211read_memberINS_1AEEEDtptcvPT_Li0E6memberERS2_", "decltype((test2::A*)(0)->member) test2::read_member<test2::A>(test2::A&)"},
     {"_ZN5test37get_p_1INS_7DerivedEEEDtptcvPT_Li0EsrNS_5Path1E1pERS2_", "decltype((test3::Derived*)(0)->test3::Path1::p) test3::get_p_1<test3::Derived>(test3::Derived&)"},
     {"_ZN5test37get_p_1INS_7DerivedEEEDtptcvPT_Li0EsrNS_5Path2E1pERS2_", "decltype((test3::Derived*)(0)->test3::Path2::p) test3::get_p_1<test3::Derived>(test3::Derived&)"},
     {"_ZN5test41gEPNS_3zedIXadL_ZNS_3foo3barEEEEE", "test4::g(test4::zed<&(test4::foo::bar)>*)"},
     {"_ZN5test51gEPNS_3zedIXadL_ZNS_3foo3barEEEEE", "test5::g(test5::zed<&(test5::foo::bar)>*)"},
     {"_ZN5test71gEPNS_3zedIXadL_ZNS_3foo3barEvEEEE", "test7::g(test7::zed<&(test7::foo::bar())>*)"},
     {"_ZN5test91fIiNS_3barEEEvRKNT0_3baz1XE", "void test9::f<int, test9::bar>(test9::bar::baz::X const&)"},
     {"_ZN6test101fILc3EEEvNS_1SIXquLb0ELc97ET_EEE", "void test10::f<(char)3>(test10::S<(false) ? ((char)97) : ((char)3)>)"},
     {"_ZN6test111fEz", "test11::f(...)"},
     {"_ZN6test121fENS_1AILt33000EEE", "test12::f(test12::A<(unsigned short)33000>)"},
     {"_ZN6test151fILi7EEEvNS_1SIXplT_LNS_1EE3EEEE", "void test15::f<7>(test15::S<(7) + ((test15::E)3)>)"},
     {"_ZN6test174funcINS_1BEEENS_1AIXszclsrT_3fooEEEEv", "test17::A<sizeof (test17::B::foo())> test17::func<test17::B>()"},
     {"_ZN6test181fINS_1AEEEvNS_1SIXadsrT_plEEE", "void test18::f<test18::A>(test18::S<&(test18::A::operator+)>)"},
     {"_ZN6test181fINS_1AEEEvNS_1SIXadsrT_anEEE", "void test18::f<test18::A>(test18::S<&(test18::A::operator&)>)"},
     {"_ZN6test191gINS_1AEEEvNS_1SIXadsrT_1fIiEEEE", "void test19::g<test19::A>(test19::S<&(test19::A::f<int>)>)"},
     {"_ZN6test191gINS_1AEEEvNS_1SIXadsrT_plEEE", "void test19::g<test19::A>(test19::S<&(test19::A::operator+)>)"},
     {"_ZN6test191gINS_1AEEEvNS_1SIXadsrT_cviEEE", "void test19::g<test19::A>(test19::S<&(test19::A::operator int)>)"},
     {"_ZN6test191gINS_1AEEEvNS_1SIXadsrT_miIdEEEE", "void test19::g<test19::A>(test19::S<&(test19::A::operator-<double>)>)"},
     {"_ZN6test205test0IiEEvDTcl1fIPT_ELi0EEE", "void test20::test0<int>(decltype(f<int*>(0)))"},
     {"_ZN6test2112vla_arg_funcEiPA_i", "test21::vla_arg_func(int, int (*) [])"},
     {"_ZN6test221fEDn", "test22::f(std::nullptr_t)"},
     {"_ZN6test231fERA10_KPv", "test23::f(void* const (&) [10])"},
     {"_ZN6test231fERA10_A5_VKPv", "test23::f(void* const volatile (&) [10][5])"},
     {"_Z4testIcLj5ELj10ELj15EEvRAT0__AT1__AT2__T_", "void test<char, 5u, 10u, 15u>(char (&) [5u][10u][15u])"},
     {"_ZN6test205test1IiEEvDTcl1fIEcvT__EEE", "void test20::test1<int>(decltype(f<>((int)())))"},
     {"_Z2f0IJEEv1XIXsZT_EJDpRT_EE", "void f0<>(X<sizeof...()>)"},
     {"_Z2f0IJifdEEv1XIXsZT_EJDpRT_EE", "void f0<int, float, double>(X<sizeof...(int, float, double), int&, float&, double&>)"},
     {"_Z2f1IJEEvv", "void f1<>()"},
     {"_Z2f1IJiEEvv", "void f1<int>()"},
     {"_Z2f1IJifEEvv", "void f1<int, float>()"},
     {"_Z2f2IJEEvDpT_", "void f2<>()"},
     {"_Z2f2IJiEEvDpT_", "void f2<int>(int)"},
     {"_Z2f2IJifEEvDpT_", "void f2<int, float>(int, float)"},
     {"_Z2f3IJEEvDpPKT_", "void f3<>()"},
     {"_Z2f3IJiEEvDpPKT_", "void f3<int>(int const*)"},
     {"_Z2f3IJifEEvDpPKT_", "void f3<int, float>(int const*, float const*)"},
     {"_Z2f4IJifdEE5tupleIJDpT_EEv", "tuple<int, float, double> f4<int, float, double>()"},
     {"_Z2f5IiJifdEE8identityIFT_DpT0_EEv", "identity<int (int, float, double)> f5<int, int, float, double>()"},
     {"_Z2f6IJLi1ELi2ELi3EEE9int_tupleIJXspT_EEEv", "int_tuple<1, 2, 3> f6<1, 2, 3>()"},
     {"_Z2f7IJ8identity13add_referenceEE14template_tupleIJDpT_EEv", "template_tuple<identity, add_reference> f7<identity, add_reference>()"},
     {"_ZNK10__cxxabiv111__libcxxabi5__sub20first_demangled_nameEPc.eh", "__cxxabiv1::__libcxxabi::__sub::first_demangled_name(char*) const (.eh)"},
     {"_ZSt13copy_backwardIN9__gnu_cxx17__normal_iteratorIPNS0_19_Hashtable_iteratorISt4pairIK16CSCppSymbolOwnerP20CSCppSymbolOwnerDataES4_27CSCppSymbolOwnerHashFunctorSt10_Select1stIS8_E29CSCppSymbolOwnerEqualsFunctorSaIS7_EEESt6vectorISE_SaISE_EEEESJ_ET0_T_SL_SK_", "__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > > std::copy_backward<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >*, std::vector<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> >, std::allocator<__gnu_cxx::_Hashtable_iterator<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*>, CSCppSymbolOwner, CSCppSymbolOwnerHashFunctor, std::_Select1st<std::pair<CSCppSymbolOwner const, CSCppSymbolOwnerData*> >, CSCppSymbolOwnerEqualsFunctor, std::allocator<CSCppSymbolOwnerData*> > > > >)"},
     {"_ZTC14SSDatabaseImpl0_N8Security10CssmClient6DbImplE", "construction vtable for Security::CssmClient::DbImpl-in-SSDatabaseImpl"},
     {"_ZGRZN1N1gEvE1a", "reference temporary for N::g()::a"},
     {"_ZN5boost4bindIvN10libtorrent15peer_connectionEiRKNS1_11disk_io_jobENS1_12peer_requestENS_10shared_ptrINS1_7torrentEEENS_13intrusive_ptrIS2_EENS_3argILi1EEENSC_ILi2EEES6_S9_EENS_3_bi6bind_tIT_NS_4_mfi3mf4ISH_T0_T1_T2_T3_T4_EENSF_9list_av_5IT5_T6_T7_T8_T9_E4typeEEEMSK_FSH_SL_SM_SN_SO_ESR_SS_ST_SU_SV_", "boost::_bi::bind_t<void, boost::_mfi::mf4<void, libtorrent::peer_connection, int, libtorrent::disk_io_job const&, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent> >, boost::_bi::list_av_5<boost::intrusive_ptr<libtorrent::peer_connection>, boost::arg<1>, boost::arg<2>, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent> >::type> boost::bind<void, libtorrent::peer_connection, int, libtorrent::disk_io_job const&, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent>, boost::intrusive_ptr<libtorrent::peer_connection>, boost::arg<1>, boost::arg<2>, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent> >(void (libtorrent::peer_connection::*)(int, libtorrent::disk_io_job const&, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent>), boost::intrusive_ptr<libtorrent::peer_connection>, boost::arg<1>, boost::arg<2>, libtorrent::peer_request, boost::shared_ptr<libtorrent::torrent>)"},
     {"_ZN7WebCore20createFileThreadTaskINS_15FileStreamProxyEN3WTF6StringERKS3_ddEENS2_10PassOwnPtrINS_10FileThread4TaskEEEPT_MSA_FvT1_T3_ERKT0_RKT2_", "WTF::PassOwnPtr<WebCore::FileThread::Task> WebCore::createFileThreadTask<WebCore::FileStreamProxy, WTF::String, WTF::String const&, double, double>(WebCore::FileStreamProxy*, void (WebCore::FileStreamProxy::*)(WTF::String const&, double), WTF::String const&, double const&)"},
     {"_ZN3WTF15deleteAllValuesIPN7WebCore5XPath4Step8NodeTestEKNS_9HashTableIS5_S5_NS_17IdentityExtractorENS_7PtrHashIS5_EENS_10HashTraitsIS5_EESB_EEEEvRT0_", "void WTF::deleteAllValues<WebCore::XPath::Step::NodeTest*, WTF::HashTable<WebCore::XPath::Step::NodeTest*, WebCore::XPath::Step::NodeTest*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::XPath::Step::NodeTest*>, WTF::HashTraits<WebCore::XPath::Step::NodeTest*>, WTF::HashTraits<WebCore::XPath::Step::NodeTest*> > const>(WTF::HashTable<WebCore::XPath::Step::NodeTest*, WebCore::XPath::Step::NodeTest*, WTF::IdentityExtractor, WTF::PtrHash<WebCore::XPath::Step::NodeTest*>, WTF::HashTraits<WebCore::XPath::Step::NodeTest*>, WTF::HashTraits<WebCore::XPath::Step::NodeTest*> > const&)"},
     {"_Z13JVTLib_103270ILi1EEvPsDv2_xS1_", "void JVTLib_103270<1>(short*, long long vector[2], long long vector[2])"},
     {"_ZN8platform20split_string_convertIcPFiRKSsEiSaIiESt6vectorEEjPKT_S9_S7_T0_RT3_IT1_T2_E", "unsigned int platform::split_string_convert<char, int (*)(std::string const&), int, std::allocator<int>, std::vector>(char const*, char const*, char, int (*)(std::string const&), std::vector<int, std::allocator<int> >&)"},
     {"_ZN2MF12_GLOBAL__N_114WeakCallHelperINS0_15DecodeQueueImplEEEvRKN5boost8functionIFvvEEERKNS3_8weak_ptrIT_EE", "void MF::(anonymous namespace)::WeakCallHelper<MF::(anonymous namespace)::DecodeQueueImpl>(boost::function<void ()> const&, boost::weak_ptr<MF::(anonymous namespace)::DecodeQueueImpl> const&)"},
     {"_ZZN4NIds4NStr14TCStrAggregateINS0_13TCTCStrTraitsINS0_11TCStrTraitsIcNS0_17CDefaultStrParamsEEENS0_14TCStrImp_FixedIS5_Lx256EEEEEE21f_AddFromIteratorUTF8INS0_16CStrIteratorUTF8EEEvRxRKT_ENKSA_ISB_EUlmE0_clEm", "void NIds::NStr::TCStrAggregate<NIds::NStr::TCTCStrTraits<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, NIds::NStr::TCStrImp_Fixed<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, 256ll> > >::f_AddFromIteratorUTF8<NIds::NStr::CStrIteratorUTF8>(long long&, NIds::NStr::CStrIteratorUTF8 const&)::NIds::NStr::TCStrAggregate<NIds::NStr::TCTCStrTraits<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, NIds::NStr::TCStrImp_Fixed<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, 256ll> > >::f_AddFromIteratorUTF8<NIds::NStr::CStrIteratorUTF8>::'lambda0'(unsigned long)::operator()(unsigned long) const"},
     {"_ZZN4NIds4NStr14TCStrAggregateINS0_13TCTCStrTraitsINS0_11TCStrTraitsIcNS0_17CDefaultStrParamsEEENS0_14TCStrImp_FixedIS5_Lx256EEEEEE21f_AddFromIteratorUTF8INS0_16CStrIteratorUTF8EEEvRxRKT_ENKSA_ISB_EUt0_clEm", "void NIds::NStr::TCStrAggregate<NIds::NStr::TCTCStrTraits<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, NIds::NStr::TCStrImp_Fixed<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, 256ll> > >::f_AddFromIteratorUTF8<NIds::NStr::CStrIteratorUTF8>(long long&, NIds::NStr::CStrIteratorUTF8 const&)::NIds::NStr::TCStrAggregate<NIds::NStr::TCTCStrTraits<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, NIds::NStr::TCStrImp_Fixed<NIds::NStr::TCStrTraits<char, NIds::NStr::CDefaultStrParams>, 256ll> > >::f_AddFromIteratorUTF8<NIds::NStr::CStrIteratorUTF8>::'unnamed0'::operator()(unsigned long) const"},
     {"_ZNK3com9markzware2js11cJSArgumentcvRKT_I8cMyClassEEv", "com::markzware::js::cJSArgument::operator cMyClass const&<cMyClass>() const"},
     {"_ZNKSt3__110__function6__funcIZN4DLCL8DLFutureIP15AnalysenManagerE3setINS_8functionIFS5_vEEEJEEEvT_DpOT0_EUlvE_NS_9allocatorISF_EEFvvEE7__cloneEv", "std::__1::__function::__func<void DLCL::DLFuture<AnalysenManager*>::set<std::__1::function<AnalysenManager* ()> >(std::__1::function<AnalysenManager* ()>)::'lambda'(), std::__1::allocator<void DLCL::DLFuture<AnalysenManager*>::set<std::__1::function<AnalysenManager* ()> >(std::__1::function<AnalysenManager* ()>)::'lambda'()>, void ()>::__clone() const"},
     {"___ZN19URLConnectionClient33_clientInterface_cancelConnectionEP16dispatch_queue_sU13block_pointerFvvE_block_invoke14", "invocation function for block in URLConnectionClient::_clientInterface_cancelConnection(dispatch_queue_s*, void () block_pointer)"},
     {"_Z1fIJicdEEPFvDpT_EPFvDpRPS0_ES8_S1_DpS4_S6_", "void (*f<int, char, double>(void (*)(int*&, char*&, double*&), void (*)(int*&, char*&, double*&), int, char, double, int*, char*, double*, int*&, char*&, double*&))(int, char, double)"},
     {"_ZSteqIcEN9__gnu_cxx11__enable_ifIXsr9__is_charIT_EE7__valueEbE6__typeERKSbIS2_St11char_traitsIS2_ESaIS2_EESA_", "__gnu_cxx::__enable_if<__is_char<char>::__value, bool>::__type std::operator==<char>(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)"},
     {"_ZZ10+[Foo bar]E3Baz", "+[Foo bar]::Baz"},
     {"_ZN9__gnu_cxx17__normal_iteratorIPKSt4pairISsbESt6vectorIS2_SaIS2_EEEC5ERKS4_", "__gnu_cxx::__normal_iterator<std::pair<std::string, bool> const*, std::vector<std::pair<std::string, bool>, std::allocator<std::pair<std::string, bool> > > >::__normal_iterator(std::pair<std::string, bool> const* const&)"},
     {"_Z1fIiEDTeqfp_LDnEEPT_", "decltype((fp) == (std::nullptr_t)) f<int>(int*)"},
     {"_Z1fIiEDTeqfp1_LDnEEicPT_", "decltype((fp1) == (std::nullptr_t)) f<int>(int, char, int*)"},
     {"_ZZN1S1fEiiEd0_NKUlvE_clEv", "S::f(int, int)::'lambda'()::operator()() const"},
     {"_Z3fooPM2ABi", "foo(int AB::**)"},
     {"_Z1rM1GFivEMS_KFivES_M1HFivES1_4whatIKS_E5what2IS8_ES3_", "r(int (G::*)(), int (G::*)() const, G, int (H::*)(), int (G::*)(), what<G const>, what2<G const>, int (G::*)() const)"},
     {"_Z1fPU11objcproto1A11objc_object", "f(id<A>)"},
     {"_Z1fPKU11objcproto1A7NSArray", "f(NSArray<A> const*)"},
     {"_ZNK1AIJ1Z1Y1XEEcv1BIJDpPT_EEIJS2_S1_S0_EEEv", "A<Z, Y, X>::operator B<X*, Y*, Z*><X, Y, Z>() const"},
     {"_ZNK3Ncr6Silver7Utility6detail12CallOnThreadIZ53-[DeploymentSetupController handleManualServerEntry:]E3$_5EclIJEEEDTclclL_ZNS2_4getTIS4_EERT_vEEspclsr3stdE7forwardIT_Efp_EEEDpOSA_", "decltype(-[DeploymentSetupController handleManualServerEntry:]::$_5& Ncr::Silver::Utility::detail::getT<-[DeploymentSetupController handleManualServerEntry:]::$_5>()()(std::forward<-[DeploymentSetupController handleManualServerEntry:]::$_5>(fp))) Ncr::Silver::Utility::detail::CallOnThread<-[DeploymentSetupController handleManualServerEntry:]::$_5>::operator()<>(-[DeploymentSetupController handleManualServerEntry:]::$_5&&) const"},
     {"_Zli2_xy", "operator\"\" _x(unsigned long long)"},
     {"_Z1fIiEDcT_", "decltype(auto) f<int>(int)"},
     {"_ZZ4testvEN1g3fooE5Point", "test()::g::foo(Point)"},
     {"_ZThn12_NSt9strstreamD0Ev",   "non-virtual thunk to std::strstream::~strstream()"},
     {"_ZTv0_n12_NSt9strstreamD0Ev",     "virtual thunk to std::strstream::~strstream()"},
 
     // NOTE: disable this test since it is a negative test case, you cannot demangle a non-mangled symbol
     // {"\x6D", nullptr}, 	// This use to crash with ASAN
     {"_ZTIU4_farrVKPi", "typeinfo for int* const volatile restrict _far"},
 
     // mangled names can include type manglings too, which don't start with _Z:
     {"i", "int"},
 
     {"PKFvRiE", "void (*)(int&) const"},
-    // FIXME(compnerd) pretty print this as void (*)(unsigned long &) volatile &&
-    {"PVFvRmOE", "void (*)(unsigned long&)  volatile&&"},
+    {"PVFvRmOE", "void (*)(unsigned long&) volatile &&"},
     {"PFvRmOE", "void (*)(unsigned long&) &&"},
     {"_ZTW1x", "thread-local wrapper routine for x"},
     {"_ZTHN3fooE", "thread-local initialization routine for foo"},
     {"_Z4algoIJiiiEEvZ1gEUlT_E_", "void algo<int, int, int>(g::'lambda'(int, int, int))"},
 };
 
 const unsigned N = sizeof(cases) / sizeof(cases[0]);
 
 struct FPLiteralCase {
     const char *mangled;
     // There are four possible demanglings of a given float.
     std::string expecting[4];
 } fp_literal_cases[] =
 {
     {"_ZN5test01gIfEEvRAszplcvT__ELf40a00000E_c", {
         "void test0::g<float>(char (&) [sizeof (((float)()) + (0x1.4p+2f))])",
         "void test0::g<float>(char (&) [sizeof (((float)()) + (0x2.8p+1f))])",
         "void test0::g<float>(char (&) [sizeof (((float)()) + (0x5p+0f))])",
         "void test0::g<float>(char (&) [sizeof (((float)()) + (0xap-1f))])",
     }},
     {"_ZN5test01hIfEEvRAszplcvT__ELd4014000000000000E_c", {
         "void test0::h<float>(char (&) [sizeof (((float)()) + (0x1.4p+2))])",
         "void test0::h<float>(char (&) [sizeof (((float)()) + (0x2.8p+1))])",
         "void test0::h<float>(char (&) [sizeof (((float)()) + (0x5p+0))])",
         "void test0::h<float>(char (&) [sizeof (((float)()) + (0xap-1))])",
     }},
 #if LDBL_FP80
     {"_ZN5test01hIfEEvRAcvjplstT_Le4001a000000000000000E_c", {
         "void test0::h<float>(char (&) [(unsigned int)((sizeof (float)) + (0x1.4p+2L))])",
         "void test0::h<float>(char (&) [(unsigned int)((sizeof (float)) + (0x2.8p+1L))])",
         "void test0::h<float>(char (&) [(unsigned int)((sizeof (float)) + (0x5p+0L))])",
         "void test0::h<float>(char (&) [(unsigned int)((sizeof (float)) + (0xap-1L))])",
     }},
 #endif
 };
 const unsigned NF = sizeof(fp_literal_cases) / sizeof(fp_literal_cases[0]);
 const unsigned NEF = sizeof(fp_literal_cases[0].expecting) / sizeof(fp_literal_cases[0].expecting[0]);
 
 
 const char* invalid_cases[] =
 {
     "_ZIPPreEncode",
     "Agentt",
     "NSoERj5E=Y1[uM:ga",
     "Aon_PmKVPDk7?fg4XP5smMUL6;<WsI_mgbf23cCgsHbT<l8EE\0uVRkNOoXDrgdA4[8IU>Vl<>IL8ayHpiVDDDXTY;^o9;i",
     "_ZNSt16allocator_traitsISaIN4llvm3sys2fs18directory_iteratorEEE9constructIS3_IS3_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS4_PT_DpOS7_",
 #if !LDBL_FP80
     "_ZN5test01hIfEEvRAcvjplstT_Le4001a000000000000000E_c",
 #endif
 	// The following test cases were found by libFuzzer+ASAN
     "\x44\x74\x70\x74\x71\x75\x34\x43\x41\x72\x4D\x6E\x65\x34\x9F\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x34\xD3\x73\x9E\x2A\x37",
     "\x4D\x41\x72\x63\x4E\x39\x44\x76\x72\x4D\x34\x44\x53\x4B\x6F\x44\x54\x6E\x61\x37\x47\x77\x78\x38\x43\x27\x41\x5F\x73\x70\x69\x45*",
     "\x41\x64\x6E\x32*",
     "\x43\x46\x41\x67\x73*",
     "\x72\x3A\x4E\x53\x64\x45\x39\x4F\x52\x4E\x1F\x43\x34\x64\x54\x5F\x49\x31\x41\x63\x6C\x37\x2A\x4D\x41\x67\x73\x76\x43\x54\x35\x5F\x49\x4B\x4C\x55\x6C\x73\x4C\x38\x64\x43\x41\x47\x4C\x5A\x28\x4F\x41\x6E\x77\x5F\x53\x6F\x70\x69\x45\x5F\x63\x47\x61\x4C\x31\x4F\x4C\x33\x3E\x41\x4C\x4B\x4C\x55\x6C\x73\x4C\x38\x64\x43\x66\x41\x47\x4C\x5A\x28\x4F\x41\x6E\x77\x5F\x53\x6F\x70\x69\x45\x5F\x37\x41*",
     "\x2D\x5F\x63\x47\x4F\x63\xD3",
     "\x44\x74\x70\x74\x71\x75\x32\x43\x41\x38\x65\x6E\x9B\x72\x4D\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\xC3\x53\xF9\x5F\x70\x74\x70\x69\x45\x38\xD3\x73\x9E\x2A\x37",
     "\x4C\x5A\x4C\x55\x6C\x4D\x41\x5F\x41\x67\x74\x71\x75\x34\x4D\x41\x64\x73\x4C\x44\x76\x72\x4D\x34\x44\x4B\x44\x54\x6E\x61\x37\x47\x77\x78\x38\x43\x27\x41\x5F\x73\x70\x69\x45\x6D\x73\x72\x53\x41\x6F\x41\x7B",
     "\x44\x74\x70\x74\x71\x75\x32\x43\x41\x38\x65\x6E\x9B\x72\x4D\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x2C\x53\xF9\x5F\x70\x74\x70\x69\x45\xB4\xD3\x73\x9F\x2A\x37",
     "\x4C\x5A\x4C\x55\x6C\x69\x4D\x73\x72\x53\x6F\x7A\x41\x5F\x41\x67\x74\x71\x75\x32\x4D\x41\x64\x73\x39\x28\x76\x72\x4D\x34\x44\x4B\x45\x54\x6E\x61\x37\x47\x77\x78\x38\x43\x27\x41\x5F\x73\x70\x69\x45\x6F\x45\x49\x6D\x1A\x4C\x53\x38\x6A\x7A\x5A",
     "\x44\x74\x63*",
     "\x44\x74\x71\x75\x35\x2A\xDF\x74\x44\x61\x73\x63\x35\x2A\x3B\x41\x72\x4D\x6E\x65\x34\x9F\xC1\x63\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x33\x44\x76\x35",
     "\x44\x74\x70\x74\x71\x75\x32\x43\x41\x38\x65\x6E\x9B\x72\x4D\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x38\xD3\x73\x9E\x2A\x37",
     "\x46\x44\x74\x70\x74\x71\x75\x32\x43\x41\x72\x4D\x6E\x65\x34\x9F\xC1\x43\x41\x72\x4D\x6E\x77\x38\x9A\x8E\x44\x6F\x64\x6C\x53\xF9\x5F\x70\x74\x70\x69\x45\x34\xD3\x73\x9E\x2A\x37\x72\x33\x8E\x3A\x29\x8E\x44\x35",
     "_ZcvCiIJEEDvT__FFFFT_vT_v",
     "Z1JIJ1_T_EE3o00EUlT_E0",
     "___Z2i_D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D",
 };
 
 const unsigned NI = sizeof(invalid_cases) / sizeof(invalid_cases[0]);
 
 void test()
 {
+    int fails = 0;
     std::size_t len = 0;
     char* buf = nullptr;
     for (unsigned i = 0; i < N; ++i)
     {
         int status;
         char* demang = __cxxabiv1::__cxa_demangle(cases[i][0], buf, &len, &status);
         if (demang == 0 || std::strcmp(demang, cases[i][1]) != 0)
         {
-            std::cout << cases[i][0] << " -> " << cases[i][1] << '\n';
+            std::cout << "name " << i << ": " << cases[i][0] << "\n"
+                      <<     "Expected   : " << cases[i][1] << '\n';
+            ++fails;
             if (demang)
             {
                 std::cout << "Got instead: " << demang << '\n';
-                assert(std::strcmp(demang, cases[i][1]) == 0);
             }
             else
             {
                 std::cout << "Got instead: NULL, " << status << '\n';
-                assert(demang != 0);
             }
         }
         else
         {
             buf = demang;
         }
     }
+    if (fails) {
+        std::cout << fails << " failures\n";
+        assert(false);
+    }
     free(buf);
 }
 
 void test2()
 {
     std::size_t len = 0;
     char* buf = nullptr;
     for (unsigned i = 0; i < NI; ++i)
     {
         int status;
         char* demang = __cxxabiv1::__cxa_demangle(invalid_cases[i], buf, &len, &status);
         if (status != -2)
         {
             std::cout << invalid_cases[i] << " should be invalid but is not\n" << " got status = " << status << '\n';
             assert(status == -2);
         }
         else
         {
             buf = demang;
         }
     }
     free(buf);
 }
 
 void testFPLiterals()
 {
     std::size_t len = 0;
     char* buf = nullptr;
     for (unsigned i = 0; i < NF; ++i)
     {
         FPLiteralCase *fpCase = fp_literal_cases+i;
         int status;
         char* demang = __cxxabiv1::__cxa_demangle(fpCase->mangled, buf, &len, &status);
         if (demang == 0)
         {
             std::cout << fpCase->mangled << " -> " << fpCase->expecting[0] << '\n';
             std::cout << "Got instead: NULL, " << status << '\n';
             assert(false);
             continue;
         }
         std::string *e_beg = fpCase->expecting;
         std::string *e_end = fpCase->expecting + NEF;
         if (std::find(e_beg, e_end, demang) == e_end)
         {
             std::cout << fpCase->mangled << " -> " << fpCase->expecting[0] << '\n';
             std::cout << "Got instead: " << demang << '\n';
             assert(false);
             continue;
         }
         buf = demang;
     }
     free(buf);
 }
 
 int main()
 {
     std::cout << "Testing " << N << " symbols." << std::endl;
     {
         timer t;
         test();
         test2();
         testFPLiterals();
     }
 #if 0
     std::string input;
     while (std::cin)
     {
         std::getline(std::cin, input);
         if (std::cin.fail())
             break;
         std::size_t len = 0;
         int status;
         len = 0;
         char* demang = abi::__cxa_demangle(input.c_str(), 0, &len, &status);
         switch (status)
         {
         case -3:
             std::cout << "Invalid arguments\n";
             break;
         case -2:
             std::cout << "Invalid mangled name\n";
             break;
         case -1:
             std::cout << "memory allocation failure\n";
             break;
         case 0:
             std::cout << "len = " << len << '\n';
             std::cout << demang << '\n';
             std::free(demang);
             break;
         case 1:
             std::cout << "not immplemented\n";
             break;
         }
         std::cout << '\n';
     }
 #endif
 }


More information about the llvm-dev mailing list