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
> 
> 

Reply via email to