Skip to content

Commit 685c458

Browse files
committed
c++: Fix mangling of lambdas in static data member initializers [PR107741]
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 <[email protected]>
1 parent 21cccfa commit 685c458

File tree

15 files changed

+230
-35
lines changed

15 files changed

+230
-35
lines changed

gcc/c-family/c-opts.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,7 @@ c_common_post_options (const char **pfilename)
10841084

10851085
/* Change flag_abi_version to be the actual current ABI level, for the
10861086
benefit of c_cpp_builtins, and to make comparison simpler. */
1087-
const int latest_abi_version = 19;
1087+
const int latest_abi_version = 20;
10881088
/* Generate compatibility aliases for ABI v13 (8.2) by default. */
10891089
const int abi_compat_default = 13;
10901090

gcc/common.opt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,10 +1034,13 @@ Driver Undocumented
10341034
; Default in G++ 13.
10351035
;
10361036
; 19: Emits ABI tags if needed in structured binding mangled names.
1037-
; Ignores cv-quals on [[no_unique_object]] members.
1037+
; Ignores cv-quals on [[no_unique_address]] members.
10381038
; Mangles constraints on function templates.
10391039
; Default in G++ 14.
10401040
;
1041+
; 20: Fix mangling of lambdas in static data member initializers.
1042+
; Default in G++ 15.
1043+
;
10411044
; Additional positive integers will be assigned as new versions of
10421045
; the ABI become the default version of the ABI.
10431046
fabi-version=

gcc/cp/cp-tree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7232,6 +7232,9 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
72327232
tree, bool, tree, tree);
72337233
extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
72347234
tree, tree, tree);
7235+
extern tree start_initialized_static_member (const cp_declarator *,
7236+
cp_decl_specifier_seq *, tree);
7237+
extern void finish_initialized_static_member (tree, tree, tree);
72357238
extern tree splice_template_attributes (tree *, tree);
72367239
extern bool any_dependent_type_attributes_p (tree);
72377240
extern tree cp_reconstruct_complex_type (tree, tree);

gcc/cp/decl2.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,83 @@ grokfield (const cp_declarator *declarator,
12521252
return NULL_TREE;
12531253
}
12541254

1255+
/* Like grokfield, but just for the initial grok of an initialized static
1256+
member. Used to be able to push the new decl before parsing the
1257+
initialiser. */
1258+
1259+
tree
1260+
start_initialized_static_member (const cp_declarator *declarator,
1261+
cp_decl_specifier_seq *declspecs,
1262+
tree attrlist)
1263+
{
1264+
tree value = grokdeclarator (declarator, declspecs, FIELD, SD_INITIALIZED,
1265+
&attrlist);
1266+
if (!value || error_operand_p (value))
1267+
return error_mark_node;
1268+
if (TREE_CODE (value) == TYPE_DECL)
1269+
{
1270+
error_at (declarator->init_loc,
1271+
"typedef %qD is initialized (use %qs instead)",
1272+
value, "decltype");
1273+
return error_mark_node;
1274+
}
1275+
else if (TREE_CODE (value) == FUNCTION_DECL)
1276+
{
1277+
if (TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE)
1278+
error_at (declarator->init_loc,
1279+
"invalid initializer for member function %qD",
1280+
value);
1281+
else if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE)
1282+
error_at (declarator->init_loc,
1283+
"initializer specified for static member function %qD",
1284+
value);
1285+
else
1286+
gcc_unreachable ();
1287+
return error_mark_node;
1288+
}
1289+
else if (TREE_CODE (value) == FIELD_DECL)
1290+
{
1291+
/* NSDM marked 'static', grokdeclarator has already errored. */
1292+
gcc_checking_assert (seen_error ());
1293+
return error_mark_node;
1294+
}
1295+
gcc_checking_assert (VAR_P (value));
1296+
1297+
DECL_CONTEXT (value) = current_class_type;
1298+
if (processing_template_decl)
1299+
{
1300+
value = push_template_decl (value);
1301+
if (error_operand_p (value))
1302+
return error_mark_node;
1303+
}
1304+
1305+
if (attrlist)
1306+
cplus_decl_attributes (&value, attrlist, 0);
1307+
1308+
finish_member_declaration (value);
1309+
DECL_INITIALIZED_IN_CLASS_P (value) = true;
1310+
1311+
return value;
1312+
}
1313+
1314+
/* Finish a declaration prepared with start_initialized_static_member. */
1315+
1316+
void
1317+
finish_initialized_static_member (tree decl, tree init, tree asmspec)
1318+
{
1319+
if (decl == error_mark_node)
1320+
return;
1321+
gcc_checking_assert (VAR_P (decl));
1322+
1323+
int flags;
1324+
if (init && DIRECT_LIST_INIT_P (init))
1325+
flags = LOOKUP_NORMAL;
1326+
else
1327+
flags = LOOKUP_IMPLICIT;
1328+
finish_static_data_member_decl (decl, init, /*init_const_expr_p=*/true,
1329+
asmspec, flags);
1330+
}
1331+
12551332
/* Like `grokfield', but for bitfields.
12561333
WIDTH is the width of the bitfield, a constant expression.
12571334
The other parameters are as for grokfield. */

