On Fri, Feb 07, 2025 at 08:05:54AM -0500, Jason Merrill wrote: > On 1/31/25 8:44 AM, Nathaniel Shead wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > > > -- >8 -- > > > > 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: > > > > * decl2.cc (start_initialized_static_member): Push the > > TEMPLATE_DECL when appropriate. > > * 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 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 <nathanielosh...@gmail.com> > > --- > > gcc/cp/decl2.cc | 15 ++++++++-- > > gcc/cp/parser.cc | 30 +++++++++++++++---- > > gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C | 3 ++ > > gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C | 3 ++ > > gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C | 3 ++ > > gcc/testsuite/g++.dg/abi/lambda-ctx2.h | 16 ++++++++++ > > .../g++.dg/cpp0x/static-member-init-1.C | 5 ++++ > > 7 files changed, 67 insertions(+), 8 deletions(-) > > > > diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc > > index 9e61afd359f..994a459c79c 100644 > > --- a/gcc/cp/decl2.cc > > +++ b/gcc/cp/decl2.cc > > @@ -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,8 +1307,17 @@ 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); > > Sigh, this all seems increasingly fragile. Perhaps it would have been > preferable to break up grokfield for all members rather than just > initialized variables. Or at least for all static data members. Now is not > the time for that, of course. >
Maybe, yeah; I might look into seeing what can be untangled here for GCC 16. > > return value; > > } > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > > index 7ddb7f119a4..af1c3774f74 100644 > > --- a/gcc/cp/parser.cc > > +++ b/gcc/cp/parser.cc > > @@ -24179,8 +24179,17 @@ cp_parser_init_declarator (cp_parser* parser, > > here we only handle the latter two. */ > > bool has_lambda_scope = false; > > + if (member_p && decl_specifiers->storage_class == sc_static) > > + { > > + gcc_checking_assert (!decl); > > + tree all_attrs = attr_chainon (attributes, prefix_attributes); > > + decl = start_initialized_static_member (declarator, > > + decl_specifiers, > > + all_attrs); > > + } > > Could we do this sooner, near the start_decl call? And adjust the comment > there that says we wait until after the initializer for members... > Done. > > if (decl != error_mark_node > > - && !member_p > > + && (!member_p || decl) > > I think this line can just be "decl" now? > Done. > > && (processing_template_decl || DECL_NAMESPACE_SCOPE_P (decl))) > > has_lambda_scope = true; > > @@ -33739,7 +33752,12 @@ 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 early to be able to be used in their own definition. */ > > + && !(variable_template_p (decl) > > + && DECL_CLASS_SCOPE_P (decl) > > + && DECL_INITIALIZED_IN_CLASS_P (DECL_TEMPLATE_RESULT (decl)))) > > This should be a predicate. > > Jason > Done. So far tested just on dg.exp, OK for trunk if full bootstrap + regtest succeeds? -- >8 -- 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: * decl2.cc (start_initialized_static_member): Push the TEMPLATE_DECL when appropriate. * 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 static members here. 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 <nathanielosh...@gmail.com> Reviewed-by: Jason Merrill <ja...@redhat.com> --- gcc/cp/cp-tree.h | 1 + gcc/cp/decl2.cc | 33 ++++++++++++-- gcc/cp/lambda.cc | 5 +-- gcc/cp/parser.cc | 45 ++++++++++++++----- gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C | 3 ++ gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C | 3 ++ gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C | 3 ++ gcc/testsuite/g++.dg/abi/lambda-ctx2.h | 16 +++++++ .../g++.dg/cpp0x/static-member-init-1.C | 5 +++ 9 files changed, 95 insertions(+), 19 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ec976928f5f..b7749eb2b32 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index a11c431758f..d49aaa4c61b 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -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)) diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index 5593636eaf8..c17815c8901 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -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; diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 44515bb9074..19e500904f9 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -24096,8 +24096,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; @@ -24114,10 +24132,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. */ @@ -24180,7 +24194,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; @@ -24230,10 +24244,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); @@ -33739,7 +33757,10 @@ 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 early 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 diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C index 35d394da8c5..afbbf7a86ef 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C @@ -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:} } } diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C index d4662291e0c..a7f8306233f 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C @@ -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} } diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C index 764f6061876..e61c266e833 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C @@ -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:} } } diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2.h b/gcc/testsuite/g++.dg/abi/lambda-ctx2.h index e359254db90..d6fa546f9cc 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-ctx2.h +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2.h @@ -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>; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C b/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C index e64e77faade..c79aafffbf5 100644 --- a/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C +++ b/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C @@ -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 } }; -- 2.47.0