Arguments check : caller / callee

I have the same type of problem with my own legacy codes. One approach is to define local pointers of the correct TKR, and associate those pointers with the appropriate slice of your work array. Here is a short working example.

program localp
   use, intrinsic :: iso_fortran_env, only: wp=>real32
   use, intrinsic :: iso_c_binding, only: c_f_pointer, c_loc
   implicit none
   integer, parameter :: n = 100
   real(wp), target :: work(n)
   integer, pointer :: iwp(:)

   !call sub( n, work )  ! original call violates TKR.

   call c_f_pointer( c_loc(work), iwp, [n] )
   call sub( n, iwp )  ! updated call with correct TKR.
contains
   subroutine sub( n, iwork )
      integer, intent(in)    :: n
      integer, intent(inout) :: iwork(n)
      integer :: i
      do i = 1, n
         iwork(i) = (i*(i+1))/2
      enddo
      write(*,'(*(g0))') 'iwork(1)=', iwork(1), ' iwork(', n, ')=', iwork(n)
      return
   end subroutine sub
end program localp

$ gfortran localp.f90 && a.out
iwork(1)=1 iwork(100)=5050

For this to work, you must define each pointer with the c_f_pointer(c_loc()...) sequence before the local pointer is used. In principle, it only works for interoperable types, but in practice it will also work for many other cases, such as derived types (which you will not have in legacy codes). This uses the C interoperability features of the language, but there is actually no C code involved at all, everything is done in fortran.

The above example uses an explicit shape dummy array. This is probably what you have in your legacy code. However, this approach is more general, for example it also works correctly with assumed shape dummy arrays. My work arrays in my legacy codes are of type REAL, so that is the way I wrote the above example, but this approach also works the other way, with an integer work array and a real pointer.