Bug 123677 - [16 Regression] ICE: tree check: expected constructor, have ptrmem_cst in cxx_eval_store_expression, at cp/constexpr.cc since r16-3022
Summary: [16 Regression] ICE: tree check: expected constructor, have ptrmem_cst in cxx...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 16.0
: P1 normal
Target Milestone: 16.0
Assignee: Jakub Jelinek
URL:
Keywords: ice-on-valid-code
Depends on:
Blocks: constexpr pmf, ptmf
  Show dependency treegraph
 
Reported: 2026-01-19 03:25 UTC by Jireh Guo
Modified: 2026-01-21 13:34 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2026-01-19 00:00:00


Attachments
gcc16-pr123677.patch (534 bytes, patch)
2026-01-20 16:44 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jireh Guo 2026-01-19 03:25:38 UTC
Compiler Explorer: https://godbolt.org/z/Tx476nWaM
The following c++ code crash gcc trunk with -std=c++20 flag.
But clang and 13,14,15 with checking accept this code.


Code:
```cpp
#include <functional>
class C {
public:
  void func1();
  void func2();
};
std::vector<std::vector<void (C::*)()>> farr = {{&C::func1, &C::func2}};
```

Stack dump:
```
In file included from /cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/stl_iterator.h:78,
                 from /cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/ranges_base.h:39,
                 from /cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/ranges_util.h:34,
                 from /cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/tuple:46,
                 from /cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/functional:71,
                 from <source>:1:
in 'constexpr' expansion of '((std::vector<void (C::*)()>*)<anonymous>)->std::vector<void (C::*)()>::~vector()'
<source>:7:71:   
    7 | std::vector<std::vector<void (C::*)()>> farr = {{&C::func1, &C::func2}};
      |                                                                       ^
in 'constexpr' expansion of 'std::_Destroy<void (C::**)(), void (C::*)()>(((std::vector<void (C::*)()>*)this)->std::vector<void (C::*)()>::std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_M_impl.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl::std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl_data.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl_data::_M_start, ((std::vector<void (C::*)()>*)this)->std::vector<void (C::*)()>::std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_M_impl.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl::std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl_data.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_Vector_impl_data::_M_finish, (* &((std::vector<void (C::*)()>*)this)->std::vector<void (C::*)()>::std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >.std::_Vector_base<void (C::*)(), std::allocator<void (C::*)()> >::_M_get_Tp_allocator()))'
/cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/stl_vector.h:792:15:   
  792 |         std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
      |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  793 |                       _M_get_Tp_allocator());
      |                       ~~~~~~~~~~~~~~~~~~~~~~
in 'constexpr' expansion of 'std::_Destroy<void (C::**)()>(__first, __last)'
/cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/alloc_traits.h:1045:20:   
 1045 |       std::_Destroy(__first, __last);
      |       ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
in 'constexpr' expansion of 'std::destroy_at<void (C::*)()>(std::addressof<void (C::*)()>((* __first)))'
/cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/stl_construct.h:230:19:   
  230 |           std::destroy_at(std::addressof(*__first));
      |           ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/fb/fbb1812a35f407668b457d10_gcc-trunk-20260118/include/c++/16.0.1/bits/stl_construct.h:88:9: internal compiler error: tree check: expected constructor, have ptrmem_cst in cxx_eval_store_expression, at cp/constexpr.cc:8201
   88 |         __location->~_Tp();
      |         ^~~~~~~~~~
0x2950a78 diagnostics::context::diagnostic_impl(rich_location*, diagnostics::metadata const*, diagnostics::option_id, char const*, __va_list_tag (*) [1], diagnostics::kind)
	???:0
0x294583b internal_error(char const*, ...)
	???:0
0xa234d1 tree_check_failed(tree_node const*, char const*, int, char const*, ...)
	???:0
0xb8b38f cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c785 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c2d0 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8ebc4 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c554 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c785 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c2d0 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8b893 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8ebc4 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8ebc4 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8bfd0 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c554 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c785 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c2d0 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c554 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c785 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
0xb8c2d0 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*, value_cat, bool*, bool*, tree_node**)
	???:0
Please submit a full bug report, with preprocessed source (by using -freport-bug).
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
Compiler returned: 1
```
Comment 1 Drea Pinski 2026-01-19 06:08:41 UTC
Reduced testcase:
```
struct b {
  void c();
};
typedef void (b::*a)();
struct bg {
  constexpr bg(a d) {
    auto e = new a(d);
    e->~a();
  }
} bn {
  &b::c
};

```

And yes this is valid C++20 (the original was valid C++17 even).
Comment 2 Jakub Jelinek 2026-01-19 14:39:41 UTC
Started with r16-3022-gbc42128330c0ea70a015b74b655cb8c48b6a8c06
Comment 3 Jakub Jelinek 2026-01-20 16:44:41 UTC
Created attachment 63428 [details]
gcc16-pr123677.patch

Untested fix.
The CLOBBER constexpr store code was assuming that for AGGREGATE_TYPE_P if *valp is non-NULL, it must be a CONSTRUCTOR.  But that is not the case as the testcase shows, it can be also PTRMEM_CST if TYPE_PTRMEMFUNC_P (type).  Rather than adding a special case for that, this patch just verifies it is a CONSTRUCTOR and
overwrites it otherwise.
Comment 4 GCC Commits 2026-01-21 13:31:03 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:9abe0b4f76265a062f1646a0bff1f58b70ae5695

commit r16-6954-g9abe0b4f76265a062f1646a0bff1f58b70ae5695
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Wed Jan 21 14:30:03 2026 +0100

    c++: Fix ICE with constant evaluation of a = {CLOBBER} with ptrmemfn [PR123677]
    
    The following testcase ICEs when we evaluate a = {CLOBBER} stmt.
    The code assumes that if type is an aggregate type and *valp is
    non-NULL, then it must be a CONSTRUCTOR.
    That is usually the case, but there is one exception, *valp can
    be a PTRMEM_CST if TYPE_PTRMEMFUNC_P (type) and in that
    case CONSTRUCTOR_ELTS (*valp) obviously ICEs or misbehaves.
    
    Now, while I could do something like
      if (*valp && (!TYPE_PTRMEMFUNC_P (type) || TREE_CODE (*valp) != PTRMEM_CST))
    just making sure TREE_CODE (*valp) == CONSTRUCTOR seems much easier
    and more readable.
    
    2026-01-21  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/123677
            * constexpr.cc (cxx_eval_store_expression): Only clear
            CONSTRUCTOR_ELTS (*valp) if *valp is CONSTRUCTOR.
    
            * g++.dg/cpp2a/pr123677.C: New test.
Comment 5 Jakub Jelinek 2026-01-21 13:34:24 UTC
Fixed.