[Bug c++/126361] New: [C++26][reflection] std::meta::reflect_constant throws meta::exception when target class has a copy constructor that reads a member of the source object

painthingy at gmail dot com via Gcc-bugs <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126361

            Bug ID: 126361
           Summary: [C++26][reflection] std::meta::reflect_constant throws
                    meta::exception when target class has a copy
                    constructor that reads a member of the source object
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: painthingy at gmail dot com
  Target Milestone: ---

Version: g++ (Compiler-Explorer-Build-gcc--binutils-2.44) 16.1.0
Target: x86_64-linux-gnu
Command line: -std=c++26 -freflection -Wall -Wextra -O0

Description:
std::meta::reflect_constant on a class-type argument will invoke the class's
copy constructor as part of constant evaluation.
GCC throws std::meta::exception from reflect_constant if the copy constructor's
body reads a member of the source object being copied, even though the exact
same copy constructor works fine when GCC reaches it through a different path
(constant_of/substitute on a static-storage-duration variable template
specialization, Part 2 below).
*std::addressof(x) must be equivalent to x; whether this read is accepted
should not depend on which internal code path led to the copy constructor being
evaluated. Since Part 2 shows GCC can form a reflected constant of this exact
type using this exact copy constructor, the failure in Part 1 isn't a general
restriction on reflecting class types with this kind of constructor — it looks
like a bug specific to how GCC evaluates reads for objects local to the
currently-executing constant expression.
Clang's experimental P2996 branch accepts both programs unmodified:
clang version 21.0.0git (https://github.com/Bloomberg/clang-p2996
7220baffd57ea5b0f8cf59bee494dd5b7cc2b748)
flags: -std=c++26 -fparameter-reflection -freflection-latest -O0
Part 1 — minimal failing case ( https://godbolt.org/z/ddaeT1h66):
```cpp
#include <meta>
#include <memory>
struct Probe {
    int value = 42;
    constexpr Probe() = default;
    constexpr Probe(const Probe& other) {
        value = (std::addressof(other.value) == nullptr) + 42;
        [[maybe_unused]] auto unused = *std::addressof(other.value);  // remove
-> GCC accepts
    }
};
consteval {
    std::meta::reflect_constant(Probe{});
}
int main() {}
```
GCC rejects this with:
error: uncaught exception of type 'std::meta::exception'; 'what()':
'reflect_constant failed'
raised on the std::meta::reflect_constant(Probe{}); line. Deleting the marked
unused line (keeping the addressof(...) == nullptr comparison) makes GCC accept
the program.
Part 2 — same type, same copy constructor, Probe reached via
constant_of/substitute instead of directly — GCC accepts
(https://godbolt.org/z/EKhdvnEY7):
```cpp
#include <meta>
#include <memory>
struct Probe {
    int value;
    constexpr Probe() : value(42) {}
    constexpr Probe(int v) : value(v) {}
    constexpr Probe(const Probe& other) {
        value = (std::addressof(other.value) == nullptr) + 42;
        [[maybe_unused]] auto unused = *std::addressof(other.value);
    }
};
// A variable template whose specializations are Probe objects.
template<auto... Args>
constexpr auto storage = Probe{Args...};
// make a static-storage-duration variable then get the constant reflection
consteval std::meta::info reflect_probe(const Probe& p) {
    return constant_of(
        substitute(^^storage, {std::meta::reflect_constant(p.value)}));
}
template<Probe V>
    requires (V.value == 42)
struct sink {};
consteval {
    auto c = reflect_probe(Probe{42});
    [[maybe_unused]] auto h = substitute(^^sink, {c});  // fine on GCC
}
int main() {}
```
This compiles cleanly under the same flags, with the identical copy constructor
body (including the *std::addressof(...) read).
Expected result: Part 1 should be accepted, matching Clang and matching GCC's
own behavior in Part 2.
Actual result: Part 1 is rejected with the meta::exception shown above.

Other cases:
https://godbolt.org/z/fjbc4M9Tz
https://godbolt.org/z/6vzjj5znY
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.