Skip to content

Commit

Permalink
[Clang] [AST] Fix placeholder return type name mangling for MSVC 1920…
Browse files Browse the repository at this point in the history
…+ / VS2019+ (llvm#102848)

Partial fix for llvm#92204.
This PR just fixes VS2019+ since that is the suite of compilers that I
require link compatibility with at the moment.
I still intend to fix VS2017 and to update llvm-undname in future PRs.
Once those are also finished and merged I'll close out
llvm#92204.
I am hoping to get the llvm-undname PR up in a couple of weeks to be
able to demangle the VS2019+ name mangling.

MSVC 1920+ mangles placeholder return types for non-templated functions
with "@".
For example `auto foo() { return 0; }` is mangled as `?foo@@ya@XZ`.

MSVC 1920+ mangles placeholder return types for templated functions as
the qualifiers of the AutoType followed by "_P" for `auto` and "_T" for
`decltype(auto)`.
For example `template<class T> auto foo() { return 0; }` is mangled as
`??$foo@H@@ya?A_PXZ` when `foo` is instantiated as follows `foo<int>()`.

Lambdas with placeholder return types are still mangled with clang's
custom mangling since MSVC lambda mangling hasn't been deciphered yet.
Similarly any pointers in the return type with an address space are
mangled with clang's custom mangling since that is a clang extension.

We cannot augment `mangleType` to support this mangling scheme as the
mangling schemes for variables and functions differ.
auto variables are encoded with the fully deduced type where auto return
types are not.
The following two functions with a static variable are mangled the same
```
template<class T>
int test()
{
    static int i = 0; // "?i@?1???$test@H@@yahxz@4HA"
    return i;
}

template<class T>
int test()
{
    static auto i = 0; // "?i@?1???$test@H@@yahxz@4HA"
    return i;
}
```
Inside `mangleType` once we get to mangling the `AutoType` we have no
context if we are from a variable encoding or some other encoding.
Therefore it was easier to handle any special casing for `AutoType`
return types with a separate function instead of using the `mangleType`
infrastructure.
  • Loading branch information
MaxEW707 authored Aug 15, 2024
1 parent b4dc986 commit e0d173d
Show file tree
Hide file tree
Showing 6 changed files with 533 additions and 19 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ C++ Specific Potentially Breaking Changes
ABI Changes in This Version
---------------------------

- Fixed Microsoft name mangling of placeholder, auto and decltype(auto), return types 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.

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

Expand Down
161 changes: 152 additions & 9 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ class MicrosoftCXXNameMangler {
void mangleSourceName(StringRef Name);
void mangleNestedName(GlobalDecl GD);

void mangleAutoReturnType(QualType T, QualifierMangleMode QMM);

private:
bool isStructorDecl(const NamedDecl *ND) const {
return ND == Structor || getStructor(ND) == Structor;
Expand Down Expand Up @@ -477,6 +479,11 @@ class MicrosoftCXXNameMangler {
SourceRange Range);
void mangleObjCKindOfType(const ObjCObjectType *T, Qualifiers Quals,
SourceRange Range);

void mangleAutoReturnType(const MemberPointerType *T, Qualifiers Quals);
void mangleAutoReturnType(const PointerType *T, Qualifiers Quals);
void mangleAutoReturnType(const LValueReferenceType *T, Qualifiers Quals);
void mangleAutoReturnType(const RValueReferenceType *T, Qualifiers Quals);
};
}

Expand Down Expand Up @@ -2494,6 +2501,57 @@ void MicrosoftCXXNameMangler::mangleAddressSpaceType(QualType T,
mangleArtificialTagType(TagTypeKind::Struct, ASMangling, {"__clang"});
}

void MicrosoftCXXNameMangler::mangleAutoReturnType(QualType T,
QualifierMangleMode QMM) {
assert(getASTContext().getLangOpts().isCompatibleWithMSVC(
LangOptions::MSVC2019) &&
"Cannot mangle MSVC 2017 auto return types!");

if (isa<AutoType>(T)) {
const auto *AT = T->getContainedAutoType();
Qualifiers Quals = T.getLocalQualifiers();

if (QMM == QMM_Result)
Out << '?';
if (QMM != QMM_Drop)
mangleQualifiers(Quals, false);
Out << (AT->isDecltypeAuto() ? "_T" : "_P");
return;
}

T = T.getDesugaredType(getASTContext());
Qualifiers Quals = T.getLocalQualifiers();

switch (QMM) {
case QMM_Drop:
case QMM_Result:
break;
case QMM_Mangle:
mangleQualifiers(Quals, false);
break;
default:
llvm_unreachable("QMM_Escape unexpected");
}

const Type *ty = T.getTypePtr();
switch (ty->getTypeClass()) {
case Type::MemberPointer:
mangleAutoReturnType(cast<MemberPointerType>(ty), Quals);
break;
case Type::Pointer:
mangleAutoReturnType(cast<PointerType>(ty), Quals);
break;
case Type::LValueReference:
mangleAutoReturnType(cast<LValueReferenceType>(ty), Quals);
break;
case Type::RValueReference:
mangleAutoReturnType(cast<RValueReferenceType>(ty), Quals);
break;
default:
llvm_unreachable("Invalid type expected");
}
}

void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range,
QualifierMangleMode QMM) {
// Don't use the canonical types. MSVC includes things like 'const' on
Expand Down Expand Up @@ -2907,17 +2965,51 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
// can differ by their calling convention and are typically deduced. So
// we make sure that this type gets mangled properly.
mangleType(ResultType, Range, QMM_Result);
} else if (const auto *AT = dyn_cast_or_null<AutoType>(
ResultType->getContainedAutoType())) {
Out << '?';
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
Out << '?';
} else if (IsInLambda) {
if (const auto *AT = ResultType->getContainedAutoType()) {
assert(AT->getKeyword() == AutoTypeKeyword::Auto &&
"should only need to mangle auto!");
Out << '?';
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
Out << '?';
mangleSourceName("<auto>");
Out << '@';
} else {
Out << '@';
}
} else if (const auto *AT = ResultType->getContainedAutoType()) {
assert(AT->getKeyword() != AutoTypeKeyword::GNUAutoType &&
"shouldn't need to mangle __auto_type!");
mangleSourceName(AT->isDecltypeAuto() ? "<decltype-auto>" : "<auto>");
Out << '@';
} else if (IsInLambda) {
Out << '@';

// If we have any pointer types with the clang address space extension
// then defer to the custom clang mangling to keep backwards
// compatibility. See `mangleType(const PointerType *T, Qualifiers Quals,
// SourceRange Range)` for details.
auto UseClangMangling = [](QualType ResultType) {
QualType T = ResultType;
while (const auto *PT = dyn_cast<PointerType>(T.getTypePtr())) {
T = T->getPointeeType();
if (T.getQualifiers().hasAddressSpace())
return true;
}
return false;
};

if (getASTContext().getLangOpts().isCompatibleWithMSVC(
LangOptions::MSVC2019) &&
!UseClangMangling(ResultType)) {
if (D && !D->getPrimaryTemplate()) {
Out << '@';
} else {
mangleAutoReturnType(ResultType, QMM_Result);
}
} else {
Out << '?';
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
Out << '?';
mangleSourceName(AT->isDecltypeAuto() ? "<decltype-auto>" : "<auto>");
Out << '@';
}
} else {
if (ResultType->isVoidType())
ResultType = ResultType.getUnqualifiedType();
Expand Down Expand Up @@ -4220,6 +4312,57 @@ void MicrosoftMangleContextImpl::mangleStringLiteral(const StringLiteral *SL,
Mangler.getStream() << '@';
}

void MicrosoftCXXNameMangler::mangleAutoReturnType(const MemberPointerType *T,
Qualifiers Quals) {
QualType PointeeType = T->getPointeeType();
manglePointerCVQualifiers(Quals);
manglePointerExtQualifiers(Quals, PointeeType);
if (const FunctionProtoType *FPT = PointeeType->getAs<FunctionProtoType>()) {
Out << '8';
mangleName(T->getClass()->castAs<RecordType>()->getDecl());
mangleFunctionType(FPT, nullptr, true);
} else {
mangleQualifiers(PointeeType.getQualifiers(), true);
mangleName(T->getClass()->castAs<RecordType>()->getDecl());
mangleAutoReturnType(PointeeType, QMM_Drop);
}
}

void MicrosoftCXXNameMangler::mangleAutoReturnType(const PointerType *T,
Qualifiers Quals) {
QualType PointeeType = T->getPointeeType();
assert(!PointeeType.getQualifiers().hasAddressSpace() &&
"Unexpected address space mangling required");

manglePointerCVQualifiers(Quals);
manglePointerExtQualifiers(Quals, PointeeType);

if (const FunctionProtoType *FPT = PointeeType->getAs<FunctionProtoType>()) {
Out << '6';
mangleFunctionType(FPT);
} else {
mangleAutoReturnType(PointeeType, QMM_Mangle);
}
}

void MicrosoftCXXNameMangler::mangleAutoReturnType(const LValueReferenceType *T,
Qualifiers Quals) {
QualType PointeeType = T->getPointeeType();
assert(!Quals.hasConst() && !Quals.hasVolatile() && "unexpected qualifier!");
Out << 'A';
manglePointerExtQualifiers(Quals, PointeeType);
mangleAutoReturnType(PointeeType, QMM_Mangle);
}

void MicrosoftCXXNameMangler::mangleAutoReturnType(const RValueReferenceType *T,
Qualifiers Quals) {
QualType PointeeType = T->getPointeeType();
assert(!Quals.hasConst() && !Quals.hasVolatile() && "unexpected qualifier!");
Out << "$$Q";
manglePointerExtQualifiers(Quals, PointeeType);
mangleAutoReturnType(PointeeType, QMM_Mangle);
}

MicrosoftMangleContext *MicrosoftMangleContext::create(ASTContext &Context,
DiagnosticsEngine &Diags,
bool IsAux) {
Expand Down
Loading

0 comments on commit e0d173d

Please sign in to comment.