gcc/cp/lambda.cc

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
3232
#include "gimplify.h"
3333
#include "target.h"
3434
#include "decl.h"
35+
#include "flags.h"
3536

3637
/* Constructor for a lambda expression. */
3738

@@ -1546,13 +1547,35 @@ finish_lambda_scope (void)
15461547
void
15471548
record_lambda_scope (tree lambda)
15481549
{
1549-
LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope.scope;
1550-
if (lambda_scope.scope)
1550+
tree closure = LAMBDA_EXPR_CLOSURE (lambda);
1551+
gcc_checking_assert (closure);
1552+
1553+
/* Before ABI v20, lambdas in static data member initializers did not
1554+
get a dedicated lambda scope. */
1555+
tree scope = lambda_scope.scope;
1556+
if (scope
1557+
&& VAR_P (scope)
1558+
&& DECL_CLASS_SCOPE_P (scope)
1559+
&& DECL_INITIALIZED_IN_CLASS_P (scope))
15511560
{
1552-
tree closure = LAMBDA_EXPR_CLOSURE (lambda);
1553-
gcc_checking_assert (closure);
1554-
maybe_key_decl (lambda_scope.scope, TYPE_NAME (closure));
1561+
if (!abi_version_at_least (20))
1562+
scope = NULL_TREE;
1563+
if (warn_abi && abi_version_crosses (20) && !processing_template_decl)
1564+
{
1565+
if (abi_version_at_least (20))
1566+
warning_at (location_of (closure), OPT_Wabi,
1567+
"the mangled name of %qT changed in "
1568+
"%<-fabi-version=20%> (GCC 15.1)", closure);
1569+
else
1570+
warning_at (location_of (closure), OPT_Wabi,
1571+
"the mangled name of %qT changes in "
1572+
"%<-fabi-version=20%> (GCC 15.1)", closure);
1573+
}
15551574
}
1575+
1576+
LAMBDA_EXPR_EXTRA_SCOPE (lambda) = scope;
1577+
if (scope)
1578+
maybe_key_decl (scope, TYPE_NAME (closure));
15561579
}
15571580

15581581
// Compare lambda template heads TMPL_A and TMPL_B, used for both

gcc/cp/parser.cc

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28885,6 +28885,7 @@ cp_parser_member_declaration (cp_parser* parser)
2888528885
tree first_attribute;
2888628886
tree initializer;
2888728887
bool named_bitfld = false;
28888+
bool decl_was_initialized_p = false;
2888828889

