cppfront
cppfront copied to clipboard
[BUG] UFCS fails during constant evaluation only
Title: UFCS fails during constant evaluation only.
Description:
Given a parameter named after the function being called, UFCS will fail during constant evaluation only in the case UFCS would call a member function.
Minimal reproducer (https://cpp2.godbolt.org/z/jKWG4boaq, https://compiler-explorer.com/z/zeqW6MEPK):
t: @struct type = {
f: (this) -> int == 0;
}
g: (f) = {
[[assert: t().f() == 0]] // OK.
static_assert(t().f() == 0); // Error: Argument `f` isn't "`constexpr`".
}
main: () = {
g(0);
// g(std::identity()); // Might be OK since P2280R4 (unimplemented).
}
Commands:
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: A well-formed program that calls the member function.
Actual result and error:
Cpp2 lowered to Cpp1:
//=== Cpp2 type declarations ====================================================
#include "cpp2util.h"
class t;
//=== Cpp2 type definitions and function declarations ===========================
class t {
public: [[nodiscard]] constexpr auto f() const& -> int;
};
auto g(auto const& f) -> void;
auto main() -> int;
//=== Cpp2 function definitions =================================================
[[nodiscard]] constexpr auto t::f() const& -> int { return 0; }
auto g(auto const& f) -> void{
cpp2::Default.expects(CPP2_UFCS_0(f, t()) == 0, "");// OK.
static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
}
auto main() -> int{
g(0);
// g(std::identity()); // Might be OK since P2280R4 (unimplemented).
}
Output:
main.cpp2:7:17: error: static assertion expression is not an integral constant expression
7 | static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
| ^~~~~~~~~~~~~~~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:790:38: note: expanded from macro 'CPP2_UFCS_0'
790 | #define CPP2_UFCS_0(FUNCNAME,PARAM1) \
| ^
main.cpp2:11:3: note: in instantiation of function template specialization 'g<int>' requested here
11 | g(0);
| ^
main.cpp2:7:17: note: function parameter 'f' with unknown value cannot be used in a constant expression
7 | static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
| ^
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:791:2: note: expanded from macro 'CPP2_UFCS_0'
791 | [&] CPP2_LAMBDA_NO_DISCARD (auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
| ^
main.cpp2:5:20: note: declared here
5 | auto g(auto const& f) -> void{
| ^
1 error generated.
My reading of https://eel.is/c++draft/expr.const#8
tells me that g(0); will continue failing
whereas g(std::identity()); would be well-formed.
#506's resolution for #550 adds quite some complexity to Cppfront. The same can be done here to resolve this #788. The alternative is to just embrace #550 and #788 as limitations of Cppfront, and require users to not name such variables like the function in a UFCS call.
I have potential solutions in mind for this one. But they also bring complexity to the code of Cppfront.
Maybe trial constant evaluation can be leveraged for a seamless user experience.
An alternative would be to disable UFCS
when in a constant expression (so Cppfront has to know about those)
and f isn't part of the constant expression (determined by lexical name lookup).
// g(std::identity()); // Might be OK since P2280R4 (unimplemented).
Yes, it works: https://cpp2.godbolt.org/z/1vrf534qG.