When does a type become complete?
Asked Answered
R

1

6

Should the following program be rejected? Clang seems to accept it.

template<typename T>
concept c = requires { T::n; };
struct z;
constexpr bool b(auto...) { return c<z>; }
struct z { int n; };
static_assert(not b()); // clang ok, gcc nope, msvc nope

By the time b() is evaluated, template b has been implicitly stamped out, and type z is then complete. However, the expression c<z> within b doesn't depend on any of b's template parameters. So, I guess the question boils down to whether c<z> should be resolved when template b is defined or when it is being instantiated.

Demo

Refutation answered 12/10 at 22:32 Comment(2)
@NathanOliver Not sure where you get the IF-NDR from, as the expression c<struct incomplete> should just resolve to false. (Although, MSVC seems to reject this due to some other bug.)Refutation
Ah, yes, comment retracted.Curson
M
4

This is IFNDR per [temp.res.general]/6.5 because nothing in c<z> is dependent, but its meaning in the specialization differs at the point-of-definition from the point-of-instantiation. The notes below the reference also give non-dependent use of an incomplete type as an example.


Also, while that is not exactly your question: Writing a type trait or concept to check for type completeness or that depends on type completeness doesn't really work. If anywhere in the program (including other translation units) the result differs for a given type, for example because the type has been completed between two checks, then the program will be IFNDR. That's why there is no such trait/concept in the standard library and why all standard library traits/concepts have a precondition for the type to be complete.

Matthaeus answered 12/10 at 23:15 Comment(5)
The template is a red-herring here, you can instead write constexpr bool b() { return c<z>; } and it will be the same. The problem is not different POIs, independent types are looked up at the point of declaration, the problem concept check is done on incomplete type first (that's allowed) but later the same concept check on complete type with different result, so constexpr function returns different values. That is IFNDR as it violates ODR. Compilers are allowed to (and they do) cache concept checks, so in practice it will compile and run but you can see any result.Bonnybonnyclabber
And so, because we can't really reason about IFNDR programs (just like we can't reason about programs that have UB), compilers are in the clear (i.e.: conformant) to either accept or reject this program?Refutation
The result of this MRE came about from testing some CRTP related code. Testing for type-completeness can still be achieved without any ODR violations with the help of a templated function. The trick is to use a defaulted template-parameter with the type or value of a lambda object, so that each invocation of that templated function always results in a new instantiation.Refutation
Actually it's even more basic than that, it all boils down to the fact that constraints must yield the same value for the same types anywhere in the program. "If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required." timsong-cpp.github.io/cppwp/temp.constr.atomic#3Bonnybonnyclabber
@Bonnybonnyclabber If b is not a template, then I see only one program point where the concept-id's satisfication is checked. Where is the second? Though I think eel.is/c++draft/temp#names-9 is a bit unclear about when exactly satisfication is checked.Matthaeus

© 2022 - 2024 — McMap. All rights reserved.