Fix regression in transform_inclusive_scan.
We were deducing a reference type when we needed a value type for the
transform iterator instantition.
Fixes and adds a regression test.
MODIFY
testing/transform_scan.cu
testing/transform_scan.cu
@@ -367,3 +367,56 @@ struct TestTransformScanToDiscardIterator
}
};
VariableUnitTest<TestTransformScanToDiscardIterator, IntegralTypes> TestTransformScanToDiscardIteratorInstance;
+
+// Regression test for https://github.com/NVIDIA/thrust/issues/1332
+// The issue was the internal transform_input_iterator_t created by the
+// transform_inclusive_scan implementation was instantiated using a reference
+// type for the value_type.
+template <typename T>
+void TestValueCategoryDeduction()
+{
+ thrust::device_vector<T> vec;
+
+ T a_h[10] = {5, 0, 5, 8, 6, 7, 5, 3, 0, 9};
+ vec.assign((T*)a_h, a_h + 10);
+
+
+ thrust::transform_inclusive_scan(thrust::device,
+ vec.cbegin(),
+ vec.cend(),
+ vec.begin(),
+ thrust::identity<>{},
+ thrust::maximum<>{});
+
+ ASSERT_EQUAL(T{5}, vec[0]);
+ ASSERT_EQUAL(T{5}, vec[1]);
+ ASSERT_EQUAL(T{5}, vec[2]);
+ ASSERT_EQUAL(T{8}, vec[3]);
+ ASSERT_EQUAL(T{8}, vec[4]);
+ ASSERT_EQUAL(T{8}, vec[5]);
+ ASSERT_EQUAL(T{8}, vec[6]);
+ ASSERT_EQUAL(T{8}, vec[7]);
+ ASSERT_EQUAL(T{8}, vec[8]);
+ ASSERT_EQUAL(T{9}, vec[9]);
+
+ vec.assign((T*)a_h, a_h + 10);
+ thrust::transform_exclusive_scan(thrust::device,
+ vec.cbegin(),
+ vec.cend(),
+ vec.begin(),
+ thrust::identity<>{},
+ T{},
+ thrust::maximum<>{});
+
+ ASSERT_EQUAL(T{0}, vec[0]);
+ ASSERT_EQUAL(T{5}, vec[1]);
+ ASSERT_EQUAL(T{5}, vec[2]);
+ ASSERT_EQUAL(T{5}, vec[3]);
+ ASSERT_EQUAL(T{8}, vec[4]);
+ ASSERT_EQUAL(T{8}, vec[5]);
+ ASSERT_EQUAL(T{8}, vec[6]);
+ ASSERT_EQUAL(T{8}, vec[7]);
+ ASSERT_EQUAL(T{8}, vec[8]);
+ ASSERT_EQUAL(T{8}, vec[9]);
+}
+DECLARE_GENERIC_UNITTEST(TestValueCategoryDeduction);

MODIFY
thrust/system/cuda/detail/transform_scan.h
thrust/system/cuda/detail/transform_scan.h
@@ -50,7 +50,7 @@ transform_inclusive_scan(execution_policy<Derived> &policy,
TransformOp transform_op,
ScanOp scan_op)
{
- // Use the input iterator's value type per https://wg21.link/P0571
+ // Use the transformed input iterator's value type per https://wg21.link/P0571
using input_type = typename thrust::iterator_value<InputIt>::type;
#if THRUST_CPP_DIALECT < 2017
using result_type = typename std::result_of<TransformOp(input_type)>::type;
@@ -58,19 +58,11 @@ transform_inclusive_scan(execution_policy<Derived> &policy,
using result_type = std::invoke_result_t<TransformOp, input_type>;
#endif
- typedef typename thrust::detail::eval_if<
- thrust::detail::has_result_type<TransformOp>::value,
- thrust::detail::result_type<TransformOp>,
- thrust::detail::eval_if<
- thrust::detail::is_output_iterator<OutputIt>::value,
- iterator_value<InputIt>,
- iterator_value<OutputIt>
- >
- >::type result_type;
+ using value_type = typename std::remove_reference<result_type>::type;
typedef typename iterator_traits<InputIt>::difference_type size_type;
size_type num_items = static_cast<size_type>(thrust::distance(first, last));
- typedef transform_input_iterator_t<result_type,
+ typedef transform_input_iterator_t<value_type,
InputIt,
TransformOp>
transformed_iterator_t;
@@ -98,7 +90,7 @@ transform_exclusive_scan(execution_policy<Derived> &policy,
ScanOp scan_op)
{
// Use the initial value type per https://wg21.link/P0571
- using result_type = InitialValueType;
+ using result_type = typename std::remove_reference<InitialValueType>::type;
typedef typename iterator_traits<InputIt>::difference_type size_type;

MODIFY
thrust/system/detail/generic/transform_scan.inl
thrust/system/detail/generic/transform_scan.inl
@@ -51,10 +51,11 @@ __host__ __device__
// Use the input iterator's value type per https://wg21.link/P0571
using InputType = typename thrust::iterator_value<InputIterator>::type;
#if THRUST_CPP_DIALECT < 2017
- using ValueType = typename std::result_of<UnaryFunction(InputType)>::type;
+ using ResultType = typename std::result_of<UnaryFunction(InputType)>::type;
#else
- using ValueType = std::invoke_result_t<UnaryFunction, InputType>;
+ using ResultType = std::invoke_result_t<UnaryFunction, InputType>;
#endif
+ using ValueType = typename std::remove_reference<ResultType>::type;
thrust::transform_iterator<UnaryFunction, InputIterator, ValueType> _first(first, unary_op);
thrust::transform_iterator<UnaryFunction, InputIterator, ValueType> _last(last, unary_op);
@@ -79,7 +80,7 @@ __host__ __device__
AssociativeOperator binary_op)
{
// Use the initial value type per https://wg21.link/P0571
- using ValueType = InitialValueType;
+ using ValueType = typename std::remove_reference<InitialValueType>::type;
thrust::transform_iterator<UnaryFunction, InputIterator, ValueType> _first(first, unary_op);
thrust::transform_iterator<UnaryFunction, InputIterator, ValueType> _last(last, unary_op);

Fix regression in transform_inclusive_scan.

We were deducing a reference type when we needed a value type for the
transform iterator instantition.

Fixes #1332 and adds a regression test.