]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Fix mangling of lambas in static member template initializers [PR107741]
authorNathaniel Shead <nathanieloshead@gmail.com>
Fri, 31 Jan 2025 10:19:45 +0000 (21:19 +1100)
committerNathaniel Shead <nathanieloshead@gmail.com>
Fri, 14 Feb 2025 01:14:15 +0000 (12:14 +1100)
My fix for this issue in r15-7147 turns out to not be quite sufficient;
static member templates apparently go down a different code path and
need their own handling.

PR c++/107741

gcc/cp/ChangeLog:

* cp-tree.h (is_static_data_member_initialized_in_class):
Declare new predicate.
* decl2.cc (start_initialized_static_member): Push the
TEMPLATE_DECL when appropriate.
(is_static_data_member_initialized_in_class): New predicate.
(finish_initialized_static_member): Use it.
* lambda.cc (record_lambda_scope): Likewise.
* parser.cc (cp_parser_init_declarator): Start the member decl
early for static members so that lambda scope is set.
(cp_parser_template_declaration_after_parameters): Don't
register in-class initialized static members here.

gcc/testsuite/ChangeLog:

* g++.dg/abi/lambda-ctx2-19.C: Add tests for template members.
* g++.dg/abi/lambda-ctx2-19vs20.C: Likewise.
* g++.dg/abi/lambda-ctx2-20.C: Likewise.
* g++.dg/abi/lambda-ctx2.h: Likewise.
* g++.dg/cpp0x/static-member-init-1.C: Likewise.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/cp-tree.h
gcc/cp/decl2.cc
gcc/cp/lambda.cc
gcc/cp/parser.cc
gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C
gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C
gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C
gcc/testsuite/g++.dg/abi/lambda-ctx2.h
gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C

index ec976928f5fb0eb25935b47c7b7042cc064756f9..b7749eb2b327cd4707ed587803fc7f5ece3b5100 100644 (file)
@@ -7250,6 +7250,7 @@ extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
                          tree, tree, tree);
 extern tree start_initialized_static_member    (const cp_declarator *,
                                                 cp_decl_specifier_seq *, tree);
+extern bool is_static_data_member_initialized_in_class (tree decl);
 extern void finish_initialized_static_member   (tree, tree, tree);
 extern tree splice_template_attributes         (tree *, tree);
 extern bool any_dependent_type_attributes_p    (tree);
