https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125408

            Bug ID: 125408
           Summary: GCC 16.1.1 C++20 ICE with member-pointer NTTP in a
                    constexpr generic lambda expansion
           Product: gcc
           Version: 16.1.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: llh282000500 at gmail dot com
  Target Milestone: ---

Created attachment 64519
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=64519&action=edit
repro cpp code

# GCC 16.1.1 C++20 ICE with member-pointer NTTP in a constexpr generic lambda
expansion

## Summary

GCC 16.1.1 ICEs when compiling C++20 code that uses a pointer-to-member
non-type template parameter inside a `constexpr` generic lambda pack expansion,
specifically in an expression like:

```cpp
using T1 = std::remove_cvref_t<decltype(&(t.*Ptr))>;
```

where:

- `Ptr` is a pointer-to-member NTTP, for example `&Role::id`.
- `t` is a `constexpr` reference to a static object.
- The expression is instantiated inside a generic lambda expanded over
`std::index_sequence`.

This appears to be a GCC C++ front-end crash while substituting or marking use
of the `t.*Ptr` member access expression. Even if the program were invalid, GCC
should emit a normal diagnostic instead of an ICE.

## Environment

```text
g++ (GCC) 16.1.1 20260430
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure
--enable-languages=ada,c,c++,d,fortran,go,lto,m2,objc,obj-c++,rust,cobol
--enable-bootstrap --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib
--mandir=/usr/share/man --infodir=/usr/share/info
--with-bugurl=https://gitlab.archlinux.org/archlinux/packaging/packages/gcc/-/issues
--with-build-config=bootstrap-lto --with-linker-hash-style=gnu
--with-system-zlib --enable-cet=auto --enable-checking=release
--enable-clocale=gnu --enable-default-pie --enable-default-ssp
--enable-gnu-indirect-function --enable-gnu-unique-object
--enable-libstdcxx-backtrace --enable-link-serialization=1
--enable-linker-build-id --enable-lto --enable-multilib --enable-plugin
--enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch
--disable-werror --disable-fixincludes
Thread model: posix
Linux loli 7.0.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Sun, 17 May 2026 17:23:07 +0000
x86_64 GNU/Linux
```

Comparison:

```text
The same reduced testcase compiles successfully with clang++ 22.1.5 -std=c++20.
```

Additional reproduction environment:

```text
The same issue also reproduces in another environment I currently have
available:
Arch Linux / GCC 15.2.1 / x86_64-pc-linux-gnu.
This is only an observation from my existing environments. I have not tested
the full matrix of GCC versions, distributions, or target platforms.
```

## Compile Command

```sh
g++ -std=c++20 -Wall -Wextra -pedantic -c gcc-ice-repro.cpp
```

Command used to generate preprocessed source:

```sh
g++ -std=c++20 -freport-bug -c gcc-ice-repro.cpp
```

The local preprocessed source generated by GCC is:

```text
/tmp/ccrXu0SG.out
```

It should be attached to the bug report.

## Reduced Testcase

```cpp
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

template <auto... Vs>
struct ValueWrap {};

struct Role {
    long id;
    int loliId;
};

template <typename T>
struct StaticObj {
    inline static T obj;
};

template <typename T>
inline constexpr T& getStaticObj() {
    return StaticObj<T>::obj;
}

template <typename T>
inline constexpr auto getStaticObjPtrTuple() {
    auto& [a, b] = getStaticObj<T>();
    auto t = std::tie(a, b);
    constexpr auto f = [](auto&... fs) {
        return std::make_tuple((&fs)...);
    };
    return std::apply(f, t);
}

template <auto Ptr, typename Table>
constexpr std::size_t find_member_index() {
    std::size_t res{};
    constexpr auto& t = getStaticObj<Table>();
    constexpr auto tp = getStaticObjPtrTuple<Table>();

    [&]<std::size_t... Idx>(std::index_sequence<Idx...>) constexpr {
        ([&]() constexpr {
            using T1 = std::remove_cvref_t<decltype(&(t.*Ptr))>;
            using T2 = std::remove_cvref_t<decltype(std::get<Idx>(tp))>;
            if constexpr (std::is_same_v<T1, T2>) {
                if constexpr (&(t.*Ptr) == std::get<Idx>(tp)) {
                    res = Idx;
                }
            }
        }(), ...);
    }(std::make_index_sequence<2>{});
    return res;
}

template <typename Table>
constexpr auto get_primary_key_index() {
    return ValueWrap<find_member_index<&Table::id, Table>(),
                     find_member_index<&Table::loliId, Table>()>{};
}

using Key = decltype(get_primary_key_index<Role>());

int main() {}
```

## Actual Behavior

GCC crashes:

```text
internal compiler error: in mark_use, at cp/expr.cc:190
```

Relevant diagnostic excerpt:

```text
/tmp/gcc-ice-repro-1.cpp:42:56: internal compiler error: in mark_use, at
cp/expr.cc:190
   42 |             using T1 = std::remove_cvref_t<decltype(&(t.*Ptr))>;
      |                                                      ~~^~~~~~
```

The backtrace includes:

```text
build_m_component_ref
build_x_binary_op
tsubst
tsubst_template_args
tsubst_lambda_expr
tsubst_pack_expansion
instantiate_decl
mark_used
```

## Expected Behavior

The compiler should not crash. It should either:

- compile the C++20 program successfully; or
- emit a regular diagnostic if the code is ill-formed.

## Investigation Notes

The issue was originally found in code that computes SQL primary-key member
indices at compile time. The relevant helper receives pointer-to-member NTTPs
and searches a tuple of member addresses.

The original logic does the following:

1. Gets a static object reference with `constexpr auto& t`.
2. Gets a tuple containing addresses of all members.
3. Expands a generic lambda over `std::index_sequence`.
4. Uses `decltype(&(t.*Ptr))` and `decltype(std::get<Idx>(tp))` to skip
comparisons between different pointer types.
5. Compares `&(t.*Ptr)` with the tuple element in an `if constexpr`.

Reduction shows that the crash is not related to the project dependencies,
SQLite, logging code, or macros. The relevant trigger is the combination of:

- a pointer-to-member `auto` NTTP;
- member access through `t.*Ptr`, where `t` is a `constexpr` reference to a
static object;
- instantiation through a generic lambda pack expansion;
- `decltype(&(t.*Ptr))` participating in template substitution.

GCC crashes in `mark_use` while substituting or processing this expression.
Clang 22.1.5 accepts the reduced testcase, which suggests this is a GCC
front-end bug rather than an ordinary user-code diagnostic.

Reply via email to