FieldIterator conversion operator triggers nvcc diagnostics
The FieldIterator class template provides a conversion operator that adds constness to the underlying type:
template <typename T, uint_t fieldFSize>
class FieldIterator
{
public:
using value_type = T;
/* ... */
operator const FieldIterator<const T, fieldFSize> & () const {
const FieldIterator<const T, fieldFSize> * ptr;
ptr = reinterpret_cast< const FieldIterator<const T, fieldFSize>* > ( this );
return *ptr;
}
};
When the underlying type T is already const-qualified, the conversion operator becomes redundant. This is ignored by GCC and Clang, but nvcc generates diagnostic #554-D. Here is a MWE:
#include <blockforest/Initialization.h>
#include <blockforest/StructuredBlockForest.h>
#include <domain_decomposition/BlockDataID.h>
#include <domain_decomposition/IBlock.h>
#include <field/AddToStorage.h>
#include <field/FlagField.h>
int main() {
using namespace walberla;
using FlagField = field::FlagField<uint8_t>;
auto blocks = walberla::blockforest::createUniformBlockGrid(
// number of blocks in each direction
uint_c(1), uint_c(1), uint_c(1),
// number of cells per block in each direction
uint_c(2), uint_c(2), uint_c(2),
// lattice constant
1.,
// number of cpus per direction
uint_c(1), uint_c(1), uint_c(1),
// periodicity
true, true, true);
auto flag_field_id = field::addFlagFieldToStorage<FlagField>(blocks, "flag field", uint_c(1));
for (auto &block : *blocks) {
auto flag_field = block.template getData<FlagField>(flag_field_id);
}
}
Output:
$ nvcc --compiler-bindir=/usr/bin/g++-11 -std=c++20 \
--generate-code=arch="compute_75,code=[compute_75,sm_75]" \
-Xcompiler=-fPIE -Xptxas=-O3 -Xcompiler=-O3,-Wall,-Wextra \
-I_deps/walberla-src/src/ -I_deps/walberla-build/src/ \
-isystem "/usr/lib/x86_64-linux-gnu/openmpi/include" \
-isystem "/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi" \
-isystem "/usr/local/cuda-12.0/targets/x86_64-linux/include" \
-c mwe.cu
_deps/walberla-src/src/field/iterators/FieldIterator.h(97): warning #554-D: "walberla::field::FieldIterator<T, fieldFSize>::operator const walberla::field::FieldIterator<const walberla::uint8_t, 1UL> &() const [with T=const walberla::uint8_t, fieldFSize=1UL]" will not be called for implicit or explicit conversions
detected during:
instantiation of class "walberla::field::FieldIterator<T, fieldFSize> [with T=const walberla::uint8_t, fieldFSize=1UL]"
_deps/walberla-src/src/field/iterators/FieldIterator.h(219): here
instantiation of class "walberla::field::ForwardFieldIterator<T, fieldFSize> [with T=const walberla::uint8_t, fieldFSize=1UL]"
_deps/walberla-src/src/field/Field.impl.h(1024): here
instantiation of "__nv_bool walberla::field::Field<T, fSize_>::operator==(const walberla::field::Field<T, fSize_> &) const [with T=walberla::uint8_t, fSize_=1UL]"
_deps/walberla-src/src/domain_decomposition/IBlock.h(65): here
instantiation of "__nv_bool walberla::domain_decomposition::internal::BlockData::Data<T>::operator==(const walberla::domain_decomposition::internal::BlockData::DataBase &) const [with T=walberla::field::FlagField<walberla::uint8_t>]"
_deps/walberla-src/src/domain_decomposition/IBlock.h(61): here
instantiation of "walberla::domain_decomposition::internal::BlockData::Data<T>::Data(T *) [with T=walberla::field::FlagField<walberla::uint8_t>]"
_deps/walberla-src/src/domain_decomposition/IBlock.h(85): here
[ 10 instantiation contexts not shown ]
instantiation of "std::shared_ptr<_Tp> std::allocate_shared<_Tp,_Alloc,_Args...>(const _Alloc &, _Args &&...) [with _Tp=walberla::blockforest::internal::BlockDataHandlingHelper<walberla::field::FlagField<walberla::uint8_t>>, _Alloc=std::allocator<walberla::blockforest::internal::BlockDataHandlingHelper<walberla::field::FlagField<walberla::uint8_t>>>, _Args=<std::shared_ptr<walberla::blockforest::BlockDataHandling<walberla::field::FlagField<walberla::uint8_t>>> &>]"
/usr/include/c++/11/bits/shared_ptr.h(879): here
instantiation of "std::shared_ptr<_Tp> std::make_shared<_Tp,_Args...>(_Args &&...) [with _Tp=walberla::blockforest::internal::BlockDataHandlingHelper<walberla::field::FlagField<walberla::uint8_t>>, _Args=<std::shared_ptr<walberla::blockforest::BlockDataHandling<walberla::field::FlagField<walberla::uint8_t>>> &>]"
_deps/walberla-src/src/blockforest/BlockForest.h(862): here
instantiation of "walberla::domain_decomposition::BlockDataID walberla::blockforest::BlockForest::addBlockData(const std::shared_ptr<T> &, const std::string &, const walberla::Set<walberla::uid::SUID> &, const walberla::Set<walberla::uid::SUID> &) [with T=walberla::field::AlwaysInitializeBlockDataHandling<walberla::field::FlagField<walberla::uint8_t>>]"
_deps/walberla-src/src/blockforest/StructuredBlockForest.h(139): here
instantiation of "walberla::domain_decomposition::BlockDataID walberla::blockforest::StructuredBlockForest::addBlockData(const std::shared_ptr<T> &, const std::string &, const walberla::Set<walberla::uid::SUID> &, const walberla::Set<walberla::uid::SUID> &) [with T=walberla::field::AlwaysInitializeBlockDataHandling<walberla::field::FlagField<walberla::uint8_t>>]"
_deps/walberla-src/src/field/AddToStorage.h(72): here
instantiation of "walberla::domain_decomposition::BlockDataID walberla::field::addFlagFieldToStorage(const std::shared_ptr<BlockStorage_T> &, const std::string &, walberla::uint_t, __nv_bool, const std::function<void (FlagField_T *, walberla::domain_decomposition::IBlock *)> &, const walberla::Set<walberla::uid::SUID> &, const walberla::Set<walberla::uid::SUID> &) [with FlagField_T=walberla::field::FlagField<walberla::uint8_t>, BlockStorage_T=walberla::blockforest::StructuredBlockForest]"
mwe.cu(22): here
Reproducible with nvcc 12.0 and 11.5, with C++17 and C++20 modes.
I tried leveraging SFINAE, but the following patch triggers compiler diagnostics in Clang:
diff --git a/src/field/iterators/FieldIterator.h b/src/field/iterators/FieldIterator.h
index 7557fea2..4e193048 100644
--- a/src/field/iterators/FieldIterator.h
+++ b/src/field/iterators/FieldIterator.h
@@ -95,4 +95,8 @@ namespace field {
inline bool operator!=( const FieldIterator& it ) const;
+ template <typename U = T,
+ typename = typename std::enable_if< std::is_same< U, T >::value &&
+ ! std::is_const< U >::value
+ >::type >
operator const FieldIterator<const T, fieldFSize> & () const {
const FieldIterator<const T, fieldFSize> * ptr;
The following patch fails in GhostLayerField iterators:
diff --git a/src/field/iterators/FieldIterator.h b/src/field/iterators/FieldIterator.h
index 7557fea2..470959b2 100644
--- a/src/field/iterators/FieldIterator.h
+++ b/src/field/iterators/FieldIterator.h
@@ -95,5 +95,5 @@ namespace field {
inline bool operator!=( const FieldIterator& it ) const;
- operator const FieldIterator<const T, fieldFSize> & () const {
+ operator const FieldIterator<typename std::enable_if<! std::is_const< T >::value, const T>, fieldFSize> & () const {
const FieldIterator<const T, fieldFSize> * ptr;
ptr = reinterpret_cast< const FieldIterator<const T, fieldFSize>* > ( this );
It doesn't seem possible, nor desirable according to GhostLayerField, to conditionally disable this conversion operator when the underlying type is already const-qualified. Perhaps this conversion operator is well-formed and the nvcc diagnostic is a false positive?
The diagnostic can be silenced by adding the following pragmas before waLBerla header file includes:
#ifdef __CUDACC__
#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
#pragma nv_diag_suppress 554
#else
#pragma diag_suppress 554
#endif
#endif