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 <nathanielosh...@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 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 2eeb5e3e8b1..af1e254745b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 5654bc00e4d..6e5228757a5 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -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]: diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 9195a5274e1..ed3aaaf4473 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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) diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2.C new file mode 100644 index 00000000000..26896105a6c --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2.C @@ -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:} } } diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx3.C b/gcc/testsuite/g++.dg/abi/lambda-ctx3.C new file mode 100644 index 00000000000..f92f2500531 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx3.C @@ -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:} } } diff --git a/gcc/testsuite/g++.dg/modules/lambda-8.h b/gcc/testsuite/g++.dg/modules/lambda-8.h new file mode 100644 index 00000000000..0c66f053b20 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-8.h @@ -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>; diff --git a/gcc/testsuite/g++.dg/modules/lambda-8_a.H b/gcc/testsuite/g++.dg/modules/lambda-8_a.H new file mode 100644 index 00000000000..d20958ee140 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-8_a.H @@ -0,0 +1,5 @@ +// PR c++/116568 +// { dg-additional-options "-fmodules-ts -std=c++20" } +// { dg-module-cmi {} } + +#include "lambda-8.h" diff --git a/gcc/testsuite/g++.dg/modules/lambda-8_b.C b/gcc/testsuite/g++.dg/modules/lambda-8_b.C new file mode 100644 index 00000000000..05ea4afd8c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-8_b.C @@ -0,0 +1,5 @@ +// PR c++/116568 +// { dg-additional-options "-fmodules-ts -std=c++20" } + +#include "lambda-8.h" +import "lambda-8_a.H"; -- 2.46.0