Bootstrapped and regtested (so far just dg.exp) on x86_64-pc-linux-gnu,
OK for trunk if full regtest passes? Or would it be better to try to
implement all the rules mentioned in the linked pull request for one
commit; I admit I haven't looked very closely yet at how else we
diverge?
-- >8 --
This is a step closer to implementing the suggested changes for
https://github.com/itanium-cxx-abi/cxx-abi/pull/85.
The main purpose of the patch is to solve testcase PR c++/116568, caused
by lambda expressions within the templates not correctly having the
extra mangling scope attached.
While I was working on this I found some other cases where the mangling
of lambdas was incorrect and causing issues, notably the testcase
lambda-ctx3.C which currently emits the same mangling for the base class
and member lambdas, causing mysterious assembler errors. Fixing this
ended up also improving the situation for PR c++/107741 as well, though
it doesn't seem easily possible to fix the A::x case at this time so
I've left that as an XFAIL.
PR c++/107741
PR c++/116568
gcc/cp/ChangeLog:
* cp-tree.h (LAMBDA_EXPR_EXTRA_SCOPE): Adjust comment.
* parser.cc (cp_parser_class_head): Start (and do not finish)
lambda scope for all valid types.
(cp_parser_class_specifier): Finish lambda scope after parsing
members instead.
(cp_parser_member_declaration): Adjust comment to mention
missing lambda scoping for static member initializers.
* pt.cc (instantiate_class_template): Add lambda scoping.
(instantiate_template): Likewise.
gcc/testsuite/ChangeLog:
* g++.dg/abi/lambda-ctx2.C: New test.
* g++.dg/abi/lambda-ctx3.C: New test.
* g++.dg/modules/lambda-8.h: New test.
* g++.dg/modules/lambda-8_a.H: New test.
* g++.dg/modules/lambda-8_b.C: New test.
Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
gcc/cp/cp-tree.h | 3 +-
gcc/cp/parser.cc | 31 +++++++++++++--------
gcc/cp/pt.cc | 12 +++++++-
gcc/testsuite/g++.dg/abi/lambda-ctx2.C | 34 +++++++++++++++++++++++
gcc/testsuite/g++.dg/abi/lambda-ctx3.C | 21 ++++++++++++++
gcc/testsuite/g++.dg/modules/lambda-8.h | 7 +++++
gcc/testsuite/g++.dg/modules/lambda-8_a.H | 5 ++++
gcc/testsuite/g++.dg/modules/lambda-8_b.C | 5 ++++
8 files changed, 104 insertions(+), 14 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx3.C
create mode 100644 gcc/testsuite/g++.dg/modules/lambda-8.h
create mode 100644 gcc/testsuite/g++.dg/modules/lambda-8_a.H
create mode 100644 gcc/testsuite/g++.dg/modules/lambda-8_b.C
@@ -1513,7 +1513,8 @@ enum cp_lambda_default_capture_mode_type {
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->locus)
/* The mangling scope for the lambda: FUNCTION_DECL, PARM_DECL, VAR_DECL,
- FIELD_DECL or NULL_TREE. If this is NULL_TREE, we have no linkage. */
+ FIELD_DECL, TYPE_DECL, or NULL_TREE. If this is NULL_TREE, we have no
+ linkage. */
#define LAMBDA_EXPR_EXTRA_SCOPE(NODE) \
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->extra_scope)
@@ -27051,6 +27051,8 @@ cp_parser_class_specifier (cp_parser* parser)
if (!braces.require_open (parser))
{
pop_deferring_access_checks ();
+ if (type != error_mark_node)
+ finish_lambda_scope ();
return error_mark_node;
}
@@ -27115,7 +27117,10 @@ cp_parser_class_specifier (cp_parser* parser)
if (cp_parser_allow_gnu_extensions_p (parser))
attributes = cp_parser_gnu_attributes_opt (parser);
if (type != error_mark_node)
- type = finish_struct (type, attributes);
+ {
+ type = finish_struct (type, attributes);
+ finish_lambda_scope ();
+ }
if (nested_name_specifier_p)
pop_inner_scope (old_scope, scope);
@@ -27955,6 +27960,12 @@ cp_parser_class_head (cp_parser* parser,
if (flag_concepts)
type = associate_classtype_constraints (type);
+ /* Lambdas in bases and members must have the same mangling scope for ABI.
+ We open this scope now, and will close it in cp_parser_class_specifier
+ after parsing the member list. */
+ if (type && type != error_mark_node)
+ start_lambda_scope (TYPE_NAME (type));
+
/* We will have entered the scope containing the class; the names of
base classes should be looked up in that context. For example:
@@ -27969,16 +27980,10 @@ cp_parser_class_head (cp_parser* parser,
if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
{
if (type)
- {
- pushclass (type);
- start_lambda_scope (TYPE_NAME (type));
- }
+ pushclass (type);
bases = cp_parser_base_clause (parser);
if (type)
- {
- finish_lambda_scope ();
- popclass ();
- }
+ popclass ();
}
else
bases = NULL_TREE;
@@ -28644,9 +28649,11 @@ cp_parser_member_declaration (cp_parser* parser)
pure-specifier. It is not correct to parse the
initializer before registering the member declaration
since the member declaration should be in scope while
- its initializer is processed. However, the rest of the
- front end does not yet provide an interface that allows
- us to handle this correctly. */
+ its initializer is processed. And similarly, the ABI of
+ lambdas declared in the initializer should be scoped to
+ the member. However, the rest of the front end does not
+ yet provide an interface that allows us to handle this
+ correctly. */
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
{
/* In [class.mem]:
@@ -12482,6 +12482,10 @@ instantiate_class_template (tree type)
gcc_assert (!DECL_CLASS_SCOPE_P (TYPE_MAIN_DECL (pattern))
|| COMPLETE_OR_OPEN_TYPE_P (TYPE_CONTEXT (type)));
+ /* When instantiating nested lambdas, ensure that they get the mangling
+ scope of the new class type. */
+ start_lambda_scope (TYPE_NAME (type));
+
base_list = NULL_TREE;
/* Defer access checking while we substitute into the types named in
the base-clause. */
@@ -12841,6 +12845,8 @@ instantiate_class_template (tree type)
finish_struct_1 (type);
TYPE_BEING_DEFINED (type) = 0;
+ finish_lambda_scope ();
+
/* Remember if instantiating this class ran into errors, so we can avoid
instantiating member functions in limit_bad_template_recursion. We set
this flag even if the problem was in another instantiation triggered by
@@ -22210,6 +22216,7 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
ctx = tsubst_entering_scope (DECL_CONTEXT (gen_tmpl), targ_ptr,
complain, gen_tmpl);
push_nested_class (ctx);
+ start_lambda_scope (TYPE_NAME (ctx));
}
tree pattern = DECL_TEMPLATE_RESULT (gen_tmpl);
@@ -22239,7 +22246,10 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
if (fndecl == NULL_TREE)
fndecl = tsubst_decl (pattern, targ_ptr, complain, /*use_spec_table=*/false);
if (DECL_CLASS_SCOPE_P (gen_tmpl))
- pop_nested_class ();
+ {
+ finish_lambda_scope ();
+ pop_nested_class ();
+ }
pop_from_top_level ();
if (fndecl == error_mark_node)
new file mode 100644
@@ -0,0 +1,34 @@
+// PR c++/107741
+// { dg-do compile { target c++17 } }
+
+struct A {
+ // We currently parse static member initializers for non-templates before we
+ // see their decls, and so don't get the chance to attach it as scope.
+ static constexpr auto x = []{ return 1; };
+};
+
+template <typename>
+struct B {
+ static constexpr auto x = []{ return 2; };
+};
+
+template <typename>
+struct C {
+ static int x;
+};
+
+void side_effect();
+
+template <typename T>
+int C<T>::x = (side_effect(), []{ return 3; }());
+
+template int C<int>::x;
+
+void f() {
+ A::x();
+ B<int>::x();
+}
+
+// { dg-final { scan-assembler {_ZNK1A1xMUlvE_clEv:} { xfail *-*-* } } }
+// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
new file mode 100644
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fkeep-inline-functions" }
+// See also https://github.com/itanium-cxx-abi/cxx-abi/pull/85
+
+struct A {
+ decltype([]{ return 1; }) f;
+};
+
+struct B : decltype([]{ return 2; }) {
+ decltype([]{ return 3; }) f;
+};
+
+struct C : decltype([]{ return 4; }) {
+ decltype([]{ return 5; }) f;
+};
+
+// { dg-final { scan-assembler {_ZNK1AUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BUlvE0_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1CUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1CUlvE0_clEv:} } }
new file mode 100644
@@ -0,0 +1,7 @@
+template <typename> struct S {
+ template <typename> static constexpr auto x = []{};
+ template <typename> using t = decltype([]{});
+};
+
+inline auto x = S<int>::x<int>;
+using t = S<int>::t<int>;
new file mode 100644
@@ -0,0 +1,5 @@
+// PR c++/116568
+// { dg-additional-options "-fmodules-ts -std=c++20" }
+// { dg-module-cmi {} }
+
+#include "lambda-8.h"
new file mode 100644
@@ -0,0 +1,5 @@
+// PR c++/116568
+// { dg-additional-options "-fmodules-ts -std=c++20" }
+
+#include "lambda-8.h"
+import "lambda-8_a.H";