Skip to content

Commit 8990070

Browse files
committed
c++: Fix mangling of otherwise unattached class-scope lambdas [PR118245]
This is a step closer to implementing the suggested changes for itanium-cxx-abi/cxx-abi#85. Most lambdas defined within a class should have an extra scope of that class so that uses across different TUs are properly merged by the linker. This also needs to happen during template instantiation. While I was working on this I found some other cases where the mangling of lambdas was incorrect and causing issues, notably the testcase lambda-ctx3.C which currently emits the same mangling for the base class and member lambdas, causing mysterious assembler errors since r14-9232. One notable case not handled either here or in the ABI is what is supposed to happen with such unattached lambdas declared in member templates; see lambda-uneval22. I believe that by the C++ standard, such lambdas should also dedup across TUs, but this isn't currently implemented, and it's not clear exactly how such lambdas should mangle. Since this should only affect usage of lambdas in unevaluated contexts (a C++20 feature) this patch does not add an ABI flag to control this behaviour. PR c++/118245 gcc/cp/ChangeLog: * cp-tree.h (LAMBDA_EXPR_EXTRA_SCOPE): Adjust comment. * parser.cc (cp_parser_class_head): Start (and do not finish) lambda scope for all valid types. (cp_parser_class_specifier): Finish lambda scope after parsing members instead. * pt.cc (instantiate_class_template): Add lambda scoping. gcc/testsuite/ChangeLog: * g++.dg/abi/lambda-ctx3.C: New test. * g++.dg/cpp2a/lambda-uneval22.C: New test. * g++.dg/cpp2a/lambda-uneval23.C: New test. Signed-off-by: Nathaniel Shead <[email protected]> Reviewed-by: Jason Merrill <[email protected]>
1 parent 3fafd9c commit 8990070

File tree

6 files changed

+77
-10
lines changed

6 files changed

+77
-10
lines changed

gcc/cp/cp-tree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1562,7 +1562,8 @@ enum cp_lambda_default_capture_mode_type {
15621562
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->locus)
15631563

15641564
/* The mangling scope for the lambda: FUNCTION_DECL, PARM_DECL, VAR_DECL,
1565-
FIELD_DECL or NULL_TREE. If this is NULL_TREE, we have no linkage. */
1565+
FIELD_DECL, TYPE_DECL, or NULL_TREE. If this is NULL_TREE, we have no
1566+
linkage. */
15661567
#define LAMBDA_EXPR_EXTRA_SCOPE(NODE) \
15671568
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->extra_scope)
15681569

gcc/cp/parser.cc

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27462,6 +27462,8 @@ cp_parser_class_specifier (cp_parser* parser)
2746227462
if (!braces.require_open (parser))
2746327463
{
2746427464
pop_deferring_access_checks ();
27465+
if (type != error_mark_node)
27466+
finish_lambda_scope ();
2746527467
return error_mark_node;
2746627468
}
2746727469

@@ -27526,7 +27528,10 @@ cp_parser_class_specifier (cp_parser* parser)
2752627528
if (cp_parser_allow_gnu_extensions_p (parser))
2752727529
attributes = cp_parser_gnu_attributes_opt (parser);
2752827530
if (type != error_mark_node)
27529-
type = finish_struct (type, attributes);
27531+
{
27532+
type = finish_struct (type, attributes);
27533+
finish_lambda_scope ();
27534+
}
2753027535
if (nested_name_specifier_p)
2753127536
pop_inner_scope (old_scope, scope);
2753227537