index aa9ebca12f8fc26a08f5759b887c9802db2627dc..0217a8e247402173ddafd5f47bfa78d8f49c173d 100644 (file)
@@ -1295,6 +1295,8 @@ start_initialized_static_member (const cp_declarator *declarator,
   gcc_checking_assert (VAR_P (value));
 
   DECL_CONTEXT (value) = current_class_type;
+  DECL_INITIALIZED_IN_CLASS_P (value) = true;
+
   if (processing_template_decl)
     {
       value = push_template_decl (value);
@@ -1305,12 +1307,37 @@ start_initialized_static_member (const cp_declarator *declarator,
   if (attrlist)
     cplus_decl_attributes (&value, attrlist, 0);
 
-  finish_member_declaration (value);
-  DECL_INITIALIZED_IN_CLASS_P (value) = true;
+  /* When defining a template we need to register the TEMPLATE_DECL.  */
+  tree maybe_template = value;
+  if (template_parm_scope_p ())
+    {
+      if (!DECL_TEMPLATE_SPECIALIZATION (value))
+       maybe_template = DECL_TI_TEMPLATE (value);
+      else
+       maybe_template = NULL_TREE;
+    }
+  if (maybe_template)
+    finish_member_declaration (maybe_template);
 
   return value;
 }
 
+/* Whether DECL is a static data member initialized at the point
+   of declaration within its class.  */
+
+bool
+is_static_data_member_initialized_in_class (tree decl)
+{
+  if (!decl || decl == error_mark_node)
+    return false;
+
+  tree inner = STRIP_TEMPLATE (decl);
+  return (inner
+         && VAR_P (inner)
+         && DECL_CLASS_SCOPE_P (inner)
+         && DECL_INITIALIZED_IN_CLASS_P (inner));
+}
+
 /* Finish a declaration prepared with start_initialized_static_member.  */
 
 void
@@ -1318,7 +1345,7 @@ finish_initialized_static_member (tree decl, tree init, tree asmspec)
 {
   if (decl == error_mark_node)
     return;
-  gcc_checking_assert (VAR_P (decl));
+  gcc_checking_assert (is_static_data_member_initialized_in_class (decl));
 
   int flags;
   if (init && DIRECT_LIST_INIT_P (init))
index 2d86e9892f99aea2baa4953878726c0ed813585b..c612f4fe1ad054d9f42d5287b3b3f7ee660f9ae1 100644 (file)
@@ -1555,10 +1555,7 @@ record_lambda_scope (tree lambda)
   /* Before ABI v20, lambdas in static data member initializers did not
      get a dedicated lambda scope.  */
   tree scope = lambda_scope.scope;
-  if (scope
-      && VAR_P (scope)
-      && DECL_CLASS_SCOPE_P (scope)
-      && DECL_INITIALIZED_IN_CLASS_P (scope))
+  if (is_static_data_member_initialized_in_class (scope))
     {
       if (!abi_version_at_least (20))
        scope = NULL_TREE;
index 8284d6597872865222ed9109f6938e05e7725ddb..0578aad1b1cf507cd785d43496388bf6fe14cd3b 100644 (file)
@@ -24101,8 +24101,26 @@ cp_parser_init_declarator (cp_parser* parser,
 
   /* Enter the newly declared entry in the symbol table.  If we're
      processing a declaration in a class-specifier, we wait until
-     after processing the initializer.  */
-  if (!member_p)
+     after processing the initializer, except for static data members
+     initialized here.  */
+  if (member_p)
+    {
+      if (scope)
+       /* Enter the SCOPE.  That way unqualified names appearing in the
+          initializer will be looked up in SCOPE.  */
+       pushed_scope = push_scope (scope);
+
+      if (is_initialized
+         && decl_specifiers->storage_class == sc_static
+         && !function_declarator_p (declarator))
+       {
+         tree all_attrs = attr_chainon (attributes, prefix_attributes);
+         decl = start_initialized_static_member (declarator,
+                                                 decl_specifiers,
+                                                 all_attrs);
+       }
+    }
+  else
     {
       if (parser->in_unbraced_linkage_specification_p)
        decl_specifiers->storage_class = sc_extern;
@@ -24119,10 +24137,6 @@ cp_parser_init_declarator (cp_parser* parser,
          && DECL_SOURCE_LOCATION (decl) == input_location)
        DECL_SOURCE_LOCATION (decl) = declarator->id_loc;
     }
-  else if (scope)
-    /* Enter the SCOPE.  That way unqualified names appearing in the
-       initializer will be looked up in SCOPE.  */
-    pushed_scope = push_scope (scope);
 
   /* Perform deferred access control checks, now that we know in which
      SCOPE the declared entity resides.  */
@@ -24185,7 +24199,7 @@ cp_parser_init_declarator (cp_parser* parser,
          bool has_lambda_scope = false;
 
          if (decl != error_mark_node
-             && !member_p
+             && decl
              && (processing_template_decl || DECL_NAMESPACE_SCOPE_P (decl)))
            has_lambda_scope = true;
 
@@ -24235,10 +24249,14 @@ cp_parser_init_declarator (cp_parser* parser,
          pop_scope (pushed_scope);
          pushed_scope = NULL_TREE;
        }
-      decl = grokfield (declarator, decl_specifiers,
-                       initializer, !is_non_constant_init,
-                       /*asmspec=*/NULL_TREE,
-                       attr_chainon (attributes, prefix_attributes));
+      if (decl)
+       finish_initialized_static_member (decl, initializer,
+                                         /*asmspec=*/NULL_TREE);
+      else
+       decl = grokfield (declarator, decl_specifiers,
+                         initializer, !is_non_constant_init,
+                         /*asmspec=*/NULL_TREE,
+                         attr_chainon (attributes, prefix_attributes));
       if (decl && TREE_CODE (decl) == FUNCTION_DECL)
        cp_parser_save_default_args (parser, decl);
       cp_finalize_omp_declare_simd (parser, decl);
@@ -33765,7 +33783,11 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
     }
 
   /* Register member declarations.  */
-  if (member_p && !friend_p && decl && !DECL_CLASS_TEMPLATE_P (decl))
+  if (member_p && !friend_p && decl && !DECL_CLASS_TEMPLATE_P (decl)
+      /* But this is not needed for initialised static members, that were
+        registered in start_initialized_static_member to be able to be used
+        in their own definition.  */
+      && !is_static_data_member_initialized_in_class (decl))
     finish_member_declaration (decl);
   /* If DECL is a function template, we must return to parse it later.
      (Even though there is no definition, there might be default
index 35d394da8c5e5c5c7b72795e3a17257bc374b878..afbbf7a86efbd2d177c84f8e36b57608ad3d74fc 100644 (file)
@@ -8,3 +8,6 @@
 // { dg-final { scan-assembler {_ZNK1BIiEUlvE2_clEv:} } }
 // { dg-final { scan-assembler {_ZNK1BIiEUlvE3_clEv:} } }
 // { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1DUlvE7_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1EIiEUlvE8_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1EIiEUlvE9_clEv:} } }
index d4662291e0c1f024f6180387cb5131b0093e9efb..a7f8306233f0684dc707ca4e7b734a8abd5aafbc 100644 (file)
@@ -6,3 +6,6 @@
 // { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .A::<lambda>.[^\n]*\n} }
 // { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .B<int>::<lambda>.[^\n]*\n} }
 // { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .B<int>::<lambda>.[^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .D::<lambda>.[^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .E<int>::<lambda>.[^\n]*\n} }
+// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .E<int>::<lambda>.[^\n]*\n} }
index 764f606187676ebddd9e56dc8c89108e539a086b..e61c266e833dbc0098ce39baed9bfd8825ec90c9 100644 (file)
@@ -8,3 +8,6 @@
 // { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE_clEv:} } }
 // { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE0_clEv:} } }
 // { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1D1xIiEUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1EIiE1xIiEUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1EIiE1xIiEUlvE0_clEv:} } }
index e359254db904190ce61b789a102e836b6fc2eba5..d6fa546f9ccff7aba6982c143e284f257e075074 100644 (file)
@@ -25,3 +25,19 @@ int f() {
   A::x();
   return B<int>::x;
 }
+
+struct D {
+  template <typename>
+  static constexpr auto x = []{ return 5; };
+};
+
+template <typename>
+struct E {
+  template <typename>
+  static inline auto x = (side_effect(), []{ return 6; }(), []{ return 7; }());
+};
+
+int g() {
+  D::x<int>();
+  return E<int>::x<int>;
+}
index e64e77faade074e2d22e175eaedecfc67c0fcde4..c79aafffbf565d309b79ac588f2e2157463f1316 100644 (file)
@@ -1,5 +1,10 @@
 // { dg-do compile { target c++11 } }
+// { dg-options "-pedantic" }
 
 struct S {
     static constexpr const void* x = &x;
+
+    template <typename> static inline const void* y = &y<int>;
+    // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 }
+    // { dg-warning "inline variables are only available with" "" { target c++14_down } .-2 }
 };