diff options
author | Simon Martin <simon@nasilyan.com> | 2025-03-12 20:15:39 +0100 |
---|---|---|
committer | Simon Martin <simon@nasilyan.com> | 2025-03-12 20:16:01 +0100 |
commit | 90e53ecdbfcc482ad3d0090658427de6d44a5d49 (patch) | |
tree | a76977ab64b67bd1efdee42b0be5abe965fb891a /gcc | |
parent | 9ee6c2619b256878d43800a16f7b98b3ddf59e52 (diff) | |
download | gcc-90e53ecdbfcc482ad3d0090658427de6d44a5d49.zip gcc-90e53ecdbfcc482ad3d0090658427de6d44a5d49.tar.gz gcc-90e53ecdbfcc482ad3d0090658427de6d44a5d49.tar.bz2 |
c++: Look through capture proxy from outer lambda instead of erroring out [PR110584]
We've been rejecting this valid code since r8-4571:
=== cut here ===
void foo (float);
int main () {
constexpr float x = 0;
(void) [&] () {
foo (x);
(void) [] () {
foo (x);
};
};
}
=== cut here ===
The problem is that when processing X in the inner lambda,
process_outer_var_ref errors out even though it does find the constant
capture from the enclosing lambda.
This patch makes sure that process_outer_var_ref properly looks through
normal capture proxies, if any.
PR c++/110584
gcc/cp/ChangeLog:
* cp-tree.h (strip_normal_capture_proxy): Declare.
* lambda.cc (strip_normal_capture_proxy): New function to look
through normal capture proxies.
(build_capture_proxy): Use it.
* semantics.cc (process_outer_var_ref): Likewise.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/lambda/lambda-nested10.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/lambda.cc | 14 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C | 46 |
4 files changed, 62 insertions, 7 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a839ad6..07500fa 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8114,6 +8114,7 @@ extern void insert_capture_proxy (tree); extern void insert_pending_capture_proxies (void); extern bool is_capture_proxy (tree); extern bool is_normal_capture_proxy (tree); +extern tree strip_normal_capture_proxy (tree); extern bool is_constant_capture_proxy (tree); extern void register_capture_members (tree); extern tree lambda_expr_this_capture (tree, int); diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index da075b9..ed70bb0 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -295,6 +295,17 @@ is_normal_capture_proxy (tree decl) && DECL_CAPTURED_VARIABLE (decl)); } +/* If DECL is a normal capture proxy, return the variable it captures. + Otherwise, just return DECL. */ + +tree +strip_normal_capture_proxy (tree decl) +{ + while (is_normal_capture_proxy (decl)) + decl = DECL_CAPTURED_VARIABLE (decl); + return decl; +} + /* Returns true iff DECL is a capture proxy for a normal capture of a constant variable. */ @@ -469,8 +480,7 @@ build_capture_proxy (tree member, tree init) STRIP_NOPS (init); gcc_assert (VAR_P (init) || TREE_CODE (init) == PARM_DECL); - while (is_normal_capture_proxy (init)) - init = DECL_CAPTURED_VARIABLE (init); + init = strip_normal_capture_proxy (init); retrofit_lang_decl (var); DECL_CAPTURED_VARIABLE (var) = init; } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 7c7d3e3..6e10893 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4528,6 +4528,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) tree lambda_stack = NULL_TREE; tree lambda_expr = NULL_TREE; tree initializer = convert_from_reference (decl); + tree var = strip_normal_capture_proxy (decl); /* Mark it as used now even if the use is ill-formed. */ if (!mark_used (decl, complain)) @@ -4539,9 +4540,6 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) if (containing_function && LAMBDA_FUNCTION_P (containing_function)) { /* Check whether we've already built a proxy. */ - tree var = decl; - while (is_normal_capture_proxy (var)) - var = DECL_CAPTURED_VARIABLE (var); tree d = retrieve_local_specialization (var); if (d && d != decl && is_capture_proxy (d)) @@ -4601,8 +4599,8 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) /* Only an odr-use of an outer automatic variable causes an error, and a constant variable can decay to a prvalue constant without odr-use. So don't complain yet. */ - else if (!odr_use && decl_constant_var_p (decl)) - return decl; + else if (!odr_use && decl_constant_var_p (var)) + return var; else if (lambda_expr) { if (complain & tf_error) diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C new file mode 100644 index 0000000..aced567 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C @@ -0,0 +1,46 @@ +// PR c++/110584 +// { dg-do "run" { target c++11 } } + +void foo (int i) { + if (i != 0) + __builtin_abort (); +} + +int main () { + const int x = 0; + + // We would error out on this. + (void) [&] () { + foo (x); + (void) [] () { + foo (x); + }; + } (); + // As well as those. + (void) [&] () { + (void) [] () { + foo (x); + }; + } (); + (void) [&x] () { + (void) [] () { + foo (x); + }; + } (); + // But those would work already. + (void) [] () { + (void) [&] () { + foo (x); + }; + } (); + (void) [&] () { + (void) [&] () { + foo (x); + }; + } (); + (void) [=] () { + (void) [] () { + foo (x); + }; + } (); +} |