Skip to content

Commit 4cf98f9

Browse files
author
Erich Keane
committed
[GH54588]Fix ItaniumMangler for NTTP unnamed unions w/ unnamed structs
As reported in #54588 and discussed in itanium-cxx-abi/cxx-abi#139 We are supposed to do a DFS, pre-order, decl-order search for a name for the union in this case. Prevoiusly we crashed because the IdentiferInfo pointer was nullptr, so this makes sure we have a name in the cases described by the ABI. I added an llvm-unreachable to cover an unexpected case at the end of the new function with information/reference to the ABI in case we come up with some way to get back to here. Differential Revision: https://reviews.llvm.org/D122820
1 parent b36be2f commit 4cf98f9

File tree

2 files changed

+158
-1
lines changed

2 files changed

+158
-1
lines changed

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5545,6 +5545,47 @@ static QualType getLValueType(ASTContext &Ctx, const APValue &LV) {
55455545
return T;
55465546
}
55475547

5548+
static IdentifierInfo *getUnionInitName(SourceLocation UnionLoc,
5549+
DiagnosticsEngine &Diags,
5550+
const FieldDecl *FD) {
5551+
// According to:
5552+
// http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.anonymous
5553+
// For the purposes of mangling, the name of an anonymous union is considered
5554+
// to be the name of the first named data member found by a pre-order,
5555+
// depth-first, declaration-order walk of the data members of the anonymous
5556+
// union.
5557+
5558+
if (FD->getIdentifier())
5559+
return FD->getIdentifier();
5560+
5561+
// The only cases where the identifer of a FieldDecl would be blank is if the
5562+
// field represents an anonymous record type or if it is an unnamed bitfield.
5563+
// There is no type to descend into in the case of a bitfield, so we can just
5564+
// return nullptr in that case.
5565+
if (FD->isBitField())
5566+
return nullptr;
5567+
const CXXRecordDecl *RD = FD->getType()->getAsCXXRecordDecl();
5568+
5569+
// Consider only the fields in declaration order, searched depth-first. We
5570+
// don't care about the active member of the union, as all we are doing is
5571+
// looking for a valid name. We also don't check bases, due to guidance from
5572+
// the Itanium ABI folks.
5573+
for (const FieldDecl *RDField : RD->fields()) {
5574+
if (IdentifierInfo *II = getUnionInitName(UnionLoc, Diags, RDField))
5575+
return II;
5576+
}
5577+
5578+
// According to the Itanium ABI: If there is no such data member (i.e., if all
5579+
// of the data members in the union are unnamed), then there is no way for a
5580+
// program to refer to the anonymous union, and there is therefore no need to
5581+
// mangle its name. However, we should diagnose this anyway.
5582+
unsigned DiagID = Diags.getCustomDiagID(
5583+
DiagnosticsEngine::Error, "cannot mangle this unnamed union NTTP yet");
5584+
Diags.Report(UnionLoc, DiagID);
5585+
5586+
return nullptr;
5587+
}
5588+
55485589
void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
55495590
bool TopLevel,
55505591
bool NeedExactType) {
@@ -5628,7 +5669,10 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
56285669
mangleType(T);
56295670
if (!isZeroInitialized(T, V)) {
56305671
Out << "di";
5631-
mangleSourceName(FD->getIdentifier());
5672+
IdentifierInfo *II = (getUnionInitName(
5673+
T->getAsCXXRecordDecl()->getLocation(), Context.getDiags(), FD));
5674+
if (II)
5675+
mangleSourceName(II);
56325676
mangleValueInTemplateArg(FD->getType(), V.getUnionValue(), false);
56335677
}
56345678
Out << 'E';
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// RUN: %clang_cc1 -std=c++20 -emit-llvm %s -o - -triple=x86_64-linux-gnu | FileCheck %s
2+
// RUN: %clang_cc1 -std=c++20 -emit-llvm %s -o - -triple=x86_64-linux-gnu | llvm-cxxfilt | FileCheck %s --check-prefix DEMANGLED
3+
4+
template<typename T>
5+
struct wrapper1 {
6+
union {
7+
struct {
8+
T RightName;
9+
};
10+
};
11+
};
12+
13+
template<typename T>
14+
struct wrapper2 {
15+
union {
16+
struct {
17+
T RightName;
18+
};
19+
T WrongName;
20+
};
21+
};
22+
23+
struct Base {
24+
int WrongName;
25+
};
26+
27+
template <typename T>
28+
struct wrapper3 {
29+
union {
30+
struct : Base {
31+
T RightName; };
32+
T WrongName;
33+
};
34+
};
35+
36+
template <typename T>
37+
struct wrapper4 {
38+
union {
39+
int RightName;
40+
struct {
41+
T WrongName;
42+
};
43+
T AlsoWrongName;
44+
};
45+
};
46+
47+
template <typename T>
48+
struct wrapper5 {
49+
union {
50+
struct {
51+
struct {
52+
T RightName;
53+
};
54+
T WrongName;
55+
};
56+
};
57+
};
58+
59+
template<typename T>
60+
struct wrapper6 {
61+
union {
62+
union{
63+
int : 5;
64+
T RightName;
65+
};
66+
};
67+
};
68+
69+
70+
71+
template<auto tparam> void dummy(){}
72+
73+
74+
void uses() {
75+
// Zero init'ed cases.
76+
dummy<wrapper1<int>{}>();
77+
// CHECK: call void @_Z5dummyIXtl8wrapper1IiEEEEvv
78+
// DEMANGLED: call void @void dummy<wrapper1<int>{}>()()
79+
dummy<wrapper2<float>{}>();
80+
// CHECK: call void @_Z5dummyIXtl8wrapper2IfEEEEvv
81+
// DEMANGLED: call void @void dummy<wrapper2<float>{}>()()
82+
dummy<wrapper3<short>{}>();
83+
// CHECK: call void @_Z5dummyIXtl8wrapper3IsEEEEvv
84+
// DEMANGLED: call void @void dummy<wrapper3<short>{}>()()
85+
dummy<wrapper4<double>{}>();
86+
// CHECK: call void @_Z5dummyIXtl8wrapper4IdEEEEvv
87+
// DEMANGLED: call void @void dummy<wrapper4<double>{}>()()
88+
dummy<wrapper5<long long>{}>();
89+
// CHECK: call void @_Z5dummyIXtl8wrapper5IxEEEEvv
90+
// DEMANGLED: call void @void dummy<wrapper5<long long>{}>()()
91+
dummy<wrapper6<int>{}>();
92+
// CHECK: call void @_Z5dummyIXtl8wrapper6IiEEEEvv
93+
// DEMANGLED: call void @void dummy<wrapper6<int>{}>()()
94+
95+
dummy<wrapper1<double>{123.0}>();
96+
// CHECK: call void @_Z5dummyIXtl8wrapper1IdEtlNS1_Ut_Edi9RightNametlNS2_Ut_ELd405ec00000000000EEEEEEvv
97+
// DEMANGLED: call void @void dummy<wrapper1<double>{wrapper1<double>::'unnamed'{.RightName = wrapper1<double>::'unnamed'::'unnamed'{0x1.ecp+6}}}>()()
98+
dummy<wrapper2<double>{123.0}>();
99+
// CHECK: call void @_Z5dummyIXtl8wrapper2IdEtlNS1_Ut_Edi9RightNametlNS2_Ut_ELd405ec00000000000EEEEEEvv
100+
// DEMANGLED: call void @void dummy<wrapper2<double>{wrapper2<double>::'unnamed'{.RightName = wrapper2<double>::'unnamed'::'unnamed'{0x1.ecp+6}}}>()()
101+
dummy<wrapper3<double>{123, 456}>();
102+
// CHECK: call void @_Z5dummyIXtl8wrapper3IdEtlNS1_Ut_Edi9RightNametlNS2_Ut_Etl4BaseLi123EELd407c800000000000EEEEEEvv
103+
// DEMANGLED: call void @void dummy<wrapper3<double>{wrapper3<double>::'unnamed'{.RightName = wrapper3<double>::'unnamed'::'unnamed'{Base{123}, 0x1.c8p+8}}}>()()
104+
dummy<wrapper4<double>{123}>();
105+
// CHECK: call void @_Z5dummyIXtl8wrapper4IdEtlNS1_Ut_Edi9RightNameLi123EEEEEvv
106+
// DEMANGLED: call void @void dummy<wrapper4<double>{wrapper4<double>::'unnamed'{.RightName = 123}}>()()
107+
dummy<wrapper5<double>{123.0, 456.0}>();
108+
// CHECK: call void @_Z5dummyIXtl8wrapper5IdEtlNS1_Ut_Edi9RightNametlNS2_Ut_EtlNS3_Ut_ELd405ec00000000000EELd407c800000000000EEEEEEvv
109+
// DEMANGLED: call void @void dummy<wrapper5<double>{wrapper5<double>::'unnamed'{.RightName = wrapper5<double>::'unnamed'::'unnamed'{wrapper5<double>::'unnamed'::'unnamed'::'unnamed'{0x1.ecp+6}, 0x1.c8p+8}}}>()()
110+
dummy<wrapper6<double>{1}>();
111+
// CHECK: call void @_Z5dummyIXtl8wrapper6IdEtlNS1_Ut_Edi9RightNametlNS2_Ut_Edi9RightNameLd3ff0000000000000EEEEEEvv
112+
// DEMANGELD: call void @void dummy<wrapper6<double>{wrapper6<double>::'unnamed'{.RightName = wrapper6<double>::'unnamed'::'unnamed'{.RightName = 0x1p+0}}}>()()
113+
}

0 commit comments

Comments
 (0)