https://gcc.gnu.org/g:efb9667e2fb81dc0dd4dab25a84756c040bc3506

commit r17-607-gefb9667e2fb81dc0dd4dab25a84756c040bc3506
Author: Marek Polacek <[email protected]>
Date:   Thu May 14 09:27:17 2026 -0400

    c++: capture of reference to global in template [PR123536]
    
    Thanks to DR 696 (r253266), this works:
    
      int g;
      void fn ()
      {
        int &c = g;
        auto l = [] { c++; };
        l();
      }
    
    because `c` in the lambda body is not an odr-use because we can
    evaluate it to a constant and so there's no capture.  But when
    fn is a template, we reject the code and crash.  This patch fixes
    both.
    
    Outside a template, the call to maybe_constant_value in mark_use
    evaluates `c` to `(int&) &g` but in a template, it remains `c`.
    Then we emit an error, and crash on the error_mark_node from
    process_outer_var_ref.  One of the reasons is
          else if (TYPE_REF_P (TREE_TYPE (expression)))
            /* FIXME cp_finish_decl doesn't fold reference initializers.  */
            return true;
    in value_dependent_expression_p but even if that changed, we still
    wouldn't get the referent because decl_really_constant_value wouldn't
    give it to us; the DECL_INITIAL is not a TREE_CONSTANT yet.
    
    So I stopped trying to make this work in a template, and instead
    I'm deferring the error in process_outer_var_ref to instantiation
    when it's instantiation-dependent.  The VAR_P check there is not
    to regress the diagnostic in pr57416.C.
    
    The mark_use hunk is to fix a crash on invalid (lambda-const14.C).
    
            PR c++/123536
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (process_outer_var_ref): Remove a parameter's name.
            * expr.cc (mark_use): Return if mark_rvalue_use returns
            error_mark_node.
            * semantics.cc (process_outer_var_ref): Return decl when it is
            instantiation-dependent.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/lambda/lambda-const12.C: New test.
            * g++.dg/cpp0x/lambda/lambda-const13.C: New test.
            * g++.dg/cpp0x/lambda/lambda-const14.C: New test.
            * g++.dg/template/local11.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>
    Reviewed-by: Patrick Palka <[email protected]>

Diff:
---
 gcc/cp/cp-tree.h                                   |  3 +-
 gcc/cp/expr.cc                                     |  2 +
 gcc/cp/semantics.cc                                |  9 ++-
 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C | 74 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C | 19 ++++++
 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const14.C | 16 +++++
 gcc/testsuite/g++.dg/template/local11.C            | 18 ++++++
 7 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8a168f509d40..6a8adbc18caf 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8584,7 +8584,8 @@ extern tree finish_base_specifier         (tree, tree, 
bool, tree);
 extern void finish_member_declaration          (tree);
 extern bool outer_automatic_var_p              (tree);
 extern bool parsing_lambda_declarator          ();
-extern tree process_outer_var_ref              (tree, tsubst_flags_t, bool 
force_use = false);
+extern tree process_outer_var_ref              (tree, tsubst_flags_t,
+                                                bool = false);
 extern cp_expr finish_id_expression            (tree, tree, tree,
                                                 cp_id_kind *,
                                                 bool, bool, bool *,
diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
index 4d017d530ef7..516cea084a6f 100644
--- a/gcc/cp/expr.cc
+++ b/gcc/cp/expr.cc
@@ -182,6 +182,8 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
                }
            }
          tree r = mark_rvalue_use (ref, loc, reject_builtin);
