Open JohelEGP opened 10 months ago
This could be considered a sufficiently niche issue for the sake of the general simplicity of Cpp2.
There's similar issues with the mapping of Cpp1 specifiers to Cpp2.
static constexpr
non-member objects: https://github.com/hsutter/cppfront/commit/b589f5d25e5acdbfd94791e23c0ba0f6fdcd59c6#commitcomment-129028816.
static
non-member objects: #212, #322 (https://github.com/hsutter/cppfront/issues?q=is%3Aissue+in%3Atitle+static+is%3Aclosed).static
call operator for function expressions: P1169R4 3.2.2 Can the static-ness of lambdas be implementation-defined?.The last one is for performance.
Cpp2 has no opt-in and Cppfront doesn't lower to it, so we're backwards-compatible with Cpp1.
If static
was the default, we'd lose backwards-compatibility.
An explicit this
parameter (not currently supported) would lower to a generic lambda,
and its function pointers are not member function pointers.
The only truly backwards-compatible way would be to explicitly opt-into static
.
So, for performance, maybe we'd need something like a static
virtual-specifier.
From https://wg21.link/p2996r0#enum-to-string:
template for (constexpr auto e : std::meta::members_of(^E)) {
The non-template for
equivalent in Cpp2 is:
for range do (e) {
A way to support the proposed Cpp1 template for
is with Cpp2 template for
.
Or, alternatively, if we could specify e
as constexpr
somehow.
Although, arguably, the template
keyword upfront is a good indicator of the loop's nature.
Another proposed syntax was for...
, but that doesn't work for reflection.
std::meta::members_of(^E)
is not a pack, but a range to perform heterogeneous splicing.
It also seems that constexpr
with ==
doesn't compose.
Only a subset of the built-in metafunctions generate constexpr
functions.
The built-in metafunctions are
interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print
struct
and print
don't generate functions.constexpr
functions.The ordering metafunctions will be constexpr
because they lower to =default
ed functions.
In commit b589f5d25e5acdbfd94791e23c0ba0f6fdcd59c6, the enum metafunctions had to stop using basic_value
to explicitly opt-into the ==
syntax for constexpr
.
Only interface
and polymorphic_base
are really sensitive to constexpr
.
You can only use virtual dispatch from a base pointer if its function is constexpr
.
The copyable
and value metafunctions never generate constexpr
functions.
Since P2448R2, those can simply be constexpr
functions.
From https://en.cppreference.com/w/cpp/compiler_support:
C++23 feature Paper(s) GCC Clang MSVC Apple Clang Relaxing some constexpr restrictions P2448R2 13 17 (partial)
Or maybe constexpr
should still be considered a contract that requires explicit opt-in.
Just like how operator=
works.
I've considered a @constexpr
type metafunction to mark all member functions as constexpr
.
But that might be too broad (e.g., @enum @constexpr
would mark streaming operator<<
as constexpr
).
I've also thought of giving an argument to copyable
and the value metafunctions to opt-into constexpr
.
I've considered a
@constexpr
type metafunction to mark all member functions asconstexpr
. But that might be too broad (e.g.,@enum @constexpr
would mark streamingoperator<<
asconstexpr
). I've also thought of giving an argument tocopyable
and the value metafunctions to opt-intoconstexpr
.
Maybe @constexpr
should be a variadic meta-metafunction.
It would take the metafunctions to invoke and mark the added members as constexpr
.
The reflection API would have to be extended to allow declaring never-constexpr
member functions.
For example:
.add_runexpr_function
ᵖʳᵒᵛⁱᵗⁱᵒⁿᵃˡ for operator<<
.
Then @constexpr(enum)
wouldn't declare the operator<<
as constexpr
.@constexpr(basic_value)
would declare the functions added by basic_value
as constexpr
.
Then, the enum metafunctions can resume using basic_value
.Maybe
@constexpr
should be a variadic meta-metafunction.
Doesn't seem possible without extending the reflection API. Metafunction template arguments are just strings.
See also #959.
Title:
constexpr
without==
.Description:
In reading P2996R0 Reflection for C++26 2.3 List of Types to List of Sizes, I couldn't help but wonder, how would we declare an uninitialized
constexpr
variable in Cpp2. This isn't a thing in Cpp1, yet. But I think it's been mentioned as a possible relaxation toconstexpr
, including modifiableconstexpr
globals (per TU?).But there's actually a context that already affects Cpp2. An
@interface
with aconstexpr
function (https://compiler-explorer.com/z/aro9K15b1):In Cpp2, the best approximation is (https://cpp2.godbolt.org/z/7Gha4YdK7):
But that errors with:
It's not possible for
X
to declare a constexpr default constructor orf
. We could attempt using@polymorphic_base
. In Cpp1 (https://compiler-explorer.com/z/Y653WPM7j):In Cpp2 (https://cpp2.godbolt.org/z/4MYv5E9vz):
That's a big jump in the API of
X
(a variable can be instantiated, callingf
on isn't a compile-time error, callingstd::terminate()
inf
helps at compile-time but otherwise delays to runtime). And it happens to work because the return type is a merechar
(it could be a more complicated data type, the data-less polymorphic base might need the help of globals).Minimal reproducer (https://cpp2.godbolt.org/z/7Gha4YdK7):
Commands:
```bash cppfront main.cpp2 clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp ```Expected result:
Consideration to
constexpr
without==
. It might be relevant to some users today. Cpp1 language evolution might also make it very relevant.Herb has mentioned limiting global variables to
constexpr
. That could makex: int;
at namespace scope an uninitializedconstexpr
variable. But that wouldn't help with other entities that can be left uninitialized.Actual result and error:
Cpp2 lowered to Cpp1:
```C++ //=== Cpp2 type declarations ==================================================== #include "cpp2util.h" class X; class Y; //=== Cpp2 type definitions and function declarations =========================== class X { public: [[nodiscard]] virtual auto f() const -> char = 0; public: virtual ~X() noexcept; public: X() = default; public: X(X const&) = delete; /* No 'that' constructor, suppress copy */ public: auto operator=(X const&) -> void = delete; }; class Y: public X { public: constexpr explicit Y(); public: [[nodiscard]] constexpr auto f() const -> char override; public: Y(Y const&) = delete; /* No 'that' constructor, suppress copy */ public: auto operator=(Y const&) -> void = delete; }; [[nodiscard]] constexpr auto call_f(X const* x) -> char; auto main() -> int; //=== Cpp2 function definitions ================================================= X::~X() noexcept{} constexpr Y::Y() : X{ } {} [[nodiscard]] constexpr auto Y::f() const -> char { return 'Y'; } [[nodiscard]] constexpr auto call_f(X const* x) -> char { return CPP2_UFCS_0(f, (*cpp2::assert_not_null(x))); } auto main() -> int{ static_assert([_0 = Y()]() -> auto { return call_f(&_0); }() == 'Y'); } ```Output:
```output main.cpp2:11:17: error: static assertion expression is not an integral constant expression 11 | static_assert([_0 = Y()]() -> auto { return call_f(&_0); }() == 'Y'); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp2:11:17: note: non-literal type '(lambda at main.cpp2:11:17)' cannot be used in a constant expression 1 error generated. ```See also:
=
and==
: #742.f: (x) x;
is neverconstexpr
: https://github.com/hsutter/cppfront/discussions/714#discussioncomment-7158721.