Skip to content

Commit

Permalink
fix(cpp1): handle known UFCS corner cases
Browse files Browse the repository at this point in the history
Provide transparent SFINAE.
Forward `noexcept`.
Accept object with unparenthesized comma like `v<a, b>`.

Incidentially, merge the UFCS macros.
  • Loading branch information
JohelEGP committed Aug 29, 2023
1 parent bf29741 commit c24155b
Show file tree
Hide file tree
Showing 65 changed files with 666 additions and 363 deletions.
121 changes: 35 additions & 86 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,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>
#endif

Expand Down Expand Up @@ -465,7 +465,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 @@ -655,12 +655,19 @@ class out {
//-----------------------------------------------------------------------
//
#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_FORCE_INLINE_LAMBDA_CLANG /* empty */
#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 */
#define CPP2_FORCE_INLINE_LAMBDA_CLANG __attribute__((always_inline))
#else
#define CPP2_FORCE_INLINE_LAMBDA __attribute__((always_inline))
#define CPP2_FORCE_INLINE_LAMBDA_CLANG /* empty */
#endif

#if defined(__clang_major__)
// Also check __cplusplus, only to satisfy Clang -pedantic-errors
Expand All @@ -681,84 +688,26 @@ class out {
#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)...); \
#define CPP2_UFCS_(LAMBDADEFCAPT,TEMPKW,...) \
[LAMBDADEFCAPT] CPP2_LAMBDA_NO_DISCARD (auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \
noexcept(requires { requires requires { CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }; \
requires noexcept(CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...)); } \
|| requires { requires !requires { CPP2_FORWARD(obj).TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }; \
requires noexcept(__VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...)); }) \
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(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)...); \
} else { \
return FUNCNAME CPP2_UFCS_REMPARENS TEMPARGS (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 @@ -827,17 +776,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 @@ -1425,7 +1374,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
35 changes: 35 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,35 @@
template<bool> struct t { };
constexpr bool f(const t<true>&) { return true; }
constexpr t<true> o{};

// Variables.

// _: <V: t<o.f()>> bool = (); // Blocked on #389, [GCC109781][].

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

_: bool = o.f();

// Functions.

// g: <V: t<o.f()>> () = { } // Blocked on [GCC109781][].

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

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

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

// Aliases.

// a: <V: t<o.f()>> type == bool; // Blocked on [GCC109781][].

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

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

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

main: () = { }

// [GCC109781]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109781
46 changes: 46 additions & 0 deletions regression-tests/pure2-bugfix-for-ufcs-arguments.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
print_res: (x: i32) -> i32 = {
std::cout << x;
if (x == 9) { std::cout << '\n'; }
return x;
}
t: type = {
f: (inout this) -> i32 = print_res(0);
f: (inout this, x) -> i32 = print_res(1);
f: <T> (inout this) -> i32 = print_res(2);
f: <T> (inout this, x) -> i32 = print_res(3);
f: <T, U> (inout this, x, y) -> i32 = print_res(4);
}
f: (o: t) -> i32 = print_res(5);
f: (o: t, x) -> i32 = print_res(6);
f: <T> (o: t) -> i32 = print_res(7);
f: <T> (o: t, x) -> i32 = print_res(8);
f: <T, U> (o: t, x, y) -> i32 = print_res(9);
m: t = ();
n: const t = ();
a: <T, 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: () = {
_: 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);

_ = :(a, f) = { _ = a.f(a).f(); };
}
6 changes: 6 additions & 0 deletions regression-tests/pure2-bugfix-for-ufcs-noexcept.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
t: type = {
swap: (inout this, that) = { }
}
main: () = {
// static_assert(noexcept(t().swap(t()))); // Blocked on Clang 12 (lambda in unevaluated context).
}
5 changes: 5 additions & 0 deletions regression-tests/pure2-bugfix-for-ufcs-sfinae.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// f: <T> () -> std::type_identity_t<decltype(T().a())> = { } // Blocked on Clang 12 (lambda in unevaluated context).
B: type = { }
main: () = {
// static_assert(!std::invocable<decltype(:<T> (x: T) -> std::void_t<decltype(f<T>())> = {}), B>); // Blocked on Clang 12 (lambda in unevaluated context).
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
clang version 18.0.0 (https://git.uplinklabs.net/mirrors/llvm-project.git c0abd3814564a568dfc607c216e6407eaa314f46)
clang version 18.0.0 (https://github.com/llvm/llvm-project.git c0abd3814564a568dfc607c216e6407eaa314f46)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /home/johel/root/clang-main/bin
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0123456789
9
0123456789
9
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0123456789
9
0123456789
9
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion regression-tests/test-results/mixed-bounds-check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
std::set_terminate(std::abort);

std::vector v {1, 2, 3, 4, 5, -999};
CPP2_UFCS_0(pop_back, v);
CPP2_UFCS(pop_back)(v);
std::cout << cpp2::assert_in_bounds(std::move(v), 5) << "\n";
}

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ auto add_42_to_subrange(auto& rng, cpp2::in<int> start, cpp2::in<int> end) -> vo
auto add_42_to_subrange(auto& rng, cpp2::in<int> start, cpp2::in<int> end) -> void
{
cpp2::Bounds.expects(cpp2::cmp_less_eq(0,start), "");
cpp2::Bounds.expects(cpp2::cmp_less_eq(end,CPP2_UFCS_0(ssize, rng)), "");
cpp2::Bounds.expects(cpp2::cmp_less_eq(end,CPP2_UFCS(ssize)(rng)), "");

auto count {0};
for (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ auto print_subrange(auto const& rng, cpp2::in<int> start, cpp2::in<int> end) ->

auto print_subrange(auto const& rng, cpp2::in<int> start, cpp2::in<int> end) -> void{
cpp2::Bounds.expects(cpp2::cmp_less_eq(0,start), "");
cpp2::Bounds.expects(cpp2::cmp_less_eq(end,CPP2_UFCS_0(ssize, rng)), "");
cpp2::Bounds.expects(cpp2::cmp_less_eq(end,CPP2_UFCS(ssize)(rng)), "");

auto count {0};
for (
Expand Down
64 changes: 64 additions & 0 deletions regression-tests/test-results/mixed-bugfix-for-ufcs-non-local.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@


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


#include "cpp2util.h"



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

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

// Variables.

// _: <V: t<o.f()>> bool = (); // Blocked on #389, [GCC109781][].

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

#line 11 "mixed-bugfix-for-ufcs-non-local.cpp2"
extern bool auto_11_1;

// Functions.

// g: <V: t<o.f()>> () = { } // Blocked on [GCC109781][].

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

auto g() -> void;

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

// Aliases.

// a: <V: t<o.f()>> type == bool; // Blocked on [GCC109781][].

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

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

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

auto main() -> int;

// [GCC109781]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109781


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


#line 11 "mixed-bugfix-for-ufcs-non-local.cpp2"
bool auto_11_1 {CPP2_UFCS_NONLOCAL(f)(o)};

#line 19 "mixed-bugfix-for-ufcs-non-local.cpp2"
auto g() -> void{
cpp2::Default.expects(CPP2_UFCS(f)(o), "");
#line 19 "mixed-bugfix-for-ufcs-non-local.cpp2"
}

#line 33 "mixed-bugfix-for-ufcs-non-local.cpp2"
auto main() -> int{}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mixed-bugfix-for-ufcs-non-local.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks)

Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void

#line 22 "mixed-captures-in-expressions-and-postconditions.cpp2"
{
cpp2::Default.expects(cpp2::cmp_less_eq(0,where) && cpp2::cmp_less_eq(where,CPP2_UFCS_0(ssize, vec)), "");
auto post_21_5 = cpp2::finally_success([_0 = CPP2_UFCS_0(ssize, vec)]{cpp2::Default.expects(CPP2_UFCS_0(ssize, vec)==_0 + 1, "");} );
cpp2::Default.expects(cpp2::cmp_less_eq(0,where) && cpp2::cmp_less_eq(where,CPP2_UFCS(ssize)(vec)), "");
auto post_21_5 = cpp2::finally_success([_0 = CPP2_UFCS(ssize)(vec)]{cpp2::Default.expects(CPP2_UFCS(ssize)(vec)==_0 + 1, "");} );
#line 23 "mixed-captures-in-expressions-and-postconditions.cpp2"
static_cast<void>(CPP2_UFCS(insert, vec, CPP2_UFCS_0(begin, vec) + where, val));
static_cast<void>(CPP2_UFCS(insert)(vec, CPP2_UFCS(begin)(vec) + where, val));
}

Loading

0 comments on commit c24155b

Please sign in to comment.