Skip to content

Commit

Permalink
Consider reference, pointer, and pointer-to-member TemplateArguments …
Browse files Browse the repository at this point in the history
…to be different if they have different types.

For the Itanium ABI, this implements the mangling rule suggested in
itanium-cxx-abi/cxx-abi#47, namely mangling
such template arguments as being cast to the parameter type in the case
where the template name is overloadable. This can cause a mangling
change for rare cases, where

 * the template argument declaration is converted from its declared type
   to the type of the template parameter, and
 * the template parameter either has a deduced type or is a parameter of
   a function template.

However, such changes are necessary to avoid mangling collisions. The
ABI changes can be reversed with -fclang-abi-compat=11 or earlier.

Re-commit with a fix for the regression introduced last time: don't
expect parameters and arguments to line up inside an <unresolved-name>
mangling.

Differential Revision: https://reviews.llvm.org/D91488
  • Loading branch information
zygoloid committed Dec 14, 2020
1 parent abbd57e commit 05cdf4a
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 98 deletions.
6 changes: 4 additions & 2 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@ class LangOptions : public LangOptionsBase {

/// Attempt to be ABI-compatible with code generated by Clang 11.0.x
/// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit
/// vector member on the stack instead of using registers, and to not
/// properly mangle substitutions for template names in some cases.
/// vector member on the stack instead of using registers, to not properly
/// mangle substitutions for template names in some cases, and to mangle
/// declaration template arguments without a cast to the parameter type
/// even when that can lead to mangling collisions.
Ver11,

/// Conform to the underlying platform's C and C++ ABIs as closely
Expand Down
328 changes: 243 additions & 85 deletions clang/lib/AST/ItaniumMangle.cpp

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2193,6 +2193,8 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) {
break;

case TemplateArgument::Declaration:
VisitType(Arg.getParamTypeForDecl());
// FIXME: Do we need to recursively decompose template parameter objects?
VisitDecl(Arg.getAsDecl());
break;

Expand All @@ -2201,8 +2203,8 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) {
break;

case TemplateArgument::Integral:
Arg.getAsIntegral().Profile(ID);
VisitType(Arg.getIntegralType());
Arg.getAsIntegral().Profile(ID);
break;

case TemplateArgument::Expression:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/TemplateBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
break;

case Declaration:
getParamTypeForDecl().Profile(ID);
ID.AddPointer(getAsDecl()? getAsDecl()->getCanonicalDecl() : nullptr);
break;

Expand Down
42 changes: 35 additions & 7 deletions clang/test/CodeGenCXX/clang-abi-compat.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.0 %s -emit-llvm -o - | FileCheck --check-prefixes=PRE39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.8 %s -emit-llvm -o - | FileCheck --check-prefixes=PRE39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.9 %s -emit-llvm -o - | FileCheck --check-prefixes=V39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=4.0 %s -emit-llvm -o - | FileCheck --check-prefixes=V39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=5 %s -emit-llvm -o - | FileCheck --check-prefixes=V39,V5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=11 %s -emit-llvm -o - | FileCheck --check-prefixes=V11,V5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=latest %s -emit-llvm -o - | FileCheck --check-prefixes=V39,V5,V12 %s
// RUN: %clang_cc1 -std=c++98 -triple x86_64-linux-gnu -fclang-abi-compat=3.0 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,PRE39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.0 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,PRE39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.8 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,PRE39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.9 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=4.0 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,PRE5,PRE12 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=5 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,V5,PRE12,PRE12-CXX17 %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=11 %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,V5,PRE12,PRE12-CXX17 %s
// RUN: %clang_cc1 -std=c++98 -triple x86_64-linux-gnu -fclang-abi-compat=latest %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,V5,V12 %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -fclang-abi-compat=latest %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,V39,V5,V12,V12-CXX17 %s

typedef __attribute__((vector_size(8))) long long v1xi64;
void clang39(v1xi64) {}
Expand All @@ -27,3 +29,29 @@ namespace mangle_template_prefix {
void g() { f<T>(1, 2); }
}

int arg;
template<const int *> struct clang12_unchanged {};
// CHECK: @_Z4test17clang12_unchangedIXadL_Z3argEEE
void test(clang12_unchanged<&arg>) {}

#if __cplusplus >= 201703L
// PRE12-CXX17: @_Z4test15clang12_changedIXadL_Z3argEEE
// V12-CXX17: @_Z4test15clang12_changedIXcvPKiadL_Z3argEEE
template<auto> struct clang12_changed {};
void test(clang12_changed<(const int*)&arg>) {}
#endif

// PRE12: @_Z9clang12_aIXadL_Z3argEEEvv
// V12: @_Z9clang12_aIXcvPKiadL_Z3argEEEvv
template<const int *> void clang12_a() {}
template void clang12_a<&arg>();

// PRE12: @_Z9clang12_bIXadL_Z3arrEEEvv
// V12: @_Z9clang12_bIXadsoKcL_Z3arrEEEEvv
extern const char arr[6] = "hello";
template<const char *> void clang12_b() {}
template void clang12_b<arr>();

// CHECK: @_Z9clang12_cIXadL_Z3arrEEEvv
template<const char (*)[6]> void clang12_c() {}
template void clang12_c<&arr>();
6 changes: 3 additions & 3 deletions clang/test/CodeGenCXX/mangle-class-nttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ union E {
template<E> void f() {}

// Union members.
// CHECK: define weak_odr void @_Z1fIXL1EEEEvv(
// CHECK: define weak_odr void @_Z1fIL1EEEvv(
// MSABI: define {{.*}} @"??$f@$7TE@@@@@YAXXZ"
template void f<E{}>();
// CHECK: define weak_odr void @_Z1fIXtl1EEEEvv(
Expand Down Expand Up @@ -214,10 +214,10 @@ template<H1> void f() {}
template<H2> void f() {}
template<H3> void f() {}
template<H4> void f() {}
// CHECK: define weak_odr void @_Z1fIXL2H1EEEvv
// CHECK: define weak_odr void @_Z1fIL2H1EEvv
// MSABI: define {{.*}} @"??$f@$7TH1@@@@@YAXXZ"
template void f<H1{}>();
// CHECK: define weak_odr void @_Z1fIXL2H2EEEvv
// CHECK: define weak_odr void @_Z1fIL2H2EEvv
// MSABI: define {{.*}} @"??$f@$7TH2@@@@@YAXXZ"
template void f<H2{}>();
// CHECK: define weak_odr void @_Z1fIXtl2H3EEEvv
Expand Down
113 changes: 113 additions & 0 deletions clang/test/CodeGenCXX/mangle-template.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -verify -Wno-return-type -Wno-main -std=c++11 -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s
// RUN: %clang_cc1 -verify -Wno-return-type -Wno-main -std=c++20 -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck %s --check-prefixes=CHECK,CXX20
// expected-no-diagnostics

namespace test1 {
Expand Down Expand Up @@ -221,3 +222,115 @@ namespace test16 {
void g() { f<T>(1, 2); }
}

#if __cplusplus >= 202002L
namespace cxx20 {
template<auto> struct A {};
template<typename T, T V> struct B {};

int x;
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1xEEEEE(
void f(A<&x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPiXadL_ZNS_1xEEEEE(
void f(B<int*, &x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPKiadL_ZNS_1xEEEEE(
void f(A<(const int*)&x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKiXadL_ZNS_1xEEEEE(
void f(B<const int*, &x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPvadL_ZNS_1xEEEEE(
void f(A<(void*)&x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPvXadL_ZNS_1xEEEEE(
void f(B<void*, (void*)&x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPKvadL_ZNS_1xEEEEE(
void f(A<(const void*)&x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadL_ZNS_1xEEEEE(
void f(B<const void*, (const void*)&x>) {}

struct Q { int x; };

// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1Q1xEEEEE(
void f(A<&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1QEiXadL_ZNS1_1xEEEEE
void f(B<int Q::*, &Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvMNS_1QEKiadL_ZNS1_1xEEEEE(
void f(A<(const int Q::*)&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1QEKiXadL_ZNS1_1xEEEEE(
void f(B<const int Q::*, (const int Q::*)&Q::x>) {}
}
#endif

namespace test17 {
// Ensure we mangle the types for non-type template arguments if we've lost
// track of argument / parameter correspondence.
template<int A, int ...B> struct X {};

// CHECK: define {{.*}} @_ZN6test171fILi1EJLi2ELi3ELi4EEEEvNS_1XIXT_EJLi5EXspT0_ELi6EEEE
template<int D, int ...C> void f(X<D, 5u, C..., 6u>) {}
void g() { f<1, 2, 3, 4>({}); }

// Note: there is no J...E here, because we can't form a pack argument, and
// the 5u and 6u are mangled with the original type 'j' (unsigned int) not
// with the resolved type 'i' (signed int).
// CHECK: define {{.*}} @_ZN6test171hILi4EJLi1ELi2ELi3EEEEvNS_1XIXspT0_EXLj5EEXT_EXLj6EEEE
template<int D, int ...C> void h(X<C..., 5u, D, 6u>) {}
void i() { h<4, 1, 2, 3>({}); }

#if __cplusplus >= 201402L
template<int A, const volatile int*> struct Y {};
int n;
// Case 1: &n is a resolved template argument, with a known parameter:
// mangled with no conversion.
// CXX20: define {{.*}} @_ZN6test172j1ILi1EEEvNS_1YIXT_EXadL_ZNS_1nEEEEE
template<int N> void j1(Y<N, (const int*)&n>) {}
// Case 2: &n is an unresolved template argument, with an unknown
// corresopnding parameter: mangled as the source expression.
// CXX20: define {{.*}} @_ZN6test172j2IJLi1EEEEvNS_1YIXspT_EXcvPKiadL_ZNS_1nEEEEE
template<int ...Ns> void j2(Y<Ns..., (const int*)&n>) {}
// Case 3: &n is a resolved template argument, with a known parameter, but
// for a template that can be overloaded on type: mangled with the parameter type.
// CXX20: define {{.*}} @_ZN6test172j3ILi1EEEvDTplT_clL_ZNS_1yIXcvPVKiadL_ZNS_1nEEEEEivEEE
template<const volatile int*> int y();
template<int N> void j3(decltype(N + y<(const int*)&n>())) {}
void k() {
j1<1>(Y<1, &n>());
j2<1>(Y<1, &n>());
j3<1>(0);
}
#endif
}

namespace partially_dependent_template_args {
namespace test1 {
template<bool B> struct enable { using type = int; };
template<typename ...> struct and_ { static constexpr bool value = true; };
template<typename T> inline typename enable<and_<T, T, T>::value>::type f(T) {}
// FIXME: GCC and ICC form a J...E mangling for the pack here. Clang
// doesn't do so when mangling an <unresolved-prefix>. It's not clear who's
// right. See https://github.com/itanium-cxx-abi/cxx-abi/issues/113.
// CHECK: @_ZN33partially_dependent_template_args5test11fIiEENS0_6enableIXsr4and_IT_S3_S3_EE5valueEE4typeES3_
void g() { f(0); }
}

namespace test2 {
struct X { int n; };
template<unsigned> int f(X);

template<typename T> void g1(decltype(f<0>(T()))) {}
template<typename T> void g2(decltype(f<0>({}) + T())) {}
template<typename T> void g3(decltype(f<0>(X{}) + T())) {}
template<int N> void g4(decltype(f<0>(X{N})));

// The first of these mangles the unconverted argument Li0E because the
// callee is unresolved, the rest mangle the converted argument Lj0E
// because the callee is resolved.
void h() {
// CHECK: @_ZN33partially_dependent_template_args5test22g1INS0_1XEEEvDTcl1fIXLi0EEEcvT__EEE
g1<X>({});
// CHECK: @_ZN33partially_dependent_template_args5test22g2IiEEvDTplclL_ZNS0_1fILj0EEEiNS0_1XEEilEEcvT__EE
g2<int>({});
// CHECK: @_ZN33partially_dependent_template_args5test22g3IiEEvDTplclL_ZNS0_1fILj0EEEiNS0_1XEEtlS3_EEcvT__EE
g3<int>({});
// CHECK: @_ZN33partially_dependent_template_args5test22g4ILi0EEEvDTclL_ZNS0_1fILj0EEEiNS0_1XEEtlS3_T_EEE
g4<0>({});
}
}
}
11 changes: 11 additions & 0 deletions clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ namespace Auto {
int &r = f(B<&a>());
float &s = f(B<&b>());

void type_affects_identity(B<&a>) {}
void type_affects_identity(B<(const int*)&a>) {}
void type_affects_identity(B<(void*)&a>) {}
void type_affects_identity(B<(const void*)&a>) {}

// pointers to members
template<typename T, auto *T::*p> struct B<p> {};
template<typename T, auto **T::*p> struct B<p> {};
Expand All @@ -198,6 +203,12 @@ namespace Auto {
char &u = f(B<&X::p>());
short &v = f(B<&X::pp>());

struct Y : X {};
void type_affects_identity(B<&X::n>) {}
void type_affects_identity(B<(int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}}
void type_affects_identity(B<(const int X::*)&X::n>) {}
void type_affects_identity(B<(const int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}}

// A case where we need to do auto-deduction, and check whether the
// resulting dependent types match during partial ordering. These
// templates are not ordered due to the mismatching function parameter.
Expand Down

0 comments on commit 05cdf4a

Please sign in to comment.