Skip to content

refactor: improve UFCS error messages #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 79 additions & 10 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,44 +689,81 @@ class out {
#define CPP2_FORCE_INLINE_LAMBDA __attribute__((always_inline))
#endif

#define CPP2_UFCS_REMPARENS(...) __VA_ARGS__
#define CPP2_UFCS_REMPARENS_STR(...) #__VA_ARGS__
#define CPP2_UFCS_ERROR(MEMBER_CALL,NONMEMBER_CALL) R"(
UFCS candidates are invalid:
1) )" CPP2_UFCS_REMPARENS_STR MEMBER_CALL R"(
2) )" CPP2_UFCS_REMPARENS_STR NONMEMBER_CALL R"(
Calling them unconditionally to have them diagnosed.)"

// Note: [&] is because a nested UFCS might be viewed as trying to capture 'this'

#define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); }) { \
return CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); \
} else { \
} else if constexpr (requires{ FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }) { \
return FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.FUNCNAME(__VA_ARGS__)), (FUNCNAME(PARAM1, __VA_ARGS__)))); \
} \
CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); \
FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_0(FUNCNAME,PARAM1) \
[&](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(); }) { \
return CPP2_FORWARD(obj).FUNCNAME(); \
} else { \
} else if constexpr (requires{ FUNCNAME(CPP2_FORWARD(obj)); }) { \
return FUNCNAME(CPP2_FORWARD(obj)); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.FUNCNAME()), (FUNCNAME(PARAM1)))); \
} \
CPP2_FORWARD(obj).FUNCNAME(); \
FUNCNAME(CPP2_FORWARD(obj)); \
} \
}(PARAM1)

#define CPP2_UFCS_REMPARENS(...) __VA_ARGS__

#define CPP2_UFCS_TEMPLATE(FUNCNAME,TEMPARGS,PARAM1,...) \
[&](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); }) { \
return CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); \
} else { \
} else if constexpr (requires{ FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }) { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(__VA_ARGS__)), (FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(PARAM1, __VA_ARGS__)))); \
} \
CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); \
FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_TEMPLATE_0(FUNCNAME,TEMPARGS,PARAM1) \
[&](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); }) { \
return CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); \
} else { \
} else if constexpr (requires{ FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); }) { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS()), (FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(PARAM1)))); \
} \
CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); \
FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} \
}(PARAM1)

Expand All @@ -737,35 +774,67 @@ class out {
[](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); }) { \
return CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); \
} else { \
} else if constexpr (requires{ FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }) { \
return FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.FUNCNAME(__VA_ARGS__)), (FUNCNAME(PARAM1, __VA_ARGS__)))); \
} \
CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); \
FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_0_NONLOCAL(FUNCNAME,PARAM1) \
[](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(); }) { \
return CPP2_FORWARD(obj).FUNCNAME(); \
} else { \
} else if constexpr (requires{ FUNCNAME(CPP2_FORWARD(obj)); }) { \
return FUNCNAME(CPP2_FORWARD(obj)); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.FUNCNAME()), (FUNCNAME(PARAM1)))); \
} \
CPP2_FORWARD(obj).FUNCNAME(); \
FUNCNAME(CPP2_FORWARD(obj)); \
} \
}(PARAM1)

#define CPP2_UFCS_TEMPLATE_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1,...) \
[](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); }) { \
return CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); \
} else { \
} else if constexpr (requires{ FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }) { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(__VA_ARGS__)), (FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(PARAM1, __VA_ARGS__)))); \
} \
CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(params)...); \
FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_TEMPLATE_0_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1) \
[](auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); }) { \
return CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); \
} else { \
} else if constexpr (requires{ FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); }) { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} else { \
{ \
constexpr bool ufcs_invocable = std::is_void_v<CPP2_TYPEOF(obj)>; \
static_assert(ufcs_invocable, \
CPP2_UFCS_ERROR((PARAM1.template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS()), (FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS(PARAM1)))); \
} \
CPP2_FORWARD(obj).template FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (); \
FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} \
}(PARAM1)

Expand Down
13 changes: 13 additions & 0 deletions regression-tests/pure2-bugfix-for-better-ufcs-error-messages.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
vi: std::vector<int> = ();
main: () = {
vi.push_back("oops");
vi.push_back();
vi.push_back<0>("oops");
vi.push_back<0>();
vi.push_back<0, 0>(0, 0);
}
i0: i32 = vi.push_back("oops");
i1: i32 = vi.push_back();
i2: i32 = vi.push_back<0>("oops");
i3: i32 = vi.push_back<0>();
i4: i32 = vi.push_back<0, 0>(0, 0);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

#define CPP2_USE_MODULES Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"



//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-bugfix-for-better-ufcs-error-messages.cpp2"
extern std::vector<int> vi;
auto main() -> int;


#line 9 "pure2-bugfix-for-better-ufcs-error-messages.cpp2"
extern cpp2::i32 i0;
extern cpp2::i32 i1;
extern cpp2::i32 i2;
extern cpp2::i32 i3;
extern cpp2::i32 i4;

//=== Cpp2 function definitions =================================================

#line 1 "pure2-bugfix-for-better-ufcs-error-messages.cpp2"
std::vector<int> vi {};
auto main() -> int{
CPP2_UFCS(push_back, vi, "oops");
CPP2_UFCS_0(push_back, vi);
CPP2_UFCS_TEMPLATE(push_back, (<0>), vi, "oops");
CPP2_UFCS_TEMPLATE_0(push_back, (<0>), vi);
CPP2_UFCS_TEMPLATE(push_back, (<0,0>), vi, 0, 0);
}
cpp2::i32 i0 {CPP2_UFCS_NONLOCAL(push_back, vi, "oops")};
cpp2::i32 i1 {CPP2_UFCS_0_NONLOCAL(push_back, vi)};
cpp2::i32 i2 {CPP2_UFCS_TEMPLATE_NONLOCAL(push_back, (<0>), vi, "oops")};
cpp2::i32 i3 {CPP2_UFCS_TEMPLATE_0_NONLOCAL(push_back, (<0>), vi)};
cpp2::i32 i4 {CPP2_UFCS_TEMPLATE_NONLOCAL(push_back, (<0,0>), vi, 0, 0)};

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-bugfix-for-better-ufcs-error-messages.cpp2... ok (all Cpp2, passes safety checks)