On Tue, 6 Jan 2026, Peter Damianov wrote:
> When a local class has an NSDMI that references a static local variable
> from the enclosing template function, we ICE during synthesized method
> walking.
>
> The issue is that r14-8981 added maybe_push_to_top_level in
> synthesized_method_walk, which calls push_function_context for
> function-local classes (non-lambdas). This clears current_function_decl.
But push_function_context clears cfun, not current_function_decl?
Ah, and then pop_function_context clobbers current_function_decl with
the saved cfun, which is NULL here. So the problem really seems to be
with pop_function_context assuming current_function_decl must agree with
cfun which isn't true in the C++ front end. (cfun is generally set when
parsing/processing a function, and current_function_decl is for
temporarily entering the scope of a function.)
Perhaps maybe_push/pop_top_level needs to save/restore
current_function_decl separately alongside calling
push/pop_function_context? Or maybe replace the push/pop_function_context
calls with our own saving/restoring of cfun? Wonder what Jason thinks.
> When processing an NSDMI that references a static local variable from
> the enclosing function, tsubst_decl calls enclosing_instantiation_of to
> find the function instantiation.
> That function walks up current_function_decl looking for a function with
> matching source location, but since current_function_decl is NULL, the
> loop exits immediately and hits gcc_unreachable().
>
> This patch makes enclosing_instantiation_of return NULL_TREE when it
> cannot find the enclosing instantiation, and have the caller fall back
> to using tsubst_decl on DECL_CONTEXT to obtain the instantiated function.
>
> gcc/cp/ChangeLog:
>
> PR c++/123354
> * pt.cc (enclosing_instantiation_of): Return NULL_TREE instead
> of crashing when current_function_decl is NULL.
> (tsubst_decl): Handle NULL return from enclosing_instantiation_of
> by using tsubst to find the enclosing function instantiation.
>
> gcc/testsuite/ChangeLog:
>
> PR c++/123354
> * g++.dg/template/pr123354.C: New test.
> ---
> gcc/cp/pt.cc | 10 +++++++++-
> gcc/testsuite/g++.dg/template/pr123354.C | 18 ++++++++++++++++++
> 2 files changed, 27 insertions(+), 1 deletion(-)
> create mode 100644 gcc/testsuite/g++.dg/template/pr123354.C
>
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 759418c344e..4ec9cd71b20 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -15520,7 +15520,9 @@ enclosing_instantiation_of (tree tctx)
> for (; fn; fn = decl_function_context (fn))
> if (DECL_SOURCE_LOCATION (fn) == DECL_SOURCE_LOCATION (tctx))
> return fn;
> - gcc_unreachable ();
> +
> + /* If we can't find the enclosing instantiation, return NULL. */
> + return NULL_TREE;
> }
>
> /* Substitute the ARGS into the T, which is a _DECL. Return the
> @@ -15938,6 +15940,12 @@ tsubst_decl (tree t, tree args, tsubst_flags_t
> complain,
> if (TREE_STATIC (t))
> {
> tree fn = enclosing_instantiation_of (DECL_CONTEXT (t));
> + if (fn == NULL_TREE)
> + /* We're in a context where current_function_decl is cleared
> + (e.g., synthesized_method_walk); use tsubst_decl to find
> + the enclosing function instantiation. */
> + fn = tsubst_decl (DECL_CONTEXT (t), args, complain,
> + use_spec_table);
> if (fn != current_function_decl)
> ctx = fn;
> }
> diff --git a/gcc/testsuite/g++.dg/template/pr123354.C
> b/gcc/testsuite/g++.dg/template/pr123354.C
> new file mode 100644
> index 00000000000..f87956a4c39
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/pr123354.C
> @@ -0,0 +1,18 @@
> +// PR c++/123354
> +// { dg-do compile { target c++11 } }
> +// ICE with static local var referenced in NSDMI of local class
> +
> +template<typename T>
> +void foo() {
> + static constexpr int value = 42;
> + struct s1_t {
> + struct s2_t {
> + int dummy { 0 };
> + char* ptr { static_cast<char*>(::operator new(sizeof(value)))};
> + } s2;
> + } object;
> +}
> +
> +int main() {
> + foo<void>();
> +}
> --
> 2.52.0
>
>