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