+         if (r == error_mark_node)
+           return error_mark_node;
          if (r != ref)
            {
              if (!rvalue_p)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 490441dba6c0..0d47b20fb3c2 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4595,7 +4595,8 @@ set_contract_capture_flag (tree d, bool val)
    id-expression, and we do lambda capture.  */
 
 tree
-process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
+process_outer_var_ref (tree decl, tsubst_flags_t complain,
+                      bool odr_use/*=false*/)
 {
   if (cp_unevaluated_operand)
     {
@@ -4697,6 +4698,10 @@ process_outer_var_ref (tree decl, tsubst_flags_t 
complain, bool odr_use)
      constant without odr-use.  So don't complain yet.  */
   else if (!odr_use && decl_constant_var_p (var))
     return var;
+  /* Don't complain when DECL is dependent, because it can turn out to
+     be constant (and therefore needing no capture) when instantiating.  */
+  else if (VAR_P (var) && instantiation_dependent_expression_p (var))
+    return var;
   else if (lambda_expr)
     {
       if (complain & tf_error)
@@ -4715,7 +4720,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t 
complain, bool odr_use)
        }
       return error_mark_node;
     }
-  else if (processing_contract_condition && (TREE_CODE (decl) == PARM_DECL))
+  else if (processing_contract_condition && TREE_CODE (decl) == PARM_DECL)
     /* Use of a parameter in a contract condition is fine.  */
     return decl;
   else
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C
new file mode 100644
index 000000000000..e87627b7e249
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C
@@ -0,0 +1,74 @@
+// PR c++/123536
+// { dg-do compile { target c++11 } }
+
+int g;
+
+struct S {
+  static int static_data_member;
+};
+
+template<int = 1>
+void
+fn1 ()
+{
+  int &c = g;
+  auto l = [] { c++; };
+  l();
+}
+
+template<int N = 1>
+void
+fn2 ()
+{
+  const int &c = N;
+  // TODO This will be valid once we implement P2686.
+  auto l = [] { int i = c; (void) i; }; // { dg-error ".c. is not captured" }
+  l();
+}
+
+void
+fn3 ()
+{
+  int &c = g;
+  auto l = [] { c++; };
+  l();
+}
+
+void
+fn4 ()
+{
+  int n = 42;
+  const int &c = n;
+  auto l = [] { int i = c; (void) i; }; // { dg-error ".c. is not captured" }
+  l();
+}
+
+template<class T>
+void
+fn5 ()
+{
+  int& c = T::static_data_member;
+  auto l = [] { c++; };
+  l();
+}
+
+template<class T>
+void
+fn6 ()
+{
+  int& c = T::static_data_member;
+  [&c] {
+    [] { c++; }();
+  };
+}
+
+void
+bar ()
+{
+  fn1 ();
+  fn2 ();
+  fn3 ();
+  fn4 ();
+  fn5<S>();
+  fn6<S>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C
new file mode 100644
index 000000000000..a6044c52e7e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C
@@ -0,0 +1,19 @@
+// PR c++/123536
+// { dg-do compile { target c++11 } }
+
+int g;
+
+template<class T>
+void
+fn1 ()
+{
+  T c = g;
+  auto l = [] { c++; };
+  l();
+}
+
+void
+bar ()
+{
+  fn1<int&> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const14.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const14.C
new file mode 100644
index 000000000000..2299556a4e68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const14.C
@@ -0,0 +1,16 @@
+// PR c++/123536
+// { dg-do compile { target c++11 } }
+
+template<class T>
+void
+f ()
+{
+  int& c = T::x;       // { dg-error ".x. is not a member of .int." }
+  auto l = [] { c++; };        // { dg-error ".c. is not captured" }
+}
+
+void
+g ()
+{
+  f<int>();
+}
diff --git a/gcc/testsuite/g++.dg/template/local11.C 
b/gcc/testsuite/g++.dg/template/local11.C
new file mode 100644
index 000000000000..f75b748caa83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/local11.C
@@ -0,0 +1,18 @@
+// PR c++/123536
+
+int g;
+
+template<class T>
+void
+fn1 ()
+{
+  T c = g;
+  struct A { static void f() { c++; } };
+  A::f();
+}
+
+void
+bar ()
+{
+  fn1<int&> ();
+}

Reply via email to