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

Reply via email to