c++: Lambda context mangling
Commit Message
VAR and FIELD decls can become part of a lambda context, when the
lambda is 'attached' to that entity (It's a C++20 ODR thing that was
discovered with modules, but is actually separate.) We were not
marking those decls as substitution candidates, leading to demangling
failures and compiler variance.
The alternative I chose is to bump the ABI, and add them to the
substitution table. I think this is the intent of the ABI.
The other alternative would be for the demangler to pop off the most
recent substitution when it saw the 'M'. But we'd have to change [at
least] the libiberty demangler[*] and the clang demangler and the clang
mangler, which seems a bigger challenge.
I'll commit this next week, unless y'all have comments.
We have other divergence from clang, with templated lambdas. Clang
added new manglings [Ty, Tn, Tt] for the lambda's template head (Richard
has a pull request, but it's not merged
https://github.com/itanium-cxx-abi/cxx-abi/issues/31). Without that, we
consider all the template args to be auto when demangling (which is
going to look odd for non-type template args). This divergence in
signature also affects the lambda numbering, as gcc & clang differ in
their opinions about 'same lamda signature'. thoughts on addressing
that to in this cycle?
I have some demangler fixes coming up.
nathan
[*] the demangler already seems to have a bug with 'M' handling.
Comments
On 10/7/22 08:27, Nathan Sidwell wrote:
> VAR and FIELD decls can become part of a lambda context, when the
> lambda is 'attached' to that entity (It's a C++20 ODR thing that was
> discovered with modules, but is actually separate.) We were not
> marking those decls as substitution candidates, leading to demangling
> failures and compiler variance.
>
> The alternative I chose is to bump the ABI, and add them to the
> substitution table. I think this is the intent of the ABI.
Agreed: <closure-prefix> is a <prefix>, and <prefix>es are substitution
candidates.
> The other alternative would be for the demangler to pop off the most
> recent substitution when it saw the 'M'. But we'd have to change [at
> least] the libiberty demangler[*] and the clang demangler and the clang
> mangler, which seems a bigger challenge.
>
> I'll commit this next week, unless y'all have comments.
>
> We have other divergence from clang, with templated lambdas. Clang
> added new manglings [Ty, Tn, Tt] for the lambda's template head (Richard
> has a pull request, but it's not merged
> https://github.com/itanium-cxx-abi/cxx-abi/issues/31). Without that, we
> consider all the template args to be auto when demangling (which is
> going to look odd for non-type template args). This divergence in
> signature also affects the lambda numbering, as gcc & clang differ in
> their opinions about 'same lamda signature'. thoughts on addressing
> that to in this cycle?
That sounds good: From looking over the issue and pull request, it seems
pretty well baked.
> I have some demangler fixes coming up.
>
> nathan
>
> [*] the demangler already seems to have a bug with 'M' handling.
>
From 6e943ec67fbe1f2bd09325eb6a2dc6405edfc00f Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan@acm.org>
Date: Fri, 30 Sep 2022 08:43:10 -0700
Subject: [PATCH 1/3] c++: Lambda context mangling
VAR and FIELD decls can become part of a lambda context, when the
lambda is 'attached' to that entity (It's a C++20 ODR thing that was
discovered with modules, but is actually separate.) We were not
marking those decls as substitution candidates, leading to demangling
failures and variance from other compilers.
The alternative I chose is to bump the ABI, and add them to the
substitution table. I think this is the intent of the ABI.
The othe alternative would be for the demangler to pop off the most
recent substitition when it saw the 'M'. But we'd have to change [at
least] the libiberty demangler and the clang demangler and the clang
mangler, which seems a bigger challenge.
gcc/
* common.opt (-fabi-version=): Document 18.
* doc/invoke.texi (-fabi-version): Document 18.
gcc/c-family/
* c-opts.cc (c_common_post_options): Bump abi to 18.
gcc/cp/
* mangle.cc (write_prefix): Add VAR_DECL & FIELD_DECL to
substitution table under abi=18. Note possible mismatch.
gcc/testsuite/
* g++.dg/abi/lambda-ctx1-17.C: New.
* g++.dg/abi/lambda-ctx1-18.C: New.
* g++.dg/abi/lambda-ctx1-18vs17.C: New.
* g++.dg/abi/lambda-ctx1.h: New.
* g++.dg/abi/lambda-vis.C: Adjust expected mangles.
* g++.dg/abi/macro0.C: Adjust.
---
gcc/c-family/c-opts.cc | 2 +-
gcc/common.opt | 3 +++
gcc/cp/mangle.cc | 9 ++++++++-
gcc/doc/invoke.texi | 3 +++
gcc/testsuite/g++.dg/abi/lambda-ctx1-17.C | 10 ++++++++++
gcc/testsuite/g++.dg/abi/lambda-ctx1-18.C | 11 ++++++++++
gcc/testsuite/g++.dg/abi/lambda-ctx1-18vs17.C | 9 +++++++++
gcc/testsuite/g++.dg/abi/lambda-ctx1.h | 20 +++++++++++++++++++
gcc/testsuite/g++.dg/abi/lambda-vis.C | 8 +++++---
gcc/testsuite/g++.dg/abi/macro0.C | 2 +-
10 files changed, 71 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx1-17.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx1-18.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx1-18vs17.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx1.h
@@ -975,7 +975,7 @@ c_common_post_options (const char **pfilename)
/* Change flag_abi_version to be the actual current ABI level, for the
benefit of c_cpp_builtins, and to make comparison simpler. */
- const int latest_abi_version = 17;
+ const int latest_abi_version = 18;
/* Generate compatibility aliases for ABI v13 (8.2) by default. */
const int abi_compat_default = 13;
@@ -1004,6 +1004,9 @@ Driver Undocumented
; member initializers in C++14 and up.
; Default in G++ 12.
;
+; 18: Corrects errors in mangling of lambdas with additional context.
+; Default in G++ 13.
+;
; Additional positive integers will be assigned as new versions of
; the ABI become the default version of the ABI.
fabi-version=
@@ -1252,7 +1252,14 @@ write_prefix (const tree node)
{
/* <data-member-prefix> := <member source-name> M */
write_char ('M');
- return;
+
+ /* Before ABI 18, we did not count these as substitution
+ candidates. This leads to incorrect demanglings (and
+ ABI divergence to other compilers). */
+ if (abi_warn_or_compat_version_crosses (18))
+ G.need_abi_warning = true;
+ if (!abi_version_at_least (18))
+ return;
}
}
@@ -2940,6 +2940,9 @@ Version 17, which first appeared in G++ 12, fixes layout of classes
that inherit from aggregate classes with default member initializers
in C++14 and up.
+Version 18, which first appeard in G++ 13, fixes manglings of lambdas
+that have additional context.
+
See also @option{-Wabi}.
@item -fabi-compat-version=@var{n}
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++20 } }
+// { dg-options -fabi-version=17 }
+
+#include "lambda-ctx1.h"
+
+// These demangle incorrectly, due to a missed substitution candidate
+// { dg-final { scan-assembler {_ZNK1C1fMUlT_E_clIMS_iEEDaS0_:} } }
+// { dg-final { scan-assembler {_ZNK2L2MUlT_T0_E_clIifEEvS_S0_:} } }
+// { dg-final { scan-assembler {_ZNK1B2L3MUlT_T0_E_clIjdEEvS0_S1_:} } }
+// { dg-final { scan-assembler {_Z3fooIN1qMUlvE_EN1qMUlvE0_EEiOT_OT0_:} } }
new file mode 100644
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++20 } }
+// { dg-options -fabi-version=18 }
+
+#include "lambda-ctx1.h"
+
+// These correctly include the lambda's extra context as a
+// substitution candidate, and thus demangle as expected
+// { dg-final { scan-assembler {_ZNK1C1fMUlT_E_clIMS_iEEDaS1_:} } }
+// { dg-final { scan-assembler {_ZNK2L2MUlT_T0_E_clIifEEvS0_S1_:} } }
+// { dg-final { scan-assembler {_ZNK1B2L3MUlT_T0_E_clIjdEEvS1_S2_:} } }
+// { dg-final { scan-assembler {_Z3fooIN1qMUlvE_ENS0_UlvE0_EEiOT_OT0_:} } }
new file mode 100644
@@ -0,0 +1,9 @@
+// { dg-do compile { target c++20 } }
+// { dg-options {-fabi-version=18 -Wabi=17} }
+
+#include "lambda-ctx1.h"
+
+// { dg-regexp {[^\n]*lambda-ctx1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK1B2L3MUlT_T0_E_clIjdEEvS0_S1_'\) and '-fabi-version=18' \('_ZNK1B2L3MUlT_T0_E_clIjdEEvS1_S2_'\) [^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK2L2MUlT_T0_E_clIifEEvS_S0_'\) and '-fabi-version=18' \('_ZNK2L2MUlT_T0_E_clIifEEvS0_S1_'\) [^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK1C1fMUlT_E_clIMS_iEEDaS0_'\) and '-fabi-version=18' \('_ZNK1C1fMUlT_E_clIMS_iEEDaS1_'\) [^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx1.h:[:0-9]* warning: the mangled name [^\n]* \('_Z3fooIN1qMUlvE_EN1qMUlvE0_EEiOT_OT0_'\) and '-fabi-version=18' \('_Z3fooIN1qMUlvE_ENS0_UlvE0_EEiOT_OT0_'\) [^\n]*\n} }
new file mode 100644
@@ -0,0 +1,20 @@
+inline auto L2 = [] <typename T, typename U> (T, U) -> void {};
+namespace B
+{
+ inline auto L3 = [] <typename T, typename U> (T, U) -> void {};
+}
+
+struct C
+{
+ int f = [] (auto){ return 1;}(&C::f);
+ C ();
+};
+
+C::C ()
+{
+ L2 (1, 1.2f);
+ B::L3 (1u, 1.2);
+}
+
+template <typename A, typename B> int foo (A&&, B&&) {return 0;}
+inline int q = foo ([](){}, [](){});
@@ -13,9 +13,11 @@ int gvar = gfoo (capture ([]{}));
inline int ivar = ifoo (capture ([]{}));
-// { dg-final { scan-assembler {_?_Z7captureINL4svarMUlvE_EE7WrapperIT_EOS2_:} } }
-// { dg-final { scan-assembler {_?_Z7captureIN4gvarMUlvE_EE7WrapperIT_EOS2_:} } }
-// { dg-final { scan-assembler {_?_Z7captureIN4ivarMUlvE_EE7WrapperIT_EOS2_:} } }
+// These manglings change between ABIs 17 and 18 (the final
+// substitution number).
+// { dg-final { scan-assembler {_?_Z7captureINL4svarMUlvE_EE7WrapperIT_EOS3_:} } }
+// { dg-final { scan-assembler {_?_Z7captureIN4gvarMUlvE_EE7WrapperIT_EOS3_:} } }
+// { dg-final { scan-assembler {_?_Z7captureIN4ivarMUlvE_EE7WrapperIT_EOS3_:} } }
// Calls to the foos are emitted.
// { dg-final { scan-assembler {call[ \t]*_?_Z4sfooI7WrapperINL4svarMUlvE_EEEiT_} { target { i?86-*-* x86_64-*-* } } } }
@@ -1,6 +1,6 @@
// This testcase will need to be kept in sync with c_common_post_options.
// { dg-options "-fabi-version=0" }
-#if __GXX_ABI_VERSION != 1017
+#if __GXX_ABI_VERSION != 1018
#error "Incorrect value of __GXX_ABI_VERSION"
#endif
--
2.30.2