Bound checking for empty arrays --- behavior of various compilers - fortran

Update 20210914: Absoft support confirms that the behavior of af95 / af90 described below is unintended and indeed a bug. Absoft developers will work to resolve it. The other compilers act correctly in this regard. Thank #Vladimir F for the answer, comments, and suggestions.
I have the impression that Fortran is cool with arrays of size 0. However, with Absoft Pro 21.0, I encountered a (strange) error involving such arrays. In contrast, gfortran, ifort, nagfor, pgfortran, sunf95, and g95 are all happy with the same piece of code.
Below is a minimal working example.
! testempty.f90
!!!!!! A module that offends AF90/AF95 !!!!!!!!!!!!!!!!!!!!!!!!
module empty_mod
implicit none
private
public :: foo
contains
subroutine foo(n)
implicit none
integer, intent(in) :: n
integer :: a(0)
integer :: b(n - 1)
call bar(a) ! AF90/AF95 is happy with this line.
call bar(b) ! AF90/AF95 is angry with this line.
end subroutine foo
subroutine bar(x)
implicit none
integer, intent(out) :: x(:)
x = 1 ! BAR(B) annoys AF90/AF95 regardless of this line.
end subroutine bar
end module empty_mod
!!!!!! Module ends !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!! Main program !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
program testempty
use empty_mod, only : foo
implicit none
call foo(2) ! AF90/AF95 is happy with this line.
call foo(1) ! AF90/AF95 is angry with this line.
write (*, *) 'Succeed!' ! Declare victory when arriving here.
end program testempty
!!!!!! Main program ends !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Name this piece of code as testempty.f90. Then run
$ af95 -no-pie -et -Rb -g -O0 -o atest testempty.f90
$ ./atest
This is what happened on my machine (Ubuntu 20.04, linux 5.4.0-77-generic, x86_64):
./atest
? FORTRAN Runtime Error:
? Subscript 1 is out of range for dimension 1 for array
? B with bounds 1:
? File testempty.f90; Line 19
? atest, run-time exception on Mon Sep 13 14:08:41 2021
? Program counter: 000000001004324B
? Signal SIGABRT, Abort
? Traceback follows
OBJECT PC ROUTINE LINE SOURCE
libpthread.so.0 000000001004324B raise N/A N/A
atest 00000000004141F3 __abs_f90rerr N/A N/A
atest 000000000041CA81 _BOUNDS_ERROR N/A N/A
atest 00000000004097B4 __FOO.in.EMPTY_MO N/A N/A
atest 000000000040993A MAIN__ 40 testempty.f90
atest 000000000042A209 main N/A N/A
libc.so.6 000000000FD0C0B3 __libc_start_main N/A N/A
atest 000000000040956E _start N/A N/A
So af95 was annoyed by call bar(b). With af90, the result was the same.
I tested the same code using gfortran, ifort, nagfor, pgfortran, sunf95, and g95. All of them were quite happy with the code even though I imposed bound checking explicitly. Below is the Makefile for the tests.
# This Makefile tests the following compilers on empty arrays.
#
# af95: Absoft 64-bit Pro Fortran 21.0.0
# gfortran: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
# ifort: ifort (IFORT) 2021.2.0 20210228
# nagfor: NAG Fortran Compiler Release 7.0(Yurakucho) Build 7036
# pgfortran: pgfortran (aka nvfortran) 21.3-0 LLVM 64-bit x86-64
# sunf95: Oracle Developer Studio 12.6
# g95: G95 (GCC 4.0.3 (g95 0.94!) Jan 17 2013)
#
# Tested on Ubuntu 20.04 with Linux 5.4.0-77-generic x86_64
.PHONY: test clean
test:
make -s gtest
make -s itest
make -s ntest
make -s ptest
make -s stest
make -s 9test
make -s atest
gtest: FC = gfortran -Wall -Wextra -fcheck=all
itest: FC = ifort -warn all -check all
ntest: FC = nagfor -C
ptest: FC = pgfortran -C -Mbounds
stest: FC = sunf95 -w3 -xcheck=%all -C
9test: FC = g95 -Wall -Wextra -fbounds-check
atest: FC = af95 -no-pie -et -Rb
%test: testempty.f90
$(FC) -g -O0 -o $# $<
./$#
clean:
rm -f *.o *.mod *.dbg *test
Questions:
Is the behavior of af95/af90 standard-conforming?
Does my code contain anything that violates the Fortran standards?
In general, is it considered dangerous to involve empty arrays in Fortran code? Sometimes they are inevitable given the fact the data sizes are often undecidable before runtime.
By "standards", I mean 2003, 2008, and 2018.
Thank you very much for any comments or criticism.
(The same question is posed on Fortran Discourse, and I hope it does not violate the rules here.)

