A question about the logical comparison of arrays

When someone was getting bad answers with a new compiler after running
a program for a long time successfully with another it came down to the
somewhat non-intuitive fact that array(i) and array(i:i) are NOT the same
thing. And changing the code to use array(i) made it behave consistently
and as desired with both compilers.

Along the way I was using an analogy of logical operators basically
obeying the same rules as a user-defined operator for an elemental
function - that would mean comparing two arrays of a different size,
even if one array only had one element was not allowed; even if there
were other logical options like

  • only comparing up to the size of the smaller array
  • returning .false. for any remaining comparisons once the smallest
    array was exhausted
  • repeating values in the smaller arrays till they matched the size
    of the largest.

So when hoping to demonstrate this I was surprised that the first three
compilers I tried all compiled and ran the following program without
complaint, and even with a lot of debug flags I only got one to produce
a warning ( NOT an error, and they all continued to run).

So one padded out the answer to the size of the largest array with .FALSE.
and two appear to have stopped once the size of the smaller array was
reached, but no one returned a run-time error that I tried so far. I could
see how it would be a natural extension to treat a single element array
as a scalar but even when I increased the size of II() to two elments
the behavior continued. Is “ii.eq.[10,10,10,10]” always non-standard
unless II() has four elements, and it is just a case of the compilers
not reporting the error, or is one of these behaviors actually defined
by the standard?

program testit
integer,allocatable :: ii(:)
   ii=[10]
   do i=1,2
      write(*,*)     ii.eq.[10,10,10,10]
      write(*,*) all(ii.eq.[10,10,10,10])
      write(*,*) all(ii(1).eq.[10,10,10,10]) ! assumed OK
      ! everyone catches this as not conformable
      !write(*,*) 'array   ',all(ii(1:1).eq.[10,10,10,10])  
      ii=[10,10]
   enddo
end program testit
gfortran app/main.f90
 T F F F
 F
 T
 T T F F
 F
 T
ifort app/main.f90
 T
 T
 T
 T T
 T
 T
nvfortran app/main.f90
 T
 T
 T
 T  T
 T
 T

Is this undefined behavior the compiler can do what it wants with, or
is there an expected behavior, or are the compilers required to produce
an error per the standard? Especially with ALL() and ANY() involved I am not quite so sure anymore.

For whatever it’s worth, the code in the above 2 quoted statements do not conform to the standard. However there are no numbered constraints associated with this that I can find in the standard. The processors are therefore not required to include error-detection capabilities when the instructions do not adhere to the standard, in this case when the arrays in the elemental comparison operation do not conform. Thus the onus falls squarely on the programmer.

The compilers mentioned are nice though in that they do issue error diagnostics when they are able to detect array shape nonconformance at compile-time, like in the case with the commented out line. Some processors may do so at run-time based on additional compiler options e.g., IFORT might do so with -check:shape

An array section is an array (it can be zero-sized, of size 1, etc. and of rank 1 or greater depending on the circumstances), not a scalar per the standard whereas an array element reference is treated as scalar.

Perhaps I missed other switches; ifort can indeed report the error; although it does so as a warning rather than an error. A bit disturbing, or at least disappointing that detecting this is not required. At least that confirms my original thoughts. Did not get the support from the compilers I expected when trying to prove the point though. Since the incorrect usage had produced the correct behavior for so long (with one particular compiler) they were rather sceptical it was really wrong. Thanks for taking the time to confirm it.

With -check option, IFORT does issue a run-time error:

   integer, allocatable :: x(:)
   x = [ 10 ]
   print *, any( x == [ 10, 10 ] )
end

C:\Temp>ifort /check:shape /traceback p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.

Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation. All rights reserved.

-out:p.exe
-subsystem:console
-incremental:no
p.obj

C:\Temp>p.exe
forrtl: severe (408): fort: (33): Shape mismatch: The extent of dimension 1 of array X is 1 and the corresponding extent of array is 2

Image PC Routine Line Source
p.exe 00007FF7687656BC Unknown Unknown Unknown
p.exe 00007FF768761130 MAIN__ 3 p.f90
p.exe 00007FF7687B1F3E Unknown Unknown Unknown
p.exe 00007FF7687B22C0 Unknown Unknown Unknown
KERNEL32.DLL 00007FFF1D0D7034 Unknown Unknown Unknown
ntdll.dll 00007FFF1DCC2651 Unknown Unknown Unknown

C:\Temp>

The Nag compiler with -C=all catches the first 2 at run time.

1 Like

Nice. Having two compilers confirming A(I:I) == [1,2,3,…] is illegal and NOT the same as A(I)=[1,2,3,…] has changed the conversation from arguing about that to their recompiling a bunch of code with those flags on to look for more of the same, and a request for a second compiler; so a relatively happy ending. Just need to check if the default flags in debug mode in fpm(1) have those flags on.

There aren’t a lot of strict and/or detailed flags on by default. I’ve proposed adding an additional “built-in” profile named strict for just this purpose, but was somewhat waiting for the user specified profiles to be finished before doing so. I’d propose that all possible run-time and compile-time checks be on with that profile, but I could be convinced that a few should be reserved for yet another extreme-checking profile (i.e. nagfor’s -C=undefined option).

I also think that after 6 months to a year of users having the ability to specify their own profiles, we should do a survey to see what the most popular profiles and options are, and perhaps make those the built-in ones.

3 Likes

perhaps packages listed on fortran-lang should have a checklist bar: compiles with --profile strict; compiles with compiler XXXX; Developer API documentation; User documentation; number of stars; builds with Make|Cmake|(fpm if just the general package list); available via conda, fpm, number of lines of code, …

1 Like