[Mpi-comments] MPI 3.0: Fortran 2008 interface - issue with the LOGICAL kind

Tobias Burnus burnus at net-b.de
Tue Feb 26 06:19:07 CST 2013


Dear all,

Digest version: The Fortran 2008 interface should use 
"LOGICAL(kind=C_BOOL)" instead of "LOGICAL".


MPI 3 [1] adds a new Fortran 2008 interface (or rather one using Fortran 
2008 [2] plus TS 29113 [3]), which uses C binding ("bind(C)"). For 
instance the following (from "6.7.2 Communicators"):

ABSTRACT INTERFACE
SUBROUTINE MPI_Comm_copy_attr_function(oldcomm, comm_keyval, extra_state,
attribute_val_in, attribute_val_out, flag, ierror) BIND(C)
TYPE(MPI_Comm) :: oldcomm
INTEGER :: comm_keyval, ierror
INTEGER(KIND=MPI_ADDRESS_KIND) :: extra_state, attribute_val_in,
attribute_val_out
LOGICAL :: flag


The code assumes that the default-kind INTEGER and LOGICAL are 
interoperable. It is very likely that default-kind integers are 
interoperable, given that C supports many integer types (signed char, 
short, int, long, long long) and that Fortran's default-kind integer 
typically matches "int".


However, for LOGICAL this is not necessarily the case. Fortran's 
default-kind LOGICAL is always [5] as wide as the default-kind INTEGER 
("LOGICAL(kind=4)"). However, C99's [4] _Bool is on nearly all systems 
(one exception is 32bit Darwin) only one byte wide (sizeof(_Bool) == 1) 
[6]. The Fortran standard only defines interoperability with this 
logical kind:


Fortran 2008, "15.2.2 Named constants and derived types in the module": 
"The value of C_BOOL shall be a valid value for a logical kind parameter 
on the processor or shall be -1."

Fortran 2008, "Table 15.2: Interoperability between Fortran and C types":
"Fortran type / Named constant from the ISO C BINDING module (kind type 
parameter if value is positive) / C type"
...
"LOGICAL / C_BOOL / _Bool"


Hence, a conforming compiler might only accept a single interoperable 
LOGICAL kind, namely: C_BOOL. That's actually the case for the 
PathScale/pathf95 compiler — and also GCC/gfortran (version >= 4.8) with 
-std=f2003/f2008/f2008ts only accepts LOGICAL(C_BOOL) but not LOGICAL [7].


Note an additional potential issue: C99's _Bool only allows two values, 
0 and 1; see C99's "6.3.1.2 Boolean type": "When any scalar value is 
converted to _Bool, the result is 0 if the value compares equal to 0; 
otherwise, the result is 1."

At least the GCC makes use of this restriction and sets TYPE_PRECISION 
to 1 for C99/C++'s _Bool/bool and Fortran's LOGICAL (for all KIND= 
values). As the hardware typically supports instructions of the form 
"jump if zero" or "jump if not zero", also other values work in "if 
(bool_var)". However, for negation (C/C++: "!", Fortran: ".not."), GCC 
only flips one bit. Hence, for C99's _Bool, C++'s bool and Fortran's 
LOGICAL (any kind), negating "-2" leads to "-1".

Example: If you have in one file:
"signed char bool_var = -2; sub(bool_var)"
and in the other file either:
void sub(_Bool bool_var)
or
subroutine sub(bool_var) bind(C)
logical(kind=C_Bool), value :: bool_var
both "bool_var" and "!bool_var"/".not.bool_var" evaluate to true/.true.

In that sense, Fortran's "LOGICAL :: v" is not interoperable with C's 
"int v" unless the value is restricted to 0 and 1.

Note regarding C99: If you assign or cast to _Bool, the compiler ensures 
that only the value 0 and 1 is used. Additionally, many operations in 
C99 return an "int", for instance "&&" or "||" do (see C99, "6.5.13 
Logical AND operator" and 6.5.14). Thus, in pure C code one does not 
easily run into this issue. Nor in pure Fortran code as one there 
typically doesn't mix LOGICAL with INTEGER. But when mixing C and 
Fortran (or mixes code compiled iwth different compilers [8]), this can 
lead to wrong code.


Hence, I strongly suggest to change in all BIND(C) procedures LOGICAL to 
LOGICAL(C_Bool). For the three reasons above: (a) the standard only 
defines interop with kind=C_Bool; (b) pathf95 [and gfortran with 
-std=f2008ts] only support C_Bool kind logicals with Bind(C); (c) at 
least with GCC, using default-kind LOGICAL might lead to wrong results 
if the value is not 0 or 1.


Tobias

PS: The issue came up with Open MPI's bug report 
https://svn.open-mpi.org/trac/ompi/ticket/3523


[1] MPI 3: http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf; 
erratum http://www.mpi-forum.org/docs/errata-30.pdf
[2] Fortran 2008 (FDIS): 
ftp://ftp.nag.co.uk/sc22wg5/N1801-N1850/N1830.pdf ; corrigenda 1 & 2: 
ftp://ftp.nag.co.uk/sc22wg5/N1901-N1950/N1903.pdf , 
ftp://ftp.nag.co.uk/sc22wg5/N1951-N2000/N1958.pdf
[3] TS 29113 (FDIS): ftp://ftp.nag.co.uk/sc22wg5/N1901-N1950/N1942.pdf
[4] C99+TC1,TC2,TC3: 
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
[5] That's required in Fortran 2008's "16.5.3.2 Storage sequence": "a 
nonpointer scalar object that is default integer, default real, or 
default logical occupies a single numeric storage unit,"
[6] That _Bool has is one byte wide is implied by C99's "The rank of 
_Bool shall be less than the rank of all other standard integer types." 
(6.3.1.1), but in my reading it does not formally demand it. With GCC, 
one can force this width for 32bit Darwin via -mone-byte-bool.
[7] Currently, GCC still accepts LOGICAL / LOGICAL(4) with default 
options (-std=gnu); however, that might change. See discussion at 
http://gcc.gnu.org/ml/fortran/2013-01/msg00090.html and 
http://gcc.gnu.org/PR55758
[8] In particular, Intel's Fortran compiler uses "-1" for .true., 
independent of logical kind and bind(C); for the reasons outlined above, 
it thus interacts badly with GCC/gfortran.



More information about the mpi-comments mailing list