The patch fixes a bug in the detection of the usage of static variables inside generic lambdas. It does so by forcing the instantiation of generic lambdas within their enclosing scope. This ensures that the scope's static variables usage is computed correctly.
Signed-off-by: Lucas Chollet <[email protected]> --- Hey everyone, This is my first patch submission to GCC so I hope I followed all the right steps. To test the patch I both bootstraped the compiler and ran the c++ testsuite on both master and my branch with the command: ``` make -j -C gcc -k check-c++-all ``` Finally I compared the results with: ``` ./contrib/dg-cmp-results.sh -v -v '*' \ ../gcc_pristine/gcc/testsuite/g++/g++.sum \ ../gcc_build/gcc/testsuite/g++/g++.sum ``` And got this result: ``` dg-cmp-results.sh: Verbosity is 2, Variant is "*" Older log file: ../gcc_pristine/gcc/testsuite/g++/g++.sum Test run by lucas on Wed Nov 12 21:55:15 2025 Native configuration is x86_64-pc-linux-gnu Newer log file: ../gcc_build/gcc/testsuite/g++/g++.sum Test run by lucas on Thu Nov 13 10:25:06 2025 Native configuration is x86_64-pc-linux-gnu NA->UNSUPPORTED: g++.dg/warn/Wunused-var-42.C -std=gnu++11 NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++14 (test for warnings, line 19) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++14 (test for warnings, line 25) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++14 (test for warnings, line 31) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++14 (test for excess errors) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++17 (test for warnings, line 19) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++17 (test for warnings, line 25) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++17 (test for warnings, line 31) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++17 (test for excess errors) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++20 (test for warnings, line 19) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++20 (test for warnings, line 25) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++20 (test for warnings, line 31) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++20 (test for excess errors) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++23 (test for warnings, line 19) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++23 (test for warnings, line 25) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++23 (test for warnings, line 31) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++23 (test for excess errors) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++26 -fimplicit-constexpr (test for warnings, line 19) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++26 -fimplicit-constexpr (test for warnings, line 25) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++26 -fimplicit-constexpr (test for warnings, line 31) NA->PASS: g++.dg/warn/Wunused-var-42.C -std=gnu++26 -fimplicit-constexpr (test for excess errors) NA->UNSUPPORTED: g++.dg/warn/Wunused-var-42.C -std=gnu++98 ``` So everything seems to be in order to me :^). gcc/ChangeLog: 2025-11-13 Lucas Chollet <[email protected]> PR c++/114450 cp/ * decl2.cc (has_generic_lambda_param_p): New. (mark_used): Don't allow deferring template instantiations for generic lambdas. testsuite/ * g++.dg/warn/Wunused-var-42.C: New. gcc/cp/decl2.cc | 37 +++++++++++++++++++++- gcc/testsuite/g++.dg/warn/Wunused-var-42.C | 33 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wunused-var-42.C diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 9e135af41b3..7e02b193f96 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -6332,6 +6332,37 @@ fn_template_being_defined (tree decl) return fn_being_defined (pattern); } +/* True if the function declaration DECL takes a generic lambda as one of its + template parameters. */ + +static bool +has_generic_lambda_param_p (tree decl) +{ + if (TREE_CODE (decl) != FUNCTION_DECL) + return false; + + tree tinfo = DECL_TEMPLATE_INFO (decl); + if (!tinfo) + return false; + + tree args = TI_ARGS (tinfo); + for (int i = 0; i < TREE_VEC_LENGTH (args); i) + { + tree arg = TREE_VEC_ELT (args, i); + if (TREE_CODE (arg) == RECORD_TYPE && LAMBDA_TYPE_P (arg)) + { + tree callop = lambda_function (arg); + tree call_type = TREE_TYPE (callop); + for (tree parms = TYPE_ARG_TYPES (call_type); + parms; + parms = TREE_CHAIN (parms)) + if (uses_template_parms (TREE_VALUE (parms))) + return true; + } + } + return false; +} + /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program. If DECL is a specialization or implicitly declared class member, generate the actual definition. Return false if something goes @@ -6581,7 +6612,11 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */) need. Therefore, we always try to defer instantiation. */ { ++function_depth; - instantiate_decl (decl, /*defer_ok=*/true, + /* Generic lambdas needs to be instantiated while we parse their + parent scope. Otherwise, we won't be able the usage of local + static variable. See bug 114450. */ + bool defer_ok = !(warn_unused && has_generic_lambda_param_p (decl)); + instantiate_decl (decl, /*defer_ok=*/defer_ok, /*expl_inst_class_mem_p=*/false); --function_depth; } diff --git a/gcc/testsuite/g++.dg/warn/Wunused-var-42.C b/gcc/testsuite/g++.dg/warn/Wunused-var-42.C new file mode 100644 index 00000000000..f84fb94c334 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wunused-var-42.C @@ -0,0 +1,33 @@ +// { dg-do compile { target c14 } } +// { dg-options "-Wunused" } + +template <typename F> +void f (F &&d) +{ + static unsigned context; + d(context); +} + +void g () +{ + static int b; + f([](auto c) { return c <= b; }); +} + +void h () +{ + static int b = 0; // { dg-warning "unused variable" } + f([](auto c) { return c <= 0; }); +} + +void i () +{ + static int b = 0; // { dg-warning "set but not used" } + [](auto c) { return c <= b; }; +} + +void j () +{ + static int b = 0; // { dg-warning "unused variable" } + [](auto c) { return c <= 0; }; +} -- 2.51.0