The program looks OK to me. Zero-sized arrays are perfectly possible in Fortran although I admit I normally do not have automatic ones - but that is just a coincidence.
I think it is a compiler bug in the Absoft compiler or its array bounds checker.

Related

Strange behavior of "gfortran -Wconversion"

Consider the following code.
! test.f90
program test
use iso_fortran_env, only: INT64, REAL64
print *, real(0_INT64, REAL64)
print *, real(1000_INT64, REAL64)
print *, real(huge(0_INT64), REAL64)
end program test
When compiling it with gfortran in the following way:
$ gfortran -Wconversion -std=f2008 test.f90
I got the following warning:
test.f90:5:18:
5 | print *, real(huge(0_INT64), REAL64)
| 1
Warning: Change of value in conversion from ‘INTEGER(8)’ to ‘REAL(8)’ at (1) [-Wconversion]
Note that gfortran is happy with the first two conversions, but not the last one.
Question: Is the warning illustrated above an expected behavior of gfortran? I thought that NO warning should be produced in any of the three cases, since the conversion is done explicitly by REAL( , INT64).
Here is the version information of my gfortran:
$ gfortran --version
GNU Fortran (Ubuntu 9.3.0-10ubuntu2) 9.3.0
As a reference, ifort 19.1.127 compiles test.f90 without any complaint:
$ ifort -warn all -stand f08 test.f90
Thank you very much for any comments or critics.
Answer by #dave_thompson_085 in the comments:
“0 and 1000 can be represented exactly in REAL64 (and even in REAL32). HUGE(INT64) is 9223372036854775807 and it cannot. REAL64 has 53 bits for the 'mantissa' (really, significand), and after subtracting the sign and adding the hidden bit this supports just under 16 decimal digits of magnitude. 9223372036854775807 is 19 decimal digits. This is not a diagnostic required by the standard, so it's up to each 'processor' (compiler) what to do about it.”
Thank you very much, #dave_thompson_085.

Fortran (re-)allocation on assignment and gfortran warnings

A simple code:
program main
integer, allocatable :: A(:,:)
integer :: B(3,4)
B=1
A = B !A will get allocated, with the same shape and bounds as B
end program main
Compiling the above code with: gfortran-8 -std=f2008 -fcheck=all -Wall -Wextra -fbounds-check -fimplicit-none array.f90
I got the following warning:
Warning: ‘a.offset’ may be used uninitialized in this function
Warning: ‘a.dim[0].lbound’ may be used uninitialized in this function
Warning: ‘a.dim[0].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
Is there somebody having an idea of why I got these warnings?
This is a well known GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77504 that has been reported in many duplicates to the bugzilla (even by myself) - see the Duplicates section for the other incarnations.
As far as I understand it the actual code generated by the compiler should work correctly and it is just an incorrect warning. As Steve pointed out in the comment, use -Wno-maybe-uninitialized to hide this warning. I have included it into my build scripts as well.

"Fortran runtime error: End of file" while writing

