Skip to content

Commit

Permalink
[clang-cl] [AST] Reapply #102848 Fix placeholder return type name man…
Browse files Browse the repository at this point in the history
…gling for MSVC 1920+ / VS2019+ (#104722)

Reapply llvm/llvm-project#102848.

The description in this PR will detail the changes from the reverted
original PR above.

For `auto&&` return types that can partake in reference collapsing we
weren't properly handling that mangling that can arise.
When collapsing occurs an inner reference is created with the collapsed
reference type. If we return `int&` from such a function then an inner
reference of `int&` is created within the `auto&&` return type.
`getPointeeType` on a reference type goes through all inner references
before returning the pointee type which ends up being a builtin type,
`int`, which is unexpected.

We can use `getPointeeTypeAsWritten` to get the `AutoType` as expected
however for the instantiated template declaration reference collapsing
already occurred on the return type. This means `auto&&` is turned into
`auto&` in our example above.
We end up mangling an lvalue reference type.
This is unintended as MSVC mangles on the declaration of the return
type, `auto&&` in this case, which is treated as an rvalue reference.
```
template<class T>
auto&& AutoReferenceCollapseT(int& x) { return static_cast<int&>(x); }

void test() 
{
    int x = 1;
    auto&& rref = AutoReferenceCollapseT<void>(x); // "??$AutoReferenceCollapseT@X@@ya$$QEA_PAEAH@Z"
    // Mangled as an rvalue reference to auto
}
```

If we are mangling a template with a placeholder return type we want to
get the first template declaration and use its return type to do the
mangling of any instantiations.

This fixes the bug reported in the original PR that caused the revert
with libcxx `std::variant`.
I also tested locally with libcxx and the following test code which
fails in the original PR but now works in this PR.
```
#include <variant>

void test()
{
    std::variant<int> v{ 1 };
    int& r = std::get<0>(v);
    (void)r;
}
```
  • Loading branch information
MaxEW707 authored Aug 24, 2024
1 parent 9f82f6d commit 43b8885
Show file tree
Hide file tree
Showing 6 changed files with 556 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
170 changes: 161 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,60 @@ 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!");
(void)AT;
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 (isa<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 {
if (D && D->getPrimaryTemplate()) {
const FunctionProtoType *FPT = D->getPrimaryTemplate()
->getTemplatedDecl()
->getFirstDecl()
->getType()
->castAs<FunctionProtoType>();
ResultType = FPT->getReturnType();
}
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 +4321,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 43b8885

Please sign in to comment.