https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110122
Bug ID: 110122 Summary: using an aggregate with a member variable with a user defined copy constructor in a class NTTP causes capture and use of the `this` pointer in a generic lambda to produce the following error "-copy constructor- used before its definition" Product: gcc Version: 13.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: waffl3x at protonmail dot com Target Milestone: --- https://godbolt.org/z/c7nnfjzGK struct Foo { constexpr Foo() = default; constexpr Foo(Foo const&) {} }; struct Bar { Foo _; }; template<Bar v = Bar{}> struct Doppelganger { void disguise() { [this]<typename = void>(){ this; }(); } }; void execute() { Doppelganger<>{}.disguise(); } error: 'constexpr Bar::Bar(const Bar&)' used before its definition The bug is present in GCC 13.0 and 13.1 and in the current trunk. This was a dreadful one, I am pretty sure there are 2 different bugs at play here. It took a while to whittle it down to this example, as I was reducing my code, I was shocked to find that removing a innocuous looking copy constructor "fixed" the bug. It seemed like the user declared copy constructor is being produced late. The bug does not manifest when passing `Foo` directly to `Doppelganger`'s non-type template parameter, which I also found odd. Finally, capturing and using the `this` pointer in the body of a generic lambda (the bug does not manifest with a regular lambda) finally causes the bug to pop up. I was not able to find a more reduced version. I found it strange that a generic lambda is required, I would have thought that it being a generic lambda would delay when the copy constructor definition was required, but instead it seems to speed it up. As far as I've observed this seems to be what's happening, as omitting the call to the member function avoids triggering the bug. One final thing to note, declaring the copy constructor as default also avoids the bug, while a user declared default constructor has no effect at all. If anyone manages to reduce it further I would be very interested to see it, I spent a lot of time at it and couldn't seem to find any way to get it to work when passing an object of `Foo` directly to `Doppelganger` despite that it feels like it should be irrelevant. Perhaps it being a member of `Bar` moves up when the definition of the copy constructor is required? As an aside, even when deleting the copy constructor, the diagnostic for the correctly rejected code is still rather unhelpful. It confused me for quite a while as it points to the use of the `this` pointer, or any uses of member variables, almost as if the compiler was mistaking the expression for the non type template parameter, `Bar` in this case. I now realize that use of the `this` pointer probably requires the full type be instantiated from the class template, or I should say thats my guess. My point is, perhaps the compiler should be bailing out earlier, like when the class template is being instantiated with a NTTP that would be invalid. Upon saying that I realized I should test whether it does or not, and I found that it was previously the case, maybe not as early as it could be, but I'm sure the trade off was made for a reason. Unfortunately, this is not the case in GCC 13.1 and GCC trunk. https://godbolt.org/z/8b7Mj91xM I haven't checked if this bug was reported yet, or if it is a bug at all, I will have to look into whether the decision to require copy constructors for NTTP's got overturned or not. You can find that topic being discussed here, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577 , as of a few months ago it seems like NTTP's require copy constructors. Once I have confirmed that I can make another report for this bug if there are no objections, as despite being relevant to this bug, it seems to be slightly different. To recap, I believe there are at least 2 bugs here, the first being the delayed (or perhaps completely omitted) definition of class `Foo`'s user defined copy constructor. The second being a class with an undefined copy constructor is incorrectly accepted as a NTTP, even when an object of that type is initialized. (I have also confirmed that accessing said object does not change that outcome. https://godbolt.org/z/fhsoPGEda ) And finally, there is a 3rd possible bug here involving generic lambdas (as a regular lambda does not trigger the bug), however, despite how problematic generic lambdas seem to be, I am not confident that it is actually responsible for any problems here. It might just be that the time the generic lambda's call operator is instantiated is before the delayed definition of the copy constructor of `Foo`, which isn't because the lambda is instantiating things too early, it's just that the copy constructor is being instantiated too late. I apologize for the long winded write up, I spent about 4 hours trying to troubleshoot my original code (not including yesterday) and another 4 hours trying to reduce it once I confirmed that the bug was with GCC, so I wanted to be thorough with relaying my thoughts of it. Hopefully my musings on it are somewhat helpful rather than not at all.