Skip to content

declarations in metafunctions #809

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
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
145 changes: 57 additions & 88 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
// in our -pure-cpp2 "import std;" simulation mode... if you need this,
// use mixed mode (not -pure-cpp2) and #include all the headers you need
// including this one
//
//
// #include <execution>
#ifdef __cpp_lib_expected
#include <expected>
Expand Down Expand Up @@ -526,7 +526,7 @@ template<typename T>
auto Typeid() -> decltype(auto) {
#ifdef CPP2_NO_RTTI
Type.expects(
!"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
!"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
"'any' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers
);
#else
Expand Down Expand Up @@ -575,7 +575,7 @@ struct {
template<typename T>
[[nodiscard]] auto cpp2_new(auto&& ...args) const -> std::shared_ptr<T> {
// Prefer { } to ( ) as noted for unique.new
//
//
// Note this does mean we don't get the make_shared optimization a lot
// of the time -- we can restore that as soon as make_shared improves to
// allow list initialization. But the make_shared optimization isn't a
Expand Down Expand Up @@ -745,13 +745,22 @@ class out {
//
//-----------------------------------------------------------------------
//
// Workaround <https://github.com/llvm/llvm-project/issues/70556>.
#define CPP2_FORCE_INLINE_LAMBDA_CLANG /* empty */

#if defined(_MSC_VER) && !defined(__clang_major__)
#define CPP2_FORCE_INLINE __forceinline
#define CPP2_FORCE_INLINE_LAMBDA [[msvc::forceinline]]
#define CPP2_FORCE_INLINE __forceinline
#define CPP2_FORCE_INLINE_LAMBDA [[msvc::forceinline]]
#define CPP2_LAMBDA_NO_DISCARD
#else
#define CPP2_FORCE_INLINE __attribute__((always_inline))
#define CPP2_FORCE_INLINE_LAMBDA __attribute__((always_inline))
#define CPP2_FORCE_INLINE __attribute__((always_inline))
#if defined(__clang__)
#define CPP2_FORCE_INLINE_LAMBDA /* empty */
#undef CPP2_FORCE_INLINE_LAMBDA_CLANG
#define CPP2_FORCE_INLINE_LAMBDA_CLANG __attribute__((always_inline))
#else
#define CPP2_FORCE_INLINE_LAMBDA __attribute__((always_inline))
#endif

#if defined(__clang_major__)
// Also check __cplusplus, only to satisfy Clang -pedantic-errors
Expand All @@ -776,85 +785,45 @@ class out {
#endif
#endif

#define CPP2_UFCS_IS_NOTHROW(TEMPKW,...) \
requires { requires requires { std::declval<Obj>().TEMPKW __VA_ARGS__(std::declval<Params>()...); }; \
requires noexcept(std::declval<Obj>().TEMPKW __VA_ARGS__(std::declval<Params>()...)); } \
|| requires { requires !requires { std::declval<Obj>().TEMPKW __VA_ARGS__(std::declval<Params>()...); }; \
requires noexcept(__VA_ARGS__(std::declval<Obj>(), std::declval<Params>()...)); }
#define CPP2_UFCS_IS_NOTHROW_PARAM(TEMPKW,...) /*empty*/
#define CPP2_UFCS_IS_NOTHROW_ARG(TEMPKW,...) CPP2_UFCS_IS_NOTHROW(TEMPKW,__VA_ARGS__)
#if defined(__GNUC__) && !defined(__clang__)
// Workaround <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101043>.
// This isn't the default because Clang also ICEs on an application.
#undef CPP2_UFCS_IS_NOTHROW_PARAM
#undef CPP2_UFCS_IS_NOTHROW_ARG
#define CPP2_UFCS_IS_NOTHROW_PARAM(TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(TEMPKW,__VA_ARGS__)
#define CPP2_UFCS_IS_NOTHROW_ARG(TEMPKW,...) IsNothrow
#if __GNUC__ < 11
#undef CPP2_UFCS_IS_NOTHROW_PARAM
#undef CPP2_UFCS_IS_NOTHROW_ARG
#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/
#define CPP2_UFCS_IS_NOTHROW_ARG(...) false // GCC 10 UFCS is always potentially-throwing.
#endif
#endif

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

#define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&] CPP2_LAMBDA_NO_DISCARD (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 { \
return FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_0(FUNCNAME,PARAM1) \
[&] CPP2_LAMBDA_NO_DISCARD (auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(); }) { \
return CPP2_FORWARD(obj).FUNCNAME(); \
} else { \
return FUNCNAME(CPP2_FORWARD(obj)); \
} \
}(PARAM1)

#define CPP2_UFCS_REMPARENS(...) __VA_ARGS__

#define CPP2_UFCS_TEMPLATE(FUNCNAME,TEMPARGS,PARAM1,...) \
[&] CPP2_LAMBDA_NO_DISCARD (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 { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_TEMPLATE_0(FUNCNAME,TEMPARGS,PARAM1) \
[&] CPP2_LAMBDA_NO_DISCARD (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 { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} \
}(PARAM1)


// But for non-local lambdas [&] is not allowed

#define CPP2_UFCS_NONLOCAL(FUNCNAME,PARAM1,...) \
[] CPP2_LAMBDA_NO_DISCARD (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 { \
return FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)

#define CPP2_UFCS_0_NONLOCAL(FUNCNAME,PARAM1) \
[] CPP2_LAMBDA_NO_DISCARD (auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(); }) { \
return CPP2_FORWARD(obj).FUNCNAME(); \
} else { \
return FUNCNAME(CPP2_FORWARD(obj)); \
} \
}(PARAM1)

#define CPP2_UFCS_TEMPLATE_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1,...) \
[] CPP2_LAMBDA_NO_DISCARD (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)...); \
#define CPP2_UFCS_(LAMBDADEFCAPT,TEMPKW,...) \
[LAMBDADEFCAPT]<typename Obj, typename... Params CPP2_UFCS_IS_NOTHROW_PARAM(TEMPKW,__VA_ARGS__)> \
CPP2_LAMBDA_NO_DISCARD (Obj&& obj, Params&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \
noexcept(CPP2_UFCS_IS_NOTHROW_ARG(TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \
requires requires { CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); } \
|| requires { __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); } { \
if constexpr (requires{ CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }) { \
return CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); \
} else { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
return __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
} \
}(PARAM1, __VA_ARGS__)
}

#define CPP2_UFCS_TEMPLATE_0_NONLOCAL(FUNCNAME,TEMPARGS,PARAM1) \
[] CPP2_LAMBDA_NO_DISCARD (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 { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (CPP2_FORWARD(obj)); \
} \
}(PARAM1)
#define CPP2_UFCS(...) CPP2_UFCS_(&,,__VA_ARGS__)
#define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,template,__VA_ARGS__)
#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,,__VA_ARGS__)
#define CPP2_UFCS_TEMPLATE_NONLOCAL(...) CPP2_UFCS_(,template,__VA_ARGS__)


//-----------------------------------------------------------------------
Expand Down Expand Up @@ -914,7 +883,7 @@ inline auto to_string(std::string const& s) -> std::string const&

template<typename T>
inline auto to_string(T const& sv) -> std::string
requires (std::is_convertible_v<T, std::string_view>
requires (std::is_convertible_v<T, std::string_view>
&& !std::is_convertible_v<T, const char*>)
{
return std::string{sv};
Expand Down Expand Up @@ -1054,17 +1023,17 @@ auto is( X const& ) -> bool {

template< typename C, typename X >
requires (
( std::is_base_of_v<X, C> ||
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
( std::is_base_of_v<X, C> ||
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
) && !std::is_same_v<C,X>)
auto is( X const& x ) -> bool {
return Dynamic_cast<C const*>(&x) != nullptr;
}

template< typename C, typename X >
requires (
( std::is_base_of_v<X, C> ||
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
( std::is_base_of_v<X, C> ||
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
) && !std::is_same_v<C,X>)
auto is( X const* x ) -> bool {
return Dynamic_cast<C const*>(x) != nullptr;
Expand Down Expand Up @@ -1726,7 +1695,7 @@ constexpr auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
// Returns a function object that takes a 'value' of the same type as
// 'flags', and evaluates to true if and only if 'value' has set all of
// the bits set in 'flags'
//
//
//-----------------------------------------------------------------------
//
template <typename T>
Expand Down
43 changes: 43 additions & 0 deletions regression-tests/mixed-bugfix-for-ufcs-non-local.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace ns {

template<bool> struct t { };
constexpr bool f(const t<true>&) { return true; }
constexpr t<true> o{};

} // namespace ns

ns: namespace = {

// Variables.

v0: <_: t<o.f()>> bool == false; // Fails on GCC ([GCC109781][]).

v1: t<o.f()> == t<true>(); // Fails on Clang 12 (lambda in unevaluated context).

v2: bool == o.f();

// Functions.

g: <_: t<o.f()>> () = { } // Fails on GCC ([GCC109781][]).

g: (_: t<o.f()>) = { } // Fails on Clang 12 (lambda in unevaluated context).

g: () pre(o.f()) = { }

h: () -> t<o.f()> = o; // Fails on Clang 12 (lambda in unevaluated context).

// Aliases.

a: <_: t<o.f()>> type == bool; // Fails on GCC ([GCC109781][]).

b: <_: t<o.f()>> _ == false; // Fails on GCC ([GCC109781][]).

c: type == t<o.f()>; // Fails on Clang 12 (lambda in unevaluated context).

d: _ == t<o.f()>(); // Fails on Clang 12 (lambda in unevaluated context).

} // namespace ns

main: () = { }

// [GCC109781]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109781
51 changes: 51 additions & 0 deletions regression-tests/pure2-bugfix-for-ufcs-arguments.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
print_res: (x: i32) -> i32 = {
std::cout << x;
if x == 9 { std::cout << '\n'; }
return x;
}

t: @struct type = {
f: (inout this) -> i32 = print_res(0);
f: (inout this, _) -> i32 = print_res(1);
f: <_> (inout this) -> i32 = print_res(2);
f: <_> (inout this, _) -> i32 = print_res(3);
f: <_, U> (inout this, _, _) -> i32 = print_res(4);
}

f: (_: t) -> i32 = print_res(5);
f: (_: t, _) -> i32 = print_res(6);
f: <_> (_: t) -> i32 = print_res(7);
f: <_> (_: t, _) -> i32 = print_res(8);
f: <_, U> (_: t, _, _) -> i32 = print_res(9);

m: t = ();
n: const t = ();
a: <_, U> _ == n;

_: i32 = m.f();
_: i32 = m.f(0);
_: i32 = m.f<t>();
_: i32 = m.f<t>(0);
_: i32 = m.f<t, t>(0, 0);
_: i32 = n.f();
_: i32 = n.f(0);
_: i32 = n.f<t>();
_: i32 = n.f<t>(0);
_: i32 = n.f<t, t>(0, 0);
_: i32 = a<t, t>.f<t, t>(0, 0);

main: () = {
_ = m.f();
_ = m.f(0);
_ = m.f<t>();
_ = m.f<t>(0);
_ = m.f<t, t>(0, 0);
_ = n.f();
_ = n.f(0);
_ = n.f<t>();
_ = n.f<t>(0);
_ = n.f<t, t>(0, 0);
_ = a<t, t>.f<t, t>(0, 0);

_ = :(a, f) = { _ = a.f(a).f(); };
}
Loading