2888928890
/* Peek at the next token. */
2889028891
token = cp_lexer_peek_token (parser->lexer);
@@ -29054,9 +29055,8 @@ cp_parser_member_declaration (cp_parser* parser)
2905429055
pure-specifier. It is not correct to parse the
2905529056
initializer before registering the member declaration
2905629057
since the member declaration should be in scope while
29057-
its initializer is processed. However, the rest of the
29058-
front end does not yet provide an interface that allows
29059-
us to handle this correctly. */
29058+
its initializer is processed. As such we might build
29059+
decl pre-emptively. */
2906029060
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
2906129061
{
2906229062
/* In [class.mem]:
@@ -29083,18 +29083,31 @@ cp_parser_member_declaration (cp_parser* parser)
2908329083
initializer = cp_parser_pure_specifier (parser);
2908429084
else if (decl_specifiers.storage_class != sc_static)
2908529085
initializer = cp_parser_save_nsdmi (parser);
29086-
else if (cxx_dialect >= cxx11)
29086+
else
2908729087
{
29088-
/* Don't require a constant rvalue in C++11, since we
29089-
might want a reference constant. We'll enforce
29090-
constancy later. */
29091-
cp_lexer_consume_token (parser->lexer);
29092-
/* Parse the initializer. */
29093-
initializer = cp_parser_initializer_clause (parser);
29088+
decl = start_initialized_static_member (declarator,
29089+
&decl_specifiers,
29090+
attributes);
29091+
start_lambda_scope (decl);
29092+
29093+
if (cxx_dialect >= cxx11)
29094+
{
29095+
/* Don't require a constant rvalue in C++11, since we
29096+
might want a reference constant. We'll enforce
29097+
constancy later. */
29098+
cp_lexer_consume_token (parser->lexer);
29099+
/* Parse the initializer. */
29100+
initializer = cp_parser_initializer_clause (parser);
29101+
}
29102+
else
29103+
/* Parse the initializer. */
29104+
initializer = cp_parser_constant_initializer (parser);
29105+
29106+
finish_lambda_scope ();
29107+
finish_initialized_static_member (decl, initializer,
29108+
asm_specification);
29109+
decl_was_initialized_p = true;
2909429110
}
29095-
else
29096-
/* Parse the initializer. */
29097-
initializer = cp_parser_constant_initializer (parser);
2909829111
}
2909929112
else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)
2910029113
&& !function_declarator_p (declarator))
@@ -29104,7 +29117,17 @@ cp_parser_member_declaration (cp_parser* parser)
2910429117
if (decl_specifiers.storage_class != sc_static)
2910529118
initializer = cp_parser_save_nsdmi (parser);
2910629119
else
29107-
initializer = cp_parser_initializer (parser);
29120+
{
29121+
decl = start_initialized_static_member (declarator,
29122+
&decl_specifiers,
29123+
attributes);
29124+
start_lambda_scope (decl);
29125+
initializer = cp_parser_initializer (parser);
29126+
finish_lambda_scope ();
29127+
finish_initialized_static_member (decl, initializer,
29128+
asm_specification);
29129+
decl_was_initialized_p = true;
29130+
}
2910829131
}
2910929132
/* Detect invalid bit-field cases such as
2911029133

@@ -29179,16 +29202,19 @@ cp_parser_member_declaration (cp_parser* parser)
2917929202
declarator->id_loc = token->location;
2918029203

2918129204
/* Create the declaration. */
29182-
decl = grokfield (declarator, &decl_specifiers,
29183-
initializer, /*init_const_expr_p=*/true,
29184-
asm_specification, attributes);
29185-
29186-
if (parser->fully_implicit_function_template_p)
29205+
if (!decl_was_initialized_p)
2918729206
{
29188-
if (friend_p)
29189-
finish_fully_implicit_template (parser, 0);
29190-
else
29191-
decl = finish_fully_implicit_template (parser, decl);
29207+
decl = grokfield (declarator, &decl_specifiers,
29208+
initializer, /*init_const_expr_p=*/true,
29209+
asm_specification, attributes);
29210+
29211+
if (parser->fully_implicit_function_template_p)
29212+
{
29213+
if (friend_p)
29214+
finish_fully_implicit_template (parser, 0);
29215+
else
29216+
decl = finish_fully_implicit_template (parser, decl);
29217+
}
2919229218
}
2919329219
}
2919429220

@@ -29242,7 +29268,7 @@ cp_parser_member_declaration (cp_parser* parser)
2924229268
assume_semicolon = true;
2924329269
}
2924429270

29245-
if (decl)
29271+
if (decl && !decl_was_initialized_p)
2924629272
{
2924729273
/* Add DECL to the list of members. */
2924829274
if (!friend_p

gcc/doc/invoke.texi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3020,6 +3020,9 @@ that have additional context.
30203020
Version 19, which first appeard in G++ 14, fixes manglings of structured
30213021
bindings to include ABI tags.
30223022

3023+
Version 20, which first appeared in G++ 15, fixes manglings of lambdas
3024+
in static data member initializers.
3025+
30233026
See also @option{-Wabi}.
30243027

30253028
@opindex fabi-compat-version
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// { dg-do compile { target c++17 } }
2+
// { dg-options -fabi-version=19 }
3+
4+
#include "lambda-ctx2.h"
5+
6+
// A and B demangle incorrectly due to static data members missing a lambda scope
7+
// { dg-final { scan-assembler {_ZNK1AUlvE_clEv:} } }
8+
// { dg-final { scan-assembler {_ZNK1BIiEUlvE2_clEv:} } }
9+
// { dg-final { scan-assembler {_ZNK1BIiEUlvE3_clEv:} } }
10+
// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// { dg-do compile { target c++17 } }
2+
// { dg-options "-fabi-version=20 -Wabi=19" }
3+
4+
#include "lambda-ctx2.h"
5+
6+
// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .A::<lambda>.[^\n]*\n} }
7+
// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .B<int>::<lambda>.[^\n]*\n} }
8+
// { dg-regexp {[^\n]*lambda-ctx2.h:[:0-9]* warning: the mangled name of .B<int>::<lambda>.[^\n]*\n} }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// { dg-do compile { target c++17 } }
2+
// { dg-options -fabi-version=20 }
3+
4+
#include "lambda-ctx2.h"
5+
6+
// These correctly associate lambdas in A::x and B::x.
7+
// { dg-final { scan-assembler {_ZNK1A1xMUlvE_clEv:} } }
8+
// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE_clEv:} } }
9+
// { dg-final { scan-assembler {_ZNK1BIiE1xMUlvE0_clEv:} } }
10+
// { dg-final { scan-assembler {_ZNK1CIiE1xMUlvE_clEv:} } }

0 commit comments

Comments
 (0)