runtime failure due to useing character*8 in DECLARE_CCTK_ARGUMENTS_CHECKED macro
I just learned (via Skype) that Cactus' use of character*8
variables can cause runtime errors in Fortran:
At line 13 of file Cactus/arrangements/EinsteinBase/TmunuBase/src/SetStressEnergyState.F90 Fortran runtime error: Actual string length is shorter than the declared one for dummy argument 'dtbetax_p' (0/8)
and digging down a bit and looking at the processed Fortran file (configs/sim/build/TmunuBase/SetStressEnergyState.f90) one finds:
characTer*8, intent(IN) :: dtbetax_p integer, parameter :: cctki_use_dtbetax_p = kind(dtbetax_p)
and https://stackoverflow.com/questions/4780069/passing-a-string-as-an-argument-when-the-dummy-has-specified-length where the Fortran 2003 standard gets quoted with an explanation:
If a scalar dummy argument is of type default character, the length len of the dummy argument shall be less than or equal to the length of the actual argument. The dummy argument becomes associated with the leftmost len characters of the actual argument.
This all used gfortran 11.
Safest would be if we could declare as characTer*0
if that is allowed. Of course it’s still wrong in that the actual argument passed to dtbetax_p
is never a character at all (it’s either a double precision real or NULL) and we only use the characTer
construct to avoid assignment and also unused actual argument warnings.
Comments (19)
-
-
reporter We are using
characTer*8, intent(IN) :: dtbetax_p
for grid functions that are neither read nor written by a scheduled function that is that should not even be accessible. Since there is only a single C to Fortran wrapper per thorn, they are still being passed to the Fortran subroutine but we want to make sure that the compiler aborts with an error if the code actually tries to access it.Declaring it as a nonsensical type achieves that since the compiler will not let you assign a real or integer number to a
charcater*8
. We can only usecharacter*(*)
if that does not add an extra integer “length of string” to the end of the argument list.We may however have to do something like:
CCTK_REAL :: dtbetax_p integer, parameter :: cctki_use_dtbetax_p = kind(dtbetax_p) #define dtbetax_p dtbetax_p_IS_NOT_ACCESSIBLE
-
Clever!
Character types are a bit magical in Fortran; they don’t really behave like strings in C. You could use
logical*8
instead; assignments betweeninteger
/`real` andlogical
are also disallowed in Fortran. -
That’s a trivial enough change to make
-
reporter Changing from
charater*8
tological*8
may not be not be enough to avoid assignment. The intel compiler (ifort (IFORT) 18.0.3 20180410) compiles this without warning:program foo implicit none logical*8 :: bar integer*4 :: baz bar = baz end program
On the other hand we are not worried about assignment to the pointer itself. We want to fails is things like:
bar(1,2,3)
for reading and writing and passingbar
to a subroutine (which we likely cannot prevent no matter what).So it should be enough for our purposes.
-
reporter Ah, one exception: we do worry about assignment without parenthesis in the case of grid scalars eg
ADMBase::shift_state
would be accessed like thisif(shift_state .ne. 0) then STOP endif
. Though I am not sure if eithercharater*8
orlogical*8
would prevent this (I suspect not). -
Which line triggers the original error about the string length? Is it the call to the
kind
function, or is it the call to that routine? -
reporter It apparently happened in a simulation of Kiki’s (Kyriaki Dionysopoulou). Apparently this happened (the error messages in the ticket description) in a regular run and “with different parameter files” and “completely different setups” (quoting Kiki). I have no idea why this never showed up before in the time since it has been introduced.
Not sure if this matters but apparently this happened on (at least) an older, Intel macOS laptop using:
CPP = cpp-11
FPP = cpp-11
CC = gcc-11
CXX = g++-11
F90 = gfortran-11GNU Fortran (Homebrew GCC 11.2.0) 11.2.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -
reporter The error message mentions a line number (13) in
Cactus/arrangements/EinsteinBase/TmunuBase/src/SetStressEnergyState.F90
which is the line:subroutine TmunuBase_SetStressEnergyState (CCTK_ARGUMENTS)
The error message also gives the dummy argument in question and the found and expected lengths (0 and 8 respectively):
'dtbetax_p' (0/8)
-
The problem is caused by bounds checking, in particular the
-fcheck=bounds
option that is used in debug mode.See <https://godbolt.org/z/zhGv3bjj6> for a shortened example. The error message and the comparison (line 8 ) are clearly visible.
I assume what happens is that gfortran stores the actual string length in memory, presumably just before the actual string. With bounds checking enabled, the string length is checked. Cactus never sets up the string length since it just reinterprets pointers, assuming that gfortran’s string layout mimics that of C.
You could define a new type (“struct”) instead of using
character*8
:subroutine sub(beta) implicit none type empty end type type(empty) beta end subroutine sub
However, this wouldn’t allow the
kind
trick to make the function argument appear used. -
reporter Interesting. A
logical*8
would still be viable (there is no bounds check for it). I’d rather risk cases of grid scalars not being found (read only at least, write access is caught due toINTENT(IN)
) than having lots of “unused actual argument” warnings. -
reporter Something like this would work:
type inaccessible integer :: dummy end type type(inaccessible), intent(IN):: alp integer, parameter :: cctki_use_alp = kind(alp%dummy)
Trying to access it via eg
foo = alp
produces an error:Error: Cannot convert TYPE(inaccessible) to INTEGER(4) at (1)
One could even make
dummy
of the actualCCTK_XXX
type of the grid variable. -
reporter In today’s ET call http://lists.einsteintoolkit.org/pipermail/users/2021-October/008299.html it was decided to go ahead with https://bitbucket.org/einsteintoolkit/tickets/issues/2571/runtime-failure-due-to-useing-character-8#comment-61325643 and see if that break anything.
-
reporter -
reporter -
assigned issue to
-
assigned issue to
-
reporter - changed status to open
-
reporter Approved by @Steven R. Brandt
-
reporter Applied as git hash e5fc437 "TestReadWrite: fix some comments" of cactustest
Applied as git hash 094f0c87 "Cactus: update documentation about unused variables in CHECKED macros" of cactus
-
reporter - changed status to resolved
- Log in to comment
A declaration
character*8
as input argument is really strange. one would only use that if one knows that the length is 8, similar to passing a character array in C (instead of a character pointer).Usually one would write
character*(*)
, and the compiler then passes the actual string length as additional (hidden) argument, and uses that length in the callee.In this case I rather think that the Fortran code is a mistranslation from C, and that the code generating the
_CHECKED
macro is wrong.dtbetax_p
should be declared asCCTK_REAL dtbetax_p(ash0, ash1, ash2)
instead. None of the Cactus grid function or parameter passing arguments or declaration use Fortrancharacter
variables.