r371004 - [c++20] Fix some ambiguities in our mangling of lambdas with explicit
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 4 18:23:47 PDT 2019
Author: rsmith
Date: Wed Sep 4 18:23:47 2019
New Revision: 371004
URL: http://llvm.org/viewvc/llvm-project?rev=371004&view=rev
Log:
[c++20] Fix some ambiguities in our mangling of lambdas with explicit
template parameters.
This finishes the implementation of the proposal described in
https://github.com/itanium-cxx-abi/cxx-abi/issues/31. (We already
implemented the <lambda-sig> extensions, but didn't take them into
account when computing mangling numbers, and didn't deal properly with
expanded parameter packs, and didn't disambiguate between different
levels of template parameters in manglings.)
Modified:
cfe/trunk/include/clang/AST/Mangle.h
cfe/trunk/lib/AST/DeclBase.cpp
cfe/trunk/lib/AST/ItaniumCXXABI.cpp
cfe/trunk/lib/AST/ItaniumMangle.cpp
cfe/trunk/lib/Sema/SemaLambda.cpp
cfe/trunk/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp
Modified: cfe/trunk/include/clang/AST/Mangle.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Mangle.h?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Mangle.h (original)
+++ cfe/trunk/include/clang/AST/Mangle.h Wed Sep 4 18:23:47 2019
@@ -170,6 +170,8 @@ public:
virtual void mangleCXXDtorComdat(const CXXDestructorDecl *D,
raw_ostream &) = 0;
+ virtual void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) = 0;
+
static bool classof(const MangleContext *C) {
return C->getKind() == MK_Itanium;
}
Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Wed Sep 4 18:23:47 2019
@@ -12,6 +12,7 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Attr.h"
#include "clang/AST/AttrIterator.h"
@@ -1043,6 +1044,12 @@ DeclContext *DeclContext::getLookupParen
getLexicalParent()->getRedeclContext()->isRecord())
return getLexicalParent();
+ // A lookup within the call operator of a lambda never looks in the lambda
+ // class; instead, skip to the context in which that closure type is
+ // declared.
+ if (isLambdaCallOperator(this))
+ return getParent()->getParent();
+
return getParent();
}
Modified: cfe/trunk/lib/AST/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumCXXABI.cpp?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumCXXABI.cpp Wed Sep 4 18:23:47 2019
@@ -19,10 +19,12 @@
#include "CXXABI.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Mangle.h"
#include "clang/AST/MangleNumberingContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/iterator.h"
using namespace clang;
@@ -73,10 +75,33 @@ struct DecompositionDeclName {
}
namespace llvm {
+template<typename T> bool isDenseMapKeyEmpty(T V) {
+ return llvm::DenseMapInfo<T>::isEqual(
+ V, llvm::DenseMapInfo<T>::getEmptyKey());
+}
+template<typename T> bool isDenseMapKeyTombstone(T V) {
+ return llvm::DenseMapInfo<T>::isEqual(
+ V, llvm::DenseMapInfo<T>::getTombstoneKey());
+}
+
+template<typename T>
+Optional<bool> areDenseMapKeysEqualSpecialValues(T LHS, T RHS) {
+ bool LHSEmpty = isDenseMapKeyEmpty(LHS);
+ bool RHSEmpty = isDenseMapKeyEmpty(RHS);
+ if (LHSEmpty || RHSEmpty)
+ return LHSEmpty && RHSEmpty;
+
+ bool LHSTombstone = isDenseMapKeyTombstone(LHS);
+ bool RHSTombstone = isDenseMapKeyTombstone(RHS);
+ if (LHSTombstone || RHSTombstone)
+ return LHSTombstone && RHSTombstone;
+
+ return None;
+}
+
template<>
struct DenseMapInfo<DecompositionDeclName> {
using ArrayInfo = llvm::DenseMapInfo<ArrayRef<const BindingDecl*>>;
- using IdentInfo = llvm::DenseMapInfo<const IdentifierInfo*>;
static DecompositionDeclName getEmptyKey() {
return {ArrayInfo::getEmptyKey()};
}
@@ -88,10 +113,10 @@ struct DenseMapInfo<DecompositionDeclNam
return llvm::hash_combine_range(Key.begin(), Key.end());
}
static bool isEqual(DecompositionDeclName LHS, DecompositionDeclName RHS) {
- if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getEmptyKey()))
- return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getEmptyKey());
- if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getTombstoneKey()))
- return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getTombstoneKey());
+ if (Optional<bool> Result = areDenseMapKeysEqualSpecialValues(
+ LHS.Bindings, RHS.Bindings))
+ return *Result;
+
return LHS.Bindings.size() == RHS.Bindings.size() &&
std::equal(LHS.begin(), LHS.end(), RHS.begin());
}
@@ -103,29 +128,32 @@ namespace {
/// Keeps track of the mangled names of lambda expressions and block
/// literals within a particular context.
class ItaniumNumberingContext : public MangleNumberingContext {
- llvm::DenseMap<const Type *, unsigned> ManglingNumbers;
+ ItaniumMangleContext *Mangler;
+ llvm::StringMap<unsigned> LambdaManglingNumbers;
+ unsigned BlockManglingNumber = 0;
llvm::DenseMap<const IdentifierInfo *, unsigned> VarManglingNumbers;
llvm::DenseMap<const IdentifierInfo *, unsigned> TagManglingNumbers;
llvm::DenseMap<DecompositionDeclName, unsigned>
DecompsitionDeclManglingNumbers;
public:
+ ItaniumNumberingContext(ItaniumMangleContext *Mangler) : Mangler(Mangler) {}
+
unsigned getManglingNumber(const CXXMethodDecl *CallOperator) override {
- const FunctionProtoType *Proto =
- CallOperator->getType()->getAs<FunctionProtoType>();
- ASTContext &Context = CallOperator->getASTContext();
-
- FunctionProtoType::ExtProtoInfo EPI;
- EPI.Variadic = Proto->isVariadic();
- QualType Key =
- Context.getFunctionType(Context.VoidTy, Proto->getParamTypes(), EPI);
- Key = Context.getCanonicalType(Key);
- return ++ManglingNumbers[Key->castAs<FunctionProtoType>()];
+ const CXXRecordDecl *Lambda = CallOperator->getParent();
+ assert(Lambda->isLambda());
+
+ // Computation of the <lambda-sig> is non-trivial and subtle. Rather than
+ // duplicating it here, just mangle the <lambda-sig> directly.
+ llvm::SmallString<128> LambdaSig;
+ llvm::raw_svector_ostream Out(LambdaSig);
+ Mangler->mangleLambdaSig(Lambda, Out);
+
+ return ++LambdaManglingNumbers[LambdaSig];
}
unsigned getManglingNumber(const BlockDecl *BD) override {
- const Type *Ty = nullptr;
- return ++ManglingNumbers[Ty];
+ return ++BlockManglingNumber;
}
unsigned getStaticLocalNumber(const VarDecl *VD) override {
@@ -154,10 +182,13 @@ public:
};
class ItaniumCXXABI : public CXXABI {
+private:
+ std::unique_ptr<MangleContext> Mangler;
protected:
ASTContext &Context;
public:
- ItaniumCXXABI(ASTContext &Ctx) : Context(Ctx) { }
+ ItaniumCXXABI(ASTContext &Ctx)
+ : Mangler(Ctx.createMangleContext()), Context(Ctx) {}
MemberPointerInfo
getMemberPointerInfo(const MemberPointerType *MPT) const override {
@@ -218,7 +249,8 @@ public:
std::unique_ptr<MangleNumberingContext>
createMangleNumberingContext() const override {
- return std::make_unique<ItaniumNumberingContext>();
+ return std::make_unique<ItaniumNumberingContext>(
+ cast<ItaniumMangleContext>(Mangler.get()));
}
};
}
Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Wed Sep 4 18:23:47 2019
@@ -170,6 +170,8 @@ public:
void mangleStringLiteral(const StringLiteral *, raw_ostream &) override;
+ void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) override;
+
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
if (isLambda(ND))
@@ -424,6 +426,7 @@ public:
void mangleName(const NamedDecl *ND);
void mangleType(QualType T);
void mangleNameOrStandardSubstitution(const NamedDecl *ND);
+ void mangleLambdaSig(const CXXRecordDecl *Lambda);
private:
@@ -550,7 +553,7 @@ private:
void mangleTemplateArgs(const TemplateArgumentList &AL);
void mangleTemplateArg(TemplateArgument A);
- void mangleTemplateParameter(unsigned Index);
+ void mangleTemplateParameter(unsigned Depth, unsigned Index);
void mangleFunctionParam(const ParmVarDecl *parm);
@@ -965,7 +968,7 @@ void CXXNameMangler::mangleUnscopedTempl
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
assert(!AdditionalAbiTags &&
"template template param cannot have abi tags");
- mangleTemplateParameter(TTP->getIndex());
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else if (isa<BuiltinTemplateDecl>(ND)) {
mangleUnscopedName(ND, AdditionalAbiTags);
} else {
@@ -1686,16 +1689,42 @@ void CXXNameMangler::mangleUnqualifiedBl
// ::= Tn <type> # template non-type parameter
// ::= Tt <template-param-decl>* E # template template parameter
void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) {
- if (isa<TemplateTypeParmDecl>(Decl)) {
+ if (auto *Ty = dyn_cast<TemplateTypeParmDecl>(Decl)) {
+ if (Ty->isParameterPack())
+ Out << "Tp";
Out << "Ty";
} else if (auto *Tn = dyn_cast<NonTypeTemplateParmDecl>(Decl)) {
- Out << "Tn";
- mangleType(Tn->getType());
+ if (Tn->isExpandedParameterPack()) {
+ for (unsigned I = 0, N = Tn->getNumExpansionTypes(); I != N; ++I) {
+ Out << "Tn";
+ mangleType(Tn->getExpansionType(I));
+ }
+ } else {
+ QualType T = Tn->getType();
+ if (Tn->isParameterPack()) {
+ Out << "Tp";
+ T = T->castAs<PackExpansionType>()->getPattern();
+ }
+ Out << "Tn";
+ mangleType(T);
+ }
} else if (auto *Tt = dyn_cast<TemplateTemplateParmDecl>(Decl)) {
- Out << "Tt";
- for (auto *Param : *Tt->getTemplateParameters())
- mangleTemplateParamDecl(Param);
- Out << "E";
+ if (Tt->isExpandedParameterPack()) {
+ for (unsigned I = 0, N = Tt->getNumExpansionTemplateParameters(); I != N;
+ ++I) {
+ Out << "Tt";
+ for (auto *Param : *Tt->getExpansionTemplateParameters(I))
+ mangleTemplateParamDecl(Param);
+ Out << "E";
+ }
+ } else {
+ if (Tt->isParameterPack())
+ Out << "Tp";
+ Out << "Tt";
+ for (auto *Param : *Tt->getTemplateParameters())
+ mangleTemplateParamDecl(Param);
+ Out << "E";
+ }
}
}
@@ -1726,12 +1755,7 @@ void CXXNameMangler::mangleLambda(const
}
Out << "Ul";
- for (auto *D : Lambda->getLambdaExplicitTemplateParameters())
- mangleTemplateParamDecl(D);
- const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()->
- getAs<FunctionProtoType>();
- mangleBareFunctionType(Proto, /*MangleReturnType=*/false,
- Lambda->getLambdaStaticInvoker());
+ mangleLambdaSig(Lambda);
Out << "E";
// The number is omitted for the first closure type with a given
@@ -1746,6 +1770,15 @@ void CXXNameMangler::mangleLambda(const
Out << '_';
}
+void CXXNameMangler::mangleLambdaSig(const CXXRecordDecl *Lambda) {
+ for (auto *D : Lambda->getLambdaExplicitTemplateParameters())
+ mangleTemplateParamDecl(D);
+ const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()->
+ getAs<FunctionProtoType>();
+ mangleBareFunctionType(Proto, /*MangleReturnType=*/false,
+ Lambda->getLambdaStaticInvoker());
+}
+
void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) {
switch (qualifier->getKind()) {
case NestedNameSpecifier::Global:
@@ -1852,7 +1885,7 @@ void CXXNameMangler::mangleTemplatePrefi
// <template-template-param> ::= <template-param>
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
- mangleTemplateParameter(TTP->getIndex());
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else {
manglePrefix(getEffectiveDeclContext(ND), NoFunction);
if (isa<BuiltinTemplateDecl>(ND))
@@ -1885,8 +1918,8 @@ void CXXNameMangler::mangleType(Template
goto HaveDecl;
HaveDecl:
- if (isa<TemplateTemplateParmDecl>(TD))
- mangleTemplateParameter(cast<TemplateTemplateParmDecl>(TD)->getIndex());
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TD))
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
else
mangleName(TD);
break;
@@ -2964,7 +2997,7 @@ void CXXNameMangler::mangleType(const Me
// <type> ::= <template-param>
void CXXNameMangler::mangleType(const TemplateTypeParmType *T) {
- mangleTemplateParameter(T->getIndex());
+ mangleTemplateParameter(T->getDepth(), T->getIndex());
}
// <type> ::= <template-param>
@@ -3535,7 +3568,7 @@ void CXXNameMangler::mangleDeclRefExpr(c
case Decl::NonTypeTemplateParm:
const NonTypeTemplateParmDecl *PD = cast<NonTypeTemplateParmDecl>(D);
- mangleTemplateParameter(PD->getIndex());
+ mangleTemplateParameter(PD->getDepth(), PD->getIndex());
break;
}
}
@@ -4264,13 +4297,13 @@ recurse:
Out << "sZ";
const NamedDecl *Pack = SPE->getPack();
if (const TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Pack))
- mangleTemplateParameter(TTP->getIndex());
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
else if (const NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(Pack))
- mangleTemplateParameter(NTTP->getIndex());
+ mangleTemplateParameter(NTTP->getDepth(), NTTP->getIndex());
else if (const TemplateTemplateParmDecl *TempTP
= dyn_cast<TemplateTemplateParmDecl>(Pack))
- mangleTemplateParameter(TempTP->getIndex());
+ mangleTemplateParameter(TempTP->getDepth(), TempTP->getIndex());
else
mangleFunctionParam(cast<ParmVarDecl>(Pack));
break;
@@ -4557,13 +4590,21 @@ void CXXNameMangler::mangleTemplateArg(T
}
}
-void CXXNameMangler::mangleTemplateParameter(unsigned Index) {
+void CXXNameMangler::mangleTemplateParameter(unsigned Depth, unsigned Index) {
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
- if (Index == 0)
- Out << "T_";
- else
- Out << 'T' << (Index - 1) << '_';
+ // ::= TL <L-1 non-negative number> __
+ // ::= TL <L-1 non-negative number> _
+ // <parameter-2 non-negative number> _
+ //
+ // The latter two manglings are from a proposal here:
+ // https://github.com/itanium-cxx-abi/cxx-abi/issues/31#issuecomment-528122117
+ Out << 'T';
+ if (Depth != 0)
+ Out << 'L' << (Depth - 1) << '_';
+ if (Index != 0)
+ Out << (Index - 1);
+ Out << '_';
}
void CXXNameMangler::mangleSeqID(unsigned SeqID) {
@@ -5080,6 +5121,12 @@ void ItaniumMangleContextImpl::mangleStr
llvm_unreachable("Can't mangle string literals");
}
+void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda,
+ raw_ostream &Out) {
+ CXXNameMangler Mangler(*this, Out);
+ Mangler.mangleLambdaSig(Lambda);
+}
+
ItaniumMangleContext *
ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) {
return new ItaniumMangleContextImpl(Context, Diags);
Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Wed Sep 4 18:23:47 2019
@@ -407,6 +407,8 @@ CXXMethodDecl *Sema::startLambdaDefiniti
MethodType, MethodTypeInfo, SC_None,
/*isInline=*/true, ConstexprKind, EndLoc);
Method->setAccess(AS_public);
+ if (!TemplateParams)
+ Class->addDecl(Method);
// Temporarily set the lexical declaration context to the current
// context, so that the Scope stack matches the lexical nesting.
@@ -418,9 +420,10 @@ CXXMethodDecl *Sema::startLambdaDefiniti
TemplateParams,
Method) : nullptr;
if (TemplateMethod) {
- TemplateMethod->setLexicalDeclContext(CurContext);
TemplateMethod->setAccess(AS_public);
Method->setDescribedFunctionTemplate(TemplateMethod);
+ Class->addDecl(TemplateMethod);
+ TemplateMethod->setLexicalDeclContext(CurContext);
}
// Add parameters.
@@ -1641,8 +1644,9 @@ ExprResult Sema::BuildLambdaExpr(SourceL
? CallOperator->getDescribedFunctionTemplate()
: cast<Decl>(CallOperator);
+ // FIXME: Is this really the best choice? Keeping the lexical decl context
+ // set as CurContext seems more faithful to the source.
TemplateOrNonTemplateCallOperatorDecl->setLexicalDeclContext(Class);
- Class->addDecl(TemplateOrNonTemplateCallOperatorDecl);
PopExpressionEvaluationContext();
Modified: cfe/trunk/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp?rev=371004&r1=371003&r2=371004&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp Wed Sep 4 18:23:47 2019
@@ -33,9 +33,68 @@ void call_inline_func() {
inline_func();
}
+template<typename T, int> struct X {};
+
+inline auto pack = []<typename ...T, T ...N>(T (&...)[N]) {};
+int arr1[] = {1};
+int arr2[] = {1, 2};
+// CHECK: @_ZNK4packMUlTpTyTpTnT_DpRAT0__S_E_clIJiiEJLi1ELi2EEEEDaS2_(
+void use_pack() { pack(arr1, arr2); }
+
+inline void collision() {
+ auto a = []<typename T, template<typename U, T> typename>{};
+ auto b = []<typename T, template<typename U, U> typename>{};
+ auto c = []<typename T, template<typename U, T> typename>{};
+ a.operator()<int, X>();
+ // CHECK: @_ZZ9collisionvENKUlTyTtTyTnT_EvE_clIi1XEEDav
+ b.operator()<int, X>();
+ // CHECK: @_ZZ9collisionvENKUlTyTtTyTnTL0__EvE_clIi1XEEDav
+ c.operator()<int, X>();
+ // CHECK: @_ZZ9collisionvENKUlTyTtTyTnT_EvE0_clIi1XEEDav
+}
+void use_collision() { collision(); }
+
template<typename> void f() {
// CHECK: define linkonce_odr {{.*}} @_ZZ1fIiEvvENKUlT_E_clIiEEDaS0_(
auto x = [](auto){};
x(0);
}
void use_f() { f<int>(); }
+
+template<typename> struct Y {
+ template<int> struct Z {};
+};
+
+template<typename ...T> void expanded() {
+ auto x = []<T..., template<T> typename...>{};
+ auto y = []<int, template<int> typename>{};
+ auto z = []<int, int, template<int> typename, template<int> typename>{};
+ // FIXME: Should we really require 'template' for y and z?
+ x.template operator()<(T())..., Y<T>::template Z...>();
+ y.template operator()<0, Y<int>::Z>();
+ y.template operator()<1, Y<int>::Z>();
+ z.template operator()<1, 2, Y<int>::Z, Y<float>::Z>();
+}
+void use_expanded() {
+ // CHECK: @_ZZ8expandedIJEEvvENKUlvE_clIJEJEEEDav(
+ // CHECK: @_ZZ8expandedIJEEvvENKUlTniTtTniEvE_clILi0EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJEEvvENKUlTniTtTniEvE_clILi1EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJEEvvENKUlTniTniTtTniETtTniEvE_clILi1ELi2EN1YIiE1ZENS2_IfE1ZEEEDav(
+ expanded<>();
+
+ // FIXME: Should we really be using J...E for arguments corresponding to an
+ // expanded parameter pack?
+ // Note that the <lambda-sig>s of 'x' and 'y' collide here, after pack expansion.
+ // CHECK: @_ZZ8expandedIJiEEvvENKUlTniTtTniEvE_clIJLi0EEJN1YIiE1ZEEEEDav(
+ // CHECK: @_ZZ8expandedIJiEEvvENKUlTniTtTniEvE0_clILi0EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJiEEvvENKUlTniTtTniEvE0_clILi1EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJiEEvvENKUlTniTniTtTniETtTniEvE_clILi1ELi2EN1YIiE1ZENS2_IfE1ZEEEDav(
+ expanded<int>();
+
+ // Note that the <lambda-sig>s of 'x' and 'z' collide here, after pack expansion.
+ // CHECK: @_ZZ8expandedIJiiEEvvENKUlTniTniTtTniETtTniEvE_clIJLi0ELi0EEJN1YIiE1ZES4_EEEDav(
+ // CHECK: @_ZZ8expandedIJiiEEvvENKUlTniTtTniEvE_clILi0EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJiiEEvvENKUlTniTtTniEvE_clILi1EN1YIiE1ZEEEDav(
+ // CHECK: @_ZZ8expandedIJiiEEvvENKUlTniTniTtTniETtTniEvE0_clILi1ELi2EN1YIiE1ZENS2_IfE1ZEEEDav(
+ expanded<int, int>();
+}
More information about the cfe-commits
mailing list