Implement demangling support for C++20 lambda expression extensions.
This implements demangling support for the mangling extensions specified
in https://github.com/itanium-cxx-abi/cxx-abi/pull/85, much of which is
implemented in Clang r359967 and r371004.
Specifically, this provides demangling for:
* <template-param-decl> in <lambda-sig>
* <template-param> with non-zero level
* lambda-expression literals (not emitted by Clang yet)
* nullptr literals
* string literals
(The final two seem unrelated, but handling them was necessary in order
to disambiguate between lambda expressions and the other forms of
literal for which we have a type but no value.)
When demangling a <lambda-sig>, we form template parameters with no
corresponding argument, so we cannot substitute in the argument in the
demangling. Instead we invent synthetic names for the template
parameters (eg, '[]<typename $T>($T *x)').
git-svn-id: https://llvm.org/svn/llvm-project/libcxxabi/trunk@371273 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index 7912b1c..7c18268 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -171,6 +171,16 @@
return printStr("SpecialSubKind::iostream");
}
}
+ void print(TemplateParamKind TPK) {
+ switch (TPK) {
+ case TemplateParamKind::Type:
+ return printStr("TemplateParamKind::Type");
+ case TemplateParamKind::NonType:
+ return printStr("TemplateParamKind::NonType");
+ case TemplateParamKind::Template:
+ return printStr("TemplateParamKind::Template");
+ }
+ }
void newLine() {
printStr("\n");
diff --git a/src/demangle/ItaniumDemangle.h b/src/demangle/ItaniumDemangle.h
index aaccb27..f5bb0d6 100644
--- a/src/demangle/ItaniumDemangle.h
+++ b/src/demangle/ItaniumDemangle.h
@@ -57,6 +57,11 @@
X(LocalName) \
X(VectorType) \
X(PixelVectorType) \
+ X(SyntheticTemplateParamName) \
+ X(TypeTemplateParamDecl) \
+ X(NonTypeTemplateParamDecl) \
+ X(TemplateTemplateParamDecl) \
+ X(TemplateParamPackDecl) \
X(ParameterPack) \
X(TemplateArgumentPack) \
X(ParameterPackExpansion) \
@@ -91,6 +96,8 @@
X(ThrowExpr) \
X(UUIDOfExpr) \
X(BoolExpr) \
+ X(StringLiteral) \
+ X(LambdaExpr) \
X(IntegerCastExpr) \
X(IntegerLiteral) \
X(FloatLiteral) \
@@ -303,7 +310,7 @@
return Q1 = static_cast<Qualifiers>(Q1 | Q2);
}
-class QualType : public Node {
+class QualType final : public Node {
protected:
const Qualifiers Quals;
const Node *Child;
@@ -964,6 +971,127 @@
}
};
+enum class TemplateParamKind { Type, NonType, Template };
+
+/// An invented name for a template parameter for which we don't have a
+/// corresponding template argument.
+///
+/// This node is created when parsing the <lambda-sig> for a lambda with
+/// explicit template arguments, which might be referenced in the parameter
+/// types appearing later in the <lambda-sig>.
+class SyntheticTemplateParamName final : public Node {
+ TemplateParamKind Kind;
+ unsigned Index;
+
+public:
+ SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_)
+ : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Kind, Index); }
+
+ void printLeft(OutputStream &S) const override {
+ switch (Kind) {
+ case TemplateParamKind::Type:
+ S += "$T";
+ break;
+ case TemplateParamKind::NonType:
+ S += "$N";
+ break;
+ case TemplateParamKind::Template:
+ S += "$TT";
+ break;
+ }
+ if (Index > 0)
+ S << Index - 1;
+ }
+};
+
+/// A template type parameter declaration, 'typename T'.
+class TypeTemplateParamDecl final : public Node {
+ Node *Name;
+
+public:
+ TypeTemplateParamDecl(Node *Name_)
+ : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Name); }
+
+ void printLeft(OutputStream &S) const override {
+ S += "typename ";
+ }
+
+ void printRight(OutputStream &S) const override {
+ Name->print(S);
+ }
+};
+
+/// A non-type template parameter declaration, 'int N'.
+class NonTypeTemplateParamDecl final : public Node {
+ Node *Name;
+ Node *Type;
+
+public:
+ NonTypeTemplateParamDecl(Node *Name_, Node *Type_)
+ : Node(KNonTypeTemplateParamDecl, Cache::Yes), Name(Name_), Type(Type_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Name, Type); }
+
+ void printLeft(OutputStream &S) const override {
+ Type->printLeft(S);
+ if (!Type->hasRHSComponent(S))
+ S += " ";
+ }
+
+ void printRight(OutputStream &S) const override {
+ Name->print(S);
+ Type->printRight(S);
+ }
+};
+
+/// A template template parameter declaration,
+/// 'template<typename T> typename N'.
+class TemplateTemplateParamDecl final : public Node {
+ Node *Name;
+ NodeArray Params;
+
+public:
+ TemplateTemplateParamDecl(Node *Name_, NodeArray Params_)
+ : Node(KTemplateTemplateParamDecl, Cache::Yes), Name(Name_),
+ Params(Params_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Name, Params); }
+
+ void printLeft(OutputStream &S) const override {
+ S += "template<";
+ Params.printWithComma(S);
+ S += "> typename ";
+ }
+
+ void printRight(OutputStream &S) const override {
+ Name->print(S);
+ }
+};
+
+/// A template parameter pack declaration, 'typename ...T'.
+class TemplateParamPackDecl final : public Node {
+ Node *Param;
+
+public:
+ TemplateParamPackDecl(Node *Param_)
+ : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Param); }
+
+ void printLeft(OutputStream &S) const override {
+ Param->printLeft(S);
+ S += "...";
+ }
+
+ void printRight(OutputStream &S) const override {
+ Param->printRight(S);
+ }
+};
+
/// An unexpanded parameter pack (either in the expression or type context). If
/// this AST is correct, this node will have a ParameterPackExpansion node above
/// it.
@@ -1410,21 +1538,36 @@
};
class ClosureTypeName : public Node {
+ NodeArray TemplateParams;
NodeArray Params;
StringView Count;
public:
- ClosureTypeName(NodeArray Params_, StringView Count_)
- : Node(KClosureTypeName), Params(Params_), Count(Count_) {}
+ ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_,
+ StringView Count_)
+ : Node(KClosureTypeName), TemplateParams(TemplateParams_),
+ Params(Params_), Count(Count_) {}
- template<typename Fn> void match(Fn F) const { F(Params, Count); }
+ template<typename Fn> void match(Fn F) const {
+ F(TemplateParams, Params, Count);
+ }
+
+ void printDeclarator(OutputStream &S) const {
+ if (!TemplateParams.empty()) {
+ S += "<";
+ TemplateParams.printWithComma(S);
+ S += ">";
+ }
+ S += "(";
+ Params.printWithComma(S);
+ S += ")";
+ }
void printLeft(OutputStream &S) const override {
S += "\'lambda";
S += Count;
- S += "\'(";
- Params.printWithComma(S);
- S += ")";
+ S += "\'";
+ printDeclarator(S);
}
};
@@ -1902,6 +2045,38 @@
}
};
+class StringLiteral : public Node {
+ const Node *Type;
+
+public:
+ StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Type); }
+
+ void printLeft(OutputStream &S) const override {
+ S += "\"<";
+ Type->print(S);
+ S += ">\"";
+ }
+};
+
+class LambdaExpr : public Node {
+ const Node *Type;
+
+ void printLambdaDeclarator(OutputStream &S) const;
+
+public:
+ LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {}
+
+ template<typename Fn> void match(Fn F) const { F(Type); }
+
+ void printLeft(OutputStream &S) const override {
+ S += "[]";
+ printLambdaDeclarator(S);
+ S += "{...}";
+ }
+};
+
class IntegerCastExpr : public Node {
// ty(integer)
const Node *Ty;
@@ -2034,6 +2209,39 @@
#undef FOR_EACH_NODE_KIND
+inline void LambdaExpr::printLambdaDeclarator(OutputStream &S) const {
+ struct LambdaDeclaratorPrinter {
+ OutputStream &S;
+ void operator()(const ClosureTypeName *LambdaType) {
+ LambdaType->printDeclarator(S);
+ }
+
+ // Walk through any qualifiers to find the lambda-expression.
+ void operator()(const SpecialName *Name) {
+ Name->match([&](StringView, const Node *Name) { Name->visit(*this); });
+ }
+ void operator()(const NestedName *Name) {
+ Name->match([&](const Node *, const Node *Name) { Name->visit(*this); });
+ }
+ void operator()(const LocalName *Name) {
+ Name->match([&](const Node *, const Node *Name) { Name->visit(*this); });
+ }
+ void operator()(const QualifiedName *Name) {
+ Name->match([&](const Node *, const Node *Name) { Name->visit(*this); });
+ }
+ void operator()(const GlobalQualifiedName *Name) {
+ Name->match([&](const Node *Child) { Child->visit(*this); });
+ }
+ void operator()(const StdQualifiedName *Name) {
+ Name->match([&](const Node *Child) { Child->visit(*this); });
+ }
+ void operator()(const Node *) {
+ // If we can't find the lambda type, just print '[]{...}'.
+ }
+ };
+ return Type->visit(LambdaDeclaratorPrinter{S});
+}
+
template <class T, size_t N>
class PODSmallVector {
static_assert(std::is_pod<T>::value,
@@ -2167,10 +2375,39 @@
// table.
PODSmallVector<Node *, 32> Subs;
+ using TemplateParamList = PODSmallVector<Node *, 8>;
+
+ class ScopedTemplateParamList {
+ AbstractManglingParser *Parser;
+ size_t OldNumTemplateParamLists;
+ TemplateParamList Params;
+
+ public:
+ ScopedTemplateParamList(AbstractManglingParser *Parser)
+ : Parser(Parser),
+ OldNumTemplateParamLists(Parser->TemplateParams.size()) {
+ Parser->TemplateParams.push_back(&Params);
+ }
+ ~ScopedTemplateParamList() {
+ assert(Parser->TemplateParams.size() >= OldNumTemplateParamLists);
+ Parser->TemplateParams.dropBack(OldNumTemplateParamLists);
+ }
+ void push_back(Node *Param) {
+ Params.push_back(Param);
+ }
+ };
+
// Template parameter table. Like the above, but referenced like "T42_".
// This has a smaller size compared to Subs and Names because it can be
// stored on the stack.
- PODSmallVector<Node *, 8> TemplateParams;
+ TemplateParamList OuterTemplateParams;
+
+ // Lists of template parameters indexed by template parameter depth,
+ // referenced like "TL2_4_". If nonempty, element 0 is always
+ // OuterTemplateParams; inner elements are always template parameter lists of
+ // lambda expressions. For a generic lambda with no explicit template
+ // parameter list, the corresponding parameter list pointer will be null.
+ PODSmallVector<TemplateParamList *, 4> TemplateParams;
// Set of unresolved forward <template-param> references. These can occur in a
// conversion operator's type, and are resolved in the enclosing <encoding>.
@@ -2178,7 +2415,9 @@
bool TryToParseTemplateArgs = true;
bool PermitForwardTemplateReferences = false;
- bool ParsingLambdaParams = false;
+ size_t ParsingLambdaParamsAtLevel = (size_t)-1;
+
+ unsigned NumSyntheticTemplateParameters[3] = {};
Alloc ASTAllocator;
@@ -2193,9 +2432,10 @@
Names.clear();
Subs.clear();
TemplateParams.clear();
- ParsingLambdaParams = false;
+ ParsingLambdaParamsAtLevel = (size_t)-1;
TryToParseTemplateArgs = true;
PermitForwardTemplateReferences = false;
+ NumSyntheticTemplateParameters = {};
ASTAllocator.reset();
}
@@ -2253,6 +2493,7 @@
bool parseSeqId(size_t *Out);
Node *parseSubstitution();
Node *parseTemplateParam();
+ Node *parseTemplateParamDecl();
Node *parseTemplateArgs(bool TagTemplates = false);
Node *parseTemplateArg();
@@ -2301,9 +2542,10 @@
size_t E = ForwardTemplateRefs.size();
for (; I < E; ++I) {
size_t Idx = ForwardTemplateRefs[I]->Index;
- if (Idx >= TemplateParams.size())
+ if (TemplateParams.empty() || !TemplateParams[0] ||
+ Idx >= TemplateParams[0]->size())
return true;
- ForwardTemplateRefs[I]->Ref = TemplateParams[Idx];
+ ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx];
}
ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin);
return false;
@@ -2470,7 +2712,12 @@
// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters
template <typename Derived, typename Alloc>
Node *
-AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *) {
+AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) {
+ // <template-params> refer to the innermost <template-args>. Clear out any
+ // outer args that we may have inserted into TemplateParams.
+ if (State != nullptr)
+ TemplateParams.clear();
+
if (consumeIf("Ut")) {
StringView Count = parseNumber();
if (!consumeIf('_'))
@@ -2478,22 +2725,60 @@
return make<UnnamedTypeName>(Count);
}
if (consumeIf("Ul")) {
- NodeArray Params;
- SwapAndRestore<bool> SwapParams(ParsingLambdaParams, true);
+ SwapAndRestore<size_t> SwapParams(ParsingLambdaParamsAtLevel,
+ TemplateParams.size());
+ ScopedTemplateParamList LambdaTemplateParams(this);
+
+ size_t ParamsBegin = Names.size();
+ while (look() == 'T' &&
+ StringView("yptn").find(look(1)) != StringView::npos) {
+ Node *T = parseTemplateParamDecl();
+ if (!T)
+ return nullptr;
+ LambdaTemplateParams.push_back(T);
+ Names.push_back(T);
+ }
+ NodeArray TempParams = popTrailingNodeArray(ParamsBegin);
+
+ // FIXME: If TempParams is empty and none of the function parameters
+ // includes 'auto', we should remove LambdaTemplateParams from the
+ // TemplateParams list. Unfortunately, we don't find out whether there are
+ // any 'auto' parameters until too late in an example such as:
+ //
+ // template<typename T> void f(
+ // decltype([](decltype([]<typename T>(T v) {}),
+ // auto) {})) {}
+ // template<typename T> void f(
+ // decltype([](decltype([]<typename T>(T w) {}),
+ // int) {})) {}
+ //
+ // Here, the type of v is at level 2 but the type of w is at level 1. We
+ // don't find this out until we encounter the type of the next parameter.
+ //
+ // However, compilers can't actually cope with the former example in
+ // practice, and it's likely to be made ill-formed in future, so we don't
+ // need to support it here.
+ //
+ // If we encounter an 'auto' in the function parameter types, we will
+ // recreate a template parameter scope for it, but any intervening lambdas
+ // will be parsed in the 'wrong' template parameter depth.
+ if (TempParams.empty())
+ TemplateParams.pop_back();
+
if (!consumeIf("vE")) {
- size_t ParamsBegin = Names.size();
do {
Node *P = getDerived().parseType();
if (P == nullptr)
return nullptr;
Names.push_back(P);
} while (!consumeIf('E'));
- Params = popTrailingNodeArray(ParamsBegin);
}
+ NodeArray Params = popTrailingNodeArray(ParamsBegin);
+
StringView Count = parseNumber();
if (!consumeIf('_'))
return nullptr;
- return make<ClosureTypeName>(Params, Count);
+ return make<ClosureTypeName>(TempParams, Params, Count);
}
if (consumeIf("Ub")) {
(void)parseNumber();
@@ -3949,6 +4234,7 @@
// ::= L <type> <value float> E # floating literal
// ::= L <string type> E # string literal
// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
+// ::= L <lambda type> E # lambda expression
// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
// ::= L <mangled-name> E # external name
template <typename Derived, typename Alloc>
@@ -4020,6 +4306,19 @@
return R;
}
return nullptr;
+ case 'A': {
+ Node *T = getDerived().parseType();
+ if (T == nullptr)
+ return nullptr;
+ // FIXME: We need to include the string contents in the mangling.
+ if (consumeIf('E'))
+ return make<StringLiteral>(T);
+ return nullptr;
+ }
+ case 'D':
+ if (consumeIf("DnE"))
+ return make<NameType>("nullptr");
+ return nullptr;
case 'T':
// Invalid mangled name per
// http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
@@ -4036,7 +4335,7 @@
return make<IntegerCastExpr>(T, N);
}
if (consumeIf('E'))
- return T;
+ return make<LambdaExpr>(T);
return nullptr;
}
}
@@ -5062,11 +5361,22 @@
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
+// ::= TL <level-1> __
+// ::= TL <level-1> _ <parameter-2 non-negative number> _
template <typename Derived, typename Alloc>
Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParam() {
if (!consumeIf('T'))
return nullptr;
+ size_t Level = 0;
+ if (consumeIf('L')) {
+ if (parsePositiveInteger(&Level))
+ return nullptr;
+ ++Level;
+ if (!consumeIf('_'))
+ return nullptr;
+ }
+
size_t Index = 0;
if (!consumeIf('_')) {
if (parsePositiveInteger(&Index))
@@ -5076,15 +5386,11 @@
return nullptr;
}
- // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter list
- // are mangled as the corresponding artificial template type parameter.
- if (ParsingLambdaParams)
- return make<NameType>("auto");
-
// If we're in a context where this <template-param> refers to a
// <template-arg> further ahead in the mangled name (currently just conversion
// operator types), then we should only look it up in the right context.
- if (PermitForwardTemplateReferences) {
+ // This can only happen at the outermost level.
+ if (PermitForwardTemplateReferences && Level == 0) {
Node *ForwardRef = make<ForwardTemplateReference>(Index);
if (!ForwardRef)
return nullptr;
@@ -5094,9 +5400,78 @@
return ForwardRef;
}
- if (Index >= TemplateParams.size())
+ if (Level >= TemplateParams.size() || !TemplateParams[Level] ||
+ Index >= TemplateParams[Level]->size()) {
+ // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter
+ // list are mangled as the corresponding artificial template type parameter.
+ if (ParsingLambdaParamsAtLevel == Level && Level <= TemplateParams.size()) {
+ // This will be popped by the ScopedTemplateParamList in
+ // parseUnnamedTypeName.
+ if (Level == TemplateParams.size())
+ TemplateParams.push_back(nullptr);
+ return make<NameType>("auto");
+ }
+
return nullptr;
- return TemplateParams[Index];
+ }
+
+ return (*TemplateParams[Level])[Index];
+}
+
+// <template-param-decl> ::= Ty # type parameter
+// ::= Tn <type> # non-type parameter
+// ::= Tt <template-param-decl>* E # template parameter
+// ::= Tp <template-param-decl> # parameter pack
+template <typename Derived, typename Alloc>
+Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParamDecl() {
+ auto InventTemplateParamName = [&](TemplateParamKind Kind) {
+ unsigned Index = NumSyntheticTemplateParameters[(int)Kind]++;
+ Node *N = make<SyntheticTemplateParamName>(Kind, Index);
+ if (N) TemplateParams.back()->push_back(N);
+ return N;
+ };
+
+ if (consumeIf("Ty")) {
+ Node *Name = InventTemplateParamName(TemplateParamKind::Type);
+ if (!Name)
+ return nullptr;
+ return make<TypeTemplateParamDecl>(Name);
+ }
+
+ if (consumeIf("Tn")) {
+ Node *Name = InventTemplateParamName(TemplateParamKind::NonType);
+ if (!Name)
+ return nullptr;
+ Node *Type = parseType();
+ if (!Type)
+ return nullptr;
+ return make<NonTypeTemplateParamDecl>(Name, Type);
+ }
+
+ if (consumeIf("Tt")) {
+ Node *Name = InventTemplateParamName(TemplateParamKind::Template);
+ if (!Name)
+ return nullptr;
+ size_t ParamsBegin = Names.size();
+ ScopedTemplateParamList TemplateTemplateParamParams(this);
+ while (!consumeIf("E")) {
+ Node *P = parseTemplateParamDecl();
+ if (!P)
+ return nullptr;
+ Names.push_back(P);
+ }
+ NodeArray Params = popTrailingNodeArray(ParamsBegin);
+ return make<TemplateTemplateParamDecl>(Name, Params);
+ }
+
+ if (consumeIf("Tp")) {
+ Node *P = parseTemplateParamDecl();
+ if (!P)
+ return nullptr;
+ return make<TemplateParamPackDecl>(P);
+ }
+
+ return nullptr;
}
// <template-arg> ::= <type> # type or template
@@ -5153,8 +5528,11 @@
// <template-params> refer to the innermost <template-args>. Clear out any
// outer args that we may have inserted into TemplateParams.
- if (TagTemplates)
+ if (TagTemplates) {
TemplateParams.clear();
+ TemplateParams.push_back(&OuterTemplateParams);
+ OuterTemplateParams.clear();
+ }
size_t ArgsBegin = Names.size();
while (!consumeIf('E')) {
@@ -5172,7 +5550,7 @@
if (!TableEntry)
return nullptr;
}
- TemplateParams.push_back(TableEntry);
+ TemplateParams.back()->push_back(TableEntry);
} else {
Node *Arg = getDerived().parseTemplateArg();
if (Arg == nullptr)
diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp
index 220c2fe..9f7f167 100644
--- a/test/test_demangle.pass.cpp
+++ b/test/test_demangle.pass.cpp
@@ -29582,8 +29582,8 @@
{"_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*)"},
+ {"_Z1fIiEDTeqfp_LDnEEPT_", "decltype((fp) == (nullptr)) f<int>(int*)"},
+ {"_Z1fIiEDTeqfp1_LDnEEicPT_", "decltype((fp1) == (nullptr)) 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)"},
@@ -29669,6 +29669,10 @@
{"_ZN6test481fINS_1SEEEvPTuNT_1uE", "void test48::f<test48::S>(union test48::S::u*)"},
{"_ZN6test451fINS_1SEEEvPTeNT_1eE", "void test45::f<test45::S>(enum test45::S::e*)"},
+ // String literals
+ // FIXME: We need to encode the string contents in order to avoid symbol collisions.
+ {"_Z1fIcEvDTcv3StrIT_ELA6_KcEE", "void f<char>(decltype((Str<char>)(\"<char const [6]>\")))"},
+
// Initializer list expressions
{"_ZN5test43tf2INS_1XEEEvDTnw_T_piilLi1EEEE", "void test4::tf2<test4::X>(decltype(new test4::X({1})))"},
{"_ZN5test73fA1IiEEDTcmtlNS_1AELi1ELi2EEcvT__EES2_", "decltype((test7::A{1, 2}) , ((int)())) test7::fA1<int>(int)"},
@@ -29775,6 +29779,12 @@
// C++2a char8_t:
{"_ZTSPDu", "typeinfo name for char8_t*"},
+
+ // C++2a lambda-expressions:
+ {"_ZNK1xMUlTyT_E_clIiEEDaS_", "auto x::'lambda'<typename $T>($T)::operator()<int>(x) const"},
+ {"_ZNK1xMUlTnPA3_ivE_clILS0_0EEEDav", "auto x::'lambda'<int (*$N) [3]>()::operator()<(int [3])0>() const"},
+ {"_ZNK1xMUlTyTtTyTnT_TpTnPA3_TL0__ETpTyvE_clIi1XJfEEEDav", "auto x::'lambda'<typename $T, template<typename $T0, $T $N, $T0 (*...$N0) [3]> typename $TT, typename ...$T1>()::operator()<int, X, float>() const"},
+ {"_ZN1AIiE1fIfEEvDTLZ1AIiEEUlTyTtTyTnTL1__ETL0_1_T_TL0__E_EE", "void A<int>::f<float>(decltype([]<typename $T, template<typename $T0, $T0 $N> typename $TT>($TT, int, $T){...}))"},
};
const unsigned N = sizeof(cases) / sizeof(cases[0]);