diff --git a/include/cpp2util.h b/include/cpp2util.h index 1ee0a06f76..5fb96e34a3 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -908,6 +908,7 @@ class contract_group { constexpr contract_group (handler h = {}) : reporter{h} { } constexpr auto set_handler(handler h = {}) { reporter = h; } + constexpr auto get_handler() const -> handler { return reporter; } constexpr auto is_active () const -> bool { return reporter != handler{}; } constexpr auto enforce(bool b, CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) @@ -2073,7 +2074,7 @@ auto as(X&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto) if constexpr (std::is_same_v< typename It::type, C >) { if (CPP2_FORWARD(x).index() == It::index) { ptr = &std::get(x); return true; } }; return false; }); - if (!ptr) { Throw( std::bad_variant_access(), "'as' cast failed for 'variant'"); } + type_safety.enforce(ptr, "'as' cast failed for 'variant'"); return cpp2::forward_like(*ptr); } @@ -2083,28 +2084,22 @@ auto as(X&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto) // is Type // -template - requires (std::is_same_v && !std::is_same_v && !std::is_same_v) -constexpr auto is( X const& x ) -> bool - { return x.type() == Typeid(); } - -template - requires (std::is_same_v && std::is_same_v) -constexpr auto is( X const& x ) -> bool - { return !x.has_value(); } - +template X> +constexpr auto is( X const& x ) -> bool{ + if (!x.has_value()) { + return std::is_same_v; + } + return x.type() == Typeid(); +} // is Value // inline constexpr auto is( std::any const& x, auto&& value ) -> bool { // Predicate case - if constexpr (requires{ bool{ value(x) }; }) { + if constexpr (valid_predicate) { return value(x); } - else if constexpr (std::is_function_v || requires{ &value.operator(); }) { - return false; - } // Value case else if constexpr (requires{ bool{ *std::any_cast(&x) == value }; }) { @@ -2118,10 +2113,12 @@ inline constexpr auto is( std::any const& x, auto&& value ) -> bool // as // -template - requires (!std::is_reference_v && std::is_same_v && !std::is_same_v) -constexpr auto as( X const& x ) -> T - { return std::any_cast( x ); } +template X> +constexpr auto as( X && x ) -> decltype(auto) { + constness_like_t* ptr = std::any_cast( &x ); + type_safety.enforce(ptr, "'as' cast failed for 'std::any'"); + return cpp2::forward_like(*ptr); +} //------------------------------------------------------------------------------------------------------------- @@ -2130,16 +2127,16 @@ constexpr auto as( X const& x ) -> T // is Type // -template - requires std::is_same_v> -constexpr auto is( X const& x ) -> bool - { return x.has_value(); } - -template - requires std::is_same_v -constexpr auto is( std::optional const& x ) -> bool - { return !x.has_value(); } - +template X> +constexpr auto is( X const& x ) -> bool { + if (!x.has_value()) { + return std::same_as; + } + if constexpr (requires { static_cast(*x);}) { + return true; + } + return false; +} // is Value // @@ -2147,12 +2144,9 @@ template constexpr auto is( std::optional const& x, auto&& value ) -> bool { // Predicate case - if constexpr (requires{ bool{ value(x) }; }) { + if constexpr (valid_predicate) { return value(x); } - else if constexpr (std::is_function_v || requires{ &value.operator(); }) { - return false; - } // Value case else if constexpr (requires{ bool{ x.value() == value }; }) { @@ -2164,10 +2158,17 @@ constexpr auto is( std::optional const& x, auto&& value ) -> bool // as // -template - requires std::is_same_v> -constexpr auto as( X const& x ) -> decltype(auto) - { return x.value(); } +template X> +constexpr auto as( X&& x ) -> decltype(auto) { + constness_like_t* ptr = nullptr; + if constexpr (requires { static_cast&>(*x); }) { + if (x.has_value()) { + ptr = &static_cast&>(*x); + } + } + type_safety.enforce(ptr, "'as' cast failed for 'std::optional'"); + return cpp2::forward_like(*ptr); +} } // impl diff --git a/regression-tests/mixed-as-with-typesafety.cpp2 b/regression-tests/mixed-as-with-typesafety.cpp2 new file mode 100644 index 0000000000..3ee64879ea --- /dev/null +++ b/regression-tests/mixed-as-with-typesafety.cpp2 @@ -0,0 +1,32 @@ +void throw_error(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM) { + throw std::runtime_error(std::string("Type safety exception: ") + msg); +} + +make_throwable: (inout cg : cpp2::contract_group) -> _ = { + h := cg.get_handler(); + sh := :(pcg : *cpp2::contract_group) = { + pcg*.set_handler(h$); + }; + cg.set_handler(throw_error); + return std::unique_ptr(cg&, sh); +} + +void expect_no_throw(auto&& fun) try { + fun(); +} catch(std::exception const& e) { + std::cout << e.what() << std::endl; +} catch(...) { + std::cout << "Unknown exception!" << std::endl; +} + +main: () = { + o : std::optional = (); + + (_ := make_throwable(cpp2::type_safety)) { + expect_no_throw( :() = { + std::cout << (o$ as int) << std::endl; // that will throw + }); + } + + std::cout << (o as int) << std::endl; // that will terminate +} \ No newline at end of file diff --git a/regression-tests/test-results/apple-clang-14-c++2b/mixed-as-with-typesafety.cpp.execution b/regression-tests/test-results/apple-clang-14-c++2b/mixed-as-with-typesafety.cpp.execution new file mode 100644 index 0000000000..d67092806b --- /dev/null +++ b/regression-tests/test-results/apple-clang-14-c++2b/mixed-as-with-typesafety.cpp.execution @@ -0,0 +1,3 @@ +Type safety exception: 'as' cast failed for 'std::optional' +Type safety violation: 'as' cast failed for 'std::optional' +libc++abi: terminating diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output b/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output index 7ed50a83d1..b6358b20cd 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output +++ b/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output @@ -1,66 +1,66 @@ In file included from pure2-default-arguments.cpp:7: ../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context - 2086 | template + 2086 | template X> | ^~~ ../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ 2047 | return false; | ^ ../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’ - 2086 | template + 2086 | template X> | ^~~~~~~~~~~~ ../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ 2107 | } | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ ../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context - 2086 | template + 2086 | template X> | ^ ../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ 2047 | return false; | ^ ../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’ - 2086 | template + 2086 | template X> | ^ ../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ 2107 | } | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ ../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context - 2087 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) - | ^~~ + 2087 | constexpr auto is( X const& x ) -> bool{ + | ^ ../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ 2047 | return false; | ^ ../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’ - 2087 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) - | ^~~~~~~~~~~~ + 2087 | constexpr auto is( X const& x ) -> bool{ + | ^ ../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ 2107 | } | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ ../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context - 2087 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) - | ^~~~~~ + 2087 | constexpr auto is( X const& x ) -> bool{ + | ^ ../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ 2047 | return false; | ^ ../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’ - 2087 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) - | ^~~~~~~~~~~~ + 2087 | constexpr auto is( X const& x ) -> bool{ + | ^ ../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ 2107 | } | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index b08a69eb07..20b01c3432 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index b08a69eb07..20b01c3432 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | { + 2100 | return value(x); | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | return false; | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/mixed-as-with-typesafety.cpp b/regression-tests/test-results/mixed-as-with-typesafety.cpp new file mode 100644 index 0000000000..0da226f2fb --- /dev/null +++ b/regression-tests/test-results/mixed-as-with-typesafety.cpp @@ -0,0 +1,63 @@ + + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "mixed-as-with-typesafety.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "mixed-as-with-typesafety.cpp2" +void throw_error(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM) { + throw std::runtime_error(std::string("Type safety exception: ") + msg); +} + +#line 5 "mixed-as-with-typesafety.cpp2" +[[nodiscard]] auto make_throwable(cpp2::contract_group& cg) -> auto; +#line 13 "mixed-as-with-typesafety.cpp2" + +void expect_no_throw(auto&& fun) try { + fun(); +} catch(std::exception const& e) { + std::cout << e.what() << std::endl; +} catch(...) { + std::cout << "Unknown exception!" << std::endl; +} + +auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "mixed-as-with-typesafety.cpp2" + +#line 5 "mixed-as-with-typesafety.cpp2" +[[nodiscard]] auto make_throwable(cpp2::contract_group& cg) -> auto{ + auto h {CPP2_UFCS(get_handler)(cg)}; + auto sh {[_0 = cpp2::move(h)](cpp2::contract_group* pcg) mutable -> void{ + CPP2_UFCS(set_handler)((*cpp2::impl::assert_not_null(pcg)), _0); + }}; + CPP2_UFCS(set_handler)(cg, throw_error); + return std::unique_ptr(&cg, cpp2::move(sh)); +} + +#line 22 "mixed-as-with-typesafety.cpp2" +auto main() -> int{ + std::optional o {}; +{ +[[maybe_unused]] auto const& unnamed_param_1{make_throwable(cpp2::type_safety)}; + +#line 25 "mixed-as-with-typesafety.cpp2" + { + expect_no_throw([_0 = o]() mutable -> void{ + std::cout << (cpp2::impl::as_(_0)) << std::endl;// that will throw + }); + } +} + +#line 31 "mixed-as-with-typesafety.cpp2" + std::cout << (cpp2::impl::as_(cpp2::move(o))) << std::endl;// that will terminate +} + diff --git a/regression-tests/test-results/mixed-as-with-typesafety.cpp2.output b/regression-tests/test-results/mixed-as-with-typesafety.cpp2.output new file mode 100644 index 0000000000..df8f873de1 --- /dev/null +++ b/regression-tests/test-results/mixed-as-with-typesafety.cpp2.output @@ -0,0 +1,2 @@ +mixed-as-with-typesafety.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks) +