From cbfd8cd7341b63f14bb8b4200ce35af43979d25d Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Fri, 27 Nov 2020 17:06:01 -0800 Subject: [PATCH] Parameterize `Expects1 and `Ensures` by contract group Allows independently controlling handling of different categories of bugs, such as bounds checks vs. null checks. User-extensible: Companies can instantiate their own `contract_group` objects for their own categories of contract checks, including for distinguishing contract "levels" like `Normal` vs. `Audit` by just creating those two groups that can then be controlled independently or in combination. --- README.md | 4 +- include/gsl/algorithm | 6 +-- include/gsl/assert | 13 +++-- include/gsl/gsl | 2 +- include/gsl/multi_span | 96 ++++++++++++++++++------------------- include/gsl/narrow | 2 +- include/gsl/pointers | 8 ++-- include/gsl/span | 84 ++++++++++++++++---------------- include/gsl/span_ext | 2 +- include/gsl/string_span | 12 ++--- include/gsl/util | 8 ++-- tests/assertion_tests.cpp | 6 +-- tests/string_span_tests.cpp | 10 ++-- 13 files changed, 129 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 64becc1..362111c 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ shared_ptr | ☑ | an alias to `std::shared_ptr` stack_array | ☐ | a stack-allocated array dyn_array | ☐ | a heap-allocated array [**3. Assertions**][cg-assertions] | | -Expects | ☑ | a precondition assertion; on failure it terminates -Ensures | ☑ | a postcondition assertion; on failure it terminates +Expects | ☑ | a precondition assertion; on failure it terminates by default if no user-installed handler +Ensures | ☑ | a postcondition assertion; on failure it terminates by default if no user-installed handler [**4. Utilities**][cg-utilities] | | move_owner | ☐ | a helper function that moves one `owner` to the other byte | ☑ | either an alias to std::byte or a byte type diff --git a/include/gsl/algorithm b/include/gsl/algorithm index b27475d..0ef5ca5 100644 --- a/include/gsl/algorithm +++ b/include/gsl/algorithm @@ -17,7 +17,7 @@ #ifndef GSL_ALGORITHM_H #define GSL_ALGORITHM_H -#include // for Expects +#include // for contracts #include // for dynamic_extent, span #include // for copy_n @@ -27,7 +27,7 @@ #ifdef _MSC_VER #pragma warning(push) -// turn off some warnings that are noisy about our Expects statements +// turn off some warnings that are noisy about our contract checks #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4996) // unsafe use of std::copy_n @@ -47,7 +47,7 @@ void copy(span src, span (SrcExtent <= DestExtent), "Source range is longer than target range"); - Expects(dest.size() >= src.size()); + Expects(dest.size() >= src.size(), Bounds); // clang-format off GSL_SUPPRESS(stl.1) // NO-FORMAT: attribute // clang-format on diff --git a/include/gsl/assert b/include/gsl/assert index 848ee89..76e5344 100644 --- a/include/gsl/assert +++ b/include/gsl/assert @@ -108,18 +108,23 @@ public: auto set_handler(handler h) -> handler { return chandler.exchange(h); } auto get_handler() -> handler { return chandler; } - auto assertion(bool b) { if (!b && chandler.load()) chandler.load()(); } + auto expects(bool b) { assertion(b); } + auto ensures(bool b) { assertion(b); } private: + auto assertion(bool b) { if (!b && chandler.load()) chandler.load()(); } std::atomic chandler; }; -auto static cg_default = contract_group{ &gsl::details::terminate }; +auto static Default = contract_group{ &gsl::details::terminate }; +auto static Bounds = contract_group{ Default.get_handler() }; +auto static Null = contract_group{ Default.get_handler() }; +auto static Testing = contract_group{ Default.get_handler() }; } // namespace gsl -#define Expects(cond) gsl::cg_default.assertion(cond) -#define Ensures(cond) gsl::cg_default.assertion(cond) +#define Expects(cond, kind) kind.expects(cond) +#define Ensures(cond, kind) kind.ensures(cond) #if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) && defined(__clang__) #pragma clang diagnostic pop diff --git a/include/gsl/gsl b/include/gsl/gsl index ac5e144..c5f47b2 100644 --- a/include/gsl/gsl +++ b/include/gsl/gsl @@ -18,7 +18,7 @@ #define GSL_GSL_H #include // copy -#include // Ensures/Expects +#include // contracts #include // byte #include // owner, not_null #include // multi_span, strided_span... diff --git a/include/gsl/multi_span b/include/gsl/multi_span index 14b86b3..7aa8d4e 100644 --- a/include/gsl/multi_span +++ b/include/gsl/multi_span @@ -17,7 +17,7 @@ #ifndef GSL_MULTI_SPAN_H #define GSL_MULTI_SPAN_H -#include // for Expects +#include // for contracts #include // for byte #include // for narrow_cast @@ -38,7 +38,7 @@ #if defined(_MSC_VER) && !defined(__clang__) -// turn off some warnings that are noisy about our Expects statements +// turn off some warnings that are noisy about our contract checks #pragma warning(push) #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code @@ -136,7 +136,7 @@ public: // clang-format on constexpr reference operator[](std::size_t component_idx) { - Expects(component_idx < Rank); // Component index must be less than rank + Expects(component_idx < Rank, Bounds); // Component index must be less than rank return elems[component_idx]; } @@ -147,7 +147,7 @@ public: // clang-format on constexpr const_reference operator[](std::size_t component_idx) const { - Expects(component_idx < Rank); // Component index must be less than rank + Expects(component_idx < Rank, Bounds); // Component index must be less than rank return elems[component_idx]; } @@ -377,7 +377,7 @@ namespace details constexpr BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { - Expects(0 <= *arr); + Expects(0 <= *arr, Bounds); } constexpr BoundsRanges() noexcept : m_bound(0) {} @@ -403,7 +403,7 @@ namespace details constexpr size_type linearize(const T& arr) const { const size_type index = this->Base::totalSize() * arr[Dim]; - Expects(index < m_bound); + Expects(index < m_bound, Bounds); return index + this->Base::template linearize(arr); } @@ -485,7 +485,7 @@ namespace details // clang-format off GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // clang-format on - Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range + Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange, Bounds); // Index is out of range // clang-format off GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // clang-format on @@ -680,16 +680,16 @@ public: constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) { Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || - MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize(), Bounds); } constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { // Size of the initializer list must match the rank of the array Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || - MyRanges::DynamicNum == il.size()); + MyRanges::DynamicNum == il.size(), Bounds); // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + Expects(m_ranges.totalSize() <= PTRDIFF_MAX, Bounds); } constexpr sliced_type slice() const noexcept @@ -729,7 +729,7 @@ public: static_assert(std::is_integral::value, "Dimension parameter must be supplied as an integral type."); auto real_dim = narrow_cast(dim); - Expects(real_dim < rank); + Expects(real_dim < rank, Bounds); return m_ranges.elementNum(real_dim); } @@ -834,7 +834,7 @@ public: size_type ret = 0; for (std::size_t i = 0; i < rank; i++) { - Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + Expects(idx[i] < m_extents[i], Bounds); // index is out of bounds of the array ret += idx[i] * m_strides[i]; } return ret; @@ -960,7 +960,7 @@ public: } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" - Expects(false); + Expects(false, Bounds); return *this; } @@ -992,7 +992,7 @@ public: linear_idx = linear_idx % stride[i]; } // index is out of bounds of the array - Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_), Bounds); return *this; } @@ -1132,7 +1132,7 @@ namespace details BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); - Expects(src.size() == dest.size()); + Expects(src.size() == dest.size(), Bounds); } } // namespace details @@ -1210,13 +1210,13 @@ namespace details template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size { - Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX, Bounds); return BoundsType{totalSize}; } template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size { - Expects(BoundsType::static_size <= totalSize); + Expects(BoundsType::static_size <= totalSize, Bounds); return {}; } template @@ -1345,7 +1345,7 @@ public: (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), "nullptr_t construction of multi_span only possible " "for dynamic or fixed, zero-length spans."); - Expects(size == 0); + Expects(size == 0, Bounds); } // construct from a single element @@ -1373,7 +1373,7 @@ public: // construct from pointer + length - multidimensional constexpr multi_span(pointer data, bounds_type bounds) : data_(data), bounds_(std::move(bounds)) { - Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0, Bounds); } // construct from begin,end pointer pair @@ -1384,7 +1384,7 @@ public: : multi_span(begin, details::newBoundsHelper(static_cast(end) - begin)) { - Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end), Bounds); } // construct from n-dimensions static array @@ -1488,14 +1488,14 @@ public: Count <= bounds_type::static_size, "Count is out of bounds."); - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + Expects(bounds_type::static_size != dynamic_range || Count <= this->size(), Bounds); return {this->data(), Count}; } // first() - extract the first count elements into a new multi_span constexpr multi_span first(size_type count) const { - Expects(count >= 0 && count <= this->size()); + Expects(count >= 0 && count <= this->size(), Bounds); return {this->data(), count}; } @@ -1508,14 +1508,14 @@ public: Count <= bounds_type::static_size, "Count is out of bounds."); - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + Expects(bounds_type::static_size != dynamic_range || Count <= this->size(), Bounds); return {this->data() + this->size() - Count, Count}; } // last() - extract the last count elements into a new multi_span constexpr multi_span last(size_type count) const { - Expects(count >= 0 && count <= this->size()); + Expects(count >= 0 && count <= this->size(), Bounds); return {this->data() + this->size() - count, count}; } @@ -1531,7 +1531,7 @@ public: "You must describe a sub-range within bounds of the multi_span."); Expects(bounds_type::static_size != dynamic_range || - (Offset <= this->size() && Count <= this->size() - Offset)); + (Offset <= this->size() && Count <= this->size() - Offset), Bounds); return {this->data() + Offset, Count}; } @@ -1541,7 +1541,7 @@ public: size_type count = dynamic_range) const { Expects((offset >= 0 && offset <= this->size()) && - (count == dynamic_range || (count <= this->size() - offset))); + (count == dynamic_range || (count <= this->size() - offset)), Bounds); return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; } @@ -1619,11 +1619,11 @@ public: // clang-format on constexpr Ret operator[](size_type idx) const { - Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array + Expects(idx >= 0 && idx < bounds_.size(), Bounds); // index is out of bounds of the array const size_type ridx = idx * bounds_.stride(); // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); + Expects(ridx < bounds_.total_size(), Bounds); return Ret{data_ + ridx, bounds_.slice()}; } @@ -1771,7 +1771,7 @@ constexpr auto as_multi_span(multi_span s) -> multi_s "Target type must be a trivial type and its size must match the byte array size"); Expects((s.size_bytes() % narrow_cast(sizeof(U))) == 0 && - (s.size_bytes() / narrow_cast(sizeof(U))) < PTRDIFF_MAX); + (s.size_bytes() / narrow_cast(sizeof(U))) < PTRDIFF_MAX, Bounds); return {reinterpret_cast(s.data()), s.size_bytes() / narrow_cast(sizeof(U))}; } @@ -1795,7 +1795,7 @@ constexpr auto as_multi_span(multi_span s) ByteSpan::bounds_type::static_size % sizeof(U) == 0), "Target type must be a trivial type and its size must match the byte array size"); - Expects((s.size_bytes() % sizeof(U)) == 0); + Expects((s.size_bytes() % sizeof(U)) == 0, Bounds); return {reinterpret_cast(s.data()), s.size_bytes() / narrow_cast(sizeof(U))}; } @@ -1848,7 +1848,7 @@ constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< !details::is_multi_span>::value, multi_span, dynamic_range>> { - Expects(arr.size() < PTRDIFF_MAX); + Expects(arr.size() < PTRDIFF_MAX, Bounds); return {arr.data(), narrow_cast(arr.size())}; } @@ -1865,7 +1865,7 @@ GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute constexpr auto as_multi_span(std::basic_string& str) -> multi_span { - Expects(str.size() < PTRDIFF_MAX); + Expects(str.size() < PTRDIFF_MAX, Bounds); return {&str[0], narrow_cast(str.size())}; } @@ -1905,9 +1905,9 @@ public: constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) : data_(ptr), bounds_(std::move(bounds)) { - Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0, Bounds); // Bounds cross data boundaries - Expects(this->bounds().total_size() <= size); + Expects(this->bounds().total_size() <= size, Bounds); // clang-format off GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: false positive // clang-format on @@ -1980,11 +1980,11 @@ public: // clang-format on constexpr Ret operator[](size_type idx) const { - Expects(idx < bounds_.size()); // index is out of bounds of the array + Expects(idx < bounds_.size(), Bounds); // index is out of bounds of the array const size_type ridx = idx * bounds_.stride(); // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); + Expects(ridx < bounds_.total_size(), Bounds); return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; } @@ -2084,7 +2084,7 @@ private: // clang-format off GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // clang-format on - Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0), Bounds); index_type ret = extent; ret[Rank - 1] /= d; @@ -2099,7 +2099,7 @@ private: // clang-format off GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // clang-format on - Expects(strides[Rank - 1] == 1); + Expects(strides[Rank - 1] == 1, Bounds); return strides; } @@ -2111,15 +2111,15 @@ private: static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) { // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); + Expects(strides[Rank - 1] == 1, Bounds); // The strides must have contiguous chunks of // memory that can contain a multiple of new type elements - Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0), Bounds); for (std::size_t i = Rank - 1; i > 0; --i) { // Only strided arrays with regular strides can be resized - Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0), Bounds); } index_type ret = strides / d; @@ -2152,7 +2152,7 @@ private: void validateThis() const { // iterator is out of range of the array - Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size(), Bounds); } // clang-format off @@ -2223,13 +2223,13 @@ public: contiguous_span_iterator& operator-=(difference_type n) { return *this += -n; } difference_type operator-(const contiguous_span_iterator& rhs) const { - Expects(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator, Bounds); return data_ - rhs.data_; } reference operator[](difference_type n) const { return *(*this + n); } bool operator==(const contiguous_span_iterator& rhs) const { - Expects(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator, Bounds); return data_ == rhs.data_; } @@ -2237,7 +2237,7 @@ public: bool operator<(const contiguous_span_iterator& rhs) const { - Expects(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator, Bounds); return data_ < rhs.data_; } @@ -2323,7 +2323,7 @@ public: general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } difference_type operator-(const general_span_iterator& rhs) const { - Expects(m_container == rhs.m_container); + Expects(m_container == rhs.m_container, Bounds); return m_itr - rhs.m_itr; } @@ -2334,13 +2334,13 @@ public: bool operator==(const general_span_iterator& rhs) const { - Expects(m_container == rhs.m_container); + Expects(m_container == rhs.m_container, Bounds); return m_itr == rhs.m_itr; } bool operator!=(const general_span_iterator& rhs) const { return !(*this == rhs); } bool operator<(const general_span_iterator& rhs) const { - Expects(m_container == rhs.m_container); + Expects(m_container == rhs.m_container, Bounds); return m_itr < rhs.m_itr; } bool operator<=(const general_span_iterator& rhs) const { return !(rhs < *this); } diff --git a/include/gsl/narrow b/include/gsl/narrow index d47d9fd..c5d0893 100644 --- a/include/gsl/narrow +++ b/include/gsl/narrow @@ -16,7 +16,7 @@ #ifndef GSL_NARROW_H #define GSL_NARROW_H -#include // for Expects +#include // for contracts #include // for narrow_cast namespace gsl { diff --git a/include/gsl/pointers b/include/gsl/pointers index 42e8c09..34fd34d 100644 --- a/include/gsl/pointers +++ b/include/gsl/pointers @@ -17,7 +17,7 @@ #ifndef GSL_POINTERS_H #define GSL_POINTERS_H -#include // for Ensures, Expects +#include // for contracts #include // for forward #include // for ptrdiff_t, nullptr_t, size_t @@ -74,13 +74,13 @@ public: template ::value>> constexpr not_null(U&& u) : ptr_(std::forward(u)) { - Expects(ptr_ != nullptr); + Expects(ptr_ != nullptr, Null); } template ::value>> constexpr not_null(T u) : ptr_(std::move(u)) { - Expects(ptr_ != nullptr); + Expects(ptr_ != nullptr, Null); } template ::value>> @@ -91,7 +91,7 @@ public: not_null& operator=(const not_null& other) = default; constexpr std::conditional_t::value, T, const T&> get() const { - Ensures(ptr_ != nullptr); + Ensures(ptr_ != nullptr, Null); return ptr_; } diff --git a/include/gsl/span b/include/gsl/span index 506eb4c..f15d8ca 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -17,7 +17,7 @@ #ifndef GSL_SPAN_H #define GSL_SPAN_H -#include // for Expects +#include // for contracts #include // for byte #include // for narrow_cast @@ -29,7 +29,7 @@ #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(push) -// turn off some warnings that are noisy about our Expects statements +// turn off some warnings that are noisy about our contract checks #pragma warning(disable : 4127) // conditional expression is constant #pragma warning( \ disable : 4146) // unary minus operator applied to unsigned type, result still unsigned @@ -137,21 +137,21 @@ namespace details constexpr reference operator*() const noexcept { - Expects(begin_ && end_); - Expects(begin_ <= current_ && current_ < end_); + Expects(begin_ && end_, Bounds); + Expects(begin_ <= current_ && current_ < end_, Bounds); return *current_; } constexpr pointer operator->() const noexcept { - Expects(begin_ && end_); - Expects(begin_ <= current_ && current_ < end_); + Expects(begin_ && end_, Bounds); + Expects(begin_ <= current_ && current_ < end_, Bounds); return current_; } constexpr span_iterator& operator++() noexcept { - Expects(begin_ && current_ && end_); - Expects(current_ < end_); + Expects(begin_ && current_ && end_, Bounds); + Expects(current_ < end_, Bounds); // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute // clang-format on @@ -168,8 +168,8 @@ namespace details constexpr span_iterator& operator--() noexcept { - Expects(begin_ && end_); - Expects(begin_ < current_); + Expects(begin_ && end_, Bounds); + Expects(begin_ < current_, Bounds); --current_; return *this; } @@ -183,9 +183,9 @@ namespace details constexpr span_iterator& operator+=(const difference_type n) noexcept { - if (n != 0) Expects(begin_ && current_ && end_); - if (n > 0) Expects(end_ - current_ >= n); - if (n < 0) Expects(current_ - begin_ >= -n); + if (n != 0) Expects(begin_ && current_ && end_, Bounds); + if (n > 0) Expects(end_ - current_ >= n, Bounds); + if (n < 0) Expects(current_ - begin_ >= -n, Bounds); // clang-format off GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute // clang-format on @@ -208,9 +208,9 @@ namespace details constexpr span_iterator& operator-=(const difference_type n) noexcept { - if (n != 0) Expects(begin_ && current_ && end_); - if (n > 0) Expects(current_ - begin_ >= n); - if (n < 0) Expects(end_ - current_ >= -n); + if (n != 0) Expects(begin_ && current_ && end_, Bounds); + if (n > 0) Expects(current_ - begin_ >= n, Bounds); + if (n < 0) Expects(end_ - current_ >= -n, Bounds); current_ -= n; return *this; } @@ -227,7 +227,7 @@ namespace details std::enable_if_t, value_type>::value, int> = 0> constexpr difference_type operator-(const span_iterator& rhs) const noexcept { - Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + Expects(begin_ == rhs.begin_ && end_ == rhs.end_, Bounds); return current_ - rhs.current_; } @@ -241,7 +241,7 @@ namespace details std::enable_if_t, value_type>::value, int> = 0> constexpr bool operator==(const span_iterator& rhs) const noexcept { - Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + Expects(begin_ == rhs.begin_ && end_ == rhs.end_, Bounds); return current_ == rhs.current_; } @@ -258,7 +258,7 @@ namespace details std::enable_if_t, value_type>::value, int> = 0> constexpr bool operator<(const span_iterator& rhs) const noexcept { - Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + Expects(begin_ == rhs.begin_ && end_ == rhs.end_, Bounds); return current_ < rhs.current_; } @@ -294,14 +294,14 @@ namespace details { // test that [lhs, rhs) forms a valid range inside an STL algorithm Expects(lhs.begin_ == rhs.begin_ // range spans have to match && lhs.end_ == rhs.end_ && - lhs.current_ <= rhs.current_); // range must not be transposed + lhs.current_ <= rhs.current_, Bounds); // range must not be transposed } constexpr void _Verify_offset(const difference_type n) const noexcept { // test that *this + n is within the range of this call - if (n != 0) Expects(begin_ && current_ && end_); - if (n > 0) Expects(end_ - current_ >= n); - if (n < 0) Expects(current_ - begin_ >= -n); + if (n != 0) Expects(begin_ && current_ && end_, Bounds); + if (n > 0) Expects(end_ - current_ >= n, Bounds); + if (n < 0) Expects(current_ - begin_ >= -n, Bounds); } // clang-format off @@ -346,7 +346,7 @@ namespace details constexpr explicit extent_type(extent_type); - constexpr explicit extent_type(size_type size) { Expects(size == Ext); } + constexpr explicit extent_type(size_type size) { Expects(size == Ext, Bounds); } constexpr size_type size() const noexcept { return Ext; } @@ -370,7 +370,7 @@ namespace details constexpr explicit extent_type(size_type size) : size_(size) { - Expects(size != dynamic_extent); + Expects(size != dynamic_extent, Bounds); } constexpr size_type size() const noexcept { return size_; } @@ -382,7 +382,7 @@ namespace details template constexpr extent_type::extent_type(extent_type ext) { - Expects(ext.size() == Ext); + Expects(ext.size() == Ext, Bounds); } template @@ -431,7 +431,7 @@ public: template = 0> constexpr explicit span(pointer ptr, size_type count) noexcept : storage_(ptr, count) { - Expects(count == Extent); + Expects(count == Extent, Bounds); } template = 0> @@ -442,7 +442,7 @@ public: constexpr explicit span(pointer firstElem, pointer lastElem) noexcept : storage_(firstElem, narrow_cast(lastElem - firstElem)) { - Expects(lastElem - firstElem == static_cast(Extent)); + Expects(lastElem - firstElem == static_cast(Extent), Bounds); } template = 0> @@ -553,7 +553,7 @@ public: template constexpr span first() const noexcept { - Expects(Count <= size()); + Expects(Count <= size(), Bounds); return span{data(), Count}; } @@ -563,7 +563,7 @@ public: // clang-format on constexpr span last() const noexcept { - Expects(Count <= size()); + Expects(Count <= size(), Bounds); return span{data() + (size() - Count), Count}; } @@ -574,7 +574,7 @@ public: constexpr auto subspan() const noexcept -> typename details::calculate_subspan_type::type { - Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset))); + Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset)), Bounds); using type = typename details::calculate_subspan_type::type; return type{data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; @@ -582,13 +582,13 @@ public: constexpr span first(size_type count) const noexcept { - Expects(count <= size()); + Expects(count <= size(), Bounds); return {data(), count}; } constexpr span last(size_type count) const noexcept { - Expects(count <= size()); + Expects(count <= size(), Bounds); return make_subspan(size() - count, dynamic_extent, subspan_selector{}); } @@ -603,7 +603,7 @@ public: constexpr size_type size_bytes() const noexcept { - Expects(size() < dynamic_extent / sizeof(element_type)); + Expects(size() < dynamic_extent / sizeof(element_type), Bounds); return size() * sizeof(element_type); } @@ -615,19 +615,19 @@ public: // clang-format on constexpr reference operator[](size_type idx) const noexcept { - Expects(idx < size()); + Expects(idx < size(), Bounds); return data()[idx]; } constexpr reference front() const noexcept { - Expects(size() > 0); + Expects(size() > 0, Bounds); return data()[0]; } constexpr reference back() const noexcept { - Expects(size() > 0); + Expects(size() > 0, Bounds); return data()[size() - 1]; } @@ -688,14 +688,14 @@ private: constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p) { - Expects(ExtentType::size() != dynamic_extent); + Expects(ExtentType::size() != dynamic_extent, Bounds); } template constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) { - Expects(ExtentType::size() != dynamic_extent); - Expects(data || ExtentType::size() == 0); + Expects(ExtentType::size() != dynamic_extent, Bounds); + Expects(data || ExtentType::size() == 0, Bounds); } constexpr pointer data() const noexcept { return data_; } @@ -729,11 +729,11 @@ private: constexpr span make_subspan(size_type offset, size_type count, subspan_selector) const noexcept { - Expects(size() >= offset); + Expects(size() >= offset, Bounds); if (count == dynamic_extent) { return {KnownNotNull{data() + offset}, size() - offset}; } - Expects(size() - offset >= count); + Expects(size() - offset >= count, Bounds); return {KnownNotNull{data() + offset}, count}; } }; diff --git a/include/gsl/span_ext b/include/gsl/span_ext index b7c12cf..6162353 100644 --- a/include/gsl/span_ext +++ b/include/gsl/span_ext @@ -124,7 +124,7 @@ template constexpr ElementType& at(span s, index i) { // No bounds checking here because it is done in span::operator[] called below - Ensures(i >= 0); + Ensures(i >= 0, Bounds); return s[narrow_cast(i)]; } diff --git a/include/gsl/string_span b/include/gsl/string_span index a76236d..7efefcc 100644 --- a/include/gsl/string_span +++ b/include/gsl/string_span @@ -17,7 +17,7 @@ #ifndef GSL_STRING_SPAN_H #define GSL_STRING_SPAN_H -#include // for Ensures, Expects +#include // for contracts #include // for operator!=, operator==, dynamic_extent #include // for narrow_cast @@ -114,19 +114,19 @@ template "isocpp/CppCoreGuidelines PR#1680")]] constexpr span ensure_sentinel(T* seq, std::size_t max = static_cast(-1)) { - Ensures(seq != nullptr); + Ensures(seq != nullptr, Null); // clang-format off GSL_SUPPRESS(f.23) // TODO: false positive // TODO: suppress does not work // clang-format on auto cur = seq; - Ensures(cur != nullptr); // workaround for removing the warning + Ensures(cur != nullptr, Null); // workaround for removing the warning // clang-format off GSL_SUPPRESS(bounds.1) // TODO: suppress does not work // clang-format on while (static_cast(cur - seq) < max && *cur != Sentinel) ++cur; - Ensures(*cur == Sentinel); + Ensures(*cur == Sentinel, Bounds); return {seq, static_cast(cur - seq)}; } @@ -437,8 +437,8 @@ public: constexpr basic_zstring_span(impl_type s) : span_(s) { // expects a zero-terminated span - Expects(s.size() > 0); - Expects(s[s.size() - 1] == value_type{}); + Expects(s.size() > 0, Bounds); + Expects(s[s.size() - 1] == value_type{}, Bounds); } // copy diff --git a/include/gsl/util b/include/gsl/util index 2d67b7f..ec830a4 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -17,7 +17,7 @@ #ifndef GSL_UTIL_H #define GSL_UTIL_H -#include // for Expects +#include // for contracts #include #include // for ptrdiff_t, size_t @@ -108,7 +108,7 @@ GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute // clang-format on constexpr T& at(T (&arr)[N], const index i) { - Expects(i >= 0 && i < narrow_cast(N)); + Expects(i >= 0 && i < narrow_cast(N), Bounds); return arr[narrow_cast(i)]; } @@ -119,7 +119,7 @@ GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute // clang-format on constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) { - Expects(i >= 0 && i < narrow_cast(cont.size())); + Expects(i >= 0 && i < narrow_cast(cont.size()), Bounds); using size_type = decltype(cont.size()); return cont[narrow_cast(i)]; } @@ -130,7 +130,7 @@ GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute // clang-format on constexpr T at(const std::initializer_list cont, const index i) { - Expects(i >= 0 && i < narrow_cast(cont.size())); + Expects(i >= 0 && i < narrow_cast(cont.size()), Bounds); return *(cont.begin() + i); } diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp index 6b5fb0b..6559cb3 100644 --- a/tests/assertion_tests.cpp +++ b/tests/assertion_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include // for fail_fast (ptr only), Ensures, Expects +#include // for fail_fast (ptr only), contracts using namespace gsl; @@ -25,14 +25,14 @@ static constexpr char deathstring[] = "Expected Death"; int f(int i) { - Expects(i > 0 && i < 10); + Expects(i > 0 && i < 10, Testing); return i; } int g(int i) { i++; - Ensures(i > 0 && i < 10); + Ensures(i > 0 && i < 10, Testing); return i; } } // namespace diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 3c919d0..88c2ed8 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -16,7 +16,7 @@ #include -#include // for Expects, fail_fast (ptr only) +#include // for contracts, fail_fast (ptr only) #include // for owner #include // for span, dynamic_extent #include // for basic_string_span, operator==, ensure_z @@ -82,7 +82,7 @@ void use(basic_string_span) czstring_span<> CreateTempName(string_span<> span) { - Expects(span.size() > 1); + Expects(span.size() > 1, Testing); std::size_t last = 0; if (span.size() > 4) { @@ -99,7 +99,7 @@ czstring_span<> CreateTempName(string_span<> span) cwzstring_span<> CreateTempNameW(wstring_span<> span) { - Expects(span.size() > 1); + Expects(span.size() > 1, Testing); std::size_t last = 0; if (span.size() > 4) { @@ -116,7 +116,7 @@ cwzstring_span<> CreateTempNameW(wstring_span<> span) cu16zstring_span<> CreateTempNameU16(u16string_span<> span) { - Expects(span.size() > 1); + Expects(span.size() > 1, Testing); std::size_t last = 0; if (span.size() > 4) { @@ -133,7 +133,7 @@ cu16zstring_span<> CreateTempNameU16(u16string_span<> span) cu32zstring_span<> CreateTempNameU32(u32string_span<> span) { - Expects(span.size() > 1); + Expects(span.size() > 1, Testing); std::size_t last = 0; if (span.size() > 4) {