Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions stl/inc/mdspan
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,13 @@ public:
if constexpr (_Extents::rank() == 0) {
return 1;
} else {
typename _Extents::index_type _Result = 1;
// Use an unsigned type to prevent overflow when called from mdspan::size.
typename _Extents::size_type _Result = 1;
for (size_t _Dim = 0; _Dim < _Idx; ++_Dim) {
_Result *= _Exts.extent(_Dim);
_Result *= static_cast<_Extents::size_type>(_Exts.extent(_Dim));

@MattStephanson Matt Stephanson (MattStephanson) Sep 20, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the static_cast here is redundant because it will be covered by [expr.arith.conv]/p1.4.1 or p1.4.3.1, either of which results in size_type being the common type. But I opted for clarity of the desired behavior.

}
return _Result;

return static_cast<_Extents::index_type>(_Result);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may result in a negative number if index_type is signed, but I think the only way that can happen without a precondition violation is in the call from mdspan::size. This is a well-defined conversion from C++20 on, and casting back to size_type, which size() does, recovers the original product.

This is the reason that the specialization below, for the rank_dynamic() == 0 case, works as-is.

}
}
};
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ tests\GH_003676_format_large_hh_mm_ss_values
tests\GH_003735_char_traits_signatures
tests\GH_003840_tellg_when_reading_lf_file_in_text_mode
tests\GH_003867_output_nan
tests\GH_004023_mdspan_fwd_prod_overflow
tests\LWG2381_num_get_floating_point
tests\LWG2597_complex_branch_cut
tests\LWG3018_shared_ptr_function
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/GH_004023_mdspan_fwd_prod_overflow/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_latest_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <cstdint>
#include <mdspan>
#include <type_traits>

using namespace std;

struct layout_packed_upper { // LAPACK packed storage, VERY INCOMPLETE
template <class Extents>
class mapping {
Comment thread
MattStephanson marked this conversation as resolved.
public:
using extents_type = Extents;
using index_type = extents_type::index_type;
using size_type = extents_type::size_type;
using rank_type = extents_type::rank_type;
using layout_type = layout_packed_upper;

constexpr mapping() noexcept = default;
constexpr mapping(const extents_type& ex_) : ex{ex_} {}

constexpr const extents_type& extents() const {
return ex;
}

constexpr index_type required_span_size() const {
if (dim % 2 == 0) {
return (dim / 2) * (dim + 1);
} else {
return ((dim + 1) / 2) * dim;
}
}

private:
extents_type ex = extents_type();
index_type dim = ex.extent(0);
};
};

int main() {
constexpr int32_t dim = 47'000; // sqrt(2^32) > dim > sqrt(2^31), dim assumed even below
constexpr auto expected_size = [] {
int32_t unused;
bool overflow = _Mul_overflow(dim, dim, unused);
assert(overflow);

using UType = remove_cv_t<make_unsigned_t<decltype(dim)>>;
const auto udim = static_cast<UType>(dim);
UType result{};
overflow = _Mul_overflow(udim, udim, result);
assert(!overflow);
return result;
}();

constexpr auto expected_req_size = [] {
int32_t result{};
const bool overflow = _Mul_overflow(dim / 2, dim + 1, result);
assert(!overflow);
return result;
}();

{
using E = extents<int32_t, dim, dim>;
constexpr mdspan<double, E, layout_packed_upper> m(nullptr);
static_assert(m.size() == expected_size);
static_assert(m.mapping().required_span_size() == expected_req_size);
}

{
using E = dextents<int32_t, 2>;
constexpr mdspan<double, E, layout_packed_upper> m(nullptr, dim, dim);
static_assert(m.size() == expected_size);
static_assert(m.mapping().required_span_size() == expected_req_size);
}

return 0;
}