On Sun, Sep 15, 2024 at 4:38 AM Robin Savonen Söderholm via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Hi!

Another thing I found that I'd like in concepts is the notion that e.g. a function related to a type can or shall be evaluated at compile time. For example, this:
```c++
template <typename T>
concept has_foo = requires() {
  { T::foo() /*consteval*/ } -> is_foo_like;
};
```
This is because I may want to use the result of `foo` to distinguish what kind of reflections I should be allowed to use on `T`, and what should be considered a compile error.

Unfortunately, "constexpr-friendliness" is a dynamic property, based on runtime-ish values, not on compile-time-ish types.
Consider:
    struct A { static constexpr int foo(int i) { if (i == 42) puts("hi"); return i; } };
    template<class T> concept Fantasy = requires (int i) { A::foo(i) consteval; };  // fantasy syntax for "A::foo(i) is constexpr-friendly"
Now, is `Fantasy<A>` true or false?
The expression `A::foo(41)` is certainly constexpr-friendly.
But the expression `A::foo(42)` is not constexpr-friendly; it's evaluable only at runtime.
So what is the constexpr-friendliness of the expression `A::foo(i)` in a vacuum, without knowing the value of `i`? We don't know.

This is a real problem, but I think it's fundamentally unsolvable given the tools we have designed for ourselves in C++.
In fact there's an even harder problem for Concepts, which goes like this:

    struct B { static constexpr int bar(int i) { return i; } };
    struct C { static consteval int bar(int i) { return i; } };
    template<class T> int runtime(int i) { return T::bar(i); }  // instantiating with B is OK; with C is ill-formed
    template<class T> concept RuntimeFriendly = requires (int i) { T::bar(i); };
    static_assert(RuntimeFriendly<B>);
    static_assert(not RuntimeFriendly<C>);  // we'd like this to succeed, but compilers reject this today

We need a way to distinguish "concepts [or SFINAE] about runtime" from "concepts [or SFINAE] about compile-time." Otherwise, the space of "stuff you can do with a C++ expression" will continue to outpace the space of "stuff you can test with SFINAE/concepts."

I have the vague sense that someone, like Barry, might be working on the latter problem. But maybe that's just because I see people (like Barry) working on fixing up the `consteval` feature and so I blindly assume that this would be part of that whole package.

A solution to the latter problem is certainly required by one or more otherwise-nice papers such as
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3295r0.html (Ben Craig proposes to make std::allocator available on freestanding but as "consteval only," which means all your concepts/traits are going to give the wrong answers regarding runtime, unless we can solve the problem)

TLDR, we started with "runtime stuff" and then "compile-time Concepts/SFINAE that lets us ask questions about runtime stuff."
Then we added "constexpr stuff, i.e. a compile-time runtime"; but we failed to add "compile-time compile-time stuff that lets us ask questions about the compile-time stuff."

Off the top of my head, I suppose one possible syntax for this would be, like,
    template<class T> concept constexpr Fantasy = requires (int i = 42) { A::foo(i); };
    template<class T> concept RuntimeFriendly = requires (int i) T::bar(i); };
    template<class T> concept consteval DontCareAboutRuntime = requires (int i = 42) { A::allocate(i); };
where `DontCareAboutRuntime<std::allocator<int>>` might be `true` on freestanding, even though no runtime function `A::allocate` would exist.
Notice the explicit providing of "default values" for each requires-expression parameter.

HTH,
Arthur