On Mon, May 18, 2026 at 09:34:23AM -0400, Patrick Palka wrote:
On Fri, 15 May 2026, Marek Polacek wrote:
On Thu, May 14, 2026 at 09:50:52PM -0400, Patrick Palka wrote:
On Thu, 14 May 2026, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16.2?
-- >8 --
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 delaying the processing to instantiating when we know that
maybe_constant_value wouldn't even try to evaluate.
It seems with this patch we still incorrectly reject the case where the outer
local variable is initially type-dependent (not a regression):
int g;
template<class T>
void
fn1 ()
{
T c = g;
auto l = [] { c++; }; // bogus error: 'c' is not captured
l();
}
void
bar ()
{
fn1<int&> ();
}
via process_outer_var_ref, this time called from finish_id_expression_1.
Maybe we can make both testcases work if we instead give process_outer_var_ref
an early exit for when the outer variable is from a template?
Thanks for that testcase; I hadn't considered it.
What do y'all think about this approach, then? I think we want the new
check only after checking !odr_use && decl_constant_var_p.
dg.exp passed thus far.
-- >8 --
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.
PR c++/123536
gcc/cp/ChangeLog:
* 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.
---
gcc/cp/semantics.cc | 5 ++
.../g++.dg/cpp0x/lambda/lambda-const12.C | 48 +++++++++++++++++++
.../g++.dg/cpp0x/lambda/lambda-const13.C | 19 ++++++++
3 files changed, 72 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 6564d9e37a6..201519ef411 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4699,6 +4699,11 @@ process_outer_var_ref (tree decl, tsubst_flags_t
complain, bool odr_use)
return var;
else if (lambda_expr)
{
+ /* Don't complain when DECL is dependent, because it can turn out to
+ be constant (and therefore needing no capture) when instantiating. */
+ if (instantiation_dependent_expression_p (decl))
+ return decl;
+
Presumably we also want to accept the nested class version of
lambda-const13.C:
int g;
template<class T>
void
fn1 ()
{
T c = g;
struct A { static void f() { c++; } };
A::f();
}
void
bar ()
{
fn1<int&> ();
}
So the early exit should be independent of whether we're inside a
lambda.
Oop, yes. I moved the check up and also added VAR_P because
I *think* for PARM_DECLs we don't need/want that i_d_e_p check.
Without it pr57416.C regresses its diagnostic a bit.
Does instantiation_dependent_expression_p work for:
template<class T>
void
fn2 ()
{
int& c = T::static_data_member; // not marked constant at parse time
auto l = [] { c++; };
l();
}
It does seem to work. I discovered a crash on invalid when trying
out this test, thus the mark_use hunk. Tested in lambda-const14.C.
Maybe the early exit test should just be whether the context of the
outer variable is dependent.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16.2?
-- >8 --
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:
* 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.
---
gcc/cp/expr.cc | 2 +
gcc/cp/semantics.cc | 6 +-
.../g++.dg/cpp0x/lambda/lambda-const12.C | 62 +++++++++++++++++++
.../g++.dg/cpp0x/lambda/lambda-const13.C | 19 ++++++
.../g++.dg/cpp0x/lambda/lambda-const14.C | 16 +++++
gcc/testsuite/g++.dg/template/local11.C | 18 ++++++
6 files changed, 122 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const12.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const13.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const14.C
create mode 100644 gcc/testsuite/g++.dg/template/local11.C
diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
index 4d017d530ef..516cea084a6 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 6564d9e37a6..5e64a220e4f 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4697,6 +4697,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 (decl) && instantiation_dependent_expression_p (decl))
+ return decl;