I have written a piece of code, compiled with GNU Fortran (GCC) 7.2.1 20171128 on Arch Linux, that tries to write to a file. The unit is opened with the newunit=... Fortran 2008-feature
When trying to write to the file, the code crashes, raising the error Fortran runtime error: End of file.
Non working code
Here's a minimal non-working version of the code. If the file does not exist, the code crashes with gfortran 7.2.1
program foo
implicit none
character(len=80) :: filename
character(len=5) :: nchar
integer :: ilun=1
call title(1, nchar)
! nchar = '00001'
filename = trim(nchar)//'.txt'
write(*, '(a, "<", a, ">")') 'filename ', trim(filename)
open(newunit=ilun, file=trim(filename), form='formatted', status='replace')
write(ilun, '(a1,a12,a10)') '#', 'Family', 'Count'
close(ilun)
end program foo
subroutine title(n, nchar)
implicit none
integer, intent(in) :: n
character(len=5), intent(out) :: nchar
write(nchar, '(i0.5)') n
end subroutine title
Here the command I'm using rm -f 00001.txt; gfortran foo.f90 -o a.out && ./a.out.
Working code
By comparison, the following code compiles and works perfectly on the same machine
program foo
implicit none
character(len=80) :: filename
character(len=5) :: nchar
integer :: ilun=1
! call title(1, nchar)
nchar = '00001'
filename = trim(nchar)//'.txt'
write(*, '(a, "<", a, ">")') 'filename ', trim(filename)
open(newunit=ilun, file=trim(filename), form='formatted', status='replace')
write(ilun, '(a1,a12,a10)') '#', 'Family', 'Count'
close(ilun)
end program foo
Here's the command I'm using rm -f 00001.txt; gfortran foo.f90 -o a.out && ./a.out.
Important note
Both codes work well when compiled using ifort (any version tried between ifort15 and ifort18) as well as GNU Fortran (GCC) 6.4.1 20171003 and GNU Fortran (GCC) 7.2.0, so there seems to be an issue introduced in version 7.2.1 of gfortran or on the version bundled with Arch Linux.
A few comments
If you uncomment nchar = '00001' in the non-working example, it still doesn't work.
If you change newunit=ilun to unit=ilun, with e.g. ilun=10 before, it works in any case
System details
OS: GNU Linux
Distribution: Arch Linux (up-to-date as of 15-12-2017)
$ uname -a
Linux manchot 4.14.4-1-ARCH #1 SMP PREEMPT Tue Dec 5 19:10:06 UTC 2017 x86_64 GNU/Linux
$ gfortran --version
GNU Fortran (GCC) 7.2.1 20171128
Copyright (C) 2017 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.
This issue is related to the Arch Linux distribution of Gfortran 7.2.1. It has now been fixed (see https://bugs.archlinux.org/task/56768).
If you encounter the issue, you should update your installation using
pacman -Syu gcc-fortran

Loop vectorization gives different answer

I am building some unit tests and find that my code gives a slightly different result when vectorized. In my example case below, an array a is summed in one dimension and added to an initial value x. Most elements of a are too small to change x. The code is:
module datamod
use ISO_FORTRAN_ENV, only : dp => REAL64
implicit none
! -- Array dimensions are large enough for gfortran to vectorize
integer, parameter :: N = 6
integer, parameter :: M = 10
real(dp) :: x(N), a(N,M)
contains
subroutine init_ax
! -- Set a and x so the issue manifests
x = 0.
x(1) = 0.1e+03_dp
a = 0.
! -- Each negative component is too small to individually change x(1)
! -- But the positive component is just big enough
a( 1, 1) = -0.4e-14_dp
a( 1, 2) = -0.4e-14_dp
a( 1, 3) = -0.4e-14_dp
a( 1, 4) = 0.8e-14_dp
a( 1, 5) = -0.4e-14_dp
end subroutine init_ax
end module datamod
program main
use datamod, only : a, x, N, M, init_ax
implicit none
integer :: i, j
call init_ax
! -- The loop in question
do i=1,N
do j=1,M
x(i) = x(i) + a(i,j)
enddo
enddo
write(*,'(a,e26.18)') 'x(1) is: ', x(1)
end program main
The code gives the following results in gfortran without and with loop vectorization. Note that ftree-vectorize is included in -O3, so the problem manifests when using -O3 also.
mach5% gfortran -O2 main.f90 && ./a.out
x(1) is: 0.100000000000000014E+03
mach5% gfortran -O2 -ftree-vectorize main.f90 && ./a.out
x(1) is: 0.999999999999999858E+02
I know that certain compiler options can change the answer, such as -fassociative-math. However, none of those are included in the standard -O3 optimization package according to the gcc optimization options page.
It seems to me as though the vectorized code is adding up all components of a first, and then adding to x. However, this is incorrect because the code as written requires each component of a to be added to x.
What is going on here? May loop vectorization change the answer in some cases? Gfortran versions 4.7 and 5.3 had the same problem, but Intel 16.0 and PGI 15.10 did not.
I copied the code you provided (to a file called test.f90) and then I compiled and ran it using version 4.8.5 of gfortran. I found that results from the -O2 and -O2 -ftree-vectorize options differ just as your results differ. However, when I simply used -O3, I found that the results matched -O2.
$ gfortran --version
GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
$ gfortran -O2 test.f90 && ./a.out
x(1) is: 0.100000000000000014E+03
$ gfortran -O2 -ftree-vectorize test.f90 && ./a.out
x(1) is: 0.999999999999999858E+02
$ gfortran -O3 test.f90 && ./a.out
x(1) is: 0.100000000000000014E+03

Fortran unidentified reference to 'gplot_'

For the past several days I haven't been able to get some Fortran code to compile (note that I am not the author of this code, I downloaded it from the author's web page. It's open source). The main program is called multitaper.f95, and it uses modules called plot and spectra, as well as libraries called mwlib.a and gplot.a. I have multitaper.f95, mwlib.a, gplot.a, plot.mod, and spectra.mod all in the same directory for simplicity. The plot and spectra moduels are made from gplot.f90 and mtspec.f95, respectively, which are also in the same directory as everything else. However, the following command produces an error message:
gfortran multitaper.f95 mtspec.f95 gplot.f90 -o multitaper -L gplot.a mwlib.a
/tmp/ccBJzwYI.o: In function `MAIN__':
multitaper.f95:(.text+0x1111): undefined reference to `gplot_'
multitaper.f95:(.text+0x11af): undefined reference to `gplot_'
multitaper.f95:(.text+0x1246): undefined reference to `gplot_'
collect2: error: ld returned 1 exit status
The contents of gplot.f90 are
module plot
!
! Interface to allow for x or y to be a matrix, and plot each vector
! of y against x. The matrix is given by horizontal vectors, y(:,1) is
! the first vector.
! April 22 2005
! Allowing to also use real(4) and real(8) units.
!
!
! Interface
! dvv vector vs vector
! dvm vector vs matrix
! dmm matrix vs matrix
! rvv real(4) vector vs vector
! rvm real(4) vector vs matrix
! rmm real(4) matrix vs matrix
!
interface gnuplot
module procedure gnuplot_dvv, gnuplot_dvm, gnuplot_dmm, &
gnuplot_rvv, gnuplot_rvm, gnuplot_rmm, &
gnuplot_rv, gnuplot_rm
end interface gnuplot
! The subroutines
contains
!The NEW GNUPlot subroutines
include 'gnuplot.f90'
end module plot
!======================================================================
Gnuplot.f90 has 3700 lines of code, so I won't post it in here. I'm new to Fortran, so I apologize in advance if I'm simply doing something stupid. But, I've been combing the internet for days looking for a solution, but I didn't turn up much. I found this (Fortran compilation error - undefined reference), but multitaper.f95 has a use statement for both spectra and plot, and gplot and the subroutines of gnuplot.f90 are not private. According to https://gcc.gnu.org/wiki/GFortranGettingStarted and http://www.oceanographers.net/forums/showthread.php?378-How-to-make-a-FORTRAN-library my terminal incantation should work, to the best of my knowledge. Just to be sure, I went ahead and tried compiling gplot.f90 and mtspec.f95 separately and giving the object files to the gfortran command instead, but this changed nothing. I also tried changing the file extension of gplot.f90 and gnuplot.f90 to f95, since there shouldn't be anything in there that would result in a version conflict, but once again this produced the same error message. I also tried including the full path to the directory after the -L command, just in case, and got the same error message.
The Makefile doesn't work any better, and I admittedly don't know much about them and how they work. When I run the make command, I get the following error as when I try to compile it manually. The contents of the Makefile are as follows:
# Location of files
DIR = /N/u/rccaton/Quarry/EarthHum/mtspec/program
LIB = /N/u/rccaton/Quarry/EarthHum/mtspec/program
#LIB2 =
# Objects and Libraries
OBJS = $(LIB)/mwlib.a \
$(LIB)/gplot.a \
# /Applications/Absoft/lib/libU77.a
# Module locations
MODS = $(LIB)
MODS2 = $(LIB)
# Compiler
#FC = g95
#FC = f95
FC = gfortran
# Compiler flags
# none
FLAGS =
# debug
#FFLAGS = -g
# Module flag
# Sun Compiler
#MFLAG = -M
# Nag Compiler
#MFLAG = -i
MFLAG = -I
# Absoft Compiler
#MFLAG = -p
# g95 compiler
#MFLAG = -I
MODULE = $(MFLAG)$(MODS) # $(MFLAG)$(MODS2)
# Compile
all : multitaper
%: %.f95 $(OBJS)
$(FC) $(FFLAGS) $(MODULE) $< $(OBJS) -o $#
# Clean
clean :
rm multitaper
At the end of the day it makes to difference to me whether I end up compiling it with the makefile or with manual commands, I just need it to run. Sorry if this was verbose, I just want to provide as much relevant info as possible. Thank you for any assistance you can give.
I would suggest to find which symbol is exported from gplot.a
nm gplot.a | grep -i "gplot"
and change compilers flags accordingly, search for gfortran flags:
-fno-underscoring
-fsecond-underscore
-fcase-*
In case it does not help, ask the author to give you correct MakeFile.