From 1cdb8d295eee5faf47f4230e0c3b6a54462a81ce Mon Sep 17 00:00:00 2001 From: Werner Henze <34543625+beinhaerter@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:50:31 +0100 Subject: [PATCH] More checks for non-compilable code, plus fix for span (#1180) --- include/gsl/span | 7 + tests/algorithm_tests.cpp | 2 +- tests/byte_tests.cpp | 6 +- tests/notnull_tests.cpp | 156 +++++++--- tests/owner_tests.cpp | 25 +- tests/pointers_tests.cpp | 33 +-- tests/span_tests.cpp | 503 +++++++++++++-------------------- tests/strict_notnull_tests.cpp | 119 +++++--- 8 files changed, 439 insertions(+), 412 deletions(-) diff --git a/include/gsl/span b/include/gsl/span index a01b687..f307e6e 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -582,6 +582,8 @@ public: template constexpr span first() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "first() cannot extract more elements from a span than it contains."); Expects(Count <= size()); return span{data(), Count}; } @@ -592,6 +594,8 @@ public: // clang-format on constexpr span last() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "last() cannot extract more elements from a span than it contains."); Expects(Count <= size()); return span{data() + (size() - Count), Count}; } @@ -603,6 +607,9 @@ public: constexpr auto subspan() const noexcept -> typename details::calculate_subspan_type::type { + static_assert(Extent == dynamic_extent || (Extent >= Offset && (Count == dynamic_extent || + Count <= Extent - Offset)), + "subspan() cannot extract more elements from a span than it contains."); Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset))); using type = typename details::calculate_subspan_type::type; diff --git a/tests/algorithm_tests.cpp b/tests/algorithm_tests.cpp index e369be0..b4a4f81 100644 --- a/tests/algorithm_tests.cpp +++ b/tests/algorithm_tests.cpp @@ -188,7 +188,7 @@ TEST(algorithm_tests, incompatible_type) span src_span_dyn(src); span src_span_static(src); span dst_span_dyn(dst); - span dst_span_static(dst); + span dst_span_static(gsl::make_span(dst)); // every line should produce a compilation error copy(src_span_dyn, dst_span_dyn); diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index ca94a9b..77a947a 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -67,6 +67,8 @@ TEST(byte_tests, construction) to_byte(char{}); to_byte(3); to_byte(3u); + to_byte<-1>(); + to_byte<256u>(); #endif } @@ -174,7 +176,3 @@ static constexpr bool static_assert(!ToIntegerCompilesFor, "!ToIntegerCompilesFor"); } // namespace - -#ifdef CONFIRM_COMPILATION_ERRORS -copy(src_span_static, dst_span_static); -#endif diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index b6f8b37..56b549b 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -18,17 +18,25 @@ #include // for not_null, operator<, operator<=, operator> -#include // for addressof -#include // for uint16_t -#include // for shared_ptr, make_shared, operator<, opera... -#include // for operator<<, ostringstream, basic_ostream:... -#include // for basic_string, operator==, string, operator<< -#include // for type_info -#include // for variant, monostate, get +#include // for addressof +#include // for uint16_t +#include // for shared_ptr, make_shared, operator<, opera... +#include // for operator<<, ostringstream, basic_ostream:... +#include // for basic_string, operator==, string, operator<< +#include // for declval +#include // for type_info +#include // for variant, monostate, get #include "deathTestCommon.h" using namespace gsl; +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + struct MyBase { }; @@ -141,16 +149,39 @@ bool helper_const(not_null p) { return *p == 12; } int* return_pointer() { return nullptr; } } // namespace +template +static constexpr bool CtorCompilesFor_A = false; +template +static constexpr bool + CtorCompilesFor_A{std::declval()})>> = true; + +template +static constexpr bool CtorCompilesFor_B = false; +template +static constexpr bool CtorCompilesFor_B{N})>> = true; + +template +static constexpr bool DefaultCtorCompilesFor = false; +template +static constexpr bool DefaultCtorCompilesFor{})>> = true; + +template +static constexpr bool CtorCompilesFor_C = false; +template +static constexpr bool + CtorCompilesFor_C{std::declval>()})>> = + true; + TEST(notnull_tests, TestNotNullConstructors) { { -#ifdef CONFIRM_COMPILATION_ERRORS - not_null p = nullptr; // yay...does not compile! - not_null*> p1 = 0; // yay...does not compile! - not_null p2; // yay...does not compile! - std::unique_ptr up = std::make_unique(120); - not_null p3 = up; + static_assert(CtorCompilesFor_A, "CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_A, "!CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_B, "!CtorCompilesFor_B"); + static_assert(!DefaultCtorCompilesFor, "!DefaultCtorCompilesFor"); + static_assert(!CtorCompilesFor_C, "CtorCompilesFor_C"); +#ifdef CONFIRM_COMPILATION_ERRORS // Forbid non-nullptr assignable types not_null> f(std::vector{1}); not_null z(10); @@ -276,6 +307,27 @@ TEST(notnull_tests, TestNotNullostream) ostream_helper("string"); } +template +static constexpr bool AssignmentCompilesFor = false; +template +static constexpr bool + AssignmentCompilesFor&>().operator=( + std::declval&>()))>> = true; + +template +static constexpr bool SCastCompilesFor = false; +template +static constexpr bool + SCastCompilesFor(std::declval&>()))>> = + true; + +template +static constexpr bool RCastCompilesFor = false; +template +static constexpr bool RCastCompilesFor< + U, V, void_t(std::declval&>()))>> = true; + TEST(notnull_tests, TestNotNullCasting) { MyBase base; @@ -288,15 +340,30 @@ TEST(notnull_tests, TestNotNullCasting) q = p; // allowed with heterogeneous copy ctor EXPECT_TRUE(q == p); -#ifdef CONFIRM_COMPILATION_ERRORS - q = u; // no viable conversion possible between MyBase* and Unrelated* - p = q; // not possible to implicitly convert MyBase* to MyDerived* + static_assert(AssignmentCompilesFor, + "AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + + static_assert(SCastCompilesFor, "SCastCompilesFor"); + static_assert(SCastCompilesFor, "SCastCompilesFor"); + static_assert(!SCastCompilesFor, "!SCastCompilesFor"); + static_assert(!SCastCompilesFor, + "!SCastCompilesFor"); + static_assert(!RCastCompilesFor, + "!SCastCompilesFor"); + static_assert(!RCastCompilesFor, + "!SCastCompilesFor"); - not_null r = p; - not_null s = reinterpret_cast(p); -#endif not_null t(reinterpret_cast(p.get())); EXPECT_TRUE(reinterpret_cast(p.get()) == reinterpret_cast(t.get())); + + (void) static_cast(p); + (void) static_cast(p); } TEST(notnull_tests, TestNotNullAssignment) @@ -438,6 +505,18 @@ TEST(notnull_tests, TestNotNullCustomPtrComparison) #if defined(__cplusplus) && (__cplusplus >= 201703L) +template +static constexpr bool TypeDeductionCtorCompilesFor = false; +template +static constexpr bool + TypeDeductionCtorCompilesFor()})>> = true; + +template +static constexpr bool TypeDeductionHelperCompilesFor = false; +template +static constexpr bool + TypeDeductionHelperCompilesFor()}))>> = true; + TEST(notnull_tests, TestNotNullConstructorTypeDeduction) { { @@ -454,9 +533,9 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction) const int i = 42; not_null x{&i}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(not_null{&i}); -#endif + static_assert(TypeDeductionHelperCompilesFor, "TypeDeductionHelperCompilesFor"); + static_assert(!TypeDeductionHelperCompilesFor, + "!TypeDeductionHelperCompilesFor"); helper_const(not_null{&i}); EXPECT_TRUE(*x == 42); @@ -478,9 +557,6 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction) const int* p = &i; not_null x{p}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(not_null{p}); -#endif helper_const(not_null{p}); EXPECT_TRUE(*x == 42); @@ -515,12 +591,15 @@ TEST(notnull_tests, TestNotNullConstructorTypeDeduction) EXPECT_DEATH(helper_const(not_null{p}), expected); } -#ifdef CONFIRM_COMPILATION_ERRORS - { - not_null x{nullptr}; - helper(not_null{nullptr}); - helper_const(not_null{nullptr}); - } + static_assert(TypeDeductionCtorCompilesFor, "TypeDeductionCtorCompilesFor"); +#if defined(_MSC_VER) && !defined(__clang__) + // Fails on gcc, clang, xcode, VS clang with + // "error : no type named 'type' in 'std::enable_if'; 'enable_if' cannot be used to + // disable this declaration" + static_assert(!TypeDeductionCtorCompilesFor, + "!TypeDeductionCtorCompilesFor"); + static_assert(!TypeDeductionHelperCompilesFor, + "!TypeDeductionHelperCompilesFor"); #endif } @@ -536,6 +615,11 @@ TEST(notnull_tests, TestVariantEmplace) } #endif // #if defined(__cplusplus) && (__cplusplus >= 201703L) +template +static constexpr bool HelperCompilesFor = false; +template +static constexpr bool HelperCompilesFor()))>> = true; + TEST(notnull_tests, TestMakeNotNull) { { @@ -552,9 +636,8 @@ TEST(notnull_tests, TestMakeNotNull) const int i = 42; const auto x = make_not_null(&i); -#ifdef CONFIRM_COMPILATION_ERRORS - helper(make_not_null(&i)); -#endif + static_assert(HelperCompilesFor>, + "HelperCompilesFor>"); helper_const(make_not_null(&i)); EXPECT_TRUE(*x == 42); @@ -576,9 +659,8 @@ TEST(notnull_tests, TestMakeNotNull) const int* p = &i; const auto x = make_not_null(p); -#ifdef CONFIRM_COMPILATION_ERRORS - helper(make_not_null(p)); -#endif + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); helper_const(make_not_null(p)); EXPECT_TRUE(*x == 42); diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index 87c7cec..3b7a38b 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -17,6 +17,7 @@ #include #include // for owner +#include // for declval using namespace gsl; @@ -32,12 +33,18 @@ TEST(owner_tests, basic_test) delete p; } -TEST(owner_tests, check_pointer_constraint) -{ -#ifdef CONFIRM_COMPILATION_ERRORS - { - owner integerTest = 10; - owner> sharedPtrTest(new int(10)); - } -#endif -} +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +template +static constexpr bool OwnerCompilesFor = false; +template +static constexpr bool OwnerCompilesFor{})>> = + true; +static_assert(OwnerCompilesFor, "OwnerCompilesFor"); +static_assert(!OwnerCompilesFor, "!OwnerCompilesFor"); +static_assert(!OwnerCompilesFor>, "!OwnerCompilesFor>"); diff --git a/tests/pointers_tests.cpp b/tests/pointers_tests.cpp index 7757c27..1c15712 100644 --- a/tests/pointers_tests.cpp +++ b/tests/pointers_tests.cpp @@ -6,6 +6,13 @@ #include #include +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + namespace { // Custom pointer type that can be used for gsl::not_null, but for which these cannot be swapped. @@ -22,6 +29,13 @@ struct NotMoveAssignableCustomPtr int dummy{}; // Without this clang warns, that NotMoveAssignableCustomPtr() is unneeded }; +template +static constexpr bool SwapCompilesFor = false; +template +static constexpr bool + SwapCompilesFor(std::declval&>(), + std::declval&>()))>> = true; + TEST(pointers_test, swap) { // taken from gh-1129: @@ -69,22 +83,9 @@ TEST(pointers_test, swap) EXPECT_TRUE(*a == 1); EXPECT_TRUE(*b == 0); } + + static_assert(!SwapCompilesFor, + "!SwapCompilesFor"); } -#if __cplusplus >= 201703l -using std::void_t; -#else // __cplusplus >= 201703l -template -using void_t = void; -#endif // __cplusplus < 201703l - -template -static constexpr bool SwapCompilesFor = false; -template -static constexpr bool - SwapCompilesFor(std::declval&>(), - std::declval&>()))>> = true; -static_assert(!SwapCompilesFor, - "!SwapCompilesFor"); - } // namespace diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index d86458b..01fe26d 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -48,6 +48,13 @@ using namespace gsl; +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + namespace { @@ -62,8 +69,7 @@ struct AddressOverloaded #if (__cplusplus > 201402L) [[maybe_unused]] #endif - AddressOverloaded - operator&() const + AddressOverloaded operator&() const { return {}; } @@ -216,6 +222,12 @@ TEST(span_test, from_pointer_length_constructor) TEST(span_test, from_pointer_pointer_construction) { + // const auto terminateHandler = std::set_terminate([] { + // std::cerr << "Expected Death. from_pointer_pointer_construction"; + // std::abort(); + // }); + // const auto expected = GetExpectedDeathString(terminateHandler); + int arr[4] = {1, 2, 3, 4}; { @@ -245,19 +257,11 @@ TEST(span_test, from_pointer_pointer_construction) EXPECT_TRUE(s.data() == &arr[0]); } - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ + //{ // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this would be UB // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; // EXPECT_DEATH(workaround_macro(), expected); //} - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ - // int* p = nullptr; - // auto workaround_macro = [&]() { span s{&arr[0], p}; }; - // EXPECT_DEATH(workaround_macro(), expected); - //} - { int* p = nullptr; span s{p, p}; @@ -271,19 +275,21 @@ TEST(span_test, from_pointer_pointer_construction) EXPECT_TRUE(s.size() == 0); EXPECT_TRUE(s.data() == nullptr); } - - // this will fail the std::distance() precondition, which asserts on MSVC debug builds - //{ - // int* p = nullptr; - // auto workaround_macro = [&]() { span s{&arr[0], p}; }; - // EXPECT_DEATH(workaround_macro(), expected); - //} } +template +static constexpr bool CtorCompilesFor = false; +template +static constexpr bool CtorCompilesFor()})>> = true; + TEST(span_test, from_array_constructor) { int arr[5] = {1, 2, 3, 4, 5}; + static_assert(!CtorCompilesFor, int[5]>, "!CtorCompilesFor, int[5]>"); + static_assert(!CtorCompilesFor, int[5]>, "!CtorCompilesFor, int[5]>"); + static_assert(!CtorCompilesFor, int[2][3]>, "!CtorCompilesFor, int[2][3]>"); + { const span s{arr}; EXPECT_TRUE(s.size() == 5); @@ -298,70 +304,28 @@ TEST(span_test, from_array_constructor) int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s{arr}; - } + static_assert(!CtorCompilesFor, int[2][3]>, + "!CtorCompilesFor, int[2][3]>"); + static_assert(!CtorCompilesFor, int[2][3]>, + "!CtorCompilesFor, int[2][3]>"); - { - span s{arr}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == &arr[0]); - } - - { - span s{arr2d}; - EXPECT_TRUE(s.size() == 6); - EXPECT_TRUE(s.data() == &arr2d[0][0]); - EXPECT_TRUE(s[0] == 1); - EXPECT_TRUE(s[5] == 6); - } - - { - span s{arr2d}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == &arr2d[0][0]); - } - - { - span s{arr2d}; - } -#endif { const span s{std::addressof(arr2d[0]), 1}; EXPECT_TRUE(s.size() == 1); EXPECT_TRUE(s.data() == std::addressof(arr2d[0])); } - int arr3d[2][3][2] = { { {1, 2}, {3, 4}, {5, 6} }, { {7, 8}, {9, 10}, {11, 12} } }; + int arr3d[2][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}}; -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s{arr3d}; - EXPECT_TRUE(s.size() == 12); - EXPECT_TRUE(s.data() == &arr3d[0][0][0]); - EXPECT_TRUE(s[0] == 1); - EXPECT_TRUE(s[11] == 12); - } + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); - { - span s{arr3d}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == &arr3d[0][0][0]); - } - - { - span s{arr3d}; - } - - { - span s{arr3d}; - EXPECT_TRUE(s.size() == 12); - EXPECT_TRUE(s.data() == &arr3d[0][0][0]); - EXPECT_TRUE(s[0] == 1); - EXPECT_TRUE(s[5] == 6); - } -#endif { const span s{std::addressof(arr3d[0]), 1}; EXPECT_TRUE(s.size() == 1); @@ -389,6 +353,13 @@ TEST(span_test, from_dynamic_array_constructor) delete[] arr; } +template +static constexpr bool ConversionCompilesFor = false; +template +static constexpr bool + ConversionCompilesFor()(std::declval()))>> = + true; + TEST(span_test, from_std_array_constructor) { std::array arr = {1, 2, 3, 4}; @@ -428,43 +399,31 @@ TEST(span_test, from_std_array_constructor) EXPECT_TRUE(ao_arr.data() == fs.data()); } -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s{arr}; - EXPECT_TRUE(s.size() == 2); - EXPECT_TRUE(s.data() == arr.data()); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); - span cs{arr}; - EXPECT_TRUE(cs.size() == 2); - EXPECT_TRUE(cs.data() == arr.data()); - } + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); - { - span s{arr}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == arr.data()); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); - span cs{arr}; - EXPECT_TRUE(cs.size() == 0); - EXPECT_TRUE(cs.data() == arr.data()); - } - - { - span s{arr}; - } - - { - auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span s) { static_cast(s); }; - // try to take a temporary std::array - take_a_span(get_an_array()); - } +#if !defined(_MSC_VER) || (_MSC_VER > 1942) || (__cplusplus >= 201703L) + // Fails on "Visual Studio 16 2019/Visual Studio 17 2022, windows-2019/2022, Debug/Release, 14". + static_assert(!ConversionCompilesFor, std::array>, + "!ConversionCompilesFor, std::array>"); #endif { auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span s) { static_cast(s); }; + auto take_a_span = [](span) {}; // try to take a temporary std::array + static_assert(ConversionCompilesFor, std::array>, + "ConversionCompilesFor, std::array>"); take_a_span(get_an_array()); } } @@ -493,23 +452,12 @@ TEST(span_test, from_const_std_array_constructor) EXPECT_TRUE(s.data() == ao_arr.data()); } -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s{arr}; - EXPECT_TRUE(s.size() == 2); - EXPECT_TRUE(s.data() == arr.data()); - } - - { - span s{arr}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == arr.data()); - } - - { - span s{arr}; - } -#endif + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); { auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; @@ -535,27 +483,14 @@ TEST(span_test, from_std_array_const_constructor) EXPECT_TRUE(s.data() == arr.data()); } -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s{arr}; - EXPECT_TRUE(s.size() == 2); - EXPECT_TRUE(s.data() == arr.data()); - } - - { - span s{arr}; - EXPECT_TRUE(s.size() == 0); - EXPECT_TRUE(s.data() == arr.data()); - } - - { - span s{arr}; - } - - { - span s{arr}; - } -#endif + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); } TEST(span_test, from_container_constructor) @@ -577,32 +512,28 @@ TEST(span_test, from_container_constructor) const std::string cstr = "hello"; { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{str}; - EXPECT_TRUE(s.size() == str.size()); - EXPECT_TRUE(s.data() == str.data())); -#endif - span cs{str}; - EXPECT_TRUE(cs.size() == str.size()); - EXPECT_TRUE(cs.data() == str.data()); + static_assert(CtorCompilesFor, std::string&> == (__cplusplus >= 201703L), + "CtorCompilesFor, std::string&> == (__cplusplus >= 201703L)"); + + span cs{str}; + EXPECT_TRUE(cs.size() == str.size()); + EXPECT_TRUE(cs.data() == str.data()); } { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{cstr}; -#endif + static_assert(!CtorCompilesFor, const std::string&>, + "!CtorCompilesFor, const std::string&>"); + span cs{cstr}; EXPECT_TRUE(cs.size() == cstr.size()); EXPECT_TRUE(cs.data() == cstr.data()); } - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](span s) { static_cast(s); }; - use_span(get_temp_vector()); -#endif - } +#if !defined(_MSC_VER) || (_MSC_VER > 1942) || (__cplusplus >= 201703L) + // Fails on "Visual Studio 16 2019/Visual Studio 17 2022, windows-2019/2022, Debug/Release, 14". + static_assert(!ConversionCompilesFor, std::vector>, + "!ConversionCompilesFor, std::vector>"); +#endif // !defined(_MSC_VER) || (_MSC_VER > 1942) || (__cplusplus >= 201703L) { auto get_temp_vector = []() -> std::vector { return {}; }; @@ -610,13 +541,8 @@ TEST(span_test, from_container_constructor) use_span(get_temp_vector()); } - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span s) { static_cast(s); }; - use_span(get_temp_string()); -#endif - } + static_assert(!ConversionCompilesFor, std::string>, + "!ConversionCompilesFor, std::string>"); { auto get_temp_string = []() -> std::string { return {}; }; @@ -624,13 +550,10 @@ TEST(span_test, from_container_constructor) use_span(get_temp_string()); } - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_vector = []() -> const std::vector { return {}; }; - auto use_span = [](span s) { static_cast(s); }; - use_span(get_temp_vector()); -#endif - } + static_assert(!ConversionCompilesFor, const std::vector>, + "!ConversionCompilesFor, const std::vector>"); + static_assert(!ConversionCompilesFor, const std::string>, + "!ConversionCompilesFor, const std::string>"); { auto get_temp_string = []() -> const std::string { return {}; }; @@ -638,12 +561,8 @@ TEST(span_test, from_container_constructor) use_span(get_temp_string()); } - { -#ifdef CONFIRM_COMPILATION_ERRORS - std::map m; - span s{m}; -#endif - } + static_assert(!CtorCompilesFor, std::map&>, + "!CtorCompilesFor, std::map&>"); } TEST(span_test, from_convertible_span_constructor) @@ -695,52 +614,20 @@ TEST(span_test, from_convertible_span_constructor) EXPECT_DEATH(T{avd}, expected); } -#ifdef CONFIRM_COMPILATION_ERRORS - { - std::array arr{}; - span avd{arr}; - span avcd = avd; - static_cast(avcd); - } - - { - std::array arr{}; - span avd{arr}; - span avcd = avd; - static_cast(avcd); - } - - { - std::array arr{}; - span avd{arr}; - span avcd = avd; - static_cast(avcd); - } - - { - span avd; - span avb = avd; - static_cast(avb); - } - - { - span s; - span s2 = s; - static_cast(s2); - } - - { - span s; - span s2 = s; - static_cast(s2); - } - - { - span s; - span s2 = s; - static_cast(s2); - } -#endif + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); } TEST(span_test, copy_move_and_assignment) @@ -800,10 +687,9 @@ TEST(span_test, first) { span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - EXPECT_TRUE(av.first<6>().size() == 6); - EXPECT_TRUE(av.first<-1>().size() == -1); + (void) av.first<6>(); #endif - EXPECT_DEATH(av.first(6).size(), expected); + EXPECT_DEATH(av.first(6), expected); } { @@ -844,9 +730,9 @@ TEST(span_test, last) { span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - EXPECT_TRUE(av.last<6>().size() == 6); + (void) av.last<6>(); #endif - EXPECT_DEATH(av.last(6).size(), expected); + EXPECT_DEATH(av.last(6), expected); } { @@ -871,6 +757,9 @@ TEST(span_test, subspan) EXPECT_TRUE((av.subspan<2, 2>().size()) == 2); EXPECT_TRUE(decltype(av.subspan<2, 2>())::extent == 2); EXPECT_TRUE(av.subspan(2, 2).size() == 2); + + EXPECT_TRUE((av.subspan<2, 3>().size()) == 3); + EXPECT_TRUE(decltype(av.subspan<2, 3>())::extent == 3); EXPECT_TRUE(av.subspan(2, 3).size() == 3); } @@ -887,8 +776,12 @@ TEST(span_test, subspan) EXPECT_TRUE(decltype(av.subspan<0, 5>())::extent == 5); EXPECT_TRUE(av.subspan(0, 5).size() == 5); - EXPECT_DEATH(av.subspan(0, 6).size(), expected); - EXPECT_DEATH(av.subspan(1, 5).size(), expected); +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.subspan<0, 6>(); + (void) av.subspan<1, 5>(); +#endif + EXPECT_DEATH(av.subspan(0, 6), expected); + EXPECT_DEATH(av.subspan(1, 5), expected); } { @@ -896,14 +789,22 @@ TEST(span_test, subspan) EXPECT_TRUE((av.subspan<4, 0>().size()) == 0); EXPECT_TRUE(decltype(av.subspan<4, 0>())::extent == 0); EXPECT_TRUE(av.subspan(4, 0).size() == 0); + + EXPECT_TRUE((av.subspan<5, 0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<5, 0>())::extent == 0); EXPECT_TRUE(av.subspan(5, 0).size() == 0); - EXPECT_DEATH(av.subspan(6, 0).size(), expected); + +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.subspan<6, 0>(); +#endif + EXPECT_DEATH(av.subspan(6, 0), expected); } { span av = arr; EXPECT_TRUE(av.subspan<1>().size() == 4); EXPECT_TRUE(decltype(av.subspan<1>())::extent == 4); + EXPECT_TRUE(av.subspan(1).size() == 4); } { @@ -911,35 +812,58 @@ TEST(span_test, subspan) EXPECT_TRUE((av.subspan<0, 0>().size()) == 0); EXPECT_TRUE(decltype(av.subspan<0, 0>())::extent == 0); EXPECT_TRUE(av.subspan(0, 0).size() == 0); - EXPECT_DEATH((av.subspan<1, 0>().size()), expected); + + EXPECT_DEATH((av.subspan<1, 0>()), expected); + EXPECT_DEATH((av.subspan(1, 0)), expected); } { span av; + EXPECT_TRUE((av.subspan<0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<0>())::extent == dynamic_extent); EXPECT_TRUE(av.subspan(0).size() == 0); - EXPECT_DEATH(av.subspan(1).size(), expected); + + EXPECT_DEATH(av.subspan<1>(), expected); + EXPECT_TRUE(decltype(av.subspan<1>())::extent == dynamic_extent); + EXPECT_DEATH(av.subspan(1), expected); } { span av = arr; EXPECT_TRUE(av.subspan(0).size() == 5); + EXPECT_TRUE(av.subspan<0>().size() == 5); EXPECT_TRUE(av.subspan(1).size() == 4); + EXPECT_TRUE(av.subspan<1>().size() == 4); EXPECT_TRUE(av.subspan(4).size() == 1); + EXPECT_TRUE(av.subspan<4>().size() == 1); EXPECT_TRUE(av.subspan(5).size() == 0); - EXPECT_DEATH(av.subspan(6).size(), expected); + EXPECT_TRUE(av.subspan<5>().size() == 0); + EXPECT_DEATH(av.subspan(6), expected); + EXPECT_DEATH(av.subspan<6>(), expected); const auto av2 = av.subspan(1); for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast(i) + 2); + const auto av3 = av.subspan<1>(); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast(i) + 2); } { span av = arr; EXPECT_TRUE(av.subspan(0).size() == 5); + EXPECT_TRUE(av.subspan<0>().size() == 5); EXPECT_TRUE(av.subspan(1).size() == 4); + EXPECT_TRUE(av.subspan<1>().size() == 4); EXPECT_TRUE(av.subspan(4).size() == 1); + EXPECT_TRUE(av.subspan<4>().size() == 1); EXPECT_TRUE(av.subspan(5).size() == 0); - EXPECT_DEATH(av.subspan(6).size(), expected); + EXPECT_TRUE(av.subspan<5>().size() == 0); + EXPECT_DEATH(av.subspan(6), expected); +#ifdef CONFIRM_COMPILATION_ERRORS + EXPECT_DEATH(av.subspan<6>(), expected); +#endif const auto av2 = av.subspan(1); for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast(i) + 2); + const auto av3 = av.subspan<1>(); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast(i) + 2); } } @@ -1114,9 +1038,21 @@ TEST(span_test, rbegin_rend) } } +template +static constexpr bool AsWritableBytesCompilesFor = false; +template +static constexpr bool + AsWritableBytesCompilesFor()))>> = true; + TEST(span_test, as_bytes) { int a[] = {1, 2, 3, 4}; + + static_assert(AsWritableBytesCompilesFor>, "AsWriteableBytesCompilesFor>"); + // you should not be able to get writeable bytes for const objects + static_assert(!AsWritableBytesCompilesFor>, + "!AsWriteableBytesCompilesFor>"); + { const span s = a; EXPECT_TRUE(s.size() == 4); @@ -1147,17 +1083,6 @@ TEST(span_test, as_writable_bytes) { int a[] = {1, 2, 3, 4}; - { -#ifdef CONFIRM_COMPILATION_ERRORS - // you should not be able to get writeable bytes for const objects - span s = a; - EXPECT_TRUE(s.size() == 4); - span bs = as_writable_bytes(s); - EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); - EXPECT_TRUE(bs.size() == s.size_bytes()); -#endif - } - { span s; const auto bs = as_writable_bytes(s); @@ -1197,30 +1122,15 @@ TEST(span_test, fixed_size_conversions) static_cast(s); } -// initialization or assignment to static span that REDUCES size is NOT ok -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s = arr; - } - { - span s2 = s4; - static_cast(s2); - } -#endif + // initialization or assignment to static span that REDUCES size is NOT ok + static_assert(!ConversionCompilesFor, int[4]>, + "!ConversionCompilesFor, int[4]>"); + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); // even when done dynamically - { - /* - // this now results in a compile-time error, rather than runtime. - // There is no suitable conversion from dynamic span to fixed span. - span s = arr; - auto f = [&]() { - const span s2 = s; - static_cast(s2); - }; - EXPECT_DEATH(f(), expected); - */ - } + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); // but doing so explicitly is ok @@ -1234,32 +1144,24 @@ TEST(span_test, fixed_size_conversions) static_cast(s1); } - /* - // this is not a legal operation in std::span, so we are no longer supporting it - // conversion from span to span via call to `first` - // then convert from span to span - // The dynamic to fixed extents are not supported in the standard - // to make this work, span would need to be span. - { - - // NB: implicit conversion to span from span - span s1 = s4.first(1); - static_cast(s1); - } - */ + // this is not a legal operation in std::span, so we are no longer supporting it + // conversion from span to span via call to `first` + // then convert from span to span + // The dynamic to fixed extents are not supported in the standard + // to make this work, span would need to be span. + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); // initialization or assignment to static span that requires size INCREASE is not ok. int arr2[2] = {1, 2}; -#ifdef CONFIRM_COMPILATION_ERRORS - { - span s3 = arr2; - } - { - span s2 = arr2; - span s4a = s2; - } -#endif + static_assert(!ConversionCompilesFor, int[2]>, + "!ConversionCompilesFor, int[2]>"); + static_assert(!ConversionCompilesFor, int[2]>, + "!ConversionCompilesFor, int[2]>"); + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); + { auto f = [&]() { const span _s4{arr2, 2}; @@ -1268,16 +1170,11 @@ TEST(span_test, fixed_size_conversions) EXPECT_DEATH(f(), expected); } - /* - // This no longer compiles. There is no suitable conversion from dynamic span to a fixed size - span. - // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one - span av = arr2; auto f = [&]() { - const span _s4 = av; - static_cast(_s4); - }; - EXPECT_DEATH(f(), expected); - */ + // This no longer compiles. There is no suitable conversion from dynamic span to a fixed size + // span. + // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); } TEST(span_test, interop_with_std_regex) @@ -1376,8 +1273,6 @@ TEST(span_test, msvc_compile_error_PR1100) int arr[]{1, 7, 2, 9}; gsl::span sp{arr, std::size(arr)}; std::ranges::sort(sp); - for (const auto& e : sp) { - (void)e; - } + for (const auto& e : sp) { (void) e; } } #endif // defined(__cpp_lib_span) && defined(__cpp_lib_ranges) diff --git a/tests/strict_notnull_tests.cpp b/tests/strict_notnull_tests.cpp index 82f9c5c..d75f5d6 100644 --- a/tests/strict_notnull_tests.cpp +++ b/tests/strict_notnull_tests.cpp @@ -17,10 +17,19 @@ #include // for not_null, operator<, operator<=, operator> #include +#include // for declval + #include "deathTestCommon.h" using namespace gsl; +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + // stand-in for a user-defined ref-counted class template struct RefCounted @@ -53,21 +62,39 @@ GSL_SUPPRESS(f.4) // NO-FORMAT: attribute bool strict_helper_const(strict_not_null p) { return *p == 12; } int* return_pointer() { return nullptr; } -#ifdef CONFIRM_COMPILATION_ERRORS -const int* return_pointer_const() { return nullptr; } -#endif } // namespace +template +static constexpr bool CtorCompilesFor_A = false; +template +static constexpr bool + CtorCompilesFor_A{std::declval()})>> = true; + +template +static constexpr bool CtorCompilesFor_B = false; +template +static constexpr bool CtorCompilesFor_B{N})>> = true; + +template +static constexpr bool DefaultCtorCompilesFor = false; +template +static constexpr bool DefaultCtorCompilesFor{})>> = true; + +template +static constexpr bool CtorCompilesFor_C = false; +template +static constexpr bool CtorCompilesFor_C< + U, void_t{std::declval>()})>> = true; + TEST(strict_notnull_tests, TestStrictNotNullConstructors) { { + static_assert(CtorCompilesFor_A, "CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_A, "!CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_B, "!CtorCompilesFor_B"); + static_assert(!DefaultCtorCompilesFor, "!DefaultCtorCompilesFor"); + static_assert(!CtorCompilesFor_C, "CtorCompilesFor_C"); #ifdef CONFIRM_COMPILATION_ERRORS - strict_not_null p = nullptr; // yay...does not compile! - strict_not_null*> p1 = 0; // yay...does not compile! - strict_not_null p2; // yay...does not compile! - std::unique_ptr up = std::make_unique(120); - strict_not_null p3 = up; - // Forbid non-nullptr assignable types strict_not_null> f(std::vector{1}); strict_not_null z(10); @@ -162,6 +189,26 @@ TEST(strict_notnull_tests, TestStrictNotNullConstructors) } } +template +static constexpr bool StrictHelperCompilesFor = false; +template +static constexpr bool + StrictHelperCompilesFor()))>> = true; + + +template +static constexpr bool StrictHelperConstCompilesFor = false; +template +static constexpr bool + StrictHelperConstCompilesFor()))>> = + true; + + +template +static constexpr bool HelperCompilesFor = false; +template +static constexpr bool HelperCompilesFor()))>> = true; + TEST(strict_notnull_tests, TestStrictNotNull) { { @@ -170,14 +217,15 @@ TEST(strict_notnull_tests, TestStrictNotNull) #ifdef CONFIRM_COMPILATION_ERRORS strict_not_null snn = &x; - strict_helper(&x); - strict_helper_const(&x); - strict_helper(return_pointer()); - strict_helper_const(return_pointer_const()); #endif + static_assert(!StrictHelperCompilesFor, "!StrictHelperCompilesFor"); + static_assert(!StrictHelperConstCompilesFor, + "!StrictHelperCompilesFor"); const strict_not_null snn1{&x}; + static_assert(StrictHelperCompilesFor>, + "StrictHelperCompilesFor>"); helper(snn1); helper_const(snn1); @@ -190,17 +238,17 @@ TEST(strict_notnull_tests, TestStrictNotNull) #ifdef CONFIRM_COMPILATION_ERRORS strict_not_null snn = &x; - strict_helper(&x); - strict_helper_const(&x); - strict_helper(return_pointer()); - strict_helper_const(return_pointer_const()); #endif + static_assert(!StrictHelperCompilesFor, "!StrictHelperFor"); + static_assert(!StrictHelperConstCompilesFor, + "!StrictHelperCompilesFor"); const strict_not_null snn1{&x}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(snn1); -#endif + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + static_assert(StrictHelperConstCompilesFor>, + "StrictHelperCompilesFor>"); helper_const(snn1); EXPECT_TRUE(*snn1 == 42); @@ -227,9 +275,8 @@ TEST(strict_notnull_tests, TestStrictNotNull) strict_not_null snn1{&x}; const strict_not_null snn2{&x}; -#ifdef CONFIRM_COMPILATION_ERRORS - strict_helper(snn1); -#endif + static_assert(!StrictHelperCompilesFor>, + "!StrictHelperCompilesFor>"); strict_helper_const(snn1); strict_helper_const(snn2); @@ -261,9 +308,8 @@ TEST(strict_notnull_tests, TestStrictNotNull) const not_null nn1 = snn; const not_null nn2{snn}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(snn); -#endif + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); helper_const(snn); EXPECT_TRUE(snn == nn1); @@ -303,9 +349,8 @@ TEST(strict_notnull_tests, TestStrictNotNull) const strict_not_null snn1{nn}; const strict_not_null snn2{nn}; -#ifdef CONFIRM_COMPILATION_ERRORS - strict_helper(nn); -#endif + static_assert(!StrictHelperCompilesFor>, + "!StrictHelperCompilesFor>"); strict_helper_const(nn); EXPECT_TRUE(snn1 == nn); @@ -319,12 +364,6 @@ TEST(strict_notnull_tests, TestStrictNotNull) EXPECT_TRUE(hash_nn(snn1) == hash_nn(snn2)); EXPECT_TRUE(hash_snn(snn1) == hash_snn(nn)); } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - strict_not_null p{nullptr}; - } -#endif } #if defined(__cplusplus) && (__cplusplus >= 201703L) @@ -351,9 +390,8 @@ TEST(strict_notnull_tests, TestStrictNotNullConstructorTypeDeduction) const int i = 42; strict_not_null x{&i}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(strict_not_null{&i}); -#endif + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); helper_const(strict_not_null{&i}); EXPECT_TRUE(*x == 42); @@ -375,9 +413,8 @@ TEST(strict_notnull_tests, TestStrictNotNullConstructorTypeDeduction) const int* p = &i; strict_not_null x{p}; -#ifdef CONFIRM_COMPILATION_ERRORS - helper(strict_not_null{p}); -#endif + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); helper_const(strict_not_null{p}); EXPECT_TRUE(*x == 42);