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 <[email protected]>
> > ---
> > 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 <[email protected]>
Reviewed-by: Jason Merrill <[email protected]>
---
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