Skip to content

Fix MSVC 1920+ auto NTTP mangling for pointers to members #97007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ ABI Changes in This Version
earlier versions of Clang unless such code is built with the compiler option
`-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior.

- Fixed Microsoft name mangling for auto non-type template arguments of pointer
to member type for MSVC 1920+. This change resolves incompatibilities with code
compiled by MSVC 1920+ but will introduce incompatibilities with code compiled by
earlier versions of Clang unless such code is built with the compiler option
`-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior.
(GH#70899).

AST Dumping Potentially Breaking Changes
----------------------------------------

Expand Down
73 changes: 56 additions & 17 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,15 @@ class MicrosoftCXXNameMangler {
void mangleFunctionEncoding(GlobalDecl GD, bool ShouldMangle);
void mangleVariableEncoding(const VarDecl *VD);
void mangleMemberDataPointer(const CXXRecordDecl *RD, const ValueDecl *VD,
const NonTypeTemplateParmDecl *PD,
QualType TemplateArgType,
StringRef Prefix = "$");
void mangleMemberDataPointerInClassNTTP(const CXXRecordDecl *,
const ValueDecl *);
void mangleMemberFunctionPointer(const CXXRecordDecl *RD,
const CXXMethodDecl *MD,
const NonTypeTemplateParmDecl *PD,
QualType TemplateArgType,
StringRef Prefix = "$");
void mangleFunctionPointer(const FunctionDecl *FD,
const NonTypeTemplateParmDecl *PD,
Expand Down Expand Up @@ -673,12 +677,17 @@ void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) {
}
}

void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD,
const ValueDecl *VD,
StringRef Prefix) {
void MicrosoftCXXNameMangler::mangleMemberDataPointer(
const CXXRecordDecl *RD, const ValueDecl *VD,
const NonTypeTemplateParmDecl *PD, QualType TemplateArgType,
StringRef Prefix) {
// <member-data-pointer> ::= <integer-literal>
// ::= $F <number> <number>
// ::= $G <number> <number> <number>
//
// <auto-nttp> ::= $ M <type> <integer-literal>
// <auto-nttp> ::= $ M <type> F <name> <number>
// <auto-nttp> ::= $ M <type> G <name> <number> <number>

int64_t FieldOffset;
int64_t VBTableOffset;
Expand Down Expand Up @@ -707,7 +716,18 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD,
case MSInheritanceModel::Unspecified: Code = 'G'; break;
}

Out << Prefix << Code;
Out << Prefix;

if (VD &&
getASTContext().getLangOpts().isCompatibleWithMSVC(
LangOptions::MSVC2019) &&
PD && PD->getType()->getTypeClass() == Type::Auto &&
!TemplateArgType.isNull()) {
Out << "M";
mangleType(TemplateArgType, SourceRange(), QMM_Drop);
}

Out << Code;

mangleNumber(FieldOffset);

Expand All @@ -728,7 +748,7 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointerInClassNTTP(
// ::= 8 <postfix> @ <unqualified-name> @

if (IM != MSInheritanceModel::Single && IM != MSInheritanceModel::Multiple)
return mangleMemberDataPointer(RD, VD, "");
return mangleMemberDataPointer(RD, VD, nullptr, QualType(), "");

if (!VD) {
Out << 'N';
Expand All @@ -742,14 +762,19 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointerInClassNTTP(
Out << '@';
}

void
MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD,
const CXXMethodDecl *MD,
StringRef Prefix) {
void MicrosoftCXXNameMangler::mangleMemberFunctionPointer(
const CXXRecordDecl *RD, const CXXMethodDecl *MD,
const NonTypeTemplateParmDecl *PD, QualType TemplateArgType,
StringRef Prefix) {
// <member-function-pointer> ::= $1? <name>
// ::= $H? <name> <number>
// ::= $I? <name> <number> <number>
// ::= $J? <name> <number> <number> <number>
//
// <auto-nttp> ::= $ M <type> 1? <name>
// <auto-nttp> ::= $ M <type> H? <name> <number>
// <auto-nttp> ::= $ M <type> I? <name> <number> <number>
// <auto-nttp> ::= $ M <type> J? <name> <number> <number> <number>

MSInheritanceModel IM = RD->getMSInheritanceModel();

Expand All @@ -767,7 +792,17 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD,
uint64_t VBTableOffset = 0;
uint64_t VBPtrOffset = 0;
if (MD) {
Out << Prefix << Code << '?';
Out << Prefix;

if (getASTContext().getLangOpts().isCompatibleWithMSVC(
LangOptions::MSVC2019) &&
PD && PD->getType()->getTypeClass() == Type::Auto &&
!TemplateArgType.isNull()) {
Out << "M";
mangleType(TemplateArgType, SourceRange(), QMM_Drop);
}

Out << Code << '?';
if (MD->isVirtual()) {
MicrosoftVTableContext *VTContext =
cast<MicrosoftVTableContext>(getASTContext().getVTableContext());
Expand Down Expand Up @@ -859,7 +894,7 @@ void MicrosoftCXXNameMangler::mangleMemberFunctionPointerInClassNTTP(

if (!MD) {
if (RD->getMSInheritanceModel() != MSInheritanceModel::Single)
return mangleMemberFunctionPointer(RD, MD, "");
return mangleMemberFunctionPointer(RD, MD, nullptr, QualType(), "");

Out << 'N';
return;
Expand Down Expand Up @@ -1732,12 +1767,15 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD,
if (isa<FieldDecl>(ND) || isa<IndirectFieldDecl>(ND)) {
mangleMemberDataPointer(cast<CXXRecordDecl>(ND->getDeclContext())
->getMostRecentNonInjectedDecl(),
cast<ValueDecl>(ND));
cast<ValueDecl>(ND),
cast<NonTypeTemplateParmDecl>(Parm),
TA.getParamTypeForDecl());
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
if (MD && MD->isInstance()) {
mangleMemberFunctionPointer(
MD->getParent()->getMostRecentNonInjectedDecl(), MD);
MD->getParent()->getMostRecentNonInjectedDecl(), MD,
cast<NonTypeTemplateParmDecl>(Parm), TA.getParamTypeForDecl());
} else {
mangleFunctionPointer(FD, cast<NonTypeTemplateParmDecl>(Parm),
TA.getParamTypeForDecl());
Expand Down Expand Up @@ -1767,12 +1805,12 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD,
const CXXRecordDecl *RD = MPT->getMostRecentCXXRecordDecl();
if (MPT->isMemberFunctionPointerType() &&
!isa<FunctionTemplateDecl>(TD)) {
mangleMemberFunctionPointer(RD, nullptr);
mangleMemberFunctionPointer(RD, nullptr, nullptr, QualType());
return;
}
if (MPT->isMemberDataPointer()) {
if (!isa<FunctionTemplateDecl>(TD)) {
mangleMemberDataPointer(RD, nullptr);
mangleMemberDataPointer(RD, nullptr, nullptr, QualType());
Copy link
Contributor Author

@MaxEW707 MaxEW707 Jun 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These use cases appear to be added from, 5518a9d, with support for C++20 nttp changes.

From looking at the code we only hit this path when compiling for C++20 and it also already properly mangles according to VS2019+ by doing $M <type> elsewhere.

I intend to take a look at C++20 NTTP after this PR and then take a more holistic look to see if we can better merge the C++11-17 and C++20 mangling paths in MicrosoftMangle.cpp.

return;
}
// nullptr data pointers are always represented with a single field
Expand Down Expand Up @@ -1979,9 +2017,10 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T,
cast_or_null<CXXMethodDecl>(D));
} else {
if (T->isMemberDataPointerType())
mangleMemberDataPointer(RD, D, "");
mangleMemberDataPointer(RD, D, nullptr, QualType(), "");
else
mangleMemberFunctionPointer(RD, cast_or_null<CXXMethodDecl>(D), "");
mangleMemberFunctionPointer(RD, cast_or_null<CXXMethodDecl>(D), nullptr,
QualType(), "");
}
return;
}
Expand Down
71 changes: 71 additions & 0 deletions clang/test/CodeGenCXX/mangle-ms-auto-templates-memptrs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19.20 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-windows-msvc | FileCheck --check-prefix=AFTER %s
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19.14 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-windows-msvc | FileCheck --check-prefix=BEFORE %s

template <auto a>
class AutoParmTemplate {
public:
AutoParmTemplate() {}
};

template <auto a>
auto AutoFunc() {
return a;
}

struct A {};
struct B {};

struct S { int a; void f(); virtual void g(); };
struct M : A, B { int a; void f(); virtual void g(); };
struct V : virtual A { int a; void f(); virtual void g(); };

void template_mangling() {

AutoParmTemplate<&S::f> auto_method_single_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MP8S@@EAAXXZ1?f@1@QEAAXXZ@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$1?f@S@@QEAAXXZ@@QEAA@XZ"

AutoParmTemplate<&M::f> auto_method_multiple_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MP8M@@EAAXXZH?f@1@QEAAXXZA@@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$H?f@M@@QEAAXXZA@@@QEAA@XZ"

AutoParmTemplate<&V::f> auto_method_virtual_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MP8V@@EAAXXZI?f@1@QEAAXXZA@A@@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$I?f@V@@QEAAXXZA@A@@@QEAA@XZ"

AutoFunc<&S::f>();
// AFTER: call {{.*}} @"??$AutoFunc@$MP8S@@EAAXXZ1?f@1@QEAAXXZ@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$1?f@S@@QEAAXXZ@@YA?A?<auto>@@XZ"

AutoFunc<&M::f>();
// AFTER: call {{.*}} @"??$AutoFunc@$MP8M@@EAAXXZH?f@1@QEAAXXZA@@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$H?f@M@@QEAAXXZA@@@YA?A?<auto>@@XZ"

AutoFunc<&V::f>();
// AFTER: call {{.*}} @"??$AutoFunc@$MP8V@@EAAXXZI?f@1@QEAAXXZA@A@@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$I?f@V@@QEAAXXZA@A@@@YA?A?<auto>@@XZ"

AutoParmTemplate<&S::a> auto_data_single_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MPEQS@@H07@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$07@@QEAA@XZ"

AutoParmTemplate<&M::a> auto_data_multiple_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MPEQM@@H0M@@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$0M@@@QEAA@XZ"

AutoParmTemplate<&V::a> auto_data_virtual_inheritance;
// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$MPEQV@@HFBA@A@@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$FBA@A@@@QEAA@XZ"

AutoFunc<&S::a>();
// AFTER: call {{.*}} @"??$AutoFunc@$MPEQS@@H07@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$07@@YA?A?<auto>@@XZ"

AutoFunc<&M::a>();
// AFTER: call {{.*}} @"??$AutoFunc@$MPEQM@@H0M@@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$0M@@@YA?A?<auto>@@XZ"

AutoFunc<&V::a>();
// AFTER: call {{.*}} @"??$AutoFunc@$MPEQV@@HFBA@A@@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$FBA@A@@@YA?A?<auto>@@XZ"
}
24 changes: 24 additions & 0 deletions clang/test/CodeGenCXX/mangle-ms-auto-templates-nullptr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19.20 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-windows-msvc | FileCheck --check-prefix=AFTER %s
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19.14 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-windows-msvc | FileCheck --check-prefix=BEFORE %s

template <auto a>
class AutoParmTemplate {
public:
AutoParmTemplate() {}
};

template <auto a>
auto AutoFunc() {
return a;
}

void template_mangling() {

AutoParmTemplate<nullptr> auto_nullptr;
Copy link
Contributor Author

@MaxEW707 MaxEW707 Jun 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate file since nullptr gets mangled the same as an integer literal.

In VS2017 this gets mangled without the $M <type> so clang emits an error for emitting the same mangled name definition for two different objects if these tests are inside mangle-ms-auto-templates.cpp which has tests for mangling integer literals.

VS2017 gets around this by erroneously putting all the definitions into COMDAT sections which clang does not do at the moment when trying to be VS2017 compatible.

// AFTER: call {{.*}} @"??0?$AutoParmTemplate@$M$$T0A@@@QEAA@XZ"
// BEFORE: call {{.*}} @"??0?$AutoParmTemplate@$0A@@@QEAA@XZ"

AutoFunc<nullptr>();
// AFTER: call {{.*}} @"??$AutoFunc@$M$$T0A@@@YA?A?<auto>@@XZ"
// BEFORE: call {{.*}} @"??$AutoFunc@$0A@@@YA?A?<auto>@@XZ"
}
7 changes: 4 additions & 3 deletions llvm/lib/Demangle/MicrosoftDemangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2343,12 +2343,13 @@ Demangler::demangleTemplateParameterList(std::string_view &MangledName) {
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
TPRN->Symbol = parse(MangledName);
TPRN->Affinity = PointerAffinity::Reference;
} else if (llvm::itanium_demangle::starts_with(MangledName, "$F") ||
llvm::itanium_demangle::starts_with(MangledName, "$G")) {
} else if (startsWith(MangledName, "$F", "F", !IsAutoNTTP) ||
startsWith(MangledName, "$G", "G", !IsAutoNTTP)) {
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();

// Data member pointer.
MangledName.remove_prefix(1);
if (!IsAutoNTTP)
MangledName.remove_prefix(1); // Remove leading '$'
char InheritanceSpecifier = MangledName.front();
MangledName.remove_prefix(1);

Expand Down
42 changes: 42 additions & 0 deletions llvm/test/Demangle/ms-auto-templates.test
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,45 @@

??0?$AutoNTTPClass@$MH0A@$M_N0A@$MD0GB@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<0, 0, 97>::AutoNTTPClass<0, 0, 97>(void)

??0?$AutoNTTPClass@$M$$T0A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<0>::AutoNTTPClass<0>(void)

??0?$AutoNTTPClass@$0A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<0>::AutoNTTPClass<0>(void)

??0?$AutoNTTPClass@$MP8S@@EAAXXZ1?f@1@QEAAXXZ@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<&public: void __cdecl S::f(void)>::AutoNTTPClass<&public: void __cdecl S::f(void)>(void)

??0?$AutoNTTPClass@$1?f@S@@QEAAXXZ@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<&public: void __cdecl S::f(void)>::AutoNTTPClass<&public: void __cdecl S::f(void)>(void)

??0?$AutoNTTPClass@$MP8M@@EAAXXZH?f@1@QEAAXXZA@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{public: void __cdecl M::f(void), 0}>::AutoNTTPClass<{public: void __cdecl M::f(void), 0}>(void)

??0?$AutoNTTPClass@$H?f@M@@QEAAXXZA@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{public: void __cdecl M::f(void), 0}>::AutoNTTPClass<{public: void __cdecl M::f(void), 0}>(void)

??0?$AutoNTTPClass@$MP8V@@EAAXXZI?f@1@QEAAXXZA@A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{public: void __cdecl V::f(void), 0, 0}>::AutoNTTPClass<{public: void __cdecl V::f(void), 0, 0}>(void)

??0?$AutoNTTPClass@$I?f@V@@QEAAXXZA@A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{public: void __cdecl V::f(void), 0, 0}>::AutoNTTPClass<{public: void __cdecl V::f(void), 0, 0}>(void)

??0?$AutoNTTPClass@$MPEQS@@H07@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<8>::AutoNTTPClass<8>(void)

??0?$AutoNTTPClass@$07@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<8>::AutoNTTPClass<8>(void)

??0?$AutoNTTPClass@$MPEQM@@H0M@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<12>::AutoNTTPClass<12>(void)

??0?$AutoNTTPClass@$0M@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<12>::AutoNTTPClass<12>(void)

??0?$AutoNTTPClass@$MPEQV@@HFBA@A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{16, 0}>::AutoNTTPClass<{16, 0}>(void)

??0?$AutoNTTPClass@$FBA@A@@@QEAA@XZ
; CHECK: public: __cdecl AutoNTTPClass<{16, 0}>::AutoNTTPClass<{16, 0}>(void)
Loading