1

I have a code as follows:

The function is declared in a module using an interface block

    module my_subs
      implicit none

      interface
         function cross(a,b)
           integer, dimension(3) :: cross
           integer, dimension(3), intent(in) :: a, b
         end function cross
      end interface
    end module my_subs

    program crosstest
      use my_subs
      implicit none

      integer, dimension(3) :: m, n
      integer, dimension(3) :: r

      m = [1, 2, 3]
      n = [4, 5, 6]
      r = cross(m,n)
      write(*,*) r
    end program crosstest

    function cross(a,b)
      implicit none
      integer, dimension(3) :: cross
      integer, dimension(3), intent(in) :: a, b

      cross(1) = a(2)*b(3) - a(3)*b(2)
      cross(2) = a(3)*b(1) - a(1)*b(3)
      cross(3) = a(1)*b(2) - a(2)*b(1)
    end function cross

According to this website, the use of interface blocks allows main programs and external subprograms to interface appropriately. However, I tested different mismatch of array size scenarios, I got the following result:

  • Change dimension at line 6 to 2 and 4, the code cannot be compiled;
  • Change dimension at line 7 to 2, the code can be compiled and produce the correct output;
  • Change dimension at line 7 to 4, the code cannot be compiled;
  • Change dimension at line 27 to 2 and 4, the code can be compiled and produce the correct output;
  • Change dimension at line 28 to 2 and 4, the code can be compiled and produce the correct output;

I am confuse about the different scenarios I performed, because I suppose the use of interface can help me to detect any mismatch of array size. In this case, is it better for me to move the function cross(a,b) into the module my_subs using contains?

francescalus
  • 30,576
  • 16
  • 61
  • 96
Wai Kiat
  • 789
  • 1
  • 8
  • 13
  • Just to be clear, you are talking about changing the interface/function independently, so that they no long match? [The obvious thing to say is, don't do that: bad things may happen and it will be your fault not the compiler's.] If you are doing that, you are wondering why some cases don't result in the compiler complaining but some do? – francescalus Nov 23 '17 at 10:49
  • Currently, the function `cross` is compiled *outside* of a module. It is here the responsibility of the programmer to write the corresponding correct interface. Place `cross` in the module if you want the benefits of modules. – Pierre de Buyl Nov 23 '17 at 10:55
  • When you say "the code cannot be compiled" it would be very useful if you you provided any error messages. Which compiler(s) might be useful as well. In fact I can guess what is going on, but these bits of information will make it easier to write an answer – Ian Bush Nov 23 '17 at 11:13
  • @francescalus Yes, I changed the interface or function independently. Yes, in some scenarios, they worked magically well and I wonder why. So, there are times where compiler could not detect the error? – Wai Kiat Nov 23 '17 at 11:29
  • Hi, @IanBush. The error message is as follows `practice1.f90(21): error #6366: The shapes of the array expressions do not conform. [R] r = cross(m,n) --^ compilation aborted for practice1.f90 (code 1)` This is for the first scenario. – Wai Kiat Nov 23 '17 at 11:34

2 Answers2

1

You can check the interface by putting some declarations in function cross that test whether the interface as declared in module my_subs matches what function cross thinks its interface should be:

interface in function cross:
    module my_subs
      implicit none

      interface
         function cross(a,b)
           integer, dimension(3) :: cross
           integer, dimension(3), intent(in) :: a, b
         end function cross
      end interface
    end module my_subs

    program crosstest
      use my_subs
      implicit none

      integer, dimension(3) :: m, n
      integer, dimension(3) :: r

      m = [1, 2, 3]
      n = [4, 5, 6]
      r = cross(m,n)
      write(*,*) r
    end program crosstest

    function cross(a,b) result(res)
      use my_subs, only: check => cross
      implicit none
      integer, dimension(3) :: res
      integer, dimension(3), intent(in) :: a, b
      procedure(check), pointer :: test => cross

      res(1) = a(2)*b(3) - a(3)*b(2)
      res(2) = a(3)*b(1) - a(1)*b(3)
      res(3) = a(1)*b(2) - a(2)*b(1)
    end function cross

gfortran zaps this in all cases of mismatch you tested. I'm not sure that it should: if TKR of a dummy argument matches, shouldn't the rules of sequence association produce a correct invocation of the procedure? I haven't used submodules, but I think that they might do roughly the same thing as my example does.

user5713492
  • 954
  • 5
  • 11
0

When using an interface block to provide an explicit interface within a scope (in this case, in the module, which is then used by the main program) it is a requirement on the programmer that the specified interface matches the actual procedure.1

As given first, these things match happily. Changing the size of the function result or dummy arguments of one statement of the procedure but not the other creates a mismatch. Such code is a violation of the Fortran standard.

In general, a compiler isn't required to detect this violation. It may take your interface block on faith or it may do some extra work to see whether it should believe you. This latter is possible, especially if the external procedure is given in the same file as the interface block. You may see some error messages relating to this.

Further, if the interface block is given, the compiler will test the reference to the procedure against the interface, not the procedure's actual definition.

One failing, on the programmer's part, is if the actual argument isn't compatible with the dummy argument. Such a case is when the dummy argument is an explicit shape array but the actual argument is smaller than the dummy argument. [It isn't an error to have the actual argument larger.]

This problem is one of your cases:

  interface
     function cross(a,b)
       integer, dimension(3) :: cross
       integer, dimension(4), intent(in) :: a, b  ! Dummy extent 4
     end function cross
  end interface

  print*, cross([1,2,3], [4,5,6])                 ! Actuals extent 3
  end

Again, a compiler isn't obliged to notice this for you. It's being nice in the case where it can detect the problem.

Now, in another case you are stating that the function result is an array of shape [4] or [2]. But you are trying to assign that to an array of shape [3]. That won't work.

In conclusion, if you use an interface block to provide an explicit interface for an external procedure make sure it is correct. Turning the external procedure into a module procedure means it's the compiler's responsibility, not yours, to have the correct interface visible.


1 "Matching" here means that the characteristics of the procedure as stated by the interface block are consistent with the procedure's definition. The extents of the function result and dummy arguments are part of this. A procedure defined pure needn't have the interface block stating it is pure.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Thank you so much. I would like to confirm something with you. In the fourth paragraph, procedure = function? So in this case, do you mean that the compiler will check the values of variable passing to the function against the function defined in the interface? And, if i use `contains` instead of `interface`, and place the computation of `cross` into the function block defined in `module`, then this will be the compiler's responsibility? So, when is it appropriate to use interface? I am not from a programming background, but I am really keen to learn. – Wai Kiat Nov 23 '17 at 15:31
  • "procedure" = "subroutine or function". – francescalus Nov 23 '17 at 15:50
  • 2
    Putting the function into the module, replacing the interface block, is one way of providing an explicit interface. As a general rule interface blocks should be the last resort for providing an explicit interface. (See [my comment here](https://stackoverflow.com/questions/47455067/passing-an-array-of-undefined-size-to-a-subroutine#comment81869519_47457320)].) If you can use module procedures instead, do so. – francescalus Nov 23 '17 at 15:53
  • Also, compile-time options to detect out-of-bounds array access can help to debug this type of issue. – Pierre de Buyl Nov 23 '17 at 18:57
  • @francescalus Thank you so much for sharing your experience. – Wai Kiat Nov 25 '17 at 09:30
  • @PierredeBuyl Yes, I tried using `-cb` in my Intel Fortran. This can detect the error of different array sizes in `interface`. Thank you. – Wai Kiat Nov 25 '17 at 09:44