@@ -28366,6 +28371,12 @@ cp_parser_class_head (cp_parser* parser,
2836628371
if (flag_concepts)
2836728372
type = associate_classtype_constraints (type);
2836828373

28374+
/* Lambdas in bases and members must have the same mangling scope for ABI.
28375+
We open this scope now, and will close it in cp_parser_class_specifier
28376+
after parsing the member list. */
28377+
if (type && type != error_mark_node)
28378+
start_lambda_scope (TYPE_NAME (type));
28379+
2836928380
/* We will have entered the scope containing the class; the names of
2837028381
base classes should be looked up in that context. For example:
2837128382

@@ -28380,16 +28391,10 @@ cp_parser_class_head (cp_parser* parser,
2838028391
if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
2838128392
{
2838228393
if (type)
28383-
{
28384-
pushclass (type);
28385-
start_lambda_scope (TYPE_NAME (type));
28386-
}
28394+
pushclass (type);
2838728395
bases = cp_parser_base_clause (parser);
2838828396
if (type)
28389-
{
28390-
finish_lambda_scope ();
28391-
popclass ();
28392-
}
28397+
popclass ();
2839328398
}
2839428399
else
2839528400
bases = NULL_TREE;

gcc/cp/pt.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12606,6 +12606,10 @@ instantiate_class_template (tree type)
1260612606
gcc_assert (!DECL_CLASS_SCOPE_P (TYPE_MAIN_DECL (pattern))
1260712607
|| COMPLETE_OR_OPEN_TYPE_P (TYPE_CONTEXT (type)));
1260812608

12609+
/* When instantiating nested lambdas, ensure that they get the mangling
12610+
scope of the new class type. */
12611+
start_lambda_scope (TYPE_NAME (type));
12612+
1260912613
base_list = NULL_TREE;
1261012614
/* Defer access checking while we substitute into the types named in
1261112615
the base-clause. */
@@ -12967,6 +12971,8 @@ instantiate_class_template (tree type)
1296712971
finish_struct_1 (type);
1296812972
TYPE_BEING_DEFINED (type) = 0;
1296912973

12974+
finish_lambda_scope ();
12975+
1297012976
/* Remember if instantiating this class ran into errors, so we can avoid
1297112977
instantiating member functions in limit_bad_template_recursion. We set
1297212978
this flag even if the problem was in another instantiation triggered by
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// { dg-do compile { target c++20 } }
2+
// { dg-additional-options "-fkeep-inline-functions" }
3+
// See also https://github.com/itanium-cxx-abi/cxx-abi/pull/85
4+
5+
struct A {
6+
decltype([]{ return 1; }) f;
7+
};
8+
9+
struct B : decltype([]{ return 2; }) {
10+
decltype([]{ return 3; }) f;
11+
};
12+
13+
template <typename T>
14+
struct C : decltype([]{ return 4; }) {
15+
decltype([]{ return 5; }) f;
16+
};
17+
18+
template struct C<int>;
19+
template struct C<double>;
20+
21+
// { dg-final { scan-assembler {_ZNK1AUlvE_clEv:} } }
22+
// { dg-final { scan-assembler {_ZNK1BUlvE_clEv:} } }
23+
// { dg-final { scan-assembler {_ZNK1BUlvE0_clEv:} } }
24+
// { dg-final { scan-assembler {_ZNK1CIiEUlvE_clEv:} } }
25+
// { dg-final { scan-assembler {_ZNK1CIiEUlvE0_clEv:} } }
26+
// { dg-final { scan-assembler {_ZNK1CIdEUlvE_clEv:} } }
27+
// { dg-final { scan-assembler {_ZNK1CIdEUlvE0_clEv:} } }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// { dg-do compile { target c++20 } }
2+
3+
struct S {
4+
template <int I>
5+
using T = decltype([]{ return I; });
6+
7+
template <int I>
8+
decltype([]{ return I; }) f() { return {}; }
9+
};
10+
11+
void a(S::T<0>*); // { dg-error "declared using local type" }
12+
void b(S::T<1>*); // { dg-error "declared using local type" }
13+
void c(decltype(S{}.f<0>())*); // { dg-error "declared using local type" }
14+
void d(decltype(S{}.f<1>())*); // { dg-error "declared using local type" }
15+
16+
int main() {
17+
a(nullptr);
18+
b(nullptr);
19+
c(nullptr);
20+
d(nullptr);
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// PR c++/118245
2+
// { dg-do compile { target c++20 } }
3+
4+
template<auto> struct Cask {};
5+
struct T1 : Cask<[]{}> {
6+
Cask<[]{}> c{};
7+
};

0 commit comments

Comments
 (0)