[v3,1/6] c++: Fix mangling of lambdas in static data member initializers [PR107741]
Commit Message
This fixes an issue where lambdas declared in the initializer of a
static data member within the class body do not get a mangling scope of
that variable; this results in mangled names that do not conform to the
ABI spec.
To do this, the patch splits up grokfield for this case specifically,
allowing a declaration to be build and used in start_lambda_scope before
parsing the initializer, so that record_lambda_scope works correctly.
As a drive-by, this also fixes the issue of a static member not being
visible within its own initializer.
PR c++/107741
gcc/c-family/ChangeLog:
* c-opts.cc (c_common_post_options): Bump ABI version.
gcc/ChangeLog:
* common.opt: Add -fabi-version=20.
* doc/invoke.texi: Likewise.
gcc/cp/ChangeLog:
* cp-tree.h (start_initialized_static_member): Declare.
(finish_initialized_static_member): Declare.
* decl2.cc (start_initialized_static_member): New function.
(finish_initialized_static_member): New function.
* lambda.cc (record_lambda_scope): Support falling back to old
ABI (maybe with warning).
* parser.cc (cp_parser_member_declaration): Build decl early
when parsing an initialized static data member.
gcc/testsuite/ChangeLog:
* g++.dg/abi/macro0.C: Bump ABI version.
* g++.dg/abi/mangle74.C: Remove XFAILs.
* g++.dg/other/fold1.C: Restore originally raised error.
* g++.dg/abi/lambda-ctx2-19.C: New test.
* g++.dg/abi/lambda-ctx2-19vs20.C: New test.
* g++.dg/abi/lambda-ctx2-20.C: New test.
* g++.dg/abi/lambda-ctx2.h: New test.
* g++.dg/cpp0x/static-member-init-1.C: New test.
Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
gcc/c-family/c-opts.cc | 2 +-
gcc/common.opt | 5 +-
gcc/cp/cp-tree.h | 3 +
gcc/cp/decl2.cc | 77 +++++++++++++++++++
gcc/cp/lambda.cc | 33 ++++++--
gcc/cp/parser.cc | 74 ++++++++++++------
gcc/doc/invoke.texi | 3 +
gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C | 10 +++
gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C | 8 ++
gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C | 10 +++
gcc/testsuite/g++.dg/abi/lambda-ctx2.h | 27 +++++++
gcc/testsuite/g++.dg/abi/macro0.C | 2 +-
gcc/testsuite/g++.dg/abi/mangle74.C | 4 +-
.../g++.dg/cpp0x/static-member-init-1.C | 5 ++
gcc/testsuite/g++.dg/other/fold1.C | 2 +-
15 files changed, 230 insertions(+), 35 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C
create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2.h
create mode 100644 gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C
Comments
On 1/6/25 7:21 AM, Nathaniel Shead wrote:
> This fixes an issue where lambdas declared in the initializer of a
> static data member within the class body do not get a mangling scope of
> that variable; this results in mangled names that do not conform to the
> ABI spec.
>
> To do this, the patch splits up grokfield for this case specifically,
> allowing a declaration to be build and used in start_lambda_scope before
> parsing the initializer, so that record_lambda_scope works correctly.
>
> As a drive-by, this also fixes the issue of a static member not being
> visible within its own initializer.
OK.
> PR c++/107741
>
> gcc/c-family/ChangeLog:
>
> * c-opts.cc (c_common_post_options): Bump ABI version.
>
> gcc/ChangeLog:
>
> * common.opt: Add -fabi-version=20.
> * doc/invoke.texi: Likewise.
>
> gcc/cp/ChangeLog:
>
> * cp-tree.h (start_initialized_static_member): Declare.
> (finish_initialized_static_member): Declare.
> * decl2.cc (start_initialized_static_member): New function.
> (finish_initialized_static_member): New function.
> * lambda.cc (record_lambda_scope): Support falling back to old
> ABI (maybe with warning).
> * parser.cc (cp_parser_member_declaration): Build decl early
> when parsing an initialized static data member.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/abi/macro0.C: Bump ABI version.
> * g++.dg/abi/mangle74.C: Remove XFAILs.
> * g++.dg/other/fold1.C: Restore originally raised error.
> * g++.dg/abi/lambda-ctx2-19.C: New test.
> * g++.dg/abi/lambda-ctx2-19vs20.C: New test.
> * g++.dg/abi/lambda-ctx2-20.C: New test.
> * g++.dg/abi/lambda-ctx2.h: New test.
> * g++.dg/cpp0x/static-member-init-1.C: New test.
>
> Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> ---
> gcc/c-family/c-opts.cc | 2 +-
> gcc/common.opt | 5 +-
> gcc/cp/cp-tree.h | 3 +
> gcc/cp/decl2.cc | 77 +++++++++++++++++++
> gcc/cp/lambda.cc | 33 ++++++--
> gcc/cp/parser.cc | 74 ++++++++++++------
> gcc/doc/invoke.texi | 3 +
> gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C | 10 +++
> gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C | 8 ++
> gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C | 10 +++
> gcc/testsuite/g++.dg/abi/lambda-ctx2.h | 27 +++++++
> gcc/testsuite/g++.dg/abi/macro0.C | 2 +-
> gcc/testsuite/g++.dg/abi/mangle74.C | 4 +-
> .../g++.dg/cpp0x/static-member-init-1.C | 5 ++
> gcc/testsuite/g++.dg/other/fold1.C | 2 +-
> 15 files changed, 230 insertions(+), 35 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C
> create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C
> create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C
> create mode 100644 gcc/testsuite/g++.dg/abi/lambda-ctx2.h
> create mode 100644 gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C
>
> diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
> index b81d1350b1a..87b231861a6 100644
> --- a/gcc/c-family/c-opts.cc
> +++ b/gcc/c-family/c-opts.cc
> @@ -1084,7 +1084,7 @@ c_common_post_options (const char **pfilename)
>
> /* Change flag_abi_version to be the actual current ABI level, for the
> benefit of c_cpp_builtins, and to make comparison simpler. */
> - const int latest_abi_version = 19;
> + const int latest_abi_version = 20;
> /* Generate compatibility aliases for ABI v13 (8.2) by default. */
> const int abi_compat_default = 13;
>
> diff --git a/gcc/common.opt b/gcc/common.opt
> index e2ac99df1d0..4c2560a0632 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1034,10 +1034,13 @@ Driver Undocumented
> ; Default in G++ 13.
> ;
> ; 19: Emits ABI tags if needed in structured binding mangled names.
> -; Ignores cv-quals on [[no_unique_object]] members.
> +; Ignores cv-quals on [[no_unique_address]] members.
> ; Mangles constraints on function templates.
> ; Default in G++ 14.
> ;
> +; 20: Fix mangling of lambdas in static data member initializers.
> +; Default in G++ 15.
> +;
> ; Additional positive integers will be assigned as new versions of
> ; the ABI become the default version of the ABI.
> fabi-version=
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 1d741ecedc3..f77a325bdd0 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7224,6 +7224,9 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
> tree, bool, tree, tree);
> 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 void finish_initialized_static_member (tree, tree, tree);
> extern tree splice_template_attributes (tree *, tree);
> extern bool any_dependent_type_attributes_p (tree);
> extern tree cp_reconstruct_complex_type (tree, tree);
> diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> index 869701bceee..5e534935dfd 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -1252,6 +1252,83 @@ grokfield (const cp_declarator *declarator,
> return NULL_TREE;
> }
>
> +/* Like grokfield, but just for the initial grok of an initialized static
> + member. Used to be able to push the new decl before parsing the
> + initialiser. */
> +
> +tree
> +start_initialized_static_member (const cp_declarator *declarator,
> + cp_decl_specifier_seq *declspecs,
> + tree attrlist)
> +{
> + tree value = grokdeclarator (declarator, declspecs, FIELD, SD_INITIALIZED,
> + &attrlist);
> + if (!value || error_operand_p (value))
> + return error_mark_node;
> + if (TREE_CODE (value) == TYPE_DECL)
> + {
> + error_at (declarator->init_loc,
> + "typedef %qD is initialized (use %qs instead)",
> + value, "decltype");
> + return error_mark_node;
> + }
> + else if (TREE_CODE (value) == FUNCTION_DECL)
> + {
> + if (TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE)
> + error_at (declarator->init_loc,
> + "invalid initializer for member function %qD",
> + value);
> + else if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE)
> + error_at (declarator->init_loc,
> + "initializer specified for static member function %qD",
> + value);
> + else
> + gcc_unreachable ();
> + return error_mark_node;
> + }
> + else if (TREE_CODE (value) == FIELD_DECL)
> + {
> + /* NSDM marked 'static', grokdeclarator has already errored. */
> + gcc_checking_assert (seen_error ());
> + return error_mark_node;
> + }
> + gcc_checking_assert (VAR_P (value));
> +
> + DECL_CONTEXT (value) = current_class_type;
> + if (processing_template_decl)
> + {
> + value = push_template_decl (value);
> + if (error_operand_p (value))
> + return error_mark_node;
> + }
> +
> + if (attrlist)
> + cplus_decl_attributes (&value, attrlist, 0);
> +
> + finish_member_declaration (value);
> + DECL_INITIALIZED_IN_CLASS_P (value) = true;
> +
> + return value;
> +}
> +
> +/* Finish a declaration prepared with start_initialized_static_member. */
> +
> +void
> +finish_initialized_static_member (tree decl, tree init, tree asmspec)
> +{
> + if (decl == error_mark_node)
> + return;
> + gcc_checking_assert (VAR_P (decl));
> +
> + int flags;
> + if (init && DIRECT_LIST_INIT_P (init))
> + flags = LOOKUP_NORMAL;
> + else
> + flags = LOOKUP_IMPLICIT;
> + finish_static_data_member_decl (decl, init, /*init_const_expr_p=*/true,
> + asmspec, flags);
> +}
> +
> /* Like `grokfield', but for bitfields.
> WIDTH is the width of the bitfield, a constant expression.
> The other parameters are as for grokfield. */
> diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
> index be8a0fe01cb..3166f5eab2c 100644
> --- a/gcc/cp/lambda.cc
> +++ b/gcc/cp/lambda.cc
> @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
> #include "gimplify.h"
> #include "target.h"
> #include "decl.h"
> +#include "flags.h"
>
> /* Constructor for a lambda expression. */
>
> @@ -1540,13 +1541,35 @@ finish_lambda_scope (void)
> void
> record_lambda_scope (tree lambda)
> {
> - LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope.scope;
> - if (lambda_scope.scope)
> + tree closure = LAMBDA_EXPR_CLOSURE (lambda);
> + gcc_checking_assert (closure);
> +
> + /* 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))
> {
> - tree closure = LAMBDA_EXPR_CLOSURE (lambda);
> - gcc_checking_assert (closure);
> - maybe_key_decl (lambda_scope.scope, TYPE_NAME (closure));
> + if (!abi_version_at_least (20))
> + scope = NULL_TREE;
> + if (warn_abi && abi_version_crosses (20) && !processing_template_decl)
> + {
> + if (abi_version_at_least (20))
> + warning_at (location_of (closure), OPT_Wabi,
> + "the mangled name of %qT changed in "
> + "%<-fabi-version=20%> (GCC 15.1)", closure);
> + else
> + warning_at (location_of (closure), OPT_Wabi,
> + "the mangled name of %qT changes in "
> + "%<-fabi-version=20%> (GCC 15.1)", closure);
> + }
> }
> +
> + LAMBDA_EXPR_EXTRA_SCOPE (lambda) = scope;
> + if (scope)
> + maybe_key_decl (scope, TYPE_NAME (closure));
> }
>
> // Compare lambda template heads TMPL_A and TMPL_B, used for both
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index f548dc31c2b..dd986444449 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -28737,6 +28737,7 @@ cp_parser_member_declaration (cp_parser* parser)
> tree first_attribute;
> tree initializer;
> bool named_bitfld = false;
> + bool decl_was_initialized_p = false;
>
> /* Peek at the next token. */
> token = cp_lexer_peek_token (parser->lexer);
> @@ -28906,9 +28907,8 @@ 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. As such we might build
> + decl pre-emptively. */
> if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
> {
> /* In [class.mem]:
> @@ -28935,18 +28935,31 @@ cp_parser_member_declaration (cp_parser* parser)
> initializer = cp_parser_pure_specifier (parser);
> else if (decl_specifiers.storage_class != sc_static)
> initializer = cp_parser_save_nsdmi (parser);
> - else if (cxx_dialect >= cxx11)
> + else
> {
> - /* Don't require a constant rvalue in C++11, since we
> - might want a reference constant. We'll enforce
> - constancy later. */
> - cp_lexer_consume_token (parser->lexer);
> - /* Parse the initializer. */
> - initializer = cp_parser_initializer_clause (parser);
> + decl = start_initialized_static_member (declarator,
> + &decl_specifiers,
> + attributes);
> + start_lambda_scope (decl);
> +
> + if (cxx_dialect >= cxx11)
> + {
> + /* Don't require a constant rvalue in C++11, since we
> + might want a reference constant. We'll enforce
> + constancy later. */
> + cp_lexer_consume_token (parser->lexer);
> + /* Parse the initializer. */
> + initializer = cp_parser_initializer_clause (parser);
> + }
> + else
> + /* Parse the initializer. */
> + initializer = cp_parser_constant_initializer (parser);
> +
> + finish_lambda_scope ();
> + finish_initialized_static_member (decl, initializer,
> + asm_specification);
> + decl_was_initialized_p = true;
> }
> - else
> - /* Parse the initializer. */
> - initializer = cp_parser_constant_initializer (parser);
> }
> else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)
> && !function_declarator_p (declarator))
> @@ -28956,7 +28969,17 @@ cp_parser_member_declaration (cp_parser* parser)
> if (decl_specifiers.storage_class != sc_static)
> initializer = cp_parser_save_nsdmi (parser);
> else
> - initializer = cp_parser_initializer (parser);
> + {
> + decl = start_initialized_static_member (declarator,
> + &decl_specifiers,
> + attributes);
> + start_lambda_scope (decl);
> + initializer = cp_parser_initializer (parser);
> + finish_lambda_scope ();
> + finish_initialized_static_member (decl, initializer,
> + asm_specification);
> + decl_was_initialized_p = true;
> + }
> }
> /* Detect invalid bit-field cases such as
>
> @@ -29031,16 +29054,19 @@ cp_parser_member_declaration (cp_parser* parser)
> declarator->id_loc = token->location;
>
> /* Create the declaration. */
> - decl = grokfield (declarator, &decl_specifiers,
> - initializer, /*init_const_expr_p=*/true,
> - asm_specification, attributes);
> -
> - if (parser->fully_implicit_function_template_p)
> + if (!decl_was_initialized_p)
> {
> - if (friend_p)
> - finish_fully_implicit_template (parser, 0);
> - else
> - decl = finish_fully_implicit_template (parser, decl);
> + decl = grokfield (declarator, &decl_specifiers,
> + initializer, /*init_const_expr_p=*/true,
> + asm_specification, attributes);
> +
> + if (parser->fully_implicit_function_template_p)
> + {
> + if (friend_p)
> + finish_fully_implicit_template (parser, 0);
> + else
> + decl = finish_fully_implicit_template (parser, decl);
> + }
> }
> }
>
> @@ -29094,7 +29120,7 @@ cp_parser_member_declaration (cp_parser* parser)
> assume_semicolon = true;
> }
>
> - if (decl)
> + if (decl && !decl_was_initialized_p)
> {
> /* Add DECL to the list of members. */
> if (!friend_p
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 0a7a81b2067..e2e7f29f9ce 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -3018,6 +3018,9 @@ that have additional context.
> Version 19, which first appeard in G++ 14, fixes manglings of structured
> bindings to include ABI tags.
>
> +Version 20, which first appeared in G++ 15, fixes manglings of lambdas
> +in static data member initializers.
> +
> See also @option{-Wabi}.
>
> @opindex fabi-compat-version
> diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C
> new file mode 100644
> index 00000000000..35d394da8c5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target c++17 } }
> +// { dg-options -fabi-version=19 }
> +
> +#include "lambda-ctx2.h"
> +
> +// A and B demangle incorrectly due to static data members missing a lambda scope
> +// { dg-final { scan-assembler {_ZNK1AUlvE_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1BIiEUlvE2_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1BIiEUlvE3_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
> diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C
> new file mode 100644
> index 00000000000..d4662291e0c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-19vs20.C
> @@ -0,0 +1,8 @@
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-fabi-version=20 -Wabi=19" }
> +
> +#include "lambda-ctx2.h"
> +
> +// { 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} }
> diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C b/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C
> new file mode 100644
> index 00000000000..764f6061876
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2-20.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target c++17 } }
> +// { dg-options -fabi-version=20 }
> +
> +#include "lambda-ctx2.h"
> +
> +// These correctly associate lambdas in A::x and B::x.
> +// { dg-final { scan-assembler {_ZNK1A1xMUlvE_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE0_clEv:} } }
> +// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
> diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx2.h b/gcc/testsuite/g++.dg/abi/lambda-ctx2.h
> new file mode 100644
> index 00000000000..e359254db90
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx2.h
> @@ -0,0 +1,27 @@
> +// PR c++/107741
> +
> +void side_effect();
> +
> +struct A {
> + static constexpr auto x = []{ return 1; };
> +};
> +
> +template <typename>
> +struct B {
> + static inline auto x = (side_effect(), []{ return 2; }(), []{ return 3; }());
> +};
> +
> +template <typename>
> +struct C {
> + static int x;
> +};
> +
> +template <typename T>
> +int C<T>::x = (side_effect(), []{ return 4; }());
> +
> +template int C<int>::x;
> +
> +int f() {
> + A::x();
> + return B<int>::x;
> +}
> diff --git a/gcc/testsuite/g++.dg/abi/macro0.C b/gcc/testsuite/g++.dg/abi/macro0.C
> index 183184e0f0a..f6a57c11ae7 100644
> --- a/gcc/testsuite/g++.dg/abi/macro0.C
> +++ b/gcc/testsuite/g++.dg/abi/macro0.C
> @@ -1,6 +1,6 @@
> // This testcase will need to be kept in sync with c_common_post_options.
> // { dg-options "-fabi-version=0" }
>
> -#if __GXX_ABI_VERSION != 1019
> +#if __GXX_ABI_VERSION != 1020
> #error "Incorrect value of __GXX_ABI_VERSION"
> #endif
> diff --git a/gcc/testsuite/g++.dg/abi/mangle74.C b/gcc/testsuite/g++.dg/abi/mangle74.C
> index 7451ce81495..14fe9b9fbf2 100644
> --- a/gcc/testsuite/g++.dg/abi/mangle74.C
> +++ b/gcc/testsuite/g++.dg/abi/mangle74.C
> @@ -26,5 +26,5 @@ int thorn ()
> }
>
> // { dg-final { scan-assembler "_ZNK3varMUlvE_clEv:" } }
> -// { dg-final { scan-assembler "_ZNK3Foo3barMUlvE_clEv:" { xfail *-*-* } } }
> -// { dg-final { scan-assembler-not "_ZNK3FooUlvE_clEv:" { xfail *-*-* } } }
> +// { dg-final { scan-assembler "_ZNK3Foo3barMUlvE_clEv:" } }
> +// { dg-final { scan-assembler-not "_ZNK3FooUlvE_clEv:" } }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C b/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C
> new file mode 100644
> index 00000000000..e64e77faade
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/static-member-init-1.C
> @@ -0,0 +1,5 @@
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> + static constexpr const void* x = &x;
> +};
> diff --git a/gcc/testsuite/g++.dg/other/fold1.C b/gcc/testsuite/g++.dg/other/fold1.C
> index 8d8df3de68e..d2fc518c220 100644
> --- a/gcc/testsuite/g++.dg/other/fold1.C
> +++ b/gcc/testsuite/g++.dg/other/fold1.C
> @@ -3,6 +3,6 @@
>
> struct A
> {
> - static const int i = i; // { dg-error "not declared" }
> + static const int i = j; // { dg-error "not declared" }
> int x[i]; // { dg-error "11:size of array .x. is not an integral constant-expression" }
> };
@@ -1084,7 +1084,7 @@ c_common_post_options (const char **pfilename)
/* Change flag_abi_version to be the actual current ABI level, for the
benefit of c_cpp_builtins, and to make comparison simpler. */
- const int latest_abi_version = 19;
+ const int latest_abi_version = 20;
/* Generate compatibility aliases for ABI v13 (8.2) by default. */
const int abi_compat_default = 13;
@@ -1034,10 +1034,13 @@ Driver Undocumented
; Default in G++ 13.
;
; 19: Emits ABI tags if needed in structured binding mangled names.
-; Ignores cv-quals on [[no_unique_object]] members.
+; Ignores cv-quals on [[no_unique_address]] members.
; Mangles constraints on function templates.
; Default in G++ 14.
;
+; 20: Fix mangling of lambdas in static data member initializers.
+; Default in G++ 15.
+;
; Additional positive integers will be assigned as new versions of
; the ABI become the default version of the ABI.
fabi-version=
@@ -7224,6 +7224,9 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
tree, bool, tree, tree);
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 void finish_initialized_static_member (tree, tree, tree);
extern tree splice_template_attributes (tree *, tree);
extern bool any_dependent_type_attributes_p (tree);
extern tree cp_reconstruct_complex_type (tree, tree);
@@ -1252,6 +1252,83 @@ grokfield (const cp_declarator *declarator,
return NULL_TREE;
}
+/* Like grokfield, but just for the initial grok of an initialized static
+ member. Used to be able to push the new decl before parsing the
+ initialiser. */
+
+tree
+start_initialized_static_member (const cp_declarator *declarator,
+ cp_decl_specifier_seq *declspecs,
+ tree attrlist)
+{
+ tree value = grokdeclarator (declarator, declspecs, FIELD, SD_INITIALIZED,
+ &attrlist);
+ if (!value || error_operand_p (value))
+ return error_mark_node;
+ if (TREE_CODE (value) == TYPE_DECL)
+ {
+ error_at (declarator->init_loc,
+ "typedef %qD is initialized (use %qs instead)",
+ value, "decltype");
+ return error_mark_node;
+ }
+ else if (TREE_CODE (value) == FUNCTION_DECL)
+ {
+ if (TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE)
+ error_at (declarator->init_loc,
+ "invalid initializer for member function %qD",
+ value);
+ else if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE)
+ error_at (declarator->init_loc,
+ "initializer specified for static member function %qD",
+ value);
+ else
+ gcc_unreachable ();
+ return error_mark_node;
+ }
+ else if (TREE_CODE (value) == FIELD_DECL)
+ {
+ /* NSDM marked 'static', grokdeclarator has already errored. */
+ gcc_checking_assert (seen_error ());
+ return error_mark_node;
+ }
+ gcc_checking_assert (VAR_P (value));
+
+ DECL_CONTEXT (value) = current_class_type;
+ if (processing_template_decl)
+ {
+ value = push_template_decl (value);
+ if (error_operand_p (value))
+ return error_mark_node;
+ }
+
+ if (attrlist)
+ cplus_decl_attributes (&value, attrlist, 0);
+
+ finish_member_declaration (value);
+ DECL_INITIALIZED_IN_CLASS_P (value) = true;
+
+ return value;
+}
+
+/* Finish a declaration prepared with start_initialized_static_member. */
+
+void
+finish_initialized_static_member (tree decl, tree init, tree asmspec)
+{
+ if (decl == error_mark_node)
+ return;
+ gcc_checking_assert (VAR_P (decl));
+
+ int flags;
+ if (init && DIRECT_LIST_INIT_P (init))
+ flags = LOOKUP_NORMAL;
+ else
+ flags = LOOKUP_IMPLICIT;
+ finish_static_data_member_decl (decl, init, /*init_const_expr_p=*/true,
+ asmspec, flags);
+}
+
/* Like `grokfield', but for bitfields.
WIDTH is the width of the bitfield, a constant expression.
The other parameters are as for grokfield. */
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimplify.h"
#include "target.h"
#include "decl.h"
+#include "flags.h"
/* Constructor for a lambda expression. */
@@ -1540,13 +1541,35 @@ finish_lambda_scope (void)
void
record_lambda_scope (tree lambda)
{
- LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope.scope;
- if (lambda_scope.scope)
+ tree closure = LAMBDA_EXPR_CLOSURE (lambda);
+ gcc_checking_assert (closure);
+
+ /* 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))
{
- tree closure = LAMBDA_EXPR_CLOSURE (lambda);
- gcc_checking_assert (closure);
- maybe_key_decl (lambda_scope.scope, TYPE_NAME (closure));
+ if (!abi_version_at_least (20))
+ scope = NULL_TREE;
+ if (warn_abi && abi_version_crosses (20) && !processing_template_decl)
+ {
+ if (abi_version_at_least (20))
+ warning_at (location_of (closure), OPT_Wabi,
+ "the mangled name of %qT changed in "
+ "%<-fabi-version=20%> (GCC 15.1)", closure);
+ else
+ warning_at (location_of (closure), OPT_Wabi,
+ "the mangled name of %qT changes in "
+ "%<-fabi-version=20%> (GCC 15.1)", closure);
+ }
}
+
+ LAMBDA_EXPR_EXTRA_SCOPE (lambda) = scope;
+ if (scope)
+ maybe_key_decl (scope, TYPE_NAME (closure));
}
// Compare lambda template heads TMPL_A and TMPL_B, used for both
@@ -28737,6 +28737,7 @@ cp_parser_member_declaration (cp_parser* parser)
tree first_attribute;
tree initializer;
bool named_bitfld = false;
+ bool decl_was_initialized_p = false;
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
@@ -28906,9 +28907,8 @@ 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. As such we might build
+ decl pre-emptively. */
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
{
/* In [class.mem]:
@@ -28935,18 +28935,31 @@ cp_parser_member_declaration (cp_parser* parser)
initializer = cp_parser_pure_specifier (parser);
else if (decl_specifiers.storage_class != sc_static)
initializer = cp_parser_save_nsdmi (parser);
- else if (cxx_dialect >= cxx11)
+ else
{
- /* Don't require a constant rvalue in C++11, since we
- might want a reference constant. We'll enforce
- constancy later. */
- cp_lexer_consume_token (parser->lexer);
- /* Parse the initializer. */
- initializer = cp_parser_initializer_clause (parser);
+ decl = start_initialized_static_member (declarator,
+ &decl_specifiers,
+ attributes);
+ start_lambda_scope (decl);
+
+ if (cxx_dialect >= cxx11)
+ {
+ /* Don't require a constant rvalue in C++11, since we
+ might want a reference constant. We'll enforce
+ constancy later. */
+ cp_lexer_consume_token (parser->lexer);
+ /* Parse the initializer. */
+ initializer = cp_parser_initializer_clause (parser);
+ }
+ else
+ /* Parse the initializer. */
+ initializer = cp_parser_constant_initializer (parser);
+
+ finish_lambda_scope ();
+ finish_initialized_static_member (decl, initializer,
+ asm_specification);
+ decl_was_initialized_p = true;
}
- else
- /* Parse the initializer. */
- initializer = cp_parser_constant_initializer (parser);
}
else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)
&& !function_declarator_p (declarator))
@@ -28956,7 +28969,17 @@ cp_parser_member_declaration (cp_parser* parser)
if (decl_specifiers.storage_class != sc_static)
initializer = cp_parser_save_nsdmi (parser);
else
- initializer = cp_parser_initializer (parser);
+ {
+ decl = start_initialized_static_member (declarator,
+ &decl_specifiers,
+ attributes);
+ start_lambda_scope (decl);
+ initializer = cp_parser_initializer (parser);
+ finish_lambda_scope ();
+ finish_initialized_static_member (decl, initializer,
+ asm_specification);
+ decl_was_initialized_p = true;
+ }
}
/* Detect invalid bit-field cases such as
@@ -29031,16 +29054,19 @@ cp_parser_member_declaration (cp_parser* parser)
declarator->id_loc = token->location;
/* Create the declaration. */
- decl = grokfield (declarator, &decl_specifiers,
- initializer, /*init_const_expr_p=*/true,
- asm_specification, attributes);
-
- if (parser->fully_implicit_function_template_p)
+ if (!decl_was_initialized_p)
{
- if (friend_p)
- finish_fully_implicit_template (parser, 0);
- else
- decl = finish_fully_implicit_template (parser, decl);
+ decl = grokfield (declarator, &decl_specifiers,
+ initializer, /*init_const_expr_p=*/true,
+ asm_specification, attributes);
+
+ if (parser->fully_implicit_function_template_p)
+ {
+ if (friend_p)
+ finish_fully_implicit_template (parser, 0);
+ else
+ decl = finish_fully_implicit_template (parser, decl);
+ }
}
}
@@ -29094,7 +29120,7 @@ cp_parser_member_declaration (cp_parser* parser)
assume_semicolon = true;
}
- if (decl)
+ if (decl && !decl_was_initialized_p)
{
/* Add DECL to the list of members. */
if (!friend_p
@@ -3018,6 +3018,9 @@ that have additional context.
Version 19, which first appeard in G++ 14, fixes manglings of structured
bindings to include ABI tags.
+Version 20, which first appeared in G++ 15, fixes manglings of lambdas
+in static data member initializers.
+
See also @option{-Wabi}.
@opindex fabi-compat-version
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++17 } }
+// { dg-options -fabi-version=19 }
+
+#include "lambda-ctx2.h"
+
+// A and B demangle incorrectly due to static data members missing a lambda scope
+// { dg-final { scan-assembler {_ZNK1AUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BIiEUlvE2_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BIiEUlvE3_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
new file mode 100644
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-fabi-version=20 -Wabi=19" }
+
+#include "lambda-ctx2.h"
+
+// { 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} }
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++17 } }
+// { dg-options -fabi-version=20 }
+
+#include "lambda-ctx2.h"
+
+// These correctly associate lambdas in A::x and B::x.
+// { dg-final { scan-assembler {_ZNK1A1xMUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE0_clEv:} } }
+// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
new file mode 100644
@@ -0,0 +1,27 @@
+// PR c++/107741
+
+void side_effect();
+
+struct A {
+ static constexpr auto x = []{ return 1; };
+};
+
+template <typename>
+struct B {
+ static inline auto x = (side_effect(), []{ return 2; }(), []{ return 3; }());
+};
+
+template <typename>
+struct C {
+ static int x;
+};
+
+template <typename T>
+int C<T>::x = (side_effect(), []{ return 4; }());
+
+template int C<int>::x;
+
+int f() {
+ A::x();
+ return B<int>::x;
+}
@@ -1,6 +1,6 @@
// This testcase will need to be kept in sync with c_common_post_options.
// { dg-options "-fabi-version=0" }
-#if __GXX_ABI_VERSION != 1019
+#if __GXX_ABI_VERSION != 1020
#error "Incorrect value of __GXX_ABI_VERSION"
#endif
@@ -26,5 +26,5 @@ int thorn ()
}
// { dg-final { scan-assembler "_ZNK3varMUlvE_clEv:" } }
-// { dg-final { scan-assembler "_ZNK3Foo3barMUlvE_clEv:" { xfail *-*-* } } }
-// { dg-final { scan-assembler-not "_ZNK3FooUlvE_clEv:" { xfail *-*-* } } }
+// { dg-final { scan-assembler "_ZNK3Foo3barMUlvE_clEv:" } }
+// { dg-final { scan-assembler-not "_ZNK3FooUlvE_clEv:" } }
new file mode 100644
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++11 } }
+
+struct S {
+ static constexpr const void* x = &x;
+};
@@ -3,6 +3,6 @@
struct A
{
- static const int i = i; // { dg-error "not declared" }
+ static const int i = j; // { dg-error "not declared" }
int x[i]; // { dg-error "11:size of array .x. is not an integral constant-expression" }
};