From 6cc4ea72bc8b952fffdc54bde244d53d679d7e10 Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Sun, 14 Apr 2024 13:31:22 +0200 Subject: [PATCH 1/5] Extend is() to handle free function predicates This change address to issues: 1. implicit downcasting of arguments, 2. add support for predicates with generic type argument 1. Currently, if is() is checked against predicate the predicate argument can be implicitly casted that can lead to downcasting double to int equal_pi: (x:int) -> bool = { return x == 3; }; d := 3.14; if d is (equal_pi) { // d will be casted to int and will return true // ... } After this change downcasting will not happen no false positive/negative matches will happen. 2. Currently, if is() is checked against predicate with generic type it does not compile. After this change the code will compile and work as expected. --- include/cpp2util.h | 79 ++++++++++++++++++- ...ure2-is-with-free-functions-predicate.cpp2 | 31 ++++++++ .../mixed-inspect-values.cpp.execution | 2 +- ...ith-free-functions-predicate.cpp.execution | 6 ++ ...s-with-free-functions-predicate.cpp.output | 0 ...pure2-is-with-free-functions-predicate.cpp | 68 ++++++++++++++++ ...-with-free-functions-predicate.cpp2.output | 2 + 7 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 regression-tests/pure2-is-with-free-functions-predicate.cpp2 create mode 100644 regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.execution create mode 100644 regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.output create mode 100644 regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp create mode 100644 regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp2.output diff --git a/include/cpp2util.h b/include/cpp2util.h index cffd93ac9..1a560c22a 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -356,6 +356,71 @@ using longdouble = long double; using _schar = signed char; // normally use i8 instead using _uchar = unsigned char; // normally use u8 instead +//----------------------------------------------------------------------- +// +// Helper for concepts +// +//----------------------------------------------------------------------- +// + +template +auto argument_of_helper(Ret(*) (Arg)) -> Arg; + +template +auto argument_of_helper(Ret(F::*) (Arg)) -> Arg; + +template +auto argument_of_helper(Ret(F::*) (Arg) const) -> Arg; + +template +auto argument_of_helper(F) -> CPP2_TYPEOF(argument_of_helper(&F::operator())); + +template +using argument_of_t = CPP2_TYPEOF(argument_of_helper(std::declval())); + +template +using pointee_t = std::iter_value_t; + +//----------------------------------------------------------------------- +// +// Concepts +// +//----------------------------------------------------------------------- +// + +template +concept dereferencable = requires (X x) { *x; }; + +template +concept default_constructible = std::is_default_constructible_v>; + +template +concept bounded_array = std::is_bounded_array_v>; + +template +concept pointer_like = dereferencable && default_constructible && std::equality_comparable + && !bounded_array; + +template< typename From, typename To > +concept brace_initializable_to = requires (From x) { To{x}; }; + +template< typename X, typename C > +concept same_type_as = std::same_as, std::remove_cvref_t>; + +template +concept has_defined_argument = requires { + std::declval>(); +}; + +template +concept covertible_to_argument_of = same_type_as> + || (pointer_like> && brace_initializable_to>>) + || (!pointer_like> && brace_initializable_to>) + ; + +template +concept valid_predicate = (std::predicate && !has_defined_argument) + || (std::predicate && has_defined_argument && covertible_to_argument_of); //----------------------------------------------------------------------- // @@ -1225,12 +1290,9 @@ inline constexpr auto is( auto const& x, auto&& value ) -> bool } // Predicate case - else if constexpr (requires{ bool{ value(x) }; }) { + else if constexpr (valid_predicate) { return value(x); } - else if constexpr (std::is_function_v || requires{ &value.operator(); }) { - return false; - } // Value equality case else if constexpr (requires{ bool{x == value}; }) { @@ -1239,6 +1301,15 @@ inline constexpr auto is( auto const& x, auto&& value ) -> bool return false; } +//----------------------------------------------------------------------- +// +// and "is predicate" for generic function used as predicate +// + +template +inline constexpr auto is( X const& x, bool (*value)(X const&) ) -> bool { + return value(x); +} //------------------------------------------------------------------------------------------------------------- // Built-in as diff --git a/regression-tests/pure2-is-with-free-functions-predicate.cpp2 b/regression-tests/pure2-is-with-free-functions-predicate.cpp2 new file mode 100644 index 000000000..b2e0f145c --- /dev/null +++ b/regression-tests/pure2-is-with-free-functions-predicate.cpp2 @@ -0,0 +1,31 @@ +fun: (v) = { + if v is (pred_i) { + std::cout << "(v)$ is integer bigger than 3" << std::endl; + } + + if v is (pred_d) { + std::cout << "(v)$ is double bigger than 3" << std::endl; + } + + if v is (pred_) { + std::cout << "(v)$ is bigger than 3" << std::endl; + } +} + +main: () -> int = { + fun(3.14); + fun(42); + fun('a'); +} + +pred_i: (x : int ) -> bool = { + return x > 3; +} + +pred_d: (x : double ) -> bool = { + return x > 3; +} + +pred_: (x) -> bool = { + return x > 3; +} diff --git a/regression-tests/test-results/apple-clang-14/mixed-inspect-values.cpp.execution b/regression-tests/test-results/apple-clang-14/mixed-inspect-values.cpp.execution index 245368cfc..e7130e0f8 100644 --- a/regression-tests/test-results/apple-clang-14/mixed-inspect-values.cpp.execution +++ b/regression-tests/test-results/apple-clang-14/mixed-inspect-values.cpp.execution @@ -9,4 +9,4 @@ zero 3 integer -42 xyzzy -3 +(no match) diff --git a/regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.execution b/regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.execution new file mode 100644 index 000000000..c8a07e430 --- /dev/null +++ b/regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.execution @@ -0,0 +1,6 @@ +3.140000 is double bigger than 3 +3.140000 is bigger than 3 +42 is integer bigger than 3 +42 is bigger than 3 +a is integer bigger than 3 +a is bigger than 3 diff --git a/regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.output b/regression-tests/test-results/apple-clang-14/pure2-is-with-free-functions-predicate.cpp.output new file mode 100644 index 000000000..e69de29bb diff --git a/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp b/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp new file mode 100644 index 000000000..63dbefc80 --- /dev/null +++ b/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp @@ -0,0 +1,68 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-is-with-free-functions-predicate.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-is-with-free-functions-predicate.cpp2" +auto fun(auto const& v) -> void; + +#line 15 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto main() -> int; + +#line 21 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_i(cpp2::impl::in x) -> bool; + +#line 25 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_d(cpp2::impl::in x) -> bool; + +#line 29 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_(auto const& x) -> bool; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-is-with-free-functions-predicate.cpp2" +auto fun(auto const& v) -> void{ +#line 2 "pure2-is-with-free-functions-predicate.cpp2" + if (cpp2::impl::is(v, (pred_i))) { + std::cout << (cpp2::to_string(v) + " is integer bigger than 3") << std::endl; + } + + if (cpp2::impl::is(v, (pred_d))) { + std::cout << (cpp2::to_string(v) + " is double bigger than 3") << std::endl; + } + + if (cpp2::impl::is(v, (pred_))) { + std::cout << (cpp2::to_string(v) + " is bigger than 3") << std::endl; + } +} + +#line 15 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto main() -> int{ + fun(3.14); + fun(42); + fun('a'); +} + +#line 21 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_i(cpp2::impl::in x) -> bool{ + return cpp2::impl::cmp_greater(x,3); +} + +#line 25 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_d(cpp2::impl::in x) -> bool{ + return cpp2::impl::cmp_greater(x,3); +} + +#line 29 "pure2-is-with-free-functions-predicate.cpp2" +[[nodiscard]] auto pred_(auto const& x) -> bool{ + return cpp2::impl::cmp_greater(x,3); +} + diff --git a/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp2.output b/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp2.output new file mode 100644 index 000000000..cd9cc4aec --- /dev/null +++ b/regression-tests/test-results/pure2-is-with-free-functions-predicate.cpp2.output @@ -0,0 +1,2 @@ +pure2-is-with-free-functions-predicate.cpp2... ok (all Cpp2, passes safety checks) + From 5d533851dd3d7df4f0cd8ade636be32b79d1d73e Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Sun, 14 Apr 2024 13:31:23 +0200 Subject: [PATCH 2/5] Add test for is with unnamed predicates Adding test for using is with lambdas, and generic lambdas. --- .../pure2-is-with-unnamed-predicates.cpp2 | 19 ++++++++ ...2-is-with-unnamed-predicates.cpp.execution | 6 +++ ...ure2-is-with-unnamed-predicates.cpp.output | 0 .../pure2-is-with-unnamed-predicates.cpp | 44 +++++++++++++++++++ ...re2-is-with-unnamed-predicates.cpp2.output | 2 + 5 files changed, 71 insertions(+) create mode 100644 regression-tests/pure2-is-with-unnamed-predicates.cpp2 create mode 100644 regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.execution create mode 100644 regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.output create mode 100644 regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp create mode 100644 regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp2.output diff --git a/regression-tests/pure2-is-with-unnamed-predicates.cpp2 b/regression-tests/pure2-is-with-unnamed-predicates.cpp2 new file mode 100644 index 000000000..d7253551e --- /dev/null +++ b/regression-tests/pure2-is-with-unnamed-predicates.cpp2 @@ -0,0 +1,19 @@ +fun: (v) = { + if v is (:(x:int) x>3;) { + std::cout << "(v)$ is integer bigger than 3" << std::endl; + } + + if v is (:(x:double) x>3;) { + std::cout << "(v)$ is double bigger than 3" << std::endl; + } + + if v is (:(x) x>3;) { + std::cout << "(v)$ is bigger than 3" << std::endl; + } +} + +main: () -> int = { + fun(3.14); + fun(42); + fun('a'); +} diff --git a/regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.execution b/regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.execution new file mode 100644 index 000000000..c8a07e430 --- /dev/null +++ b/regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.execution @@ -0,0 +1,6 @@ +3.140000 is double bigger than 3 +3.140000 is bigger than 3 +42 is integer bigger than 3 +42 is bigger than 3 +a is integer bigger than 3 +a is bigger than 3 diff --git a/regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.output b/regression-tests/test-results/apple-clang-14/pure2-is-with-unnamed-predicates.cpp.output new file mode 100644 index 000000000..e69de29bb diff --git a/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp b/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp new file mode 100644 index 000000000..5689749ac --- /dev/null +++ b/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp @@ -0,0 +1,44 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-is-with-unnamed-predicates.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-is-with-unnamed-predicates.cpp2" +auto fun(auto const& v) -> void; + +#line 15 "pure2-is-with-unnamed-predicates.cpp2" +[[nodiscard]] auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-is-with-unnamed-predicates.cpp2" +auto fun(auto const& v) -> void{ +#line 2 "pure2-is-with-unnamed-predicates.cpp2" + if (cpp2::impl::is(v, ([](cpp2::impl::in x) mutable -> auto { return cpp2::impl::cmp_greater(x,3); }))) { + std::cout << (cpp2::to_string(v) + " is integer bigger than 3") << std::endl; + } + + if (cpp2::impl::is(v, ([](cpp2::impl::in x) mutable -> auto { return cpp2::impl::cmp_greater(x,3); }))) { + std::cout << (cpp2::to_string(v) + " is double bigger than 3") << std::endl; + } + + if (cpp2::impl::is(v, ([](auto const& x) mutable -> auto { return cpp2::impl::cmp_greater(x,3); }))) { + std::cout << (cpp2::to_string(v) + " is bigger than 3") << std::endl; + } +} + +#line 15 "pure2-is-with-unnamed-predicates.cpp2" +[[nodiscard]] auto main() -> int{ + fun(3.14); + fun(42); + fun('a'); +} + diff --git a/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp2.output b/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp2.output new file mode 100644 index 000000000..6065d9729 --- /dev/null +++ b/regression-tests/test-results/pure2-is-with-unnamed-predicates.cpp2.output @@ -0,0 +1,2 @@ +pure2-is-with-unnamed-predicates.cpp2... ok (all Cpp2, passes safety checks) + From 5d00337f9b9efa8f73dc3f8c64b767ec568d536a Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Sun, 14 Apr 2024 13:31:23 +0200 Subject: [PATCH 3/5] Rewrite is() for variables and templates Adjust to new style of using one function for a match and series of constexpr ifs. There is a possibility to extend is() for matching std::integer_sequence but unfortunatelly clang is not support it (gcc & msvc do). template